/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include #include #include // Needed to load default bullet list configuration #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ::com::sun::star; sal_uInt16 SwNumRule::snRefCount = 0; SwNumFormat* SwNumRule::saBaseFormats[ RULE_END ][ MAXLEVEL ] = { {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr } }; SwNumFormat* SwNumRule::saLabelAlignmentBaseFormats[ RULE_END ][ MAXLEVEL ] = { {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr } }; const sal_uInt16 SwNumRule::saDefNumIndents[ MAXLEVEL ] = { o3tl::toTwips(25, o3tl::Length::in100), o3tl::toTwips(50, o3tl::Length::in100), o3tl::toTwips(75, o3tl::Length::in100), o3tl::toTwips(100, o3tl::Length::in100), o3tl::toTwips(125, o3tl::Length::in100), o3tl::toTwips(150, o3tl::Length::in100), o3tl::toTwips(175, o3tl::Length::in100), o3tl::toTwips(200, o3tl::Length::in100), o3tl::toTwips(225, o3tl::Length::in100), o3tl::toTwips(250, o3tl::Length::in100), }; OUString SwNumRule::GetOutlineRuleName() { return "Outline"; } const SwNumFormat& SwNumRule::Get( sal_uInt16 i ) const { assert( i < MAXLEVEL && meRuleType < RULE_END ); return maFormats[ i ] ? *maFormats[ i ] : ( meDefaultNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION ? *saBaseFormats[ meRuleType ][ i ] : *saLabelAlignmentBaseFormats[ meRuleType ][ i ] ); } const SwNumFormat* SwNumRule::GetNumFormat( sal_uInt16 i ) const { const SwNumFormat * pResult = nullptr; assert( i < MAXLEVEL && meRuleType < RULE_END ); if ( i < MAXLEVEL && meRuleType < RULE_END) { pResult = maFormats[ i ].get(); } return pResult; } // #i91400# void SwNumRule::SetName( const OUString & rName, IDocumentListsAccess& rDocListAccess) { if ( msName == rName ) return; if (mpNumRuleMap) { mpNumRuleMap->erase(msName); (*mpNumRuleMap)[rName] = this; if ( !GetDefaultListId().isEmpty() ) { rDocListAccess.trackChangeOfListStyleName( msName, rName ); } } msName = rName; } void SwNumRule::GetTextNodeList( SwNumRule::tTextNodeList& rTextNodeList ) const { rTextNodeList = maTextNodeList; } SwNumRule::tTextNodeList::size_type SwNumRule::GetTextNodeListSize() const { return maTextNodeList.size(); } void SwNumRule::AddTextNode( SwTextNode& rTextNode ) { tTextNodeList::iterator aIter = std::find( maTextNodeList.begin(), maTextNodeList.end(), &rTextNode ); if ( aIter == maTextNodeList.end() ) { maTextNodeList.push_back( &rTextNode ); } } void SwNumRule::RemoveTextNode( SwTextNode& rTextNode ) { tTextNodeList::iterator aIter = std::find( maTextNodeList.begin(), maTextNodeList.end(), &rTextNode ); if ( aIter != maTextNodeList.end() ) { maTextNodeList.erase( aIter ); } } void SwNumRule::SetNumRuleMap(std::unordered_map * pNumRuleMap) { mpNumRuleMap = pNumRuleMap; } sal_uInt16 SwNumRule::GetNumIndent( sal_uInt8 nLvl ) { OSL_ENSURE( MAXLEVEL > nLvl, "NumLevel is out of range" ); return saDefNumIndents[ nLvl ]; } sal_uInt16 SwNumRule::GetBullIndent( sal_uInt8 nLvl ) { OSL_ENSURE( MAXLEVEL > nLvl, "NumLevel is out of range" ); return saDefNumIndents[ nLvl ]; } static void lcl_SetRuleChgd( SwTextNode& rNd, sal_uInt8 nLevel ) { if( rNd.GetActualListLevel() == nLevel ) rNd.NumRuleChgd(); } SwNumFormat::SwNumFormat() : SvxNumberFormat(SVX_NUM_ARABIC), SwClient( nullptr ), m_pVertOrient(new SwFormatVertOrient( 0, text::VertOrientation::NONE)) ,m_cGrfBulletCP(USHRT_MAX)//For i120928,record the cp info of graphic within bullet { } SwNumFormat::SwNumFormat( const SwNumFormat& rFormat) : SvxNumberFormat(rFormat), SwClient( rFormat.GetRegisteredInNonConst() ), m_pVertOrient(new SwFormatVertOrient( 0, rFormat.GetVertOrient())) ,m_cGrfBulletCP(rFormat.m_cGrfBulletCP)//For i120928,record the cp info of graphic within bullet { sal_Int16 eMyVertOrient = rFormat.GetVertOrient(); SetGraphicBrush( rFormat.GetBrush(), &rFormat.GetGraphicSize(), &eMyVertOrient); } SwNumFormat::SwNumFormat(const SvxNumberFormat& rNumFormat, SwDoc* pDoc) : SvxNumberFormat(rNumFormat) , m_pVertOrient(new SwFormatVertOrient( 0, rNumFormat.GetVertOrient())) , m_cGrfBulletCP(USHRT_MAX) { sal_Int16 eMyVertOrient = rNumFormat.GetVertOrient(); SetGraphicBrush( rNumFormat.GetBrush(), &rNumFormat.GetGraphicSize(), &eMyVertOrient); const OUString rCharStyleName = rNumFormat.SvxNumberFormat::GetCharFormatName(); if( !rCharStyleName.isEmpty() ) { SwCharFormat* pCFormat = pDoc->FindCharFormatByName( rCharStyleName ); if( !pCFormat ) { sal_uInt16 nId = SwStyleNameMapper::GetPoolIdFromUIName( rCharStyleName, SwGetPoolIdFromName::ChrFmt ); pCFormat = nId != USHRT_MAX ? pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool( nId ) : pDoc->MakeCharFormat( rCharStyleName, nullptr ); } pCFormat->Add( this ); } else EndListeningAll(); } SwNumFormat::~SwNumFormat() { } // #i22362# bool SwNumFormat::IsEnumeration() const { // #i30655# native numbering did not work any longer // using this code. Therefore HBRINKM and I agreed upon defining // IsEnumeration() as !IsItemize() return !IsItemize(); } bool SwNumFormat::IsItemize() const { bool bResult; switch(GetNumberingType()) { case SVX_NUM_CHAR_SPECIAL: case SVX_NUM_BITMAP: bResult = true; break; default: bResult = false; } return bResult; } SwNumFormat& SwNumFormat::operator=( const SwNumFormat& rNumFormat) { SvxNumberFormat::operator=(rNumFormat); StartListeningToSameModifyAs(rNumFormat); //For i120928,record the cp info of graphic within bullet m_cGrfBulletCP = rNumFormat.m_cGrfBulletCP; return *this; } bool SwNumFormat::operator==( const SwNumFormat& rNumFormat) const { bool bRet = SvxNumberFormat::operator==(rNumFormat) && GetRegisteredIn() == rNumFormat.GetRegisteredIn(); return bRet; } void SwNumFormat::SetCharFormat( SwCharFormat* pChFormat) { if( pChFormat ) pChFormat->Add( this ); else EndListeningAll(); } void SwNumFormat::SwClientNotify(const SwModify&, const SfxHint& rHint) { if (rHint.GetId() != SfxHintId::SwLegacyModify) return; auto pLegacy = static_cast(&rHint); // Look for the NumRules object in the Doc where this NumFormat is set. // The format does not need to exist! const SwCharFormat* pFormat = nullptr; switch(pLegacy->GetWhich()) { case RES_ATTRSET_CHG: case RES_FMT_CHG: pFormat = GetCharFormat(); break; } if(pFormat && !pFormat->GetDoc()->IsInDtor()) UpdateNumNodes(*const_cast(pFormat->GetDoc())); else CheckRegistration(pLegacy->m_pOld); } OUString SwNumFormat::GetCharFormatName() const { if(static_cast(GetRegisteredIn())) return static_cast(GetRegisteredIn())->GetName(); return OUString(); } void SwNumFormat::SetGraphicBrush( const SvxBrushItem* pBrushItem, const Size* pSize, const sal_Int16* pOrient) { if(pOrient) m_pVertOrient->SetVertOrient( *pOrient ); SvxNumberFormat::SetGraphicBrush( pBrushItem, pSize, pOrient); } void SwNumFormat::UpdateNumNodes( SwDoc& rDoc ) { bool bDocIsModified = rDoc.getIDocumentState().IsModified(); bool bFnd = false; for( SwNumRuleTable::size_type n = rDoc.GetNumRuleTable().size(); !bFnd && n; ) { const SwNumRule* pRule = rDoc.GetNumRuleTable()[ --n ]; for( sal_uInt8 i = 0; i < MAXLEVEL; ++i ) if( pRule->GetNumFormat( i ) == this ) { SwNumRule::tTextNodeList aTextNodeList; pRule->GetTextNodeList( aTextNodeList ); for ( auto& rpTextNode : aTextNodeList ) { lcl_SetRuleChgd( *rpTextNode, i ); } bFnd = true; break; } } if( bFnd && !bDocIsModified ) rDoc.getIDocumentState().ResetModified(); } const SwFormatVertOrient* SwNumFormat::GetGraphicOrientation() const { sal_Int16 eOrient = SvxNumberFormat::GetVertOrient(); if(text::VertOrientation::NONE == eOrient) return nullptr; else { m_pVertOrient->SetVertOrient(eOrient); return m_pVertOrient.get(); } } SwNumRule::SwNumRule( const OUString& rNm, const SvxNumberFormat::SvxNumPositionAndSpaceMode eDefaultNumberFormatPositionAndSpaceMode, SwNumRuleType eType ) : mpNumRuleMap(nullptr), msName( rNm ), meRuleType( eType ), mnPoolFormatId( USHRT_MAX ), mnPoolHelpId( USHRT_MAX ), mnPoolHlpFileId( UCHAR_MAX ), mbAutoRuleFlag( true ), mbInvalidRuleFlag( true ), mbContinusNum( false ), mbAbsSpaces( false ), mbHidden( false ), mbCountPhantoms( true ), mbUsedByRedline( false ), meDefaultNumberFormatPositionAndSpaceMode( eDefaultNumberFormatPositionAndSpaceMode ) { if( !snRefCount++ ) // for the first time, initialize { SwNumFormat* pFormat; sal_uInt8 n; // numbering: // position-and-space mode LABEL_WIDTH_AND_POSITION: for( n = 0; n < MAXLEVEL; ++n ) { pFormat = new SwNumFormat; pFormat->SetIncludeUpperLevels( 1 ); pFormat->SetStart( 1 ); pFormat->SetAbsLSpace( lNumberIndent + SwNumRule::GetNumIndent( n ) ); pFormat->SetFirstLineOffset( lNumberFirstLineOffset ); pFormat->SetListFormat("%" + OUString::number(n + 1) + "%."); pFormat->SetBulletChar(numfunc::GetBulletChar(n)); SwNumRule::saBaseFormats[ NUM_RULE ][ n ] = pFormat; } // position-and-space mode LABEL_ALIGNMENT // first line indent of general numbering in inch: -0,25 inch const tools::Long cFirstLineIndent = o3tl::toTwips(-0.25, o3tl::Length::in); // indent values of general numbering in inch: const tools::Long cIndentAt[ MAXLEVEL ] = { o3tl::toTwips(50, o3tl::Length::in100), o3tl::toTwips(75, o3tl::Length::in100), o3tl::toTwips(100, o3tl::Length::in100), o3tl::toTwips(125, o3tl::Length::in100), o3tl::toTwips(150, o3tl::Length::in100), o3tl::toTwips(175, o3tl::Length::in100), o3tl::toTwips(200, o3tl::Length::in100), o3tl::toTwips(225, o3tl::Length::in100), o3tl::toTwips(250, o3tl::Length::in100), o3tl::toTwips(275, o3tl::Length::in100), }; for( n = 0; n < MAXLEVEL; ++n ) { pFormat = new SwNumFormat; pFormat->SetIncludeUpperLevels( 1 ); pFormat->SetStart( 1 ); pFormat->SetPositionAndSpaceMode( SvxNumberFormat::LABEL_ALIGNMENT ); pFormat->SetLabelFollowedBy( SvxNumberFormat::LISTTAB ); pFormat->SetListtabPos( cIndentAt[ n ] ); pFormat->SetFirstLineIndent( cFirstLineIndent ); pFormat->SetIndentAt( cIndentAt[ n ] ); pFormat->SetListFormat( "%" + OUString::number(n + 1) + "%."); pFormat->SetBulletChar( numfunc::GetBulletChar(n)); SwNumRule::saLabelAlignmentBaseFormats[ NUM_RULE ][ n ] = pFormat; } // outline: // position-and-space mode LABEL_WIDTH_AND_POSITION: for( n = 0; n < MAXLEVEL; ++n ) { pFormat = new SwNumFormat; pFormat->SetNumberingType(SVX_NUM_NUMBER_NONE); pFormat->SetIncludeUpperLevels( MAXLEVEL ); pFormat->SetStart( 1 ); pFormat->SetCharTextDistance( lOutlineMinTextDistance ); pFormat->SetBulletChar( numfunc::GetBulletChar(n)); SwNumRule::saBaseFormats[ OUTLINE_RULE ][ n ] = pFormat; } // position-and-space mode LABEL_ALIGNMENT: for( n = 0; n < MAXLEVEL; ++n ) { pFormat = new SwNumFormat; pFormat->SetNumberingType(SVX_NUM_NUMBER_NONE); pFormat->SetIncludeUpperLevels( MAXLEVEL ); pFormat->SetStart( 1 ); pFormat->SetPositionAndSpaceMode( SvxNumberFormat::LABEL_ALIGNMENT ); pFormat->SetBulletChar( numfunc::GetBulletChar(n)); SwNumRule::saLabelAlignmentBaseFormats[ OUTLINE_RULE ][ n ] = pFormat; } } OSL_ENSURE( !msName.isEmpty(), "NumRule without a name!" ); } SwNumRule::SwNumRule( const SwNumRule& rNumRule ) : mpNumRuleMap(nullptr), msName( rNumRule.msName ), meRuleType( rNumRule.meRuleType ), mnPoolFormatId( rNumRule.GetPoolFormatId() ), mnPoolHelpId( rNumRule.GetPoolHelpId() ), mnPoolHlpFileId( rNumRule.GetPoolHlpFileId() ), mbAutoRuleFlag( rNumRule.mbAutoRuleFlag ), mbInvalidRuleFlag( true ), mbContinusNum( rNumRule.mbContinusNum ), mbAbsSpaces( rNumRule.mbAbsSpaces ), mbHidden( rNumRule.mbHidden ), mbCountPhantoms( true ), mbUsedByRedline( false ), meDefaultNumberFormatPositionAndSpaceMode( rNumRule.meDefaultNumberFormatPositionAndSpaceMode ), msDefaultListId( rNumRule.msDefaultListId ) { ++snRefCount; for( sal_uInt16 n = 0; n < MAXLEVEL; ++n ) if( rNumRule.maFormats[ n ] ) Set( n, *rNumRule.maFormats[ n ] ); } SwNumRule::~SwNumRule() { for (auto & i : maFormats) i.reset(); if (mpNumRuleMap) { mpNumRuleMap->erase(GetName()); } if( !--snRefCount ) // the last one closes the door (?) { // Numbering: SwNumFormat** ppFormats = &SwNumRule::saBaseFormats[0][0]; int n; for( n = 0; n < MAXLEVEL; ++n, ++ppFormats ) { delete *ppFormats; *ppFormats = nullptr; } // Outline: for( n = 0; n < MAXLEVEL; ++n, ++ppFormats ) { delete *ppFormats; *ppFormats = nullptr; } ppFormats = &SwNumRule::saLabelAlignmentBaseFormats[0][0]; for( n = 0; n < MAXLEVEL; ++n, ++ppFormats ) { delete *ppFormats; *ppFormats = nullptr; } for( n = 0; n < MAXLEVEL; ++n, ++ppFormats ) { delete *ppFormats; *ppFormats = nullptr; } } maTextNodeList.clear(); maParagraphStyleList.clear(); } void SwNumRule::CheckCharFormats( SwDoc& rDoc ) { for(auto& rpNumFormat : maFormats) { if( rpNumFormat ) { SwCharFormat* pFormat = rpNumFormat->GetCharFormat(); if( pFormat && pFormat->GetDoc() != &rDoc ) { // copy SwNumFormat* pNew = new SwNumFormat( *rpNumFormat ); pNew->SetCharFormat( rDoc.CopyCharFormat( *pFormat ) ); rpNumFormat.reset(pNew); } } } } SwNumRule& SwNumRule::operator=( const SwNumRule& rNumRule ) { if( this != &rNumRule ) { for( sal_uInt16 n = 0; n < MAXLEVEL; ++n ) Set( n, rNumRule.maFormats[ n ].get() ); meRuleType = rNumRule.meRuleType; msName = rNumRule.msName; mbAutoRuleFlag = rNumRule.mbAutoRuleFlag; mbInvalidRuleFlag = true; mbContinusNum = rNumRule.mbContinusNum; mbAbsSpaces = rNumRule.mbAbsSpaces; mbHidden = rNumRule.mbHidden; mnPoolFormatId = rNumRule.GetPoolFormatId(); mnPoolHelpId = rNumRule.GetPoolHelpId(); mnPoolHlpFileId = rNumRule.GetPoolHlpFileId(); } return *this; } void SwNumRule::Reset( const OUString& rName ) { for( sal_uInt16 n = 0; n < MAXLEVEL; ++n ) Set( n, nullptr); meRuleType = NUM_RULE; msName = rName; mbAutoRuleFlag = true; mbInvalidRuleFlag = true; mbContinusNum = false; mbAbsSpaces = false; mbHidden = false; mnPoolFormatId = USHRT_MAX; mnPoolHelpId = USHRT_MAX; mnPoolHlpFileId = UCHAR_MAX; } bool SwNumRule::operator==( const SwNumRule& rRule ) const { bool bRet = meRuleType == rRule.meRuleType && msName == rRule.msName && mbAutoRuleFlag == rRule.mbAutoRuleFlag && mbContinusNum == rRule.mbContinusNum && mbAbsSpaces == rRule.mbAbsSpaces && mnPoolFormatId == rRule.GetPoolFormatId() && mnPoolHelpId == rRule.GetPoolHelpId() && mnPoolHlpFileId == rRule.GetPoolHlpFileId(); if( bRet ) { for( sal_uInt8 n = 0; n < MAXLEVEL; ++n ) if( rRule.Get( n ) != Get( n ) ) { bRet = false; break; } } return bRet; } void SwNumRule::Set( sal_uInt16 i, const SwNumFormat& rNumFormat ) { OSL_ENSURE( i < MAXLEVEL, "Serious defect" ); if( i < MAXLEVEL ) { if( !maFormats[ i ] || (rNumFormat != Get( i )) ) { maFormats[ i ].reset(new SwNumFormat( rNumFormat )); mbInvalidRuleFlag = true; } } } void SwNumRule::Set( sal_uInt16 i, const SwNumFormat* pNumFormat ) { OSL_ENSURE( i < MAXLEVEL, "Serious defect" ); if( i >= MAXLEVEL ) return; if( !maFormats[ i ] ) { if( pNumFormat ) { maFormats[ i ].reset(new SwNumFormat( *pNumFormat )); mbInvalidRuleFlag = true; } } else if( !pNumFormat ) { maFormats[ i ].reset(); mbInvalidRuleFlag = true; } else if( *maFormats[i] != *pNumFormat ) { *maFormats[ i ] = *pNumFormat; mbInvalidRuleFlag = true; } } OUString SwNumRule::MakeNumString( const SwNodeNum& rNum, bool bInclStrings ) const { if (rNum.IsCounted()) return MakeNumString(rNum.GetNumberVector(), bInclStrings); return OUString(); } OUString SwNumRule::MakeNumString( const SwNumberTree::tNumberVector & rNumVector, const bool bInclStrings, const unsigned int _nRestrictToThisLevel, SwNumRule::Extremities* pExtremities, LanguageType nLang ) const { OUStringBuffer aStr; SwNumberTree::tNumberVector::size_type nLevel = rNumVector.size() - 1; if ( pExtremities ) pExtremities->nPrefixChars = pExtremities->nSuffixChars = 0; if ( nLevel > _nRestrictToThisLevel ) { nLevel = _nRestrictToThisLevel; } assert(nLevel < MAXLEVEL); const SwNumFormat& rMyNFormat = Get( o3tl::narrowing(nLevel) ); if (rMyNFormat.GetNumberingType() == SVX_NUM_NUMBER_NONE) { if (!rMyNFormat.HasListFormat()) return OUString(); // If numbering is disabled for this level we should emit just prefix/suffix // Remove everything between first %1% and last %n% (including markers) OUString sLevelFormat = rMyNFormat.GetListFormat(bInclStrings); sal_Int32 nFirstPosition = sLevelFormat.indexOf("%"); sal_Int32 nLastPosition = sLevelFormat.lastIndexOf("%"); if (nFirstPosition >= 0 && nLastPosition >= nFirstPosition) sLevelFormat = sLevelFormat.replaceAt(nFirstPosition, nLastPosition - nFirstPosition + 1, u""); return sLevelFormat; } css::lang::Locale aLocale( LanguageTag::convertToLocale(nLang)); if (rMyNFormat.HasListFormat()) { OUString sLevelFormat = rMyNFormat.GetListFormat(bInclStrings); // In this case we are ignoring GetIncludeUpperLevels: we put all // level numbers requested by level format for (SwNumberTree::tNumberVector::size_type i=0; i <= nLevel; ++i) { OUString sReplacement; const SwNumFormat& rNFormat = Get(i); if (rNFormat.GetNumberingType() == SVX_NUM_NUMBER_NONE) { // Numbering disabled - replacement is empty // And we should skip all level string content until next level marker: // so %1%.%2%.%3% with second level as NONE will result 1.1, not 1..1 OUString sFind("%" + OUString::number(i + 1) + "%"); sal_Int32 nPositionToken = sLevelFormat.indexOf(sFind); sal_Int32 nPositionNextToken = sLevelFormat.indexOf('%', nPositionToken + sFind.getLength()); if (nPositionToken >= 0 && nPositionNextToken >= nPositionToken) { sLevelFormat = sLevelFormat.replaceAt(nPositionToken, nPositionNextToken - nPositionToken, u""); } } else if (rNumVector[i]) sReplacement = Get(i).GetNumStr(rNumVector[i], aLocale); else sReplacement = "0"; // all 0 level are a 0 OUString sFind("%" + OUString::number(i + 1) + "%"); sal_Int32 nPosition = sLevelFormat.indexOf(sFind); if (nPosition >= 0) sLevelFormat = sLevelFormat.replaceAt(nPosition, sFind.getLength(), sReplacement); } aStr = sLevelFormat; } else { // Fallback case: level format is not defined // So use old way with levels joining by dot "." SwNumberTree::tNumberVector::size_type i = nLevel; if (!IsContinusNum() && // - do not include upper levels, if level isn't numbered. rMyNFormat.GetNumberingType() != SVX_NUM_NUMBER_NONE && rMyNFormat.GetIncludeUpperLevels()) // Just the own level? { sal_uInt8 n = rMyNFormat.GetIncludeUpperLevels(); if (1 < n) { if (i + 1 >= n) i -= n - 1; else i = 0; } } for (; i <= nLevel; ++i) { const SwNumFormat& rNFormat = Get(i); if (SVX_NUM_NUMBER_NONE == rNFormat.GetNumberingType()) { // Should 1.1.1 --> 2. NoNum --> 1..1 or 1.1 ?? // if( i != rNum.nMyLevel ) // aStr += "."; continue; } if (rNumVector[i]) aStr.append(rNFormat.GetNumStr(rNumVector[i], aLocale)); else aStr.append("0"); // all 0 level are a 0 if (i != nLevel && !aStr.isEmpty()) aStr.append("."); } // The type doesn't have any number, so don't append // the post-/prefix string if (bInclStrings && SVX_NUM_CHAR_SPECIAL != rMyNFormat.GetNumberingType() && SVX_NUM_BITMAP != rMyNFormat.GetNumberingType()) { const OUString& sPrefix = rMyNFormat.GetPrefix(); const OUString& sSuffix = rMyNFormat.GetSuffix(); aStr.insert(0, sPrefix); aStr.append(sSuffix); if (pExtremities) { pExtremities->nPrefixChars = sPrefix.getLength(); pExtremities->nSuffixChars = sSuffix.getLength(); } } } return aStr.makeStringAndClear(); } OUString SwNumRule::MakeRefNumString( const SwNodeNum& rNodeNum, const bool bInclSuperiorNumLabels, const int nRestrictInclToThisLevel ) const { OUString aRefNumStr; if ( rNodeNum.GetLevelInListTree() >= 0 ) { bool bOldHadPrefix = true; const SwNodeNum* pWorkingNodeNum( &rNodeNum ); do { bool bMakeNumStringForPhantom( false ); if ( pWorkingNodeNum->IsPhantom() ) { int nListLevel = pWorkingNodeNum->GetLevelInListTree(); if (nListLevel < 0) nListLevel = 0; if (nListLevel >= MAXLEVEL) nListLevel = MAXLEVEL - 1; SwNumFormat aFormat( Get( o3tl::narrowing(nListLevel) ) ); bMakeNumStringForPhantom = aFormat.IsEnumeration() && SVX_NUM_NUMBER_NONE != aFormat.GetNumberingType(); } if ( bMakeNumStringForPhantom || ( !pWorkingNodeNum->IsPhantom() && pWorkingNodeNum->GetTextNode() && pWorkingNodeNum->GetTextNode()->HasNumber() ) ) { Extremities aExtremities; OUString aPrevStr = MakeNumString( pWorkingNodeNum->GetNumberVector(), true, MAXLEVEL, &aExtremities); sal_Int32 nStrip = 0; while ( nStrip < aExtremities.nPrefixChars ) { const sal_Unicode c = aPrevStr[nStrip]; if ( c!='\t' && c!=' ') break; ++nStrip; } if (nStrip) { aPrevStr = aPrevStr.copy( nStrip ); aExtremities.nPrefixChars -= nStrip; } if (bOldHadPrefix && aExtremities.nSuffixChars && !aExtremities.nPrefixChars ) { aPrevStr = aPrevStr.copy(0, aPrevStr.getLength() - aExtremities.nSuffixChars); } bOldHadPrefix = ( aExtremities.nPrefixChars > 0); aRefNumStr = aPrevStr + aRefNumStr; } if ( bInclSuperiorNumLabels && pWorkingNodeNum->GetLevelInListTree() > 0 ) { sal_uInt8 n = Get( o3tl::narrowing(pWorkingNodeNum->GetLevelInListTree()) ).GetIncludeUpperLevels(); pWorkingNodeNum = dynamic_cast(pWorkingNodeNum->GetParent()); // skip parents, whose list label is already contained in the actual list label. while ( pWorkingNodeNum && n > 1 ) { pWorkingNodeNum = dynamic_cast(pWorkingNodeNum->GetParent()); --n; } } else { break; } } while ( pWorkingNodeNum && pWorkingNodeNum->GetLevelInListTree() >= 0 && pWorkingNodeNum->GetLevelInListTree() >= nRestrictInclToThisLevel ); } if (aRefNumStr.endsWith(".")) { // tdf#144563: looks like a special case for refs by MS Word: if numbering is ending with dot, this dot is removed aRefNumStr = aRefNumStr.copy(0, aRefNumStr.getLength() - 1); } return aRefNumStr; } OUString SwNumRule::MakeParagraphStyleListString() const { OUString aParagraphStyleListString; for (const auto& rParagraphStyle : maParagraphStyleList) { if (!aParagraphStyleListString.isEmpty()) aParagraphStyleListString += ", "; aParagraphStyleListString += rParagraphStyle->GetName(); } return aParagraphStyleListString; } /** Copy method of SwNumRule A kind of copy constructor, so that the num formats are attached to the right CharFormats of a Document. Copies the NumFormats and returns itself. */ SwNumRule& SwNumRule::CopyNumRule( SwDoc& rDoc, const SwNumRule& rNumRule ) { for( sal_uInt16 n = 0; n < MAXLEVEL; ++n ) { Set( n, rNumRule.maFormats[ n ].get() ); if( maFormats[ n ] && maFormats[ n ]->GetCharFormat() && !rDoc.GetCharFormats()->ContainsFormat(maFormats[n]->GetCharFormat())) { // If we copy across different Documents, then copy the // corresponding CharFormat into the new Document. maFormats[n]->SetCharFormat( rDoc.CopyCharFormat( *maFormats[n]-> GetCharFormat() ) ); } } meRuleType = rNumRule.meRuleType; msName = rNumRule.msName; mbAutoRuleFlag = rNumRule.mbAutoRuleFlag; mnPoolFormatId = rNumRule.GetPoolFormatId(); mnPoolHelpId = rNumRule.GetPoolHelpId(); mnPoolHlpFileId = rNumRule.GetPoolHlpFileId(); mbInvalidRuleFlag = true; return *this; } void SwNumRule::SetSvxRule(const SvxNumRule& rNumRule, SwDoc* pDoc) { for( sal_uInt16 n = 0; n < MAXLEVEL; ++n ) { const SvxNumberFormat* pSvxFormat = rNumRule.Get(n); maFormats[n].reset( pSvxFormat ? new SwNumFormat(*pSvxFormat, pDoc) : nullptr ); } mbInvalidRuleFlag = true; mbContinusNum = rNumRule.IsContinuousNumbering(); } SvxNumRule SwNumRule::MakeSvxNumRule() const { SvxNumRule aRule(SvxNumRuleFlags::CONTINUOUS | SvxNumRuleFlags::CHAR_STYLE | SvxNumRuleFlags::ENABLE_LINKED_BMP | SvxNumRuleFlags::ENABLE_EMBEDDED_BMP, MAXLEVEL, mbContinusNum, meRuleType == NUM_RULE ? SvxNumRuleType::NUMBERING : SvxNumRuleType::OUTLINE_NUMBERING ); for( sal_uInt16 n = 0; n < MAXLEVEL; ++n ) { const SwNumFormat & rNumFormat = Get(n); if(rNumFormat.GetCharFormat()) { SwNumFormat aNewFormat = rNumFormat; aNewFormat.SetCharFormatName(rNumFormat.GetCharFormat()->GetName()); aRule.SetLevel(n, aNewFormat, maFormats[n] != nullptr); } else aRule.SetLevel(n, rNumFormat, maFormats[n] != nullptr); } return aRule; } void SwNumRule::SetInvalidRule(bool bFlag) { if (bFlag) { o3tl::sorted_vector< SwList* > aLists; for ( const SwTextNode* pTextNode : maTextNodeList ) { // #i111681# - applying patch from cmc SwList* pList = pTextNode->GetDoc().getIDocumentListsAccess().getListByName( pTextNode->GetListId() ); OSL_ENSURE( pList, " - list at which the text node is registered at does not exist. This is a serious issue."); if ( pList ) { aLists.insert( pList ); } } for ( auto aList : aLists ) aList->InvalidateListTree(); } mbInvalidRuleFlag = bFlag; } /// change indent of all list levels by given difference void SwNumRule::ChangeIndent( const sal_Int32 nDiff ) { for ( sal_uInt16 i = 0; i < MAXLEVEL; ++i ) { SwNumFormat aTmpNumFormat( Get(i) ); const SvxNumberFormat::SvxNumPositionAndSpaceMode ePosAndSpaceMode( aTmpNumFormat.GetPositionAndSpaceMode() ); if ( ePosAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) { auto nNewIndent = nDiff + aTmpNumFormat.GetAbsLSpace(); if ( nNewIndent < 0 ) { nNewIndent = 0; } aTmpNumFormat.SetAbsLSpace( nNewIndent ); } else if ( ePosAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT ) { // adjust also the list tab position, if a list tab stop is applied if ( aTmpNumFormat.GetLabelFollowedBy() == SvxNumberFormat::LISTTAB ) { const tools::Long nNewListTab = aTmpNumFormat.GetListtabPos() + nDiff; aTmpNumFormat.SetListtabPos( nNewListTab ); } const tools::Long nNewIndent = nDiff + aTmpNumFormat.GetIndentAt(); aTmpNumFormat.SetIndentAt( nNewIndent ); } Set( i, aTmpNumFormat ); } SetInvalidRule( true ); } /// set indent of certain list level to given value void SwNumRule::SetIndent( const short nNewIndent, const sal_uInt16 nListLevel ) { SwNumFormat aTmpNumFormat( Get(nListLevel) ); const SvxNumberFormat::SvxNumPositionAndSpaceMode ePosAndSpaceMode( aTmpNumFormat.GetPositionAndSpaceMode() ); if ( ePosAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) { aTmpNumFormat.SetAbsLSpace( nNewIndent ); } else if ( ePosAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT ) { // adjust also the list tab position, if a list tab stop is applied if ( aTmpNumFormat.GetLabelFollowedBy() == SvxNumberFormat::LISTTAB ) { const tools::Long nNewListTab = aTmpNumFormat.GetListtabPos() + ( nNewIndent - aTmpNumFormat.GetIndentAt() ); aTmpNumFormat.SetListtabPos( nNewListTab ); } aTmpNumFormat.SetIndentAt( nNewIndent ); } SetInvalidRule( true ); } /// set indent of first list level to given value and change other list level's /// indents accordingly void SwNumRule::SetIndentOfFirstListLevelAndChangeOthers( const short nNewIndent ) { SwNumFormat aTmpNumFormat( Get(0) ); sal_Int32 nDiff( 0 ); const SvxNumberFormat::SvxNumPositionAndSpaceMode ePosAndSpaceMode( aTmpNumFormat.GetPositionAndSpaceMode() ); if ( ePosAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) { nDiff = nNewIndent - aTmpNumFormat.GetFirstLineOffset() - aTmpNumFormat.GetAbsLSpace(); } else if ( ePosAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT ) { nDiff = nNewIndent - aTmpNumFormat.GetIndentAt(); } if ( nDiff != 0 ) { ChangeIndent( nDiff ); } } void SwNumRule::Validate(const SwDoc& rDoc) { o3tl::sorted_vector< SwList* > aLists; for ( const SwTextNode* pTextNode : maTextNodeList ) { aLists.insert( pTextNode->GetDoc().getIDocumentListsAccess().getListByName( pTextNode->GetListId() ) ); } for ( auto aList : aLists ) aList->ValidateListTree(rDoc); SetInvalidRule(false); } void SwNumRule::SetCountPhantoms(bool bCountPhantoms) { mbCountPhantoms = bCountPhantoms; } SwNumRule::tParagraphStyleList::size_type SwNumRule::GetParagraphStyleListSize() const { return maParagraphStyleList.size(); } void SwNumRule::AddParagraphStyle( SwTextFormatColl& rTextFormatColl ) { tParagraphStyleList::iterator aIter = std::find( maParagraphStyleList.begin(), maParagraphStyleList.end(), &rTextFormatColl ); if ( aIter == maParagraphStyleList.end() ) { maParagraphStyleList.push_back( &rTextFormatColl ); } } void SwNumRule::RemoveParagraphStyle( SwTextFormatColl& rTextFormatColl ) { tParagraphStyleList::iterator aIter = std::find( maParagraphStyleList.begin(), maParagraphStyleList.end(), &rTextFormatColl ); if ( aIter != maParagraphStyleList.end() ) { maParagraphStyleList.erase( aIter ); } } void SwNumRule::dumpAsXml(xmlTextWriterPtr pWriter) const { (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwNumRule")); (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("msName"), BAD_CAST(msName.toUtf8().getStr())); (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("mnPoolFormatId"), BAD_CAST(OString::number(mnPoolFormatId).getStr())); (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("mbAutoRuleFlag"), BAD_CAST(OString::boolean(mbAutoRuleFlag).getStr())); for (const auto& pFormat : maFormats) { if (!pFormat) { continue; } pFormat->dumpAsXml(pWriter); } (void)xmlTextWriterEndElement(pWriter); } void SwNumRule::GetGrabBagItem(uno::Any& rVal) const { if (mpGrabBagItem) mpGrabBagItem->QueryValue(rVal); else rVal <<= uno::Sequence(); } void SwNumRule::SetGrabBagItem(const uno::Any& rVal) { if (!mpGrabBagItem) mpGrabBagItem = std::make_shared(); mpGrabBagItem->PutValue(rVal, 0); } bool SwNumRule::HasContinueList() const { // In case all text nodes are after each other, then we won't have a later list that wants to // continue us. SwNodeOffset nIndex(0); for (size_t i = 0; i < maTextNodeList.size(); ++i) { SwTextNode* pNode = maTextNodeList[i]; if (i > 0) { if (pNode->GetIndex() != nIndex + 1) { // May have a continue list. return true; } } nIndex = pNode->GetIndex(); } // Definitely won't have a continue list. return false; } namespace numfunc { namespace { /** class containing default bullet list configuration data */ class SwDefBulletConfig : private utl::ConfigItem { public: static SwDefBulletConfig& getInstance(); const OUString& GetFontname() const { return msFontname; } bool IsFontnameUserDefined() const { return mbUserDefinedFontname; } const vcl::Font& GetFont() const { return *mpFont; } sal_Unicode GetChar( sal_uInt8 p_nListLevel ) const { if (p_nListLevel >= MAXLEVEL) { p_nListLevel = MAXLEVEL - 1; } return mnLevelChars[p_nListLevel]; } SwDefBulletConfig(); private: /** sets internal default bullet configuration data to default values */ void SetToDefault(); /** returns sequence of default bullet configuration property names */ static uno::Sequence GetPropNames(); /** loads default bullet configuration properties and applies values to internal data */ void LoadConfig(); /** initialize font instance for default bullet list */ void InitFont(); /** catches notification about changed default bullet configuration data */ virtual void Notify( const uno::Sequence& aPropertyNames ) override; virtual void ImplCommit() override; // default bullet list configuration data OUString msFontname; bool mbUserDefinedFontname; FontWeight meFontWeight; FontItalic meFontItalic; sal_Unicode mnLevelChars[MAXLEVEL]; // default bullet list font instance std::optional mpFont; }; } SwDefBulletConfig& SwDefBulletConfig::getInstance() { static SwDefBulletConfig theSwDefBulletConfig; return theSwDefBulletConfig; } SwDefBulletConfig::SwDefBulletConfig() : ConfigItem( "Office.Writer/Numbering/DefaultBulletList" ), // default bullet font is now OpenSymbol msFontname( OUString("OpenSymbol") ), mbUserDefinedFontname( false ), meFontWeight( WEIGHT_DONTKNOW ), meFontItalic( ITALIC_NONE ) { SetToDefault(); LoadConfig(); InitFont(); // enable notification for changes on default bullet configuration change EnableNotification( GetPropNames() ); } void SwDefBulletConfig::SetToDefault() { msFontname = "OpenSymbol"; mbUserDefinedFontname = false; meFontWeight = WEIGHT_DONTKNOW; meFontItalic = ITALIC_NONE; mnLevelChars[0] = 0x2022; mnLevelChars[1] = 0x25e6; mnLevelChars[2] = 0x25aa; mnLevelChars[3] = 0x2022; mnLevelChars[4] = 0x25e6; mnLevelChars[5] = 0x25aa; mnLevelChars[6] = 0x2022; mnLevelChars[7] = 0x25e6; mnLevelChars[8] = 0x25aa; mnLevelChars[9] = 0x2022; } uno::Sequence SwDefBulletConfig::GetPropNames() { uno::Sequence aPropNames(13); OUString* pNames = aPropNames.getArray(); pNames[0] = "BulletFont/FontFamilyname"; pNames[1] = "BulletFont/FontWeight"; pNames[2] = "BulletFont/FontItalic"; pNames[3] = "BulletCharLvl1"; pNames[4] = "BulletCharLvl2"; pNames[5] = "BulletCharLvl3"; pNames[6] = "BulletCharLvl4"; pNames[7] = "BulletCharLvl5"; pNames[8] = "BulletCharLvl6"; pNames[9] = "BulletCharLvl7"; pNames[10] = "BulletCharLvl8"; pNames[11] = "BulletCharLvl9"; pNames[12] = "BulletCharLvl10"; return aPropNames; } void SwDefBulletConfig::LoadConfig() { uno::Sequence aPropNames = GetPropNames(); uno::Sequence aValues = GetProperties( aPropNames ); const uno::Any* pValues = aValues.getConstArray(); OSL_ENSURE( aValues.getLength() == aPropNames.getLength(), " - GetProperties failed"); if ( aValues.getLength() != aPropNames.getLength() ) return; for ( int nProp = 0; nProp < aPropNames.getLength(); ++nProp ) { if ( pValues[nProp].hasValue() ) { switch ( nProp ) { case 0: { OUString aStr; pValues[nProp] >>= aStr; msFontname = aStr; mbUserDefinedFontname = true; } break; case 1: case 2: { sal_Int16 nTmp = 0; pValues[nProp] >>= nTmp; if ( nProp == 1 ) meFontWeight = static_cast(nTmp); else if ( nProp == 2 ) meFontItalic = static_cast(nTmp); } break; case 3: case 4: case 5: case 6: case 7: case 8: case 9: case 10: case 11: case 12: { sal_Unicode cChar = sal_Unicode(); pValues[nProp] >>= cChar; mnLevelChars[nProp-3] = cChar; } break; } } } } void SwDefBulletConfig::InitFont() { mpFont.emplace( msFontname, OUString(), Size( 0, 14 ) ); mpFont->SetWeight( meFontWeight ); mpFont->SetItalic( meFontItalic ); mpFont->SetCharSet( RTL_TEXTENCODING_SYMBOL ); } void SwDefBulletConfig::Notify( const uno::Sequence& ) { SetToDefault(); LoadConfig(); InitFont(); } void SwDefBulletConfig::ImplCommit() { } OUString const & GetDefBulletFontname() { return SwDefBulletConfig::getInstance().GetFontname(); } bool IsDefBulletFontUserDefined() { return SwDefBulletConfig::getInstance().IsFontnameUserDefined(); } const vcl::Font& GetDefBulletFont() { return SwDefBulletConfig::getInstance().GetFont(); } sal_Unicode GetBulletChar( sal_uInt8 nLevel ) { return SwDefBulletConfig::getInstance().GetChar( nLevel ); } namespace { /** class containing configuration data about user interface behavior regarding lists and list items. configuration item about behavior of /-key at first position of first list item */ class SwNumberingUIBehaviorConfig : private utl::ConfigItem { public: static SwNumberingUIBehaviorConfig& getInstance(); bool ChangeIndentOnTabAtFirstPosOfFirstListItem() const { return mbChangeIndentOnTabAtFirstPosOfFirstListItem; } SwNumberingUIBehaviorConfig(); private: /** sets internal configuration data to default values */ void SetToDefault(); /** returns sequence of configuration property names */ static css::uno::Sequence GetPropNames(); /** loads configuration properties and applies values to internal data */ void LoadConfig(); /** catches notification about changed configuration data */ virtual void Notify( const css::uno::Sequence& aPropertyNames ) override; virtual void ImplCommit() override; // configuration data bool mbChangeIndentOnTabAtFirstPosOfFirstListItem; }; } SwNumberingUIBehaviorConfig& SwNumberingUIBehaviorConfig::getInstance() { static SwNumberingUIBehaviorConfig theSwNumberingUIBehaviorConfig; return theSwNumberingUIBehaviorConfig; } SwNumberingUIBehaviorConfig::SwNumberingUIBehaviorConfig() : ConfigItem( "Office.Writer/Numbering/UserInterfaceBehavior" ), mbChangeIndentOnTabAtFirstPosOfFirstListItem( true ) { SetToDefault(); LoadConfig(); // enable notification for changes on configuration change EnableNotification( GetPropNames() ); } void SwNumberingUIBehaviorConfig::SetToDefault() { mbChangeIndentOnTabAtFirstPosOfFirstListItem = true; } css::uno::Sequence SwNumberingUIBehaviorConfig::GetPropNames() { css::uno::Sequence aPropNames { "ChangeIndentOnTabAtFirstPosOfFirstListItem" }; return aPropNames; } void SwNumberingUIBehaviorConfig::ImplCommit() {} void SwNumberingUIBehaviorConfig::LoadConfig() { css::uno::Sequence aPropNames = GetPropNames(); css::uno::Sequence aValues = GetProperties( aPropNames ); const css::uno::Any* pValues = aValues.getConstArray(); OSL_ENSURE( aValues.getLength() == aPropNames.getLength(), " - GetProperties failed"); if ( aValues.getLength() != aPropNames.getLength() ) return; for ( int nProp = 0; nProp < aPropNames.getLength(); ++nProp ) { if ( pValues[nProp].hasValue() ) { switch ( nProp ) { case 0: { pValues[nProp] >>= mbChangeIndentOnTabAtFirstPosOfFirstListItem; } break; default: { OSL_FAIL( " - unknown configuration property"); } } } } } void SwNumberingUIBehaviorConfig::Notify( const css::uno::Sequence& ) { SetToDefault(); LoadConfig(); } bool ChangeIndentOnTabAtFirstPosOfFirstListItem() { return SwNumberingUIBehaviorConfig::getInstance().ChangeIndentOnTabAtFirstPosOfFirstListItem(); } bool NumDownChangesIndent(const SwWrtShell& rShell) { SwPaM* pCursor = rShell.GetCursor(); if (!pCursor) { return true; } SwTextNode* pTextNode = pCursor->GetNode().GetTextNode(); if (!pTextNode) { return true; } const SwNumRule* pNumRule = pTextNode->GetNumRule(); if (!pNumRule) { return true; } int nOldLevel = pTextNode->GetActualListLevel(); int nNewLevel = nOldLevel + 1; if (nNewLevel >= MAXLEVEL) { return true; } const SwNumFormat& rOldFormat = pNumRule->Get(nOldLevel); if (rOldFormat.GetNumberingType() != SVX_NUM_NUMBER_NONE) { return true; } const SwNumFormat& rNewFormat = pNumRule->Get(nNewLevel); if (rNewFormat.GetNumberingType() != SVX_NUM_NUMBER_NONE) { return true; } // This is the case when the numbering levels don't differ, so changing between them is not // a better alternative to inserting a tab character. return rOldFormat.GetIndentAt() != rNewFormat.GetIndentAt(); } SvxNumberFormat::SvxNumPositionAndSpaceMode GetDefaultPositionAndSpaceMode() { if (utl::ConfigManager::IsFuzzing()) return SvxNumberFormat::LABEL_ALIGNMENT; SvxNumberFormat::SvxNumPositionAndSpaceMode ePosAndSpaceMode; switch (GetODFSaneDefaultVersion()) { case SvtSaveOptions::ODFSVER_010: case SvtSaveOptions::ODFSVER_011: { ePosAndSpaceMode = SvxNumberFormat::LABEL_WIDTH_AND_POSITION; } break; default: // >= ODFSVER_012 { ePosAndSpaceMode = SvxNumberFormat::LABEL_ALIGNMENT; } } return ePosAndSpaceMode; } } void SwNumRuleTable::dumpAsXml(xmlTextWriterPtr pWriter) const { (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwNumRuleTable")); for (SwNumRule* pNumRule : *this) pNumRule->dumpAsXml(pWriter); (void)xmlTextWriterEndElement(pWriter); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */