Merge pull request #2841 from OpenRCT2/theme-refactor

Theme refactor
This commit is contained in:
Ted John 2016-02-03 19:42:44 +00:00
commit 2ae6f1466e
39 changed files with 1406 additions and 951 deletions

View File

@ -51,6 +51,7 @@ if (NOT PNG_FOUND)
endif (NOT PNG_FOUND)
PKG_CHECK_MODULES(ZLIB REQUIRED zlib)
PKG_CHECK_MODULES(JANSSON REQUIRED jansson>=2.7)
# Handle creating the rct2 text and data files on OS X and Linux
# See details in src/openrct2.c:openrct2_setup_rct2_segment for how the values
@ -143,23 +144,22 @@ else (STATIC)
endif (STATIC)
if (STATIC)
SET(REQUIREDLIBS ${PNG_STATIC_LIBRARIES} ${ZLIB_STATIC_LIBRARIES})
SET(REQUIREDLIBS ${PNG_STATIC_LIBRARIES} ${JANSSON_STATIC_LIBRARIES} ${ZLIB_STATIC_LIBRARIES})
else (STATIC)
SET(REQUIREDLIBS ${PNG_LIBRARIES} ${ZLIB_LIBRARIES})
SET(REQUIREDLIBS ${PNG_LIBRARIES} ${JANSSON_LIBRARIES} ${ZLIB_LIBRARIES})
endif (STATIC)
if (NOT DISABLE_HTTP_TWITCH)
PKG_CHECK_MODULES(LIBCURL REQUIRED libcurl)
PKG_CHECK_MODULES(JANSSON REQUIRED jansson>=2.7)
if (WIN32)
# Curl depends on openssl and ws2 in mingw builds, but is not wired up in pkg-config
PKG_CHECK_MODULES(SSL REQUIRED openssl)
set(WSLIBS ws2_32)
endif (WIN32)
if (STATIC)
SET(HTTPLIBS ${LIBCURL_STATIC_LIBRARIES} ${JANSSON_STATIC_LIBRARIES} ${SSL_STATIC_LIBRARIES} ${WSLIBS})
SET(HTTPLIBS ${LIBCURL_STATIC_LIBRARIES} ${SSL_STATIC_LIBRARIES} ${WSLIBS})
else (STATIC)
SET(HTTPLIBS ${LIBCURL_LIBRARIES} ${JANSSON_LIBRARIES} ${SSL_LIBRARIES} ${WSLIBS})
SET(HTTPLIBS ${LIBCURL_LIBRARIES} ${SSL_LIBRARIES} ${WSLIBS})
endif (STATIC)
endif (NOT DISABLE_HTTP_TWITCH)

View File

