diff options
author | Attila Szűcs <attila.szucs@collabora.com> | 2024-07-25 06:21:35 +0200 |
---|---|---|
committer | Caolán McNamara <caolan.mcnamara@collabora.com> | 2024-10-18 12:49:41 +0200 |
commit | 599365d293bb0c0fcc2b5b1183e50fa9b93a8e3f (patch) | |
tree | 8d91ace1459c3691f38ad926272dcc44ea49e899 /sw | |
parent | 9cb748847f63f73a7a0bbd63d34a5f3ebf789919 (diff) |
SW: extract transform charts (using dialog code)
Implemented chart data extraction, and transformation
added insert/delete/modify row/column, and the ability to set
resize data table
setcolumndesc / setrowdesc .. for 1 descriptor,
or with multiple descriptor to set
set 1 cell value, or set the whole table cells values.
it will resize the table, but you can still use partial arrays
like
"data": [ [ 1,2,3,4 ],
[ 2,3,4,5 ],
[ 3 ],
[ 4,5,6,7 ],
[ 2,2,1 ],
[ 5,6,7,8 ] ],
that means the 3. row 2,3,4th cell will not be overwritten.
for insert column, it call codes from DataBrowserModel.
(used by dialog code)
added tests, fixed a contentcontrol date problem
2. commit (made by Caolan) squashed into 1. commit:
WIP hack this to link
move stuff from chartcontroller to chartcore and adjust visibility
until it links at least
Change-Id: I5529f4da33f046eef7e947d755e29486fa966274
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/172849
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice@gmail.com>
Reviewed-by: Caolán McNamara <caolan.mcnamara@collabora.com>
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/175121
Tested-by: Jenkins
Diffstat (limited to 'sw')
-rw-r--r-- | sw/qa/uibase/shells/data/docStructureChartExampleOriginal.odt | bin | 0 -> 35081 bytes | |||
-rw-r--r-- | sw/qa/uibase/shells/shells.cxx | 196 | ||||
-rw-r--r-- | sw/source/uibase/shells/textsh1.cxx | 583 | ||||
-rw-r--r-- | sw/source/uibase/uno/loktxdoc.cxx | 139 |
4 files changed, 917 insertions, 1 deletions
diff --git a/sw/qa/uibase/shells/data/docStructureChartExampleOriginal.odt b/sw/qa/uibase/shells/data/docStructureChartExampleOriginal.odt Binary files differnew file mode 100644 index 000000000000..da775ecf4aaa --- /dev/null +++ b/sw/qa/uibase/shells/data/docStructureChartExampleOriginal.odt diff --git a/sw/qa/uibase/shells/shells.cxx b/sw/qa/uibase/shells/shells.cxx index e4ed7367fc29..28a31a50ed28 100644 --- a/sw/qa/uibase/shells/shells.cxx +++ b/sw/qa/uibase/shells/shells.cxx @@ -13,6 +13,9 @@ #include <com/sun/star/packages/zip/ZipFileAccess.hpp> #include <com/sun/star/text/BibliographyDataType.hpp> #include <com/sun/star/text/XTextDocument.hpp> +#include <com/sun/star/text/XTextEmbeddedObjectsSupplier.hpp> +#include <com/sun/star/chart2/XChartDocument.hpp> +#include <com/sun/star/chart/XChartDataArray.hpp> #include <sfx2/dispatch.hxx> #include <sfx2/viewfrm.hxx> @@ -41,6 +44,8 @@ #include <ndtxt.hxx> #include <ftnidx.hxx> #include <txtftn.hxx> +#include <unotxdoc.hxx> +#include <tools/json_writer.hxx> /// Covers sw/source/uibase/shells/ fixes. class SwUibaseShellsTest : public SwModelTestBase @@ -583,6 +588,197 @@ CPPUNIT_TEST_FIXTURE(SwUibaseShellsTest, testInsertFieldmarkReadonly) CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), rIDMA.getFieldmarksCount()); } +CPPUNIT_TEST_FIXTURE(SwUibaseShellsTest, testDocumentStructureTransformChart) +{ + createSwDoc("docStructureChartExampleOriginal.odt"); + OString aJson = R"json( +{ + "Transforms": { + "Charts.ByEmbedIndex.0": { + "modifyrow.1": [ 19, 15 ], + "datayx.3.1": 37, + "deleterow.0": "", + "deleterow.1": "", + "insertrow.0": [ 15, 17 ], + "setrowdesc.0": "Paul", + "insertrow.2": [ 19, 22 ], + "setrowdesc.2": "Barbara", + "insertrow.4": [ 29, 27 ], + "setrowdesc.4": "Elizabeth", + "insertrow.5": [ 14, 26 ], + "setrowdesc.5": "William", + "insertcolumn.1": [ 1,2,3,4,5,6 ], + "insertcolumn.0": [ 2,1,2,1,2,1 ], + "insertcolumn.4": [ 7,7,7,7,7,7 ], + "setcolumndesc.4": "c4", + "setcolumndesc.0": "c0", + "setcolumndesc.2": "c2" + }, + "Charts.BySubTitle.Subtitle2": { + "deletecolumn.3": "" + }, + "Charts.ByEmbedName.Object3": { + "resize": [ 12, 3 ], + "setrowdesc": + [ + "United Kingdom", + "United States of America", + "Canada", + "Brazil", + "Germany", + "India", + "Japan", + "Sudan", + "Norway", + "Italy", + "France", + "Egypt" + ], + "modifycolumn.0": [ 12, 9, 8, 5, 5, 4, 3, 3, 2, 2, 1, 1], + "resize": [ 12, 2 ] + }, + "Charts.ByTitle.Fixed issues": { + "data": [ [ 3,1,2 ], + [ 2,0,1 ], + [ 3,2,0 ], + [ 2,1,1 ], + [ 4,2,2 ], + [ 2,2,1 ], + [ 3,2,0,0 ], + [ 3,1,2,1 ], + [ 3,3,1,0,1 ], + [ 2,1,0,1,2 ], + [ 4,2,1,2,2,3 ], + [ 2,1,0,0,1,4 ], + [ 3,2,2,1,3,2 ], + [ 4,2,1,1,2,3 ], + [ 3,3,2,0,3,5 ], + [ 3,3,1,2,2,3 ], + [ 5,1,1,1,1,4 ], + [ 2,2,2,1,2,3 ] ], + "setrowdesc": ["2023.01",".02",".03",".04",".05",".06",".07",".08",".09",".10",".11",".12","2023.01",".02",".03",".04",".05",".06"], + "setcolumndesc": ["Jennifer", "Charles", "Thomas", "Maria", "Lisa", "Daniel"] + } + } +} +)json"_ostr; + + //transform + uno::Sequence<css::beans::PropertyValue> aArgs = { + comphelper::makePropertyValue(u"DataJson"_ustr, + uno::Any(OStringToOUString(aJson, RTL_TEXTENCODING_UTF8))), + }; + dispatchCommand(mxComponent, u".uno:TransformDocumentStructure"_ustr, aArgs); + + // Check transformed values of the 3 chart + for (int nShape = 0; nShape < 3; nShape++) + { + uno::Reference<text::XTextEmbeddedObjectsSupplier> xEOS(mxComponent, uno::UNO_QUERY); + CPPUNIT_ASSERT(xEOS.is()); + uno::Reference<container::XIndexAccess> xEmbeddeds(xEOS->getEmbeddedObjects(), + uno::UNO_QUERY); + CPPUNIT_ASSERT(xEmbeddeds.is()); + + uno::Reference<beans::XPropertySet> xShapeProps(xEmbeddeds->getByIndex(nShape), + uno::UNO_QUERY); + CPPUNIT_ASSERT(xShapeProps.is()); + + uno::Reference<frame::XModel> xDocModel; + xShapeProps->getPropertyValue(u"Model"_ustr) >>= xDocModel; + CPPUNIT_ASSERT(xDocModel.is()); + + uno::Reference<chart2::XChartDocument> xChartDoc(xDocModel, uno::UNO_QUERY); + CPPUNIT_ASSERT(xChartDoc.is()); + + uno::Reference<chart::XChartDataArray> xDataArray(xChartDoc->getDataProvider(), + uno::UNO_QUERY); + CPPUNIT_ASSERT(xDataArray.is()); + + switch (nShape) + { + case 0: + { + std::vector<std::vector<double>> aValues = { + { 2, 15, 1, 7 }, { 1, 19, 2, 7 }, { 2, 19, 3, 7 }, + { 1, 25, 4, 7 }, { 2, 29, 5, 7 }, { 1, 14, 6, 7 }, + }; + // RowDescriptions + std::vector<OUString> aRowDescsValues + = { u"Paul"_ustr, u"Mary"_ustr, u"Barbara"_ustr, + u"David"_ustr, u"Elizabeth"_ustr, u"William"_ustr }; + // ColumnDescriptions + std::vector<OUString> aColDescsValues + = { u"c0"_ustr, u"2022"_ustr, u"c2"_ustr, u"c4"_ustr }; + + uno::Sequence<uno::Sequence<double>> aData = xDataArray->getData(); + for (size_t nY = 0; nY < aValues.size(); nY++) + { + for (size_t nX = 0; nX < aValues[nY].size(); nX++) + { + CPPUNIT_ASSERT_EQUAL(aData[nY][nX], aValues[nY][nX]); + } + } + + uno::Sequence<OUString> aColDescs = xDataArray->getColumnDescriptions(); + for (size_t i = 0; i < aColDescsValues.size(); i++) + { + CPPUNIT_ASSERT_EQUAL(aColDescs[i], aColDescsValues[i]); + } + uno::Sequence<OUString> aRowDescs = xDataArray->getRowDescriptions(); + for (size_t i = 0; i < aRowDescsValues.size(); i++) + { + CPPUNIT_ASSERT_EQUAL(aRowDescs[i], aRowDescsValues[i]); + } + } + break; + case 1: + { + uno::Sequence<uno::Sequence<double>> aData = xDataArray->getData(); + CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(18), aData.getLength()); + uno::Sequence<double>* pRows = aData.getArray(); + CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(6), pRows[0].getLength()); + } + break; + case 2: + { + uno::Sequence<uno::Sequence<double>> aData = xDataArray->getData(); + CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(12), aData.getLength()); + uno::Sequence<double>* pRows = aData.getArray(); + CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2), pRows[0].getLength()); + } + break; + default: + break; + } + } +} + +CPPUNIT_TEST_FIXTURE(SwUibaseShellsTest, testDocumentStructureExtractChart) +{ + createSwDoc("docStructureChartExampleOriginal.odt"); + + //extract + tools::JsonWriter aJsonWriter; + std::string_view aCommand(".uno:ExtractDocumentStructure"); + auto pXTextDocument = dynamic_cast<SwXTextDocument*>(mxComponent.get()); + pXTextDocument->getCommandValues(aJsonWriter, aCommand); + + OString aExpectedStr + = "{ \"DocStructure\": { \"Charts.ByEmbedIndex.0\": { \"name\": \"Object1\", \"title\": " + "\"Paid leave days\", \"subtitle\": \"Subtitle2\", \"RowDescriptions\": [ \"James\", " + "\"Mary\", \"Patricia\", \"David\"], \"ColumnDescriptions\": [ \"2022\", \"2023\"], " + "\"DataValues\": [ \"Row.0\": [ \"22\", \"24\"], \"Row.1\": [ \"18\", \"16\"], " + "\"Row.2\": [ \"32\", \"32\"], \"Row.3\": [ \"25\", \"23\"]]}, " + "\"Charts.ByEmbedIndex.1\": { \"name\": \"Object2\", \"title\": \"Fixed issues\", " + "\"subtitle\": \"Subtitle1\", \"RowDescriptions\": [ \"\"], \"ColumnDescriptions\": [ \" " + "\"], \"DataValues\": [ \"Row.0\": [ \"NaN\"]]}, \"Charts.ByEmbedIndex.2\": { \"name\": " + "\"Object3\", \"title\": \"Employees from countries\", \"subtitle\": \"Subtitle3\", " + "\"RowDescriptions\": [ \"\"], \"ColumnDescriptions\": [ \"Column 1\"], \"DataValues\": " + "[ \"Row.0\": [ \"NaN\"]]}}}"_ostr; + + CPPUNIT_ASSERT_EQUAL(aExpectedStr, aJsonWriter.finishAndGetAsOString()); +} + CPPUNIT_TEST_FIXTURE(SwUibaseShellsTest, testUpdateRefmarks) { // Given a document with two refmarks, one is not interesting the other is a citation: diff --git a/sw/source/uibase/shells/textsh1.cxx b/sw/source/uibase/shells/textsh1.cxx index 2ac91aed446c..6d0aebb3e9a5 100644 --- a/sw/source/uibase/shells/textsh1.cxx +++ b/sw/source/uibase/shells/textsh1.cxx @@ -134,6 +134,28 @@ #include <rtl/uri.hxx> #include <unotxdoc.hxx> +#include <com/sun/star/text/XTextEmbeddedObjectsSupplier.hpp> +#include <com/sun/star/chart2/XInternalDataProvider.hpp> +#include <com/sun/star/chart2/XChartDocument.hpp> +#include <com/sun/star/chart/XChartDocument.hpp> +#include <com/sun/star/chart/XChartDataArray.hpp> +#include <com/sun/star/chart2/XTitle.hpp> +#include <com/sun/star/chart2/XTitled.hpp> +#include <com/sun/star/chart/ChartDataRowSource.hpp> +#include <com/sun/star/util/XModifiable.hpp> + +#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp> +#include <com/sun/star/chart2/XChartTypeContainer.hpp> +#include <com/sun/star/chart2/XDataSeriesContainer.hpp> +#include <com/sun/star/util/XCloneable.hpp> + +#include <com/sun/star/chart/ChartDataRowSource.hpp> +#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp> +#include <com/sun/star/chart2/XChartTypeContainer.hpp> +#include <com/sun/star/chart2/XDataSeriesContainer.hpp> +//#include <../../../../chart2/source/controller/inc/ChartController.hxx> +//#include <DataSeries.hxx> + using namespace ::com::sun::star; using namespace com::sun::star::beans; using namespace ::com::sun::star::container; @@ -790,6 +812,109 @@ void DeleteFields(SfxRequest& rReq, SwWrtShell& rWrtSh) pDoc->DeleteFormatRefMark(pMark); } } + +bool lcl_ChangeChartColumnCount(const uno::Reference<chart2::XChartDocument>& xChartDoc, sal_Int32 nId, + bool bInsert, bool bResize = false) +{ + uno::Reference<chart2::XDiagram> xDiagram = xChartDoc->getFirstDiagram(); + if (!xDiagram.is()) + return false; + uno::Reference<chart2::XCoordinateSystemContainer> xCooSysContainer(xDiagram, uno::UNO_QUERY); + if (!xCooSysContainer.is()) + return false; + uno::Sequence<uno::Reference<chart2::XCoordinateSystem>> xCooSysSequence( + xCooSysContainer->getCoordinateSystems()); + if (xCooSysSequence.getLength() <= 0) + return false; + uno::Reference<chart2::XChartTypeContainer> xChartTypeContainer(xCooSysSequence[0], + uno::UNO_QUERY); + if (!xChartTypeContainer.is()) + return false; + uno::Sequence<uno::Reference<chart2::XChartType>> xChartTypeSequence( + xChartTypeContainer->getChartTypes()); + if (xChartTypeSequence.getLength() <= 0) + return false; + uno::Reference<chart2::XDataSeriesContainer> xDSContainer(xChartTypeSequence[0], + uno::UNO_QUERY); + if (!xDSContainer.is()) + return false; + + uno::Reference<chart2::XInternalDataProvider> xIDataProvider(xChartDoc->getDataProvider(), + uno::UNO_QUERY); + if (!xIDataProvider.is()) + return false; + + uno::Sequence<uno::Reference<chart2::XDataSeries>> aSeriesSeq(xDSContainer->getDataSeries()); + + int nSeriesCount = aSeriesSeq.getLength(); + + if (bResize) + { + // Resize is actually some inserts, or deletes + if (nId > nSeriesCount) + { + bInsert = true; + } + else if (nId < nSeriesCount) + { + bInsert = false; + } + else + { + // Resize to the same size. No change needed + return true; + } + } + + // insert or delete + if (bInsert) + { + // insert + if (nId > nSeriesCount && !bResize) + return false; + + int nInsertCount = bResize ? nId - nSeriesCount : 1; + + // call dialog code + if (bResize) + { + for (int i = 0; i < nInsertCount; i++) + { + xIDataProvider->insertDataSeries(nSeriesCount); + } + return true; + } + + xIDataProvider->insertDataSeries(nId); + } + else + { + // delete 1 or more columns + if (nId >= nSeriesCount) + return false; + int nDeleteCount = bResize ? nSeriesCount - nId : 1; + for (int i = 0; i < nDeleteCount; i++) + { + xDSContainer->removeDataSeries(aSeriesSeq[nId]); + } + } + return true; +} + +bool lcl_ResizeChartColumns(const uno::Reference<chart2::XChartDocument>& xChartDoc, sal_Int32 nSize) +{ + return lcl_ChangeChartColumnCount(xChartDoc, nSize, false, true); +} + +bool lcl_InsertChartColumns(const uno::Reference<chart2::XChartDocument>& xChartDoc, sal_Int32 nId) +{ + return lcl_ChangeChartColumnCount(xChartDoc, nId, true); +} + +bool lcl_DeleteChartColumns(const uno::Reference<chart2::XChartDocument>& xChartDoc, sal_Int32 nId) +{ + return lcl_ChangeChartColumnCount(xChartDoc, nId, false); +} } void SwTextShell::Execute(SfxRequest &rReq) @@ -2275,6 +2400,7 @@ void SwTextShell::Execute(SfxRequest &rReq) std::stringstream aStream( (std::string(OUStringToOString(aDataJson, RTL_TEXTENCODING_UTF8)))); boost::property_tree::read_json(aStream, aTree); + // Todo: try catch - in case of fail: log wrong JSON format // get the loaded content controls uno::Reference<text::XContentControlsSupplier> xCCSupplier( @@ -2296,6 +2422,29 @@ void SwTextShell::Execute(SfxRequest &rReq) }; std::vector<std::string> aIdTexts = { ".ByIndex.", ".ByTag.", ".ByAlias.", ".ById." }; + // get charts + uno::Reference<text::XTextEmbeddedObjectsSupplier> xEOS( + GetView().GetDocShell()->GetModel(), uno::UNO_QUERY); + if (!xEOS.is()) + break; + uno::Reference<container::XIndexAccess> xEmbeddeds(xEOS->getEmbeddedObjects(), + uno::UNO_QUERY); + if (!xEmbeddeds.is()) + break; + + sal_Int32 nEOcount = xEmbeddeds->getCount(); + + enum class ChartFilterType + { + ERROR = -1, + INDEX, + NAME, + TITLE, + SUBTITLE + }; + std::vector<std::string> aIdChartTexts + = { ".ByEmbedIndex.", ".ByEmbedName.", ".ByTitle.", ".BySubTitle." }; + // Iterate through the JSON data loaded into a tree structure for (const auto& aItem : aTree) { @@ -2304,6 +2453,431 @@ void SwTextShell::Execute(SfxRequest &rReq) // Handle all transformations for (const auto& aItem2 : aItem.second) { + if (aItem2.first.starts_with("Charts")) + { + std::string aTextEnd = aItem2.first.substr(6); + std::string aValue = ""; + ChartFilterType iKeyId = ChartFilterType::ERROR; + // Find how the chart is identified: ByIndex, ByTitle... + for (size_t i = 0; i < aIdChartTexts.size(); i++) + { + if (aTextEnd.starts_with(aIdChartTexts[i])) + { + iKeyId = static_cast<ChartFilterType>(i); + aValue = aTextEnd.substr(aIdChartTexts[i].length()); + break; + } + } + if (iKeyId != ChartFilterType::ERROR) + { + for (int i = 0; i < nEOcount; ++i) + { + uno::Reference<beans::XPropertySet> xShapeProps( + xEmbeddeds->getByIndex(i), uno::UNO_QUERY); + if (!xShapeProps.is()) + continue; + + uno::Reference<frame::XModel> xDocModel; + xShapeProps->getPropertyValue(u"Model"_ustr) >>= xDocModel; + if (!xDocModel.is()) + continue; + + uno::Reference<chart2::XChartDocument> xChartDoc( + xDocModel, uno::UNO_QUERY); + if (!xChartDoc.is()) + continue; + + uno::Reference<chart2::data::XDataProvider> xDataProvider( + xChartDoc->getDataProvider()); + if (!xDataProvider.is()) + continue; + + uno::Reference<chart::XChartDataArray> xDataArray( + xChartDoc->getDataProvider(), uno::UNO_QUERY); + if (!xDataArray.is()) + continue; + + uno::Reference<chart2::XInternalDataProvider> xIDataProvider( + xChartDoc->getDataProvider(), uno::UNO_QUERY); + if (!xIDataProvider.is()) + continue; + + uno::Reference<util::XModifiable> xModi(xDocModel, + uno::UNO_QUERY); + if (!xModi.is()) + continue; + + switch (iKeyId) + { + case ChartFilterType::INDEX: + { + if (stoi(aValue) != i) + continue; + } + break; + case ChartFilterType::NAME: + { + uno::Reference<container::XNamed> xNamedShape( + xEmbeddeds->getByIndex(i), uno::UNO_QUERY); + if (xNamedShape.is()) + { + OUString aName; + aName = xNamedShape->getName(); + if (OStringToOUString(aValue, RTL_TEXTENCODING_UTF8) + != aName) + continue; + } + } + break; + case ChartFilterType::TITLE: + { + uno::Reference<chart2::XTitled> xTitled( + xChartDoc, uno::UNO_QUERY_THROW); + if (!xTitled.is()) + continue; + uno::Reference<chart2::XTitle> xTitle + = xTitled->getTitleObject(); + if (!xTitle.is()) + continue; + + OUString aTitle; + const uno::Sequence< + uno::Reference<chart2::XFormattedString>> + aFSSeq = xTitle->getText(); + for (auto const& fs : aFSSeq) + aTitle += fs->getString(); + if (OStringToOUString(aValue, RTL_TEXTENCODING_UTF8) + != aTitle) + continue; + } + break; + case ChartFilterType::SUBTITLE: + { + uno::Reference<chart2::XDiagram> xDiagram + = xChartDoc->getFirstDiagram(); + if (!xDiagram.is()) + continue; + + uno::Reference<chart2::XTitled> xTitled( + xDiagram, uno::UNO_QUERY_THROW); + if (!xTitled.is()) + continue; + + uno::Reference<chart2::XTitle> xSubTitle( + xTitled->getTitleObject()); + if (!xSubTitle.is()) + continue; + + OUString aSubTitle; + const uno::Sequence< + uno::Reference<chart2::XFormattedString>> + aFSSeq = xSubTitle->getText(); + for (auto const& fs : aFSSeq) + aSubTitle += fs->getString(); + if (OStringToOUString(aValue, RTL_TEXTENCODING_UTF8) + != aSubTitle) + continue; + } + break; + default: + continue; + } + + // We have a match, this chart need to be transformed + // Set all the values (of the chart) what is needed + + // Check if the InternalDataProvider is row or column based. + bool bChartUseColumns = false; + uno::Sequence<beans::PropertyValue> aArguments( + xDataProvider->detectArguments(nullptr)); + for (sal_Int32 j = 0; j < aArguments.getLength(); ++j) + { + if (aArguments[j].Name == "DataRowSource") + { + css::chart::ChartDataRowSource eRowSource; + if (aArguments[j].Value >>= eRowSource) + bChartUseColumns + = (eRowSource + == css::chart::ChartDataRowSource_COLUMNS); + break; + } + } + + for (const auto& aItem3 : aItem2.second) + { + if (aItem3.first.starts_with("deletecolumn.") + || aItem3.first.starts_with("deleterow.") + || aItem3.first.starts_with("insertcolumn.") + || aItem3.first.starts_with("insertrow.") + || aItem3.first.starts_with("modifycolumn.") + || aItem3.first.starts_with("modifyrow.")) + { + // delete insert, or modify a row, or column + // column, or row? + bool bSetColumn = (aItem3.first[6] == 'c'); + int nId = stoi(aItem3.first.substr(bSetColumn ? 13 : 10)); + bool bDelete = aItem3.first.starts_with("delete"); + // delete/insert a row/column if needed + if (!aItem3.first.starts_with("modify")) + { + if (bChartUseColumns == bSetColumn) + { + if (bDelete) + { + if (!lcl_DeleteChartColumns(xChartDoc, nId)) + continue; + xIDataProvider->deleteSequence(nId); + } + else + { + if (!lcl_InsertChartColumns(xChartDoc, nId)) + continue; + } + } + else + { + if (bDelete) + { + xIDataProvider + ->deleteDataPointForAllSequences(nId); + } + else + { + xIDataProvider + ->insertDataPointForAllSequences(nId + - 1); + } + } + } + // set values also, if needed + if (!bDelete && aItem3.second.size() > 0) + { + uno::Sequence<uno::Sequence<double>> aData + = xDataArray->getData(); + uno::Sequence<double>* pRows = aData.getArray(); + + int nIndex = 0; + int nX = nId; + int nY = nId; + for (const auto& aItem4 : aItem3.second) + { + if (bSetColumn) + { + nY = nIndex; + } + else + { + nX = nIndex; + } + if (nY < aData.getLength() && nY >= 0 + && nX < pRows[nY].getLength() && nX >= 0) + { + double* pCols = pRows[nY].getArray(); + pCols[nX] + = aItem4.second.get_value<double>(); + } + // else log: set invalid cell: index [nY,Nx] + nIndex++; + } + + xDataArray->setData(aData); + } + } + else if (aItem3.first.starts_with("setrowdesc")) + { + // set row descriptions + uno::Sequence<OUString> aRowDesc + = xDataArray->getRowDescriptions(); + OUString* aRowdata = aRowDesc.getArray(); + + if (aItem3.first.starts_with("setrowdesc.")) + { + // set only 1 description + int nValue = stoi(aItem3.first.substr(11)); + if (nValue >= 0 && nValue < aRowDesc.getLength()) + { + aRowdata[nValue] = OStringToOUString( + aItem3.second.get_value<std::string>(), + RTL_TEXTENCODING_UTF8); + } + // Todo: else log: wrong index at setcolumndesc. + } + else + { + // set an array of description at once + int nIndex = 0; + for (const auto& aItem4 : aItem3.second) + { + if (nIndex >= aRowDesc.getLength()) + { + // Todo log: too many parameters + break; + } + aRowdata[nIndex] = OStringToOUString( + aItem4.second.get_value<std::string>(), + RTL_TEXTENCODING_UTF8); + nIndex++; + } + } + xDataArray->setRowDescriptions(aRowDesc); + } + else if (aItem3.first.starts_with("setcolumndesc")) + { + // set colimn descriptions + uno::Sequence<OUString> aColDesc + = xDataArray->getColumnDescriptions(); + OUString* aColdata = aColDesc.getArray(); + + if (aItem3.first.starts_with("setcolumndesc.")) + { + int nValue = stoi(aItem3.first.substr(14)); + if (nValue >= 0 && nValue < aColDesc.getLength()) + { + aColdata[nValue] = OStringToOUString( + aItem3.second.get_value<std::string>(), + RTL_TEXTENCODING_UTF8); + } + // Todo: else log: wrong index at setcolumndesc. + } + else + { + int nIndex = 0; + for (const auto& aItem4 : aItem3.second) + { + if (nIndex >= aColDesc.getLength()) + { + // Todo log: too many parameters + break; + } + aColdata[nIndex] = OStringToOUString( + aItem4.second.get_value<std::string>(), + RTL_TEXTENCODING_UTF8); + nIndex++; + } + } + xDataArray->setColumnDescriptions(aColDesc); + } + else if (aItem3.first.starts_with("resize")) + { + if (aItem3.second.size() >= 2) + { + auto aItem4 = aItem3.second.begin(); + int nY = aItem4->second.get_value<int>(); + int nX = (++aItem4)->second.get_value<int>(); + + if (nX < 1 || nY < 1) + { + // Todo log: wrong resize argument + continue; + } + // here we need to use the new insert column thing + if (!lcl_ResizeChartColumns(xChartDoc, nX)) + continue; + + uno::Sequence<uno::Sequence<double>> aData + = xDataArray->getData(); + if (aData.getLength() != nY) + aData.realloc(nY); + + for (sal_Int32 j = 0; j < nY; ++j) + { + uno::Sequence<double>* pRows = aData.getArray(); + // resize row if needed + if (pRows[j].getLength() != nX) + { + pRows[j].realloc(nX); + } + } + xDataArray->setData(aData); + } + // Todo: else log: not enought parameter for resize + } + else if (aItem3.first.starts_with("data")) + { + // set table data values + uno::Sequence<uno::Sequence<double>> aData + = xDataArray->getData(); + + // set only 1 cell data + if (aItem3.first.starts_with("datayx.")) + { + int nPoint = aItem3.first.find('.', 7); + int nY = stoi(aItem3.first.substr(7, nPoint - 7)); + int nX = stoi(aItem3.first.substr(nPoint + 1)); + if (nY < aData.getLength() && nY >= 0) + { + uno::Sequence<double>* pRows = aData.getArray(); + if (nX < pRows[nY].getLength() && nX >= 0) + { + double* pCols = pRows[nY].getArray(); + pCols[nX] + = aItem3.second.get_value<double>(); + } + } + } + else + { + // set the whole data table + // resize if needed + int nRowsCount = aItem3.second.size(); + int nColsCount = 0; + + for (const auto& aItem4 : aItem3.second) + { + if (nColsCount + < static_cast<int>(aItem4.second.size())) + { + nColsCount = aItem4.second.size(); + } + } + + if (nColsCount > 0) + { + // here we need to use the new insert column thing + if(!lcl_ResizeChartColumns(xChartDoc, nColsCount)) + continue; + + if (aData.getLength() != nRowsCount) + aData.realloc(nRowsCount); + + // set all the rows + sal_Int32 nY = 0; + for (const auto& aItem4 : aItem3.second) + { + uno::Sequence<double>* pRows + = aData.getArray(); + // resize row if needed + if (pRows[nY].getLength() != nColsCount) + { + pRows[nY].realloc(nColsCount); + } + double* pCols = pRows[nY].getArray(); + // set all values in the row + sal_Int32 nX = 0; + for (const auto& aItem5 : aItem4.second) + { + if (nX >= nColsCount) + { + break; + } + pCols[nX] + = aItem5.second.get_value<double>(); + nX++; + } + nY++; + } + } + } + xDataArray->setData(aData); + } + // Todo: else Log that this chart command was not recognised + xModi->setModified(true); + } + } + } + // todo: else log chart transfrom not recognised + } + if (aItem2.first.starts_with("ContentControls")) { std::string aTextEnd = aItem2.first.substr(15); @@ -2482,6 +3056,15 @@ void SwTextShell::Execute(SfxRequest &rReq) continue; xContentControlText->setString(aCheckContent); } + else if (aItem3.first == "date") + { + std::string aDate + = aItem3.second.get_value<std::string>(); + xContentControlProps->setPropertyValue( + UNO_NAME_CURRENT_DATE, + uno::Any(OStringToOUString(aDate, + RTL_TEXTENCODING_UTF8))); + } else if (aItem3.first == "alias") { xContentControlProps->setPropertyValue( diff --git a/sw/source/uibase/uno/loktxdoc.cxx b/sw/source/uibase/uno/loktxdoc.cxx index 8a29250c5bdd..7db06fe50a09 100644 --- a/sw/source/uibase/uno/loktxdoc.cxx +++ b/sw/source/uibase/uno/loktxdoc.cxx @@ -44,6 +44,14 @@ #include <unocontentcontrol.hxx> #include <com/sun/star/text/XTextContent.hpp> +#include <com/sun/star/text/XTextEmbeddedObjectsSupplier.hpp> +#include <com/sun/star/chart2/XInternalDataProvider.hpp> +#include <com/sun/star/chart2/XChartDocument.hpp> +#include <com/sun/star/chart/XChartDocument.hpp> +#include <com/sun/star/chart/XChartDataArray.hpp> +#include <com/sun/star/chart2/XTitle.hpp> +#include <com/sun/star/chart2/XTitled.hpp> + using namespace ::com::sun::star; namespace @@ -403,7 +411,6 @@ void GetDocStructure(tools::JsonWriter& rJsonWriter, SwDocShell* /*pDocShell*/, int iCCcount = xContentControls->getCount(); - auto commentsNode = rJsonWriter.startNode("DocStructure"); for (int i = 0; i < iCCcount; ++i) { OString aNodeName("ContentControls.ByIndex."_ostr + OString::number(i)); @@ -477,6 +484,9 @@ void GetDocStructure(tools::JsonWriter& rJsonWriter, SwDocShell* /*pDocShell*/, OUString aDateLanguage; xContentControlProps->getPropertyValue(UNO_NAME_DATE_LANGUAGE) >>= aDateLanguage; rJsonWriter.put(UNO_NAME_DATE_LANGUAGE, aDateLanguage); + OUString aCurrentDate; + xContentControlProps->getPropertyValue(UNO_NAME_CURRENT_DATE) >>= aCurrentDate; + rJsonWriter.put(UNO_NAME_CURRENT_DATE, aCurrentDate); } break; case SwContentControlType::PLAIN_TEXT: @@ -497,6 +507,125 @@ void GetDocStructure(tools::JsonWriter& rJsonWriter, SwDocShell* /*pDocShell*/, } } +void GetDocStructureCharts(tools::JsonWriter& rJsonWriter, SwDocShell* /*pDocShell*/, + const std::map<OUString, OUString>& rArguments, + uno::Reference<container::XIndexAccess>& xEmbeddeds) +{ + auto it = rArguments.find(u"filter"_ustr); + if (it != rArguments.end()) + { + // If filter is present but we are filtering not to charts + if (!it->second.equals(u"charts"_ustr)) + return; + } + + sal_Int32 nEOcount = xEmbeddeds->getCount(); + + for (int i = 0; i < nEOcount; ++i) + { + uno::Reference<beans::XPropertySet> xShapeProps(xEmbeddeds->getByIndex(i), uno::UNO_QUERY); + if (!xShapeProps.is()) + continue; + + uno::Reference<frame::XModel> xDocModel; + xShapeProps->getPropertyValue(u"Model"_ustr) >>= xDocModel; + if (!xDocModel.is()) + continue; + + uno::Reference<chart2::XChartDocument> xChartDoc(xDocModel, uno::UNO_QUERY); + if (!xChartDoc.is()) + continue; + + uno::Reference<chart2::data::XDataProvider> xDataProvider(xChartDoc->getDataProvider()); + if (!xDataProvider.is()) + continue; + + uno::Reference<chart::XChartDataArray> xDataArray(xChartDoc->getDataProvider(), + uno::UNO_QUERY); + if (!xDataArray.is()) + continue; + + uno::Reference<chart2::XDiagram> xDiagram = xChartDoc->getFirstDiagram(); + if (!xDiagram.is()) + continue; + + //we have the chart Data now, we can start to extract it + OString aNodeName("Charts.ByEmbedIndex."_ostr + OString::number(i)); + auto aChartNode = rJsonWriter.startNode(aNodeName); + + //get the object name + uno::Reference<container::XNamed> xNamedShape(xEmbeddeds->getByIndex(i), uno::UNO_QUERY); + if (xNamedShape.is()) + { + OUString aName; + aName = xNamedShape->getName(); + rJsonWriter.put("name", aName); + } + + //get the chart title, if there is one + uno::Reference<chart2::XTitled> xTitled(xChartDoc, uno::UNO_QUERY_THROW); + if (xTitled.is()) + { + uno::Reference<chart2::XTitle> xTitle = xTitled->getTitleObject(); + if (xTitle.is()) + { + OUString aTitle; + const uno::Sequence<uno::Reference<chart2::XFormattedString>> aFSSeq + = xTitle->getText(); + for (auto const& fs : aFSSeq) + aTitle += fs->getString(); + rJsonWriter.put("title", aTitle); + } + } + + //get the chart subtitle, if there is one + uno::Reference<chart2::XTitled> xSubTitled(xDiagram, uno::UNO_QUERY_THROW); + if (xSubTitled.is()) + { + uno::Reference<chart2::XTitle> xSubTitle = xSubTitled->getTitleObject(); + if (xSubTitle.is()) + { + OUString aSubTitle; + const uno::Sequence<uno::Reference<chart2::XFormattedString>> aFSSeq + = xSubTitle->getText(); + for (auto const& fs : aFSSeq) + aSubTitle += fs->getString(); + rJsonWriter.put("subtitle", aSubTitle); + } + } + + { + uno::Sequence<OUString> aRowDesc = xDataArray->getRowDescriptions(); + auto aRowDescNode = rJsonWriter.startArray("RowDescriptions"); + for (int j = 0; j < aRowDesc.getLength(); j++) + { + rJsonWriter.putSimpleValue(aRowDesc[j]); + } + } + { + uno::Sequence<OUString> aColDesc = xDataArray->getColumnDescriptions(); + auto aColDescNode = rJsonWriter.startArray("ColumnDescriptions"); + for (int j = 0; j < aColDesc.getLength(); j++) + { + rJsonWriter.putSimpleValue(aColDesc[j]); + } + } + { + uno::Sequence<uno::Sequence<double>> aData = xDataArray->getData(); + auto aDataValuesNode = rJsonWriter.startArray("DataValues"); + for (int j = 0; j < aData.getLength(); j++) + { + OString aRowNodeName("Row."_ostr + OString::number(j)); + auto aRowNode = rJsonWriter.startArray(aRowNodeName); + for (int k = 0; k < aData[j].getLength(); k++) + { + rJsonWriter.putSimpleValue(OUString::number(aData[j][k])); + } + } + } + } +} + /// Implements getCommandValues(".uno:Sections"). /// /// Parameters: @@ -607,6 +736,14 @@ void SwXTextDocument::getCommandValues(tools::JsonWriter& rJsonWriter, std::stri } else if (o3tl::starts_with(rCommand, aExtractDocStructure)) { + auto commentsNode = rJsonWriter.startNode("DocStructure"); + + uno::Reference<container::XIndexAccess> xEmbeddeds(getEmbeddedObjects(), uno::UNO_QUERY); + if (xEmbeddeds.is()) + { + GetDocStructureCharts(rJsonWriter, m_pDocShell, aMap, xEmbeddeds); + } + uno::Reference<container::XIndexAccess> xContentControls = getContentControls(); GetDocStructure(rJsonWriter, m_pDocShell, aMap, xContentControls); } |