diff --git a/doomsday/client/client.pro b/doomsday/client/client.pro index 528ec5fe76..8adcb8b441 100644 --- a/doomsday/client/client.pro +++ b/doomsday/client/client.pro @@ -131,7 +131,9 @@ DENG_CONVENIENCE_HEADERS += \ include/EntityDatabase \ include/Face \ include/FontLineWrapping \ + include/FontManifest \ include/Fonts \ + include/FontScheme \ include/Game \ include/Games \ include/GLTextComposer \ @@ -335,7 +337,9 @@ DENG_HEADERS += \ include/resource/colorpalettes.h \ include/resource/compositebitmapfont.h \ include/resource/compositetexture.h \ + include/resource/fontmanifest.h \ include/resource/fonts.h \ + include/resource/fontscheme.h \ include/resource/hq2x.h \ include/resource/image.h \ include/resource/lumpcache.h \ @@ -693,7 +697,9 @@ SOURCES += \ src/resource/colorpalettes.cpp \ src/resource/compositebitmapfont.cpp \ src/resource/compositetexture.cpp \ + src/resource/fontmanifest.cpp \ src/resource/fonts.cpp \ + src/resource/fontscheme.cpp \ src/resource/hq2x.cpp \ src/resource/image.cpp \ src/resource/material.cpp \ diff --git a/doomsday/client/include/FontManifest b/doomsday/client/include/FontManifest new file mode 100644 index 0000000000..75810da16f --- /dev/null +++ b/doomsday/client/include/FontManifest @@ -0,0 +1 @@ +#include "resource/fontmanifest.h" diff --git a/doomsday/client/include/FontScheme b/doomsday/client/include/FontScheme new file mode 100644 index 0000000000..030f25b1fd --- /dev/null +++ b/doomsday/client/include/FontScheme @@ -0,0 +1 @@ +#include "resource/fontscheme.h" diff --git a/doomsday/client/include/resource/fontmanifest.h b/doomsday/client/include/resource/fontmanifest.h new file mode 100644 index 0000000000..6cc38da06d --- /dev/null +++ b/doomsday/client/include/resource/fontmanifest.h @@ -0,0 +1,152 @@ +/** @file fontmanifest.h Font resource manifest. + * + * @authors Copyright © 2003-2013 Jaakko Keränen + * @authors Copyright © 2005-2013 Daniel Swanson + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. You should have received a copy of the GNU + * General Public License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef DENG_RESOURCE_FONTMANIFEST_H +#define DENG_RESOURCE_FONTMANIFEST_H + +#include "AbstractFont" +#include "uri.hh" +#include +#include +#include + +namespace de { + +class Fonts; +class FontScheme; + +/** + * FontManifest. Stores metadata for a unique Font in the collection. + */ +class FontManifest : public PathTree::Node, +DENG2_OBSERVES(AbstractFont, Deletion) +{ +public: + /// Required Font instance is missing. @ingroup errors + DENG2_ERROR(MissingFontError); + + DENG2_DEFINE_AUDIENCE(Deletion, void manifestBeingDeleted(FontManifest const &manifest)) + DENG2_DEFINE_AUDIENCE(UniqueIdChanged, void manifestUniqueIdChanged(FontManifest &manifest)) + +public: + /// Scheme-unique identifier chosen by the owner of the collection. + int _uniqueId; + + /// The defined font instance (if any). + QScopedPointer(_font); + +public: + FontManifest(PathTree::NodeArgs const &args); + ~FontManifest(); + + /** + * Returns the owning scheme of the manifest. + */ + FontScheme &scheme() const; + + /// Convenience method for returning the name of the owning scheme. + String const &schemeName() const; + + /** + * Compose a URI of the form "scheme:path" for the FontRecord. + * + * The scheme component of the URI will contain the symbolic name of + * the scheme for the FontRecord. + * + * The path component of the URI will contain the percent-encoded path + * of the FontRecord. + */ + inline Uri composeUri(QChar sep = '/') const + { + return Uri(schemeName(), path(sep)); + } + + /** + * Compose a URN of the form "urn:scheme:uniqueid" for the font + * FontRecord. + * + * The scheme component of the URI will contain the identifier 'urn'. + * + * The path component of the URI is a string which contains both the + * symbolic name of the scheme followed by the unique id of the font + * FontRecord, separated with a colon. + * + * @see uniqueId(), setUniqueId() + */ + inline Uri composeUrn() const + { + return Uri("urn", String("%1:%2").arg(schemeName()).arg(uniqueId(), 0, 10)); + } + + /** + * Returns a textual description of the manifest. + * + * @return Human-friendly description the manifest. + */ + String description(Uri::ComposeAsTextFlags uriCompositionFlags = Uri::DefaultComposeAsTextFlags) const; + + /** + * Returns the scheme-unique identifier for the manifest. + */ + int uniqueId() const; + + /** + * Change the unique identifier property of the manifest. + * + * @return @c true iff @a newUniqueId differed to the existing unique + * identifier, which was subsequently changed. + */ + bool setUniqueId(int newUniqueId); + + /** + * Returns @c true if a Font is presently associated with the manifest. + */ + bool hasFont() const; + + /** + * Returns the logical Font associated with the manifest. + */ + AbstractFont &font() const; + + /** + * Change the logical Font associated with the manifest. + * + * @param newFont New logical Font to associate. + */ + void setFont(AbstractFont *newFont); + + /** + * Clear the logical Font associated with the manifest. + * + * Same as @c setFont(0) + */ + inline void clearFont() { setFont(0); } + + /// Returns a reference to the application's font collection. + static Fonts &fonts(); + +protected: + // Observes AbstractFont::Deletion. + void fontBeingDeleted(AbstractFont const &font); +}; + +} // namespace de + +#endif // DENG_RESOURCE_FONTMANIFEST_H diff --git a/doomsday/client/include/resource/fonts.h b/doomsday/client/include/resource/fonts.h index 8a0c269bec..e0c9cde8e2 100644 --- a/doomsday/client/include/resource/fonts.h +++ b/doomsday/client/include/resource/fonts.h @@ -22,9 +22,11 @@ #define DENG_RESOURCE_FONTS_H #include "AbstractFont" +#include "FontManifest" +#include "FontScheme" #include "uri.hh" #include -#include +#include #include #include @@ -33,333 +35,6 @@ namespace de { -class Fonts; -class FontScheme; - -/** - * FontManifest. Stores metadata for a unique Font in the collection. - */ -class FontManifest : public PathTree::Node, -DENG2_OBSERVES(AbstractFont, Deletion) -{ -public: - /// Required Font instance is missing. @ingroup errors - DENG2_ERROR(MissingFontError); - - DENG2_DEFINE_AUDIENCE(Deletion, void manifestBeingDeleted(FontManifest const &manifest)) - DENG2_DEFINE_AUDIENCE(UniqueIdChanged, void manifestUniqueIdChanged(FontManifest &manifest)) - -public: - /// Scheme-unique identifier chosen by the owner of the collection. - int _uniqueId; - - /// The defined font instance (if any). - QScopedPointer(_font); - -public: - FontManifest(PathTree::NodeArgs const &args); - ~FontManifest(); - - /** - * Returns the owning scheme of the manifest. - */ - FontScheme &scheme() const; - - /// Convenience method for returning the name of the owning scheme. - String const &schemeName() const; - - /** - * Compose a URI of the form "scheme:path" for the FontRecord. - * - * The scheme component of the URI will contain the symbolic name of - * the scheme for the FontRecord. - * - * The path component of the URI will contain the percent-encoded path - * of the FontRecord. - */ - inline Uri composeUri(QChar sep = '/') const - { - return Uri(schemeName(), path(sep)); - } - - /** - * Compose a URN of the form "urn:scheme:uniqueid" for the font - * FontRecord. - * - * The scheme component of the URI will contain the identifier 'urn'. - * - * The path component of the URI is a string which contains both the - * symbolic name of the scheme followed by the unique id of the font - * FontRecord, separated with a colon. - * - * @see uniqueId(), setUniqueId() - */ - inline Uri composeUrn() const - { - return Uri("urn", String("%1:%2").arg(schemeName()).arg(uniqueId(), 0, 10)); - } - - /** - * Returns a textual description of the manifest. - * - * @return Human-friendly description the manifest. - */ - String description(Uri::ComposeAsTextFlags uriCompositionFlags = Uri::DefaultComposeAsTextFlags) const; - - /** - * Returns the scheme-unique identifier for the manifest. - */ - int uniqueId() const; - - /** - * Change the unique identifier property of the manifest. - * - * @return @c true iff @a newUniqueId differed to the existing unique - * identifier, which was subsequently changed. - */ - bool setUniqueId(int newUniqueId); - - /** - * Returns @c true if a Font is presently associated with the manifest. - */ - bool hasFont() const; - - /** - * Returns the logical Font associated with the manifest. - */ - AbstractFont &font() const; - - /** - * Change the logical Font associated with the manifest. - * - * @param newFont New logical Font to associate. - */ - void setFont(AbstractFont *newFont); - - /** - * Clear the logical Font associated with the manifest. - * - * Same as @c setFont(0) - */ - inline void clearFont() { setFont(0); } - - /// Returns a reference to the application's font collection. - static Fonts &fonts(); - -protected: - // Observes AbstractFont::Deletion. - void fontBeingDeleted(AbstractFont const &font); -}; - -class FontScheme : -DENG2_OBSERVES(FontManifest, UniqueIdChanged), -DENG2_OBSERVES(FontManifest, Deletion) -{ - typedef class FontManifest Manifest; - -public: - /// The requested manifests could not be found in the index. - DENG2_ERROR(NotFoundError); - - /// The specified path was not valid. @ingroup errors - DENG2_ERROR(InvalidPathError); - - DENG2_DEFINE_AUDIENCE(ManifestDefined, void schemeManifestDefined(FontScheme &scheme, Manifest &manifest)) - - /// Minimum length of a symbolic name. - static int const min_name_length = DENG2_URI_MIN_SCHEME_LENGTH; - - /// Manifests in the scheme are placed into a tree. - typedef PathTreeT Index; - -public: /// @todo make private: - /// Symbolic name of the scheme. - String _name; - - /// Mappings from paths to manifests. - Index _index; - - /// LUT which translates scheme-unique-ids to their associated manifest (if any). - /// Index with uniqueId - uniqueIdBase. - QList _uniqueIdLut; - bool _uniqueIdLutDirty; - int _uniqueIdBase; - -public: - /** - * Construct a new (empty) texture subspace scheme. - * - * @param symbolicName Symbolic name of the new subspace scheme. Must - * have at least @ref min_name_length characters. - */ - FontScheme(String symbolicName); - ~FontScheme(); - - /// @return Symbolic name of this scheme (e.g., "System"). - String const &name() const; - - /// @return Total number of records in the scheme. - inline int size() const { return index().size(); } - - /// @return Total number of records in the scheme. Same as @ref size(). - inline int count() const { return size(); } - - /** - * Clear all records in the scheme (any GL textures which have been - * acquired for associated font textures will be released). - */ - void clear(); - - /** - * Insert a new manifest at the given @a path into the scheme. - * If a manifest already exists at this path, the existing manifest is - * returned and the call is a no-op. - * - * @param path Virtual path for the resultant manifest. - * @return The (possibly newly created) manifest at @a path. - */ - Manifest &declare(Path const &path); - - /** - * Determines if a manifest exists on the given @a path. - * @return @c true if a manifest exists; otherwise @a false. - */ - bool has(Path const &path) const; - - /** - * Search the scheme for a manifest matching @a path. - * - * @return Found manifest. - */ - Manifest const &find(Path const &path) const; - - /// @copydoc find() - Manifest &find(Path const &path); - - /** - * Search the scheme for a manifest whose associated unique - * identifier matches @a uniqueId. - * - * @return Found manifest. - */ - Manifest const &findByUniqueId(int uniqueId) const; - - /// @copydoc findByUniqueId() - Manifest &findByUniqueId(int uniqueId); - - /** - * Provides access to the manifest index for efficient traversal. - */ - Index const &index() const; - -protected: - // Observes Manifest UniqueIdChanged - void manifestUniqueIdChanged(Manifest &manifest); - - // Observes Manifest Deletion. - void manifestBeingDeleted(Manifest const &manifest); - -private: - bool inline uniqueIdInLutRange(int uniqueId) const - { - return (uniqueId - _uniqueIdBase >= 0 && (uniqueId - _uniqueIdBase) < _uniqueIdLut.size()); - } - - void findUniqueIdRange(int *minId, int *maxId) - { - if(!minId && !maxId) return; - - if(minId) *minId = DDMAXINT; - if(maxId) *maxId = DDMININT; - - PathTreeIterator iter(_index.leafNodes()); - while(iter.hasNext()) - { - Manifest &manifest = iter.next(); - int const uniqueId = manifest.uniqueId(); - if(minId && uniqueId < *minId) *minId = uniqueId; - if(maxId && uniqueId > *maxId) *maxId = uniqueId; - } - } - - void deindex(Manifest &manifest) - { - /// @todo Only destroy the font if this is the last remaining reference. - manifest.clearFont(); - - unlinkInUniqueIdLut(manifest); - } - - /// @pre uniqueIdMap is large enough if initialized! - void unlinkInUniqueIdLut(Manifest const &manifest) - { - DENG2_ASSERT(&manifest.scheme() == this); // sanity check. - // If the lut is already considered 'dirty' do not unlink. - if(!_uniqueIdLutDirty) - { - int uniqueId = manifest.uniqueId(); - DENG2_ASSERT(uniqueIdInLutRange(uniqueId)); - _uniqueIdLut[uniqueId - _uniqueIdBase] = NOFONTID; - } - } - - /// @pre uniqueIdLut has been initialized and is large enough! - void linkInUniqueIdLut(Manifest &manifest) - { - DENG2_ASSERT(&manifest.scheme() == this); // sanity check. - int uniqueId = manifest.uniqueId(); - DENG_ASSERT(uniqueIdInLutRange(uniqueId)); - _uniqueIdLut[uniqueId - _uniqueIdBase] = &manifest; - } - - void rebuildUniqueIdLut() - { - // Is a rebuild necessary? - if(!_uniqueIdLutDirty) return; - - // Determine the size of the LUT. - int minId, maxId; - findUniqueIdRange(&minId, &maxId); - - int lutSize = 0; - if(minId > maxId) // None found? - { - _uniqueIdBase = 0; - } - else - { - _uniqueIdBase = minId; - lutSize = maxId - minId + 1; - } - - // Fill the LUT with initial values. -#ifdef DENG2_QT_4_7_OR_NEWER - _uniqueIdLut.reserve(lutSize); -#endif - int i = 0; - for(; i < _uniqueIdLut.size(); ++i) - { - _uniqueIdLut[i] = 0; - } - for(; i < lutSize; ++i) - { - _uniqueIdLut.push_back(0); - } - - if(lutSize) - { - // Populate the LUT. - PathTreeIterator iter(_index.leafNodes()); - while(iter.hasNext()) - { - linkInUniqueIdLut(iter.next()); - } - } - - _uniqueIdLutDirty = false; - } -}; - /** * Font resource collection. * diff --git a/doomsday/client/include/resource/fontscheme.h b/doomsday/client/include/resource/fontscheme.h new file mode 100644 index 0000000000..eb2b5517c8 --- /dev/null +++ b/doomsday/client/include/resource/fontscheme.h @@ -0,0 +1,245 @@ +/** @file fontscheme.h Font resource scheme. + * + * @authors Copyright © 2003-2013 Jaakko Keränen + * @authors Copyright © 2005-2013 Daniel Swanson + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. You should have received a copy of the GNU + * General Public License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef DENG_RESOURCE_FONTSCHEME_H +#define DENG_RESOURCE_FONTSCHEME_H + +#include "FontManifest" +#include "uri.hh" +#include +#include +#include +#include +#include + +namespace de { + +class FontScheme : +DENG2_OBSERVES(FontManifest, UniqueIdChanged), +DENG2_OBSERVES(FontManifest, Deletion) +{ + typedef class FontManifest Manifest; + +public: + /// The requested manifests could not be found in the index. + DENG2_ERROR(NotFoundError); + + /// The specified path was not valid. @ingroup errors + DENG2_ERROR(InvalidPathError); + + DENG2_DEFINE_AUDIENCE(ManifestDefined, void schemeManifestDefined(FontScheme &scheme, Manifest &manifest)) + + /// Minimum length of a symbolic name. + static int const min_name_length = DENG2_URI_MIN_SCHEME_LENGTH; + + /// Manifests in the scheme are placed into a tree. + typedef PathTreeT Index; + +public: /// @todo make private: + /// Symbolic name of the scheme. + String _name; + + /// Mappings from paths to manifests. + Index _index; + + /// LUT which translates scheme-unique-ids to their associated manifest (if any). + /// Index with uniqueId - uniqueIdBase. + QList _uniqueIdLut; + bool _uniqueIdLutDirty; + int _uniqueIdBase; + +public: + /** + * Construct a new (empty) texture subspace scheme. + * + * @param symbolicName Symbolic name of the new subspace scheme. Must + * have at least @ref min_name_length characters. + */ + FontScheme(String symbolicName); + ~FontScheme(); + + /// @return Symbolic name of this scheme (e.g., "System"). + String const &name() const; + + /// @return Total number of records in the scheme. + inline int size() const { return index().size(); } + + /// @return Total number of records in the scheme. Same as @ref size(). + inline int count() const { return size(); } + + /** + * Clear all records in the scheme (any GL textures which have been + * acquired for associated font textures will be released). + */ + void clear(); + + /** + * Insert a new manifest at the given @a path into the scheme. + * If a manifest already exists at this path, the existing manifest is + * returned and the call is a no-op. + * + * @param path Virtual path for the resultant manifest. + * @return The (possibly newly created) manifest at @a path. + */ + Manifest &declare(Path const &path); + + /** + * Determines if a manifest exists on the given @a path. + * @return @c true if a manifest exists; otherwise @a false. + */ + bool has(Path const &path) const; + + /** + * Search the scheme for a manifest matching @a path. + * + * @return Found manifest. + */ + Manifest const &find(Path const &path) const; + + /// @copydoc find() + Manifest &find(Path const &path); + + /** + * Search the scheme for a manifest whose associated unique + * identifier matches @a uniqueId. + * + * @return Found manifest. + */ + Manifest const &findByUniqueId(int uniqueId) const; + + /// @copydoc findByUniqueId() + Manifest &findByUniqueId(int uniqueId); + + /** + * Provides access to the manifest index for efficient traversal. + */ + Index const &index() const; + +protected: + // Observes Manifest UniqueIdChanged + void manifestUniqueIdChanged(Manifest &manifest); + + // Observes Manifest Deletion. + void manifestBeingDeleted(Manifest const &manifest); + +private: + bool inline uniqueIdInLutRange(int uniqueId) const + { + return (uniqueId - _uniqueIdBase >= 0 && (uniqueId - _uniqueIdBase) < _uniqueIdLut.size()); + } + + void findUniqueIdRange(int *minId, int *maxId) + { + if(!minId && !maxId) return; + + if(minId) *minId = DDMAXINT; + if(maxId) *maxId = DDMININT; + + PathTreeIterator iter(_index.leafNodes()); + while(iter.hasNext()) + { + Manifest &manifest = iter.next(); + int const uniqueId = manifest.uniqueId(); + if(minId && uniqueId < *minId) *minId = uniqueId; + if(maxId && uniqueId > *maxId) *maxId = uniqueId; + } + } + + void deindex(Manifest &manifest) + { + /// @todo Only destroy the font if this is the last remaining reference. + manifest.clearFont(); + + unlinkInUniqueIdLut(manifest); + } + + /// @pre uniqueIdMap is large enough if initialized! + void unlinkInUniqueIdLut(Manifest const &manifest) + { + DENG2_ASSERT(&manifest.scheme() == this); // sanity check. + // If the lut is already considered 'dirty' do not unlink. + if(!_uniqueIdLutDirty) + { + int uniqueId = manifest.uniqueId(); + DENG2_ASSERT(uniqueIdInLutRange(uniqueId)); + _uniqueIdLut[uniqueId - _uniqueIdBase] = 0; + } + } + + /// @pre uniqueIdLut has been initialized and is large enough! + void linkInUniqueIdLut(Manifest &manifest) + { + DENG2_ASSERT(&manifest.scheme() == this); // sanity check. + int uniqueId = manifest.uniqueId(); + DENG_ASSERT(uniqueIdInLutRange(uniqueId)); + _uniqueIdLut[uniqueId - _uniqueIdBase] = &manifest; + } + + void rebuildUniqueIdLut() + { + // Is a rebuild necessary? + if(!_uniqueIdLutDirty) return; + + // Determine the size of the LUT. + int minId, maxId; + findUniqueIdRange(&minId, &maxId); + + int lutSize = 0; + if(minId > maxId) // None found? + { + _uniqueIdBase = 0; + } + else + { + _uniqueIdBase = minId; + lutSize = maxId - minId + 1; + } + + // Fill the LUT with initial values. +#ifdef DENG2_QT_4_7_OR_NEWER + _uniqueIdLut.reserve(lutSize); +#endif + int i = 0; + for(; i < _uniqueIdLut.size(); ++i) + { + _uniqueIdLut[i] = 0; + } + for(; i < lutSize; ++i) + { + _uniqueIdLut.push_back(0); + } + + if(lutSize) + { + // Populate the LUT. + PathTreeIterator iter(_index.leafNodes()); + while(iter.hasNext()) + { + linkInUniqueIdLut(iter.next()); + } + } + + _uniqueIdLutDirty = false; + } +}; + +} // namespace de + +#endif // DENG_RESOURCE_FONTSCHEME_H diff --git a/doomsday/client/src/resource/fontmanifest.cpp b/doomsday/client/src/resource/fontmanifest.cpp new file mode 100644 index 0000000000..d7bfdcbc5c --- /dev/null +++ b/doomsday/client/src/resource/fontmanifest.cpp @@ -0,0 +1,123 @@ +/** @file fontmanifest.cpp Font resource manifest. + * + * @authors Copyright © 2003-2013 Jaakko Keränen + * @authors Copyright © 2005-2013 Daniel Swanson + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. You should have received a copy of the GNU + * General Public License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include "resource/fontmanifest.h" +#include "dd_main.h" // App_Fonts() +#include "FontScheme" +#include + +namespace de { + +FontManifest::FontManifest(PathTree::NodeArgs const &args) + : Node(args) + , _uniqueId(0) +{} + +FontManifest::~FontManifest() +{ + DENG2_FOR_AUDIENCE(Deletion, i) i->manifestBeingDeleted(*this); +} + +Fonts &FontManifest::fonts() +{ + return App_Fonts(); +} + +FontScheme &FontManifest::scheme() const +{ + LOG_AS("FontManifest::scheme"); + /// @todo Optimize: FontRecord should contain a link to the owning FontScheme. + foreach(FontScheme *scheme, fonts().allSchemes()) + { + if(&scheme->index() == &tree()) return *scheme; + } + /// @throw Error Failed to determine the scheme of the manifest (should never happen...). + throw Error("FontManifest::scheme", String("Failed to determine scheme for manifest [%1]").arg(de::dintptr(this))); +} + +String const &FontManifest::schemeName() const +{ + return scheme().name(); +} + +String FontManifest::description(de::Uri::ComposeAsTextFlags uriCompositionFlags) const +{ + return String("%1").arg(composeUri().compose(uriCompositionFlags | Uri::DecodePath), + ( uriCompositionFlags.testFlag(Uri::OmitScheme)? -14 : -22 ) ); +} + +int FontManifest::uniqueId() const +{ + return _uniqueId; +} + +bool FontManifest::setUniqueId(int newUniqueId) +{ + if(_uniqueId == newUniqueId) return false; + + _uniqueId = newUniqueId; + + // Notify interested parties that the uniqueId has changed. + DENG2_FOR_AUDIENCE(UniqueIdChanged, i) i->manifestUniqueIdChanged(*this); + + return true; +} + +bool FontManifest::hasFont() const +{ + return !_font.isNull(); +} + +AbstractFont &FontManifest::font() const +{ + if(hasFont()) + { + return *_font.data(); + } + /// @throw MissingFontError There is no font associated with the manifest. + throw MissingFontError("FontRecord::font", "No font is associated"); +} + +void FontManifest::setFont(AbstractFont *newFont) +{ + if(_font.data() != newFont) + { + if(AbstractFont *curFont = _font.data()) + { + // Cancel notifications about the existing font. + curFont->audienceForDeletion -= this; + } + + _font.reset(newFont); + + if(AbstractFont *curFont = _font.data()) + { + // We want notification when the new font is about to be deleted. + curFont->audienceForDeletion += this; + } + } +} + +void FontManifest::fontBeingDeleted(AbstractFont const & /*font*/) +{ + _font.reset(); +} + +} // namespace de diff --git a/doomsday/client/src/resource/fonts.cpp b/doomsday/client/src/resource/fonts.cpp index ecb5b11f17..6c7955b287 100644 --- a/doomsday/client/src/resource/fonts.cpp +++ b/doomsday/client/src/resource/fonts.cpp @@ -40,214 +40,6 @@ D_CMD(PrintFontStats); namespace de { -FontScheme::FontScheme(String symbolicName) - : _name(symbolicName) - , _uniqueIdLutDirty(false) - , _uniqueIdBase(0) -{} - -FontScheme::~FontScheme() -{ - clear(); - DENG2_ASSERT(_index.isEmpty()); // sanity check. -} - -void FontScheme::clear() -{ - /*PathTreeIterator iter(_index.leafNodes()); - while(iter.hasNext()) - { - deindex(iter.next()); - }*/ - _index.clear(); - _uniqueIdLutDirty = true; -} - -String const &FontScheme::name() const -{ - return _name; -} - -FontScheme::Manifest &FontScheme::declare(Path const &path) -{ - LOG_AS("FontScheme::declare"); - - if(path.isEmpty()) - { - /// @throw InvalidPathError An empty path was specified. - throw InvalidPathError("FontScheme::declare", "Missing/zero-length path was supplied"); - } - - int const sizeBefore = _index.size(); - Manifest *newManifest = &_index.insert(path); - DENG2_ASSERT(newManifest); - - if(_index.size() != sizeBefore) - { - // We want notification if/when the manifest's uniqueId changes. - newManifest->audienceForUniqueIdChanged += this; - - // We want notification when the manifest is about to be deleted. - newManifest->audienceForDeletion += this; - - // Notify interested parties that a new manifest was defined in the scheme. - DENG2_FOR_AUDIENCE(ManifestDefined, i) i->schemeManifestDefined(*this, *newManifest); - } - - return *newManifest; -} - -bool FontScheme::has(Path const &path) const -{ - return _index.has(path, Index::NoBranch | Index::MatchFull); -} - -FontScheme::Manifest const &FontScheme::find(Path const &path) const -{ - if(has(path)) - { - return _index.find(path, Index::NoBranch | Index::MatchFull); - } - /// @throw NotFoundError Failed to locate a matching manifest. - throw NotFoundError("FontScheme::find", "Failed to locate a manifest matching \"" + path.asText() + "\""); -} - -FontScheme::Manifest &FontScheme::find(Path const &path) -{ - Manifest const &found = const_cast(this)->find(path); - return const_cast(found); -} - -FontScheme::Manifest const &FontScheme::findByUniqueId(int uniqueId) const -{ - const_cast(this)->rebuildUniqueIdLut(); - - if(uniqueIdInLutRange(uniqueId)) - { - Manifest *manifest = _uniqueIdLut[uniqueId - _uniqueIdBase]; - if(manifest) return *manifest; - } - /// @throw NotFoundError No manifest was found with a matching resource URI. - throw NotFoundError("FontScheme::findByUniqueId", "No manifest found with a unique ID matching \"" + QString("%1").arg(uniqueId) + "\""); -} - -FontScheme::Manifest &FontScheme::findByUniqueId(int uniqueId) -{ - Manifest const &found = const_cast(this)->findByUniqueId(uniqueId); - return const_cast(found); -} - -FontScheme::Index const &FontScheme::index() const -{ - return _index; -} - -void FontScheme::manifestUniqueIdChanged(Manifest & /*manifest*/) -{ - // We'll need to rebuild the id map. - _uniqueIdLutDirty = true; -} - -void FontScheme::manifestBeingDeleted(Manifest const &manifest) -{ - deindex(const_cast(manifest)); -} - -FontManifest::FontManifest(PathTree::NodeArgs const &args) - : Node(args) - , _uniqueId(0) -{} - -FontManifest::~FontManifest() -{ - DENG2_FOR_AUDIENCE(Deletion, i) i->manifestBeingDeleted(*this); -} - -Fonts &FontManifest::fonts() -{ - return App_Fonts(); -} - -FontScheme &FontManifest::scheme() const -{ - LOG_AS("FontManifest::scheme"); - /// @todo Optimize: FontRecord should contain a link to the owning FontScheme. - foreach(FontScheme *scheme, fonts().allSchemes()) - { - if(&scheme->index() == &tree()) return *scheme; - } - /// @throw Error Failed to determine the scheme of the manifest (should never happen...). - throw Error("FontManifest::scheme", String("Failed to determine scheme for manifest [%1]").arg(de::dintptr(this))); -} - -String const &FontManifest::schemeName() const -{ - return scheme().name(); -} - -String FontManifest::description(de::Uri::ComposeAsTextFlags uriCompositionFlags) const -{ - return String("%1").arg(composeUri().compose(uriCompositionFlags | Uri::DecodePath), - ( uriCompositionFlags.testFlag(Uri::OmitScheme)? -14 : -22 ) ); -} - -int FontManifest::uniqueId() const -{ - return _uniqueId; -} - -bool FontManifest::setUniqueId(int newUniqueId) -{ - if(_uniqueId == newUniqueId) return false; - - _uniqueId = newUniqueId; - - // Notify interested parties that the uniqueId has changed. - DENG2_FOR_AUDIENCE(UniqueIdChanged, i) i->manifestUniqueIdChanged(*this); - - return true; -} - -bool FontManifest::hasFont() const -{ - return !_font.isNull(); -} - -AbstractFont &FontManifest::font() const -{ - if(hasFont()) - { - return *_font.data(); - } - /// @throw MissingFontError There is no font associated with the manifest. - throw MissingFontError("FontRecord::font", "No font is associated"); -} - -void FontManifest::setFont(AbstractFont *newFont) -{ - if(_font.data() != newFont) - { - if(AbstractFont *curFont = _font.data()) - { - // Cancel notifications about the existing font. - curFont->audienceForDeletion -= this; - } - - _font.reset(newFont); - - if(AbstractFont *curFont = _font.data()) - { - // We want notification when the new font is about to be deleted. - curFont->audienceForDeletion += this; - } - } -} - -void FontManifest::fontBeingDeleted(AbstractFont const & /*font*/) -{ - _font.reset(); -} - DENG2_PIMPL(Fonts) { /// System subspace schemes containing the textures. diff --git a/doomsday/client/src/resource/fontscheme.cpp b/doomsday/client/src/resource/fontscheme.cpp new file mode 100644 index 0000000000..f1efc6b899 --- /dev/null +++ b/doomsday/client/src/resource/fontscheme.cpp @@ -0,0 +1,139 @@ +/** @file fontscheme.cpp Font resource scheme. + * + * @authors Copyright © 2003-2013 Jaakko Keränen + * @authors Copyright © 2005-2013 Daniel Swanson + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. You should have received a copy of the GNU + * General Public License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include "resource/fontscheme.h" +#include + +namespace de { + +FontScheme::FontScheme(String symbolicName) + : _name(symbolicName) + , _uniqueIdLutDirty(false) + , _uniqueIdBase(0) +{} + +FontScheme::~FontScheme() +{ + clear(); + DENG2_ASSERT(_index.isEmpty()); // sanity check. +} + +void FontScheme::clear() +{ + /*PathTreeIterator iter(_index.leafNodes()); + while(iter.hasNext()) + { + deindex(iter.next()); + }*/ + _index.clear(); + _uniqueIdLutDirty = true; +} + +String const &FontScheme::name() const +{ + return _name; +} + +FontScheme::Manifest &FontScheme::declare(Path const &path) +{ + LOG_AS("FontScheme::declare"); + + if(path.isEmpty()) + { + /// @throw InvalidPathError An empty path was specified. + throw InvalidPathError("FontScheme::declare", "Missing/zero-length path was supplied"); + } + + int const sizeBefore = _index.size(); + Manifest *newManifest = &_index.insert(path); + DENG2_ASSERT(newManifest); + + if(_index.size() != sizeBefore) + { + // We want notification if/when the manifest's uniqueId changes. + newManifest->audienceForUniqueIdChanged += this; + + // We want notification when the manifest is about to be deleted. + newManifest->audienceForDeletion += this; + + // Notify interested parties that a new manifest was defined in the scheme. + DENG2_FOR_AUDIENCE(ManifestDefined, i) i->schemeManifestDefined(*this, *newManifest); + } + + return *newManifest; +} + +bool FontScheme::has(Path const &path) const +{ + return _index.has(path, Index::NoBranch | Index::MatchFull); +} + +FontScheme::Manifest const &FontScheme::find(Path const &path) const +{ + if(has(path)) + { + return _index.find(path, Index::NoBranch | Index::MatchFull); + } + /// @throw NotFoundError Failed to locate a matching manifest. + throw NotFoundError("FontScheme::find", "Failed to locate a manifest matching \"" + path.asText() + "\""); +} + +FontScheme::Manifest &FontScheme::find(Path const &path) +{ + Manifest const &found = const_cast(this)->find(path); + return const_cast(found); +} + +FontScheme::Manifest const &FontScheme::findByUniqueId(int uniqueId) const +{ + const_cast(this)->rebuildUniqueIdLut(); + + if(uniqueIdInLutRange(uniqueId)) + { + Manifest *manifest = _uniqueIdLut[uniqueId - _uniqueIdBase]; + if(manifest) return *manifest; + } + /// @throw NotFoundError No manifest was found with a matching resource URI. + throw NotFoundError("FontScheme::findByUniqueId", "No manifest found with a unique ID matching \"" + QString("%1").arg(uniqueId) + "\""); +} + +FontScheme::Manifest &FontScheme::findByUniqueId(int uniqueId) +{ + Manifest const &found = const_cast(this)->findByUniqueId(uniqueId); + return const_cast(found); +} + +FontScheme::Index const &FontScheme::index() const +{ + return _index; +} + +void FontScheme::manifestUniqueIdChanged(Manifest & /*manifest*/) +{ + // We'll need to rebuild the id map. + _uniqueIdLutDirty = true; +} + +void FontScheme::manifestBeingDeleted(Manifest const &manifest) +{ + deindex(const_cast(manifest)); +} + +} // namespace de