/* -*- 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 <vector>
using namespace ::ooo::vba;
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( )
{
css::uno::Reference<css::frame::XModel> xModel(getThisWordDoc(mxContext));
css::uno::Reference<css::document::XUndoManagerSupplier> xUndoSupplier(
xModel, css::uno::UNO_QUERY_THROW);
css::uno::Reference<css::document::XUndoManager> xUndoManager(xUndoSupplier->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: */
↑ V1048 The 'nIndentAt' variable was assigned the same value.
↑ V1048 The 'nFirstLineIndent' variable was assigned the same value.
↑ V1048 The 'nListtabStopPosition' variable was assigned the same value.