/* * This source file is part of RmlUi, the HTML/CSS Interface Middleware * * For the latest information, see http://github.com/mikke89/RmlUi * * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd * Copyright (c) 2019 The RmlUi Team, and contributors * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #include "precompiled.h" #include "StyleSheetFactory.h" #include "../../Include/RmlUi/Core/StyleSheet.h" #include "StreamFile.h" #include "StyleSheetNodeSelectorNthChild.h" #include "StyleSheetNodeSelectorNthLastChild.h" #include "StyleSheetNodeSelectorNthOfType.h" #include "StyleSheetNodeSelectorNthLastOfType.h" #include "StyleSheetNodeSelectorFirstChild.h" #include "StyleSheetNodeSelectorLastChild.h" #include "StyleSheetNodeSelectorFirstOfType.h" #include "StyleSheetNodeSelectorLastOfType.h" #include "StyleSheetNodeSelectorOnlyChild.h" #include "StyleSheetNodeSelectorOnlyOfType.h" #include "StyleSheetNodeSelectorEmpty.h" #include "../../Include/RmlUi/Core/Log.h" namespace Rml { namespace Core { static StyleSheetFactory* instance = NULL; StyleSheetFactory::StyleSheetFactory() { RMLUI_ASSERT(instance == NULL); instance = this; } StyleSheetFactory::~StyleSheetFactory() { instance = NULL; } bool StyleSheetFactory::Initialise() { new StyleSheetFactory(); instance->selectors["nth-child"] = new StyleSheetNodeSelectorNthChild(); instance->selectors["nth-last-child"] = new StyleSheetNodeSelectorNthLastChild(); instance->selectors["nth-of-type"] = new StyleSheetNodeSelectorNthOfType(); instance->selectors["nth-last-of-type"] = new StyleSheetNodeSelectorNthLastOfType(); instance->selectors["first-child"] = new StyleSheetNodeSelectorFirstChild(); instance->selectors["last-child"] = new StyleSheetNodeSelectorLastChild(); instance->selectors["first-of-type"] = new StyleSheetNodeSelectorFirstOfType(); instance->selectors["last-of-type"] = new StyleSheetNodeSelectorLastOfType(); instance->selectors["only-child"] = new StyleSheetNodeSelectorOnlyChild(); instance->selectors["only-of-type"] = new StyleSheetNodeSelectorOnlyOfType(); instance->selectors["empty"] = new StyleSheetNodeSelectorEmpty(); return true; } void StyleSheetFactory::Shutdown() { if (instance != NULL) { ClearStyleSheetCache(); for (SelectorMap::iterator i = instance->selectors.begin(); i != instance->selectors.end(); ++i) delete (*i).second; delete instance; } } SharedPtr StyleSheetFactory::GetStyleSheet(const String& sheet_name) { // Look up the sheet definition in the cache StyleSheets::iterator itr = instance->stylesheets.find(sheet_name); if (itr != instance->stylesheets.end()) { return (*itr).second; } // Don't currently have the sheet, attempt to load it SharedPtr sheet = instance->LoadStyleSheet(sheet_name); if (!sheet) return nullptr; // Add it to the cache, and add a reference count so the cache will keep hold of it. instance->stylesheets[sheet_name] = sheet; return sheet; } SharedPtr StyleSheetFactory::GetStyleSheet(const StringList& sheets) { // Generate a unique key for these sheets String combined_key; for (size_t i = 0; i < sheets.size(); i++) { URL path(sheets[i]); combined_key += path.GetFileName(); } // Look up the sheet definition in the cache. StyleSheets::iterator itr = instance->stylesheet_cache.find(combined_key); if (itr != instance->stylesheet_cache.end()) { return (*itr).second; } // Load and combine the sheets. SharedPtr sheet; for (size_t i = 0; i < sheets.size(); i++) { SharedPtr sub_sheet = GetStyleSheet(sheets[i]); if (sub_sheet) { if (sheet) { SharedPtr new_sheet = sheet->CombineStyleSheet(*sub_sheet); sheet = new_sheet; } else sheet = sub_sheet; } else Log::Message(Log::LT_ERROR, "Failed to load style sheet %s.", sheets[i].c_str()); } if (!sheet) return nullptr; // Add to cache, and a reference to the sheet to hold it in the cache. instance->stylesheet_cache[combined_key] = sheet; return sheet; } // Clear the style sheet cache. void StyleSheetFactory::ClearStyleSheetCache() { instance->stylesheets.clear(); instance->stylesheet_cache.clear(); } // Returns one of the available node selectors. StyleSheetNodeSelector* StyleSheetFactory::GetSelector(const String& name) { size_t index = name.find("("); SelectorMap::iterator i = instance->selectors.find(name.substr(0, index)); if (i == instance->selectors.end()) return NULL; return (*i).second; } SharedPtr StyleSheetFactory::LoadStyleSheet(const String& sheet) { SharedPtr new_style_sheet; // Open stream, construct new sheet and pass the stream into the sheet // TODO: Make this support ASYNC StreamFile* stream = new StreamFile(); if (stream->Open(sheet)) { new_style_sheet = std::make_shared(); if (!new_style_sheet->LoadStyleSheet(stream)) { new_style_sheet = nullptr; } } stream->RemoveReference(); return new_style_sheet; } } }