2013-11-12 23:28:15 +00:00
# include "ThemeData.h"
# include "Renderer.h"
# include "resources/Font.h"
# include "Sound.h"
# include "resources/TextureResource.h"
# include "Log.h"
2014-05-01 02:12:45 +00:00
# include "Settings.h"
2017-04-03 18:45:28 +00:00
# include "pugixml/src/pugixml.hpp"
2013-12-30 23:23:34 +00:00
# include <boost/assign.hpp>
2017-05-14 04:07:28 +00:00
# include <boost/xpressive/xpressive.hpp>
2013-11-12 23:28:15 +00:00
2014-01-03 14:26:39 +00:00
# include "components/ImageComponent.h"
# include "components/TextComponent.h"
2014-01-26 22:20:21 +00:00
// This is a work around for some ambiguity that is introduced in C++11 that boost::assign::map_list_of leave open.
// We use makeMap(actualmap) to implicitly convert the boost::assign::map_list_of's return type to ElementMapType.
// Problem exists with gcc 4.7 and Boost 1.51. Works fine with MSVC2010 without this hack.
typedef std : : map < std : : string , ThemeData : : ElementPropertyType > ElementMapType ;
template < typename T >
ElementMapType makeMap ( const T & mapInit )
{
ElementMapType m = mapInit ;
return m ;
}
2017-03-10 18:49:15 +00:00
std : : vector < std : : string > ThemeData : : sSupportedViews = boost : : assign : : list_of ( " system " ) ( " basic " ) ( " detailed " ) ( " video " ) ;
2017-04-22 14:15:16 +00:00
std : : vector < std : : string > ThemeData : : sSupportedFeatures = boost : : assign : : list_of ( " video " ) ( " carousel " ) ( " z-index " ) ;
2017-03-10 18:49:15 +00:00
2014-01-26 22:20:21 +00:00
std : : map < std : : string , ElementMapType > ThemeData : : sElementMap = boost : : assign : : map_list_of
( " image " , makeMap ( boost : : assign : : map_list_of
2013-12-30 23:23:34 +00:00
( " pos " , NORMALIZED_PAIR )
( " size " , NORMALIZED_PAIR )
2014-01-10 23:45:47 +00:00
( " maxSize " , NORMALIZED_PAIR )
2013-12-30 23:23:34 +00:00
( " origin " , NORMALIZED_PAIR )
2017-07-17 03:10:29 +00:00
( " rotation " , FLOAT )
( " rotationOrigin " , NORMALIZED_PAIR )
2013-12-30 23:23:34 +00:00
( " path " , PATH )
2017-10-01 03:17:58 +00:00
( " default " , PATH )
2014-01-23 21:30:32 +00:00
( " tile " , BOOLEAN )
2017-04-22 14:15:16 +00:00
( " color " , COLOR )
( " zIndex " , FLOAT ) ) )
2014-01-26 22:20:21 +00:00
( " text " , makeMap ( boost : : assign : : map_list_of
2013-12-30 23:23:34 +00:00
( " pos " , NORMALIZED_PAIR )
( " size " , NORMALIZED_PAIR )
2017-07-17 03:10:29 +00:00
( " origin " , NORMALIZED_PAIR )
( " rotation " , FLOAT )
( " rotationOrigin " , NORMALIZED_PAIR )
2013-12-30 23:23:34 +00:00
( " text " , STRING )
2017-03-13 21:11:07 +00:00
( " backgroundColor " , COLOR )
2013-12-30 23:23:34 +00:00
( " fontPath " , PATH )
( " fontSize " , FLOAT )
2017-03-13 21:11:07 +00:00
( " color " , COLOR )
2014-05-03 19:51:50 +00:00
( " alignment " , STRING )
2014-05-14 22:01:40 +00:00
( " forceUppercase " , BOOLEAN )
2017-03-13 21:11:07 +00:00
( " lineSpacing " , FLOAT )
2017-04-22 14:15:16 +00:00
( " value " , STRING )
( " zIndex " , FLOAT ) ) )
2014-01-26 22:20:21 +00:00
( " textlist " , makeMap ( boost : : assign : : map_list_of
2013-12-30 23:23:34 +00:00
( " pos " , NORMALIZED_PAIR )
( " size " , NORMALIZED_PAIR )
2017-07-17 03:10:29 +00:00
( " origin " , NORMALIZED_PAIR )
2017-05-29 01:08:07 +00:00
( " selectorHeight " , FLOAT )
( " selectorOffsetY " , FLOAT )
2013-12-30 23:23:34 +00:00
( " selectorColor " , COLOR )
2017-05-29 01:08:07 +00:00
( " selectorImagePath " , PATH )
( " selectorImageTile " , BOOLEAN )
2013-12-30 23:23:34 +00:00
( " selectedColor " , COLOR )
( " primaryColor " , COLOR )
( " secondaryColor " , COLOR )
( " fontPath " , PATH )
2014-01-03 16:40:36 +00:00
( " fontSize " , FLOAT )
2014-01-10 20:24:07 +00:00
( " scrollSound " , PATH )
2014-01-22 03:10:49 +00:00
( " alignment " , STRING )
2014-05-03 19:51:50 +00:00
( " horizontalMargin " , FLOAT )
2014-05-14 23:27:22 +00:00
( " forceUppercase " , BOOLEAN )
2017-04-22 14:15:16 +00:00
( " lineSpacing " , FLOAT )
( " zIndex " , FLOAT ) ) )
2014-01-26 22:20:21 +00:00
( " container " , makeMap ( boost : : assign : : map_list_of
2014-01-03 14:26:39 +00:00
( " pos " , NORMALIZED_PAIR )
2017-04-22 14:15:16 +00:00
( " size " , NORMALIZED_PAIR )
2017-07-17 03:10:29 +00:00
( " origin " , NORMALIZED_PAIR )
2017-04-22 14:15:16 +00:00
( " zIndex " , FLOAT ) ) )
2014-01-26 22:20:21 +00:00
( " ninepatch " , makeMap ( boost : : assign : : map_list_of
2014-01-06 19:27:34 +00:00
( " pos " , NORMALIZED_PAIR )
( " size " , NORMALIZED_PAIR )
2017-04-22 14:15:16 +00:00
( " path " , PATH )
( " zIndex " , FLOAT ) ) )
2014-01-26 22:20:21 +00:00
( " datetime " , makeMap ( boost : : assign : : map_list_of
2014-01-19 22:06:13 +00:00
( " pos " , NORMALIZED_PAIR )
( " size " , NORMALIZED_PAIR )
( " color " , COLOR )
( " fontPath " , PATH )
2014-05-03 19:51:50 +00:00
( " fontSize " , FLOAT )
2017-04-22 14:15:16 +00:00
( " forceUppercase " , BOOLEAN )
( " zIndex " , FLOAT ) ) )
2014-01-26 22:20:21 +00:00
( " rating " , makeMap ( boost : : assign : : map_list_of
2014-01-19 23:37:08 +00:00
( " pos " , NORMALIZED_PAIR )
( " size " , NORMALIZED_PAIR )
2017-07-17 03:10:29 +00:00
( " origin " , NORMALIZED_PAIR )
( " rotation " , FLOAT )
( " rotationOrigin " , NORMALIZED_PAIR )
2017-06-08 23:18:27 +00:00
( " color " , COLOR )
2014-01-19 23:37:08 +00:00
( " filledPath " , PATH )
2017-04-22 14:15:16 +00:00
( " unfilledPath " , PATH )
( " zIndex " , FLOAT ) ) )
2014-01-26 22:20:21 +00:00
( " sound " , makeMap ( boost : : assign : : map_list_of
2014-05-29 20:41:47 +00:00
( " path " , PATH ) ) )
( " helpsystem " , makeMap ( boost : : assign : : map_list_of
( " pos " , NORMALIZED_PAIR )
( " textColor " , COLOR )
2014-05-29 21:27:18 +00:00
( " iconColor " , COLOR )
2014-05-29 20:41:47 +00:00
( " fontPath " , PATH )
2016-12-04 23:47:34 +00:00
( " fontSize " , FLOAT ) ) )
( " video " , makeMap ( boost : : assign : : map_list_of
( " pos " , NORMALIZED_PAIR )
( " size " , NORMALIZED_PAIR )
2017-02-24 01:42:35 +00:00
( " maxSize " , NORMALIZED_PAIR )
2016-12-04 23:47:34 +00:00
( " origin " , NORMALIZED_PAIR )
2017-07-17 03:10:29 +00:00
( " rotation " , FLOAT )
( " rotationOrigin " , NORMALIZED_PAIR )
2016-12-04 23:47:34 +00:00
( " default " , PATH )
( " delay " , FLOAT )
2017-04-22 14:15:16 +00:00
( " zIndex " , FLOAT )
2016-12-04 23:47:34 +00:00
( " showSnapshotNoVideo " , BOOLEAN )
2017-03-13 21:11:07 +00:00
( " showSnapshotDelay " , BOOLEAN ) ) )
( " carousel " , makeMap ( boost : : assign : : map_list_of
( " type " , STRING )
( " size " , NORMALIZED_PAIR )
( " pos " , NORMALIZED_PAIR )
2017-08-15 02:34:34 +00:00
( " origin " , NORMALIZED_PAIR )
2017-03-13 21:11:07 +00:00
( " color " , COLOR )
( " logoScale " , FLOAT )
2017-08-15 02:34:34 +00:00
( " logoRotation " , FLOAT )
( " logoRotationOrigin " , NORMALIZED_PAIR )
2017-03-13 21:11:07 +00:00
( " logoSize " , NORMALIZED_PAIR )
2017-08-15 02:34:34 +00:00
( " logoAlignment " , STRING )
2017-04-22 14:15:16 +00:00
( " maxLogoCount " , FLOAT )
( " zIndex " , FLOAT ) ) ) ;
2013-12-30 23:23:34 +00:00
namespace fs = boost : : filesystem ;
2014-06-09 18:12:21 +00:00
# define MINIMUM_THEME_FORMAT_VERSION 3
2017-05-14 04:07:28 +00:00
# define CURRENT_THEME_FORMAT_VERSION 5
2013-12-30 23:23:34 +00:00
2013-12-31 03:48:28 +00:00
// helper
unsigned int getHexColor ( const char * str )
{
ThemeException error ;
if ( ! str )
throw error < < " Empty color " ;
size_t len = strlen ( str ) ;
if ( len ! = 6 & & len ! = 8 )
throw error < < " Invalid color (bad length, \" " < < str < < " \" - must be 6 or 8) " ;
unsigned int val ;
std : : stringstream ss ;
ss < < str ;
ss > > std : : hex > > val ;
if ( len = = 6 )
val = ( val < < 8 ) | 0xFF ;
return val ;
}
// helper
std : : string resolvePath ( const char * in , const fs : : path & relative )
{
if ( ! in | | in [ 0 ] = = ' \0 ' )
return in ;
fs : : path relPath = relative . parent_path ( ) ;
boost : : filesystem : : path path ( in ) ;
// we use boost filesystem here instead of just string checks because
// some directories could theoretically start with ~ or .
if ( * path . begin ( ) = = " ~ " )
{
path = getHomePath ( ) + ( in + 1 ) ;
} else if ( * path . begin ( ) = = " . " )
{
path = relPath / ( in + 1 ) ;
}
return path . generic_string ( ) ;
}
2017-05-14 04:07:28 +00:00
std : : map < std : : string , std : : string > mVariables ;
2013-12-31 03:48:28 +00:00
2017-05-14 04:07:28 +00:00
std : : string & format_variables ( const boost : : xpressive : : smatch & what )
{
return mVariables [ what [ 1 ] . str ( ) ] ;
}
std : : string resolvePlaceholders ( const char * in )
{
if ( ! in | | in [ 0 ] = = ' \0 ' )
return std : : string ( in ) ;
std : : string inStr ( in ) ;
using namespace boost : : xpressive ;
sregex rex = " ${ " > > ( s1 = + ( ' . ' | _w ) ) > > ' } ' ;
std : : string output = regex_replace ( inStr , rex , format_variables ) ;
return output ;
}
2013-12-30 23:23:34 +00:00
ThemeData : : ThemeData ( )
2013-11-12 23:28:15 +00:00
{
2013-12-30 23:23:34 +00:00
mVersion = 0 ;
2013-11-12 23:28:15 +00:00
}
2017-05-14 04:07:28 +00:00
void ThemeData : : loadFile ( std : : map < std : : string , std : : string > sysDataMap , const std : : string & path )
2013-11-12 23:28:15 +00:00
{
2013-12-31 03:48:28 +00:00
mPaths . push_back ( path ) ;
2013-12-30 23:23:34 +00:00
2013-12-31 03:48:28 +00:00
ThemeException error ;
error . setFiles ( mPaths ) ;
2013-12-30 23:23:34 +00:00
if ( ! fs : : exists ( path ) )
2013-12-31 03:48:28 +00:00
throw error < < " File does not exist! " ;
2013-12-30 23:23:34 +00:00
mVersion = 0 ;
mViews . clear ( ) ;
2017-05-14 04:07:28 +00:00
mVariables . clear ( ) ;
mVariables . insert ( sysDataMap . begin ( ) , sysDataMap . end ( ) ) ;
2013-12-30 23:23:34 +00:00
pugi : : xml_document doc ;
pugi : : xml_parse_result res = doc . load_file ( path . c_str ( ) ) ;
if ( ! res )
throw error < < " XML parsing error: \n " < < res . description ( ) ;
pugi : : xml_node root = doc . child ( " theme " ) ;
if ( ! root )
throw error < < " Missing <theme> tag! " ;
// parse version
2014-06-09 18:12:21 +00:00
mVersion = root . child ( " formatVersion " ) . text ( ) . as_float ( - 404 ) ;
2013-12-30 23:23:34 +00:00
if ( mVersion = = - 404 )
2014-06-09 18:12:21 +00:00
throw error < < " <formatVersion> tag missing! \n It's either out of date or you need to add <formatVersion> " < < CURRENT_THEME_FORMAT_VERSION < < " </formatVersion> inside your <theme> tag. " ;
2013-12-30 23:23:34 +00:00
2014-06-09 18:12:21 +00:00
if ( mVersion < MINIMUM_THEME_FORMAT_VERSION )
throw error < < " Theme uses format version " < < mVersion < < " . Minimum supported version is " < < MINIMUM_THEME_FORMAT_VERSION < < " . " ;
2013-12-30 23:23:34 +00:00
2017-05-14 04:07:28 +00:00
parseVariables ( root ) ;
2013-12-31 03:48:28 +00:00
parseIncludes ( root ) ;
parseViews ( root ) ;
2017-03-10 18:49:15 +00:00
parseFeatures ( root ) ;
2013-11-12 23:28:15 +00:00
}
2013-12-31 03:48:28 +00:00
void ThemeData : : parseIncludes ( const pugi : : xml_node & root )
2013-11-12 23:28:15 +00:00
{
2013-12-30 23:23:34 +00:00
ThemeException error ;
2013-12-31 03:48:28 +00:00
error . setFiles ( mPaths ) ;
2013-12-30 23:23:34 +00:00
2013-12-31 03:48:28 +00:00
for ( pugi : : xml_node node = root . child ( " include " ) ; node ; node = node . next_sibling ( " include " ) )
2013-12-30 23:23:34 +00:00
{
2013-12-31 03:48:28 +00:00
const char * relPath = node . text ( ) . get ( ) ;
std : : string path = resolvePath ( relPath , mPaths . back ( ) ) ;
if ( ! ResourceManager : : getInstance ( ) - > fileExists ( path ) )
throw error < < " Included file \" " < < relPath < < " \" not found! (resolved to \" " < < path < < " \" ) " ;
2013-12-30 23:23:34 +00:00
2013-12-31 03:48:28 +00:00
error < < " from included file \" " < < relPath < < " \" : \n " ;
2013-12-30 23:23:34 +00:00
2013-12-31 03:48:28 +00:00
mPaths . push_back ( path ) ;
2013-12-30 23:23:34 +00:00
2013-12-31 03:48:28 +00:00
pugi : : xml_document includeDoc ;
pugi : : xml_parse_result result = includeDoc . load_file ( path . c_str ( ) ) ;
if ( ! result )
throw error < < " Error parsing file: \n " < < result . description ( ) ;
2017-10-28 20:07:31 +00:00
pugi : : xml_node theme = includeDoc . child ( " theme " ) ;
if ( ! theme )
2013-12-31 03:48:28 +00:00
throw error < < " Missing <theme> tag! " ;
2013-12-30 23:23:34 +00:00
2017-10-28 20:07:31 +00:00
parseVariables ( theme ) ;
parseIncludes ( theme ) ;
parseViews ( theme ) ;
parseFeatures ( theme ) ;
2013-12-31 03:48:28 +00:00
mPaths . pop_back ( ) ;
}
2013-11-12 23:28:15 +00:00
}
2017-03-10 18:49:15 +00:00
void ThemeData : : parseFeatures ( const pugi : : xml_node & root )
{
ThemeException error ;
error . setFiles ( mPaths ) ;
for ( pugi : : xml_node node = root . child ( " feature " ) ; node ; node = node . next_sibling ( " feature " ) )
{
if ( ! node . attribute ( " supported " ) )
throw error < < " Feature missing \" supported \" attribute! " ;
const std : : string supportedAttr = node . attribute ( " supported " ) . as_string ( ) ;
if ( std : : find ( sSupportedFeatures . begin ( ) , sSupportedFeatures . end ( ) , supportedAttr ) ! = sSupportedFeatures . end ( ) )
{
parseViews ( node ) ;
}
}
}
2017-05-14 04:07:28 +00:00
void ThemeData : : parseVariables ( const pugi : : xml_node & root )
{
ThemeException error ;
error . setFiles ( mPaths ) ;
pugi : : xml_node variables = root . child ( " variables " ) ;
if ( ! variables )
return ;
for ( pugi : : xml_node_iterator it = variables . begin ( ) ; it ! = variables . end ( ) ; + + it )
{
std : : string key = it - > name ( ) ;
std : : string val = it - > text ( ) . as_string ( ) ;
if ( ! val . empty ( ) )
mVariables . insert ( std : : pair < std : : string , std : : string > ( key , val ) ) ;
}
}
2013-12-31 03:48:28 +00:00
void ThemeData : : parseViews ( const pugi : : xml_node & root )
2013-11-12 23:28:15 +00:00
{
2013-12-30 23:23:34 +00:00
ThemeException error ;
2013-12-31 03:48:28 +00:00
error . setFiles ( mPaths ) ;
2013-11-12 23:28:15 +00:00
2013-12-31 03:48:28 +00:00
// parse views
for ( pugi : : xml_node node = root . child ( " view " ) ; node ; node = node . next_sibling ( " view " ) )
{
if ( ! node . attribute ( " name " ) )
throw error < < " View missing \" name \" attribute! " ;
2013-11-12 23:28:15 +00:00
2014-03-07 03:35:13 +00:00
const char * delim = " \t \r \n , " ;
const std : : string nameAttr = node . attribute ( " name " ) . as_string ( ) ;
size_t prevOff = nameAttr . find_first_not_of ( delim , 0 ) ;
size_t off = nameAttr . find_first_of ( delim , prevOff ) ;
std : : string viewKey ;
while ( off ! = std : : string : : npos | | prevOff ! = std : : string : : npos )
{
viewKey = nameAttr . substr ( prevOff , off - prevOff ) ;
prevOff = nameAttr . find_first_not_of ( delim , off ) ;
off = nameAttr . find_first_of ( delim , prevOff ) ;
2017-03-10 18:49:15 +00:00
if ( std : : find ( sSupportedViews . begin ( ) , sSupportedViews . end ( ) , viewKey ) ! = sSupportedViews . end ( ) )
{
ThemeView & view = mViews . insert ( std : : pair < std : : string , ThemeView > ( viewKey , ThemeView ( ) ) ) . first - > second ;
parseView ( node , view ) ;
}
2014-03-07 03:35:13 +00:00
}
2013-12-31 03:48:28 +00:00
}
2013-11-12 23:28:15 +00:00
}
2013-12-31 03:48:28 +00:00
void ThemeData : : parseView ( const pugi : : xml_node & root , ThemeView & view )
2013-11-12 23:28:15 +00:00
{
2013-12-31 03:48:28 +00:00
ThemeException error ;
error . setFiles ( mPaths ) ;
2013-11-12 23:28:15 +00:00
2013-12-31 03:48:28 +00:00
for ( pugi : : xml_node node = root . first_child ( ) ; node ; node = node . next_sibling ( ) )
2013-11-12 23:28:15 +00:00
{
2013-12-31 03:48:28 +00:00
if ( ! node . attribute ( " name " ) )
throw error < < " Element of type \" " < < node . name ( ) < < " \" missing \" name \" attribute! " ;
2013-11-12 23:28:15 +00:00
2013-12-31 03:48:28 +00:00
auto elemTypeIt = sElementMap . find ( node . name ( ) ) ;
if ( elemTypeIt = = sElementMap . end ( ) )
throw error < < " Unknown element of type \" " < < node . name ( ) < < " \" ! " ;
2014-03-07 03:35:13 +00:00
const char * delim = " \t \r \n , " ;
2014-01-25 01:25:15 +00:00
const std : : string nameAttr = node . attribute ( " name " ) . as_string ( ) ;
size_t prevOff = nameAttr . find_first_not_of ( delim , 0 ) ;
size_t off = nameAttr . find_first_of ( delim , prevOff ) ;
while ( off ! = std : : string : : npos | | prevOff ! = std : : string : : npos )
{
std : : string elemKey = nameAttr . substr ( prevOff , off - prevOff ) ;
prevOff = nameAttr . find_first_not_of ( delim , off ) ;
off = nameAttr . find_first_of ( delim , prevOff ) ;
parseElement ( node , elemTypeIt - > second ,
2014-01-26 22:20:21 +00:00
view . elements . insert ( std : : pair < std : : string , ThemeElement > ( elemKey , ThemeElement ( ) ) ) . first - > second ) ;
2014-01-25 01:25:15 +00:00
if ( std : : find ( view . orderedKeys . begin ( ) , view . orderedKeys . end ( ) , elemKey ) = = view . orderedKeys . end ( ) )
view . orderedKeys . push_back ( elemKey ) ;
}
2013-12-31 03:48:28 +00:00
}
2013-11-12 23:28:15 +00:00
}
2013-12-31 03:48:28 +00:00
void ThemeData : : parseElement ( const pugi : : xml_node & root , const std : : map < std : : string , ElementPropertyType > & typeMap , ThemeElement & element )
2013-12-30 23:23:34 +00:00
{
ThemeException error ;
2013-12-31 03:48:28 +00:00
error . setFiles ( mPaths ) ;
2013-11-12 23:28:15 +00:00
2014-01-01 05:39:22 +00:00
element . type = root . name ( ) ;
2013-12-30 23:23:34 +00:00
element . extra = root . attribute ( " extra " ) . as_bool ( false ) ;
2014-01-03 14:26:39 +00:00
2013-12-30 23:23:34 +00:00
for ( pugi : : xml_node node = root . first_child ( ) ; node ; node = node . next_sibling ( ) )
2013-11-12 23:28:15 +00:00
{
2013-12-30 23:23:34 +00:00
auto typeIt = typeMap . find ( node . name ( ) ) ;
if ( typeIt = = typeMap . end ( ) )
throw error < < " Unknown property type \" " < < node . name ( ) < < " \" (for element of type " < < root . name ( ) < < " ). " ;
2013-11-12 23:28:15 +00:00
2017-05-14 04:07:28 +00:00
std : : string str = resolvePlaceholders ( node . text ( ) . as_string ( ) ) ;
2013-12-30 23:23:34 +00:00
switch ( typeIt - > second )
2013-11-12 23:28:15 +00:00
{
2013-12-30 23:23:34 +00:00
case NORMALIZED_PAIR :
2013-11-12 23:28:15 +00:00
{
2013-12-30 23:23:34 +00:00
size_t divider = str . find ( ' ' ) ;
if ( divider = = std : : string : : npos )
2014-01-25 01:25:15 +00:00
throw error < < " invalid normalized pair (property \" " < < node . name ( ) < < " \" , value \" " < < str . c_str ( ) < < " \" ) " ;
2013-11-12 23:28:15 +00:00
2013-12-30 23:23:34 +00:00
std : : string first = str . substr ( 0 , divider ) ;
std : : string second = str . substr ( divider , std : : string : : npos ) ;
2017-10-28 20:24:35 +00:00
Vector2f val ( atof ( first . c_str ( ) ) , atof ( second . c_str ( ) ) ) ;
2013-12-30 23:23:34 +00:00
element . properties [ node . name ( ) ] = val ;
break ;
}
case STRING :
2017-05-14 04:07:28 +00:00
element . properties [ node . name ( ) ] = str ;
2013-12-30 23:23:34 +00:00
break ;
case PATH :
2013-11-12 23:28:15 +00:00
{
2017-05-14 04:07:28 +00:00
std : : string path = resolvePath ( str . c_str ( ) , mPaths . back ( ) . string ( ) ) ;
2013-12-31 03:48:28 +00:00
if ( ! ResourceManager : : getInstance ( ) - > fileExists ( path ) )
{
std : : stringstream ss ;
ss < < " Warning " < < error . msg ; // "from theme yadda yadda, included file yadda yadda
ss < < " could not find file \" " < < node . text ( ) . get ( ) < < " \" " ;
if ( node . text ( ) . get ( ) ! = path )
ss < < " (which resolved to \" " < < path < < " \" ) " ;
LOG ( LogWarning ) < < ss . str ( ) ;
}
2013-12-30 23:23:34 +00:00
element . properties [ node . name ( ) ] = path ;
break ;
}
case COLOR :
2017-05-14 04:07:28 +00:00
element . properties [ node . name ( ) ] = getHexColor ( str . c_str ( ) ) ;
2013-12-30 23:23:34 +00:00
break ;
case FLOAT :
2017-05-14 04:07:28 +00:00
{
float floatVal = static_cast < float > ( strtod ( str . c_str ( ) , 0 ) ) ;
element . properties [ node . name ( ) ] = floatVal ;
2013-12-30 23:23:34 +00:00
break ;
2017-05-14 04:07:28 +00:00
}
2013-12-30 23:23:34 +00:00
case BOOLEAN :
2017-05-14 04:07:28 +00:00
{
// only look at first char
char first = str [ 0 ] ;
// 1*, t* (true), T* (True), y* (yes), Y* (YES)
bool boolVal = ( first = = ' 1 ' | | first = = ' t ' | | first = = ' T ' | | first = = ' y ' | | first = = ' Y ' ) ;
element . properties [ node . name ( ) ] = boolVal ;
2013-12-30 23:23:34 +00:00
break ;
2017-05-14 04:07:28 +00:00
}
2013-12-30 23:23:34 +00:00
default :
2014-01-25 01:25:15 +00:00
throw error < < " Unknown ElementPropertyType for \" " < < root . attribute ( " name " ) . as_string ( ) < < " \" , property " < < node . name ( ) ;
2013-11-12 23:28:15 +00:00
}
}
}
2013-12-31 03:48:28 +00:00
2017-02-25 04:19:29 +00:00
bool ThemeData : : hasView ( const std : : string & view )
{
auto viewIt = mViews . find ( view ) ;
return ( viewIt ! = mViews . end ( ) ) ;
}
2014-01-01 05:39:22 +00:00
const ThemeData : : ThemeElement * ThemeData : : getElement ( const std : : string & view , const std : : string & element , const std : : string & expectedType ) const
{
auto viewIt = mViews . find ( view ) ;
if ( viewIt = = mViews . end ( ) )
2014-03-07 03:35:13 +00:00
return NULL ; // not found
2014-01-01 05:39:22 +00:00
auto elemIt = viewIt - > second . elements . find ( element ) ;
if ( elemIt = = viewIt - > second . elements . end ( ) ) return NULL ;
if ( elemIt - > second . type ! = expectedType & & ! expectedType . empty ( ) )
{
LOG ( LogWarning ) < < " requested mismatched theme type for [ " < < view < < " . " < < element < < " ] - expected \" "
< < expectedType < < " \" , got \" " < < elemIt - > second . type < < " \" " ;
return NULL ;
}
return & elemIt - > second ;
}
2014-01-03 16:40:36 +00:00
const std : : shared_ptr < ThemeData > & ThemeData : : getDefault ( )
2014-01-01 05:39:22 +00:00
{
2014-01-03 16:40:36 +00:00
static std : : shared_ptr < ThemeData > theme = nullptr ;
if ( theme = = nullptr )
2014-01-01 05:39:22 +00:00
{
2014-01-03 16:40:36 +00:00
theme = std : : shared_ptr < ThemeData > ( new ThemeData ( ) ) ;
2014-01-10 20:58:03 +00:00
const std : : string path = getHomePath ( ) + " /.emulationstation/es_theme_default.xml " ;
if ( fs : : exists ( path ) )
{
try
{
2017-05-14 04:07:28 +00:00
std : : map < std : : string , std : : string > emptyMap ;
theme - > loadFile ( emptyMap , path ) ;
2014-01-10 20:58:03 +00:00
} catch ( ThemeException & e )
{
LOG ( LogError ) < < e . what ( ) ;
theme = std : : shared_ptr < ThemeData > ( new ThemeData ( ) ) ; //reset to empty
}
}
2014-01-01 05:39:22 +00:00
}
2014-01-03 14:26:39 +00:00
2014-01-03 16:40:36 +00:00
return theme ;
}
2014-01-03 14:26:39 +00:00
std : : vector < GuiComponent * > ThemeData : : makeExtras ( const std : : shared_ptr < ThemeData > & theme , const std : : string & view , Window * window )
{
std : : vector < GuiComponent * > comps ;
auto viewIt = theme - > mViews . find ( view ) ;
if ( viewIt = = theme - > mViews . end ( ) )
return comps ;
2014-01-09 23:13:52 +00:00
for ( auto it = viewIt - > second . orderedKeys . begin ( ) ; it ! = viewIt - > second . orderedKeys . end ( ) ; it + + )
2014-01-03 14:26:39 +00:00
{
2014-01-09 23:13:52 +00:00
ThemeElement & elem = viewIt - > second . elements . at ( * it ) ;
if ( elem . extra )
2014-01-03 14:26:39 +00:00
{
GuiComponent * comp = NULL ;
2014-01-09 23:13:52 +00:00
const std : : string & t = elem . type ;
2014-01-03 14:26:39 +00:00
if ( t = = " image " )
comp = new ImageComponent ( window ) ;
else if ( t = = " text " )
comp = new TextComponent ( window ) ;
2017-04-22 14:15:16 +00:00
comp - > setDefaultZIndex ( 10 ) ;
2014-01-09 23:13:52 +00:00
comp - > applyTheme ( theme , view , * it , ThemeFlags : : ALL ) ;
2014-01-03 14:26:39 +00:00
comps . push_back ( comp ) ;
}
}
return comps ;
}
2014-05-01 02:12:45 +00:00
std : : map < std : : string , ThemeSet > ThemeData : : getThemeSets ( )
{
std : : map < std : : string , ThemeSet > sets ;
static const size_t pathCount = 2 ;
fs : : path paths [ pathCount ] = {
" /etc/emulationstation/themes " ,
getHomePath ( ) + " /.emulationstation/themes "
} ;
fs : : directory_iterator end ;
for ( size_t i = 0 ; i < pathCount ; i + + )
{
if ( ! fs : : is_directory ( paths [ i ] ) )
continue ;
for ( fs : : directory_iterator it ( paths [ i ] ) ; it ! = end ; + + it )
{
if ( fs : : is_directory ( * it ) )
{
ThemeSet set = { * it } ;
sets [ set . getName ( ) ] = set ;
}
}
}
return sets ;
}
fs : : path ThemeData : : getThemeFromCurrentSet ( const std : : string & system )
{
auto themeSets = ThemeData : : getThemeSets ( ) ;
if ( themeSets . empty ( ) )
{
// no theme sets available
return " " ;
}
auto set = themeSets . find ( Settings : : getInstance ( ) - > getString ( " ThemeSet " ) ) ;
if ( set = = themeSets . end ( ) )
{
// currently selected theme set is missing, so just pick the first available set
set = themeSets . begin ( ) ;
Settings : : getInstance ( ) - > setString ( " ThemeSet " , set - > first ) ;
}
return set - > second . getThemePath ( system ) ;
}