[Libreoffice-commits] core.git: Branch 'distro/nisz/libreoffice-7-1' - 5 commits - editeng/source include/editeng include/oox include/vcl oox/source sc/inc sc/qa sc/source sw/qa sw/source vcl/inc vcl/source vcl/unx writerfilter/source
László Németh (via logerrit)
logerrit at kemper.freedesktop.org
Thu Aug 19 12:32:44 UTC 2021
editeng/source/items/svxfont.cxx | 74 ++++++++++-----
include/editeng/svxfont.hxx | 7 +
include/oox/export/drawingml.hxx | 8 +
include/oox/export/vmlexport.hxx | 6 -
include/vcl/toolkit/treelistbox.hxx | 6 +
include/vcl/weld.hxx | 2
oox/source/export/drawingml.cxx | 31 ++++--
oox/source/export/vmlexport.cxx | 10 +-
sc/inc/fillinfo.hxx | 4
sc/inc/typedstrdata.hxx | 7 +
sc/qa/uitest/autofilter/autofilter.py | 31 ++++++
sc/qa/uitest/data/autofilter/tdf140968.xlsx |binary
sc/qa/unit/data/xlsx/tdf119292.xlsx |binary
sc/qa/unit/subsequent_filters-test.cxx | 41 +++++++-
sc/source/core/data/column3.cxx | 6 -
sc/source/core/tool/typedstrdata.cxx | 34 +++++-
sc/source/filter/xml/XMLExportDatabaseRanges.cxx | 39 +++++++
sc/source/filter/xml/xmlfilti.cxx | 21 +++-
sc/source/ui/cctrl/checklistmenu.cxx | 14 ++
sc/source/ui/inc/checklistmenu.hxx | 14 ++
sc/source/ui/inc/output.hxx | 7 -
sc/source/ui/unoobj/datauno.cxx | 11 +-
sc/source/ui/view/gridwin.cxx | 8 +
sc/source/ui/view/gridwin2.cxx | 4
sc/source/ui/view/output.cxx | 44 ++++++--
sc/source/ui/view/output2.cxx | 64 +++++++-----
sw/qa/extras/odfexport/data/tdf95806.docx |binary
sw/qa/extras/odfexport/odfexport.cxx | 17 +++
sw/qa/extras/ooxmlexport/data/tdf118535.odt |binary
sw/qa/extras/ooxmlexport/data/tdf141172.docx |binary
sw/qa/extras/ooxmlexport/ooxmlexport14.cxx | 8 +
sw/qa/extras/ooxmlexport/ooxmlexport16.cxx | 13 ++
sw/source/filter/ww8/docxattributeoutput.cxx | 47 +++++++--
sw/source/filter/ww8/docxattributeoutput.hxx | 11 +-
sw/source/filter/ww8/docxexport.cxx | 4
vcl/inc/svimpbox.hxx | 1
vcl/source/app/salvtables.cxx | 5 +
vcl/source/treelist/treelistbox.cxx | 6 -
vcl/unx/gtk3/gtk3gtkinst.cxx | 4
writerfilter/source/dmapper/DomainMapperTableHandler.cxx | 61 +++++++++++-
writerfilter/source/dmapper/DomainMapperTableHandler.hxx | 4
writerfilter/source/dmapper/DomainMapper_Impl.hxx | 7 +
writerfilter/source/dmapper/PropertyMap.cxx | 8 +
43 files changed, 540 insertions(+), 149 deletions(-)
New commits:
commit 866293a50c24dc473805cb06715620a5680704d2
Author: László Németh <nemeth at numbertext.org>
AuthorDate: Fri Mar 26 16:28:03 2021 +0100
Commit: Gabor Kelemen <kelemen.gabor2 at nisz.hu>
CommitDate: Thu Aug 19 13:53:39 2021 +0200
tdf#95806 tdf#125877 tdf#141172 DOCX: fix tables in footnotes
and endnotes by converting them to floating tables during the
import, and removing floating at the DOCX export.
Writer core doesn't support non-floating tables in footnotes
and endnotes officially, (flowfrm:cxx: "Tables in footnotes
are not truly supported"), so their DOCX import resulted
serious problems:
– missing table paint (tdf#95806);
– table loss during saving to ODT (tdf#125877);
– table loss during copying them or their footnotes and
endnotes in the document (this resulted the regression
of the optimized footnote and endnote import: tdf#141172);
– table loss during changing the order of the chapters in
the Navigator.
Change-Id: Ife8af41936ae3ab003a3a9ad33b98c1d813e9c82
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/113162
Tested-by: László Németh <nemeth at numbertext.org>
Reviewed-by: László Németh <nemeth at numbertext.org>
(cherry picked from commit e11c51eefe8c3210cef2b5850f401ba67a401d01)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/120647
Tested-by: Gabor Kelemen <kelemen.gabor2 at nisz.hu>
Reviewed-by: Gabor Kelemen <kelemen.gabor2 at nisz.hu>
diff --git a/sw/qa/extras/odfexport/data/tdf95806.docx b/sw/qa/extras/odfexport/data/tdf95806.docx
new file mode 100644
index 000000000000..65bfaae3e423
Binary files /dev/null and b/sw/qa/extras/odfexport/data/tdf95806.docx differ
diff --git a/sw/qa/extras/odfexport/odfexport.cxx b/sw/qa/extras/odfexport/odfexport.cxx
index 54dbddf20825..3d65229497bf 100644
--- a/sw/qa/extras/odfexport/odfexport.cxx
+++ b/sw/qa/extras/odfexport/odfexport.cxx
@@ -232,6 +232,23 @@ DECLARE_ODFEXPORT_TEST(testTdf130314, "tdf130314.docx")
CPPUNIT_ASSERT_EQUAL(2, getPages());
}
+DECLARE_ODFEXPORT_TEST(testTdf125877, "tdf95806.docx")
+{
+ CPPUNIT_ASSERT_EQUAL(1, getPages());
+ uno::Reference<text::XTextTablesSupplier> xSupplier(mxComponent, uno::UNO_QUERY);
+ uno::Reference<container::XIndexAccess> xTables(xSupplier->getTextTables(), uno::UNO_QUERY);
+ uno::Reference<text::XTextTablesSupplier> xTextTablesSupplier(mxComponent, uno::UNO_QUERY);
+
+ // This was 0 (lost table during ODT export in footnotes)
+ // Note: fix also tdf#95806: painting table layout is correct
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTables->getCount());
+
+ // floating table: there is a frame now
+ uno::Reference<text::XTextFramesSupplier> xTextFramesSupplier(mxComponent, uno::UNO_QUERY);
+ uno::Reference<container::XIndexAccess> xIndexAccess(xTextFramesSupplier->getTextFrames(), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xIndexAccess->getCount());
+}
+
DECLARE_ODFEXPORT_TEST(testTdf103567, "tdf103567.odt")
{
CPPUNIT_ASSERT_EQUAL(1, getShapes());
diff --git a/sw/qa/extras/ooxmlexport/data/tdf141172.docx b/sw/qa/extras/ooxmlexport/data/tdf141172.docx
new file mode 100644
index 000000000000..0e1647ae27e1
Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf141172.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport14.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport14.cxx
index db2137081b18..a740b6295999 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport14.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport14.cxx
@@ -1118,6 +1118,14 @@ DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testTdf123757, "tdf123757.docx")
assertXPath(pXml, "/w:document/w:body/w:tbl", 2);
}
+DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testTdf141172, "tdf141172.docx")
+{
+ xmlDocUniquePtr pXml = parseExport("word/endnotes.xml");
+ CPPUNIT_ASSERT(pXml);
+ // This was 1 (lost table during copying endnote content)
+ assertXPath(pXml, "/w:endnotes/w:endnote/w:tbl", 2);
+}
+
DECLARE_OOXMLEXPORT_TEST(testContSectBreakHeaderFooter, "cont-sect-break-header-footer.docx")
{
// Load a document with a continuous section break on page 2.
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx
index e54562a569c5..585c58142e08 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -3980,7 +3980,10 @@ void DocxAttributeOutput::TableDefinition( ww8::WW8TableNodeInfoInner::Pointer_t
XFastAttributeListRef xAttributeList(pAttributeList);
m_pSerializer->singleElementNS(XML_w, XML_tblLook, xAttributeList);
}
- else if (rGrabBagElement.first == "TablePosition" )
+ else if (rGrabBagElement.first == "TablePosition" &&
+ // skip empty table position (tables in footnotes converted to
+ // floating tables temporarily, don't export this)
+ rGrabBagElement.second != uno::Any() )
{
FastAttributeList *attrListTablePos = FastSerializerHelper::createAttrList( );
const uno::Sequence<beans::PropertyValue> aTablePosition = rGrabBagElement.second.get<uno::Sequence<beans::PropertyValue> >();
diff --git a/writerfilter/source/dmapper/DomainMapperTableHandler.cxx b/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
index 42024b048c45..91575b838bef 100644
--- a/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
+++ b/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
@@ -32,6 +32,7 @@
#include <com/sun/star/table/XCellRange.hpp>
#include <com/sun/star/text/HoriOrientation.hpp>
#include <com/sun/star/text/SizeType.hpp>
+#include <com/sun/star/text/TextContentAnchorType.hpp>
#include <com/sun/star/text/XTextField.hpp>
#include <com/sun/star/text/XTextRangeCompare.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
@@ -339,9 +340,43 @@ void lcl_adjustBorderDistance(TableInfo& rInfo, const table::BorderLine2& rLeftB
rInfo.nRightBorderDistance = nActualR;
}
+void lcl_fillEmptyFrameProperties(std::vector<beans::PropertyValue>& rFrameProperties)
+{
+ // fill empty frame properties to create an invisible frame around the table:
+ // hide frame borders and zero inner and outer frame margins
+ beans::PropertyValue aValue;
+ aValue.Name = getPropertyName( PROP_ANCHOR_TYPE );
+ aValue.Value <<= text::TextContentAnchorType_AS_CHARACTER;
+ rFrameProperties.push_back(aValue);
+
+ table::BorderLine2 aEmptyBorder;
+ static const std::vector<std::u16string_view> aBorderNames
+ = { u"TopBorder", u"LeftBorder", u"BottomBorder", u"RightBorder" };
+ for (size_t i = 0; i < aBorderNames.size(); ++i)
+ {
+ beans::PropertyValue aBorderValue;
+ aBorderValue.Name = aBorderNames[i];
+ aBorderValue.Value <<= aEmptyBorder;
+ rFrameProperties.push_back(aBorderValue);
+ }
+ static const std::vector<std::u16string_view> aMarginNames
+ = { u"TopBorderDistance", u"LeftBorderDistance",
+ u"BottomBorderDistance", u"RightBorderDistance",
+ u"TopMargin", u"LeftMargin", u"BottomMargin", u"RightMargin" };
+ for (size_t i = 0; i < aMarginNames.size(); ++i)
+ {
+ beans::PropertyValue aMarginValue;
+ aMarginValue.Name = aMarginNames[i];
+ aMarginValue.Value <<= sal_Int32(10);
+ rFrameProperties.push_back(aMarginValue);
+ }
+}
+
}
-TableStyleSheetEntry * DomainMapperTableHandler::endTableGetTableStyle(TableInfo & rInfo, std::vector<beans::PropertyValue>& rFrameProperties)
+TableStyleSheetEntry * DomainMapperTableHandler::endTableGetTableStyle(TableInfo & rInfo,
+ std::vector<beans::PropertyValue>& rFrameProperties,
+ bool bConvertToFloatingInFootnote)
{
// will receive the table style if any
TableStyleSheetEntry* pTableStyle = nullptr;
@@ -394,6 +429,11 @@ TableStyleSheetEntry * DomainMapperTableHandler::endTableGetTableStyle(TableInfo
aGrabBag["TablePosition"] <<= aGrabBagTS;
}
+ else if (bConvertToFloatingInFootnote)
+ {
+ // define empty "TablePosition" to avoid export temporary floating
+ aGrabBag["TablePosition"] = uno::Any();
+ }
std::optional<PropertyMap::Property> aTableStyleVal = m_aTableProperties->getProperty(META_PROP_TABLE_STYLE_NAME);
if(aTableStyleVal)
@@ -1375,7 +1415,15 @@ void DomainMapperTableHandler::endTable(unsigned int nestedTableLevel, bool bTab
(m_rDMapper_Impl.getTableManager().getCurrentTablePosition());
TableInfo aTableInfo;
aTableInfo.nNestLevel = nestedTableLevel;
- aTableInfo.pTableStyle = endTableGetTableStyle(aTableInfo, aFrameProperties);
+
+ // non-floating tables need floating in footnotes and endnotes, because
+ // Writer core cannot handle (i.e. save in ODT, copy, edit etc.) them otherwise
+ bool bConvertToFloating = aFrameProperties.empty() &&
+ nestedTableLevel <= 1 &&
+ m_rDMapper_Impl.IsInFootOrEndnote();
+ bool bFloating = !aFrameProperties.empty() || bConvertToFloating;
+
+ aTableInfo.pTableStyle = endTableGetTableStyle(aTableInfo, aFrameProperties, bConvertToFloating);
// expands to uno::Sequence< Sequence< beans::PropertyValues > >
std::vector<HorizontallyMergedCell> aMerges;
@@ -1392,7 +1440,8 @@ void DomainMapperTableHandler::endTable(unsigned int nestedTableLevel, bool bTab
uno::Reference<text::XTextRange> xStart;
uno::Reference<text::XTextRange> xEnd;
- bool bFloating = !aFrameProperties.empty();
+ if ( bConvertToFloating )
+ lcl_fillEmptyFrameProperties(aFrameProperties);
// OOXML table style may contain paragraph properties, apply these on cell paragraphs
if ( m_aTableRanges[0].hasElements() && m_aTableRanges[0][0].hasElements() )
@@ -1585,7 +1634,11 @@ void DomainMapperTableHandler::endTable(unsigned int nestedTableLevel, bool bTab
sal_Int32 nTableWidthType = text::SizeType::FIX;
m_aTableProperties->getValue(TablePropertyMap::TABLE_WIDTH_TYPE, nTableWidthType);
if (m_rDMapper_Impl.GetSectionContext() && nestedTableLevel <= 1 && !m_rDMapper_Impl.IsInHeaderFooter())
- m_rDMapper_Impl.m_aPendingFloatingTables.emplace_back(xStart, xEnd, comphelper::containerToSequence(aFrameProperties), nTableWidth, nTableWidthType);
+ {
+ m_rDMapper_Impl.m_aPendingFloatingTables.emplace_back(xStart, xEnd,
+ comphelper::containerToSequence(aFrameProperties),
+ nTableWidth, nTableWidthType, bConvertToFloating);
+ }
else
{
// m_xText points to the body text, get the current xText from m_rDMapper_Impl, in case e.g. we would be in a header.
diff --git a/writerfilter/source/dmapper/DomainMapperTableHandler.hxx b/writerfilter/source/dmapper/DomainMapperTableHandler.hxx
index 900ff747f4cc..5a2a37acd90f 100644
--- a/writerfilter/source/dmapper/DomainMapperTableHandler.hxx
+++ b/writerfilter/source/dmapper/DomainMapperTableHandler.hxx
@@ -70,7 +70,9 @@ class DomainMapperTableHandler final : public virtual SvRefBase
/// Did we have a foot or endnote in this table?
bool m_bHadFootOrEndnote;
- TableStyleSheetEntry * endTableGetTableStyle(TableInfo & rInfo, std::vector<css::beans::PropertyValue>& rFrameProperties);
+ TableStyleSheetEntry * endTableGetTableStyle(TableInfo & rInfo,
+ std::vector<css::beans::PropertyValue>& rFrameProperties,
+ bool bConvertToFloating);
CellPropertyValuesSeq_t endTableGetCellProperties(TableInfo & rInfo, std::vector<HorizontallyMergedCell>& rMerges);
css::uno::Sequence<css::beans::PropertyValues> endTableGetRowProperties();
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.hxx b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
index 70048e22c540..6d9e2a948c16 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.hxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
@@ -384,16 +384,19 @@ struct FloatingTableInfo
sal_Int32 m_nTableWidthType;
/// Break type of the section that contains this table.
sal_Int32 m_nBreakType = -1;
+ /// Tables in footnotes and endnotes are always floating
+ bool m_bConvertToFloatingInFootnote = false;
FloatingTableInfo(css::uno::Reference<css::text::XTextRange> const& xStart,
css::uno::Reference<css::text::XTextRange> const& xEnd,
const css::uno::Sequence<css::beans::PropertyValue>& aFrameProperties,
- sal_Int32 nTableWidth, sal_Int32 nTableWidthType)
+ sal_Int32 nTableWidth, sal_Int32 nTableWidthType, bool bConvertToFloatingInFootnote)
: m_xStart(xStart),
m_xEnd(xEnd),
m_aFrameProperties(aFrameProperties),
m_nTableWidth(nTableWidth),
- m_nTableWidthType(nTableWidthType)
+ m_nTableWidthType(nTableWidthType),
+ m_bConvertToFloatingInFootnote(bConvertToFloatingInFootnote)
{
}
css::uno::Any getPropertyValue(const OUString &propertyName);
diff --git a/writerfilter/source/dmapper/PropertyMap.cxx b/writerfilter/source/dmapper/PropertyMap.cxx
index f3fa51ab8c55..13cefc9694ea 100644
--- a/writerfilter/source/dmapper/PropertyMap.cxx
+++ b/writerfilter/source/dmapper/PropertyMap.cxx
@@ -1111,6 +1111,9 @@ void SectionPropertyMap::HandleMarginsHeaderFooter( bool bFirstPage, DomainMappe
bool SectionPropertyMap::FloatingTableConversion( const DomainMapper_Impl& rDM_Impl, FloatingTableInfo& rInfo )
{
+ // always convert non-floating tables to floating ones in footnotes and endnotes
+ if ( rInfo.m_bConvertToFloatingInFootnote )
+ return true;
// This is OOXML version of the code deciding if the table needs to be
// in a floating frame.
// For ww8 code, see SwWW8ImplReader::FloatingTableConversion in
@@ -1361,12 +1364,15 @@ void SectionPropertyMap::CloseSectionGroup( DomainMapper_Impl& rDM_Impl )
// Text area width is known at the end of a section: decide if tables should be converted or not.
std::vector<FloatingTableInfo>& rPendingFloatingTables = rDM_Impl.m_aPendingFloatingTables;
- uno::Reference<text::XTextAppendAndConvert> xBodyText( rDM_Impl.GetBodyText(), uno::UNO_QUERY );
for ( FloatingTableInfo & rInfo : rPendingFloatingTables )
{
rInfo.m_nBreakType = m_nBreakType;
if ( FloatingTableConversion( rDM_Impl, rInfo ) )
{
+ uno::Reference<text::XTextAppendAndConvert> xBodyText(
+ rInfo.m_bConvertToFloatingInFootnote
+ ? rInfo.m_xStart->getText()
+ : rDM_Impl.GetBodyText(), uno::UNO_QUERY );
std::vector<css::uno::Any> aFramedRedlines = rDM_Impl.aFramedRedlines;
try
{
commit ce443d747c279ee8fb5ebc0174ab62d6c3a026ed
Author: Tibor Nagy <nagy.tibor2 at nisz.hu>
AuthorDate: Thu Mar 4 14:30:21 2021 +0100
Commit: Gabor Kelemen <kelemen.gabor2 at nisz.hu>
CommitDate: Thu Aug 19 13:10:29 2021 +0200
tdf#119292 sc layout: fix overlapping wrapped cell texts
if rotated by 90 or 270 degrees using clipping to
cell boundaries, like MSO does. E.g. this avoid of
importing MSO documents with unreadable header cells.
Note: it's possible to improve the patch extending
clipping for the neighboring empty area instead of
clipping all text exceeding the cell boundary
(as in the case of the non-rotated, non-wrapped text).
Co-authored-by: Attila Szűcs (NISZ)
Change-Id: Idd37f7eb7208ff3818dcdab93ef0ec57275db954
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111964
Tested-by: László Németh <nemeth at numbertext.org>
Reviewed-by: László Németh <nemeth at numbertext.org>
(cherry picked from commit 967e0cc303c7be4a88905b327b9d02ba12f5e375)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/120646
Tested-by: Gabor Kelemen <kelemen.gabor2 at nisz.hu>
Reviewed-by: Gabor Kelemen <kelemen.gabor2 at nisz.hu>
diff --git a/editeng/source/items/svxfont.cxx b/editeng/source/items/svxfont.cxx
index f5d0e2d79a26..61039b316ac6 100644
--- a/editeng/source/items/svxfont.cxx
+++ b/editeng/source/items/svxfont.cxx
@@ -86,32 +86,62 @@ void SvxFont::SetNonAutoEscapement(short nNewEsc, const OutputDevice* pOutDev)
nEsc = -MAX_ESC_POS;
}
-void SvxFont::DrawArrow( OutputDevice &rOut, const tools::Rectangle& rRect,
- const Size& rSize, const Color& rCol, bool bLeft )
+tools::Polygon SvxFont::DrawArrow( OutputDevice &rOut, const tools::Rectangle& rRect,
+ const Size& rSize, const Color& rCol, bool bLeftOrTop, bool bVertical )
{
- tools::Long nLeft = ( rRect.Left() + rRect.Right() - rSize.Width() )/ 2;
- tools::Long nRight = nLeft + rSize.Width();
- tools::Long nMid = ( rRect.Top() + rRect.Bottom() ) / 2;
- tools::Long nTop = nMid - rSize.Height() / 2;
- tools::Long nBottom = nTop + rSize.Height();
- if( nLeft < rRect.Left() )
+ tools::Polygon aPoly;
+ Point aTmp;
+ Point aNxt;
+ if (bVertical)
{
- nLeft = rRect.Left();
- nRight = rRect.Right();
+ tools::Long nLeft = ((rRect.Left() + rRect.Right()) / 2) - (rSize.Height() / 2);
+ tools::Long nRight = ((rRect.Left() + rRect.Right()) / 2) + (rSize.Height() / 2);
+ tools::Long nMid = (rRect.Left() + rRect.Right()) / 2;
+ tools::Long nTop = ((rRect.Top() + rRect.Bottom()) / 2) - (rSize.Height() / 2);
+ tools::Long nBottom = nTop + rSize.Height();
+ if (nTop < rRect.Top())
+ {
+ if (bLeftOrTop)
+ {
+ nTop = rRect.Top();
+ nBottom = rRect.Bottom();
+ }
+ else
+ {
+ nTop = rRect.Bottom();
+ nBottom = rRect.Bottom() - (rSize.Height() / 2);
+ }
+ }
+ aTmp.setX(nRight);
+ aTmp.setY(nBottom);
+ aNxt.setX(nMid);
+ aNxt.setY(nTop);
+ aPoly.Insert(0, aTmp);
+ aPoly.Insert(0, aNxt);
+ aTmp.setX(nLeft);
+ aPoly.Insert(0, aTmp);
}
- if( nTop < rRect.Top() )
+ else
{
- nTop = rRect.Top();
- nBottom = rRect.Bottom();
+ tools::Long nLeft = (rRect.Left() + rRect.Right() - rSize.Width()) / 2;
+ tools::Long nRight = nLeft + rSize.Width();
+ tools::Long nMid = (rRect.Top() + rRect.Bottom()) / 2;
+ tools::Long nTop = nMid - rSize.Height() / 2;
+ tools::Long nBottom = nTop + rSize.Height();
+ if (nLeft < rRect.Left())
+ {
+ nLeft = rRect.Left();
+ nRight = rRect.Right();
+ }
+ aTmp.setX(bLeftOrTop ? nLeft : nRight);
+ aTmp.setY(nMid);
+ aNxt.setX(bLeftOrTop ? nRight : nLeft);
+ aNxt.setY(nTop);
+ aPoly.Insert(0, aTmp);
+ aPoly.Insert(0, aNxt);
+ aNxt.setY(nBottom);
+ aPoly.Insert(0, aNxt);
}
- tools::Polygon aPoly;
- Point aTmp( bLeft ? nLeft : nRight, nMid );
- Point aNxt( bLeft ? nRight : nLeft, nTop );
- aPoly.Insert( 0, aTmp );
- aPoly.Insert( 0, aNxt );
- aNxt.setY( nBottom );
- aPoly.Insert( 0, aNxt );
- aPoly.Insert( 0, aTmp );
Color aOldLineColor = rOut.GetLineColor();
Color aOldFillColor = rOut.GetFillColor();
rOut.SetFillColor( rCol );
@@ -120,9 +150,9 @@ void SvxFont::DrawArrow( OutputDevice &rOut, const tools::Rectangle& rRect,
rOut.DrawLine( aTmp, aNxt );
rOut.SetLineColor( aOldLineColor );
rOut.SetFillColor( aOldFillColor );
+ return aPoly;
}
-
OUString SvxFont::CalcCaseMap(const OUString &rTxt) const
{
if (!IsCaseMap() || rTxt.isEmpty())
diff --git a/include/editeng/svxfont.hxx b/include/editeng/svxfont.hxx
index c8e8c6670b12..b385ada1592d 100644
--- a/include/editeng/svxfont.hxx
+++ b/include/editeng/svxfont.hxx
@@ -23,6 +23,7 @@
#include <tools/long.hxx>
#include <vcl/font.hxx>
#include <editeng/editengdllapi.h>
+#include <tools/poly.hxx>
// Percentage of height of lower case small capital letters compared to upper case letters
// See i#1526# for full explanation
@@ -103,8 +104,10 @@ public:
const Point &rPos, const OUString &rTxt,
const sal_Int32 nIdx = 0, const sal_Int32 nLen = SAL_MAX_INT32 ) const;
- static void DrawArrow( OutputDevice &rOut, const tools::Rectangle& rRect,
- const Size& rSize, const Color& rCol, bool bLeft );
+ static tools::Polygon DrawArrow( OutputDevice &rOut, const tools::Rectangle& rRect,
+ const Size& rSize, const Color& rCol, bool bLeftOrTop,
+ bool bVertical );
+
SvxFont& operator=( const SvxFont& rFont );
SvxFont& operator=( const Font& rFont );
};
diff --git a/sc/inc/fillinfo.hxx b/sc/inc/fillinfo.hxx
index 0e17f179cc58..38b477f6b110 100644
--- a/sc/inc/fillinfo.hxx
+++ b/sc/inc/fillinfo.hxx
@@ -43,10 +43,10 @@ enum class ScRotateDir : sal_uInt8 {
};
enum class ScClipMark : sal_uInt8 {
- NONE = 0x00, Left = 0x01, Right = 0x02
+ NONE = 0x00, Left = 0x01, Right = 0x02, Bottom = 0x03, Top = 0x04
};
namespace o3tl {
- template<> struct typed_flags<ScClipMark> : is_typed_flags<ScClipMark, 0x03> {};
+ template<> struct typed_flags<ScClipMark> : is_typed_flags<ScClipMark, 0x07> {};
}
const sal_uInt8 SC_CLIPMARK_SIZE = 64;
diff --git a/sc/qa/unit/data/xlsx/tdf119292.xlsx b/sc/qa/unit/data/xlsx/tdf119292.xlsx
new file mode 100644
index 000000000000..cf3d3dc08dbb
Binary files /dev/null and b/sc/qa/unit/data/xlsx/tdf119292.xlsx differ
diff --git a/sc/qa/unit/subsequent_filters-test.cxx b/sc/qa/unit/subsequent_filters-test.cxx
index de39d062e804..38115f9ec2d6 100644
--- a/sc/qa/unit/subsequent_filters-test.cxx
+++ b/sc/qa/unit/subsequent_filters-test.cxx
@@ -107,6 +107,7 @@ public:
//ods, xls, xlsx filter tests
void testCondFormatOperatorsSameRangeXLSX();
+ void testTdf119292();
void testCondFormatFormulaIsXLSX();
void testCondFormatBeginsAndEndsWithXLSX();
void testExtCondFormatXLSX();
@@ -304,6 +305,7 @@ public:
CPPUNIT_TEST_SUITE(ScFiltersTest);
CPPUNIT_TEST(testCondFormatOperatorsSameRangeXLSX);
+ CPPUNIT_TEST(testTdf119292);
CPPUNIT_TEST(testCondFormatFormulaIsXLSX);
CPPUNIT_TEST(testCondFormatBeginsAndEndsWithXLSX);
CPPUNIT_TEST(testExtCondFormatXLSX);
@@ -559,21 +561,54 @@ void ScFiltersTest::testCondFormatOperatorsSameRangeXLSX()
CPPUNIT_ASSERT_EQUAL(ScFormatEntry::Type::ExtCondition, pEntry->GetType());
const ScCondFormatEntry* pCondition = static_cast<const ScCondFormatEntry*>(pEntry);
- CPPUNIT_ASSERT_EQUAL( ScConditionMode::ContainsText, pCondition->GetOperation());
+ CPPUNIT_ASSERT_EQUAL(ScConditionMode::ContainsText, pCondition->GetOperation());
pEntry = pFormat->GetEntry(1);
CPPUNIT_ASSERT(pEntry);
CPPUNIT_ASSERT_EQUAL(ScFormatEntry::Type::ExtCondition, pEntry->GetType());
pCondition = static_cast<const ScCondFormatEntry*>(pEntry);
- CPPUNIT_ASSERT_EQUAL( ScConditionMode::BeginsWith, pCondition->GetOperation());
+ CPPUNIT_ASSERT_EQUAL(ScConditionMode::BeginsWith, pCondition->GetOperation());
pEntry = pFormat->GetEntry(2);
CPPUNIT_ASSERT(pEntry);
CPPUNIT_ASSERT_EQUAL(ScFormatEntry::Type::ExtCondition, pEntry->GetType());
pCondition = static_cast<const ScCondFormatEntry*>(pEntry);
- CPPUNIT_ASSERT_EQUAL( ScConditionMode::EndsWith, pCondition->GetOperation());
+ CPPUNIT_ASSERT_EQUAL(ScConditionMode::EndsWith, pCondition->GetOperation());
+
+ xDocSh->DoClose();
+}
+
+void ScFiltersTest::testTdf119292()
+{
+ ScDocShellRef xDocSh = loadDoc(u"tdf119292.", FORMAT_XLSX);
+ CPPUNIT_ASSERT_MESSAGE("Failed to load tdf119292.xlsx", xDocSh.is());
+
+ ScDocument& rDoc = xDocSh->GetDocument();
+ auto* pDev = rDoc.GetRefDevice();
+ Size aMarkSize(4, 6);
+ Color aArrowFillCol(COL_LIGHTRED);
+
+ // check the points of the polygon if the text is rotated 90 degrees
+ tools::Rectangle aMarkRect1(0, 0, 45, 3);
+ tools::Polygon aPoly90degrees = SvxFont::DrawArrow(*pDev, aMarkRect1, aMarkSize, aArrowFillCol, true, true);
+ Point aPoly90Pos1 = aPoly90degrees.GetPoint(0);
+ Point aPoly90Pos2 = aPoly90degrees.GetPoint(1);
+ Point aPoly90Pos3 = aPoly90degrees.GetPoint(2);
+ CPPUNIT_ASSERT_EQUAL(Point(19,3),aPoly90Pos1);
+ CPPUNIT_ASSERT_EQUAL(Point(22,0),aPoly90Pos2);
+ CPPUNIT_ASSERT_EQUAL(Point(25,3),aPoly90Pos3);
+
+ // check the points of the polygon if the text is rotated 270 degrees
+ tools::Rectangle aMarkRect2(89, 62, 134, 57);
+ tools::Polygon aPoly270degrees = SvxFont::DrawArrow(*pDev, aMarkRect2, aMarkSize, aArrowFillCol, false, true);
+ Point aPoly270Pos1 = aPoly270degrees.GetPoint(0);
+ Point aPoly270Pos2 = aPoly270degrees.GetPoint(1);
+ Point aPoly270Pos3 = aPoly270degrees.GetPoint(2);
+ CPPUNIT_ASSERT_EQUAL(Point(108,54),aPoly270Pos1);
+ CPPUNIT_ASSERT_EQUAL(Point(111,57),aPoly270Pos2);
+ CPPUNIT_ASSERT_EQUAL(Point(114,54),aPoly270Pos3);
xDocSh->DoClose();
}
diff --git a/sc/source/ui/inc/output.hxx b/sc/source/ui/inc/output.hxx
index 528b4ef770f4..d4682e414327 100644
--- a/sc/source/ui/inc/output.hxx
+++ b/sc/source/ui/inc/output.hxx
@@ -231,6 +231,7 @@ private:
bool bSnapPixel;
bool bAnyClipped; // internal
+ bool bVertical;
bool bTabProtected;
bool bLayoutRTL;
@@ -277,11 +278,11 @@ private:
std::unique_ptr<ScFieldEditEngine> CreateOutputEditEngine();
- void ShowClipMarks( DrawEditParam& rParam, tools::Long nEngineHeight, const Size& aCellSize,
- bool bMerged, OutputAreaParam& aAreaParam );
+ void ShowClipMarks( DrawEditParam& rParam, tools::Long nEngineWidth, const Size& aCellSize,
+ bool bMerged, OutputAreaParam& aAreaParam, bool bTop );
ClearableClipRegionPtr Clip(DrawEditParam& rParam, const Size& aCellSize, OutputAreaParam& aAreaParam,
- tools::Long nEngineHeight, bool bWrapFields);
+ tools::Long nEngineWidth, bool bWrapFields, bool bTop);
bool AdjustAreaParamClipRect(OutputAreaParam& rAreaParam);
tools::Long SetEngineTextAndGetWidth( DrawEditParam& rParam, const OUString& rSetString,
diff --git a/sc/source/ui/view/output.cxx b/sc/source/ui/view/output.cxx
index 382792f88782..3652995e37d3 100644
--- a/sc/source/ui/view/output.cxx
+++ b/sc/source/ui/view/output.cxx
@@ -174,6 +174,7 @@ ScOutputData::ScOutputData( OutputDevice* pNewDev, ScOutputType eNewType,
bMarkClipped( false ), // sal_False for printer/metafile etc.
bSnapPixel( false ),
bAnyClipped( false ),
+ bVertical(false),
mpTargetPaintWindow(nullptr), // #i74769# use SdrPaintWindow direct
mpSpellCheckCxt(nullptr)
{
@@ -2553,19 +2554,42 @@ void ScOutputData::DrawClipMarks()
tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
Size aMarkSize( nMarkPixel, (nMarkPixel-1)*2 );
- if ( pInfo->nClipMark & ( bLayoutRTL ? ScClipMark::Right : ScClipMark::Left ) )
+ if (bVertical)
{
- // visually left
- tools::Rectangle aMarkRect = aCellRect;
- aMarkRect.SetRight( aCellRect.Left()+nMarkPixel-1 );
- SvxFont::DrawArrow( *mpDev, aMarkRect, aMarkSize, aArrowFillCol, true );
+ if (pInfo->nClipMark & (bLayoutRTL ? ScClipMark::Bottom : ScClipMark::Top))
+ {
+ // visually top
+ tools::Rectangle aMarkRect = aCellRect;
+ aMarkRect.SetBottom(aCellRect.Top() + nMarkPixel - 1);
+ SvxFont::DrawArrow(*mpDev, aMarkRect, aMarkSize, aArrowFillCol, true, true);
+ }
+ if (pInfo->nClipMark & (bLayoutRTL ? ScClipMark::Top : ScClipMark::Bottom))
+ {
+ // visually bottom
+ tools::Rectangle aMarkRect = aCellRect;
+ aMarkRect.SetTop(aCellRect.Bottom() + nMarkPixel + 1);
+ SvxFont::DrawArrow(*mpDev, aMarkRect, aMarkSize, aArrowFillCol, false,
+ true);
+ }
}
- if ( pInfo->nClipMark & ( bLayoutRTL ? ScClipMark::Left : ScClipMark::Right ) )
+ else
{
- // visually right
- tools::Rectangle aMarkRect = aCellRect;
- aMarkRect.SetLeft( aCellRect.Right()-nMarkPixel+1 );
- SvxFont::DrawArrow( *mpDev, aMarkRect, aMarkSize, aArrowFillCol, false );
+ if (pInfo->nClipMark & (bLayoutRTL ? ScClipMark::Right : ScClipMark::Left))
+ {
+ // visually left
+ tools::Rectangle aMarkRect = aCellRect;
+ aMarkRect.SetRight(aCellRect.Left() + nMarkPixel - 1);
+ SvxFont::DrawArrow(*mpDev, aMarkRect, aMarkSize, aArrowFillCol, true,
+ false);
+ }
+ if (pInfo->nClipMark & (bLayoutRTL ? ScClipMark::Left : ScClipMark::Right))
+ {
+ // visually right
+ tools::Rectangle aMarkRect = aCellRect;
+ aMarkRect.SetLeft(aCellRect.Right() - nMarkPixel + 1);
+ SvxFont::DrawArrow(*mpDev, aMarkRect, aMarkSize, aArrowFillCol, false,
+ false);
+ }
}
}
nPosX += pRowInfo[0].pCellInfo[nX+1].nWidth * nLayoutSign;
diff --git a/sc/source/ui/view/output2.cxx b/sc/source/ui/view/output2.cxx
index 367d0dca1f5a..078333e39ba5 100644
--- a/sc/source/ui/view/output2.cxx
+++ b/sc/source/ui/view/output2.cxx
@@ -3150,48 +3150,56 @@ void ScOutputData::DrawEditStandard(DrawEditParam& rParam)
rParam.adjustForHyperlinkInPDF(aURLStart, mpDev);
}
-void ScOutputData::ShowClipMarks( DrawEditParam& rParam, tools::Long nEngineHeight, const Size& aCellSize,
- bool bMerged, OutputAreaParam& aAreaParam)
+void ScOutputData::ShowClipMarks( DrawEditParam& rParam, tools::Long nEngineWidth, const Size& aCellSize,
+ bool bMerged, OutputAreaParam& aAreaParam, bool bTop)
{
- // Show clip marks if height is at least 5pt too small and
+ // Show clip marks if width is at least 5pt too small and
// there are several lines of text.
// Not for asian vertical text, because that would interfere
// with the default right position of the text.
// Only with automatic line breaks, to avoid having to find
// the cells with the horizontal end of the text again.
- if ( !(nEngineHeight - aCellSize.Height() > 100 &&
- rParam.mbBreak && bMarkClipped &&
- ( rParam.mpEngine->GetParagraphCount() > 1 || rParam.mpEngine->GetLineCount(0) > 1 )) )
- return;
-
- CellInfo* pClipMarkCell = nullptr;
- if ( bMerged )
+ if (nEngineWidth - aCellSize.Width() > 100 && rParam.mbBreak && bMarkClipped
+ && (rParam.mpEngine->GetParagraphCount() > 1 || rParam.mpEngine->GetLineCount(0) > 1))
{
- // anywhere in the merged area...
- SCCOL nClipX = ( rParam.mnX < nX1 ) ? nX1 : rParam.mnX;
- pClipMarkCell = &pRowInfo[(rParam.mnArrY != 0) ? rParam.mnArrY : 1].pCellInfo[nClipX+1];
- }
- else
- pClipMarkCell = &rParam.mpThisRowInfo->pCellInfo[rParam.mnX+1];
-
- pClipMarkCell->nClipMark |= ScClipMark::Right; //! also allow left?
- bAnyClipped = true;
+ CellInfo* pClipMarkCell = nullptr;
+ if (bMerged)
+ {
+ // anywhere in the merged area...
+ SCCOL nClipX = (rParam.mnX < nX1) ? nX1 : rParam.mnX;
+ pClipMarkCell = &pRowInfo[(rParam.mnArrY != 0) ? rParam.mnArrY : 1].pCellInfo[nClipX + 1];
+ }
+ else
+ pClipMarkCell = &rParam.mpThisRowInfo->pCellInfo[rParam.mnX + 1];
- const tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
- if ( aAreaParam.maClipRect.Right() - nMarkPixel > aAreaParam.maClipRect.Left() )
- aAreaParam.maClipRect.AdjustRight( -nMarkPixel );
+ bAnyClipped = true;
+ bVertical = true;
+ const tools::Long nMarkPixel = static_cast<tools::Long>(SC_CLIPMARK_SIZE * mnPPTX);
+ if (bTop)
+ {
+ pClipMarkCell->nClipMark |= ScClipMark::Top;
+ if (aAreaParam.maClipRect.Top() - nMarkPixel < aAreaParam.maClipRect.Bottom())
+ aAreaParam.maClipRect.AdjustTop(+nMarkPixel);
+ }
+ else
+ {
+ pClipMarkCell->nClipMark |= ScClipMark::Bottom;
+ if (aAreaParam.maClipRect.Top() - nMarkPixel < aAreaParam.maClipRect.Bottom())
+ aAreaParam.maClipRect.AdjustBottom(-nMarkPixel);
+ }
+ }
}
ClearableClipRegionPtr ScOutputData::Clip( DrawEditParam& rParam, const Size& aCellSize,
- OutputAreaParam& aAreaParam, tools::Long nEngineHeight,
- bool bWrapFields)
+ OutputAreaParam& aAreaParam, tools::Long nEngineWidth,
+ bool bWrapFields, bool bTop)
{
// Also take fields in a cell with automatic breaks into account: clip to cell width
bool bClip = AdjustAreaParamClipRect(aAreaParam) || aAreaParam.mbLeftClip || aAreaParam.mbRightClip || bWrapFields;
bool bSimClip = false;
const Size& aRefOne = mpRefDevice->PixelToLogic(Size(1,1));
- if ( nEngineHeight >= aCellSize.Height() + aRefOne.Height() )
+ if ( nEngineWidth >= aCellSize.Width() + aRefOne.Width() )
{
const ScMergeAttr* pMerge = &rParam.mpPattern->GetItem(ATTR_MERGE);
const bool bMerged = pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1;
@@ -3207,7 +3215,7 @@ ClearableClipRegionPtr ScOutputData::Clip( DrawEditParam& rParam, const Size& aC
else
bSimClip = true;
- ShowClipMarks( rParam, nEngineHeight, aCellSize, bMerged, aAreaParam);
+ ShowClipMarks( rParam, nEngineWidth, aCellSize, bMerged, aAreaParam, bTop);
}
// Clip marks are already handled in GetOutputArea
@@ -3389,7 +3397,7 @@ void ScOutputData::DrawEditBottomTop(DrawEditParam& rParam)
Point aURLStart;
{
- const auto pClipRegion = Clip( rParam, aCellSize, aAreaParam, nEngineHeight, bWrapFields );
+ const auto pClipRegion = Clip( rParam, aCellSize, aAreaParam, nEngineWidth, bWrapFields, true );
Point aLogicStart(nStartX, nStartY);
rParam.calcStartPosForVertical(aLogicStart, aCellSize.Width(), nEngineWidth, nTopM, mpRefDevice);
@@ -3635,7 +3643,7 @@ void ScOutputData::DrawEditTopBottom(DrawEditParam& rParam)
Point aURLStart;
{
- const auto pClipRegion = Clip( rParam, aCellSize, aAreaParam, nEngineHeight, bWrapFields );
+ const auto pClipRegion = Clip( rParam, aCellSize, aAreaParam, nEngineWidth, bWrapFields, false );
Point aLogicStart(nStartX, nStartY);
rParam.calcStartPosForVertical(aLogicStart, aCellSize.Width(), nEngineWidth, nTopM, mpRefDevice);
commit 420d0ced463988c6cb7596721a54ec2d50c58c69
Author: Daniel Arato (NISZ) <arato.daniel at nisz.hu>
AuthorDate: Tue Mar 9 14:11:11 2021 +0100
Commit: Gabor Kelemen <kelemen.gabor2 at nisz.hu>
CommitDate: Thu Aug 19 12:14:57 2021 +0200
tdf#118535 DOCX export: save header image once
Writer used to dump the same image file as many times
as it was featured in different headers or footers in
the document, bloating the .docx file size.
This is countered by making all "relationships" in the
header*.xml.rels files point to the same image.
Change-Id: I44d72630289c721d58d8f7e208517df2f1fe621c
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/112656
Tested-by: László Németh <nemeth at numbertext.org>
Reviewed-by: László Németh <nemeth at numbertext.org>
(cherry picked from commit 797fef38612fb2fd62d1f6591619b9361e526bca)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/120643
Tested-by: Gabor Kelemen <kelemen.gabor2 at nisz.hu>
Reviewed-by: Gabor Kelemen <kelemen.gabor2 at nisz.hu>
diff --git a/include/oox/export/drawingml.hxx b/include/oox/export/drawingml.hxx
index 8c4ccd0bd224..a845a5a79dc2 100644
--- a/include/oox/export/drawingml.hxx
+++ b/include/oox/export/drawingml.hxx
@@ -124,8 +124,10 @@ public:
virtual void WriteTextBox(css::uno::Reference<css::drawing::XShape> xShape) = 0;
/// Look up the RelId of a graphic based on its checksum.
virtual OUString FindRelId(BitmapChecksum nChecksum) = 0;
- /// Store the RelId of a graphic based on its checksum.
- virtual void CacheRelId(BitmapChecksum nChecksum, const OUString& rRelId) = 0;
+ /// Look up the filename of a graphic based on its checksum.
+ virtual OUString FindFileName(BitmapChecksum nChecksum) = 0;
+ /// Store the RelId and filename of a graphic based on its checksum.
+ virtual void CacheRelId(BitmapChecksum nChecksum, const OUString& rRelId, const OUString& rFileName) = 0;
/// Get textbox which belongs to the shape.
virtual css::uno::Reference<css::text::XTextFrame> GetUnoTextFrame(
css::uno::Reference<css::drawing::XShape> xShape) = 0;
@@ -191,7 +193,7 @@ public:
void SetBackgroundDark(bool bIsDark) { mbIsBackgroundDark = bIsDark; }
/// If bRelPathToMedia is true add "../" to image folder path while adding the image relationship
- OUString WriteImage( const Graphic &rGraphic , bool bRelPathToMedia = false);
+ OUString WriteImage( const Graphic &rGraphic , bool bRelPathToMedia = false, OUString* pFileName = nullptr );
void WriteColor( ::Color nColor, sal_Int32 nAlpha = MAX_PERCENT );
void WriteColor( const OUString& sColorSchemeName, const css::uno::Sequence< css::beans::PropertyValue >& aTransformations, sal_Int32 nAlpha = MAX_PERCENT );
diff --git a/include/oox/export/vmlexport.hxx b/include/oox/export/vmlexport.hxx
index 9aea58378660..53889dac86fd 100644
--- a/include/oox/export/vmlexport.hxx
+++ b/include/oox/export/vmlexport.hxx
@@ -62,8 +62,10 @@ public:
virtual void WriteVMLTextBox(css::uno::Reference<css::drawing::XShape> xShape) = 0;
/// Look up the RelId of a graphic based on its checksum.
virtual OUString FindRelId(BitmapChecksum nChecksum) = 0;
- /// Store the RelId of a graphic based on its checksum.
- virtual void CacheRelId(BitmapChecksum nChecksum, const OUString& rRelId) = 0;
+ /// Look up the filename of a graphic based on its checksum.
+ virtual OUString FindFileName(BitmapChecksum nChecksum) = 0;
+ /// Store the RelId and filename of a graphic based on its checksum.
+ virtual void CacheRelId(BitmapChecksum nChecksum, const OUString& rRelId, const OUString& rFileName) = 0;
protected:
VMLTextExport() {}
virtual ~VMLTextExport() {}
diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx
index e8c90c475725..4970cea7a29a 100644
--- a/oox/source/export/drawingml.cxx
+++ b/oox/source/export/drawingml.cxx
@@ -1163,7 +1163,7 @@ const char* DrawingML::GetRelationCompPrefix() const
return "unknown";
}
-OUString DrawingML::WriteImage( const Graphic& rGraphic , bool bRelPathToMedia )
+OUString DrawingML::WriteImage( const Graphic& rGraphic , bool bRelPathToMedia, OUString* pFileName )
{
GfxLink aLink = rGraphic.GetGfxLink ();
OUString sMediaType;
@@ -1267,15 +1267,18 @@ OUString DrawingML::WriteImage( const Graphic& rGraphic , bool bRelPathToMedia )
sRelationCompPrefix = "../";
else
sRelationCompPrefix = GetRelationCompPrefix();
+ OUString sPath = OUStringBuffer()
+ .appendAscii( sRelationCompPrefix.getStr() )
+ .appendAscii( sRelPathToMedia.getStr() )
+ .append( static_cast<sal_Int32>(mnImageCounter ++) )
+ .appendAscii( pExtension )
+ .makeStringAndClear();
sRelId = mpFB->addRelation( mpFS->getOutputStream(),
oox::getRelationship(Relationship::IMAGE),
- OUStringBuffer()
- .appendAscii( sRelationCompPrefix.getStr() )
- .appendAscii( sRelPathToMedia.getStr() )
- .append( static_cast<sal_Int32>(mnImageCounter ++) )
- .appendAscii( pExtension )
- .makeStringAndClear() );
+ sPath );
+ if (pFileName)
+ *pFileName = sPath;
return sRelId;
}
@@ -1435,6 +1438,7 @@ OUString DrawingML::WriteXGraphicBlip(uno::Reference<beans::XPropertySet> const
bool bRelPathToMedia)
{
OUString sRelId;
+ OUString sFileName;
if (!rxGraphic.is())
return sRelId;
@@ -1444,16 +1448,25 @@ OUString DrawingML::WriteXGraphicBlip(uno::Reference<beans::XPropertySet> const
{
BitmapChecksum nChecksum = aGraphic.GetChecksum();
sRelId = mpTextExport->FindRelId(nChecksum);
+ sFileName = mpTextExport->FindFileName(nChecksum);
}
if (sRelId.isEmpty())
{
- sRelId = WriteImage(aGraphic, bRelPathToMedia);
+ sRelId = WriteImage(aGraphic, bRelPathToMedia, &sFileName);
if (mpTextExport)
{
BitmapChecksum nChecksum = aGraphic.GetChecksum();
- mpTextExport->CacheRelId(nChecksum, sRelId);
+ mpTextExport->CacheRelId(nChecksum, sRelId, sFileName);
}
}
+ else
+ {
+ // Include the same relation again. This makes it possible to
+ // reuse an image across different headers.
+ sRelId = mpFB->addRelation( mpFS->getOutputStream(),
+ oox::getRelationship(Relationship::IMAGE),
+ sFileName );
+ }
mpFS->startElementNS(XML_a, XML_blip, FSNS(XML_r, XML_embed), sRelId);
diff --git a/oox/source/export/vmlexport.cxx b/oox/source/export/vmlexport.cxx
index 5ef5ed096b2e..37e618a1478d 100644
--- a/oox/source/export/vmlexport.cxx
+++ b/oox/source/export/vmlexport.cxx
@@ -742,8 +742,9 @@ void VMLExport::Commit( EscherPropertyContainer& rProps, const tools::Rectangle&
OUString aImageId = m_pTextExport->FindRelId(nChecksum);
if (aImageId.isEmpty())
{
- aImageId = m_pTextExport->GetDrawingML().WriteImage(aGraphic);
- m_pTextExport->CacheRelId(nChecksum, aImageId);
+ OUString aFileName;
+ aImageId = m_pTextExport->GetDrawingML().WriteImage(aGraphic, false, &aFileName);
+ m_pTextExport->CacheRelId(nChecksum, aImageId, aFileName);
}
pAttrList->add(FSNS(XML_r, XML_id),
OUStringToOString(aImageId, RTL_TEXTENCODING_UTF8));
@@ -764,8 +765,9 @@ void VMLExport::Commit( EscherPropertyContainer& rProps, const tools::Rectangle&
OUString aImageId = m_pTextExport->FindRelId(nChecksum);
if (aImageId.isEmpty())
{
- aImageId = m_pTextExport->GetDrawingML().WriteImage(aGraphic);
- m_pTextExport->CacheRelId(nChecksum, aImageId);
+ OUString aFileName;
+ aImageId = m_pTextExport->GetDrawingML().WriteImage(aGraphic, false, &aFileName);
+ m_pTextExport->CacheRelId(nChecksum, aImageId, aFileName);
}
pAttrList->add(FSNS(XML_r, XML_id),
OUStringToOString(aImageId, RTL_TEXTENCODING_UTF8));
diff --git a/sw/qa/extras/ooxmlexport/data/tdf118535.odt b/sw/qa/extras/ooxmlexport/data/tdf118535.odt
new file mode 100644
index 000000000000..146c6f471a18
Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf118535.odt differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx
index e4b4a2ee9c19..f0664b1f002d 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx
@@ -18,6 +18,8 @@
#include <com/sun/star/text/XTextViewCursorSupplier.hpp>
#include <com/sun/star/text/XTextTable.hpp>
#include <com/sun/star/text/XTextTablesSupplier.hpp>
+#include <com/sun/star/packages/zip/ZipFileAccess.hpp>
+#include <comphelper/configuration.hxx>
#include <editeng/escapementitem.hxx>
#include <unotools/fltrcfg.hxx>
#include <textboxhelper.hxx>
@@ -131,6 +133,17 @@ DECLARE_OOXMLEXPORT_TEST(testTdf140137, "tdf140137.docx")
// Don't throw exception during load
}
+DECLARE_OOXMLEXPORT_TEST(testTdf118535, "tdf118535.odt")
+{
+ uno::Reference<packages::zip::XZipFileAccess2> xNameAccess = packages::zip::ZipFileAccess::createWithURL(comphelper::getComponentContext(m_xSFactory), maTempFile.GetURL());
+ CPPUNIT_ASSERT_EQUAL(true, bool(xNameAccess->hasByName("word/media/image1.jpeg")));
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: false
+ // - Actual : true
+ // i.e. the embedded picture would have been saved twice.
+ CPPUNIT_ASSERT_EQUAL(false, bool(xNameAccess->hasByName("word/media/image2.jpeg")));
+}
+
DECLARE_OOXMLEXPORT_TEST(testTdf133473_shadowSize, "tdf133473.docx")
{
uno::Reference<drawing::XShape> xShape = getShape(1);
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx
index 79800cf24cbc..e54562a569c5 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -4895,15 +4895,25 @@ OUString DocxAttributeOutput::FindRelId(BitmapChecksum nChecksum)
OUString aRet;
if (!m_aSdrRelIdCache.empty() && m_aSdrRelIdCache.top().find(nChecksum) != m_aSdrRelIdCache.top().end())
- aRet = m_aSdrRelIdCache.top()[nChecksum];
+ aRet = m_aSdrRelIdCache.top()[nChecksum].first;
return aRet;
}
-void DocxAttributeOutput::CacheRelId(BitmapChecksum nChecksum, const OUString& rRelId)
+OUString DocxAttributeOutput::FindFileName(BitmapChecksum nChecksum)
+{
+ OUString aRet;
+
+ if (!m_aSdrRelIdCache.empty() && m_aSdrRelIdCache.top().find(nChecksum) != m_aSdrRelIdCache.top().end())
+ aRet = m_aSdrRelIdCache.top()[nChecksum].second;
+
+ return aRet;
+}
+
+void DocxAttributeOutput::CacheRelId(BitmapChecksum nChecksum, const OUString& rRelId, const OUString& rFileName)
{
if (!m_aSdrRelIdCache.empty())
- m_aSdrRelIdCache.top()[nChecksum] = rRelId;
+ m_aSdrRelIdCache.top()[nChecksum] = std::pair(rRelId, rFileName);
}
uno::Reference<css::text::XTextFrame> DocxAttributeOutput::GetUnoTextFrame(
@@ -4912,9 +4922,9 @@ uno::Reference<css::text::XTextFrame> DocxAttributeOutput::GetUnoTextFrame(
return SwTextBoxHelper::getUnoTextFrame(xShape);
}
-OString DocxAttributeOutput::getExistingGraphicRelId(BitmapChecksum nChecksum)
+std::pair<OString, OUString> DocxAttributeOutput::getExistingGraphicRelId(BitmapChecksum nChecksum)
{
- OString aResult;
+ std::pair<OString, OUString> aResult;
if (m_aRelIdCache.empty())
return aResult;
@@ -4929,10 +4939,10 @@ OString DocxAttributeOutput::getExistingGraphicRelId(BitmapChecksum nChecksum)
return aResult;
}
-void DocxAttributeOutput::cacheGraphicRelId(BitmapChecksum nChecksum, OString const & rRelId)
+void DocxAttributeOutput::cacheGraphicRelId(BitmapChecksum nChecksum, OString const & rRelId, OUString const & rFileName)
{
if (!m_aRelIdCache.empty())
- m_aRelIdCache.top().emplace(nChecksum, rRelId);
+ m_aRelIdCache.top().emplace(nChecksum, std::pair(rRelId, rFileName));
}
void DocxAttributeOutput::FlyFrameGraphic( const SwGrfNode* pGrfNode, const Size& rSize, const SwFlyFrameFormat* pOLEFrameFormat, SwOLENode* pOLENode, const SdrObject* pSdrObj )
@@ -4981,17 +4991,29 @@ void DocxAttributeOutput::FlyFrameGraphic( const SwGrfNode* pGrfNode, const Size
aGraphic = *pOLENode->GetGraphic();
BitmapChecksum aChecksum = aGraphic.GetChecksum();
- aRelId = getExistingGraphicRelId(aChecksum);
+ OUString aFileName;
+ std::tie(aRelId, aFileName) = getExistingGraphicRelId(aChecksum);
+ OUString aImageId;
if (aRelId.isEmpty())
{
// Not in cache, then need to write it.
m_rDrawingML.SetFS( m_pSerializer ); // to be sure that we write to the right stream
- OUString aImageId = m_rDrawingML.WriteImage(aGraphic);
+ aImageId = m_rDrawingML.WriteImage(aGraphic, false, &aFileName);
+
+ aRelId = OUStringToOString( aImageId, RTL_TEXTENCODING_UTF8 );
+ cacheGraphicRelId(aChecksum, aRelId, aFileName);
+ }
+ else
+ {
+ // Include the same relation again. This makes it possible to
+ // reuse an image across different headers.
+ aImageId = m_rDrawingML.GetFB()->addRelation( m_pSerializer->getOutputStream(),
+ oox::getRelationship(Relationship::IMAGE),
+ aFileName );
aRelId = OUStringToOString( aImageId, RTL_TEXTENCODING_UTF8 );
- cacheGraphicRelId(aChecksum, aRelId);
}
nImageType = XML_embed;
diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx b/sw/source/filter/ww8/docxattributeoutput.hxx
index 6c14e2da6bd5..4e54b1319444 100644
--- a/sw/source/filter/ww8/docxattributeoutput.hxx
+++ b/sw/source/filter/ww8/docxattributeoutput.hxx
@@ -959,14 +959,14 @@ private:
// store hardcoded value which was set during import.
sal_Int32 m_nParaBeforeSpacing,m_nParaAfterSpacing;
- OString getExistingGraphicRelId(BitmapChecksum aChecksum);
- void cacheGraphicRelId(BitmapChecksum nChecksum, OString const & rRelId);
+ std::pair<OString, OUString> getExistingGraphicRelId(BitmapChecksum aChecksum);
+ void cacheGraphicRelId(BitmapChecksum nChecksum, OString const & rRelId, OUString const & rFileName);
/// RelId <-> Graphic* cache, so that in case of alternate content, the same graphic only gets written once.
- std::stack< std::map<BitmapChecksum, OString> > m_aRelIdCache;
+ std::stack< std::map<BitmapChecksum, std::pair<OString, OUString>> > m_aRelIdCache;
/// RelId <-> BitmapChecksum cache, similar to m_aRelIdCache, but used for non-Writer graphics, handled in oox.
- std::stack< std::map<BitmapChecksum, OUString> > m_aSdrRelIdCache;
+ std::stack< std::map<BitmapChecksum, std::pair<OUString, OUString>> > m_aSdrRelIdCache;
/// members to control the existence of grabbagged SDT properties in the paragraph
sal_Int32 m_nParagraphSdtPrToken;
@@ -1025,7 +1025,8 @@ public:
/// DMLTextExport
virtual void WriteTextBox(css::uno::Reference<css::drawing::XShape> xShape) override;
virtual OUString FindRelId(BitmapChecksum nChecksum) override;
- virtual void CacheRelId(BitmapChecksum nChecksum, const OUString& rRelId) override;
+ virtual OUString FindFileName(BitmapChecksum nChecksum) override;
+ virtual void CacheRelId(BitmapChecksum nChecksum, const OUString& rRelId, const OUString& rFileName) override;
virtual css::uno::Reference<css::text::XTextFrame> GetUnoTextFrame(
css::uno::Reference<css::drawing::XShape> xShape) override;
virtual oox::drawingml::DrawingML& GetDrawingML() override;
diff --git a/sw/source/filter/ww8/docxexport.cxx b/sw/source/filter/ww8/docxexport.cxx
index 71d991192384..3d09820e545e 100644
--- a/sw/source/filter/ww8/docxexport.cxx
+++ b/sw/source/filter/ww8/docxexport.cxx
@@ -818,15 +818,11 @@ void DocxExport::WriteHeaderFooter( const SwFormat* pFormat, bool bHeader, const
SetFS( pFS );
{
DocxTableExportContext aTableExportContext(*m_pAttrOutput);
- //When the stream changes the cache which is maintained for the graphics in case of alternate content is not cleared.
- //So clearing the alternate content graphic cache.
- m_pAttrOutput->PushRelIdCache();
// do the work
if (pFormat == nullptr)
AttrOutput().EmptyParagraph();
else
WriteHeaderFooterText(*pFormat, bHeader);
- m_pAttrOutput->PopRelIdCache();
m_pAttrOutput->EndParaSdtBlock();
}
commit 3b53232b9ab403b61d153d6d2ca32cb5ad0e3a1a
Author: Attila Szűcs <szucs.attila3 at nisz.hu>
AuthorDate: Thu Jan 7 19:07:23 2021 +0100
Commit: Gabor Kelemen <kelemen.gabor2 at nisz.hu>
CommitDate: Thu Aug 19 11:40:02 2021 +0200
tdf#139115 vcl tree list: add new toggle behaviors
at clicking on list items with checkboxes to clean-up
generic VCL plugin commit 2471d6f44c7e8ecbe86a90eeb593b899a08a7408
(tdf#116675 vcl tree list: toggle by label click (e.g. in AutoFilter)).
This limits the new toggle-always to ScCheckListMenuControl
(e.g. AutoFilter).
set_clicks_to_toggle() options to set the effect of the
click on a treeview list item:
0 == clicking never toggles the checkbox (default setting)
1 == clicking always toggles the checkbox
2 == clicking only toggles the checkbox of a selected list item,
i.e. the first click selects a not selected list item, and
only the second click toggles its checkbox.
It would be better if we could set this information in the
.ui files, but I did not find a good way for that.
A good place in the code to call this function can be
found by searching for the .ui filename.
Co-authored-by: Tibor Nagy (NISZ)
Change-Id: I5c72d710508fc397f2b6f959d7680b23f8dc6f67
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/108947
Tested-by: László Németh <nemeth at numbertext.org>
Reviewed-by: László Németh <nemeth at numbertext.org>
(cherry picked from commit 3d2a431da1126f4924f6cd7e5abac6488cd480e7)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/120641
Tested-by: Gabor Kelemen <kelemen.gabor2 at nisz.hu>
Reviewed-by: Gabor Kelemen <kelemen.gabor2 at nisz.hu>
diff --git a/include/vcl/toolkit/treelistbox.hxx b/include/vcl/toolkit/treelistbox.hxx
index 7f3bf814bd19..bd7b3d5c2119 100644
--- a/include/vcl/toolkit/treelistbox.hxx
+++ b/include/vcl/toolkit/treelistbox.hxx
@@ -221,6 +221,9 @@ class VCL_DLLPUBLIC SvTreeListBox
bool mbQuickSearch; // Enables type-ahead search in the check list box.
bool mbActivateOnSingleClick; // Make single click "activate" a row like a double-click normally does
bool mbHoverSelection; // Make mouse over a row "select" a row like a single-click normally does
+ sal_Int8 mnClicksToToggle; // 0 == Click on a row not toggle its checkbox.
+ // 1 == Every click on row toggle its checkbox.
+ // 2 == First click select, second click toggle.
SvTreeListEntry* pHdlEntry;
@@ -681,6 +684,9 @@ public:
void SetHoverSelection(bool bEnable) { mbHoverSelection = bEnable; }
bool GetHoverSelection() const { return mbHoverSelection; }
+ // Set when clicks toggle the checkbox of the row.
+ void SetClicksToToggle(sal_Int8 nCount) { mnClicksToToggle = nCount; }
+
void SetForceMakeVisible(bool bEnable);
virtual FactoryFunction GetUITestFactory() const override;
diff --git a/include/vcl/weld.hxx b/include/vcl/weld.hxx
index 15236af38c3a..d75c90406fdb 100644
--- a/include/vcl/weld.hxx
+++ b/include/vcl/weld.hxx
@@ -946,6 +946,8 @@ public:
// inserted after this call which can be accessed with col index -1
virtual void enable_toggle_buttons(ColumnToggleType eType) = 0;
+ virtual void set_clicks_to_toggle(int nToggleBehavior) = 0;
+
//by index
virtual int get_selected_index() const = 0;
//Don't select when frozen, select after thaw. Note selection doesn't survive a freeze.
diff --git a/sc/source/ui/cctrl/checklistmenu.cxx b/sc/source/ui/cctrl/checklistmenu.cxx
index 3da3de6be22c..ef1942f753e1 100644
--- a/sc/source/ui/cctrl/checklistmenu.cxx
+++ b/sc/source/ui/cctrl/checklistmenu.cxx
@@ -444,6 +444,9 @@ ScCheckListMenuControl::ScCheckListMenuControl(ScCheckListMenuWindow* pParent, v
, maOpenTimer(this)
, maCloseTimer(this)
{
+ mxTreeChecks->set_clicks_to_toggle(1);
+ mxListChecks->set_clicks_to_toggle(1);
+
/*
tdf#136559 If we have no dates we don't need a tree
structure, just a list. GtkListStore can be then
diff --git a/vcl/inc/svimpbox.hxx b/vcl/inc/svimpbox.hxx
index ae171cca8e65..54382ea6e5d7 100644
--- a/vcl/inc/svimpbox.hxx
+++ b/vcl/inc/svimpbox.hxx
@@ -188,6 +188,7 @@ protected:
VclPtr<SvTreeListBox> m_pView;
VclPtr<ScrollBar> m_aVerSBar;
SvTreeListEntry* m_pCursor;
+ SvTreeListEntry* m_pCursorOld;
SvTreeListEntry* m_pStartEntry;
ImplSVEvent* m_nCurUserEvent;
Size m_aOutputSize;
diff --git a/vcl/source/app/salvtables.cxx b/vcl/source/app/salvtables.cxx
index 317770c367c3..0fa3086165ca 100644
--- a/vcl/source/app/salvtables.cxx
+++ b/vcl/source/app/salvtables.cxx
@@ -3826,6 +3826,11 @@ public:
set_font_color(pEntry, rColor);
}
+ void set_clicks_to_toggle(int nToggleBehavior)
+ {
+ m_xTreeView->SetClicksToToggle(nToggleBehavior);
+ }
+
virtual void set_font_color(const weld::TreeIter& rIter, const Color& rColor) override
{
const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter);
diff --git a/vcl/source/treelist/treelistbox.cxx b/vcl/source/treelist/treelistbox.cxx
index ba24d0e6cc73..2f79068fc85f 100644
--- a/vcl/source/treelist/treelistbox.cxx
+++ b/vcl/source/treelist/treelistbox.cxx
@@ -399,6 +399,7 @@ SvTreeListBox::SvTreeListBox(vcl::Window* pParent, WinBits nWinStyle) :
mbQuickSearch(false),
mbActivateOnSingleClick(false),
mbHoverSelection(false),
+ mnClicksToToggle(0), //at default clicking on a row won't toggle its default checkbox
eSelMode(SelectionMode::NONE),
nMinWidthInChars(0),
mnDragAction(DND_ACTION_COPYMOVE | DND_ACTION_LINK),
@@ -2283,17 +2284,18 @@ void SvTreeListBox::Paint(vcl::RenderContext& rRenderContext, const tools::Recta
void SvTreeListBox::MouseButtonDown( const MouseEvent& rMEvt )
{
+ pImpl->m_pCursorOld = pImpl->m_pCursor;
pImpl->MouseButtonDown( rMEvt );
}
void SvTreeListBox::MouseButtonUp( const MouseEvent& rMEvt )
{
// tdf#116675 clicking on an entry should toggle its checkbox
- if (rMEvt.IsLeft() && (nTreeFlags & SvTreeFlags::CHKBTN))
+ if (rMEvt.IsLeft() && (nTreeFlags & SvTreeFlags::CHKBTN) && mnClicksToToggle > 0)
{
const Point aPnt = rMEvt.GetPosPixel();
SvTreeListEntry* pEntry = GetEntry(aPnt);
- if (pEntry && pEntry->m_Items.size() > 0)
+ if (pEntry && pEntry->m_Items.size() > 0 && (mnClicksToToggle == 1 || pEntry == pImpl->m_pCursorOld))
{
SvLBoxItem* pItem = GetItem(pEntry, aPnt.X());
// if the checkbox button was clicked, that will be toggled later, do not toggle here
diff --git a/vcl/unx/gtk3/gtk3gtkinst.cxx b/vcl/unx/gtk3/gtk3gtkinst.cxx
index f067c92b4410..e4e6ab700f49 100644
--- a/vcl/unx/gtk3/gtk3gtkinst.cxx
+++ b/vcl/unx/gtk3/gtk3gtkinst.cxx
@@ -11258,6 +11258,10 @@ public:
}
}
+ virtual void set_clicks_to_toggle(int /*nToggleBehavior*/) override
+ {
+ }
+
virtual void set_extra_row_indent(const weld::TreeIter& rIter, int nIndentLevel) override
{
const GtkInstanceTreeIter& rGtkIter = static_cast<const GtkInstanceTreeIter&>(rIter);
commit 40acda4e78127fa9f513646ef210b074d40cf307
Author: Balazs Varga <balazs.varga991 at gmail.com>
AuthorDate: Thu Apr 22 10:23:41 2021 +0200
Commit: Gabor Kelemen <kelemen.gabor2 at nisz.hu>
CommitDate: Thu Aug 19 09:40:36 2021 +0200
Related: tdf#140968 avoid duplicated filter values
Group and filter based on the actual cell format,
like MSO does, to simplify filtering of values rounded by
the cell format (e.g. selecting the single AutoFilter condition
"1.0" to filter both 1.01 and 0.99).
Followed up of 4fd1333ba4bb4f2311e9098291154772bd310429
(tdf#140968 tdf#140978 XLSX import: fix lost rounded filters)
tdf#141916: odf export: Indicating the formatted filter values,
by export the XML_DATA_TYPE with XML_TEXT, to avoid bad filtering
of formatted filter values.
tdf#141775: fix regression of 4fd1333ba4bb4f2311e9098291154772bd310429
(tdf#140968 tdf#140978 XLSX import: fix lost rounded filters)
tdf#141915: fix OOXML import of 0.0 filter values of filter conditions
by setting it byValue filtering in datauno.cxx.
Change-Id: I1c840249ee1ef95aff33a4740b8bf2ebc47f2325
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/113314
Tested-by: Jenkins
Tested-by: László Németh <nemeth at numbertext.org>
Reviewed-by: László Németh <nemeth at numbertext.org>
(cherry picked from commit d5c2584bf36d21580db677b231c57f99f49aa2cb)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/120440
Tested-by: Gabor Kelemen <kelemen.gabor2 at nisz.hu>
Reviewed-by: Gabor Kelemen <kelemen.gabor2 at nisz.hu>
diff --git a/sc/inc/typedstrdata.hxx b/sc/inc/typedstrdata.hxx
index 56414f10c267..fdbb6b786e04 100644
--- a/sc/inc/typedstrdata.hxx
+++ b/sc/inc/typedstrdata.hxx
@@ -25,13 +25,14 @@ public:
Header = 4
};
- ScTypedStrData( const OUString& rStr, double nVal = 0.0,
- StringType eType = Standard, bool bDate = false );
+ ScTypedStrData( const OUString& rStr, double nVal = 0.0, StringType eType = Standard,
+ bool bDate = false, bool mbIsFormatted = false, bool bDuplicated = false );
bool IsDate() const { return mbIsDate;}
const OUString& GetString() const { return maStrValue;}
StringType GetStringType() const { return meStrType;}
double GetValue() const { return mfValue; }
+ bool IsDuplicated() const { return mbIsDuplicated; }
struct LessCaseSensitive
{
@@ -60,6 +61,8 @@ private:
double mfValue;
StringType meStrType;
bool mbIsDate;
+ bool mbIsFormatted; // true if the cell value is a formatted filter value
+ bool mbIsDuplicated; // true if the cell has a formatted filter value and has at least one duplicate formatted value.
};
class FindTypedStrData
diff --git a/sc/qa/uitest/autofilter/autofilter.py b/sc/qa/uitest/autofilter/autofilter.py
index 1a9360a6031e..6a878f68e238 100644
--- a/sc/qa/uitest/autofilter/autofilter.py
+++ b/sc/qa/uitest/autofilter/autofilter.py
@@ -381,5 +381,36 @@ class AutofilterTest(UITestCase):
self.assertTrue(is_row_hidden(doc, 7))
self.assertFalse(is_row_hidden(doc, 8))
+ def test_tdf140968(self):
+ doc = self.ui_test.load_file(get_url_for_data_file("tdf140968.xlsx"))
+
+ xGridWin = self.xUITest.getTopFocusWindow().getChild("grid_window")
+
+ xGridWin.executeAction("LAUNCH", mkPropertyValues({"AUTOFILTER": "", "COL": "0", "ROW": "0"}))
+ xFloatWindow = self.xUITest.getFloatWindow()
+ xCheckListMenu = xFloatWindow.getChild("check_list_menu")
+ xTreeList = xCheckListMenu.getChild("check_list_box")
+ self.assertEqual(4, len(xTreeList.getChildren()))
+ self.assertEqual("0.000", get_state_as_dict(xTreeList.getChild('0'))['Text'])
+ self.assertEqual("0.046", get_state_as_dict(xTreeList.getChild('1'))['Text'])
+ self.assertEqual("0.365", get_state_as_dict(xTreeList.getChild('2'))['Text'])
+ self.assertEqual("0.516", get_state_as_dict(xTreeList.getChild('3'))['Text'])
+
+ xFirstEntry = xTreeList.getChild("0")
+ xFirstEntry.executeAction("CLICK", tuple())
+ xFirstEntry = xTreeList.getChild("1")
+ xFirstEntry.executeAction("CLICK", tuple())
+
+ xOkBtn = xFloatWindow.getChild("ok")
+ xOkBtn.executeAction("CLICK", tuple())
+
+ self.assertFalse(is_row_hidden(doc, 0))
+ self.assertTrue(is_row_hidden(doc, 1))
+ self.assertTrue(is_row_hidden(doc, 2))
+ self.assertTrue(is_row_hidden(doc, 3))
+ self.assertTrue(is_row_hidden(doc, 4))
+ self.assertFalse(is_row_hidden(doc, 5))
+ self.assertFalse(is_row_hidden(doc, 6))
+
self.ui_test.close_doc()
# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/sc/qa/uitest/data/autofilter/tdf140968.xlsx b/sc/qa/uitest/data/autofilter/tdf140968.xlsx
new file mode 100644
index 000000000000..b2b5b2a3e654
Binary files /dev/null and b/sc/qa/uitest/data/autofilter/tdf140968.xlsx differ
diff --git a/sc/source/core/data/column3.cxx b/sc/source/core/data/column3.cxx
index 651425dbe048..6c4e18ec0fcf 100644
--- a/sc/source/core/data/column3.cxx
+++ b/sc/source/core/data/column3.cxx
@@ -2481,8 +2481,10 @@ class FilterEntriesHandler
sal_uInt32 nIndex = pFormatter->GetFormatIndex(NF_DATETIME_ISO_YYYYMMDD_HHMMSS);
pFormatter->GetInputLineString(fVal, nIndex, aStr);
}
- // maybe extend ScTypedStrData enum is also an option here
- mrFilterEntries.push_back(ScTypedStrData(aStr, fVal, ScTypedStrData::Value,bDate));
+ /* use string compare later for formatted and filtered cell values
+ to avoid duplicates in the filter lists with setting the mbIsFormatted */
+ bool bFormFiltVal = mrColumn.HasFiltering() && nFormat;
+ mrFilterEntries.push_back(ScTypedStrData(aStr, fVal, ScTypedStrData::Value, bDate, bFormFiltVal));
}
public:
diff --git a/sc/source/core/tool/typedstrdata.cxx b/sc/source/core/tool/typedstrdata.cxx
index a114b280293c..0420b359ab76 100644
--- a/sc/source/core/tool/typedstrdata.cxx
+++ b/sc/source/core/tool/typedstrdata.cxx
@@ -47,14 +47,22 @@ bool ScTypedStrData::EqualCaseSensitive::operator() (const ScTypedStrData& left,
if (left.meStrType != right.meStrType)
return false;
- if (left.meStrType == Value && left.mfValue != right.mfValue)
+ if (left.meStrType == Value && left.mfValue != right.mfValue &&
+ !left.mbIsFormatted)
return false;
if (left.mbIsDate != right.mbIsDate )
return false;
- return ScGlobal::GetCaseCollator()->compareString(
- left.maStrValue, right.maStrValue) == 0;
+ if (ScGlobal::GetCaseCollator()->compareString(
+ left.maStrValue, right.maStrValue) == 0)
+ {
+ // hack: it's possible, because we only compare values of the same filter range
+ const_cast<bool&>(left.mbIsDuplicated) = true;
+ return true;
+ }
+ else
+ return false;
}
bool ScTypedStrData::EqualCaseInsensitive::operator() (const ScTypedStrData& left, const ScTypedStrData& right) const
@@ -62,14 +70,22 @@ bool ScTypedStrData::EqualCaseInsensitive::operator() (const ScTypedStrData& lef
if (left.meStrType != right.meStrType)
return false;
- if (left.meStrType == Value && left.mfValue != right.mfValue)
+ if (left.meStrType == Value && left.mfValue != right.mfValue &&
+ !left.mbIsFormatted)
return false;
if (left.mbIsDate != right.mbIsDate )
return false;
- return ScGlobal::GetCollator()->compareString(
- left.maStrValue, right.maStrValue) == 0;
+ if (ScGlobal::GetCollator()->compareString(
+ left.maStrValue, right.maStrValue) == 0)
+ {
+ // hack: it's possible, because we only compare values of the same filter range
+ const_cast<bool&>(left.mbIsDuplicated) = true;
+ return true;
+ }
+ else
+ return false;
}
bool ScTypedStrData::operator< (const ScTypedStrData& r) const
@@ -79,11 +95,13 @@ bool ScTypedStrData::operator< (const ScTypedStrData& r) const
}
ScTypedStrData::ScTypedStrData(
- const OUString& rStr, double nVal, StringType nType, bool bDate ) :
+ const OUString& rStr, double nVal, StringType nType, bool bDate, bool bFormatted, bool bDuplicated ) :
maStrValue(rStr),
mfValue(nVal),
meStrType(nType),
- mbIsDate( bDate ) {}
+ mbIsDate( bDate ),
+ mbIsFormatted( bFormatted ),
+ mbIsDuplicated( bDuplicated ) {}
FindTypedStrData::FindTypedStrData(const ScTypedStrData& rVal, bool bCaseSens) :
maVal(rVal), mbCaseSens(bCaseSens) {}
diff --git a/sc/source/filter/xml/XMLExportDatabaseRanges.cxx b/sc/source/filter/xml/XMLExportDatabaseRanges.cxx
index 7611a2598f79..8455275e11bb 100644
--- a/sc/source/filter/xml/XMLExportDatabaseRanges.cxx
+++ b/sc/source/filter/xml/XMLExportDatabaseRanges.cxx
@@ -417,11 +417,25 @@ private:
class WriteSetItem
{
ScXMLExport& mrExport;
+ const ScDocument* mpDoc;
public:
- explicit WriteSetItem(ScXMLExport& r) : mrExport(r) {}
+ explicit WriteSetItem(ScXMLExport& r, const ScDocument* pDoc) : mrExport(r), mpDoc(pDoc) {}
void operator() (const ScQueryEntry::Item& rItem) const
{
- mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_VALUE, rItem.maString.getString());
+ if (rItem.meType == ScQueryEntry::ByValue)
+ {
+ OUString aValStr;
+ SvNumberFormatter* pFormatter = mpDoc->GetFormatTable();
+ pFormatter->GetInputLineString(rItem.mfVal, 0, aValStr);
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_VALUE, aValStr);
+ }
+ else
+ {
+ // Indicating the formatted filter values, by export the XML_DATA_TYPE with XML_TEXT
+ if (rItem.meType == ScQueryEntry::ByString && rItem.mbFormattedValue)
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATA_TYPE, XML_TEXT);
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_VALUE, rItem.maString.getString());
+ }
SvXMLElementExport aElem(mrExport, XML_NAMESPACE_TABLE, XML_FILTER_SET_ITEM, true, true);
}
};
@@ -445,7 +459,12 @@ private:
// Single item condition.
const ScQueryEntry::Item& rItem = rItems.front();
if (rItem.meType == ScQueryEntry::ByString)
+ {
+ // Indicating the formatted filter values, by export the XML_DATA_TYPE with XML_TEXT
+ if (rItem.mbFormattedValue)
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATA_TYPE, XML_TEXT);
mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_VALUE, rItem.maString.getString());
+ }
else if (rItem.meType == ScQueryEntry::ByDate)
{
mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_VALUE, rItem.maString.getString());
@@ -468,11 +487,23 @@ private:
// Store the 1st value for backward compatibility.
const ScQueryEntry::Item& rItem = rItems.front();
- mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_VALUE, rItem.maString.getString());
+ if (rItem.meType == ScQueryEntry::ByValue)
+ {
+ OUString aValStr;
+ SvNumberFormatter* pFormatter = mpDoc->GetFormatTable();
+ pFormatter->GetInputLineString(rItem.mfVal, 0, aValStr);
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_VALUE, aValStr);
+ }
+ else
+ {
+ if (rItem.mbFormattedValue)
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATA_TYPE, XML_TEXT);
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_VALUE, rItem.maString.getString());
+ }
mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_OPERATOR, OUString("="));
SvXMLElementExport aElemC(mrExport, XML_NAMESPACE_TABLE, XML_FILTER_CONDITION, true, true);
- std::for_each(rItems.begin(), rItems.end(), WriteSetItem(mrExport));
+ std::for_each(rItems.begin(), rItems.end(), WriteSetItem(mrExport, mpDoc));
}
}
diff --git a/sc/source/filter/xml/xmlfilti.cxx b/sc/source/filter/xml/xmlfilti.cxx
index 9b811176faaf..11e16bdfe6c7 100644
--- a/sc/source/filter/xml/xmlfilti.cxx
+++ b/sc/source/filter/xml/xmlfilti.cxx
@@ -281,7 +281,7 @@ ScXMLConditionContext::ScXMLConditionContext(
ScXMLImportContext( rImport ),
mrQueryParam(rParam),
pFilterContext(pTempFilterContext),
- sDataType(GetXMLToken(XML_TEXT)),
+ sDataType(OUString()),
nField(0),
bIsCaseSensitive(false)
{
@@ -430,6 +430,8 @@ void SAL_CALL ScXMLConditionContext::endFastElement( sal_Int32 /*nElement*/ )
svl::SharedStringPool& rPool = GetScImport().GetDocument()->GetSharedStringPool();
rItem.maString = rPool.intern(sConditionValue);
rItem.meType = ScQueryEntry::ByString;
+ if (IsXMLToken(sDataType, XML_TEXT))
+ rItem.mbFormattedValue = true;
}
}
else
@@ -444,22 +446,31 @@ ScXMLSetItemContext::ScXMLSetItemContext(
if ( !rAttrList.is() )
return;
+ ScQueryEntry::Item aItem;
+ bool bAddSetItem = false;
+
for (auto &aIter : *rAttrList)
{
switch (aIter.getToken())
{
+ case XML_ELEMENT( TABLE, XML_DATA_TYPE ):
+ {
+ aItem.mbFormattedValue = IsXMLToken(aIter.toString(), XML_TEXT);
+ }
+ break;
case XML_ELEMENT( TABLE, XML_VALUE ):
{
svl::SharedStringPool& rPool = GetScImport().GetDocument()->GetSharedStringPool();
- ScQueryEntry::Item aItem;
aItem.maString = rPool.intern(aIter.toString());
aItem.meType = ScQueryEntry::ByString;
aItem.mfVal = 0.0;
- rParent.AddSetItem(aItem);
+ bAddSetItem = true;
}
break;
}
}
+ if (bAddSetItem)
+ rParent.AddSetItem(aItem);
}
ScXMLSetItemContext::~ScXMLSetItemContext()
@@ -649,7 +660,7 @@ ScXMLDPConditionContext::ScXMLDPConditionContext( ScXMLImport& rImport,
ScXMLDPFilterContext* pTempFilterContext) :
ScXMLImportContext( rImport ),
pFilterContext(pTempFilterContext),
- sDataType(GetXMLToken(XML_TEXT)),
+ sDataType(OUString()),
nField(0),
bIsCaseSensitive(false)
{
@@ -762,6 +773,8 @@ void SAL_CALL ScXMLDPConditionContext::endFastElement( sal_Int32 /*nElement*/ )
rItem.maString = rPool.intern(sConditionValue);
rItem.meType = ScQueryEntry::ByString;
rItem.mfVal = 0.0;
+ if (IsXMLToken(sDataType, XML_TEXT))
+ rItem.mbFormattedValue = true;
}
}
pFilterContext->AddFilterField(aFilterField);
diff --git a/sc/source/ui/cctrl/checklistmenu.cxx b/sc/source/ui/cctrl/checklistmenu.cxx
index a55547cbc12b..3da3de6be22c 100644
--- a/sc/source/ui/cctrl/checklistmenu.cxx
+++ b/sc/source/ui/cctrl/checklistmenu.cxx
@@ -408,6 +408,8 @@ ScCheckListMember::ScCheckListMember()
: mbVisible(true)
, mbDate(false)
, mbLeaf(false)
+ , mbValue(false)
+ , mbDuplicated(false)
, meDatePartType(YEAR)
{
}
@@ -975,12 +977,16 @@ void ScCheckListMenuControl::addDateMember(const OUString& rsName, double nVal,
mpChecks->thaw();
}
-void ScCheckListMenuControl::addMember(const OUString& rName, bool bVisible)
+void ScCheckListMenuControl::addMember(const OUString& rName, const double nVal, bool bVisible, bool bValue, bool bDuplicated)
{
ScCheckListMember aMember;
aMember.maName = rName;
+ aMember.maRealName = rName;
+ aMember.mnValue = nVal;
aMember.mbDate = false;
aMember.mbLeaf = true;
+ aMember.mbValue = bValue;
+ aMember.mbDuplicated = bDuplicated;
aMember.mbVisible = bVisible;
aMember.mxParent.reset();
maMembers.emplace_back(std::move(aMember));
@@ -1339,7 +1345,10 @@ void ScCheckListMenuControl::getResult(ResultType& rResult)
aResultEntry.aName = maMembers[i].maRealName;
else
aResultEntry.aName = maMembers[i].maName;
+ aResultEntry.nValue = maMembers[i].mnValue;
aResultEntry.bDate = maMembers[i].mbDate;
+ aResultEntry.bValue = maMembers[i].mbValue;
+ aResultEntry.bDuplicated = maMembers[i].mbDuplicated;
aResult.insert(aResultEntry);
}
}
diff --git a/sc/source/ui/inc/checklistmenu.hxx b/sc/source/ui/inc/checklistmenu.hxx
index eb5c418a3cad..2facc0ce03ee 100644
--- a/sc/source/ui/inc/checklistmenu.hxx
+++ b/sc/source/ui/inc/checklistmenu.hxx
@@ -38,9 +38,12 @@ struct ScCheckListMember
OUString maName; // node name
OUString maRealName;
+ double mnValue; // number value of filter condition
bool mbVisible;
bool mbDate;
bool mbLeaf;
+ bool mbValue; // true if the filter condition is value
+ bool mbDuplicated; // true if there were duplicated values in the filter list
DatePartType meDatePartType;
// To store Year and Month if the member if DAY type
std::vector<OUString> maDateParts;
@@ -69,8 +72,11 @@ public:
struct ResultEntry
{
OUString aName;
+ double nValue; // number value of filter condition
bool bValid;
bool bDate;
+ bool bValue; // true if the filter condition is value
+ bool bDuplicated; // true if there were duplicated values in the filter list
bool operator<(const ResultEntry& rhs) const
{
@@ -81,7 +87,10 @@ public:
{
return aName == rhs.aName &&
bValid == rhs.bValid &&
- bDate == rhs.bDate;
+ bDate == rhs.bDate &&
+ bValue == rhs.bValue &&
+ nValue == rhs.nValue &&
+ bDuplicated == rhs.bDuplicated;
}
};
typedef std::set<ResultEntry> ResultType;
@@ -129,7 +138,8 @@ public:
void setMemberSize(size_t n);
void addDateMember(const OUString& rName, double nVal, bool bVisible);
- void addMember(const OUString& rName, bool bVisible);
+ void addMember(const OUString& rName, const double nVal, bool bVisible,
+ bool bValue = false, bool bDuplicated = false);
size_t initMembers(int nMaxMemberWidth = -1);
void setConfig(const Config& rConfig);
diff --git a/sc/source/ui/unoobj/datauno.cxx b/sc/source/ui/unoobj/datauno.cxx
index 2f22b09d857c..3d8754caee3f 100644
--- a/sc/source/ui/unoobj/datauno.cxx
+++ b/sc/source/ui/unoobj/datauno.cxx
@@ -1135,9 +1135,14 @@ void fillQueryParam(
bool bNumber = pDoc->GetFormatTable()->IsNumberFormat(rVal.StringValue, nIndex, aItem.mfVal);
if (bNumber)
{
- OUString aStr;
- pDoc->GetFormatTable()->GetInputLineString(aItem.mfVal, nIndex, aStr);
- aItem.maString = rPool.intern(aStr);
+ if (aItem.mfVal != 0.0)
+ {
+ OUString aStr;
+ pDoc->GetFormatTable()->GetInputLineString(aItem.mfVal, nIndex, aStr);
+ aItem.maString = rPool.intern(aStr);
+ }
+ else
+ aItem.meType = ScQueryEntry::ByValue;
}
}
else if (aItem.meType == ScQueryEntry::ByValue)
diff --git a/sc/source/ui/view/gridwin.cxx b/sc/source/ui/view/gridwin.cxx
index 410fdbc8b4eb..53ed9727ab7a 100644
--- a/sc/source/ui/view/gridwin.cxx
+++ b/sc/source/ui/view/gridwin.cxx
@@ -560,8 +560,10 @@ public:
{
ScQueryEntry::Item aNew;
aNew.maString = mrPool.intern(rEntry.aName);
- aNew.meType = rEntry.bDate ? ScQueryEntry::ByDate : ScQueryEntry::ByString;
- aNew.mfVal = 0.0;
+ // set the filter type to ByValue, if the filter condition is value and not a duplicated value
+ aNew.meType = rEntry.bDate ? ScQueryEntry::ByDate : rEntry.bValue && !rEntry.bDuplicated ? ScQueryEntry::ByValue : ScQueryEntry::ByString;
+ aNew.mbFormattedValue = rEntry.bDuplicated;
+ aNew.mfVal = rEntry.nValue;
mrItems.push_back(aNew);
}
}
@@ -722,7 +724,7 @@ void ScGridWindow::LaunchAutoFilterMenu(SCCOL nCol, SCROW nRow)
if ( rEntry.IsDate() )
rControl.addDateMember( aStringVal, rEntry.GetValue(), bSelected );
else
- rControl.addMember(aStringVal, bSelected);
+ rControl.addMember( aStringVal, aDoubleVal, bSelected, rEntry.GetStringType() == ScTypedStrData::Value, rEntry.IsDuplicated() );
}
// Populate the menu.
diff --git a/sc/source/ui/view/gridwin2.cxx b/sc/source/ui/view/gridwin2.cxx
index efb952dc7721..9341fc110ffa 100644
--- a/sc/source/ui/view/gridwin2.cxx
+++ b/sc/source/ui/view/gridwin2.cxx
@@ -483,9 +483,9 @@ void ScGridWindow::DPLaunchFieldPopupMenu(const Point& rScrPos, const Size& rScr
OUString aName = rMem.getDisplayName();
if (aName.isEmpty())
// Use special string for an empty name.
- rControl.addMember(ScResId(STR_EMPTYDATA), rMem.mbVisible);
+ rControl.addMember(ScResId(STR_EMPTYDATA), 0.0, rMem.mbVisible);
else
- rControl.addMember(rMem.getDisplayName(), rMem.mbVisible);
+ rControl.addMember(rMem.getDisplayName(), 0.0, rMem.mbVisible);
}
}
More information about the Libreoffice-commits
mailing list