@ -15,6 +15,8 @@
D43532601C34730200BA219B /* libpng.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D435325E1C3472E500BA219B /* libpng.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
D46105CE1C38828D00DB1EE3 /* scenario_sources.c in Sources */ = {isa = PBXBuildFile; fileRef = D46105CD1C38828D00DB1EE3 /* scenario_sources.c */; };
D47304D51C4FF8250015C0EA /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = D47304D41C4FF8250015C0EA /* libz.tbd */; };
D4A3511E1C6067C400CBCBA4 /* Json.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D4A3511C1C6067C400CBCBA4 /* Json.cpp */; };
D4A351211C60680300CBCBA4 /* Theme.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D4A351201C60680300CBCBA4 /* Theme.cpp */; };
D4ABAB061C2F812B0080CAD9 /* news_options.c in Sources */ = {isa = PBXBuildFile; fileRef = D4ABAB051C2F812B0080CAD9 /* news_options.c */; };
D4B63B8F1C43025600367A37 /* CommandLine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D4B63B8A1C43025600367A37 /* CommandLine.cpp */; };
D4B63B901C43025600367A37 /* RootCommands.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D4B63B8C1C43025600367A37 /* RootCommands.cpp */; };
@ -54,7 +56,6 @@
D4EC47F81C26342F0024B507 /* graph.c in Sources */ = {isa = PBXBuildFile; fileRef = D4EC470F1C26342F0024B507 /* graph.c */; };
D4EC47F91C26342F0024B507 /* keyboard_shortcut.c in Sources */ = {isa = PBXBuildFile; fileRef = D4EC47111C26342F0024B507 /* keyboard_shortcut.c */; };
D4EC47FA1C26342F0024B507 /* screenshot.c in Sources */ = {isa = PBXBuildFile; fileRef = D4EC47131C26342F0024B507 /* screenshot.c */; };
D4EC47FB1C26342F0024B507 /* themes.c in Sources */ = {isa = PBXBuildFile; fileRef = D4EC47151C26342F0024B507 /* themes.c */; };
D4EC47FC1C26342F0024B507 /* title_sequences.c in Sources */ = {isa = PBXBuildFile; fileRef = D4EC47171C26342F0024B507 /* title_sequences.c */; };
D4EC47FD1C26342F0024B507 /* viewport.c in Sources */ = {isa = PBXBuildFile; fileRef = D4EC47191C26342F0024B507 /* viewport.c */; };
D4EC47FE1C26342F0024B507 /* viewport_interaction.c in Sources */ = {isa = PBXBuildFile; fileRef = D4EC471B1C26342F0024B507 /* viewport_interaction.c */; };
@ -229,6 +230,12 @@
D47304D41C4FF8250015C0EA /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
D4895D321C23EFDD000CD788 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = distribution/osx/Info.plist; sourceTree = SOURCE_ROOT; };
D497D0781C20FD52002BF46A /* OpenRCT2.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OpenRCT2.app; sourceTree = BUILT_PRODUCTS_DIR; };
D4A3511A1C6067B000CBCBA4 /* Diagnostics.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Diagnostics.hpp; sourceTree = "<group>"; };
D4A3511B1C6067B900CBCBA4 /* Guard.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Guard.hpp; sourceTree = "<group>"; };
D4A3511C1C6067C400CBCBA4 /* Json.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Json.cpp; sourceTree = "<group>"; };
D4A3511D1C6067C400CBCBA4 /* Json.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Json.hpp; sourceTree = "<group>"; };
D4A3511F1C6067E500CBCBA4 /* List.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = List.hpp; sourceTree = "<group>"; };
D4A351201C60680300CBCBA4 /* Theme.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Theme.cpp; sourceTree = "<group>"; };
D4ABAB051C2F812B0080CAD9 /* news_options.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = news_options.c; sourceTree = "<group>"; };
D4B63B8A1C43025600367A37 /* CommandLine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CommandLine.cpp; sourceTree = "<group>"; };
D4B63B8B1C43025600367A37 /* CommandLine.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CommandLine.hpp; sourceTree = "<group>"; };
@ -304,7 +311,6 @@
D4EC47121C26342F0024B507 /* keyboard_shortcut.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = keyboard_shortcut.h; sourceTree = "<group>"; };
D4EC47131C26342F0024B507 /* screenshot.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = screenshot.c; sourceTree = "<group>"; };
D4EC47141C26342F0024B507 /* screenshot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = screenshot.h; sourceTree = "<group>"; };
D4EC47151C26342F0024B507 /* themes.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = themes.c; sourceTree = "<group>"; };
D4EC47161C26342F0024B507 /* themes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = themes.h; sourceTree = "<group>"; };
D4EC47171C26342F0024B507 /* title_sequences.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = title_sequences.c; sourceTree = "<group>"; };
D4EC47181C26342F0024B507 /* title_sequences.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = title_sequences.h; sourceTree = "<group>"; };
@ -728,10 +734,15 @@
children = (
D4B63B931C43028200367A37 /* Console.cpp */,
D4B63B941C43028200367A37 /* Console.hpp */,
D4A3511A1C6067B000CBCBA4 /* Diagnostics.hpp */,
D4EC46E61C26342F0024B507 /* Exception.hpp */,
D4EC46E71C26342F0024B507 /* FileStream.hpp */,
D4A3511B1C6067B900CBCBA4 /* Guard.hpp */,
D4EC46E81C26342F0024B507 /* IDisposable.hpp */,
D4EC46E91C26342F0024B507 /* IStream.hpp */,
D4A3511C1C6067C400CBCBA4 /* Json.cpp */,
D4A3511D1C6067C400CBCBA4 /* Json.hpp */,
D4A3511F1C6067E500CBCBA4 /* List.hpp */,
D4EC46EA1C26342F0024B507 /* Math.hpp */,
D4EC46EB1C26342F0024B507 /* Memory.hpp */,
D4D35E2A1C45BD9B00AAFCB4 /* Path.cpp */,
@ -781,7 +792,7 @@
D4EC47121C26342F0024B507 /* keyboard_shortcut.h */,
D4EC47131C26342F0024B507 /* screenshot.c */,
D4EC47141C26342F0024B507 /* screenshot.h */,
D4EC47151C26342F0024B507 /* themes.c */,
D4A351201C60680300CBCBA4 /* Theme.cpp */,
D4EC47161C26342F0024B507 /* themes.h */,
D4EC47171C26342F0024B507 /* title_sequences.c */,
D4EC47181C26342F0024B507 /* title_sequences.h */,
@ -1351,6 +1362,7 @@
D4EC48401C26342F0024B507 /* guest_list.c in Sources */,
D4EC482F1C26342F0024B507 /* banner.c in Sources */,
D4EC47E91C26342F0024B507 /* font.c in Sources */,
D4A3511E1C6067C400CBCBA4 /* Json.cpp in Sources */,
D4EC48021C26342F0024B507 /* convert.c in Sources */,
D4EC48481C26342F0024B507 /* mapgen.c in Sources */,
D4EC48441C26342F0024B507 /* loadsave.c in Sources */,
@ -1368,6 +1380,7 @@
D4EC481A1C26342F0024B507 /* posix.c in Sources */,
D4D35E2C1C45BD9B00AAFCB4 /* Path.cpp in Sources */,
D4B63B981C43028F00367A37 /* String.cpp in Sources */,
D4A351211C60680300CBCBA4 /* Theme.cpp in Sources */,
D4EC47E31C26342F0024B507 /* cmdline_sprite.c in Sources */,
D4EC48611C26342F0024B507 /* text_input.c in Sources */,
D4EC48431C26342F0024B507 /* land_rights.c in Sources */,
@ -1481,7 +1494,6 @@
D4EC480D1C26342F0024B507 /* marketing.c in Sources */,
D4EC48721C26342F0024B507 /* balloon.c in Sources */,
D4EC48571C26342F0024B507 /* save_prompt.c in Sources */,
D4EC47FB1C26342F0024B507 /* themes.c in Sources */,
D4EC48701C26342F0024B507 /* viewport.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -1515,9 +1527,7 @@
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
);
GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=1";
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES;

View File

@ -4039,6 +4039,8 @@ STR_5732 :Anisotropic
STR_5733 :Use NN scaling at integer scales
# tooltip for tab in options window
STR_5734 :{SMALLFONT}{BLACK}Rendering
STR_5735 :Network Status
STR_5736 :Player
#############
# Scenarios #

View File

@ -30,6 +30,7 @@
<ClCompile Include="src\cmdline_sprite.c" />
<ClCompile Include="src\config.c" />
<ClCompile Include="src\core\Console.cpp" />
<ClCompile Include="src\core\Json.cpp" />
<ClCompile Include="src\core\Path.cpp" />
<ClCompile Include="src\core\Stopwatch.cpp" />
<ClCompile Include="src\core\String.cpp" />
@ -51,7 +52,7 @@
<ClCompile Include="src\input.c" />
<ClCompile Include="src\interface\chat.c" />
<ClCompile Include="src\interface\colour.c" />
<ClCompile Include="src\interface\themes.c" />
<ClCompile Include="src\interface\Theme.cpp" />
<ClCompile Include="src\interface\console.c" />
<ClCompile Include="src\interface\graph.c" />
<ClCompile Include="src\interface\keyboard_shortcut.c" />
@ -108,6 +109,7 @@
<ClCompile Include="src\windows\player.c" />
<ClCompile Include="src\windows\server_list.c" />
<ClCompile Include="src\windows\server_start.c" />
<ClCompile Include="src\windows\themes.c" />
<ClCompile Include="src\windows\title_command_editor.c" />
<ClCompile Include="src\windows\title_editor.c" />
<ClCompile Include="src\windows\maze_construction.c" />
@ -177,7 +179,6 @@
<ClCompile Include="src\windows\track_place.c" />
<ClCompile Include="src\windows\viewport.c" />
<ClCompile Include="src\windows\water.c" />
<ClCompile Include="src\windows\themes.c" />
<ClCompile Include="src\world\banner.c" />
<ClCompile Include="src\world\climate.c" />
<ClCompile Include="src\world\footpath.c" />
@ -199,10 +200,14 @@
<ClInclude Include="src\common.h" />
<ClInclude Include="src\config.h" />
<ClInclude Include="src\core\Console.hpp" />
<ClInclude Include="src\core\Diagnostics.hpp" />
<ClInclude Include="src\core\Exception.hpp" />
<ClInclude Include="src\core\FileStream.hpp" />
<ClInclude Include="src\core\Guard.hpp" />
<ClInclude Include="src\core\IDisposable.hpp" />
<ClInclude Include="src\core\IStream.hpp" />
<ClInclude Include="src\core\Json.hpp" />
<ClInclude Include="src\core\List.hpp" />
<ClInclude Include="src\core\Math.hpp" />
<ClInclude Include="src\core\Memory.hpp" />
<ClInclude Include="src\core\Path.hpp" />
@ -380,4 +385,4 @@
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
</Project>
</Project>

View File

@ -453,12 +453,6 @@
<ClCompile Include="src\network\http.cpp">
<Filter>Source\Network</Filter>
</ClCompile>
<ClCompile Include="src\interface\themes.c">
<Filter>Source\Interface</Filter>
</ClCompile>
<ClCompile Include="src\windows\themes.c">
<Filter>Source\Windows</Filter>
</ClCompile>
<ClCompile Include="src\windows\tile_inspector.c">
<Filter>Source\Windows</Filter>
</ClCompile>
@ -582,6 +576,11 @@
<ClCompile Include="src\windows\multiplayer.c">
<Filter>Source\Windows</Filter>
</ClCompile>
<ClCompile Include="src\interface\Theme.cpp">
<Filter>Source\Interface</Filter>
</ClCompile>
<ClCompile Include="src\core\Json.cpp" />
<ClCompile Include="src\windows\themes.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\management\award.h">
@ -884,5 +883,9 @@
<ClInclude Include="src\core\Path.hpp">
<Filter>Source\Core</Filter>
</ClInclude>
<ClInclude Include="src\core\List.hpp" />
<ClInclude Include="src\core\Guard.hpp" />
<ClInclude Include="src\core\Diagnostics.hpp" />
<ClInclude Include="src\core\Json.hpp" />
</ItemGroup>
</Project>

View File

@ -19,6 +19,7 @@
*****************************************************************************/
extern "C" {
#include "../addresses.h"
#include "../config.h"
#include "../platform/platform.h"
#include "../localisation/localisation.h"

View File

@ -299,7 +299,6 @@ sound_configuration gConfigSound;
twitch_configuration gConfigTwitch;
network_configuration gConfigNetwork;
notification_configuration gConfigNotifications;
themes_configuration gConfigThemes;
title_sequences_configuration gConfigTitleSequences;
static bool config_open(const utf8string path);
@ -1044,396 +1043,6 @@ bool config_shortcut_keys_save()
#pragma endregion
#pragma region Themes
typedef struct {
size_t offset;
const_utf8string property_name;
uint8 type;
value_union default_value;
config_enum_definition *enum_definitions;
} theme_property_definition;
typedef struct {
size_t offset;
const_utf8string section_name;
theme_property_definition *property_definitions;
int property_definitions_count;
} theme_section_definition;
theme_property_definition _themeWindowDefinitions[] = {
{ offsetof(theme_window, colours[0]), "colour_0", CONFIG_VALUE_TYPE_UINT8, 0, NULL },
{ offsetof(theme_window, colours[1]), "colour_1", CONFIG_VALUE_TYPE_UINT8, 0, NULL },
{ offsetof(theme_window, colours[2]), "colour_2", CONFIG_VALUE_TYPE_UINT8, 0, NULL },
{ offsetof(theme_window, colours[3]), "colour_3", CONFIG_VALUE_TYPE_UINT8, 0, NULL },
{ offsetof(theme_window, colours[4]), "colour_4", CONFIG_VALUE_TYPE_UINT8, 0, NULL },
{ offsetof(theme_window, colours[5]), "colour_5", CONFIG_VALUE_TYPE_UINT8, 0, NULL },
};
theme_property_definition _themeFeaturesDefinitions[] = {
{ offsetof(theme_features, rct1_ride_lights), "rct1_ride_lights", CONFIG_VALUE_TYPE_BOOLEAN, false, NULL },
{ offsetof(theme_features, rct1_park_lights), "rct1_park_lights", CONFIG_VALUE_TYPE_BOOLEAN, false, NULL },
{ offsetof(theme_features, rct1_scenario_font), "rct1_scenario_font", CONFIG_VALUE_TYPE_BOOLEAN, false, NULL },
};
theme_section_definition _themeSectionDefinitions[] = {
// Special definition for theme windows
{ 0, "", _themeWindowDefinitions, countof(_themeWindowDefinitions) },
{ offsetof(theme_preset, features), "features", _themeFeaturesDefinitions, countof(_themeFeaturesDefinitions) },
};
static bool themes_open(const_utf8string path);
static bool themes_save(const_utf8string path, int preset);
static void themes_read_properties(theme_preset *theme, theme_section_definition **currentSection, utf8string line);
static void themes_set_property(theme_preset *theme, const theme_section_definition *section, const theme_property_definition *property, utf8string value, int valueSize);
static theme_section_definition* themes_get_section_def(utf8string name, int size);
static theme_property_definition *themes_get_property_def(theme_section_definition *section, utf8string name, int size);
void themes_set_default()
{
utf8 path[MAX_PATH];
platform_get_user_directory(path, "themes");
platform_ensure_directory_exists(path);
gConfigThemes.num_presets = 2;
gConfigThemes.presets = malloc(sizeof(theme_preset) * gConfigThemes.num_presets);
// Set RCT2 theme
safe_strcpy(gConfigThemes.presets[0].name, language_get_string(2741), THEME_PRESET_NAME_SIZE);
gConfigThemes.presets[0].windows = malloc(sizeof(theme_window) * gNumThemeWindows);
// Define the defaults for RCT2 here
gConfigThemes.presets[0].features.rct1_ride_lights = false;
gConfigThemes.presets[0].features.rct1_park_lights = false;
gConfigThemes.presets[0].features.rct1_scenario_font = false;
for (int i = 0; i < (int)gNumThemeWindows; i++) {
gConfigThemes.presets[0].windows[i] = gThemeWindowDefinitions[i].window;
}
// Set RCT1 theme
safe_strcpy(gConfigThemes.presets[1].name, language_get_string(2740), THEME_PRESET_NAME_SIZE);
gConfigThemes.presets[1].windows = malloc(sizeof(theme_window) * gNumThemeWindows);
// Define the defaults for RCT1 here
gConfigThemes.presets[1].features.rct1_ride_lights = true;
gConfigThemes.presets[1].features.rct1_park_lights = true;
gConfigThemes.presets[1].features.rct1_scenario_font = true;
for (int i = 0; i < (int)gNumThemeWindows; i++) {
uint8 changed_colour = 0xFF;
for (int k = 0; gThemeWindowsRCT1[k].classification != 0xFF; k++) {
if (gThemeWindowsRCT1[k].classification == gThemeWindowDefinitions[i].classification) {
changed_colour = (uint8)k;
break;
}
}
gConfigThemes.presets[1].windows[i] = (changed_colour != 0xFF ? gThemeWindowsRCT1[changed_colour].window : gThemeWindowDefinitions[i].window);
}
}
void themes_load_presets()
{
utf8 path[MAX_PATH];
file_info file;
int fileEnumHandle, i;
platform_get_user_directory(path, "themes");
strcat(path, "*.ini");
fileEnumHandle = platform_enumerate_files_begin(path);
while (platform_enumerate_files_next(fileEnumHandle, &file)) {
platform_get_user_directory(path, "themes");
strcat(path, file.path);
themes_open(path);
}
platform_enumerate_files_end(fileEnumHandle);
if (strcmp(gConfigInterface.current_theme_preset, "*RCT2") == 0) {
theme_change_preset(0);
}
else if (strcmp(gConfigInterface.current_theme_preset, "*RCT1") == 0) {
theme_change_preset(1);
}
else {
for (i = 2; i < gConfigThemes.num_presets; i++) {
if (strcmp(gConfigInterface.current_theme_preset, gConfigThemes.presets[i].name) == 0) {
theme_change_preset(i);
break;
}
}
if (i == gConfigThemes.num_presets) {
theme_change_preset(0);
}
}
}
bool themes_save_preset(int preset)
{
utf8 path[MAX_PATH];
platform_get_user_directory(path, "themes");
strcat(path, gConfigThemes.presets[preset].name);
strcat(path, ".ini");
if (themes_save(path, preset)) {
return true;
}
return false;
}
bool themes_open(const_utf8string path)
{
SDL_RWops *file;
utf8string lineBuffer;
size_t lineBufferCapacity;
size_t lineLength;
int c, preset;
theme_section_definition *currentSection;
file = SDL_RWFromFile(path, "rb");
if (file == NULL)
return false;
// Check if the colour scheme is already loaded
// No nead to read the first two presets as they're hardcoded in
for (preset = 2; preset < gConfigThemes.num_presets; preset++) {
if (strcmp(path, gConfigThemes.presets[preset].name) == 0) {
break;
}
}
// Otherwise allocate one
if (preset == gConfigThemes.num_presets) {
gConfigThemes.num_presets++;
gConfigThemes.presets = realloc(gConfigThemes.presets, sizeof(theme_preset) * gConfigThemes.num_presets);
safe_strcpy(gConfigThemes.presets[preset].name, path_get_filename(path), THEME_PRESET_NAME_SIZE);
path_remove_extension(gConfigThemes.presets[preset].name);
gConfigThemes.presets[preset].windows = malloc(sizeof(theme_window) * gNumThemeWindows);
gConfigThemes.presets[preset].features.rct1_ride_lights = false;
gConfigThemes.presets[preset].features.rct1_park_lights = false;
gConfigThemes.presets[preset].features.rct1_scenario_font = false;
for (int i = 0; i < (int)gNumThemeWindows; i++) {
gConfigThemes.presets[preset].windows[i] = gThemeWindowDefinitions[i].window;
}
}
currentSection = NULL;
lineBufferCapacity = 64;
lineBuffer = malloc(lineBufferCapacity);
lineLength = 0;
// Skim UTF-8 byte order mark
SDL_RWread(file, lineBuffer, 3, 1);
if (!(lineBuffer[0] == (utf8)0xEF && lineBuffer[1] == (utf8)0xBB && lineBuffer[2] == (utf8)0xBF))
SDL_RWseek(file, 0, SEEK_SET);
while ((c = rwopsreadc(file)) != EOF) {
if (c == '\n' || c == '\r') {
lineBuffer[lineLength++] = 0;
themes_read_properties(&gConfigThemes.presets[preset], &currentSection, (utf8string)lineBuffer);
lineLength = 0;
}
else {
lineBuffer[lineLength++] = c;
}
if (lineLength >= lineBufferCapacity) {
lineBufferCapacity *= 2;
lineBuffer = realloc(lineBuffer, lineBufferCapacity);
}
}
if (lineLength > 0) {
lineBuffer[lineLength++] = 0;
themes_read_properties(&gConfigThemes.presets[preset], &currentSection, lineBuffer);
}
free(lineBuffer);
SDL_RWclose(file);
return true;
}
static bool themes_save(const_utf8string path, int preset)
{
SDL_RWops *file;
int i, j;
value_union *value;
file = SDL_RWFromFile(path, "wb");
if (file == NULL) {
log_error("Unable to write to theme file.");
return false;
}
// Skip the window definition, we'll do that after
for (i = 1; i < countof(_themeSectionDefinitions); i++) {
theme_section_definition *section = &_themeSectionDefinitions[i];
rwopswritec(file, '[');
rwopswritestr(file, section->section_name);
rwopswritec(file, ']');
rwopswritenewline(file);
for (j = 0; j < section->property_definitions_count; j++) {
theme_property_definition *property = &section->property_definitions[j];
rwopswritestr(file, property->property_name);
rwopswritestr(file, " = ");
value = (value_union*)((size_t)&gConfigThemes.presets[preset] + (size_t)section->offset + (size_t)property->offset);
if (property->enum_definitions != NULL)
config_write_enum(file, property->type, value, property->enum_definitions);
else
config_save_property_value(file, property->type, value);
rwopswritenewline(file);
}
rwopswritenewline(file);
}
for (i = 0; i < (int)gNumThemeWindows; i++) {
theme_section_definition *section = &_themeSectionDefinitions[0];
rwopswritec(file, '[');
rwopswritestr(file, gThemeWindowDefinitions[i].section_name);
rwopswritec(file, ']');
rwopswritenewline(file);
for (j = 0; j < section->property_definitions_count; j++) {
theme_property_definition *property = &section->property_definitions[j];
rwopswritestr(file, property->property_name);
rwopswritestr(file, " = ");
value = (value_union*)((size_t)gConfigThemes.presets[preset].windows + (size_t)(sizeof(theme_window) * i) + (size_t)property->offset);
if (property->enum_definitions != NULL)
config_write_enum(file, property->type, value, property->enum_definitions);
else
config_save_property_value(file, property->type, value);
rwopswritenewline(file);
}
}
SDL_RWclose(file);
return true;
}
static void themes_read_properties(theme_preset *theme, theme_section_definition **currentSection, utf8string line)
{
utf8 *ch = (utf8*)line;
utf8_skip_whitespace(&ch);
if (*ch == '[') {
const utf8 *sectionName;
int sectionNameSize;
if (config_get_section(ch, &sectionName, &sectionNameSize))
*currentSection = themes_get_section_def((utf8string)sectionName, sectionNameSize);
} else {
if (*currentSection != NULL) {
utf8 *propertyName, *value;
int propertyNameSize, valueSize;
if (config_get_property_name_value(ch, &propertyName, &propertyNameSize, &value, &valueSize)) {
theme_property_definition *property;
property = themes_get_property_def(*currentSection, propertyName, propertyNameSize);
if (property != NULL)
themes_set_property(theme, *currentSection, property, value, valueSize);
}
}
}
}
static void themes_set_property(theme_preset *theme, const theme_section_definition *section, const theme_property_definition *property, utf8string value, int valueSize)
{
value_union *destValue = (value_union*)((size_t)theme + (size_t)section->offset + (size_t)property->offset);
// Get getting the address from the windows pointer instead
if (section == &_themeSectionDefinitions[0])
destValue = (value_union*)((size_t)theme->windows + (size_t)section->offset + (size_t)property->offset);
if (property->enum_definitions != NULL)
if (config_read_enum(destValue, _configValueTypeSize[property->type], value, valueSize, property->enum_definitions))
return;
switch (property->type) {
case CONFIG_VALUE_TYPE_BOOLEAN:
if (_strnicmp(value, "false", valueSize) == 0) destValue->value_boolean = false;
else if (_strnicmp(value, "true", valueSize) == 0) destValue->value_boolean = true;
else destValue->value_boolean = strtol(value, NULL, 0) != 0;
break;
case CONFIG_VALUE_TYPE_UINT8:
destValue->value_uint8 = (uint8)strtol(value, NULL, 0);
break;
case CONFIG_VALUE_TYPE_UINT16:
destValue->value_uint16 = (uint16)strtol(value, NULL, 0);
break;
case CONFIG_VALUE_TYPE_UINT32:
destValue->value_uint32 = (uint32)strtol(value, NULL, 0);
break;
case CONFIG_VALUE_TYPE_SINT8:
destValue->value_sint8 = (sint8)strtol(value, NULL, 0);
break;
case CONFIG_VALUE_TYPE_SINT16:
destValue->value_sint16 = (sint16)strtol(value, NULL, 0);
break;
case CONFIG_VALUE_TYPE_SINT32:
destValue->value_sint32 = (sint32)strtol(value, NULL, 0);
break;
case CONFIG_VALUE_TYPE_FLOAT:
destValue->value_float = strtof(value, NULL);
break;
case CONFIG_VALUE_TYPE_DOUBLE:
destValue->value_double = strtod(value, NULL);
break;
case CONFIG_VALUE_TYPE_STRING:
SafeFree(destValue->value_string);
destValue->value_string = malloc(valueSize + 1);
memcpy(destValue->value_string, value, valueSize);
destValue->value_string[valueSize] = 0;
break;
}
}
static theme_section_definition* themes_get_section_def(utf8string name, int size)
{
int i;
// Skip the special definition
for (i = 1; i < countof(_themeSectionDefinitions); i++) {
const_utf8string sectionName = _themeSectionDefinitions[i].section_name;
if (sectionName[size] == 0 && _strnicmp(sectionName, name, size) == 0)
return &_themeSectionDefinitions[i];
}
// Check for window definitions
for (i = 0; i < (int)gNumThemeWindows; i++) {
const_utf8string sectionName = gThemeWindowDefinitions[i].section_name;
if (sectionName[size] == 0 && _strnicmp(sectionName, name, size) == 0) {
_themeSectionDefinitions[0].offset = (size_t)(sizeof(theme_window) * i);
return &_themeSectionDefinitions[0];
}
}
return NULL;
}
static theme_property_definition *themes_get_property_def(theme_section_definition *section, utf8string name, int size)
{
int i;
for (i = 0; i < section->property_definitions_count; i++) {
const_utf8string propertyName = section->property_definitions[i].property_name;
if (propertyName[size] == 0 && _strnicmp(propertyName, name, size) == 0)
return &section->property_definitions[i];
}
return NULL;
}
#pragma endregion
#pragma region Title Sequences
static void title_sequence_open(const char *path, const char *customName);

View File

@ -253,13 +253,6 @@ typedef struct {
bool guest_died;
} notification_configuration;
typedef struct theme_window {
uint8 colours[6];
// Define any other settings for all windows here
} theme_window;
// Define structures for any other settings here
typedef struct {
uint8 rct1_ride_lights;
@ -267,22 +260,6 @@ typedef struct {
uint8 rct1_scenario_font;
} theme_features;
#define THEME_PRESET_NAME_SIZE 256
typedef struct theme_preset {
char name[THEME_PRESET_NAME_SIZE];
theme_window *windows;
// Add structures for any other settings here
theme_features features;
} theme_preset;
typedef struct {
theme_preset *presets;
uint16 num_presets;
} themes_configuration;
#define TITLE_SEQUENCE_MAX_SAVE_LENGTH 51
typedef struct {
@ -327,7 +304,6 @@ extern sound_configuration gConfigSound;
extern twitch_configuration gConfigTwitch;
extern network_configuration gConfigNetwork;
extern notification_configuration gConfigNotifications;
extern themes_configuration gConfigThemes;
extern title_sequences_configuration gConfigTitleSequences;
extern uint16 gShortcutKeys[SHORTCUT_COUNT];
@ -346,10 +322,6 @@ bool config_shortcut_keys_save();
bool config_find_or_browse_install_directory();
void themes_set_default();
void themes_load_presets();
bool themes_save_preset(int preset);
void title_sequences_set_default();
void title_sequences_load_presets();
void title_sequence_save_preset_script(int preset);

22
src/core/Diagnostics.hpp Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#if _WIN32
#include <debugapi.h>
#endif
/**
* Utility methods for asserting and logging.
*/
namespace Debug
{
void Break()
{
#if DEBUG
#if _WIN32
if (IsDebuggerPresent()) {
DebugBreak();
}
#endif
#endif
}
}

40
src/core/Guard.hpp Normal file
View File

@ -0,0 +1,40 @@
#pragma once
#include <cassert>
#include <stdio.h>
#include "Console.hpp"
#include "Diagnostics.hpp"
/**
* Utility methods for asserting function parameters.
*/
namespace Guard
{
void Assert(bool expression, const char * message = nullptr)
{
if (expression) return;
if (message != nullptr)
{
Console::Error::WriteLine(message);
}
#if DEBUG
Debug::Break();
#endif
assert(false);
}
template<typename T>
void ArgumentNotNull(T * argument, const char * message = nullptr)
{
Assert(argument != nullptr, message);
}
template<typename T>
void ArgumentInRange(T argument, T min, T max, const char * message = nullptr)
{
Assert(argument >= min && argument <= max, message);
}
};

45
src/core/Json.cpp Normal file
View File

@ -0,0 +1,45 @@
#include "FileStream.hpp"
#include "Json.hpp"
#include "Memory.hpp"
#include "String.hpp"
namespace Json
{
json_t * ReadFromFile(const utf8 * path, size_t maxSize)
{
json_t * json = nullptr;
auto fs = FileStream(path, FILE_MODE_OPEN);
size_t fileLength = (size_t)fs.GetLength();
if (fileLength > maxSize)
{
throw IOException("Json file too large.");
}
utf8 * fileData = Memory::Allocate<utf8>(fileLength + 1);
fs.Read(fileData, fileLength);
fileData[fileLength] = '\0';
json_error_t jsonLoadError;
json = json_loads(fileData, 0, &jsonLoadError);
Memory::Free(fileData);
if (json == nullptr)
{
throw JsonException(&jsonLoadError);
}
return json;
}
void WriteToFile(const utf8 * path, const json_t * json, size_t flags)
{
// Serialise JSON
const char * jsonOutput = json_dumps(json, flags);
// Write to file
auto fs = FileStream(path, FILE_MODE_WRITE);
size_t jsonOutputSize = String::SizeOf(jsonOutput);
fs.Write(jsonOutput, jsonOutputSize);
}
}

29
src/core/Json.hpp Normal file
View File

@ -0,0 +1,29 @@
#pragma once
#include <jansson.h>
#include "../common.h"
#include "Exception.hpp"
namespace Json
{
// Don't try to load JSON files that exceed 64 MiB
constexpr uint64 MAX_JSON_SIZE = 64 * 1024 * 1024;
json_t * ReadFromFile(const utf8 * path, size_t maxSize = MAX_JSON_SIZE);
void WriteToFile(const utf8 * path, const json_t * json, size_t flags = 0);
}
class JsonException : public Exception
{
private:
json_error_t _jsonError;
public:
JsonException(const char * message) : Exception(message) { }
JsonException(const json_error_t * jsonError) : JsonException(jsonError->text)
{
_jsonError = *jsonError;
}
};

90
src/core/List.hpp Normal file
View File

@ -0,0 +1,90 @@
#pragma once
#include <vector>
#include "../common.h"
#include "Guard.hpp"
#include "Memory.hpp"
/**
* A container that stores elements in contiguous memory. Automatically reallocates memory when required. Equivalent to
* std::vector.
*/
template<typename T>
class List : public std::vector<T>
{
public:
typedef typename std::vector<T>::const_reference const_reference;
typedef typename std::vector<T>::reference reference;
size_t GetCapacity() const { return this->capacity(); }
size_t GetCount() const { return this->size(); }
const T * GetItems() const { return this->data(); }
List() : std::vector<T>() { }
List(size_t capacity) : std::vector<T>(capacity) { }
List(const T * items, size_t count) : std::vector<T>(items, items + count) { }
void EnsureCapacity(size_t capacity)
{
this->reserve(capacity);
}
void ShrinkToLength()
{
this->shrink_to_fit();
}
void Clear()
{
this->clear();
}
void Add(T item)
{
this->push_back(item);
}
void Insert(T item, size_t index)
{
Guard::ArgumentInRange(index, (size_t)0, this->size());
this->insert(this->begin() + index, item);
}
bool Remove(T item)
{
for (size_t i = 0; i < this->size(); i++)
{
if (*this[i] == item)
{
RemoveAt(i);
return true;
}
}
return false;
}
void RemoveAt(size_t index)
{
Guard::ArgumentInRange(index, (size_t)0, this->size() - 1);
this->erase(this->begin() + index);
}
const T * ToArray() const
{
return Memory::DuplicateArray(this->data(), this->size());
}
const_reference operator[](size_t index) const
{
Guard::ArgumentInRange(index, (size_t)0, this->size() - 1);
return std::vector<T>::operator[](index);
}
reference operator[](size_t index)
{
Guard::ArgumentInRange(index, (size_t)0, this->size() - 1);
return std::vector<T>::operator[](index);
}
};

View File

@ -31,7 +31,7 @@ namespace Memory
template<typename T>
T * Reallocate(T * ptr, size_t size)
{
if (ptr == NULL)
if (ptr == nullptr)
{
return (T*)malloc(size);
}
@ -44,7 +44,7 @@ namespace Memory
template<typename T>
T * ReallocateArray(T * ptr, size_t count)
{
if (ptr == NULL)
if (ptr == nullptr)
{
return (T*)malloc(count * sizeof(T));
}
@ -67,13 +67,6 @@ namespace Memory
return (T*)memcpy((void*)dst, (const void*)src, size);
}
template<typename T>
T * CopyArray(T *dst, const T * src, size_t count)
{
if (count == 0) return (T*)dst;
return (T*)memcpy((void*)dst, (const void*)src, count * sizeof(T));
}
template<typename T>
T * Duplicate(const T * src, size_t size)
{
@ -81,16 +74,39 @@ namespace Memory
return Copy(result, src, size);
}
template<typename T>
T * DuplicateArray(const T * src, size_t count)
{
T *result = AllocateArray<T>(count);
return CopyArray(result, src, count);
}
template<typename T>
T * Set(T * dst, uint8 value, size_t size)
{
return (T*)memset((void*)dst, (int)value, size);
}
template<typename T>
T * CopyArray(T * dst, const T * src, size_t count)
{
// Use a loop so that copy constructors are called
// compiler should optimise to memcpy if possible
T * result = dst;
for (; count > 0; count--)
{
*dst++ = *src++;
}
return result;
}
template<typename T>
T * DuplicateArray(const T * src, size_t count)
{
T * result = AllocateArray<T>(count);
return CopyArray(result, src, count);
}
template<typename T>
void FreeArray(T * ptr, size_t count)
{
for (size_t i = 0; i < count; i++)
{
ptr[i].~T();
}
Free(ptr);
}
}

View File

@ -5,6 +5,7 @@ extern "C"
#include "../util/util.h"
}
#include "Math.hpp"
#include "Memory.hpp"
#include "Path.hpp"
#include "String.hpp"
@ -17,6 +18,44 @@ namespace Path
return safe_strcat_path(buffer, src, bufferSize);
}
utf8 * GetDirectory(utf8 * buffer, size_t bufferSize, const utf8 * path)
{
const char pathSeperator = platform_get_path_separator();
size_t lastPathSepIndex = String::LastIndexOf(path, pathSeperator);
if (lastPathSepIndex == SIZE_MAX)
{
return String::Set(buffer, bufferSize, String::Empty);
}
size_t copyLength = Math::Min(lastPathSepIndex, bufferSize - 1);
Memory::Copy(buffer, path, copyLength);
buffer[copyLength] = '\0';
return buffer;
}
utf8 * GetFileNameWithoutExtension(utf8 * buffer, size_t bufferSize, const utf8 * path)
{
const utf8 * lastDot = nullptr;
const utf8 * ch = path;
for (; *ch != '\0'; ch++)
{
if (*ch == '.')
{
lastDot = ch;
}
}
if (lastDot == nullptr)
{
return String::Set(buffer, bufferSize, path);
}
size_t truncatedLength = Math::Min<size_t>(bufferSize - 1, lastDot - path);
Memory::Copy(buffer, path, truncatedLength);
buffer[truncatedLength] = '\0';
return buffer;
}
utf8 * GetAbsolute(utf8 *buffer, size_t bufferSize, const utf8 * relativePath)
{
#if __WINDOWS__
@ -49,4 +88,13 @@ namespace Path
}
#endif
}
bool Equals(const utf8 * a, const utf8 * b)
{
bool ignoreCase = false;
#if __WINDOWS__
ignoreCase = true;
#endif
return String::Equals(a, b, ignoreCase);
}
}

