/* -*- 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 "vbalistformat.hxx"
#include <utility>
#include <vbahelper/vbahelper.hxx>
#include <ooo/vba/word/WdListApplyTo.hpp>
#include <ooo/vba/word/WdDefaultListBehavior.hpp>
#include <com/sun/star/awt/FontDescriptor.hpp>
#include <com/sun/star/container/XEnumerationAccess.hpp>
#include <com/sun/star/container/XEnumeration.hpp>
#include <com/sun/star/document/XUndoManagerSupplier.hpp>
#include <com/sun/star/beans/XMultiPropertySet.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/style/TabStop.hpp>
#include <com/sun/star/text/PositionAndSpaceMode.hpp>
#include <com/sun/star/text/XTextTable.hpp>
#include <com/sun/star/util/Color.hpp>
#include <comphelper/sequence.hxx>
#include <comphelper/sequenceashashmap.hxx>
#include <comphelper/scopeguard.hxx>
#include <editeng/numitem.hxx>
#include "vbalisttemplate.hxx"
#include "wordvbahelper.hxx"
#include <unotxdoc.hxx>
#include <vector>
using namespace ::ooo::vba;
using namespace ::ooo::vba::word;
using namespace ::com::sun::star;
SwVbaListFormat::SwVbaListFormat( const uno::Reference< ooo::vba::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, uno::Reference< text::XTextRange > xTextRange ) : SwVbaListFormat_BASE( rParent, rContext ), mxTextRange(std::move( xTextRange ))
{
}
SwVbaListFormat::~SwVbaListFormat()
{
}
void SAL_CALL SwVbaListFormat::ApplyListTemplate( const css::uno::Reference< word::XListTemplate >& ListTemplate, const css::uno::Any& ContinuePreviousList, const css::uno::Any& ApplyTo, const css::uno::Any& DefaultListBehavior )
{
bool bContinuePreviousList = true ;
if ( ContinuePreviousList.hasValue() )
ContinuePreviousList >>= bContinuePreviousList;
// "applyto" must be current selection
sal_Int32 bApplyTo = word::WdListApplyTo::wdListApplyToSelection;
if ( ApplyTo.hasValue() )
ApplyTo >>= bApplyTo;
if ( bApplyTo != word::WdListApplyTo::wdListApplyToSelection )
throw uno::RuntimeException();
// default behaviour must be wdWord8ListBehavior
sal_Int32 nDefaultListBehavior = word::WdDefaultListBehavior::wdWord8ListBehavior;
if ( DefaultListBehavior.hasValue() )
DefaultListBehavior >>= nDefaultListBehavior;
if ( nDefaultListBehavior != word::WdDefaultListBehavior::wdWord8ListBehavior )
throw uno::RuntimeException();
uno::Reference< container::XEnumerationAccess > xEnumAccess( mxTextRange, uno::UNO_QUERY_THROW );
uno::Reference< container::XEnumeration > xEnum = xEnumAccess->createEnumeration();
if (!xEnum->hasMoreElements())
return ;
SwVbaListTemplate& rListTemplate = dynamic_cast <SwVbaListTemplate&>(*ListTemplate);
bool isFirstElement = true ;
do
{
uno::Reference< beans::XPropertySet > xProps( xEnum->nextElement(), uno::UNO_QUERY_THROW );
if ( isFirstElement )
{
bool isNumberingRestart = !bContinuePreviousList;
xProps->setPropertyValue(u"ParaIsNumberingRestart" _ustr, uno::Any( isNumberingRestart ) );
if ( isNumberingRestart )
{
xProps->setPropertyValue(u"NumberingStartValue" _ustr, uno::Any( sal_Int16(1 ) ) );
}
isFirstElement = false ;
}
else
{
xProps->setPropertyValue(u"ParaIsNumberingRestart" _ustr, uno::Any( false ) );
}
rListTemplate.applyListTemplate( xProps );
}
while ( xEnum->hasMoreElements() );
}
template <class Ref>
static void addParagraphsToList(const Ref& a,
std::vector<css::uno::Reference<css::beans::XPropertySet>>& rList)
{
if (css::uno::Reference<css::lang::XServiceInfo> xInfo{ a, css::uno::UNO_QUERY })
{
if (xInfo->supportsService(u"com.sun.star.text.Paragraph" _ustr))
{
rList.emplace_back(xInfo, css::uno::UNO_QUERY_THROW);
}
else if (xInfo->supportsService(u"com.sun.star.text.TextTable" _ustr))
{
css::uno::Reference<css::text::XTextTable> xTable(xInfo, css::uno::UNO_QUERY_THROW);
const auto aNames = xTable->getCellNames();
for (const auto & rName : aNames)
{
addParagraphsToList(xTable->getCellByName(rName), rList);
}
}
}
if (css::uno::Reference<css::container::XEnumerationAccess> xEnumAccess{ a,
css::uno::UNO_QUERY })
{
auto xEnum = xEnumAccess->createEnumeration();
while (xEnum->hasMoreElements())
addParagraphsToList(xEnum->nextElement(), rList);
}
}
void SAL_CALL SwVbaListFormat::ConvertNumbersToText( )
{
rtl::Reference<SwXTextDocument> xModel(getThisWordDoc(mxContext));
css::uno::Reference<css::document::XUndoManager> xUndoManager(xModel->getUndoManager());
xUndoManager->enterUndoContext(u"ConvertNumbersToText" _ustr);
xModel->lockControllers();
comphelper::ScopeGuard g([xModel, xUndoManager]() {
xModel->unlockControllers();
xUndoManager->leaveUndoContext();
});
std::vector<css::uno::Reference<css::beans::XPropertySet>> aParagraphs;
addParagraphsToList(mxTextRange, aParagraphs);
// in reverse order, to get proper label strings
for (auto iter = aParagraphs.rbegin(); iter != aParagraphs.rend(); ++iter)
{
auto & rPropertySet = *iter;
if (bool bNumber; (rPropertySet->getPropertyValue(u"NumberingIsNumber" _ustr) >>= bNumber) && bNumber)
{
css::uno::Reference<css::text::XTextRange> xRange(rPropertySet, css::uno::UNO_QUERY_THROW);
OUString sLabelString;
rPropertySet->getPropertyValue(u"ListLabelString" _ustr) >>= sLabelString;
// sal_Int16 nAdjust = SAL_MAX_INT16; // TODO?
sal_Int16 nNumberingType = SAL_MAX_INT16; // css::style::NumberingType
sal_Int16 nPositionAndSpaceMode = SAL_MAX_INT16;
sal_Int16 nLabelFollowedBy = SAL_MAX_INT16;
sal_Int32 nListtabStopPosition = SAL_MAX_INT32;
sal_Int32 nFirstLineIndent = SAL_MAX_INT32;
sal_Int32 nIndentAt = SAL_MAX_INT32;
sal_Int32 nLeftMargin = SAL_MAX_INT32;
sal_Int32 nSymbolTextDistance = SAL_MAX_INT32;
sal_Int32 nFirstLineOffset = SAL_MAX_INT32;
OUString sCharStyleName, sBulletChar;
css::awt::FontDescriptor aBulletFont;
bool bHasFont;
css::util::Color aBulletColor = css::util::Color(COL_AUTO);
bool bHasColor;
{
sal_uInt16 nLevel = SAL_MAX_UINT16;
rPropertySet->getPropertyValue(u"NumberingLevel" _ustr) >>= nLevel;
css::uno::Reference<css::container::XIndexAccess> xNumberingRules;
rPropertySet->getPropertyValue(u"NumberingRules" _ustr) >>= xNumberingRules;
comphelper::SequenceAsHashMap aLevelRule(xNumberingRules->getByIndex(nLevel));
// See offapi/com/sun/star/text/NumberingLevel.idl
aLevelRule[u"CharStyleName" _ustr] >>= sCharStyleName;
aLevelRule[u"NumberingType" _ustr] >>= nNumberingType;
// TODO: aLevelRule["Adjust"] >>= nAdjust; // HoriOrientation::LEFT/RIGHT/CENTER
aLevelRule[u"PositionAndSpaceMode" _ustr] >>= nPositionAndSpaceMode;
// for css::text::PositionAndSpaceMode::LABEL_ALIGNMENT
aLevelRule[u"LabelFollowedBy" _ustr] >>= nLabelFollowedBy;
aLevelRule[u"ListtabStopPosition" _ustr] >>= nListtabStopPosition;
aLevelRule[u"FirstLineIndent" _ustr] >>= nFirstLineIndent;
aLevelRule[u"IndentAt" _ustr] >>= nIndentAt;
// for css::text::PositionAndSpaceMode::LABEL_WIDTH_AND_POSITION
aLevelRule[u"LeftMargin" _ustr] >>= nLeftMargin;
aLevelRule[u"SymbolTextDistance" _ustr] >>= nSymbolTextDistance;
aLevelRule[u"FirstLineOffset" _ustr] >>= nFirstLineOffset;
aLevelRule[u"BulletChar" _ustr] >>= sBulletChar;
bHasFont = (aLevelRule[u"BulletFont" _ustr] >>= aBulletFont);
bHasColor = (aLevelRule[u"BulletColor" _ustr] >>= aBulletColor);
}
if (nNumberingType != css::style::NumberingType::BITMAP) // TODO
{
if (nPositionAndSpaceMode
== css::text::PositionAndSpaceMode::LABEL_WIDTH_AND_POSITION)
{
nIndentAt = nLeftMargin;
nFirstLineIndent = nFirstLineOffset;
nListtabStopPosition = nSymbolTextDistance;
nLabelFollowedBy = SvxNumberFormat::LabelFollowedBy::LISTTAB;
}
switch (nLabelFollowedBy)
{
case SvxNumberFormat::LabelFollowedBy::LISTTAB:
sLabelString += "\t" ;
break ;
case SvxNumberFormat::LabelFollowedBy::SPACE:
sLabelString += " " ;
break ;
case SvxNumberFormat::LabelFollowedBy::NEWLINE:
sLabelString += "\n" ;
break ;
}
css::uno::Reference<css::text::XTextRange> xNumberText(xRange->getStart());
xNumberText->setString(sLabelString);
css::uno::Reference<css::beans::XPropertySet> xNumberProps(
xNumberText, css::uno::UNO_QUERY_THROW);
if (!sCharStyleName.isEmpty())
xNumberProps->setPropertyValue(u"CharStyleName" _ustr, css::uno::Any(sCharStyleName));
if (nNumberingType == css::style::NumberingType::CHAR_SPECIAL)
{
css::uno::Reference<css::text::XTextRange> xBulletText(xNumberText->getStart());
xBulletText->setString(sBulletChar);
std::unordered_map<OUString, css::uno::Any> aNameValues;
if (bHasFont)
{
aNameValues.insert({
{ "CharFontName" , css::uno::Any(aBulletFont.Name) },
{ "CharFontStyleName" , css::uno::Any(aBulletFont.StyleName) },
{ "CharFontFamily" , css::uno::Any(aBulletFont.Family) },
{ "CharFontCharSet" , css::uno::Any(aBulletFont.CharSet) },
{ "CharWeight" , css::uno::Any(aBulletFont.Weight) },
{ "CharUnderline" , css::uno::Any(aBulletFont.Underline) },
{ "CharStrikeout" , css::uno::Any(aBulletFont.Strikeout) },
{ "CharAutoKerning" , css::uno::Any(aBulletFont.Kerning) },
{ "CharFontPitch" , css::uno::Any(aBulletFont.Pitch) },
{ "CharWordMode" , css::uno::Any(aBulletFont.WordLineMode) },
{ "CharRotation" , css::uno::Any(static_cast <sal_Int16>(
std::round(aBulletFont.Orientation * 10 ))) },
});
if (aBulletFont.Height)
aNameValues[u"CharHeight" _ustr] <<= aBulletFont.Height;
}
if (bHasColor)
{
aNameValues[u"CharColor" _ustr] <<= aBulletColor;
}
if (css::uno::Reference<css::beans::XMultiPropertySet> xBulletMultiProps{
xBulletText, css::uno::UNO_QUERY })
{
xBulletMultiProps->setPropertyValues(
comphelper::mapKeysToSequence(aNameValues),
comphelper::mapValuesToSequence(aNameValues));
}
else
{
css::uno::Reference<css::beans::XPropertySet> xBulletProps(
xBulletText, css::uno::UNO_QUERY_THROW);
for (const auto & [rName, rVal] : aNameValues)
xBulletProps->setPropertyValue(rName, rVal);
}
}
else
{
// TODO: css::style::NumberingType::BITMAP
}
rPropertySet->setPropertyValue(u"ParaLeftMargin" _ustr, css::uno::Any(nIndentAt));
rPropertySet->setPropertyValue(u"ParaFirstLineIndent" _ustr, css::uno::Any(nFirstLineIndent));
if (nLabelFollowedBy == SvxNumberFormat::LabelFollowedBy::LISTTAB)
{
css::uno::Sequence<css::style::TabStop> stops;
rPropertySet->getPropertyValue(u"ParaTabStops" _ustr) >>= stops;
css::style::TabStop tabStop{};
tabStop.Position = nListtabStopPosition;
tabStop.Alignment = css::style::TabAlign::TabAlign_LEFT;
tabStop.FillChar = ' ' ;
rPropertySet->setPropertyValue(u"ParaTabStops" _ustr,
css::uno::Any(comphelper::combineSequences({ tabStop }, stops)));
// FIXME: What if added tap stop is greater than already defined ones?
}
}
else
{
continue ; // for now, keep such lists as is
}
// In case of higher outline levels, each assignment of empty value just sets level 1
while (rPropertySet->getPropertyValue(u"NumberingRules" _ustr) != css::uno::Any())
{
rPropertySet->setPropertyValue(u"NumberingRules" _ustr, css::uno::Any());
}
}
}
}
OUString
SwVbaListFormat::getServiceImplName()
{
return u"SwVbaListFormat" _ustr;
}
uno::Sequence< OUString >
SwVbaListFormat::getServiceNames()
{
static uno::Sequence< OUString > const aServiceNames
{
u"ooo.vba.word.ListFormat" _ustr
};
return aServiceNames;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Messung V0.5 in Prozent C=95 H=85 G=90
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-06-10)
¤
*© Formatika GbR, Deutschland