diff --git a/src/sago/SagoDataHolder.hpp b/src/sago/SagoDataHolder.hpp index 068650a..3c126aa 100644 --- a/src/sago/SagoDataHolder.hpp +++ b/src/sago/SagoDataHolder.hpp @@ -49,7 +49,7 @@ private: }; -class MusicHandler { +class MusicHandler final { public: MusicHandler() {}; MusicHandler(const SagoDataHolder* holder, const std::string &musicName); @@ -62,7 +62,7 @@ private: }; -class SoundHandler { +class SoundHandler final { public: SoundHandler() {}; SoundHandler(const SagoDataHolder* holder, const std::string &soundName); @@ -74,7 +74,7 @@ private: Uint64 version = 0; }; -class SagoDataHolder { +class SagoDataHolder final { public: /** * The renderer must be set before requesting a texture. @@ -112,7 +112,7 @@ public: * @return A globally unique number. */ Uint64 getVersion() const; - virtual ~SagoDataHolder(); + ~SagoDataHolder(); private: SagoDataHolder(const SagoDataHolder& base) = delete; SagoDataHolder& operator=(const SagoDataHolder& base) = delete; diff --git a/src/sago/SagoSprite.hpp b/src/sago/SagoSprite.hpp index c66fd87..688770a 100644 --- a/src/sago/SagoSprite.hpp +++ b/src/sago/SagoSprite.hpp @@ -29,7 +29,7 @@ SOFTWARE. namespace sago { -class SagoSprite { +class SagoSprite final { public: SagoSprite(); SagoSprite(const SagoDataHolder &texHolder, const std::string &texture,const SDL_Rect& initImage,const int animationFrames, const int animationFrameLength); @@ -76,7 +76,7 @@ public: SagoSprite& operator=(const SagoSprite& base); int GetWidth() const; int GetHeight() const; - virtual ~SagoSprite(); + ~SagoSprite(); private: struct SagoSpriteData; SagoSpriteData *data; diff --git a/src/sago/SagoSpriteHolder.cpp b/src/sago/SagoSpriteHolder.cpp index 2b24e6a..d219487 100644 --- a/src/sago/SagoSpriteHolder.cpp +++ b/src/sago/SagoSpriteHolder.cpp @@ -73,7 +73,7 @@ static int getDefaultValue(const rapidjson::Value& value, const char* name, int static std::string getDefaultValue(const rapidjson::Value& value, const char* name, std::string defaultValue) { assert(value.IsObject()); const auto& t = value.GetObject().FindMember(name); - if (t->value.IsString()) { + if (t != value.MemberEnd() && t->value.IsString()) { defaultValue = t->value.GetString(); } return defaultValue; @@ -106,9 +106,6 @@ void SagoSpriteHolder::ReadSpriteFile(const std::string& filename) { SDL_Rect origin = {}; origin.x = getDefaultValue(m.value, "originx",0); origin.y = getDefaultValue(m.value, "originy",0); - if (origin.x != 0) { - cerr << "Origin: " << origin.x << ", " << origin.y << "\n"; - } if (number_of_frames < 1) { number_of_frames = 1; } diff --git a/src/sago/SagoSpriteHolder.hpp b/src/sago/SagoSpriteHolder.hpp index 7e2924c..21b093e 100644 --- a/src/sago/SagoSpriteHolder.hpp +++ b/src/sago/SagoSpriteHolder.hpp @@ -30,10 +30,10 @@ SOFTWARE. namespace sago { -class SagoSpriteHolder { +class SagoSpriteHolder final { public: explicit SagoSpriteHolder(const SagoDataHolder &texHolder); - virtual ~SagoSpriteHolder(); + ~SagoSpriteHolder(); void ReadSprites(); const sago::SagoSprite& GetSprite(const std::string &spritename) const; const SagoDataHolder& GetDataHolder() const; diff --git a/src/sago/SagoTextBox.cpp b/src/sago/SagoTextBox.cpp index 01694ac..8a531bf 100644 --- a/src/sago/SagoTextBox.cpp +++ b/src/sago/SagoTextBox.cpp @@ -60,6 +60,10 @@ void SagoTextBox::SetText(const char* text) { data->text = text; } +void SagoTextBox::SetText(const std::string& text) { + data->text = text; +} + void SagoTextBox::SetColor(const SDL_Color& color) { data->color = color; } @@ -93,14 +97,11 @@ void SagoTextBox::AppendLineToCache(const std::string& text) { tf.SetFontSize(data->fontSize); tf.SetColor(data->color); tf.SetOutline(data->outline, data->outlineColor); - tf.SetText(text.c_str()); + tf.SetText(text); } void SagoTextBox::SplitAndAppendLineToCache(TTF_Font* font, const std::string& text) { - if (text.length() == 0) { - return; - } int width = data->maxWidth; TTF_SizeUTF8(font, text.c_str(),&width, nullptr); if (data->maxWidth <= 0 || width <= data->maxWidth || text.length() == 1) { @@ -156,6 +157,10 @@ void SagoTextBox::SplitAndAppendLineToCache(TTF_Font* font, const std::string& t } void SagoTextBox::UpdateCache() { + if (!data->tex) { + std::cerr << "FATAL: SagoTextBox::UpdateCache - DataHolder not set!\n"; + abort(); + } TTF_Font *font = data->tex->getFontPtr(data->fontName, data->fontSize); const char delim = '\n'; const std::string& s = data->text; diff --git a/src/sago/SagoTextBox.hpp b/src/sago/SagoTextBox.hpp index 6fe19d9..906fae2 100644 --- a/src/sago/SagoTextBox.hpp +++ b/src/sago/SagoTextBox.hpp @@ -30,13 +30,20 @@ SOFTWARE. namespace sago { -class SagoTextBox { +class SagoTextBox final { public: SagoTextBox(); - virtual ~SagoTextBox(); + ~SagoTextBox(); void SetHolder(const SagoDataHolder* holder); void SetText(const char* text); + void SetText(const std::string& text); void SetColor(const SDL_Color& color); + + /** + * Set the name of the font. Must be known to the data holder. + * The name could for instance be "freeserif". + * @param fontName Name of the font as required by SagoDataHolder + */ void SetFont(const char* fontName); void SetFontSize(int fontSize); void SetOutline(int outlineSize, const SDL_Color& color); diff --git a/src/sago/SagoTextField.cpp b/src/sago/SagoTextField.cpp index 831d3f1..2bb40f9 100644 --- a/src/sago/SagoTextField.cpp +++ b/src/sago/SagoTextField.cpp @@ -83,6 +83,22 @@ SagoTextField::SagoTextField(SagoTextField&& o) noexcept { o.data = nullptr; } +SagoTextField& SagoTextField::CopyFrom(const SagoTextField& base) { + ClearCache(); + try { + *data = *(base.data); + //Copy all data but do not reuse the cache as it would result in a double free + data->outlineTextSurface = nullptr; + data->outlineTexture = nullptr; + data->textSurface = nullptr; + data->texture = nullptr; + return *this; + } catch (...) { + delete data; + throw; + } +} + SagoTextField::~SagoTextField() { if(!data) { return; @@ -99,6 +115,10 @@ void SagoTextField::SetText(const char* text) { data->text = text; } +void SagoTextField::SetText(const std::string& text) { + data->text = text; +} + void SagoTextField::SetColor(const SDL_Color& color) { data->color = color; } @@ -121,10 +141,6 @@ const std::string& SagoTextField::GetText() const { } void SagoTextField::ClearCache() { - if (!data->tex) { - std::cerr << "FATAL: DataHolder not set!\n"; - abort(); - } if (data->texture) { SDL_DestroyTexture(data->texture); data->texture = nullptr; @@ -144,12 +160,14 @@ void SagoTextField::ClearCache() { } void SagoTextField::UpdateCache(SDL_Renderer* target) { + if (!data->tex) { + std::cerr << "FATAL: DataHolder not set!\n"; + abort(); + } ClearCache(); TTF_Font *font = data->tex->getFontPtr(data->fontName, data->fontSize); data->textSurface = TTF_RenderUTF8_Blended (font, data->text.c_str(), data->color); data->texture = SDL_CreateTextureFromSurface(target, data->textSurface); - int textWidth = 0; - SDL_QueryTexture(data->texture, NULL, NULL, &textWidth, NULL); if (data->outline > 0) { OutlineHandler oh(font, data->outline); data->outlineTextSurface = TTF_RenderUTF8_Blended (font, data->text.c_str(), data->outlineColor); @@ -160,7 +178,21 @@ void SagoTextField::UpdateCache(SDL_Renderer* target) { data->renderedVersion = data->tex->getVersion(); } -void SagoTextField::Draw(SDL_Renderer* target, int x, int y) { +void SagoTextField::GetRenderedSize(const char* text, int* w, int* h) { + TTF_Font *font = data->tex->getFontPtr(data->fontName, data->fontSize); + int ret = TTF_SizeUTF8(font, text, w, h); + if (ret) { + if (w) { + *w = 0; + } + if (h) { + *h = 0; + } + std::cerr << "GetRenderedSize failed to find size of " << text << ". Error code: " << ret << "\n"; + } +} + +void SagoTextField::Draw(SDL_Renderer* target, int x, int y, Alignment alignment, VerticalAlignment verticalAlignment) { if (data->text.empty()) { return; } @@ -173,6 +205,18 @@ void SagoTextField::Draw(SDL_Renderer* target, int x, int y) { int texW = 0; int texH = 0; SDL_QueryTexture(data->texture, NULL, NULL, &texW, &texH); + if (alignment == Alignment::center) { + x -= texW/2; + } + if (alignment == Alignment::right) { + y -= texW; + } + if (verticalAlignment == VerticalAlignment::center) { + y -= texH/2; + } + if (verticalAlignment == VerticalAlignment::bottom) { + y -= texH; + } SDL_Rect dstrect = { x, y, texW, texH }; if (data->outlineTexture) { int outlineTexW = 0; diff --git a/src/sago/SagoTextField.hpp b/src/sago/SagoTextField.hpp index ab05c49..902ee16 100644 --- a/src/sago/SagoTextField.hpp +++ b/src/sago/SagoTextField.hpp @@ -38,12 +38,21 @@ namespace sago { * Normally all values will be set at the beginning before text is drawn. * SetHolder MUST be called before the field is drawn! */ -class SagoTextField { +class SagoTextField final { public: SagoTextField(); SagoTextField(SagoTextField&& o) noexcept; SagoTextField& operator=(const SagoTextField&& base) = delete; - virtual ~SagoTextField(); + SagoTextField& operator=(const SagoTextField& base) = delete; + ~SagoTextField(); + /** + * This method creates a copy of a given font. + * The cache will not be copied. + * This is ALMOST like the "= operator" but given its own name to prevent implicit calling. + * @param base The object to copy from + * @return A reference to this object. + */ + SagoTextField& CopyFrom(const SagoTextField& base); /** * Sets the data holder. This is MANDATORY * @param holder The data holder to fetch the fonts from @@ -54,9 +63,15 @@ public: * @param text The actual UTF-8 encoded text */ void SetText(const char* text); + /** + * Set the text to display. + * @param text The actual UTF-8 encoded text + */ + void SetText(const std::string& text); void SetColor(const SDL_Color& color); /** * Set the name of the font. Must be known to the data holder. + * The name could for instance be "freeserif". * @param fontName Name of the font as required by SagoDataHolder */ void SetFont(const char* fontName); @@ -72,7 +87,18 @@ public: * @return The text */ const std::string& GetText() const; - void Draw(SDL_Renderer* target, int x, int y); + /** + * A Shorthand for calling TTF_SizeUTF8 on the right font + * The size is measuered WITHOUT the outline! + * Will fail silently on error (except writing to stderr) and set w and h to 0 if they are not null. + * @param text The text to check the rendered size for + * @param w Pointer to an int where the width of the text will be stored. Maybe null. + * @param h Pointer to an int where the hight of the text will be stored. Maybe null. + */ + void GetRenderedSize(const char* text, int* w = nullptr, int* h = nullptr); + enum class Alignment { left = 0, right=1, center = 2 }; + enum class VerticalAlignment { top = 0, center = 1, bottom = 2}; + void Draw(SDL_Renderer* target, int x, int y, Alignment alignment = Alignment::left, VerticalAlignment verticalAlignment = VerticalAlignment::top); /** * Updates the cache. * You normally do not want to call this from the outside as it is done just in time. @@ -88,7 +114,6 @@ public: void ClearCache(); private: SagoTextField(const SagoTextField& orig) = delete; - SagoTextField& operator=(const SagoTextField& base) = delete; struct SagoTextFieldData; SagoTextFieldData *data; }; diff --git a/src/sago/platform_folders.cpp b/src/sago/platform_folders.cpp index 004ceac..34e6bae 100644 --- a/src/sago/platform_folders.cpp +++ b/src/sago/platform_folders.cpp @@ -30,17 +30,14 @@ SOFTWARE. #include #include #include -#include +#include #include #if defined(_WIN32) #include #include -#define strtok_r strtok_s - -static std::string win32_utf16_to_utf8(const wchar_t* wstr) -{ +static std::string win32_utf16_to_utf8(const wchar_t* wstr) { std::string res; // If the 6th parameter is 0 then WideCharToMultiByte returns the number of bytes needed to store the result. int actualSize = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL); @@ -52,7 +49,7 @@ static std::string win32_utf16_to_utf8(const wchar_t* wstr) } if (actualSize == 0) { // WideCharToMultiByte return 0 for errors. - std::string errorMsg = "UTF16 to UTF8 failed with error code: " + GetLastError(); + const std::string errorMsg = "UTF16 to UTF8 failed with error code: " + GetLastError(); throw std::runtime_error(errorMsg.c_str()); } return res; @@ -61,8 +58,7 @@ static std::string win32_utf16_to_utf8(const wchar_t* wstr) static std::string GetWindowsFolder(int folderId, const char* errorMsg) { wchar_t szPath[MAX_PATH]; szPath[0] = 0; - if ( !SUCCEEDED( SHGetFolderPathW( NULL, folderId, NULL, 0, szPath ) ) ) - { + if ( !SUCCEEDED( SHGetFolderPathW( NULL, folderId, NULL, 0, szPath ) ) ) { throw std::runtime_error(errorMsg); } return win32_utf16_to_utf8(szPath); @@ -101,12 +97,14 @@ static std::string GetMacFolder(OSType folderType, const char* errorMsg) { #include #include #include +// For strlen and strtok +#include //Typically Linux. For easy reading the comments will just say Linux but should work with most *nixes static void throwOnRelative(const char* envName, const char* envValue) { if (envValue[0] != '/') { char buffer[200]; - snprintf(buffer, sizeof(buffer), "Environment \"%s\" does not start with an '/'. XDG specifies that the value must be absolute. The current value is: \"%s\"", envName, envValue); + std::snprintf(buffer, sizeof(buffer), "Environment \"%s\" does not start with an '/'. XDG specifies that the value must be absolute. The current value is: \"%s\"", envName, envValue); throw std::runtime_error(buffer); } } @@ -120,13 +118,13 @@ static void throwOnRelative(const char* envName, const char* envValue) { static std::string getHome() { std::string res; int uid = getuid(); - const char* homeEnv = getenv("HOME"); + const char* homeEnv = std::getenv("HOME"); if ( uid != 0 && homeEnv) { //We only acknowlegde HOME if not root. res = homeEnv; return res; } - struct passwd *pw = getpwuid(uid); + struct passwd* pw = getpwuid(uid); if (!pw) { throw std::runtime_error("Unable to get passwd struct."); } @@ -140,7 +138,7 @@ static std::string getHome() { static std::string getLinuxFolderDefault(const char* envName, const char* defaultRelativePath) { std::string res; - const char* tempRes = getenv(envName); + const char* tempRes = std::getenv(envName); if (tempRes) { throwOnRelative(envName, tempRes); res = tempRes; @@ -151,9 +149,8 @@ static std::string getLinuxFolderDefault(const char* envName, const char* defaul } static void appendExtraFoldersTokenizer(const char* envName, const char* envValue, std::vector& folders) { - std::vector buffer(envValue, envValue + strlen(envValue) + 1); - char *saveptr; - const char* p = strtok_r ( &buffer[0], ":", &saveptr); + std::vector buffer(envValue, envValue + std::strlen(envValue) + 1); + char* p = std::strtok ( &buffer[0], ":"); while (p != NULL) { if (p[0] == '/') { folders.push_back(p); @@ -163,12 +160,12 @@ static void appendExtraFoldersTokenizer(const char* envName, const char* envValu //The XDG documentation indicates that the folder should be ignored but that the program should continue. std::cerr << "Skipping path \"" << p << "\" in \"" << envName << "\" because it does not start with a \"/\"\n"; } - p = strtok_r (NULL, ":", &saveptr); + p = std::strtok (NULL, ":"); } } static void appendExtraFolders(const char* envName, const char* defaultValue, std::vector& folders) { - const char* envValue = getenv(envName); + const char* envValue = std::getenv(envName); if (!envValue) { envValue = defaultValue; } @@ -206,7 +203,7 @@ std::string getCacheDir() { #elif defined(__APPLE__) return GetMacFolder(kCachedDataFolderType, "Failed to find the Application Support Folder"); #else - return getLinuxFolderDefault("XDG_CONFIG_HOME", ".cache"); + return getLinuxFolderDefault("XDG_CACHE_HOME", ".cache"); #endif } @@ -276,7 +273,8 @@ PlatformFolders::PlatformFolders() { this->data = new PlatformFolders::PlatformFoldersData(); try { PlatformFoldersFillData(data->folders); - } catch (...) { + } + catch (...) { delete this->data; throw; } @@ -364,6 +362,33 @@ std::string PlatformFolders::getSaveGamesFolder1() const { #endif } +std::string getDesktopFolder() { + return PlatformFolders().getDesktopFolder(); +} + +std::string getDocumentsFolder() { + return PlatformFolders().getDocumentsFolder(); +} + +std::string getDownloadFolder1() { + return PlatformFolders().getDownloadFolder1(); +} + +std::string getPicturesFolder() { + return PlatformFolders().getPicturesFolder(); +} + +std::string getMusicFolder() { + return PlatformFolders().getMusicFolder(); +} + +std::string getVideoFolder() { + return PlatformFolders().getVideoFolder(); +} + +std::string getSaveGamesFolder1() { + return PlatformFolders().getSaveGamesFolder1(); +} } //namespace sago diff --git a/src/sago/platform_folders.h b/src/sago/platform_folders.h index 73fba85..bd6c0d5 100644 --- a/src/sago/platform_folders.h +++ b/src/sago/platform_folders.h @@ -48,6 +48,7 @@ namespace sago { * @return The base folder for storring program data. */ std::string getDataHome(); + /** * Retrives the base folder for storring config files. * You must add the program name yourself like this: @@ -59,6 +60,7 @@ std::string getDataHome(); * @return The base folder for storring config data. */ std::string getConfigHome(); + /** * Retrives the base folder for storring cache files. * You must add the program name yourself like this: @@ -70,6 +72,7 @@ std::string getConfigHome(); * @return The base folder for storring data that do not need to be backed up. */ std::string getCacheDir(); + /** * This will append extra folders that your program should be looking for data files in. * This does not normally include the path returned by GetDataHome(). @@ -87,6 +90,7 @@ std::string getCacheDir(); * @param homes A vector that extra folders will be appended to. */ void appendAdditionalDataDirectories(std::vector& homes); + /** * This will append extra folders that your program should be looking for config files in. * This does not normally include the path returned by GetConfigHome(). @@ -106,6 +110,58 @@ void appendAdditionalDataDirectories(std::vector& homes); void appendAdditionalConfigDirectories(std::vector& homes); /** + * The folder that represents the desktop. + * Normally you should try not to use this folder. + * @return Absolute path to the user's desktop + */ +std::string getDesktopFolder(); + +/** + * The folder to store user documents to + * @return Absolute path to the "Documents" folder + */ +std::string getDocumentsFolder(); + +/** + * The folder where files are downloaded. + * @note Windows: This version is XP compatible and returns the Desktop. Vista and later has a dedicated folder. + * @return Absolute path to the folder where files are downloaded to. + */ +std::string getDownloadFolder1(); + +/** + * The folder for storring the user's pictures. + * @return Absolute path to the "Picture" folder + */ +std::string getPicturesFolder(); + +/** + * The folder where music is stored + * @return Absolute path to the music folder + */ +std::string getMusicFolder(); + +/** + * The folder where video is stored + * @return Absolute path to the video folder + */ +std::string getVideoFolder(); + +/** + * The base folder for storring saved games. + * You must add the program name to it like this: + * @code{.cpp} + * string saved_games_folder = sago::getSaveGamesFolder1()+"/My Program Name/"; + * @endcode + * @note Windows: This is an XP compatible version and returns the path to "My Games" in Documents. Vista and later has an official folder. + * @note Linux: XDF does not define a folder for saved games. This will just return the same as GetDataHome() + * @return The folder base folder for storring save games. + */ +std::string getSaveGamesFolder1(); + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +/** * This class contains methods for finding the system depended special folders. * For Windows these folders are either by convention or given by CSIDL. * For Linux XDG convention is used. @@ -166,10 +222,13 @@ private: #elif defined(__APPLE__) #else struct PlatformFoldersData; - PlatformFoldersData *data; + PlatformFoldersData* data; #endif }; +#endif // skip doxygen + + } //namespace sago #endif /* PLATFORM_FOLDERS_H */