View File

@ -8,5 +8,8 @@ extern "C"
namespace Path
{
utf8 * Append(utf8 * buffer, size_t bufferSize, const utf8 * src);
utf8 * GetAbsolute(utf8 *buffer, size_t bufferSize, const utf8 * relativePath);
utf8 * GetDirectory(utf8 * buffer, size_t bufferSize, const utf8 * path);
utf8 * GetFileNameWithoutExtension(utf8 * buffer, size_t bufferSize, const utf8 * path);
utf8 * GetAbsolute(utf8 * buffer, size_t bufferSize, const utf8 * relativePath);
bool Equals(const utf8 * a, const utf8 * b);
}

View File

@ -10,6 +10,11 @@ extern "C"
namespace String
{
bool IsNullOrEmpty(const utf8 * str)
{
return str == nullptr || str[0] == '\0';
}
bool Equals(const utf8 * a, const utf8 * b, bool ignoreCase)
{
if (a == b) return true;
@ -51,6 +56,28 @@ namespace String
}
}
size_t LastIndexOf(const utf8 * str, utf8 match)
{
const utf8 * lastOccurance = nullptr;
const utf8 * ch = str;
for (; *ch != '\0'; ch++)
{
if (*ch == match)
{
lastOccurance = ch;
}
}
if (lastOccurance == nullptr)
{
return SIZE_MAX;
}
else
{
return (size_t)(lastOccurance - str);
}
}
size_t LengthOf(const utf8 * str)
{
return utf8_length(str);
@ -137,14 +164,14 @@ namespace String
return replacement;
}
utf8 * DiscardDuplicate(utf8 * * ptr, utf8 * replacement)
utf8 * DiscardDuplicate(utf8 * * ptr, const utf8 * replacement)
{
return DiscardUse(ptr, String::Duplicate(replacement));
}
utf8 * SkipBOM(utf8 * buffer)
{
return (utf8*)SkipBOM(buffer);
return (utf8*)SkipBOM((const utf8 *)buffer);
}
const utf8 * SkipBOM(const utf8 * buffer)

View File

