/* -*- 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/. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace css; namespace chart { namespace { void setTopCell(uno::Reference& xPropertySet) { xPropertySet->setPropertyValue(u"FillColor"_ustr, uno::Any(Color(0xFFFFFF))); xPropertySet->setPropertyValue(u"TextVerticalAdjust"_ustr, uno::Any(drawing::TextVerticalAdjust_TOP)); xPropertySet->setPropertyValue(u"ParaAdjust"_ustr, uno::Any(style::ParagraphAdjust_CENTER)); table::BorderLine2 aBorderLine; aBorderLine.LineWidth = 0; aBorderLine.Color = 0x000000; xPropertySet->setPropertyValue(u"TopBorder"_ustr, uno::Any(aBorderLine)); xPropertySet->setPropertyValue(u"LeftBorder"_ustr, uno::Any(aBorderLine)); } void copyProperty(uno::Reference& xOut, uno::Reference& xIn, OUString const& sPropertyName) { xOut->setPropertyValue(sPropertyName, xIn->getPropertyValue(sPropertyName)); } uno::Reference getFirstParagraph(uno::Reference const& xText) { uno::Reference xParagraph; uno::Reference xEnumAccess(xText, uno::UNO_QUERY); if (!xEnumAccess.is()) return xParagraph; uno::Reference xEnumeration(xEnumAccess->createEnumeration()); xParagraph.set(xEnumeration->nextElement(), uno::UNO_QUERY); return xParagraph; } uno::Reference getFirstParagraphProperties(uno::Reference const& xText) { uno::Reference xPropertySet; auto xParagraph = getFirstParagraph(xText); if (!xParagraph.is()) return xPropertySet; xPropertySet.set(xParagraph, uno::UNO_QUERY); return xPropertySet; } } // end anonymous namespace DataTableView::DataTableView( rtl::Reference<::chart::ChartModel> const& xChartModel, rtl::Reference const& rDataTableModel, css::uno::Reference const& rComponentContext, bool bAlignAxisValuesWithColumns) : m_xChartModel(xChartModel) , m_xDataTableModel(rDataTableModel) , m_xComponentContext(rComponentContext) , m_bAlignAxisValuesWithColumns(bAlignAxisValuesWithColumns) { uno::Reference xPropertySet(m_xDataTableModel); m_aLineProperties.initFromPropertySet(xPropertySet); } void DataTableView::setCellCharAndParagraphProperties( uno::Reference& xPropertySet) { uno::Reference xDataTableProperties(m_xDataTableModel); copyProperty(xPropertySet, xDataTableProperties, u"CharColor"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharFontFamily"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharFontFamilyAsian"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharFontFamilyComplex"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharFontCharSet"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharFontCharSetAsian"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharFontCharSetComplex"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharFontName"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharFontNameAsian"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharFontNameComplex"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharFontPitch"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharFontPitchAsian"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharFontPitchComplex"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharFontStyleName"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharFontStyleNameAsian"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharFontStyleNameComplex"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharHeight"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharHeightAsian"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharHeightComplex"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharKerning"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharLocale"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharLocaleAsian"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharLocaleComplex"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharPosture"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharPostureAsian"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharPostureComplex"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharRelief"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharShadowed"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharStrikeout"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharUnderline"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharUnderlineColor"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharUnderlineHasColor"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharOverline"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharOverlineColor"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharOverlineHasColor"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharWeight"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharWeightAsian"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharWeightComplex"_ustr); copyProperty(xPropertySet, xDataTableProperties, u"CharWordMode"_ustr); drawing::FillStyle eFillStyle = drawing::FillStyle_NONE; xDataTableProperties->getPropertyValue(u"FillStyle"_ustr) >>= eFillStyle; if (eFillStyle == drawing::FillStyle_SOLID) { sal_Int32 aColor = 0; if (xDataTableProperties->getPropertyValue(u"FillColor"_ustr) >>= aColor) xPropertySet->setPropertyValue(u"CharBackColor"_ustr, uno::Any(aColor)); } xPropertySet->setPropertyValue(u"ParaAdjust"_ustr, uno::Any(style::ParagraphAdjust_CENTER)); } void DataTableView::setCellProperties(css::uno::Reference& xPropertySet, bool bLeft, bool bTop, bool bRight, bool bBottom) { xPropertySet->setPropertyValue(u"FillColor"_ustr, uno::Any(Color(0xFFFFFF))); uno::Reference xDataTableProperties(m_xDataTableModel); float fFontHeight = 0.0; xDataTableProperties->getPropertyValue(u"CharHeight"_ustr) >>= fFontHeight; fFontHeight = o3tl::convert(fFontHeight, o3tl::Length::pt, o3tl::Length::mm100); sal_Int32 nXDistance = std::round(fFontHeight * 0.18f); sal_Int32 nYDistance = std::round(fFontHeight * 0.30f); xPropertySet->setPropertyValue(u"TextLeftDistance"_ustr, uno::Any(nXDistance)); xPropertySet->setPropertyValue(u"TextRightDistance"_ustr, uno::Any(nXDistance)); xPropertySet->setPropertyValue(u"TextUpperDistance"_ustr, uno::Any(nYDistance)); xPropertySet->setPropertyValue(u"TextLowerDistance"_ustr, uno::Any(nYDistance)); xPropertySet->setPropertyValue(u"TextVerticalAdjust"_ustr, uno::Any(drawing::TextVerticalAdjust_TOP)); drawing::LineStyle eStyle = drawing::LineStyle_NONE; m_aLineProperties.LineStyle >>= eStyle; if (eStyle != drawing::LineStyle_NONE) { table::BorderLine2 aBorderLine; sal_Int32 nWidth = 0; m_aLineProperties.Width >>= nWidth; aBorderLine.LineWidth = o3tl::convert(nWidth, o3tl::Length::mm100, o3tl::Length::twip); sal_Int32 nColor = 0; m_aLineProperties.Color >>= nColor; aBorderLine.Color = nColor; aBorderLine.LineStyle = table::BorderLineStyle::SOLID; if (eStyle == drawing::LineStyle_DASH) { OUString aDashName; m_aLineProperties.DashName >>= aDashName; if (!aDashName.isEmpty() && m_xChartModel.is()) { uno::Reference xDashTable( m_xChartModel->createInstance(u"com.sun.star.drawing.DashTable"_ustr), uno::UNO_QUERY); if (xDashTable.is() && xDashTable->hasByName(aDashName)) { drawing::LineDash aLineDash; xDashTable->getByName(aDashName) >>= aLineDash; if (aLineDash.Dots == 0 && aLineDash.Dashes == 0) aBorderLine.LineStyle = table::BorderLineStyle::SOLID; else if (aLineDash.Dots == 1 && aLineDash.Dashes == 0) aBorderLine.LineStyle = table::BorderLineStyle::DOTTED; else if (aLineDash.Dots == 0 && aLineDash.Dashes == 1) aBorderLine.LineStyle = table::BorderLineStyle::DASHED; else if (aLineDash.Dots == 1 && aLineDash.Dashes == 1) aBorderLine.LineStyle = table::BorderLineStyle::DASH_DOT; else if (aLineDash.Dots == 2 && aLineDash.Dashes == 1) aBorderLine.LineStyle = table::BorderLineStyle::DASH_DOT_DOT; else aBorderLine.LineStyle = table::BorderLineStyle::DASHED; } } } if (bLeft) xPropertySet->setPropertyValue(u"LeftBorder"_ustr, uno::Any(aBorderLine)); if (bTop) xPropertySet->setPropertyValue(u"TopBorder"_ustr, uno::Any(aBorderLine)); if (bRight) xPropertySet->setPropertyValue(u"RightBorder"_ustr, uno::Any(aBorderLine)); if (bBottom) xPropertySet->setPropertyValue(u"BottomBorder"_ustr, uno::Any(aBorderLine)); } } void DataTableView::createShapes(basegfx::B2DVector const& rStart, basegfx::B2DVector const& rEnd, sal_Int32 nAxisStepWidth) { if (!m_xTarget.is()) return; // Remove shapes first before we add the new ones ShapeFactory::removeSubShapes(m_xTarget); auto sParticle = ObjectIdentifier::createParticleForDataTable(m_xChartModel); auto sCID = ObjectIdentifier::createClassifiedIdentifierForParticle(sParticle); m_xTableShape = ShapeFactory::createTable(m_xTarget, sCID); // calculate the table size auto rDelta = rEnd - rStart; sal_Int32 nTableSize = basegfx::fround(rDelta.getX()); m_xTableShape->setSize({ nTableSize, 0 }); try { m_xTableShape->getPropertyValue(u"Model"_ustr) >>= m_xTable; } catch (const uno::Exception&) { return; } if (!m_xTable.is()) return; uno::Reference xBroadcaster(m_xTable, uno::UNO_QUERY); if (!xBroadcaster.is()) return; xBroadcaster->lockBroadcasts(); auto* pTableObject = static_cast(m_xTableShape->GetSdrObject()); // get the data table properties from the model bool bHBorder = false; bool bVBorder = false; bool bOutline = false; bool bKeys = false; std::vector aSymbols; m_xDataTableModel->getPropertyValue(u"HBorder"_ustr) >>= bHBorder; m_xDataTableModel->getPropertyValue(u"VBorder"_ustr) >>= bVBorder; m_xDataTableModel->getPropertyValue(u"Outline"_ustr) >>= bOutline; m_xDataTableModel->getPropertyValue(u"Keys"_ustr) >>= bKeys; // set the data table row and column size sal_Int32 nColumnCount = m_aXValues.size(); uno::Reference xTableColumns = m_xTable->getColumns(); xTableColumns->insertByIndex(0, nColumnCount); sal_Int32 nRowCount = m_aDataSeriesNames.size(); uno::Reference xTableRows = m_xTable->getRows(); xTableRows->insertByIndex(0, nRowCount); sal_Int32 nColumnWidth = 0.0; // If we don't align, we have to calculate the column width ourselves, // otherwise the column width is taken from the x-axis width if (m_bAlignAxisValuesWithColumns) nColumnWidth = nAxisStepWidth; else nColumnWidth = double(nTableSize) / nColumnCount; // Setup empty top-left cell { uno::Reference xCell = m_xTable->getCellByPosition(0, 0); uno::Reference xPropertySet(xCell, uno::UNO_QUERY); if (xPropertySet.is()) { setTopCell(xPropertySet); } } sal_Int32 nColumn; sal_Int32 nRow; // COLUMN HEADER nColumn = 1; for (auto const& rString : m_aXValues) { uno::Reference xCell = m_xTable->getCellByPosition(nColumn, 0); uno::Reference xPropertySet(xCell, uno::UNO_QUERY); uno::Reference xCellTextRange(xCell, uno::UNO_QUERY); if (xCellTextRange.is()) { auto xText = xCellTextRange->getText(); xText->insertString(xText->getStart(), rString, false); auto xTextPropertySet = getFirstParagraphProperties(xText); if (xTextPropertySet) { bool bLeft = (bOutline && nColumn == 1) || (bVBorder && nColumn > 1 && nColumn < nColumnCount); bool bRight = (bOutline && nColumn == nColumnCount) || (bVBorder && nColumn > 1 && nColumn < nColumnCount); setCellCharAndParagraphProperties(xTextPropertySet); setCellProperties(xPropertySet, bLeft, bOutline, bRight, bOutline); } } nColumn++; } // ROW HEADER // Prepare keys (symbols) sal_Int32 nMaxSymbolWidth = 0; constexpr const sal_Int32 constSymbolMargin = 100; // 1mm if (bKeys) { uno::Reference xDataTableProperties(m_xDataTableModel); float fFontHeight = 0.0; xDataTableProperties->getPropertyValue(u"CharHeight"_ustr) >>= fFontHeight; fFontHeight = o3tl::convert(fFontHeight, o3tl::Length::pt, o3tl::Length::mm100); sal_Int32 nSymbolHeight = sal_Int32(fFontHeight * 0.6); sal_Int32 nSymbolWidth = nSymbolHeight; for (VSeriesPlotter* pSeriesPlotter : m_pSeriesPlotterList) { if (pSeriesPlotter) { awt::Size aCurrentRatio = pSeriesPlotter->getPreferredLegendKeyAspectRatio(); sal_Int32 nCurrentWidth = aCurrentRatio.Width; if (aCurrentRatio.Height > 0) nCurrentWidth = nSymbolHeight * aCurrentRatio.Width / aCurrentRatio.Height; nSymbolWidth = std::max(nSymbolWidth, nCurrentWidth); } } nMaxSymbolWidth = nSymbolWidth; for (VSeriesPlotter* pSeriesPlotter : m_pSeriesPlotterList) { if (pSeriesPlotter) { awt::Size aSize(nSymbolWidth, nSymbolHeight); std::vector aNewEntries = pSeriesPlotter->createSymbols(aSize, m_xTarget, m_xComponentContext); for (auto const& rSymbol : aNewEntries) aSymbols.push_back(rSymbol); } } } nRow = 1; for (auto const& rSeriesName : m_aDataSeriesNames) { uno::Reference xCell = m_xTable->getCellByPosition(0, nRow); uno::Reference xCellPropertySet(xCell, uno::UNO_QUERY); uno::Reference xCellTextRange(xCell, uno::UNO_QUERY); if (xCellTextRange.is()) { bool bTop = (bOutline && nRow == 1) || (bHBorder && nRow > 1 && nRow < nRowCount); bool bBottom = (bOutline && nRow == nRowCount) || (bHBorder && nRow > 1 && nRow < nRowCount); auto xText = xCellTextRange->getText(); xText->insertString(xText->getStart(), rSeriesName, false); auto xTextPropertySet = getFirstParagraphProperties(xText); if (xTextPropertySet) { setCellCharAndParagraphProperties(xTextPropertySet); setCellProperties(xCellPropertySet, bOutline, bTop, bOutline, bBottom); xCellPropertySet->setPropertyValue(u"ParaAdjust"_ustr, uno::Any(style::ParagraphAdjust_LEFT)); if (bKeys) { xCellPropertySet->setPropertyValue( u"ParaLeftMargin"_ustr, uno::Any(nMaxSymbolWidth + sal_Int32(2 * constSymbolMargin))); } } } nRow++; } // TABLE nRow = 1; const sal_Int32 nTableModelRowCount = m_xTable->getRowCount(); const sal_Int32 nTableModelColCount = m_xTable->getColumnCount(); // tdf#153182 the broken bounds are most likely because we don't know if the // data-table has header rows and columns. Most likely it does not. bool bBrokenBounds = false; for (auto const& rSeries : m_pDataSeriesValues) { nColumn = 1; for (auto const& rValue : rSeries) { if (nRow >= nTableModelRowCount || nColumn >= nTableModelColCount) { bBrokenBounds = true; SAL_WARN("chart2", "exceeding bounds of table model?"); break; } uno::Reference xCell = m_xTable->getCellByPosition(nColumn, nRow); uno::Reference xCellPropertySet(xCell, uno::UNO_QUERY); uno::Reference xCellTextRange(xCell, uno::UNO_QUERY); if (xCellTextRange.is()) { auto xText = xCellTextRange->getText(); xText->insertString(xText->getStart(), rValue, false); auto xTextPropertySet = getFirstParagraphProperties(xText); if (xTextPropertySet.is()) { bool bLeft = false; bool bTop = false; bool bRight = false; bool bBottom = false; if (nColumn > 1 && bVBorder) bLeft = true; if (nRow > 1 && bHBorder) bTop = true; if (nRow == nRowCount && bOutline) bBottom = true; if (nColumn == nColumnCount && bOutline) bRight = true; setCellCharAndParagraphProperties(xTextPropertySet); setCellProperties(xCellPropertySet, bLeft, bTop, bRight, bBottom); } } nColumn++; } if (bBrokenBounds) break; nRow++; } // force recalculation of column widths in the table shape xBroadcaster->unlockBroadcasts(); pTableObject->DistributeColumns(0, nColumnCount, true, true); xBroadcaster->lockBroadcasts(); // reposition the data table changePosition(basegfx::fround(rStart.getX()), basegfx::fround(rStart.getY())); sal_Int32 nTableX = m_xTableShape->getPosition().X; sal_Int32 nTableY = m_xTableShape->getPosition().Y; uno::Reference xPropertySet(xTableColumns->getByIndex(0), uno::UNO_QUERY); for (sal_Int32 i = 1; i < xTableColumns->getCount(); ++i) { xPropertySet.set(xTableColumns->getByIndex(i), uno::UNO_QUERY); xPropertySet->setPropertyValue(u"Width"_ustr, uno::Any(nColumnWidth)); } // force recalculation of row heights in the table shape xBroadcaster->unlockBroadcasts(); pTableObject->DistributeRows(0, nRowCount, true, true); if (bKeys) { sal_Int32 nTotalHeight = 0; for (sal_Int32 i = 0; i < xTableRows->getCount(); i++) { sal_Int32 nSymbolIndex = i - 1; if (nSymbolIndex < sal_Int32(aSymbols.size())) { xPropertySet.set(xTableRows->getByIndex(i), uno::UNO_QUERY); sal_Int32 nHeight = 0; xPropertySet->getPropertyValue(u"Height"_ustr) >>= nHeight; if (i > 0) { auto& rSymbol = aSymbols[nSymbolIndex].xSymbol; sal_Int32 nSymbolHeight = rSymbol->getSize().Height; sal_Int32 nSymbolY = basegfx::fround(double(nHeight) / 2.0 - double(nSymbolHeight) / 2.0); rSymbol->setPosition( { nTableX + constSymbolMargin, nTableY + nTotalHeight + nSymbolY }); } nTotalHeight += nHeight; } } } } void DataTableView::changePosition(sal_Int32 x, sal_Int32 y) { if (!m_xTable.is()) return; uno::Reference xTableColumns = m_xTable->getColumns(); uno::Reference xPropertySet(xTableColumns->getByIndex(0), uno::UNO_QUERY); sal_Int32 nWidth = 0; xPropertySet->getPropertyValue(u"Width"_ustr) >>= nWidth; m_xTarget->setPosition({ x - nWidth, y }); } void DataTableView::initializeShapes(const rtl::Reference& xTarget) { m_xTarget = xTarget; } void DataTableView::initializeValues( std::vector>& rSeriesPlotterList) { for (auto& rSeriesPlotter : rSeriesPlotterList) { m_pSeriesPlotterList.push_back(rSeriesPlotter.get()); for (auto const& rCategory : rSeriesPlotter->getExplicitCategoriesProvider()->getSimpleCategories()) { m_aXValues.push_back(rCategory); } for (auto const& rString : rSeriesPlotter->getAllSeriesNames()) { m_aDataSeriesNames.push_back(rString); } for (VDataSeries* pSeries : rSeriesPlotter->getAllSeries()) { auto& rValues = m_pDataSeriesValues.emplace_back(); for (int i = 0; i < pSeries->getTotalPointCount(); i++) { double nValue = pSeries->getYValue(i); rValues.push_back(rSeriesPlotter->getLabelTextForValue(*pSeries, i, nValue, false)); } } } } } //namespace chart /* vim:set shiftwidth=4 softtabstop=4 expandtab: */