/* AbiSource * * Copyright (C) 2005 Daniel d'Andrada T. de Carvalho * * Copryight (C) 2009 Hubert Figuiere * * 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., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ // Class definition include #include "ODi_Style_Style_Family.h" // Internal includes #include "ODi_ElementStack.h" #include "ODi_Abi_Data.h" // AbiWord includes #include "ut_misc.h" #include "ut_std_map.h" /** * Destructor */ ODi_Style_Style_Family::~ODi_Style_Style_Family() { UT_map_delete_all_second(m_styles); UT_map_delete_all_second(m_styles_contentStream); DELETEP(m_pDefaultStyle); } /** * @param pReplacementName Name to replace the actual name of the style that is * being added. NULL if the name shouldn't be replaced. */ ODi_Style_Style* ODi_Style_Style_Family::addStyle(const gchar** ppAtts, ODi_ElementStack& rElementStack, ODi_Abi_Data & rAbiData, UT_UTF8String* pReplacementName, UT_UTF8String* pReplacementDisplayName) { ODi_Style_Style* pStyle = NULL; bool bOnContentStream; const gchar* pName; bOnContentStream = rElementStack.hasElement("office:document-content"); pName = UT_getAttribute("style:name", ppAtts); UT_ASSERT(pName); if (bOnContentStream) { if (pReplacementName) { StyleMap::const_iterator iter = m_styles_contentStream.find(pReplacementName->utf8_str()); if (iter == m_styles_contentStream.end()) { pStyle = new ODi_Style_Style(rElementStack,rAbiData); m_styles_contentStream.insert(std::make_pair(pReplacementName->utf8_str(), pStyle)); pStyle->setName(*pReplacementName); pStyle->setDisplayName(*pReplacementDisplayName); } } else { StyleMap::const_iterator iter = m_styles_contentStream.find(pName); if (iter == m_styles_contentStream.end()) { pStyle = new ODi_Style_Style(rElementStack,rAbiData); m_styles_contentStream.insert(std::make_pair(pName, pStyle)); } } } else { if (pReplacementName) { StyleMap::const_iterator iter = m_styles.find(pReplacementName->utf8_str()); if (iter == m_styles.end()) { pStyle = new ODi_Style_Style(rElementStack,rAbiData); m_styles.insert(std::make_pair(pReplacementName->utf8_str(), pStyle)); pStyle->setName(*pReplacementName); pStyle->setDisplayName(*pReplacementDisplayName); } } else { StyleMap::const_iterator iter = m_styles.find(pName); if (iter == m_styles.end()) { pStyle = new ODi_Style_Style(rElementStack,rAbiData); m_styles.insert(std::make_pair(pName, pStyle)); } } } if (pReplacementName != NULL) { UT_UTF8String originalName = pName; if (bOnContentStream) { m_removedStyleStyles_contentStream[pName] = pReplacementName->utf8_str(); } else { m_removedStyleStyles[pName] = pReplacementName->utf8_str(); } } return pStyle; } /** Remove any style that doesn't have properties */ void ODi_Style_Style_Family::_removeEmptyStyles(const StyleMap & map, bool bOnContentStream) { ODi_Style_Style* pStyle = NULL; bool foundNone; if(map.empty()) { return; } // TODO still very inefficient... but more than it used // to be. do { foundNone = true; for (StyleMap::const_iterator iter = map.begin(); iter != map.end(); ++iter) { if ( !iter->second->hasProperties() ) { pStyle = iter->second; foundNone = false; break; } } if (pStyle) { removeStyleStyle(pStyle, bOnContentStream); DELETEP(pStyle); } } while(!foundNone); } /** * Fix any problems encountered on the added styles. */ void ODi_Style_Style_Family::fixStyles() { // Problem 1: We can't have styles without properties // // The "Standard" paragraph style usually comes empty // (I have never seen otherwise) _removeEmptyStyles(m_styles, false); _removeEmptyStyles(m_styles_contentStream, true); } void ODi_Style_Style_Family::_buildAbiPropsAttrString(ODi_FontFaceDecls& rFontFaceDecls, const StyleMap & map) { for(StyleMap::const_iterator iter = map.begin(); iter != map.end(); ++iter) { iter->second->buildAbiPropsAttrString(rFontFaceDecls); } } /** * */ void ODi_Style_Style_Family::buildAbiPropsAttrString( ODi_FontFaceDecls& rFontFaceDecls) { if (m_pDefaultStyle != NULL) { m_pDefaultStyle->buildAbiPropsAttrString(rFontFaceDecls); } _buildAbiPropsAttrString(rFontFaceDecls, m_styles); _buildAbiPropsAttrString(rFontFaceDecls, m_styles_contentStream); } void ODi_Style_Style_Family::_reparentStyles(const StyleMap & map, const UT_UTF8String & removedName, const UT_UTF8String & replacementName) { for(StyleMap::const_iterator iter = map.begin(); iter != map.end(); ++iter) { ODi_Style_Style* pStyle = iter->second; if (pStyle->getParentName() == removedName) { pStyle->setParentName(replacementName); } if (pStyle->getNextStyleName() == removedName) { pStyle->setNextStyleName(replacementName); } } } /** * */ void ODi_Style_Style_Family::removeStyleStyle(ODi_Style_Style* pRemovedStyle, bool bOnContentStream) { UT_UTF8String replacementName; _findSuitableReplacement(replacementName, pRemovedStyle, bOnContentStream); // Remove the style itself if (bOnContentStream) { m_styles_contentStream.erase(pRemovedStyle->getName().utf8_str()); m_removedStyleStyles_contentStream[pRemovedStyle->getName().utf8_str()] = replacementName.utf8_str(); } else { m_styles.erase(pRemovedStyle->getName().utf8_str()); m_removedStyleStyles[pRemovedStyle->getName().utf8_str()] = replacementName.utf8_str(); } // Fix all references to it. // Note that automatic styles can't refer to each other. if (pRemovedStyle->isAutomatic()) { // It's an automatic style, nobody have references him. return; } if (replacementName == "") { replacementName.clear(); } // Some automatic styles defined on the content stream may have // references to this common style. _reparentStyles(m_styles_contentStream, pRemovedStyle->getName(), replacementName); // Now fix references from the styles defined on the styles stream. _reparentStyles(m_styles, pRemovedStyle->getName(), replacementName); } /** * */ void ODi_Style_Style_Family::defineAbiStyles(PD_Document* pDocument) const { if (m_pDefaultStyle) { m_pDefaultStyle->defineAbiStyle(pDocument); } for(StyleMap::const_iterator iter = m_styles.begin(); iter != m_styles.end(); ++iter) { iter->second->defineAbiStyle(pDocument); } } /** * Finds a suitable replacement for the style that will be removed. * * A suitable replacement is a parent style that has properties or, if none is * found, the default paragraph style. * * @param rReplacementName Will receive the name of the replacement style. * @param pRemovedStyle The style that will be removed. */ void ODi_Style_Style_Family::_findSuitableReplacement( UT_UTF8String& rReplacementName, const ODi_Style_Style* pRemovedStyle, bool bOnContentStream) { // Test for a "dead-end" if (pRemovedStyle->getParentName().empty()) { if (m_pDefaultStyle) { // Pretty simple. We use the default style. if (*(pRemovedStyle->getFamily()) == "paragraph") { // AbiWord uses "Normal" as the name of its default style. rReplacementName = "Normal"; } else { rReplacementName = m_pDefaultStyle->getName(); } } else { // We have no choice. There will be no substitute for this style. rReplacementName = ""; } return; } ODi_Style_Style* pStyle = NULL; if (bOnContentStream) { StyleMap::const_iterator iter = m_styles_contentStream.find(pRemovedStyle->getParentName().utf8_str()); if(iter != m_styles_contentStream.end()) { pStyle = iter->second; } } if (!pStyle) { // Must be a regular style, defined on the Styles stream. StyleMap::const_iterator iter = m_styles.find(pRemovedStyle->getParentName().utf8_str()); if(iter != m_styles.end()) { pStyle = iter->second; } } if (pStyle) { if (pStyle->hasProperties()) { // Alright, we've found it. rReplacementName = pStyle->getName(); } else { // Let's look deeper _findSuitableReplacement(rReplacementName, pStyle, bOnContentStream); } } else { std::string aString; // Was this parent already removed? if (bOnContentStream) { aString = m_removedStyleStyles_contentStream[pRemovedStyle->getParentName().utf8_str()]; } if (!pStyle) { aString = m_removedStyleStyles[pRemovedStyle->getParentName().utf8_str()]; } if(!aString.empty()) { rReplacementName = aString; } else { // I give up... if (m_pDefaultStyle) { // Pretty simple. We use the default style. if (*(pRemovedStyle->getFamily()) == "paragraph") { // AbiWord uses "Normal" as the name of its default style. rReplacementName = "Normal"; } else { rReplacementName = m_pDefaultStyle->getName(); } } else { // We have no choice. There will be no substitute for this style. rReplacementName = ""; } } } } /** * It links, if applicable, each style with its parent and its next style. * * By "linking" I mean that a given style will have a pointer to its parent * and its next style. */ void ODi_Style_Style_Family::linkStyles() { _linkStyles(m_styles, false); _linkStyles(m_styles_contentStream, true); } /** * */ const ODi_Style_Style* ODi_Style_Style_Family::getStyle(const gchar* pStyleName, bool bOnContentStream) const { UT_return_val_if_fail(pStyleName, NULL); const ODi_Style_Style* pStyle = NULL; // Is it the default style? if (m_pDefaultStyle && (m_pDefaultStyle->getName() == pStyleName)) { pStyle = m_pDefaultStyle; } if (!pStyle) { // It's not the default style. Let's search our style lists. if (bOnContentStream) { StyleMap::const_iterator iter = m_styles_contentStream.find(pStyleName); if(iter != m_styles_contentStream.end()) { pStyle = iter->second; } } if (!pStyle) { // Must be a regular style, defined on the Styles stream. StyleMap::const_iterator iter = m_styles.find(pStyleName); if(iter != m_styles.end()) { pStyle = iter->second; } } } if (!pStyle) { // We haven't found it. Have we removed it (done on _fixStyles())? std::string replacementName; if (bOnContentStream) { std::map::const_iterator iter = m_removedStyleStyles_contentStream.find(pStyleName); if(iter != m_removedStyleStyles_contentStream.end()) { replacementName = iter->second; } } if (replacementName.empty()) { std::map::const_iterator iter = m_removedStyleStyles.find(pStyleName); if(iter != m_removedStyleStyles.end()) { replacementName = iter->second; } } if (!replacementName.empty()) { // We will send back its replacement. return getStyle(replacementName.c_str(), bOnContentStream); } else { // This style never existed. // Let's return the default one instead, if there is one. if (m_pDefaultStyle != NULL) { pStyle = m_pDefaultStyle; } else { pStyle = NULL; } } } return pStyle; } /** * Helper function for linkStyles() */ void ODi_Style_Style_Family::_linkStyles(const StyleMap & map, bool onContentStream) { ODi_Style_Style* pStyle; const ODi_Style_Style* pOtherStyle; for(StyleMap::const_iterator iter = map.begin(); iter != map.end(); ++iter) { pStyle = iter->second; // Link to its parent style, if there is one. if (!pStyle->getParentName().empty()) { pOtherStyle = getStyle(pStyle->getParentName().utf8_str(), onContentStream); UT_ASSERT_HARMLESS(pOtherStyle); if (pOtherStyle) { pStyle->setParentStylePointer(pOtherStyle); } else { // We don't have this style! // Let's pretend that it never existed. pStyle->setParentName(NULL); } } // Link to its next style, if there is one. if (!pStyle->getNextStyleName().empty()) { pOtherStyle = getStyle(pStyle->getNextStyleName().utf8_str(), onContentStream); UT_ASSERT_HARMLESS(pOtherStyle); if (pOtherStyle) { pStyle->setNextStylePointer(pOtherStyle); } else { // We don't have this style! // Let's pretend that it never existed. pStyle->setNextStyleName(NULL); } } } }