@ -7,8 +7,12 @@ extern "C"
namespace String
{
constexpr const utf8 * Empty = "";
bool IsNullOrEmpty(const utf8 * str);
bool Equals(const utf8 * a, const utf8 * b, bool ignoreCase = false);
bool StartsWith(const utf8 * str, const utf8 * match, bool ignoreCase = false);
size_t LastIndexOf(const utf8 * str, utf8 match);
/**
* Gets the length of the given string in codepoints.
@ -35,7 +39,7 @@ namespace String
/**
* Helper method to free the string a string pointer points to and set it to a copy of a replacement string.
*/
utf8 * DiscardDuplicate(utf8 * * ptr, utf8 * replacement);
utf8 * DiscardDuplicate(utf8 * * ptr, const utf8 * replacement);
utf8 * SkipBOM(utf8 * buffer);
const utf8 * SkipBOM(const utf8 * buffer);

830
src/interface/Theme.cpp Normal file
View File

@ -0,0 +1,830 @@
#include <jansson.h>
extern "C"
{
#include "../common.h"
#include "../config.h"
#include "themes.h"
#include "window.h"
}
#include "../core/Json.hpp"
#include "../core/List.hpp"
#include "../core/Math.hpp"
#include "../core/Memory.hpp"
#include "../core/Path.hpp"
#include "../core/String.hpp"
#include "../core/Util.hpp"
struct WindowThemeDesc;
// Don't try to load theme files that exceed 64 MiB
constexpr uint64 MAX_THEME_SIZE = 64 * 1024 * 1024;
/**
* Represents a window theming style such as the colour scheme.
*/
struct WindowTheme
{
colour_t Colours[6];
};
/**
* Represents the style for a particular type of window.
*/
struct UIThemeWindowEntry
{
rct_windowclass WindowClass;
WindowTheme Theme;
json_t * ToJson() const;
static UIThemeWindowEntry FromJson(const WindowThemeDesc * wtDesc, const json_t * json);
};
/**
* Represents a user interface theme. Contains window colour schemes and appearance features.
*/
class UITheme
{
public:
utf8 * Name;
List<UIThemeWindowEntry> Entries;
uint8 Flags;
UITheme(const utf8 * name);
UITheme(const UITheme & copy);
~UITheme();
void SetName(const utf8 * name);
const UIThemeWindowEntry * GetEntry(rct_windowclass windowClass) const;
void SetEntry(const UIThemeWindowEntry * entry);
void RemoveEntry(rct_windowclass windowClass);
json_t * ToJson() const;
bool WriteToFile(const utf8 * path) const;
static UITheme * FromJson(const json_t * json);
static UITheme * FromFile(const utf8 * path);
static UITheme CreatePredefined(const utf8 * name, const UIThemeWindowEntry * entries, uint8 flags);
};
/**
* Represents the theme descriptor for a specific window type including the default colour scheme.
*/
struct WindowThemeDesc
{
rct_windowclass WindowClass;
const utf8 * WindowClassSZ;
rct_string_id WindowName;
uint8 NumColours;
WindowTheme DefaultTheme;
};
#pragma region Window Theme Descriptors
#define COLOURS_1(c0) 1, { { (c0), 0, 0, 0, 0, 0 } }
#define COLOURS_2(c0, c1) 2, { { (c0), (c1), 0, 0, 0, 0 } }
#define COLOURS_3(c0, c1, c2) 3, { { (c0), (c1), (c2), 0, 0, 0 } }
#define COLOURS_4(c0, c1, c2, c3) 4, { { (c0), (c1), (c2), (c3), 0, 0 } }
#define COLOURS_5(c0, c1, c2, c3, c4) 5, { { (c0), (c1), (c2), (c3), (c4), 0 } }
#define COLOURS_6(c0, c1, c2, c3, c4, c5) 6, { { (c0), (c1), (c2), (c3), (c4), (c5) } }
#define THEME_DEF_END { 0xFF, { 0, 0, 0, 0, 0, 0 } }
#define TWINDOW(window_class, window_name, window_string_id, theme) { window_class, window_name, window_string_id, theme }
#define THEME_WC(wc) wc, #wc
WindowThemeDesc WindowThemeDescriptors[] =
{
// WindowClass, WindowClassSZ WindowName NumColours, DefaultTheme
{ THEME_WC(WC_TOP_TOOLBAR), 5245, COLOURS_4(COLOUR_LIGHT_BLUE, COLOUR_DARK_GREEN, COLOUR_DARK_BROWN, COLOUR_GREY ) },
{ THEME_WC(WC_BOTTOM_TOOLBAR), 5246, COLOURS_4(TRANSLUCENT(COLOUR_DARK_GREEN), TRANSLUCENT(COLOUR_DARK_GREEN), COLOUR_BLACK, COLOUR_BRIGHT_GREEN ) },
{ THEME_WC(WC_RIDE), 5203, COLOURS_3(COLOUR_GREY, COLOUR_BORDEAUX_RED, COLOUR_GREY ) },
{ THEME_WC(WC_RIDE_CONSTRUCTION), 5199, COLOURS_3(COLOUR_DARK_BROWN, COLOUR_DARK_BROWN, COLOUR_DARK_BROWN ) },
{ THEME_WC(WC_RIDE_LIST), 5204, COLOURS_3(COLOUR_GREY, COLOUR_BORDEAUX_RED, COLOUR_BORDEAUX_RED ) },
{ THEME_WC(WC_SAVE_PROMPT), 5223, COLOURS_1(TRANSLUCENT(COLOUR_BORDEAUX_RED) ) },
{ THEME_WC(WC_CONSTRUCT_RIDE), 5201, COLOURS_3(COLOUR_DARK_BROWN, COLOUR_BORDEAUX_RED, COLOUR_BORDEAUX_RED ) },
{ THEME_WC(WC_DEMOLISH_RIDE_PROMPT), 5224, COLOURS_1(TRANSLUCENT(COLOUR_BORDEAUX_RED) ) },
{ THEME_WC(WC_SCENERY), 5197, COLOURS_3(COLOUR_DARK_BROWN, COLOUR_DARK_GREEN, COLOUR_DARK_GREEN ) },
{ THEME_WC(WC_OPTIONS), 5219, COLOURS_3(COLOUR_LIGHT_BLUE, COLOUR_LIGHT_BLUE, COLOUR_LIGHT_BLUE ) },
{ THEME_WC(WC_FOOTPATH), 5198, COLOURS_3(COLOUR_DARK_BROWN, COLOUR_DARK_BROWN, COLOUR_DARK_BROWN ) },
{ THEME_WC(WC_LAND), 5193, COLOURS_3(COLOUR_DARK_BROWN, COLOUR_DARK_BROWN, COLOUR_DARK_BROWN ) },
{ THEME_WC(WC_WATER), 5194, COLOURS_3(COLOUR_DARK_BROWN, COLOUR_DARK_BROWN, COLOUR_DARK_BROWN ) },
{ THEME_WC(WC_PEEP), 5205, COLOURS_3(COLOUR_GREY, COLOUR_OLIVE_GREEN, COLOUR_OLIVE_GREEN ) },
{ THEME_WC(WC_GUEST_LIST), 5206, COLOURS_3(COLOUR_GREY, COLOUR_OLIVE_GREEN, COLOUR_OLIVE_GREEN ) },
{ THEME_WC(WC_STAFF_LIST), 5208, COLOURS_3(COLOUR_GREY, COLOUR_LIGHT_PURPLE, COLOUR_LIGHT_PURPLE ) },
{ THEME_WC(WC_FIRE_PROMPT), 5225, COLOURS_1(TRANSLUCENT(COLOUR_BORDEAUX_RED) ) },
{ THEME_WC(WC_PARK_INFORMATION), 5253, COLOURS_3(COLOUR_GREY, COLOUR_DARK_YELLOW, COLOUR_DARK_YELLOW ) },
{ THEME_WC(WC_FINANCES), 5187, COLOURS_3(COLOUR_GREY, COLOUR_DARK_YELLOW, COLOUR_DARK_YELLOW ) },
{ THEME_WC(WC_TITLE_MENU), 5249, COLOURS_3(TRANSLUCENT(COLOUR_DARK_GREEN), TRANSLUCENT(COLOUR_DARK_GREEN), TRANSLUCENT(COLOUR_DARK_GREEN) ) },
{ THEME_WC(WC_TITLE_EXIT), 5250, COLOURS_3(TRANSLUCENT(COLOUR_DARK_GREEN), TRANSLUCENT(COLOUR_DARK_GREEN), TRANSLUCENT(COLOUR_DARK_GREEN) ) },
{ THEME_WC(WC_RECENT_NEWS), 5192, COLOURS_3(COLOUR_GREY, COLOUR_GREY, COLOUR_BLACK ) },
{ THEME_WC(WC_SCENARIO_SELECT), 5252, COLOURS_3(COLOUR_GREY, COLOUR_BORDEAUX_RED, COLOUR_BORDEAUX_RED ) },
{ THEME_WC(WC_TRACK_DESIGN_LIST), 5202, COLOURS_3(COLOUR_BORDEAUX_RED, COLOUR_BORDEAUX_RED, COLOUR_BORDEAUX_RED ) },
{ THEME_WC(WC_TRACK_DESIGN_PLACE), 5200, COLOURS_3(COLOUR_DARK_BROWN, COLOUR_DARK_BROWN, COLOUR_DARK_BROWN ) },
{ THEME_WC(WC_NEW_CAMPAIGN), 5188, COLOURS_3(COLOUR_DARK_YELLOW, COLOUR_DARK_YELLOW, COLOUR_DARK_YELLOW ) },
{ THEME_WC(WC_KEYBOARD_SHORTCUT_LIST), 5220, COLOURS_3(COLOUR_LIGHT_BLUE, COLOUR_LIGHT_BLUE, COLOUR_LIGHT_BLUE ) },
{ THEME_WC(WC_CHANGE_KEYBOARD_SHORTCUT), 5221, COLOURS_3(COLOUR_LIGHT_BLUE, COLOUR_LIGHT_BLUE, COLOUR_LIGHT_BLUE ) },
{ THEME_WC(WC_MAP), 5190, COLOURS_2(COLOUR_DARK_GREEN, COLOUR_DARK_BROWN ) },
{ THEME_WC(WC_BANNER), 5209, COLOURS_3(COLOUR_DARK_BROWN, COLOUR_DARK_BROWN, COLOUR_DARK_BROWN ) },
{ THEME_WC(WC_EDITOR_OBJECT_SELECTION), 5210, COLOURS_3(COLOUR_LIGHT_PURPLE, COLOUR_GREY, COLOUR_GREY ) },
{ THEME_WC(WC_EDITOR_INVENTION_LIST), 5211, COLOURS_3(COLOUR_LIGHT_PURPLE, COLOUR_GREY, COLOUR_GREY ) },
{ THEME_WC(WC_EDITOR_SCENARIO_OPTIONS), 5212, COLOURS_3(COLOUR_LIGHT_PURPLE, COLOUR_GREY, COLOUR_GREY ) },
{ THEME_WC(WC_EDTIOR_OBJECTIVE_OPTIONS), 5213, COLOURS_3(COLOUR_LIGHT_PURPLE, COLOUR_GREY, COLOUR_GREY ) },
{ THEME_WC(WC_MANAGE_TRACK_DESIGN), 5215, COLOURS_3(COLOUR_GREY, COLOUR_GREY, COLOUR_GREY ) },
{ THEME_WC(WC_TRACK_DELETE_PROMPT), 5226, COLOURS_3(COLOUR_BORDEAUX_RED, COLOUR_BORDEAUX_RED, COLOUR_BORDEAUX_RED ) },
{ THEME_WC(WC_INSTALL_TRACK), 5216, COLOURS_3(COLOUR_BORDEAUX_RED, COLOUR_BORDEAUX_RED, COLOUR_BORDEAUX_RED ) },
{ THEME_WC(WC_CLEAR_SCENERY), 5195, COLOURS_3(COLOUR_DARK_BROWN, COLOUR_DARK_BROWN, COLOUR_DARK_BROWN ) },
{ THEME_WC(WC_CHEATS), 5217, COLOURS_3(COLOUR_GREY, COLOUR_DARK_YELLOW, COLOUR_DARK_YELLOW ) },
{ THEME_WC(WC_RESEARCH), 5189, COLOURS_3(COLOUR_GREY, COLOUR_DARK_YELLOW, COLOUR_DARK_YELLOW ) },
{ THEME_WC(WC_VIEWPORT), 5191, COLOURS_3(COLOUR_DARK_BROWN, COLOUR_DARK_BROWN, COLOUR_DARK_BROWN ) },
{ THEME_WC(WC_MAPGEN), 5214, COLOURS_3(COLOUR_DARK_GREEN, COLOUR_DARK_BROWN, COLOUR_DARK_BROWN ) },
{ THEME_WC(WC_LOADSAVE), 5222, COLOURS_3(COLOUR_LIGHT_BLUE, COLOUR_LIGHT_BLUE, COLOUR_LIGHT_BLUE ) },
{ THEME_WC(WC_LOADSAVE_OVERWRITE_PROMPT), 5227, COLOURS_1(TRANSLUCENT(COLOUR_BORDEAUX_RED) ) },
{ THEME_WC(WC_TITLE_OPTIONS), 5251, COLOURS_3(TRANSLUCENT(COLOUR_DARK_GREEN), TRANSLUCENT(COLOUR_DARK_GREEN), TRANSLUCENT(COLOUR_DARK_GREEN) ) },
{ THEME_WC(WC_LAND_RIGHTS), 5196, COLOURS_3(COLOUR_DARK_YELLOW, COLOUR_DARK_YELLOW, COLOUR_DARK_YELLOW ) },
{ THEME_WC(WC_THEMES), 5218, COLOURS_3(COLOUR_GREY, COLOUR_DARK_GREEN, COLOUR_DARK_GREEN ) },
{ THEME_WC(WC_STAFF), 5207, COLOURS_3(COLOUR_GREY, COLOUR_LIGHT_PURPLE, COLOUR_LIGHT_PURPLE ) },
{ THEME_WC(WC_EDITOR_TRACK_BOTTOM_TOOLBAR), 5247, COLOURS_3(TRANSLUCENT(COLOUR_LIGHT_BLUE), TRANSLUCENT(COLOUR_LIGHT_BLUE), TRANSLUCENT(COLOUR_LIGHT_BLUE) ) },
{ THEME_WC(WC_EDITOR_SCENARIO_BOTTOM_TOOLBAR), 5248, COLOURS_3(TRANSLUCENT(COLOUR_LIGHT_BROWN), TRANSLUCENT(COLOUR_LIGHT_BROWN), TRANSLUCENT(COLOUR_MOSS_GREEN) ) },
{ THEME_WC(WC_TITLE_EDITOR), 5433, COLOURS_3(COLOUR_GREY, COLOUR_OLIVE_GREEN, COLOUR_OLIVE_GREEN ) },
{ THEME_WC(WC_TILE_INSPECTOR), 5314, COLOURS_2(COLOUR_LIGHT_BLUE, COLOUR_LIGHT_BLUE ) },
{ THEME_WC(WC_CHANGELOG), 5344, COLOURS_2(COLOUR_LIGHT_BLUE, COLOUR_LIGHT_BLUE ) },
{ THEME_WC(WC_MULTIPLAYER), 5502, COLOURS_3(COLOUR_LIGHT_BLUE, COLOUR_LIGHT_BLUE, COLOUR_LIGHT_BLUE ) },
{ THEME_WC(WC_PLAYER), 5736, COLOURS_3(COLOUR_LIGHT_BLUE, COLOUR_LIGHT_BLUE, COLOUR_LIGHT_BLUE ) },
{ THEME_WC(WC_NETWORK_STATUS), 5735, COLOURS_1(COLOUR_LIGHT_BLUE ) },
{ THEME_WC(WC_SERVER_LIST), 5498, COLOURS_2(COLOUR_LIGHT_BLUE, COLOUR_LIGHT_BLUE ) },
};
#pragma endregion
#pragma region Pre-defined Themes
#define COLOURS_RCT1(c0, c1, c2, c3, c4, c5) { { (c0), (c1), (c2), (c3), (c4), (c5) } }
UIThemeWindowEntry PredefinedThemeRCT1_Entries[] =
{
{ WC_TOP_TOOLBAR, COLOURS_RCT1(COLOUR_GREY, COLOUR_GREY, COLOUR_GREY, COLOUR_GREY, COLOUR_BLACK, COLOUR_BLACK) },
{ WC_BOTTOM_TOOLBAR, COLOURS_RCT1(TRANSLUCENT(COLOUR_GREY), TRANSLUCENT(COLOUR_GREY), COLOUR_BLACK, COLOUR_YELLOW, COLOUR_BLACK, COLOUR_BLACK) },
{ WC_RIDE, COLOURS_RCT1(COLOUR_BORDEAUX_RED, COLOUR_GREY, COLOUR_SATURATED_GREEN, COLOUR_BLACK, COLOUR_BLACK, COLOUR_BLACK) },
{ WC_RIDE_LIST, COLOURS_RCT1(COLOUR_BORDEAUX_RED, COLOUR_GREY, COLOUR_GREY, COLOUR_BLACK, COLOUR_BLACK, COLOUR_BLACK) },
{ WC_CONSTRUCT_RIDE, COLOURS_RCT1(COLOUR_BORDEAUX_RED, COLOUR_GREY, COLOUR_GREY, COLOUR_BLACK, COLOUR_BLACK, COLOUR_BLACK) },
{ WC_PEEP, COLOURS_RCT1(COLOUR_LIGHT_BROWN, COLOUR_BORDEAUX_RED, COLOUR_BORDEAUX_RED, COLOUR_BLACK, COLOUR_BLACK, COLOUR_BLACK) },
{ WC_GUEST_LIST, COLOURS_RCT1(COLOUR_LIGHT_BROWN, COLOUR_BORDEAUX_RED, COLOUR_BORDEAUX_RED, COLOUR_BLACK, COLOUR_BLACK, COLOUR_BLACK) },
{ WC_STAFF_LIST, COLOURS_RCT1(COLOUR_DARK_GREEN, COLOUR_LIGHT_PURPLE, COLOUR_LIGHT_PURPLE, COLOUR_BLACK, COLOUR_BLACK, COLOUR_BLACK) },
{ WC_FINANCES, COLOURS_RCT1(COLOUR_LIGHT_PURPLE, COLOUR_GREY, COLOUR_GREY, COLOUR_BLACK, COLOUR_BLACK, COLOUR_BLACK) },
{ WC_TITLE_MENU, COLOURS_RCT1(TRANSLUCENT(COLOUR_GREY), TRANSLUCENT(COLOUR_GREY), TRANSLUCENT(COLOUR_GREY), COLOUR_BLACK, COLOUR_BLACK, COLOUR_BLACK) },
{ WC_TITLE_EXIT, COLOURS_RCT1(TRANSLUCENT(COLOUR_GREY), TRANSLUCENT(COLOUR_GREY), TRANSLUCENT(COLOUR_GREY), COLOUR_BLACK, COLOUR_BLACK, COLOUR_BLACK) },
{ WC_NEW_CAMPAIGN, COLOURS_RCT1(COLOUR_LIGHT_PURPLE, COLOUR_LIGHT_PURPLE, COLOUR_GREY, COLOUR_BLACK, COLOUR_BLACK, COLOUR_BLACK) },
{ WC_TITLE_OPTIONS, COLOURS_RCT1(TRANSLUCENT(COLOUR_GREY), TRANSLUCENT(COLOUR_GREY), TRANSLUCENT(COLOUR_GREY), COLOUR_BLACK, COLOUR_BLACK, COLOUR_BLACK) },
{ WC_STAFF, COLOURS_RCT1(COLOUR_DARK_GREEN, COLOUR_LIGHT_PURPLE, COLOUR_LIGHT_PURPLE, COLOUR_BLACK, COLOUR_BLACK, COLOUR_BLACK) },
THEME_DEF_END
};
UIThemeWindowEntry PredefinedThemeRCT2_Entries[] =
{
THEME_DEF_END
};
const UITheme PredefinedThemeRCT1 = UITheme::CreatePredefined(
"RCT1", PredefinedThemeRCT1_Entries, UITHEME_FLAG_USE_LIGHTS_RIDE |
UITHEME_FLAG_USE_LIGHTS_PARK |
UITHEME_FLAG_USE_ALTERNATIVE_SCENARIO_SELECT_FONT);
const UITheme PredefinedThemeRCT2 = UITheme::CreatePredefined(
"RCT2", PredefinedThemeRCT2_Entries, 0);
const UITheme * PredefinedThemes[] = {
&PredefinedThemeRCT1,
&PredefinedThemeRCT2,
nullptr
};
#pragma endregion
static const WindowThemeDesc * GetWindowThemeDescriptor(rct_windowclass windowClass)
{
for (size_t i = 0; i < Util::CountOf(WindowThemeDescriptors); i++)
{
const WindowThemeDesc * desc = &WindowThemeDescriptors[i];
if (desc->WindowClass == windowClass)
{
return desc;
}
}
return nullptr;
}
static const WindowThemeDesc * GetWindowThemeDescriptor(const utf8 * windowClassSZ)
{
for (size_t i = 0; i < Util::CountOf(WindowThemeDescriptors); i++)
{
const WindowThemeDesc * desc = &WindowThemeDescriptors[i];
if (String::Equals(desc->WindowClassSZ, windowClassSZ))
{
return desc;
}
}
return nullptr;
}
static void ThrowThemeLoadException()
{
throw Exception("Invalid JSON UI theme entry.");
}
#pragma region UIThemeEntry
json_t * UIThemeWindowEntry::ToJson() const
{
const WindowThemeDesc * wtDesc = GetWindowThemeDescriptor(WindowClass);
json_t * jsonColours = json_array();
for (uint8 i = 0; i < wtDesc->NumColours; i++) {
colour_t colour = Theme.Colours[i];
json_array_append_new(jsonColours, json_integer(colour));
}
json_t * jsonEntry = json_object();
json_object_set_new(jsonEntry, "colours", jsonColours);
return jsonEntry;
}
UIThemeWindowEntry UIThemeWindowEntry::FromJson(const WindowThemeDesc * wtDesc, const json_t * json)
{
json_t * jsonColours = json_object_get(json, "colours");
if (jsonColours == nullptr)
{
ThrowThemeLoadException();
}
uint8 numColours = (uint8)json_array_size(jsonColours);
numColours = Math::Min(numColours, wtDesc->NumColours);
UIThemeWindowEntry result;
result.WindowClass = wtDesc->WindowClass;
result.Theme = wtDesc->DefaultTheme;
for (uint8 i = 0; i < numColours; i++)
{
result.Theme.Colours[i] = (colour_t)json_integer_value(json_array_get(jsonColours, i));
}
return result;
}
#pragma endregion
#pragma region UITheme
UITheme::UITheme(const utf8 * name)
{
Name = String::Duplicate(name);
Flags = 0;
}
UITheme::UITheme(const UITheme & copy)
{
Name = String::Duplicate(copy.Name);
Flags = copy.Flags;
Entries = copy.Entries;
}
UITheme::~UITheme()
{
Memory::Free(Name);
}
void UITheme::SetName(const utf8 * name)
{
String::DiscardDuplicate(&Name, name);
}
const UIThemeWindowEntry * UITheme::GetEntry(rct_windowclass windowClass) const
{
for (size_t i = 0; i < Entries.GetCount(); i++)
{
const UIThemeWindowEntry * entry = &Entries[i];
if (entry->WindowClass == windowClass)
{
return entry;
}
}
return nullptr;
}
void UITheme::SetEntry(const UIThemeWindowEntry * newEntry)
{
// Try to replace existing entry
for (size_t i = 0; i < Entries.GetCount(); i++)
{
UIThemeWindowEntry * entry = &Entries[i];
if (entry->WindowClass == newEntry->WindowClass)
{
*entry = *newEntry;
return;
}
}
Entries.Add(*newEntry);
}
void UITheme::RemoveEntry(rct_windowclass windowClass)
{
// Remove existing entry
for (size_t i = 0; i < Entries.GetCount(); i++)
{
UIThemeWindowEntry * entry = &Entries[i];
if (entry->WindowClass == windowClass)
{
Entries.RemoveAt(i);
break;
}
}
}
json_t * UITheme::ToJson() const
{
// Create entries
json_t * jsonEntries = json_object();
for (const UIThemeWindowEntry & entry : Entries)
{
const WindowThemeDesc * wtDesc = GetWindowThemeDescriptor(entry.WindowClass);
json_object_set_new(jsonEntries, wtDesc->WindowClassSZ, entry.ToJson());
}
// Create theme object
json_t * jsonTheme = json_object();
json_object_set_new(jsonTheme, "name", json_string(Name));
json_object_set_new(jsonTheme, "entries", jsonEntries);
json_object_set_new(jsonTheme, "useLightsRide", json_boolean(Flags & UITHEME_FLAG_USE_LIGHTS_RIDE));
json_object_set_new(jsonTheme, "useLightsPark", json_boolean(Flags & UITHEME_FLAG_USE_LIGHTS_PARK));
json_object_set_new(jsonTheme,
"useAltScenarioSelectFont",
json_boolean(Flags & UITHEME_FLAG_USE_ALTERNATIVE_SCENARIO_SELECT_FONT));
return jsonTheme;
}
bool UITheme::WriteToFile(const utf8 * path) const
{
json_t * jsonTheme = ToJson();
bool result;
try
{
Json::WriteToFile(path, jsonTheme, JSON_INDENT(4) | JSON_PRESERVE_ORDER);
result = true;
}
catch (Exception ex)
{
log_error("Unable to save %s: %s", path, ex.GetMessage());
result = false;
}
json_decref(jsonTheme);
return result;
}
UITheme * UITheme::FromJson(const json_t * json)
{
const char * themeName = json_string_value(json_object_get(json, "name"));
if (themeName == nullptr)
{
ThrowThemeLoadException();
}
json_t * jsonEntries = json_object_get(json, "entries");
size_t numEntries = json_object_size(jsonEntries);
UITheme * result = nullptr;
try
{
result = new UITheme(themeName);
if (json_is_true(json_object_get(json, "useLightsRide")))
{
result->Flags |= UITHEME_FLAG_USE_LIGHTS_RIDE;
}
if (json_is_true(json_object_get(json, "useLightsPark")))
{
result->Flags |= UITHEME_FLAG_USE_LIGHTS_PARK;
}
if (json_is_true(json_object_get(json, "useAltScenarioSelectFont")))
{
result->Flags |= UITHEME_FLAG_USE_ALTERNATIVE_SCENARIO_SELECT_FONT;
}
const char * jkey;
json_t * jvalue;
size_t i = 0;
json_object_foreach(jsonEntries, jkey, jvalue)
{
const WindowThemeDesc * wtDesc = GetWindowThemeDescriptor(jkey);
if (wtDesc == nullptr) continue;
UIThemeWindowEntry entry = UIThemeWindowEntry::FromJson(wtDesc, jvalue);
result->SetEntry(&entry);
}
return result;
}
catch (Exception ex)
{
delete result;
throw ex;
}
}
UITheme * UITheme::FromFile(const utf8 * path)
{
json_t * json = nullptr;
UITheme * result = nullptr;
try
{
json = Json::ReadFromFile(path);
result = UITheme::FromJson(json);
}
catch (Exception ex)
{
log_error("Unable to read theme: %s", path);
result = nullptr;
}
json_decref(json);
return result;
}
UITheme UITheme::CreatePredefined(const utf8 * name, const UIThemeWindowEntry * entries, uint8 flags)
{
auto theme = UITheme(name);
theme.Flags = flags | UITHEME_FLAG_PREDEFINED;
size_t numEntries = 0;
for (const UIThemeWindowEntry * entry = entries; entry->WindowClass != 255; entry++)
{
numEntries++;
}
theme.Entries = List<UIThemeWindowEntry>(entries, numEntries);
return theme;
}
#pragma endregion
namespace ThemeManager
{
struct AvailableTheme
{
utf8 Path[MAX_PATH];
utf8 Name[96];
};
utf8 * CurrentThemePath;
UITheme * CurrentTheme;
List<AvailableTheme> AvailableThemes;
size_t ActiveAvailableThemeIndex = SIZE_MAX;
size_t NumPredefinedThemes = 0;
void GetThemeFileName(utf8 * buffer, size_t bufferSize, const utf8 * name);
void GetAvailableThemes(List<AvailableTheme> * outThemes)
{
Guard::ArgumentNotNull(outThemes);
outThemes->Clear();
NumPredefinedThemes = 0;
for (const UITheme * * predefinedTheme = PredefinedThemes; *predefinedTheme != nullptr; predefinedTheme++)
{
AvailableTheme theme;
String::Set(theme.Path, sizeof(theme.Path), String::Empty);
String::Set(theme.Name, sizeof(theme.Name), (*predefinedTheme)->Name);
outThemes->Add(theme);
NumPredefinedThemes++;
}
utf8 themesPattern[MAX_PATH];
platform_get_user_directory(themesPattern, "themes");
Path::Append(themesPattern, sizeof(themesPattern), "*.json");
int handle = platform_enumerate_files_begin(themesPattern);
if (handle != INVALID_HANDLE)
{
file_info fileInfo;
while (platform_enumerate_files_next(handle, &fileInfo))
{
AvailableTheme theme;
Path::GetFileNameWithoutExtension(theme.Name, sizeof(theme.Name), fileInfo.path);
GetThemeFileName(theme.Path, sizeof(theme.Path), theme.Name);
outThemes->Add(theme);
if (Path::Equals(CurrentThemePath, fileInfo.path))
{
ActiveAvailableThemeIndex = outThemes->GetCount() - 1;
}
}
platform_enumerate_files_end(handle);
}
}
void LoadTheme(UITheme * theme)
{
if (CurrentTheme == theme)
{
return;
}
if (CurrentTheme != nullptr)
{
if (!(CurrentTheme->Flags & UITHEME_FLAG_PREDEFINED))
{
delete CurrentTheme;
}
}
CurrentTheme = theme;
String::DiscardUse(&CurrentThemePath, nullptr);
gfx_invalidate_screen();
}
void LoadTheme(const utf8 * path)
{
UITheme * theme = UITheme::FromFile(path);
if (theme == nullptr)
{
// Fall-back to default
theme = (UITheme *)&PredefinedThemeRCT2;
LoadTheme(theme);
}
else
{
LoadTheme(theme);
String::DiscardDuplicate(&CurrentThemePath, path);
}
}
bool LoadThemeByName(const utf8 * name)
{
for (size_t i = 0; i < ThemeManager::AvailableThemes.GetCount(); i++)
{
if (String::Equals(name, ThemeManager::AvailableThemes[i].Name))
{
const utf8 * path = ThemeManager::AvailableThemes[i].Path;
if (String::IsNullOrEmpty(path))
{
LoadTheme((UITheme *)PredefinedThemes[i]);
}
else
{
LoadTheme(ThemeManager::AvailableThemes[i].Path);
}
ActiveAvailableThemeIndex = i;
return true;
}
}
return false;
}
void Initialise()
{
ThemeManager::GetAvailableThemes(&ThemeManager::AvailableThemes);
LoadTheme((UITheme *)&PredefinedThemeRCT2);
ActiveAvailableThemeIndex = 1;
bool configValid = false;
if (!String::IsNullOrEmpty(gConfigInterface.current_theme_preset))
{
if (LoadThemeByName(gConfigInterface.current_theme_preset))
{
configValid = true;
}
}
if (!configValid)
{
String::DiscardDuplicate(&gConfigInterface.current_theme_preset, theme_manager_get_available_theme_name(1));
}
}
void GetThemeFileName(utf8 * buffer, size_t bufferSize, const utf8 * name)
{
platform_get_user_directory(buffer, "themes");
Path::Append(buffer, bufferSize, name);
String::Append(buffer, bufferSize, ".json");
}
}
extern "C"
{
void theme_manager_load_available_themes()
{
ThemeManager::GetAvailableThemes(&ThemeManager::AvailableThemes);
}
size_t theme_manager_get_num_available_themes()
{
return ThemeManager::AvailableThemes.GetCount();
}
const utf8 * theme_manager_get_available_theme_path(size_t index)
{
return ThemeManager::AvailableThemes[index].Path;
}
const utf8 * theme_manager_get_available_theme_name(size_t index)
{
return ThemeManager::AvailableThemes[index].Name;
}
size_t theme_manager_get_active_available_theme_index()
{
return ThemeManager::ActiveAvailableThemeIndex;
}
void theme_manager_set_active_available_theme(size_t index)
{
if (index < ThemeManager::NumPredefinedThemes)
{
ThemeManager::LoadTheme((UITheme *)PredefinedThemes[index]);
}
else
{
const utf8 * path = ThemeManager::AvailableThemes[index].Path;
ThemeManager::LoadTheme(path);
// HACK Check if theme load failed and fell back to RCT2
if (ThemeManager::CurrentThemePath == nullptr)
{
index = 1;
}
}
ThemeManager::ActiveAvailableThemeIndex = index;
String::DiscardDuplicate(&gConfigInterface.current_theme_preset, theme_manager_get_available_theme_name(index));
}
uint8 theme_get_colour(rct_windowclass wc, uint8 index)
{
const UIThemeWindowEntry * entry = ThemeManager::CurrentTheme->GetEntry(wc);
if (entry == nullptr)
{
const WindowThemeDesc * desc = GetWindowThemeDescriptor(wc);
return desc->DefaultTheme.Colours[index];
}
else
{
return entry->Theme.Colours[index];
}
}
void theme_set_colour(rct_windowclass wc, uint8 index, colour_t colour)
{
UIThemeWindowEntry entry;
entry.WindowClass = wc;
auto currentEntry = (UIThemeWindowEntry *)ThemeManager::CurrentTheme->GetEntry(wc);
if (currentEntry != nullptr)
{
entry.Theme = currentEntry->Theme;
}
else
{
const WindowThemeDesc * desc = GetWindowThemeDescriptor(wc);
entry.Theme = desc->DefaultTheme;
}
entry.Theme.Colours[index] = colour;
ThemeManager::CurrentTheme->SetEntry(&entry);
theme_save();
}
uint8 theme_get_flags()
{
return ThemeManager::CurrentTheme->Flags;
}
void theme_set_flags(uint8 flags)
{
ThemeManager::CurrentTheme->Flags = flags;
theme_save();
}
void theme_save()
{
ThemeManager::CurrentTheme->WriteToFile(ThemeManager::CurrentThemePath);
}
void theme_rename(const utf8 * name)
{
const utf8 * oldPath = ThemeManager::CurrentThemePath;
utf8 newPath[MAX_PATH];
ThemeManager::GetThemeFileName(newPath, sizeof(newPath), name);
platform_file_move(oldPath, newPath);
String::DiscardDuplicate(&ThemeManager::CurrentThemePath, newPath);
ThemeManager::CurrentTheme->SetName(name);
ThemeManager::CurrentTheme->WriteToFile(ThemeManager::CurrentThemePath);
theme_manager_load_available_themes();
for (size_t i = 0; i < ThemeManager::AvailableThemes.GetCount(); i++)
{
if (Path::Equals(newPath, ThemeManager::AvailableThemes[i].Path))
{
ThemeManager::ActiveAvailableThemeIndex = i;
String::DiscardDuplicate(&gConfigInterface.current_theme_preset, theme_manager_get_available_theme_name(1));
break;
}
}
}
void theme_duplicate(const utf8 * name)
{
utf8 newPath[MAX_PATH];
ThemeManager::GetThemeFileName(newPath, sizeof(newPath), name);
// Copy the theme, save it and then load it back in
UITheme * newTheme = new UITheme(*ThemeManager::CurrentTheme);
newTheme->SetName(name);
newTheme->Flags &= ~UITHEME_FLAG_PREDEFINED;
newTheme->WriteToFile(newPath);
delete newTheme;
ThemeManager::LoadTheme(newPath);
theme_manager_load_available_themes();
for (size_t i = 0; i < ThemeManager::AvailableThemes.GetCount(); i++)
{
if (Path::Equals(newPath, ThemeManager::AvailableThemes[i].Path))
{
ThemeManager::ActiveAvailableThemeIndex = i;
String::DiscardDuplicate(&gConfigInterface.current_theme_preset, theme_manager_get_available_theme_name(i));
break;
}
}
}
void theme_delete()
{
platform_file_delete(ThemeManager::CurrentThemePath);
ThemeManager::LoadTheme((UITheme *)&PredefinedThemeRCT2);
ThemeManager::ActiveAvailableThemeIndex = 1;
String::DiscardDuplicate(&gConfigInterface.current_theme_preset, theme_manager_get_available_theme_name(1));
}
void theme_manager_initialise()
{
ThemeManager::Initialise();
}
uint8 theme_desc_get_num_colours(rct_windowclass wc)
{
const WindowThemeDesc * desc = GetWindowThemeDescriptor(wc);
return desc->NumColours;
}
rct_string_id theme_desc_get_name(rct_windowclass wc)
{
const WindowThemeDesc * desc = GetWindowThemeDescriptor(wc);
return desc->WindowName;
}
void colour_scheme_update(rct_window * window)
{
colour_scheme_update_by_class(window, window->classification);
}
void colour_scheme_update_by_class(rct_window * window, rct_windowclass classification)
{
const WindowTheme * windowTheme;
const UIThemeWindowEntry * entry = ThemeManager::CurrentTheme->GetEntry(classification);
if (entry != nullptr)
{
windowTheme = &entry->Theme;
}
else
{
const WindowThemeDesc * desc = GetWindowThemeDescriptor(classification);
windowTheme = &desc->DefaultTheme;
}
bool transparent = false;
for (int i = 0; i < 6; i++) {
window->colours[i] = windowTheme->Colours[i];
if (windowTheme->Colours[i] & COLOUR_FLAG_TRANSLUCENT) {
transparent = true;
}
}
// Some windows need to be transparent even if the colours aren't.
// There doesn't seem to be any side-effects for all windows being transparent
window->flags |= WF_TRANSPARENT;
}
}

View File

@ -1,251 +0,0 @@
/*****************************************************************************
* Copyright (c) 2014 Ted John, Peter Hill
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* This file is part of OpenRCT2.
*
* OpenRCT2 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 3 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, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#include "../localisation/string_ids.h"
#include "../util/util.h"
#include "colour.h"
#include "window.h"
#include "themes.h"
#define COLOURS_1(c0) 1, { { (c0), 0, 0, 0, 0, 0 } }
#define COLOURS_2(c0, c1) 2, { { (c0), (c1), 0, 0, 0, 0 } }
#define COLOURS_3(c0, c1, c2) 3, { { (c0), (c1), (c2), 0, 0, 0 } }
#define COLOURS_4(c0, c1, c2, c3) 4, { { (c0), (c1), (c2), (c3), 0, 0 } }
#define COLOURS_5(c0, c1, c2, c3, c4) 5, { { (c0), (c1), (c2), (c3), (c4), 0 } }
#define COLOURS_6(c0, c1, c2, c3, c4, c5) 6, { { (c0), (c1), (c2), (c3), (c4), (c5) } }
#define THEME_DEF_END { 0xFF, { 0, 0, 0, 0, 0, 0 } }
#define TWINDOW(window_class, window_name, window_string_id, theme) { window_class, window_name, window_string_id, theme }
theme_window_definition gThemeWindowDefinitions[] = {
/* Window Class ini section name stringid window defaults */
{ WC_TOP_TOOLBAR, "top_toolbar", 5245, COLOURS_4(COLOUR_LIGHT_BLUE, COLOUR_DARK_GREEN, COLOUR_DARK_BROWN, COLOUR_GREY ) },
{ WC_BOTTOM_TOOLBAR, "bottom_toolbar", 5246, COLOURS_4(TRANSLUCENT(COLOUR_DARK_GREEN), TRANSLUCENT(COLOUR_DARK_GREEN), COLOUR_BLACK, COLOUR_BRIGHT_GREEN ) },
{ WC_RIDE, "ride", 5203, COLOURS_3(COLOUR_GREY, COLOUR_BORDEAUX_RED, COLOUR_GREY ) },
{ WC_RIDE_CONSTRUCTION, "ride_construction", 5199, COLOURS_3(COLOUR_DARK_BROWN, COLOUR_DARK_BROWN, COLOUR_DARK_BROWN ) },
{ WC_RIDE_LIST, "ride_list", 5204, COLOURS_3(COLOUR_GREY, COLOUR_BORDEAUX_RED, COLOUR_BORDEAUX_RED ) },
{ WC_SAVE_PROMPT, "save_prompt", 5223, COLOURS_1(TRANSLUCENT(COLOUR_BORDEAUX_RED) ) },
{ WC_CONSTRUCT_RIDE, "new_ride", 5201, COLOURS_3(COLOUR_DARK_BROWN, COLOUR_BORDEAUX_RED, COLOUR_BORDEAUX_RED ) },
{ WC_DEMOLISH_RIDE_PROMPT, "demolish_ride_prompt", 5224, COLOURS_1(TRANSLUCENT(COLOUR_BORDEAUX_RED) ) },
{ WC_SCENERY, "scenery", 5197, COLOURS_3(COLOUR_DARK_BROWN, COLOUR_DARK_GREEN, COLOUR_DARK_GREEN ) },
{ WC_OPTIONS, "options", 5219, COLOURS_3(COLOUR_LIGHT_BLUE, COLOUR_LIGHT_BLUE, COLOUR_LIGHT_BLUE ) },
{ WC_FOOTPATH, "footpath", 5198, COLOURS_3(COLOUR_DARK_BROWN, COLOUR_DARK_BROWN, COLOUR_DARK_BROWN ) },
{ WC_LAND, "land", 5193, COLOURS_3(COLOUR_DARK_BROWN, COLOUR_DARK_BROWN, COLOUR_DARK_BROWN ) },
{ WC_WATER, "water", 5194, COLOURS_3(COLOUR_DARK_BROWN, COLOUR_DARK_BROWN, COLOUR_DARK_BROWN ) },
{ WC_PEEP, "guest", 5205, COLOURS_3(COLOUR_GREY, COLOUR_OLIVE_GREEN, COLOUR_OLIVE_GREEN ) },
{ WC_GUEST_LIST, "guest_list", 5206, COLOURS_3(COLOUR_GREY, COLOUR_OLIVE_GREEN, COLOUR_OLIVE_GREEN ) },
{ WC_STAFF_LIST, "staff_list", 5208, COLOURS_3(COLOUR_GREY, COLOUR_LIGHT_PURPLE, COLOUR_LIGHT_PURPLE ) },
{ WC_FIRE_PROMPT, "staff_fire_prompt", 5225, COLOURS_1(TRANSLUCENT(COLOUR_BORDEAUX_RED) ) },
{ WC_PARK_INFORMATION, "park_information", 5253, COLOURS_3(COLOUR_GREY, COLOUR_DARK_YELLOW, COLOUR_DARK_YELLOW ) },
{ WC_FINANCES, "finances", 5187, COLOURS_3(COLOUR_GREY, COLOUR_DARK_YELLOW, COLOUR_DARK_YELLOW ) },
{ WC_TITLE_MENU, "title_menu", 5249, COLOURS_3(TRANSLUCENT(COLOUR_DARK_GREEN), TRANSLUCENT(COLOUR_DARK_GREEN), TRANSLUCENT(COLOUR_DARK_GREEN) ) },
{ WC_TITLE_EXIT, "title_exit", 5250, COLOURS_3(TRANSLUCENT(COLOUR_DARK_GREEN), TRANSLUCENT(COLOUR_DARK_GREEN), TRANSLUCENT(COLOUR_DARK_GREEN) ) },
{ WC_RECENT_NEWS, "recent_news", 5192, COLOURS_3(COLOUR_GREY, COLOUR_GREY, COLOUR_BLACK ) },
{ WC_SCENARIO_SELECT, "scenario_select", 5252, COLOURS_3(COLOUR_GREY, COLOUR_BORDEAUX_RED, COLOUR_BORDEAUX_RED ) },
{ WC_TRACK_DESIGN_LIST, "track_design_list", 5202, COLOURS_3(COLOUR_BORDEAUX_RED, COLOUR_BORDEAUX_RED, COLOUR_BORDEAUX_RED ) },
{ WC_TRACK_DESIGN_PLACE, "track_design_place", 5200, COLOURS_3(COLOUR_DARK_BROWN, COLOUR_DARK_BROWN, COLOUR_DARK_BROWN ) },
{ WC_NEW_CAMPAIGN, "new_campaign", 5188, COLOURS_3(COLOUR_DARK_YELLOW, COLOUR_DARK_YELLOW, COLOUR_DARK_YELLOW ) },
{ WC_KEYBOARD_SHORTCUT_LIST, "keyboard_shortcuts", 5220, COLOURS_3(COLOUR_LIGHT_BLUE, COLOUR_LIGHT_BLUE, COLOUR_LIGHT_BLUE ) },
{ WC_CHANGE_KEYBOARD_SHORTCUT, "change_keyboard_shortcut", 5221, COLOURS_3(COLOUR_LIGHT_BLUE, COLOUR_LIGHT_BLUE, COLOUR_LIGHT_BLUE ) },
{ WC_MAP, "map", 5190, COLOURS_2(COLOUR_DARK_GREEN, COLOUR_DARK_BROWN ) },
{ WC_BANNER, "banner", 5209, COLOURS_3(COLOUR_DARK_BROWN, COLOUR_DARK_BROWN, COLOUR_DARK_BROWN ) },
{ WC_EDITOR_OBJECT_SELECTION, "editor_object_selection", 5210, COLOURS_3(COLOUR_LIGHT_PURPLE, COLOUR_GREY, COLOUR_GREY ) },
{ WC_EDITOR_INVENTION_LIST, "editor_invention_list", 5211, COLOURS_3(COLOUR_LIGHT_PURPLE, COLOUR_GREY, COLOUR_GREY ) },
{ WC_EDITOR_SCENARIO_OPTIONS, "editor_scenario_options", 5212, COLOURS_3(COLOUR_LIGHT_PURPLE, COLOUR_GREY, COLOUR_GREY ) },
{ WC_EDTIOR_OBJECTIVE_OPTIONS, "editor_objection_options", 5213, COLOURS_3(COLOUR_LIGHT_PURPLE, COLOUR_GREY, COLOUR_GREY ) },
{ WC_MANAGE_TRACK_DESIGN, "manage_track_design", 5215, COLOURS_3(COLOUR_GREY, COLOUR_GREY, COLOUR_GREY ) },
{ WC_TRACK_DELETE_PROMPT, "track_delete_prompt", 5226, COLOURS_3(COLOUR_BORDEAUX_RED, COLOUR_BORDEAUX_RED, COLOUR_BORDEAUX_RED ) },
{ WC_INSTALL_TRACK, "install_track", 5216, COLOURS_3(COLOUR_BORDEAUX_RED, COLOUR_BORDEAUX_RED, COLOUR_BORDEAUX_RED ) },
{ WC_CLEAR_SCENERY, "clear_scenery", 5195, COLOURS_3(COLOUR_DARK_BROWN, COLOUR_DARK_BROWN, COLOUR_DARK_BROWN ) },
{ WC_CHEATS, "cheats", 5217, COLOURS_3(COLOUR_GREY, COLOUR_DARK_YELLOW, COLOUR_DARK_YELLOW ) },
{ WC_RESEARCH, "research", 5189, COLOURS_3(COLOUR_GREY, COLOUR_DARK_YELLOW, COLOUR_DARK_YELLOW ) },
{ WC_VIEWPORT, "viewport", 5191, COLOURS_3(COLOUR_DARK_BROWN, COLOUR_DARK_BROWN, COLOUR_DARK_BROWN ) },
{ WC_MAPGEN, "map_generation", 5214, COLOURS_3(COLOUR_DARK_GREEN, COLOUR_DARK_BROWN, COLOUR_DARK_BROWN ) },
{ WC_LOADSAVE, "loadsave", 5222, COLOURS_3(COLOUR_LIGHT_BLUE, COLOUR_LIGHT_BLUE, COLOUR_LIGHT_BLUE ) },
{ WC_LOADSAVE_OVERWRITE_PROMPT, "loadsave_overwrite_prompt", 5227, COLOURS_1(TRANSLUCENT(COLOUR_BORDEAUX_RED) ) },
{ WC_TITLE_OPTIONS, "title_options", 5251, COLOURS_3(TRANSLUCENT(COLOUR_DARK_GREEN), TRANSLUCENT(COLOUR_DARK_GREEN), TRANSLUCENT(COLOUR_DARK_GREEN) ) },
{ WC_LAND_RIGHTS, "land_rights", 5196, COLOURS_3(COLOUR_DARK_YELLOW, COLOUR_DARK_YELLOW, COLOUR_DARK_YELLOW ) },
{ WC_THEMES, "themes", 5218, COLOURS_3(COLOUR_GREY, COLOUR_DARK_GREEN, COLOUR_DARK_GREEN ) },
{ WC_STAFF, "staff", 5207, COLOURS_3(COLOUR_GREY, COLOUR_LIGHT_PURPLE, COLOUR_LIGHT_PURPLE ) },
{ WC_EDITOR_TRACK_BOTTOM_TOOLBAR, "editor_track_bottom_toolbar", 5247, COLOURS_3(TRANSLUCENT(COLOUR_LIGHT_BLUE), TRANSLUCENT(COLOUR_LIGHT_BLUE), TRANSLUCENT(COLOUR_LIGHT_BLUE) ) },
{ WC_EDITOR_SCENARIO_BOTTOM_TOOLBAR, "editor_scenario_bottom_toolbar", 5248, COLOURS_3(TRANSLUCENT(COLOUR_LIGHT_BROWN), TRANSLUCENT(COLOUR_LIGHT_BROWN), TRANSLUCENT(COLOUR_MOSS_GREEN) ) },
{ WC_TITLE_EDITOR, "title_sequences", 5433, COLOURS_3(COLOUR_GREY, COLOUR_OLIVE_GREEN, COLOUR_OLIVE_GREEN ) },
};
#define COLOURS_RCT1(c0, c1, c2, c3, c4, c5) { { (c0), (c1), (c2), (c3), (c4), (c5) } }
theme_window_preset gThemeWindowsRCT1[] = {
{ WC_TOP_TOOLBAR, COLOURS_RCT1(COLOUR_GREY, COLOUR_GREY, COLOUR_GREY, COLOUR_GREY, COLOUR_BLACK, COLOUR_BLACK) },
{ WC_BOTTOM_TOOLBAR, COLOURS_RCT1(TRANSLUCENT(COLOUR_GREY), TRANSLUCENT(COLOUR_GREY), COLOUR_BLACK, COLOUR_YELLOW, COLOUR_BLACK, COLOUR_BLACK) },
{ WC_RIDE, COLOURS_RCT1(COLOUR_BORDEAUX_RED, COLOUR_GREY, COLOUR_SATURATED_GREEN, COLOUR_BLACK, COLOUR_BLACK, COLOUR_BLACK) },
{ WC_RIDE_LIST, COLOURS_RCT1(COLOUR_BORDEAUX_RED, COLOUR_GREY, COLOUR_GREY, COLOUR_BLACK, COLOUR_BLACK, COLOUR_BLACK) },
{ WC_CONSTRUCT_RIDE, COLOURS_RCT1(COLOUR_BORDEAUX_RED, COLOUR_GREY, COLOUR_GREY, COLOUR_BLACK, COLOUR_BLACK, COLOUR_BLACK) },
{ WC_PEEP, COLOURS_RCT1(COLOUR_LIGHT_BROWN, COLOUR_BORDEAUX_RED, COLOUR_BORDEAUX_RED, COLOUR_BLACK, COLOUR_BLACK, COLOUR_BLACK) },
{ WC_GUEST_LIST, COLOURS_RCT1(COLOUR_LIGHT_BROWN, COLOUR_BORDEAUX_RED, COLOUR_BORDEAUX_RED, COLOUR_BLACK, COLOUR_BLACK, COLOUR_BLACK) },
{ WC_STAFF_LIST, COLOURS_RCT1(COLOUR_DARK_GREEN, COLOUR_LIGHT_PURPLE, COLOUR_LIGHT_PURPLE, COLOUR_BLACK, COLOUR_BLACK, COLOUR_BLACK) },
{ WC_FINANCES, COLOURS_RCT1(COLOUR_LIGHT_PURPLE, COLOUR_GREY, COLOUR_GREY, COLOUR_BLACK, COLOUR_BLACK, COLOUR_BLACK) },
{ WC_TITLE_MENU, COLOURS_RCT1(TRANSLUCENT(COLOUR_GREY), TRANSLUCENT(COLOUR_GREY), TRANSLUCENT(COLOUR_GREY), COLOUR_BLACK, COLOUR_BLACK, COLOUR_BLACK) },
{ WC_TITLE_EXIT, COLOURS_RCT1(TRANSLUCENT(COLOUR_GREY), TRANSLUCENT(COLOUR_GREY), TRANSLUCENT(COLOUR_GREY), COLOUR_BLACK, COLOUR_BLACK, COLOUR_BLACK) },
{ WC_NEW_CAMPAIGN, COLOURS_RCT1(COLOUR_LIGHT_PURPLE, COLOUR_LIGHT_PURPLE, COLOUR_GREY, COLOUR_BLACK, COLOUR_BLACK, COLOUR_BLACK) },
{ WC_TITLE_OPTIONS, COLOURS_RCT1(TRANSLUCENT(COLOUR_GREY), TRANSLUCENT(COLOUR_GREY), TRANSLUCENT(COLOUR_GREY), COLOUR_BLACK, COLOUR_BLACK, COLOUR_BLACK) },
{ WC_STAFF, COLOURS_RCT1(COLOUR_DARK_GREEN, COLOUR_LIGHT_PURPLE, COLOUR_LIGHT_PURPLE, COLOUR_BLACK, COLOUR_BLACK, COLOUR_BLACK) },
THEME_DEF_END
};
uint16 gCurrentTheme = 0;
uint32 gNumThemeWindows = sizeof(gThemeWindowDefinitions) / sizeof(theme_window_definition);
theme_preset* theme_get_preset()
{
return &gConfigThemes.presets[gCurrentTheme];
}
theme_window_definition* theme_window_definition_get_by_class(rct_windowclass classification)
{
for (int i = 0; i < (int)gNumThemeWindows; i++) {
if (gThemeWindowDefinitions[i].classification == classification) {
return &gThemeWindowDefinitions[i];
}
}
return NULL;
}
theme_window* theme_window_get_by_class(rct_windowclass classification)
{
for (int i = 0; i < (int)gNumThemeWindows; i++) {
if (gThemeWindowDefinitions[i].classification == classification) {
return &gConfigThemes.presets[gCurrentTheme].windows[i];
}
}
return NULL;
}
void colour_scheme_update(rct_window *window)
{
theme_window* theme = theme_window_get_by_class(window->classification);
bool transparent = false;
for (int i = 0; i < 6; i++) {
window->colours[i] = theme->colours[i];
if (theme->colours[i] & 0x80) {
transparent = true;
}
}
// Some windows need to be transparent even if the colours aren't.
// There doesn't seem to be any side-effects for all windows being transparent
window->flags |= WF_TRANSPARENT;
}
void colour_scheme_update_by_class(rct_window *window, rct_windowclass classification)
{
theme_window* theme = theme_window_get_by_class(classification);
bool transparent = false;
for (int i = 0; i < 6; i++) {
window->colours[i] = theme->colours[i];
if (theme->colours[i] & 0x80) {
transparent = true;
}
}
// Some windows need to be transparent even if the colours aren't.
// There doesn't seem to be any side-effects for all windows being transparent
window->flags |= WF_TRANSPARENT;
}
static void theme_set_preset_string(const utf8string preset)
{
SafeFree(gConfigInterface.current_theme_preset);
gConfigInterface.current_theme_preset = _strdup(preset);
}
void theme_change_preset(int preset)
{
if (preset >= 0 && preset < gConfigThemes.num_presets) {
switch (preset) {
case 0:
theme_set_preset_string("*RCT2");
break;
case 1:
theme_set_preset_string("*RCT1");
break;
default:
theme_set_preset_string(gConfigThemes.presets[preset].name);
break;
}
gCurrentTheme = preset;
}
window_invalidate_all();
}
void theme_create_preset(int duplicate, const char *name)
{
int preset = gConfigThemes.num_presets;
gConfigThemes.num_presets++;
gConfigThemes.presets = realloc(gConfigThemes.presets, sizeof(theme_preset) * gConfigThemes.num_presets);
safe_strcpy(gConfigThemes.presets[preset].name, name, THEME_PRESET_NAME_SIZE);
gConfigThemes.presets[preset].windows = malloc(sizeof(theme_window) * gNumThemeWindows);
for (int i = 0; i < (int)gNumThemeWindows; i++) {
gConfigThemes.presets[preset].windows[i] = gConfigThemes.presets[duplicate].windows[i];
}
gConfigThemes.presets[preset].features = gConfigThemes.presets[duplicate].features;
themes_save_preset(preset);
theme_change_preset(preset);
}
void theme_delete_preset(int preset)
{
if (preset >= 2) {
utf8 path[MAX_PATH];
platform_get_user_directory(path, "themes");
strcat(path, gConfigThemes.presets[preset].name);
strcat(path, ".ini");
platform_file_delete(path);
free(gConfigThemes.presets[preset].windows);
for (int i = preset; i < gConfigThemes.num_presets - 1; i++) {
gConfigThemes.presets[i] = gConfigThemes.presets[i + 1];
}
gConfigThemes.num_presets--;
theme_change_preset(0);
}
}
void theme_rename_preset(int preset, const char *newName)
{
if (preset >= 2) {
utf8 src[MAX_PATH], dest[MAX_PATH];
platform_get_user_directory(src, "themes");
platform_get_user_directory(dest, "themes");
strcat(src, gConfigThemes.presets[preset].name);
strcat(dest, newName);
strcat(src, ".ini");
strcat(dest, ".ini");
platform_file_move(src, dest);
safe_strcpy(gConfigThemes.presets[preset].name, newName, THEME_PRESET_NAME_SIZE);
if (preset == gCurrentTheme) {
gConfigInterface.current_theme_preset = gConfigThemes.presets[preset].name;
}
}
}

View File

@ -18,47 +18,40 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#ifndef _COLOUR_SCHEMES_H_
#define _COLOUR_SCHEMES_H_
#ifndef _THEMES_H_
#define _THEMES_H_
#include "../common.h"
#include "window.h"
#include "../config.h"
typedef struct {
rct_windowclass classification;
char *section_name;
rct_string_id name;
uint8 num_colours;
theme_window window;
} theme_window_definition;
typedef struct {
rct_windowclass classification;
theme_window window;
} theme_window_preset;
// The definitions for window themes as well as the RCT2 preset
extern theme_window_definition gThemeWindowDefinitions[];
// The preset for RCT1 window themes
extern theme_window_preset gThemeWindowsRCT1[];
// The index of the current theme
extern uint16 gCurrentTheme;
// The number of theme-able windows
extern uint32 gNumThemeWindows;
theme_preset* theme_get_preset();
theme_window_definition* theme_window_definition_get_by_class(rct_windowclass classification);
theme_window* theme_window_get_by_class(rct_windowclass classification);
enum {
UITHEME_FLAG_PREDEFINED = 1 << 0,
UITHEME_FLAG_USE_LIGHTS_RIDE = 1 << 1,
UITHEME_FLAG_USE_LIGHTS_PARK = 1 << 2,
UITHEME_FLAG_USE_ALTERNATIVE_SCENARIO_SELECT_FONT = 1 << 3,
};
void colour_scheme_update(rct_window *window);
void colour_scheme_update_by_class(rct_window *window, rct_windowclass classification);
void theme_change_preset(int preset);
void theme_create_preset(int duplicate, const char *name);
void theme_delete_preset(int preset);
void theme_rename_preset(int preset, const char *newName);
void theme_manager_initialise();
void theme_manager_load_available_themes();
size_t theme_manager_get_num_available_themes();
const utf8 * theme_manager_get_available_theme_path(size_t index);
const utf8 * theme_manager_get_available_theme_name(size_t index);
size_t theme_manager_get_active_available_theme_index();
void theme_manager_set_active_available_theme(size_t index);
colour_t theme_get_colour(rct_windowclass wc, uint8 index);
void theme_set_colour(rct_windowclass wc, uint8 index, colour_t colour);
uint8 theme_get_flags();
void theme_set_flags(uint8 flags);
void theme_save();
void theme_rename(const utf8 * name);
void theme_duplicate(const utf8 * name);
void theme_delete();
uint8 theme_desc_get_num_colours(rct_windowclass wc);
rct_string_id theme_desc_get_name(rct_windowclass wc);
#endif

View File

@ -662,8 +662,10 @@ void format_string_code(unsigned int format_code, char **dest, char **args)
value = *((uint32*)*args);
*args += 4;
strcpy(*dest, (char*)value);
*dest += strlen(*dest);
if (value != 0) {
strcpy(*dest, (char*)value);
*dest += strlen(*dest);
}
break;
case FORMAT_MONTHYEAR:
// Pop argument

View File

@ -26,6 +26,7 @@
#include "game.h"
#include "hook.h"
#include "interface/chat.h"
#include "interface/themes.h"
#include "interface/window.h"
#include "interface/viewport.h"
#include "localisation/localisation.h"
@ -229,8 +230,7 @@ bool openrct2_initialise()
}
http_init();
themes_set_default();
themes_load_presets();
theme_manager_initialise();
title_sequences_set_default();
title_sequences_load_presets();

View File

@ -59,6 +59,7 @@ typedef wchar_t utf16;
typedef utf16* utf16string;
typedef uint32 codepoint_t;
typedef uint8 colour_t;
#define rol8(x, shift) (((uint8)(x) << (shift)) | ((uint8)(x) >> (8 - (shift))))
#define ror8(x, shift) (((uint8)(x) >> (shift)) | ((uint8)(x) << (8 - (shift))))

View File

@ -1,14 +1,15 @@
#include "../addresses.h"
#include "../localisation/localisation.h"
#include "../interface/themes.h"
#include "../interface/widget.h"
#include "../interface/window.h"
#include "../interface/viewport.h"
#include "../world/scenery.h"
#include "../world/map.h"
#include "../world/footpath.h"
#include "../util/util.h"
#include "../openrct2.h"
#include "../platform/platform.h"
#include "../util/util.h"
#include "../world/footpath.h"
#include "../world/map.h"
#include "../world/scenery.h"
enum {
WIDX_BACKGROUND,
@ -105,9 +106,6 @@ rct_window *window_changelog_open()
window->enabled_widgets = (1 << WIDX_CLOSE);
window_init_scroll_widgets(window);
window->colours[0] = 7;
window->colours[1] = 7;
window->colours[2] = 7;
window->min_width = MIN_WW;
window->min_height = MIN_WH;
window->max_width = MIN_WW;
@ -157,6 +155,8 @@ static void window_changelog_scrollgetsize(rct_window *w, int scrollIndex, int *
static void window_changelog_invalidate(rct_window *w)
{
colour_scheme_update(w);
window_changelog_widgets[WIDX_BACKGROUND].right = w->width - 1;
window_changelog_widgets[WIDX_BACKGROUND].bottom = w->height - 1;
window_changelog_widgets[WIDX_TITLE].right = w->width - 2;

View File

@ -21,6 +21,7 @@
#include "../addresses.h"
#include "../audio/audio.h"
#include "../config.h"
#include "../game.h"
#include "../interface/themes.h"
#include "../interface/widget.h"
@ -33,10 +34,10 @@
#include "../ride/ride_data.h"
#include "../ride/track.h"
#include "../scenario.h"
#include "dropdown.h"
#include "error.h"
#include "../util/util.h"
#include "../world/footpath.h"
#include "dropdown.h"
#include "error.h"
enum {
FILTER_RCT2 = (1 << 0),

View File

@ -205,9 +205,6 @@ void window_multiplayer_open()
window->page = WINDOW_MULTIPLAYER_PAGE_PLAYERS;
window->list_information_type = 0;
window->colours[0] = 7;
window->colours[1] = 7;
window->colours[2] = 7;
}
static void window_multiplayer_set_page(rct_window* w, int page){
@ -366,6 +363,8 @@ static void window_multiplayer_players_scrollmouseover(rct_window *w, int scroll
static void window_multiplayer_players_invalidate(rct_window *w)
{
colour_scheme_update(w);
window_multiplayer_set_pressed_tab(w);
window_multiplayer_anchor_border_widgets(w);
window_multiplayer_players_widgets[WIDX_LIST].right = w->width - 4;

View File

@ -106,9 +106,6 @@ void window_network_status_open(const char* text)
window->page = 0;
window->list_information_type = 0;
window->colours[0] = 7;
window->colours[1] = 7;
window->colours[2] = 7;
}
void window_network_status_close()
@ -158,6 +155,8 @@ static void window_network_status_textinput(rct_window *w, int widgetIndex, char
static void window_network_status_invalidate(rct_window *w)
{
colour_scheme_update(w);
window_network_status_widgets[WIDX_BACKGROUND].right = w->width - 1;
window_network_status_widgets[WIDX_BACKGROUND].bottom = w->height - 1;
window_network_status_widgets[WIDX_TITLE].right = w->width - 2;

View File

@ -989,16 +989,12 @@ static void window_options_mousedown(int widgetIndex, rct_window*w, rct_widget*
case WINDOW_OPTIONS_PAGE_CONTROLS_AND_INTERFACE:
switch (widgetIndex) {
case WIDX_THEMES_DROPDOWN:
num_items = gConfigThemes.num_presets;
theme_manager_get_num_available_themes();
num_items = (int)theme_manager_get_num_available_themes();
gDropdownItemsFormat[0] = 2777;
gDropdownItemsArgs[0] = (uint32)&gConfigThemes.presets[1].name;
gDropdownItemsFormat[1] = 2777;
gDropdownItemsArgs[1] = (uint32)&gConfigThemes.presets[0].name;
for (i = 2; i < num_items; i++) {
for (int i = 0; i < num_items; i++) {
gDropdownItemsFormat[i] = 2777;
gDropdownItemsArgs[i] = (uint32)&gConfigThemes.presets[i].name;
gDropdownItemsArgs[i] = (uint32)theme_manager_get_available_theme_name(i);
}
window_dropdown_show_text_custom_width(
@ -1009,13 +1005,10 @@ static void window_options_mousedown(int widgetIndex, rct_window*w, rct_widget*
DROPDOWN_FLAG_STAY_OPEN,
num_items,
widget->right - widget->left - 3
);
);
if (gCurrentTheme == 0 || gCurrentTheme == 1) {
dropdown_set_checked(gCurrentTheme ^ 1, true);
} else {
dropdown_set_checked(gCurrentTheme, true);
}
dropdown_set_checked(theme_manager_get_active_available_theme_index(), true);
widget_invalidate(w, WIDX_THEMES_DROPDOWN);
break;
case WIDX_SCENARIO_GROUPING_DROPDOWN:
@ -1243,9 +1236,7 @@ static void window_options_dropdown(rct_window *w, int widgetIndex, int dropdown
switch (widgetIndex) {
case WIDX_THEMES_DROPDOWN:
if (dropdownIndex != -1) {
if (dropdownIndex == 0 || dropdownIndex == 1)
dropdownIndex ^= 1;
theme_change_preset(dropdownIndex);
theme_manager_set_active_available_theme(dropdownIndex);
}
config_save_default();
break;
@ -1642,7 +1633,11 @@ static void window_options_paint(rct_window *w, rct_drawpixelinfo *dpi)
break;
case WINDOW_OPTIONS_PAGE_CONTROLS_AND_INTERFACE:
gfx_draw_string_left(dpi, STR_SHOW_TOOLBAR_BUTTONS_FOR, w, w->colours[1], w->x + 10, w->y + window_options_controls_and_interface_widgets[WIDX_TOOLBAR_BUTTONS_GROUP].top + 15);
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 0, uint32) = (uint32)&gConfigThemes.presets[gCurrentTheme].name;
int activeAvailableThemeIndex = theme_manager_get_active_available_theme_index();
const utf8 * activeThemeName = theme_manager_get_available_theme_name(activeAvailableThemeIndex);
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 0, uint32) = (uint32)activeThemeName;
gfx_draw_string_left(dpi, 5238, NULL, w->colours[1], w->x + 10, w->y + window_options_controls_and_interface_widgets[WIDX_THEMES].top + 1);
gfx_draw_string_left_clipped(
dpi,

View File

@ -979,7 +979,7 @@ static void window_park_entrance_invalidate(rct_window *w)
window_park_entrance_widgets[WIDX_STATUS].top = w->height - 13;
window_park_entrance_widgets[WIDX_STATUS].bottom = w->height - 3;
if (theme_get_preset()->features.rct1_park_lights) {
if (theme_get_flags() & UITHEME_FLAG_USE_LIGHTS_PARK) {
window_park_entrance_widgets[WIDX_OPEN_OR_CLOSE].type = WWT_EMPTY;
if (RCT2_GLOBAL(RCT2_ADDRESS_OBJECTIVE_TYPE, uint8) == OBJECTIVE_GUESTS_AND_RATING) {
window_park_entrance_widgets[WIDX_CLOSE_LIGHT].type = WWT_FLATBTN;

View File

@ -376,9 +376,7 @@ void window_player_overview_invalidate(rct_window *w)
window_init_scroll_widgets(w);
}
w->colours[0] = 7;
w->colours[1] = 7;
w->colours[2] = 7;
colour_scheme_update(w);
w->pressed_widgets &= ~(WIDX_TAB_1);
w->pressed_widgets |= 1ULL << (w->page + WIDX_TAB_1);

View File

@ -1696,7 +1696,7 @@ static void window_ride_main_resize(rct_window *w)
w->flags |= WF_RESIZABLE;
int minHeight = 180;
if (theme_get_preset()->features.rct1_ride_lights)
if (theme_get_flags() & UITHEME_FLAG_USE_LIGHTS_RIDE)
minHeight = 200 + RCT1_LIGHT_OFFSET - (ride_type_has_flag(get_ride(w->number)->type, RIDE_TYPE_FLAG_NO_TEST_MODE) ? 14 : 0);
window_set_resize(w, 316, minHeight, 500, 450);
@ -2037,7 +2037,7 @@ static void window_ride_main_invalidate(rct_window *w)
window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_10);
if (theme_get_preset()->features.rct1_ride_lights) {
if (theme_get_flags() & UITHEME_FLAG_USE_LIGHTS_RIDE) {
window_ride_main_widgets[WIDX_OPEN].type = WWT_EMPTY;
window_ride_main_widgets[WIDX_CLOSE_LIGHT].type = WWT_IMGBTN;
window_ride_main_widgets[WIDX_TEST_LIGHT].type = (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_NO_TEST_MODE) ? WWT_EMPTY : WWT_IMGBTN);

View File

@ -21,6 +21,7 @@
#include "../addresses.h"
#include "../audio/audio.h"
#include "../cheats.h"
#include "../config.h"
#include "../drawing/drawing.h"
#include "../game.h"
#include "../input.h"

View File

@ -403,7 +403,7 @@ static void window_ride_list_invalidate(rct_window *w)
w->widgets[WIDX_OPEN_LIGHT].right = w->width - 7;
w->widgets[WIDX_OPEN_LIGHT].left = w->width - 20;
if (theme_get_preset()->features.rct1_ride_lights) {
if (theme_get_flags() & UITHEME_FLAG_USE_LIGHTS_RIDE) {
w->widgets[WIDX_OPEN_CLOSE_ALL].type = WWT_EMPTY;
w->widgets[WIDX_CLOSE_LIGHT].type = WWT_IMGBTN;
w->widgets[WIDX_OPEN_LIGHT].type = WWT_IMGBTN;

View File

@ -18,6 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#include "../config.h"
#include "../interface/colour.h"
#include "../interface/themes.h"
#include "../interface/widget.h"
@ -366,6 +367,8 @@ static void window_server_list_textinput(rct_window *w, int widgetIndex, char *t
static void window_server_list_invalidate(rct_window *w)
{
colour_scheme_update(w);
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS, char *) = gVersion;
window_server_list_widgets[WIDX_BACKGROUND].right = w->width - 1;
window_server_list_widgets[WIDX_BACKGROUND].bottom = w->height - 1;

View File

@ -136,9 +136,6 @@ void window_server_start_open()
window->page = 0;
window->list_information_type = 0;
window->colours[0] = 1;
window->colours[1] = 26;
window->colours[2] = 26;
sprintf(_port, "%u", gConfigNetwork.default_port);
safe_strcpy(_name, gConfigNetwork.server_name, sizeof(_name));
@ -251,6 +248,8 @@ static void window_server_start_textinput(rct_window *w, int widgetIndex, char *
static void window_server_start_invalidate(rct_window *w)
{
colour_scheme_update_by_class(w, WC_SERVER_LIST);
widget_set_checkbox_value(w, WIDX_ADVERTISE_CHECKBOX, gConfigNetwork.advertise);
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 18, uint16) = gConfigNetwork.maxplayers;
}

View File

@ -238,12 +238,17 @@ static rct_windowclass window_themes_tab_5_classes[] = {
static rct_windowclass window_themes_tab_6_classes[] = {
WC_CHEATS,
WC_TILE_INSPECTOR,
WC_THEMES,
WC_TITLE_EDITOR,
WC_OPTIONS,
WC_KEYBOARD_SHORTCUT_LIST,
WC_CHANGE_KEYBOARD_SHORTCUT,
WC_LOADSAVE
WC_LOADSAVE,
WC_CHANGELOG,
WC_SERVER_LIST,
WC_MULTIPLAYER,
WC_PLAYER,
};
static rct_windowclass window_themes_tab_7_classes[] = {
@ -251,7 +256,19 @@ static rct_windowclass window_themes_tab_7_classes[] = {
WC_DEMOLISH_RIDE_PROMPT,
WC_FIRE_PROMPT,
WC_TRACK_DELETE_PROMPT,
WC_LOADSAVE_OVERWRITE_PROMPT
WC_LOADSAVE_OVERWRITE_PROMPT,
WC_NETWORK_STATUS,
};
static rct_windowclass *window_themes_tab_classes[] = {
NULL,
window_themes_tab_1_classes,
window_themes_tab_2_classes,
window_themes_tab_3_classes,
window_themes_tab_4_classes,
window_themes_tab_5_classes,
window_themes_tab_6_classes,
window_themes_tab_7_classes,
};
static uint8 _selected_tab = 0;
@ -267,57 +284,10 @@ void window_themes_init_vars()
_selected_tab = WINDOW_THEMES_TAB_SETTINGS;
}
static theme_window_definition* get_colour_scheme_tab_definition()
static rct_windowclass get_window_class_tab_index(int index)
{
switch (_selected_tab) {
case 1: return theme_window_definition_get_by_class(window_themes_tab_1_classes[_color_index_1]);
case 2: return theme_window_definition_get_by_class(window_themes_tab_2_classes[_color_index_1]);
case 3: return theme_window_definition_get_by_class(window_themes_tab_3_classes[_color_index_1]);
case 4: return theme_window_definition_get_by_class(window_themes_tab_4_classes[_color_index_1]);
case 5: return theme_window_definition_get_by_class(window_themes_tab_5_classes[_color_index_1]);
case 6: return theme_window_definition_get_by_class(window_themes_tab_6_classes[_color_index_1]);
case 7: return theme_window_definition_get_by_class(window_themes_tab_7_classes[_color_index_1]);
}
return NULL;
}
static theme_window_definition* get_colour_scheme_tab_definition_by_index(int index)
{
switch (_selected_tab) {
case 1: return theme_window_definition_get_by_class(window_themes_tab_1_classes[index]);
case 2: return theme_window_definition_get_by_class(window_themes_tab_2_classes[index]);
case 3: return theme_window_definition_get_by_class(window_themes_tab_3_classes[index]);
case 4: return theme_window_definition_get_by_class(window_themes_tab_4_classes[index]);
case 5: return theme_window_definition_get_by_class(window_themes_tab_5_classes[index]);
case 6: return theme_window_definition_get_by_class(window_themes_tab_6_classes[index]);
case 7: return theme_window_definition_get_by_class(window_themes_tab_7_classes[index]);
}
return NULL;
}
static theme_window* get_colour_scheme_tab()
{
switch (_selected_tab) {
case 1: return theme_window_get_by_class(window_themes_tab_1_classes[_color_index_1]);
case 2: return theme_window_get_by_class(window_themes_tab_2_classes[_color_index_1]);
case 3: return theme_window_get_by_class(window_themes_tab_3_classes[_color_index_1]);
case 4: return theme_window_get_by_class(window_themes_tab_4_classes[_color_index_1]);
case 5: return theme_window_get_by_class(window_themes_tab_5_classes[_color_index_1]);
case 6: return theme_window_get_by_class(window_themes_tab_6_classes[_color_index_1]);
case 7: return theme_window_get_by_class(window_themes_tab_7_classes[_color_index_1]);
}
return NULL;
}
static theme_window* get_colour_scheme_tab_by_index(int index)
{
switch (_selected_tab) {
case 1: return theme_window_get_by_class(window_themes_tab_1_classes[index]);
case 2: return theme_window_get_by_class(window_themes_tab_2_classes[index]);
case 3: return theme_window_get_by_class(window_themes_tab_3_classes[index]);
case 4: return theme_window_get_by_class(window_themes_tab_4_classes[index]);
case 5: return theme_window_get_by_class(window_themes_tab_5_classes[index]);
case 6: return theme_window_get_by_class(window_themes_tab_6_classes[index]);
case 7: return theme_window_get_by_class(window_themes_tab_7_classes[index]);
}
return NULL;
rct_windowclass * classes = window_themes_tab_classes[_selected_tab];
return classes[index];
}
static int get_colour_scheme_tab_count()
@ -334,19 +304,6 @@ static int get_colour_scheme_tab_count()
return 0;
}
/*static int get_colour_scheme_index() {
switch (_selected_tab) {
case 1: return theme_window_get_index_by_class(window_themes_tab_1_classes[_color_index_1]);
case 2: return theme_window_get_index_by_class(window_themes_tab_2_classes[_color_index_1]);
case 3: return theme_window_get_index_by_class(window_themes_tab_3_classes[_color_index_1]);
case 4: return theme_window_get_index_by_class(window_themes_tab_4_classes[_color_index_1]);
case 5: return theme_window_get_index_by_class(window_themes_tab_5_classes[_color_index_1]);
case 6: return theme_window_get_index_by_class(window_themes_tab_6_classes[_color_index_1]);
case 7: return theme_window_get_index_by_class(window_themes_tab_7_classes[_color_index_1]);
}
return -1;
}*/
static void window_themes_draw_tab_images(rct_drawpixelinfo *dpi, rct_window *w)
{
int sprite_idx;
@ -405,27 +362,32 @@ void window_themes_open()
static void window_themes_mouseup(rct_window *w, int widgetIndex)
{
int activeAvailableThemeIndex;
const utf8 * activeThemeName;
switch (widgetIndex) {
case WIDX_THEMES_CLOSE:
window_close(w);
break;
case WIDX_THEMES_DUPLICATE_BUTTON:
window_text_input_open(w, widgetIndex, 5239, 5240, 1170, (uint32)&gConfigThemes.presets[gCurrentTheme].name, 64);
case WIDX_THEMES_DUPLICATE_BUTTON:;
activeAvailableThemeIndex = theme_manager_get_active_available_theme_index();
activeThemeName = theme_manager_get_available_theme_name(activeAvailableThemeIndex);
window_text_input_open(w, widgetIndex, 5239, 5240, 1170, (uint32)activeThemeName, 64);
break;
case WIDX_THEMES_DELETE_BUTTON:
if (gCurrentTheme >= 2) {
theme_delete_preset(gCurrentTheme);
}
else {
if (theme_get_flags() & UITHEME_FLAG_PREDEFINED) {
window_error_open(5241, STR_NONE);
} else {
theme_delete();
}
break;
case WIDX_THEMES_RENAME_BUTTON:
if (gCurrentTheme >= 2) {
window_text_input_open(w, widgetIndex, 3348, 5240, 1170, (uint32)&gConfigThemes.presets[gCurrentTheme].name, 64);
}
else {
if (theme_get_flags() & UITHEME_FLAG_PREDEFINED) {
window_error_open(5241, STR_NONE);
} else {
activeAvailableThemeIndex = theme_manager_get_active_available_theme_index();
activeThemeName = theme_manager_get_available_theme_name(activeAvailableThemeIndex);
window_text_input_open(w, widgetIndex, 3348, 5240, 1170, (uint32)activeThemeName, 64);
}
break;
}
@ -507,7 +469,7 @@ static void window_themes_resize(rct_window *w)
static void window_themes_mousedown(int widgetIndex, rct_window* w, rct_widget* widget)
{
short newSelectedTab;
int num_items, i;
int num_items;
switch (widgetIndex) {
case WIDX_THEMES_SETTINGS_TAB:
@ -529,19 +491,15 @@ static void window_themes_mousedown(int widgetIndex, rct_window* w, rct_widget*
window_invalidate(w);
break;
case WIDX_THEMES_PRESETS_DROPDOWN:
num_items = gConfigThemes.num_presets;
theme_manager_load_available_themes();
num_items = (int)theme_manager_get_num_available_themes();
widget--;
gDropdownItemsFormat[0] = 2777;
gDropdownItemsArgs[0] = (uint32)&gConfigThemes.presets[1].name;
gDropdownItemsFormat[1] = 2777;
gDropdownItemsArgs[1] = (uint32)&gConfigThemes.presets[0].name;
for (i = 2; i < num_items; i++) {
for (int i = 0; i < num_items; i++) {
gDropdownItemsFormat[i] = 2777;
gDropdownItemsArgs[i] = (uint32)&gConfigThemes.presets[i].name;
gDropdownItemsArgs[i] = (uint32)theme_manager_get_available_theme_name(i);
}
window_dropdown_show_text_custom_width(
w->x + widget->left,
w->y + widget->top,
@ -550,42 +508,35 @@ static void window_themes_mousedown(int widgetIndex, rct_window* w, rct_widget*
DROPDOWN_FLAG_STAY_OPEN,
num_items,
widget->right - widget->left - 3
);
);
if (gCurrentTheme == 0 || gCurrentTheme == 1) {
dropdown_set_checked(gCurrentTheme ^ 1, true);
} else {
dropdown_set_checked(gCurrentTheme, true);
}
dropdown_set_checked(theme_manager_get_active_available_theme_index(), true);
break;
case WIDX_THEMES_RCT1_RIDE_LIGHTS:
if (gCurrentTheme >= 2) {
theme_get_preset()->features.rct1_ride_lights ^= 1;
themes_save_preset(gCurrentTheme);
window_invalidate_all();
}
else {
if (theme_get_flags() & UITHEME_FLAG_PREDEFINED) {
window_error_open(5241, STR_NONE);
} else {
theme_set_flags(theme_get_flags() ^ UITHEME_FLAG_USE_LIGHTS_RIDE);
theme_save();
window_invalidate_all();
}
break;
case WIDX_THEMES_RCT1_PARK_LIGHTS:
if (gCurrentTheme >= 2) {
theme_get_preset()->features.rct1_park_lights ^= 1;
themes_save_preset(gCurrentTheme);
window_invalidate_all();
}
else {
if (theme_get_flags() & UITHEME_FLAG_PREDEFINED) {
window_error_open(5241, STR_NONE);
} else {
theme_set_flags(theme_get_flags() ^ UITHEME_FLAG_USE_LIGHTS_PARK);
theme_save();
window_invalidate_all();
}
break;
case WIDX_THEMES_RCT1_SCENARIO_FONT:
if (gCurrentTheme >= 2) {
theme_get_preset()->features.rct1_scenario_font ^= 1;
themes_save_preset(gCurrentTheme);
window_invalidate_all();
}
else {
if (theme_get_flags() & UITHEME_FLAG_PREDEFINED) {
window_error_open(5241, STR_NONE);
} else {
theme_set_flags(theme_get_flags() ^ UITHEME_FLAG_USE_ALTERNATIVE_SCENARIO_SELECT_FONT);
theme_save();
window_invalidate_all();
}
break;
}
@ -596,22 +547,22 @@ static void window_themes_dropdown(rct_window *w, int widgetIndex, int dropdownI
switch (widgetIndex) {
case WIDX_THEMES_LIST:
if (dropdownIndex != -1) {
get_colour_scheme_tab()->colours[_color_index_2] = dropdownIndex | (get_colour_scheme_tab()->colours[_color_index_2] & 0x80);
rct_windowclass wc = get_window_class_tab_index(_color_index_1);
uint8 colour = theme_get_colour(wc, _color_index_2);
colour = (colour & COLOUR_FLAG_TRANSLUCENT) | dropdownIndex;
theme_set_colour(wc, _color_index_2, colour);
window_invalidate_all();
_color_index_1 = -1;
_color_index_2 = -1;
if (gCurrentTheme >= 2)
themes_save_preset(gCurrentTheme);
// if (gCurrentTheme >= 2)
// themes_save_preset(gCurrentTheme);
}
break;
case WIDX_THEMES_PRESETS_DROPDOWN:
if (dropdownIndex != -1) {
if (dropdownIndex == 0 || dropdownIndex == 1)
dropdownIndex ^= 1;
theme_change_preset(dropdownIndex);
theme_manager_set_active_available_theme(dropdownIndex);
}
config_save_default();
break;
}
@ -651,33 +602,36 @@ void window_themes_scrollmousedown(rct_window *w, int scrollIndex, int x, int y)
int y2 = y % _row_height;
_color_index_1 = y / _row_height;
_color_index_2 = ((x - _button_offset_x) / 12);
if (_color_index_2 < get_colour_scheme_tab_definition()->num_colours) {
rct_windowclass wc = get_window_class_tab_index(_color_index_1);
int numColours = theme_desc_get_num_colours(wc);
if (_color_index_2 < numColours) {
if (x >= _button_offset_x && x < _button_offset_x + 12 * 6 && y2 >= _button_offset_y && y2 < _button_offset_y + 11) {
if (gCurrentTheme >= 2) {
if (theme_get_flags() & UITHEME_FLAG_PREDEFINED) {
window_error_open(5241, 5256);
} else {
window_themes_widgets[WIDX_THEMES_COLORBTN_MASK].left = _button_offset_x + _color_index_2 * 12 + window_themes_widgets[WIDX_THEMES_LIST].left;
window_themes_widgets[WIDX_THEMES_COLORBTN_MASK].top = _color_index_1 * _row_height + _button_offset_y - w->scrolls[0].v_top + window_themes_widgets[WIDX_THEMES_LIST].top;
window_themes_widgets[WIDX_THEMES_COLORBTN_MASK].right = window_themes_widgets[WIDX_THEMES_COLORBTN_MASK].left + 12;
window_themes_widgets[WIDX_THEMES_COLORBTN_MASK].bottom = window_themes_widgets[WIDX_THEMES_COLORBTN_MASK].top + 12;
window_dropdown_show_colour(w, &(window_themes_widgets[WIDX_THEMES_COLORBTN_MASK]), w->colours[1], get_colour_scheme_tab()->colours[_color_index_2]);
uint8 colour = theme_get_colour(wc, _color_index_2);
window_dropdown_show_colour(w, &(window_themes_widgets[WIDX_THEMES_COLORBTN_MASK]), w->colours[1], colour);
widget_invalidate(w, WIDX_THEMES_LIST);
}
else {
window_error_open(5241, 5256);
}
}
else if (x >= _button_offset_x && x < _button_offset_x + 12 * 6 - 1 && y2 >= _check_offset_y && y2 < _check_offset_y + 11) {
if (gCurrentTheme >= 2) {
if (get_colour_scheme_tab()->colours[_color_index_2] & 0x80) {
get_colour_scheme_tab()->colours[_color_index_2] &= 0x7F;
}
else {
get_colour_scheme_tab()->colours[_color_index_2] |= 0x80;
}
themes_save_preset(gCurrentTheme);
window_invalidate_all();
}
else {
if (theme_get_flags() & UITHEME_FLAG_PREDEFINED) {
window_error_open(5241, 5256);
} else {
uint8 colour = theme_get_colour(wc, _color_index_2);
if (colour & COLOUR_FLAG_TRANSLUCENT) {
colour &= ~COLOUR_FLAG_TRANSLUCENT;
} else {
colour |= COLOUR_FLAG_TRANSLUCENT;
}
theme_set_colour(wc, _color_index_2, colour);
window_invalidate_all();
}
}
}
@ -700,25 +654,26 @@ static void window_themes_textinput(rct_window *w, int widgetIndex, char *text)
case WIDX_THEMES_RENAME_BUTTON:
if (filename_valid_characters(text)) {
bool nameTaken = false;
for (int i = 0; i < gConfigThemes.num_presets; i++) {
if (strcmp(gConfigThemes.presets[i].name, text) == 0) {
window_error_open(5242, STR_NONE);
int numAvailableThemes = (int)theme_manager_get_num_available_themes();
for (int i = 0; i < numAvailableThemes; i++) {
const utf8 * themeName = theme_manager_get_available_theme_name(i);
if (strcmp(themeName, text) == 0) {
if (widgetIndex != WIDX_THEMES_RENAME_BUTTON) {
window_error_open(5242, STR_NONE);
}
nameTaken = true;
break;
}
}
if (!nameTaken) {
if (widgetIndex == WIDX_THEMES_DUPLICATE_BUTTON) {
theme_create_preset(gCurrentTheme, text);
theme_duplicate(text);
} else {
theme_rename(text);
}
else {
theme_rename_preset(gCurrentTheme, text);
}
config_save_default();
window_invalidate(w);
}
}
else {
} else {
window_error_open(5243, STR_NONE);
}
break;
@ -787,9 +742,9 @@ void window_themes_invalidate(rct_window *w)
window_themes_widgets[WIDX_THEMES_PRESETS].type = WWT_EMPTY;
window_themes_widgets[WIDX_THEMES_PRESETS_DROPDOWN].type = WWT_EMPTY;
widget_set_checkbox_value(w, WIDX_THEMES_RCT1_RIDE_LIGHTS, theme_get_preset()->features.rct1_ride_lights);
widget_set_checkbox_value(w, WIDX_THEMES_RCT1_PARK_LIGHTS, theme_get_preset()->features.rct1_park_lights);
widget_set_checkbox_value(w, WIDX_THEMES_RCT1_SCENARIO_FONT, theme_get_preset()->features.rct1_scenario_font);
widget_set_checkbox_value(w, WIDX_THEMES_RCT1_RIDE_LIGHTS, theme_get_flags() & UITHEME_FLAG_USE_LIGHTS_RIDE);
widget_set_checkbox_value(w, WIDX_THEMES_RCT1_PARK_LIGHTS, theme_get_flags() & UITHEME_FLAG_USE_LIGHTS_PARK);
widget_set_checkbox_value(w, WIDX_THEMES_RCT1_SCENARIO_FONT, theme_get_flags() & UITHEME_FLAG_USE_ALTERNATIVE_SCENARIO_SELECT_FONT);
}
else {
window_themes_widgets[WIDX_THEMES_LIST].type = WWT_SCROLL;
@ -811,7 +766,9 @@ void window_themes_paint(rct_window *w, rct_drawpixelinfo *dpi)
window_themes_draw_tab_images(dpi, w);
if (_selected_tab == WINDOW_THEMES_TAB_SETTINGS) {
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 0, uint32) = (uint32)&gConfigThemes.presets[gCurrentTheme].name;
int activeAvailableThemeIndex = theme_manager_get_active_available_theme_index();
const utf8 * activeThemeName = theme_manager_get_available_theme_name(activeAvailableThemeIndex);
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 0, uint32) = (uint32)activeThemeName;
gfx_draw_string_left(dpi, 5238, NULL, w->colours[1], w->x + 10, w->y + window_themes_widgets[WIDX_THEMES_PRESETS].top + 1);
gfx_draw_string_left_clipped(
dpi,
@ -821,7 +778,7 @@ void window_themes_paint(rct_window *w, rct_drawpixelinfo *dpi)
w->x + window_themes_widgets[WIDX_THEMES_PRESETS].left + 1,
w->y + window_themes_widgets[WIDX_THEMES_PRESETS].top,
w->x + window_themes_widgets[WIDX_THEMES_PRESETS_DROPDOWN].left - window_themes_widgets[WIDX_THEMES_PRESETS].left - 4
);
);
}
else if (_selected_tab == WINDOW_THEMES_TAB_FEATURES) {
@ -869,18 +826,20 @@ void window_themes_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi, int scroll
}
}
for (int j = 0; j < get_colour_scheme_tab_definition_by_index(i)->num_colours; j++) {
rct_windowclass wc = get_window_class_tab_index(i);
int numColours = theme_desc_get_num_colours(wc);
for (uint8 j = 0; j < numColours; j++) {
gfx_draw_string_left(dpi, theme_desc_get_name(wc), NULL, w->colours[1], 2, y + 4);
gfx_draw_string_left(dpi, get_colour_scheme_tab_definition_by_index(i)->name, NULL, w->colours[1], 2, y + 4);
uint32 image = ((get_colour_scheme_tab_by_index(i)->colours[j] & 0x7F) << 19) + 0x600013C3;
uint8 colour = theme_get_colour(wc, j);
uint32 image = ((colour & ~COLOUR_FLAG_TRANSLUCENT) << 19) + 0x600013C3;
if (i == _color_index_1 && j == _color_index_2) {
image = ((get_colour_scheme_tab_by_index(i)->colours[j] & 0x7F) << 19) + 0x600013C4;
image = ((colour & ~COLOUR_FLAG_TRANSLUCENT) << 19) + 0x600013C4;
}
gfx_draw_sprite(dpi, image, _button_offset_x + 12 * j, y + _button_offset_y, 0);
gfx_fill_rect_inset(dpi, _button_offset_x + 12 * j, y + _check_offset_y, _button_offset_x + 12 * j + 9, y + _check_offset_y + 10, w->colours[1], 0xE0);
if (get_colour_scheme_tab_by_index(i)->colours[j] & 0x80) {
if (colour & COLOUR_FLAG_TRANSLUCENT) {
RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_FONT_SPRITE_BASE, sint16) = -1;
gfx_draw_string(dpi, (char*)CheckBoxMarkString, w->colours[1] & 0x7F, _button_offset_x + 12 * j, y + _check_offset_y);
}

View File

@ -20,6 +20,7 @@
#include "../addresses.h"
#include "../localisation/localisation.h"
#include "../interface/themes.h"
#include "../interface/widget.h"
#include "../interface/window.h"
#include "../interface/viewport.h"
@ -170,9 +171,6 @@ void window_tile_inspector_open()
window->disabled_widgets = (1 << WIDX_CORRUPT) | (1 << WIDX_MOVE_UP) | (1 << WIDX_MOVE_DOWN) | (1 << WIDX_REMOVE);
window_init_scroll_widgets(window);
window->colours[0] = 7;
window->colours[1] = 7;
window->colours[2] = 7;
window->min_width = MIN_WW;
window->min_height = MIN_WH;
window->max_width = MAX_WW;
@ -433,6 +431,8 @@ static void window_tile_inspector_scrollmouseover(rct_window *w, int scrollIndex
static void window_tile_inspector_invalidate(rct_window *w)
{
colour_scheme_update(w);
window_tile_inspector_widgets[WIDX_BACKGROUND].right = w->width - 1;
window_tile_inspector_widgets[WIDX_BACKGROUND].bottom = w->height - 1;
window_tile_inspector_widgets[WIDX_CLOSE].left = w->width - 13;

View File

@ -351,7 +351,7 @@ static void window_scenarioselect_paint(rct_window *w, rct_drawpixelinfo *dpi)
window_draw_widgets(w, dpi);
format = (theme_get_preset()->features.rct1_scenario_font) ? 5138 : 1193;
format = (theme_get_flags() & UITHEME_FLAG_USE_ALTERNATIVE_SCENARIO_SELECT_FONT) ? 5138 : 1193;
// Text for each tab
for (i = 0; i < 8; i++) {
@ -426,8 +426,8 @@ static void window_scenarioselect_scrollpaint(rct_window *w, rct_drawpixelinfo *
colour = (colour << 24) | (colour << 16) | (colour << 8) | colour;
gfx_clear(dpi, colour);
int highlighted_format = (theme_get_preset()->features.rct1_scenario_font) ? 5139 : 1193;
int unhighlighted_format = (theme_get_preset()->features.rct1_scenario_font) ? 5139 : 1191;
int highlighted_format = (theme_get_flags() & UITHEME_FLAG_USE_ALTERNATIVE_SCENARIO_SELECT_FONT) ? 5139 : 1193;
int unhighlighted_format = (theme_get_flags() & UITHEME_FLAG_USE_ALTERNATIVE_SCENARIO_SELECT_FONT) ? 5139 : 1191;
bool wide = gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_ORIGIN;