summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/xmloff/xmltoken.hxx2
-rw-r--r--offapi/com/sun/star/sheet/DataPilotFieldLayoutMode.idl12
-rw-r--r--sc/inc/attrib.hxx9
-rw-r--r--sc/inc/dpobject.hxx3
-rw-r--r--sc/inc/dpoutput.hxx13
-rw-r--r--sc/inc/dpsave.hxx5
-rw-r--r--sc/inc/dptabsrc.hxx4
-rw-r--r--sc/inc/fillinfo.hxx7
-rw-r--r--sc/inc/globstr.hrc2
-rw-r--r--sc/qa/unit/data/xlsx/pivot-table/pivotcompact.xlsxbin0 -> 13561 bytes
-rw-r--r--sc/qa/unit/pivottable_filters_test.cxx78
-rw-r--r--sc/source/core/data/attrib.cxx11
-rw-r--r--sc/source/core/data/dpobject.cxx30
-rw-r--r--sc/source/core/data/dpoutput.cxx205
-rw-r--r--sc/source/core/data/dpsave.cxx7
-rw-r--r--sc/source/core/data/fillinfo.cxx11
-rw-r--r--sc/source/filter/excel/xepivotxml.cxx47
-rw-r--r--sc/source/filter/inc/pivottablebuffer.hxx1
-rw-r--r--sc/source/filter/oox/pivottablebuffer.cxx24
-rw-r--r--sc/source/filter/xml/XMLExportDataPilot.cxx10
-rw-r--r--sc/source/filter/xml/xmldpimp.cxx42
-rw-r--r--sc/source/filter/xml/xmldpimp.hxx5
-rw-r--r--sc/source/ui/cctrl/checklistmenu.cxx58
-rw-r--r--sc/source/ui/cctrl/dpcontrol.cxx74
-rw-r--r--sc/source/ui/dbgui/PivotLayoutDialog.cxx4
-rw-r--r--sc/source/ui/dbgui/pvfundlg.cxx4
-rw-r--r--sc/source/ui/inc/PivotLayoutDialog.hxx1
-rw-r--r--sc/source/ui/inc/checklistmenu.hxx12
-rw-r--r--sc/source/ui/inc/dpcontrol.hxx8
-rw-r--r--sc/source/ui/inc/gridwin.hxx12
-rw-r--r--sc/source/ui/view/gridwin.cxx11
-rw-r--r--sc/source/ui/view/gridwin2.cxx275
-rw-r--r--sc/source/ui/view/gridwin4.cxx16
-rw-r--r--sc/uiconfig/scalc/ui/datafieldoptionsdialog.ui1
-rw-r--r--sc/uiconfig/scalc/ui/filterdropdown.ui30
-rw-r--r--sc/uiconfig/scalc/ui/pivottablelayoutdialog.ui19
-rw-r--r--schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng23
-rw-r--r--xmloff/source/core/xmltoken.cxx2
-rw-r--r--xmloff/source/token/tokens.txt2
39 files changed, 989 insertions, 91 deletions
diff --git a/include/xmloff/xmltoken.hxx b/include/xmloff/xmltoken.hxx
index 0fc0127813e7..0c63a31d0818 100644
--- a/include/xmloff/xmltoken.hxx
+++ b/include/xmloff/xmltoken.hxx
@@ -2572,6 +2572,7 @@ namespace xmloff::token {
XML_TABULAR_LAYOUT,
XML_OUTLINE_SUBTOTALS_TOP,
XML_OUTLINE_SUBTOTALS_BOTTOM,
+ XML_COMPACT_LAYOUT,
XML_LAYOUT_MODE,
XML_DATA_PILOT_LAYOUT_INFO,
@@ -2757,6 +2758,7 @@ namespace xmloff::token {
XML_SHOW_FILTER_BUTTON,
XML_DRILL_DOWN_ON_DOUBLE_CLICK,
+ XML_SHOW_DRILL_DOWN_BUTTONS,
XML_HEADER_GRID_LAYOUT,
XML_GROUPED_BY,
XML_DAYS,
diff --git a/offapi/com/sun/star/sheet/DataPilotFieldLayoutMode.idl b/offapi/com/sun/star/sheet/DataPilotFieldLayoutMode.idl
index 470b671a3028..4bbc233c674e 100644
--- a/offapi/com/sun/star/sheet/DataPilotFieldLayoutMode.idl
+++ b/offapi/com/sun/star/sheet/DataPilotFieldLayoutMode.idl
@@ -60,6 +60,18 @@ constants DataPilotFieldLayoutMode
const long OUTLINE_SUBTOTALS_BOTTOM = 2;
+ /** In compact layout mode, the items from the following field start in the row
+ below an item's name with an indentation but in the same column as this field's
+ items are.
+
+ <P>Subtotals are shown at the top (on the same row as the item's name). When
+ the subtotals take up more than one row (manually selected, or because there
+ are several data fields), they are always shown below the item's data,
+ regardless of the setting.</p>
+ */
+
+ const long COMPACT_LAYOUT = 3;
+
};
diff --git a/sc/inc/attrib.hxx b/sc/inc/attrib.hxx
index 92a0c3865c9c..6a27d2e840d2 100644
--- a/sc/inc/attrib.hxx
+++ b/sc/inc/attrib.hxx
@@ -41,10 +41,13 @@ enum class ScMF {
ButtonPopup = 0x0020, /// dp button with popup arrow
HiddenMember = 0x0040, /// dp field button with presence of hidden member
DpTable = 0x0080, /// dp table output
- All = 0x00FF
+ DpCollapse = 0x0100, /// dp compact layout collapse button
+ DpExpand = 0x0200, /// dp compact layout expand button
+ ButtonPopup2 = 0x0400, /// dp button with popup arrow for multiple fields
+ All = 0x07FF
};
namespace o3tl {
- template<> struct typed_flags<ScMF> : is_typed_flags<ScMF, 0xff> {};
+ template<> struct typed_flags<ScMF> : is_typed_flags<ScMF, 0x07ff> {};
}
class EditTextObject;
@@ -106,6 +109,8 @@ public:
bool HasPivotButton() const;
bool HasPivotPopupButton() const;
+ bool HasPivotToggle() const;
+ bool HasPivotMultiFieldPopupButton() const;
virtual void dumpAsXml(xmlTextWriterPtr pWriter) const override;
};
diff --git a/sc/inc/dpobject.hxx b/sc/inc/dpobject.hxx
index 68715b1b4f92..0e4cf882b529 100644
--- a/sc/inc/dpobject.hxx
+++ b/sc/inc/dpobject.hxx
@@ -206,6 +206,9 @@ public:
void FillLabelData(sal_Int32 nDim, ScDPLabelData& Labels);
void FillLabelData(ScPivotParam& rParam);
+ void GetFieldIdsNames(css::sheet::DataPilotFieldOrientation nOrient, std::vector<tools::Long>& rIndices,
+ std::vector<OUString>& rNames);
+
bool GetHierarchiesNA( sal_Int32 nDim, css::uno::Reference< css::container::XNameAccess >& xHiers );
void GetHierarchies( sal_Int32 nDim, css::uno::Sequence< OUString >& rHiers );
diff --git a/sc/inc/dpoutput.hxx b/sc/inc/dpoutput.hxx
index f9ee70ac6536..94decfa44466 100644
--- a/sc/inc/dpoutput.hxx
+++ b/sc/inc/dpoutput.hxx
@@ -60,9 +60,11 @@ private:
pColNumFmt;
std::unique_ptr<sal_uInt32[]>
pRowNumFmt;
+ std::vector<bool> aRowCompactFlags;
sal_Int32 nColFmtCount;
sal_Int32 nRowFmtCount;
sal_uInt32 nSingleNumFmt;
+ size_t nRowDims; // Including empty ones.
// Output geometry related parameters
sal_Int32 nColCount;
@@ -81,6 +83,8 @@ private:
bool bSizesValid:1;
bool bSizeOverflow:1;
bool mbHeaderLayout:1; // true : grid, false : standard
+ bool mbHasCompactRowField:1; // true: at least one of the row fields has compact layout.
+ bool mbExpandCollapse:1; // true: show expand/collapse buttons
void DataCell( SCCOL nCol, SCROW nRow, SCTAB nTab,
const css::sheet::DataResult& rData );
@@ -89,18 +93,25 @@ private:
bool bColHeader, tools::Long nLevel );
void FieldCell(SCCOL nCol, SCROW nRow, SCTAB nTab, const ScDPOutLevelData& rData, bool bInTable);
+ void MultiFieldCell(SCCOL nCol, SCROW nRow, SCTAB nTab, bool bRowField);
+ /// Computes number of columns needed to write row fields.
+ SCCOL GetColumnsForRowFields() const;
void CalcSizes();
/** Query which sub-area of the table the cell is in. See
css.sheet.DataPilotTablePositionType for the interpretation of the
return value. */
sal_Int32 GetPositionType(const ScAddress& rPos);
+ /// Returns the range of row fields that are contained by table's row fields column nCol.
+ void GetRowFieldRange(SCCOL nCol, sal_Int32& nRowFieldStart, sal_Int32& nRowFieldEnd) const;
+ /// Find row field index from row position in case of compact layout.
+ sal_Int32 GetRowFieldCompact(SCCOL nColQuery, SCROW nRowQuery) const;
public:
ScDPOutput( ScDocument* pD,
css::uno::Reference< css::sheet::XDimensionsSupplier> xSrc,
- const ScAddress& rPos, bool bFilter );
+ const ScAddress& rPos, bool bFilter, bool bExpandCollapse );
~ScDPOutput();
void SetPosition( const ScAddress& rPos );
diff --git a/sc/inc/dpsave.hxx b/sc/inc/dpsave.hxx
index 78d31f96643b..f5d50db97dbb 100644
--- a/sc/inc/dpsave.hxx
+++ b/sc/inc/dpsave.hxx
@@ -247,6 +247,7 @@ private:
sal_uInt16 nRepeatEmptyMode;
bool bFilterButton; // not passed to DataPilotSource
bool bDrillDown; // not passed to DataPilotSource
+ bool bExpandCollapse; // not passed to DataPilotSource
/** if true, all dimensions already have all of their member instances
* created. */
@@ -342,6 +343,10 @@ public:
bool GetDrillDown() const
{ return bDrillDown; }
+ SC_DLLPUBLIC void SetExpandCollapse( bool bSet );
+ bool GetExpandCollapse() const
+ { return bExpandCollapse; }
+
void WriteToSource( const css::uno::Reference<css::sheet::XDimensionsSupplier>& xSource );
bool IsEmpty() const;
diff --git a/sc/inc/dptabsrc.hxx b/sc/inc/dptabsrc.hxx
index 4f7f1db572da..4476966f212a 100644
--- a/sc/inc/dptabsrc.hxx
+++ b/sc/inc/dptabsrc.hxx
@@ -528,8 +528,10 @@ public:
bool IsSubtotalsAtTop() const
{
return bEnableLayout &&
+ (aLayoutInfo.LayoutMode ==
+ css::sheet::DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_TOP ||
aLayoutInfo.LayoutMode ==
- css::sheet::DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_TOP;
+ css::sheet::DataPilotFieldLayoutMode::COMPACT_LAYOUT);
}
bool IsAddEmpty() const
diff --git a/sc/inc/fillinfo.hxx b/sc/inc/fillinfo.hxx
index 1a6ebaee53d2..81086ed358ba 100644
--- a/sc/inc/fillinfo.hxx
+++ b/sc/inc/fillinfo.hxx
@@ -139,6 +139,9 @@ struct ScCellInfo
, bFilterActive(false)
, bPrinted(false) // view-internal
, bHideGrid(false) // view-internal
+ , bPivotCollapseButton(false)
+ , bPivotExpandButton(false)
+ , bPivotPopupButtonMulti(false)
{
}
@@ -178,6 +181,9 @@ struct ScCellInfo
bool bFilterActive:1;
bool bPrinted : 1; // when required (pagebreak mode)
bool bHideGrid : 1; // output-internal
+ bool bPivotCollapseButton : 1; // dp compact layout collapse button
+ bool bPivotExpandButton : 1; // dp compact layout expand button
+ bool bPivotPopupButtonMulti : 1; // dp button with popup arrow for multiple fields
};
const SCCOL SC_ROTMAX_NONE = SCCOL_MAX;
@@ -237,6 +243,7 @@ struct RowInfo
bool bAutoFilter:1;
bool bPivotButton:1;
bool bChanged:1; // TRUE, if not tested
+ bool bPivotToggle:1;
private:
// This class allocates ScCellInfo with also one item extra before and after.
diff --git a/sc/inc/globstr.hrc b/sc/inc/globstr.hrc
index 0aa8d010f9df..01e1ab47219b 100644
--- a/sc/inc/globstr.hrc
+++ b/sc/inc/globstr.hrc
@@ -155,6 +155,8 @@
#define STR_PIVOT_TOTAL NC_("STR_PIVOT_TOTAL", "Total")
#define STR_PIVOT_DATA NC_("STR_PIVOT_DATA", "Data")
#define STR_PIVOT_GROUP NC_("STR_PIVOT_GROUP", "Group")
+#define STR_PIVOT_ROW_LABELS NC_("STR_PIVOT_ROW_LABELS", "Row Labels")
+#define STR_PIVOT_COL_LABELS NC_("STR_PIVOT_COL_LABELS", "Column Labels")
/* To translators: $1 == will be replaced by STR_SELCOUNT_ROWARG, and $2 by STR_SELCOUNT_COLARG
e.g. Selected: 1 row, 2 columns */
#define STR_SELCOUNT NC_("STR_SELCOUNT", "Selected: $1, $2")
diff --git a/sc/qa/unit/data/xlsx/pivot-table/pivotcompact.xlsx b/sc/qa/unit/data/xlsx/pivot-table/pivotcompact.xlsx
new file mode 100644
index 000000000000..673ba7e0b60a
--- /dev/null
+++ b/sc/qa/unit/data/xlsx/pivot-table/pivotcompact.xlsx
Binary files differ
diff --git a/sc/qa/unit/pivottable_filters_test.cxx b/sc/qa/unit/pivottable_filters_test.cxx
index 372d81b83ec0..c89380f1aebf 100644
--- a/sc/qa/unit/pivottable_filters_test.cxx
+++ b/sc/qa/unit/pivottable_filters_test.cxx
@@ -405,26 +405,22 @@ CPPUNIT_TEST_FIXTURE(ScPivotTableFiltersTest, testPivotTableSharedNestedDateGrou
auto testThis = [](ScDocument& rDoc) {
// Check whether right date groups are imported for both tables
// First table
- CPPUNIT_ASSERT_EQUAL(OUString("Years"), rDoc.GetString(ScAddress(0, 3, 1)));
+ // Years, Quarters, 'a' have compact layout so the only header contains a multi-field filter.
CPPUNIT_ASSERT_EQUAL(OUString("1965"), rDoc.GetString(ScAddress(0, 4, 1)));
CPPUNIT_ASSERT_EQUAL(OUString("1989"), rDoc.GetString(ScAddress(0, 11, 1)));
CPPUNIT_ASSERT_EQUAL(OUString("2000"), rDoc.GetString(ScAddress(0, 18, 1)));
CPPUNIT_ASSERT_EQUAL(OUString("2004"), rDoc.GetString(ScAddress(0, 21, 1)));
// TODO: check why this fails with the empty string
//CPPUNIT_ASSERT_EQUAL(OUString("2007"), rDoc.GetString(ScAddress(0,32,1)));
- CPPUNIT_ASSERT_EQUAL(OUString("Quarters"), rDoc.GetString(ScAddress(1, 3, 1)));
- CPPUNIT_ASSERT_EQUAL(OUString("a"), rDoc.GetString(ScAddress(2, 3, 1)));
// Second table
- CPPUNIT_ASSERT_EQUAL(OUString("Years"), rDoc.GetString(ScAddress(6, 3, 1)));
+ // Years, Quarters, 'a' have compact layout so the only row header contains a multi-field filter.
CPPUNIT_ASSERT_EQUAL(OUString("1965"), rDoc.GetString(ScAddress(6, 4, 1)));
CPPUNIT_ASSERT_EQUAL(OUString("1989"), rDoc.GetString(ScAddress(6, 11, 1)));
CPPUNIT_ASSERT_EQUAL(OUString("2000"), rDoc.GetString(ScAddress(6, 18, 1)));
CPPUNIT_ASSERT_EQUAL(OUString("2004"), rDoc.GetString(ScAddress(6, 21, 1)));
// TODO: check why this fails with the empty string
//CPPUNIT_ASSERT_EQUAL(OUString("2007"), rDoc.GetString(ScAddress(6,31,1)));
- CPPUNIT_ASSERT_EQUAL(OUString("Quarters"), rDoc.GetString(ScAddress(7, 3, 1)));
- CPPUNIT_ASSERT_EQUAL(OUString("a"), rDoc.GetString(ScAddress(8, 3, 1)));
// There should be exactly 2 pivot tables and 1 cache.
ScDPCollection* pDPs = rDoc.GetDPCollection();
@@ -2520,6 +2516,76 @@ CPPUNIT_TEST_FIXTURE(ScPivotTableFiltersTest, testTdf73845)
}
}
+CPPUNIT_TEST_FIXTURE(ScPivotTableFiltersTest, testPivotTableCompactLayoutXLSX)
+{
+ auto testThis = [](ScDocument& rDoc) {
+ ScDPCollection* pDPs = rDoc.GetDPCollection();
+ CPPUNIT_ASSERT_MESSAGE("Failed to get a live ScDPCollection instance.", pDPs);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be exactly one pivot table instance.", size_t(1),
+ pDPs->GetCount());
+
+ const ScDPObject* pDPObj = &(*pDPs)[0];
+ CPPUNIT_ASSERT_MESSAGE("Failed to get a pivot table object.", pDPObj);
+ const ScDPSaveData* pSaveData = pDPObj->GetSaveData();
+ CPPUNIT_ASSERT_MESSAGE("The show expand/collapse buttons option must be true",
+ pSaveData->GetExpandCollapse());
+
+ CPPUNIT_ASSERT_EQUAL(OUString("Row Labels"), rDoc.GetString(ScAddress(10, 1, 0)));
+
+ // Check some row fields
+ struct RowFieldLabel
+ {
+ OUString aContent;
+ ScAddress aAddr;
+ bool bIndented;
+ };
+
+ constexpr int nCases = 6;
+ const RowFieldLabel aCases[nCases] = {
+ { "aaa", ScAddress(10, 2, 0), true },
+
+ { "bbb", ScAddress(10, 3, 0), true },
+
+ { "ccc", ScAddress(10, 4, 0), true },
+
+ { "aax", ScAddress(10, 10, 0), true },
+
+ { "bbx", ScAddress(10, 14, 0), true },
+
+ { "ccc", ScAddress(10, 15, 0), true },
+ };
+
+ for (int nCaseNum = 0; nCaseNum < nCases; ++nCaseNum)
+ {
+ auto& rCase = aCases[nCaseNum];
+ CPPUNIT_ASSERT_EQUAL(rCase.aContent, rDoc.GetString(rCase.aAddr));
+ const ScIndentItem* pIndent = rDoc.GetAttr(rCase.aAddr, ATTR_INDENT);
+ if (rCase.bIndented)
+ {
+ CPPUNIT_ASSERT(pIndent);
+ CPPUNIT_ASSERT(pIndent->GetValue() > 0);
+ }
+ else
+ {
+ CPPUNIT_ASSERT(!pIndent || pIndent->GetValue() == 0);
+ }
+ }
+
+ // check col fields
+ CPPUNIT_ASSERT_EQUAL(OUString("ddd"), rDoc.GetString(ScAddress(11, 1, 0)));
+ CPPUNIT_ASSERT_EQUAL(OUString("ddx"), rDoc.GetString(ScAddress(12, 1, 0)));
+ };
+
+ createScDoc("xlsx/pivot-table/pivotcompact.xlsx");
+ testThis(*getScDoc());
+
+ saveAndReload("calc8");
+ testThis(*getScDoc());
+
+ saveAndReload("Calc Office Open XML");
+ testThis(*getScDoc());
+}
+
CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/attrib.cxx b/sc/source/core/data/attrib.cxx
index 7173bcb61217..efe494c316a5 100644
--- a/sc/source/core/data/attrib.cxx
+++ b/sc/source/core/data/attrib.cxx
@@ -155,6 +155,17 @@ bool ScMergeFlagAttr::HasPivotPopupButton() const
return bool(GetValue() & ScMF::ButtonPopup);
}
+bool ScMergeFlagAttr::HasPivotToggle() const
+{
+ auto nFlags = GetValue();
+ return (nFlags & ScMF::DpCollapse) || (nFlags & ScMF::DpExpand);
+}
+
+bool ScMergeFlagAttr::HasPivotMultiFieldPopupButton() const
+{
+ return bool(GetValue() & ScMF::ButtonPopup2);
+}
+
void ScMergeFlagAttr::dumpAsXml(xmlTextWriterPtr pWriter) const
{
(void)xmlTextWriterStartElement(pWriter, BAD_CAST("ScMergeFlagAttr"));
diff --git a/sc/source/core/data/dpobject.cxx b/sc/source/core/data/dpobject.cxx
index 66e336a9d6d1..f3c989ee8293 100644
--- a/sc/source/core/data/dpobject.cxx
+++ b/sc/source/core/data/dpobject.cxx
@@ -526,7 +526,7 @@ void ScDPObject::CreateOutput()
return;
bool bFilterButton = IsSheetData() && pSaveData && pSaveData->GetFilterButton();
- pOutput.reset( new ScDPOutput( pDoc, xSource, aOutRange.aStart, bFilterButton ) );
+ pOutput.reset( new ScDPOutput( pDoc, xSource, aOutRange.aStart, bFilterButton, pSaveData ? pSaveData->GetExpandCollapse() : false ) );
pOutput->SetHeaderLayout ( mbHeaderLayout );
sal_Int32 nOldRows = nHeaderRows;
@@ -2482,6 +2482,34 @@ void ScDPObject::FillLabelData(ScPivotParam& rParam)
}
}
+void ScDPObject::GetFieldIdsNames(sheet::DataPilotFieldOrientation nOrient, std::vector<tools::Long>& rIndices,
+ std::vector<OUString>& rNames)
+{
+ CreateObjects();
+ if (!xSource.is())
+ return;
+
+ uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
+ uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xDimsName );
+ tools::Long nDimCount = xDims->getCount();
+ for (tools::Long nDim = 0; nDim < nDimCount; ++nDim)
+ {
+ uno::Reference<uno::XInterface> xIntDim(xDims->getByIndex(nDim), uno::UNO_QUERY);
+ uno::Reference<container::XNamed> xDimName(xIntDim, uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xDimProp(xIntDim, uno::UNO_QUERY);
+
+ sheet::DataPilotFieldOrientation nDimOrient = ScUnoHelpFunctions::GetEnumProperty(
+ xDimProp, SC_UNO_DP_ORIENTATION,
+ sheet::DataPilotFieldOrientation_HIDDEN );
+
+ if ( xDimProp.is() && nDimOrient == nOrient)
+ {
+ rIndices.push_back(nDim);
+ rNames.push_back(xDimName->getName());
+ }
+ }
+}
+
bool ScDPObject::GetHierarchiesNA( sal_Int32 nDim, uno::Reference< container::XNameAccess >& xHiers )
{
bool bRet = false;
diff --git a/sc/source/core/data/dpoutput.cxx b/sc/source/core/data/dpoutput.cxx
index ce2ccb3a05dd..301e2c83ba4f 100644
--- a/sc/source/core/data/dpoutput.cxx
+++ b/sc/source/core/data/dpoutput.cxx
@@ -59,11 +59,14 @@
#include <com/sun/star/sheet/XLevelsSupplier.hpp>
#include <com/sun/star/sheet/XMembersAccess.hpp>
#include <com/sun/star/sheet/XMembersSupplier.hpp>
+#include <com/sun/star/sheet/DataPilotFieldLayoutInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldLayoutMode.hpp>
#include <limits>
#include <string_view>
#include <utility>
#include <vector>
+#include <iostream>
using namespace com::sun::star;
using ::std::vector;
@@ -504,13 +507,14 @@ uno::Sequence<sheet::MemberResult> getVisiblePageMembersAsResults( const uno::Re
}
ScDPOutput::ScDPOutput( ScDocument* pD, uno::Reference<sheet::XDimensionsSupplier> xSrc,
- const ScAddress& rPos, bool bFilter ) :
+ const ScAddress& rPos, bool bFilter, bool bExpandCollapse ) :
pDoc( pD ),
xSource(std::move( xSrc )),
aStartPos( rPos ),
nColFmtCount( 0 ),
nRowFmtCount( 0 ),
nSingleNumFmt( 0 ),
+ nRowDims( 0 ),
nColCount(0),
nRowCount(0),
nHeaderSize(0),
@@ -518,7 +522,9 @@ ScDPOutput::ScDPOutput( ScDocument* pD, uno::Reference<sheet::XDimensionsSupplie
bResultsError(false),
bSizesValid(false),
bSizeOverflow(false),
- mbHeaderLayout(false)
+ mbHeaderLayout(false),
+ mbHasCompactRowField(false),
+ mbExpandCollapse(bExpandCollapse)
{
nTabStartCol = nMemberStartCol = nDataStartCol = nTabEndCol = 0;
nTabStartRow = nMemberStartRow = nDataStartRow = nTabEndRow = 0;
@@ -601,12 +607,26 @@ ScDPOutput::ScDPOutput( ScDocument* pD, uno::Reference<sheet::XDimensionsSupplie
case sheet::DataPilotFieldOrientation_ROW:
{
uno::Sequence<sheet::MemberResult> aResult = xLevRes->getResults();
+ ++nRowDims;
if (!lcl_MemberEmpty(aResult))
{
+ bool bFieldCompact = false;
+ try
+ {
+ sheet::DataPilotFieldLayoutInfo aLayoutInfo;
+ xPropSet->getPropertyValue( SC_UNO_DP_LAYOUT ) >>= aLayoutInfo;
+ bFieldCompact = (aLayoutInfo.LayoutMode == sheet::DataPilotFieldLayoutMode::COMPACT_LAYOUT);
+ }
+ catch (uno::Exception&)
+ {
+ }
ScDPOutLevelData tmp(nDim, nHierarchy, nLev, nDimPos, nNumFmt, aResult, aName,
aCaption, bHasHiddenMember, bIsDataLayout, false);
pRowFields.push_back(tmp);
+ aRowCompactFlags.push_back(bFieldCompact);
+ mbHasCompactRowField |= bFieldCompact;
}
+
}
break;
case sheet::DataPilotFieldOrientation_PAGE:
@@ -792,6 +812,26 @@ void ScDPOutput::HeaderCell( SCCOL nCol, SCROW nRow, SCTAB nTab,
}
}
+void ScDPOutput::MultiFieldCell(SCCOL nCol, SCROW nRow, SCTAB nTab, bool bRowField)
+{
+ pDoc->SetString(nCol, nRow, nTab, ScResId(bRowField ? STR_PIVOT_ROW_LABELS : STR_PIVOT_COL_LABELS));
+
+ ScMF nMergeFlag = ScMF::Button;
+ for (auto& rData : pRowFields)
+ {
+ if (rData.mbHasHiddenMember)
+ {
+ nMergeFlag |= ScMF::HiddenMember;
+ break;
+ }
+ }
+
+ nMergeFlag |= ScMF::ButtonPopup2;
+
+ pDoc->ApplyFlagsTab(nCol, nRow, nCol, nRow, nTab, nMergeFlag);
+ lcl_SetStyleById( pDoc, nTab, nCol, nRow, nCol, nRow, STR_PIVOT_STYLENAME_FIELDNAME );
+}
+
void ScDPOutput::FieldCell(
SCCOL nCol, SCROW nRow, SCTAB nTab, const ScDPOutLevelData& rData, bool bInTable)
{
@@ -833,6 +873,22 @@ static void lcl_DoFilterButton( ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB
pDoc->ApplyFlagsTab(nCol, nRow, nCol, nRow, nTab, ScMF::Button);
}
+SCCOL ScDPOutput::GetColumnsForRowFields() const
+{
+ if (!mbHasCompactRowField)
+ return static_cast<SCCOL>(pRowFields.size());
+
+ SCCOL nNum = 0;
+ for (const auto bCompact: aRowCompactFlags)
+ if (!bCompact)
+ ++nNum;
+
+ if (aRowCompactFlags.back())
+ ++nNum;
+
+ return nNum;
+}
+
void ScDPOutput::CalcSizes()
{
if (bSizesValid)
@@ -870,7 +926,7 @@ void ScDPOutput::CalcSizes()
nTabStartRow = aStartPos.Row() + static_cast<SCROW>(nPageSize); // below page fields
nMemberStartCol = nTabStartCol;
nMemberStartRow = nTabStartRow + static_cast<SCROW>(nHeaderSize);
- nDataStartCol = nMemberStartCol + static_cast<SCCOL>(pRowFields.size());
+ nDataStartCol = nMemberStartCol + GetColumnsForRowFields();
nDataStartRow = nMemberStartRow + static_cast<SCROW>(pColFields.size());
if ( nColCount > 0 )
nTabEndCol = nDataStartCol + static_cast<SCCOL>(nColCount) - 1;
@@ -999,10 +1055,14 @@ void ScDPOutput::Output()
ScDPOutputImpl outputimp( pDoc, nTab,
nTabStartCol, nTabStartRow,
nDataStartCol, nDataStartRow, nTabEndCol, nTabEndRow );
- for (size_t nField=0; nField<pColFields.size(); nField++)
+ size_t nNumColFields = pColFields.size();
+ for (size_t nField=0; nField<nNumColFields; nField++)
{
SCCOL nHdrCol = nDataStartCol + static_cast<SCCOL>(nField); //TODO: check for overflow
- FieldCell(nHdrCol, nTabStartRow, nTab, pColFields[nField], true);
+ if (!mbHasCompactRowField || nNumColFields == 1)
+ FieldCell(nHdrCol, nTabStartRow, nTab, pColFields[nField], true);
+ else if (!nField)
+ MultiFieldCell(nHdrCol, nTabStartRow, nTab, false /* bRowField */);
SCROW nRowPos = nMemberStartRow + static_cast<SCROW>(nField); //TODO: check for overflow
const uno::Sequence<sheet::MemberResult> rSequence = pColFields[nField].maResult;
@@ -1049,23 +1109,32 @@ void ScDPOutput::Output()
// output row headers:
std::vector<bool> vbSetBorder;
vbSetBorder.resize( nTabEndRow - nDataStartRow + 1, false );
- for (size_t nField=0; nField<pRowFields.size(); nField++)
+ size_t nFieldColOffset = 0;
+ size_t nFieldIndentLevel = 0; // To calulate indent level for fields packed in a column.
+ size_t nNumRowFields = pRowFields.size();
+ for (size_t nField=0; nField<nNumRowFields; nField++)
{
+ const bool bCompactField = aRowCompactFlags[nField];
SCCOL nHdrCol = nTabStartCol + static_cast<SCCOL>(nField); //TODO: check for overflow
SCROW nHdrRow = nDataStartRow - 1;
- FieldCell(nHdrCol, nHdrRow, nTab, pRowFields[nField], true);
+ if (!mbHasCompactRowField || nNumRowFields == 1)
+ FieldCell(nHdrCol, nHdrRow, nTab, pRowFields[nField], true);
+ else if (!nField)
+ MultiFieldCell(nHdrCol, nHdrRow, nTab, true /* bRowField */);
- SCCOL nColPos = nMemberStartCol + static_cast<SCCOL>(nField); //TODO: check for overflow
+ SCCOL nColPos = nMemberStartCol + static_cast<SCCOL>(nFieldColOffset); //TODO: check for overflow
const uno::Sequence<sheet::MemberResult> rSequence = pRowFields[nField].maResult;
const sheet::MemberResult* pArray = rSequence.getConstArray();
sal_Int32 nThisRowCount = rSequence.getLength();
OSL_ENSURE( nThisRowCount == nRowCount, "count mismatch" ); //TODO: ???
for (sal_Int32 nRow=0; nRow<nThisRowCount; nRow++)
{
+ const sheet::MemberResult& rData = pArray[nRow];
+ const bool bHasMember = (rData.Flags & sheet::MemberResultFlags::HASMEMBER);
+ const bool bSubtotal = (rData.Flags & sheet::MemberResultFlags::SUBTOTAL);
SCROW nRowPos = nDataStartRow + static_cast<SCROW>(nRow); //TODO: check for overflow
- HeaderCell( nColPos, nRowPos, nTab, pArray[nRow], false, nField );
- if ( ( pArray[nRow].Flags & sheet::MemberResultFlags::HASMEMBER ) &&
- !( pArray[nRow].Flags & sheet::MemberResultFlags::SUBTOTAL ) )
+ HeaderCell( nColPos, nRowPos, nTab, rData, false, nFieldColOffset );
+ if (bHasMember && !bSubtotal)
{
if ( nField+1 < pRowFields.size() )
{
@@ -1084,17 +1153,46 @@ void ScDPOutput::Output()
if ( nField == pRowFields.size() - 2 )
outputimp.OutputBlockFrame( nColPos+1, nRowPos, nColPos+1, nEndRowPos );
- lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nDataStartCol-1,nEndRowPos, STR_PIVOT_STYLENAME_CATEGORY );
+ lcl_SetStyleById( pDoc, nTab, nColPos, nRowPos, nDataStartCol-1,nEndRowPos, STR_PIVOT_STYLENAME_CATEGORY );
}
else
- lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nDataStartCol-1,nRowPos, STR_PIVOT_STYLENAME_CATEGORY );
+ {
+ lcl_SetStyleById( pDoc, nTab, nColPos, nRowPos, nDataStartCol-1,nRowPos, STR_PIVOT_STYLENAME_CATEGORY );
+ }
+
+ // Set flags for collapse/expand buttons and indent field header text
+ {
+ bool bLast = nRowDims == (nField + 1);
+ size_t nMinIndentLevel = mbExpandCollapse ? 1 : 0;
+ tools::Long nIndent = o3tl::convert(13 * (bLast ? nFieldIndentLevel : nMinIndentLevel + nFieldIndentLevel), o3tl::Length::px, o3tl::Length::twip);
+ bool bHasContinue = (!bLast && nRow + 1 < nThisRowCount
+ && (pArray[nRow + 1].Flags & sheet::MemberResultFlags::CONTINUE));
+ if (nIndent)
+ pDoc->ApplyAttr(nColPos, nRowPos, nTab, ScIndentItem(nIndent));
+ if (mbExpandCollapse && !bLast)
+ {
+ pDoc->ApplyFlagsTab(nColPos, nRowPos, nColPos, nRowPos, nTab,
+ bHasContinue ? ScMF::DpCollapse : ScMF::DpExpand);
+ }
+ }
}
- else if ( pArray[nRow].Flags & sheet::MemberResultFlags::SUBTOTAL )
+ else if ( bSubtotal )
outputimp.AddRow( nRowPos );
// Apply the same number format as in data source.
pDoc->ApplyAttr(nColPos, nRowPos, nTab, SfxUInt32Item(ATTR_VALUE_FORMAT, pRowFields[nField].mnSrcNumFmt));
}
+
+ if (!bCompactField)
+ {
+ // Next field should be placed in next column only if current field has a non-compact layout.
+ ++nFieldColOffset;
+ nFieldIndentLevel = 0; // Reset indent level.
+ }
+ else
+ {
+ ++nFieldIndentLevel;
+ }
}
if (nColCount == 1 && nRowCount > 0 && pColFields.empty())
@@ -1284,6 +1382,83 @@ void lcl_GetTableVars( sal_Int32& rGrandTotalCols, sal_Int32& rGrandTotalRows, s
}
+void ScDPOutput::GetRowFieldRange(SCCOL nCol, sal_Int32& nRowFieldStart, sal_Int32& nRowFieldEnd) const
+{
+ if (!mbHasCompactRowField)
+ {
+ nRowFieldStart = nCol;
+ nRowFieldEnd = nCol + 1;
+ return;
+ }
+
+ if (nCol >= static_cast<SCCOL>(aRowCompactFlags.size()))
+ {
+ nRowFieldStart = nRowFieldEnd = 0;
+ return;
+ }
+
+ nRowFieldStart = -1;
+ nRowFieldEnd = -1;
+ SCCOL nCurCol = 0;
+ sal_Int32 nField = 0;
+
+ for (const auto bCompact: aRowCompactFlags)
+ {
+ if (nCurCol == nCol && nRowFieldStart == -1)
+ nRowFieldStart = nField;
+
+ if (!bCompact)
+ ++nCurCol;
+
+ ++nField;
+
+ if (nCurCol == (nCol + 1) && nRowFieldStart != -1 && nRowFieldEnd == -1)
+ {
+ nRowFieldEnd = nField;
+ break;
+ }
+ }
+
+ if (nRowFieldStart != -1 && nRowFieldEnd == -1 && nCurCol == nCol)
+ nRowFieldEnd = static_cast<sal_Int32>(aRowCompactFlags.size());
+
+ if (nRowFieldStart == -1 || nRowFieldEnd == -1)
+ {
+ SAL_WARN("sc.core", "ScDPOutput::GetRowFieldRange : unable to find field range for nCol = " << nCol);
+ nRowFieldStart = nRowFieldEnd = 0;
+ }
+}
+
+sal_Int32 ScDPOutput::GetRowFieldCompact(SCCOL nColQuery, SCROW nRowQuery) const
+{
+ if (!mbHasCompactRowField)
+ return nColQuery - nTabStartCol;
+
+ SCCOL nCol = nColQuery - nTabStartCol;
+ sal_Int32 nStartField = 0;
+ sal_Int32 nEndField = 0;
+ GetRowFieldRange(nCol, nStartField, nEndField);
+
+ for (sal_Int32 nField = nEndField - 1; nField >= nStartField; --nField)
+ {
+ const uno::Sequence<sheet::MemberResult> rSequence = pRowFields[nField].maResult;
+ const sheet::MemberResult* pArray = rSequence.getConstArray();
+ sal_Int32 nThisRowCount = rSequence.getLength();
+ SCROW nRow = nRowQuery - nDataStartRow;
+ if (nRow >= 0 && nRow < nThisRowCount)
+ {
+ const sheet::MemberResult& rData = pArray[nRow];
+ if ((rData.Flags & sheet::MemberResultFlags::HASMEMBER)
+ && !(rData.Flags & sheet::MemberResultFlags::SUBTOTAL))
+ {
+ return nField;
+ }
+ }
+ }
+
+ return -1;
+}
+
void ScDPOutput::GetPositionData(const ScAddress& rPos, DataPilotTablePositionData& rPosData)
{
using namespace ::com::sun::star::sheet;
@@ -1359,7 +1534,7 @@ void ScDPOutput::GetPositionData(const ScAddress& rPos, DataPilotTablePositionDa
}
case DataPilotTablePositionType::ROW_HEADER:
{
- tools::Long nField = nCol - nTabStartCol;
+ tools::Long nField = GetRowFieldCompact(nCol, nRow);
if (nField < 0)
break;
diff --git a/sc/source/core/data/dpsave.cxx b/sc/source/core/data/dpsave.cxx
index 9088e51999f8..78067c042acd 100644
--- a/sc/source/core/data/dpsave.cxx
+++ b/sc/source/core/data/dpsave.cxx
@@ -696,6 +696,7 @@ ScDPSaveData::ScDPSaveData() :
nRepeatEmptyMode( SC_DPSAVEMODE_DONTKNOW ),
bFilterButton( true ),
bDrillDown( true ),
+ bExpandCollapse( false ),
mbDimensionMembersBuilt(false)
{
}
@@ -707,6 +708,7 @@ ScDPSaveData::ScDPSaveData(const ScDPSaveData& r) :
nRepeatEmptyMode( r.nRepeatEmptyMode ),
bFilterButton( r.bFilterButton ),
bDrillDown( r.bDrillDown ),
+ bExpandCollapse( r.bExpandCollapse ),
mbDimensionMembersBuilt(r.mbDimensionMembersBuilt),
mpGrandTotalName(r.mpGrandTotalName)
{
@@ -1012,6 +1014,11 @@ void ScDPSaveData::SetDrillDown(bool bSet)
bDrillDown = bSet;
}
+void ScDPSaveData::SetExpandCollapse(bool bSet)
+{
+ bExpandCollapse = bSet;
+}
+
static void lcl_ResetOrient( const uno::Reference<sheet::XDimensionsSupplier>& xSource )
{
uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
diff --git a/sc/source/core/data/fillinfo.cxx b/sc/source/core/data/fillinfo.cxx
index 7d5605607579..7c95701e9705 100644
--- a/sc/source/core/data/fillinfo.cxx
+++ b/sc/source/core/data/fillinfo.cxx
@@ -225,6 +225,7 @@ void initRowInfo(const ScDocument* pDoc, RowInfo* pRowInfo, const SCSIZE nMaxRow
pThisRowInfo->bChanged = true;
pThisRowInfo->bAutoFilter = false;
pThisRowInfo->bPivotButton = false;
+ pThisRowInfo->bPivotToggle = false;
pThisRowInfo->nRotMaxCol = SC_ROTMAX_NONE;
++rArrRow;
@@ -507,6 +508,9 @@ void ScDocument::FillInfo(
bool bScenario(nOverlap & ScMF::Scenario);
bool bPivotPopupButton(nOverlap & ScMF::ButtonPopup);
bool bFilterActive(nOverlap & ScMF::HiddenMember);
+ bool bPivotCollapseButton(nOverlap & ScMF::DpCollapse);
+ bool bPivotExpandButton(nOverlap & ScMF::DpExpand);
+ bool bPivotPopupButtonMulti(nOverlap & ScMF::ButtonPopup2);
if (bMerged||bHOverlapped||bVOverlapped)
bAnyMerged = true; // internal
@@ -538,8 +542,10 @@ void ScDocument::FillInfo(
pThisRowInfo->bEmptyBack = false;
if (bAutoFilter)
pThisRowInfo->bAutoFilter = true;
- if (bPivotButton || bPivotPopupButton)
+ if (bPivotButton || bPivotPopupButton || bPivotPopupButtonMulti)
pThisRowInfo->bPivotButton = true;
+ if (bPivotCollapseButton || bPivotExpandButton)
+ pThisRowInfo->bPivotToggle = true;
ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nCol);
ScBasicCellInfo* pBasicInfo = &pThisRowInfo->basicCellInfo(nCol);
@@ -551,6 +557,9 @@ void ScDocument::FillInfo(
pInfo->bAutoFilter = bAutoFilter;
pInfo->bPivotButton = bPivotButton;
pInfo->bPivotPopupButton = bPivotPopupButton;
+ pInfo->bPivotCollapseButton = bPivotCollapseButton;
+ pInfo->bPivotExpandButton = bPivotExpandButton;
+ pInfo->bPivotPopupButtonMulti = bPivotPopupButtonMulti;
pInfo->bFilterActive = bFilterActive;
pInfo->pLinesAttr = pLinesAttr;
pInfo->mpTLBRLine = pTLBRLine;
diff --git a/sc/source/filter/excel/xepivotxml.cxx b/sc/source/filter/excel/xepivotxml.cxx
index ecc39caae37f..543a39243b93 100644
--- a/sc/source/filter/excel/xepivotxml.cxx
+++ b/sc/source/filter/excel/xepivotxml.cxx
@@ -780,6 +780,7 @@ void XclExpXmlPivotTables::SavePivotTableXml( XclExpXmlStream& rStrm, const ScDP
// appearance in each axis.
const ScDPSaveData::DimsType& rDims = rSaveData.GetDimensions();
bool bTabularMode = false;
+ bool bCompactMode = true;
for (const auto & i : rDims)
{
const ScDPSaveDimension& rDim = *i;
@@ -824,7 +825,11 @@ void XclExpXmlPivotTables::SavePivotTableXml( XclExpXmlStream& rStrm, const ScDP
;
}
if(rDim.GetLayoutInfo())
- bTabularMode |= (rDim.GetLayoutInfo()->LayoutMode == sheet::DataPilotFieldLayoutMode::TABULAR_LAYOUT);
+ {
+ const auto eLayoutMode = rDim.GetLayoutInfo()->LayoutMode;
+ bTabularMode |= (eLayoutMode == sheet::DataPilotFieldLayoutMode::TABULAR_LAYOUT);
+ bCompactMode &= (eLayoutMode == sheet::DataPilotFieldLayoutMode::COMPACT_LAYOUT);
+ }
}
sax_fastparser::FSHelperPtr& pPivotStrm = rStrm.GetCurrentStream();
@@ -839,13 +844,14 @@ void XclExpXmlPivotTables::SavePivotTableXml( XclExpXmlStream& rStrm, const ScDP
XML_applyAlignmentFormats, ToPsz10(false),
XML_applyWidthHeightFormats, ToPsz10(false),
XML_dataCaption, "Values",
+ XML_showDrill, ToPsz10(rSaveData.GetExpandCollapse()),
XML_useAutoFormatting, ToPsz10(false),
XML_itemPrintTitles, ToPsz10(true),
XML_indent, ToPsz10(false),
XML_outline, ToPsz10(!bTabularMode),
XML_outlineData, ToPsz10(!bTabularMode),
- XML_compact, ToPsz10(false),
- XML_compactData, ToPsz10(false));
+ XML_compact, ToPsz10(bCompactMode),
+ XML_compactData, ToPsz10(bCompactMode));
// NB: Excel's range does not include page field area (if any).
ScRange aOutRange = rDPObj.GetOutputRangeByType(sheet::DataPilotOutputRangeType::TABLE);
@@ -898,8 +904,13 @@ void XclExpXmlPivotTables::SavePivotTableXml( XclExpXmlStream& rStrm, const ScDP
}
bool bDimInTabularMode = false;
+ bool bDimInCompactMode = false;
if(pDim->GetLayoutInfo())
- bDimInTabularMode = (pDim->GetLayoutInfo()->LayoutMode == sheet::DataPilotFieldLayoutMode::TABULAR_LAYOUT);
+ {
+ const auto eLayoutMode = pDim->GetLayoutInfo()->LayoutMode;
+ bDimInTabularMode = (eLayoutMode == sheet::DataPilotFieldLayoutMode::TABULAR_LAYOUT);
+ bDimInCompactMode = (eLayoutMode == sheet::DataPilotFieldLayoutMode::COMPACT_LAYOUT);
+ }
sheet::DataPilotFieldOrientation eOrient = pDim->GetOrientation();
@@ -914,9 +925,13 @@ void XclExpXmlPivotTables::SavePivotTableXml( XclExpXmlStream& rStrm, const ScDP
}
else
{
- pPivotStrm->singleElement(XML_pivotField,
- XML_compact, ToPsz10(false),
- XML_showAll, ToPsz10(false));
+ if (bDimInCompactMode)
+ pPivotStrm->singleElement(XML_pivotField,
+ XML_showAll, ToPsz10(false));
+ else
+ pPivotStrm->singleElement(XML_pivotField,
+ XML_compact, ToPsz10(false),
+ XML_showAll, ToPsz10(false));
}
continue;
}
@@ -933,10 +948,15 @@ void XclExpXmlPivotTables::SavePivotTableXml( XclExpXmlStream& rStrm, const ScDP
}
else
{
- pPivotStrm->singleElement(XML_pivotField,
- XML_dataField, ToPsz10(true),
- XML_compact, ToPsz10(false),
- XML_showAll, ToPsz10(false));
+ if (bDimInCompactMode)
+ pPivotStrm->singleElement(XML_pivotField,
+ XML_dataField, ToPsz10(true),
+ XML_showAll, ToPsz10(false));
+ else
+ pPivotStrm->singleElement(XML_pivotField,
+ XML_dataField, ToPsz10(true),
+ XML_compact, ToPsz10(false),
+ XML_showAll, ToPsz10(false));
}
continue;
}
@@ -1006,7 +1026,10 @@ void XclExpXmlPivotTables::SavePivotTableXml( XclExpXmlStream& rStrm, const ScDP
pAttList->add(XML_axis, toOOXMLAxisType(eOrient));
if (bAppearsInData)
pAttList->add(XML_dataField, ToPsz10(true));
- pAttList->add(XML_compact, ToPsz10(false));
+
+ if (!bDimInCompactMode)
+ pAttList->add(XML_compact, ToPsz10(false));
+
pAttList->add(XML_showAll, ToPsz10(false));
tools::Long nSubTotalCount = pDim->GetSubTotalsCount();
diff --git a/sc/source/filter/inc/pivottablebuffer.hxx b/sc/source/filter/inc/pivottablebuffer.hxx
index 7b7f22320fb7..04db017bfefc 100644
--- a/sc/source/filter/inc/pivottablebuffer.hxx
+++ b/sc/source/filter/inc/pivottablebuffer.hxx
@@ -72,6 +72,7 @@ struct PTFieldModel
bool mbShowAll; /// True = show items without data.
bool mbOutline; /// True = show in outline view, false = show in tabular view.
bool mbSubtotalTop; /// True = show subtotals on top of items in outline or compact mode.
+ bool mbCompact; /// True = show in compact view, false = show in either outline or tabular view.
bool mbInsertBlankRow; /// True = insert blank rows after items.
bool mbInsertPageBreak; /// True = insert page breaks after items.
bool mbAutoShow; /// True = auto show (top 10) filter enabled.
diff --git a/sc/source/filter/oox/pivottablebuffer.cxx b/sc/source/filter/oox/pivottablebuffer.cxx
index e936d0fadd87..637637eb378c 100644
--- a/sc/source/filter/oox/pivottablebuffer.cxx
+++ b/sc/source/filter/oox/pivottablebuffer.cxx
@@ -201,6 +201,7 @@ PTFieldModel::PTFieldModel() :
mbShowAll( true ),
mbOutline( true ),
mbSubtotalTop( true ),
+ mbCompact( false ),
mbInsertBlankRow( false ),
mbInsertPageBreak( false ),
mbAutoShow( false ),
@@ -286,6 +287,7 @@ void PivotTableField::importPivotField( const AttributeList& rAttribs )
maModel.mbShowAll = rAttribs.getBool( XML_showAll, true );
maModel.mbOutline = rAttribs.getBool( XML_outline, true );
maModel.mbSubtotalTop = rAttribs.getBool( XML_subtotalTop, true );
+ maModel.mbCompact = maModel.mbSubtotalTop && maModel.mbOutline && rAttribs.getBool( XML_compact, true );
maModel.mbInsertBlankRow = rAttribs.getBool( XML_insertBlankRow, false );
maModel.mbInsertPageBreak = rAttribs.getBool( XML_insertPageBreak, false );
maModel.mbAutoShow = rAttribs.getBool( XML_autoShow, false );
@@ -708,9 +710,22 @@ Reference< XDataPilotField > PivotTableField::convertRowColPageField( sal_Int32
// layout settings
DataPilotFieldLayoutInfo aLayoutInfo;
- aLayoutInfo.LayoutMode = maModel.mbOutline ?
- (maModel.mbSubtotalTop ? DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_TOP : DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_BOTTOM) :
- DataPilotFieldLayoutMode::TABULAR_LAYOUT;
+ if (maModel.mbCompact)
+ {
+ aLayoutInfo.LayoutMode = DataPilotFieldLayoutMode::COMPACT_LAYOUT;
+ }
+ else if (maModel.mbOutline)
+ {
+ if (maModel.mbSubtotalTop)
+ aLayoutInfo.LayoutMode = DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_TOP;
+ else
+ aLayoutInfo.LayoutMode = DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_BOTTOM;
+ }
+ else
+ {
+ aLayoutInfo.LayoutMode = DataPilotFieldLayoutMode::TABULAR_LAYOUT;
+ }
+
aLayoutInfo.AddEmptyLines = maModel.mbInsertBlankRow;
aPropSet.setProperty( PROP_LayoutInfo, aLayoutInfo );
aPropSet.setProperty( PROP_ShowEmpty, maModel.mbShowAll );
@@ -1256,6 +1271,9 @@ void PivotTable::finalizeImport()
aDescProp.setProperty( PROP_ShowFilterButton, false );
aDescProp.setProperty( PROP_DrillDownOnDoubleClick, maDefModel.mbEnableDrill );
+ if (auto* pSaveData = mpDPObject->GetSaveData())
+ pSaveData->SetExpandCollapse(maDefModel.mbShowDrill);
+
// finalize all fields, this finds field names and creates grouping fields
finalizeFieldsImport();
diff --git a/sc/source/filter/xml/XMLExportDataPilot.cxx b/sc/source/filter/xml/XMLExportDataPilot.cxx
index da0c9c57e9cf..da65bec0dab7 100644
--- a/sc/source/filter/xml/XMLExportDataPilot.cxx
+++ b/sc/source/filter/xml/XMLExportDataPilot.cxx
@@ -418,9 +418,17 @@ void ScXMLExportDataPilot::WriteLayoutInfo(const ScDPSaveDimension* pDim)
case sheet::DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_BOTTOM:
sValueStr = GetXMLToken(XML_OUTLINE_SUBTOTALS_BOTTOM);
break;
+ case sheet::DataPilotFieldLayoutMode::COMPACT_LAYOUT:
+ sValueStr = GetXMLToken(XML_TABULAR_LAYOUT);
+ break;
}
+
if (!sValueStr.isEmpty())
rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_LAYOUT_MODE, sValueStr);
+
+ if (pLayoutInfo->LayoutMode == sheet::DataPilotFieldLayoutMode::COMPACT_LAYOUT)
+ rExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_LAYOUT_MODE, GetXMLToken(XML_COMPACT_LAYOUT));
+
SvXMLElementExport aElemDPLLI(rExport, XML_NAMESPACE_TABLE, XML_DATA_PILOT_LAYOUT_INFO, true, true);
}
@@ -793,6 +801,8 @@ void ScXMLExportDataPilot::WriteDataPilots()
rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_SHOW_FILTER_BUTTON, XML_FALSE);
if (!pDPSave->GetDrillDown())
rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DRILL_DOWN_ON_DOUBLE_CLICK, XML_FALSE);
+ if (pDPSave->GetExpandCollapse())
+ rExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_SHOW_DRILL_DOWN_BUTTONS, XML_TRUE);
if ((*pDPs)[i].GetHeaderLayout())
rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_HEADER_GRID_LAYOUT, XML_TRUE);
diff --git a/sc/source/filter/xml/xmldpimp.cxx b/sc/source/filter/xml/xmldpimp.cxx
index 307d55f7f30e..3d97756b7ea3 100644
--- a/sc/source/filter/xml/xmldpimp.cxx
+++ b/sc/source/filter/xml/xmldpimp.cxx
@@ -100,7 +100,9 @@ ScXMLDataPilotTableContext::ScXMLDataPilotTableContext( ScXMLImport& rImport,
bSourceCellRange(false),
bShowFilter(true),
bDrillDown(true),
- bHeaderGridLayout(false)
+ bShowExpandCollapse(false),
+ bHeaderGridLayout(false),
+ bHasCompactField(false)
{
if ( !rAttrList.is() )
return;
@@ -175,6 +177,11 @@ ScXMLDataPilotTableContext::ScXMLDataPilotTableContext( ScXMLImport& rImport,
bDrillDown = IsXMLToken(aIter, XML_TRUE);
}
break;
+ case XML_ELEMENT( LO_EXT, XML_SHOW_DRILL_DOWN_BUTTONS ):
+ {
+ bShowExpandCollapse = IsXMLToken(aIter, XML_TRUE);
+ }
+ break;
case XML_ELEMENT( TABLE, XML_HEADER_GRID_LAYOUT ):
{
bHeaderGridLayout = IsXMLToken(aIter, XML_TRUE);
@@ -363,7 +370,7 @@ void ScXMLDataPilotTableContext::SetButtons(ScDPObject* pDPObject)
if (bHasHidden)
nMFlag |= ScMF::HiddenMember;
- nMFlag |= ScMF::ButtonPopup;
+ nMFlag |= (bHasCompactField ? ScMF::ButtonPopup2 : ScMF::ButtonPopup);
}
pDoc->ApplyFlagsTab(aScAddress.Col(), aScAddress.Row(), aScAddress.Col(), aScAddress.Row(), aScAddress.Tab(), nMFlag);
@@ -512,6 +519,7 @@ void SAL_CALL ScXMLDataPilotTableContext::endFastElement( sal_Int32 /*nElement*/
pDPSave->SetRepeatIfEmpty(bIdentifyCategories);
pDPSave->SetFilterButton(bShowFilter);
pDPSave->SetDrillDown(bDrillDown);
+ pDPSave->SetExpandCollapse(bShowExpandCollapse);
if (pDPDimSaveData)
pDPSave->SetDimensionData(pDPDimSaveData.get());
pDPObject->SetSaveData(*pDPSave);
@@ -896,6 +904,15 @@ void ScXMLDataPilotFieldContext::SetSubTotalName(const OUString& rName)
xDim->SetSubtotalName(rName);
}
+void ScXMLDataPilotFieldContext::SetLayoutInfo(const css::sheet::DataPilotFieldLayoutInfo& aInfo)
+{
+ if (xDim)
+ xDim->SetLayoutInfo(&aInfo);
+
+ if (pDataPilotTable && aInfo.LayoutMode == sheet::DataPilotFieldLayoutMode::COMPACT_LAYOUT)
+ pDataPilotTable->SetHasCompactField();
+}
+
void ScXMLDataPilotFieldContext::AddGroup(::std::vector<OUString>&& rMembers, const OUString& rName)
{
ScXMLDataPilotGroup aGroup;
@@ -1166,6 +1183,7 @@ ScXMLDataPilotLayoutInfoContext::ScXMLDataPilotLayoutInfoContext( ScXMLImport& r
ScXMLImportContext( rImport )
{
sheet::DataPilotFieldLayoutInfo aInfo;
+ aInfo.LayoutMode = sheet::DataPilotFieldLayoutMode::TABULAR_LAYOUT;
if ( rAttrList.is() )
{
@@ -1180,12 +1198,20 @@ ScXMLDataPilotLayoutInfoContext::ScXMLDataPilotLayoutInfoContext( ScXMLImport& r
aInfo.AddEmptyLines = false;
break;
case XML_ELEMENT( TABLE, XML_LAYOUT_MODE ):
- if (IsXMLToken(aIter, XML_TABULAR_LAYOUT))
- aInfo.LayoutMode = sheet::DataPilotFieldLayoutMode::TABULAR_LAYOUT;
- else if (IsXMLToken(aIter, XML_OUTLINE_SUBTOTALS_TOP))
- aInfo.LayoutMode = sheet::DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_TOP;
- else if (IsXMLToken(aIter, XML_OUTLINE_SUBTOTALS_BOTTOM))
- aInfo.LayoutMode = sheet::DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_BOTTOM;
+ case XML_ELEMENT( LO_EXT, XML_LAYOUT_MODE ):
+ // Ensure that loext:layout-mode="compact" is not overwritten by any
+ // value of table:layout-mode.
+ if (aInfo.LayoutMode != sheet::DataPilotFieldLayoutMode::COMPACT_LAYOUT)
+ {
+ if (IsXMLToken(aIter, XML_TABULAR_LAYOUT))
+ aInfo.LayoutMode = sheet::DataPilotFieldLayoutMode::TABULAR_LAYOUT;
+ else if (IsXMLToken(aIter, XML_OUTLINE_SUBTOTALS_TOP))
+ aInfo.LayoutMode = sheet::DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_TOP;
+ else if (IsXMLToken(aIter, XML_OUTLINE_SUBTOTALS_BOTTOM))
+ aInfo.LayoutMode = sheet::DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_BOTTOM;
+ else if (IsXMLToken(aIter, XML_COMPACT_LAYOUT))
+ aInfo.LayoutMode = sheet::DataPilotFieldLayoutMode::COMPACT_LAYOUT;
+ }
break;
}
}
diff --git a/sc/source/filter/xml/xmldpimp.hxx b/sc/source/filter/xml/xmldpimp.hxx
index a7d5f607f420..641a2132872c 100644
--- a/sc/source/filter/xml/xmldpimp.hxx
+++ b/sc/source/filter/xml/xmldpimp.hxx
@@ -101,7 +101,9 @@ class ScXMLDataPilotTableContext : public ScXMLImportContext
bool bSourceCellRange:1;
bool bShowFilter:1;
bool bDrillDown:1;
+ bool bShowExpandCollapse:1;
bool bHeaderGridLayout:1;
+ bool bHasCompactField:1; // True = One or more fields have compact layout.
SelectedPagesType maSelectedPages;
@@ -134,6 +136,7 @@ public:
void AddGroupDim(const ScDPSaveGroupDimension& aGroupDim);
void SetButtons(ScDPObject* pDPObject);
void SetSelectedPage( const OUString& rDimName, const OUString& rSelected );
+ void SetHasCompactField() { bHasCompactField = true; }
};
class ScXMLDPSourceSQLContext : public ScXMLImportContext
@@ -267,7 +270,7 @@ public:
void SetFieldReference(const css::sheet::DataPilotFieldReference& aRef) { if (xDim) xDim->SetReferenceValue(&aRef); }
void SetAutoShowInfo(const css::sheet::DataPilotFieldAutoShowInfo& aInfo) { if (xDim) xDim->SetAutoShowInfo(&aInfo); }
void SetSortInfo(const css::sheet::DataPilotFieldSortInfo& aInfo) { if (xDim) xDim->SetSortInfo(&aInfo); }
- void SetLayoutInfo(const css::sheet::DataPilotFieldLayoutInfo& aInfo) { if (xDim) xDim->SetLayoutInfo(&aInfo); }
+ void SetLayoutInfo(const css::sheet::DataPilotFieldLayoutInfo& aInfo);
void SetGrouping(const OUString& rGroupSource, const double& rStart, const double& rEnd, const double& rStep,
sal_Int32 nPart, bool bDate, bool bAutoSt, bool bAutoE)
{
diff --git a/sc/source/ui/cctrl/checklistmenu.cxx b/sc/source/ui/cctrl/checklistmenu.cxx
index 456b0fe8b76a..2ff0e4a4dc00 100644
--- a/sc/source/ui/cctrl/checklistmenu.cxx
+++ b/sc/source/ui/cctrl/checklistmenu.cxx
@@ -373,6 +373,27 @@ void ScCheckListMenuControl::endSubMenu(ScListSubMenuControl& rSubMenu)
}
}
+void ScCheckListMenuControl::addFields(const std::vector<OUString>& aFields)
+{
+ if (!mbIsMultiField)
+ return;
+
+ mxFieldsCombo->clear();
+
+ for (auto& aField: aFields)
+ mxFieldsCombo->append_text(aField);
+
+ mxFieldsCombo->set_active(0);
+}
+
+tools::Long ScCheckListMenuControl::getField()
+{
+ if (!mbIsMultiField)
+ return -1;
+
+ return mxFieldsCombo->get_active();
+}
+
void ScCheckListMenuControl::selectMenuItem(size_t nPos, bool bSubMenuTimer)
{
mxMenu->select(nPos == MENU_NOT_SELECTED ? -1 : nPos);
@@ -476,13 +497,15 @@ constexpr int nCheckListVisibleRows = 9;
constexpr int nColorListVisibleRows = 9;
ScCheckListMenuControl::ScCheckListMenuControl(weld::Widget* pParent, ScViewData& rViewData,
- bool bHasDates, int nWidth)
+ bool bHasDates, int nWidth, bool bIsMultiField)
: mxBuilder(Application::CreateBuilder(pParent, "modules/scalc/ui/filterdropdown.ui"))
, mxPopover(mxBuilder->weld_popover("FilterDropDown"))
, mxContainer(mxBuilder->weld_container("container"))
, mxMenu(mxBuilder->weld_tree_view("menu"))
, mxScratchIter(mxMenu->make_iterator())
, mxNonMenu(mxBuilder->weld_widget("nonmenu"))
+ , mxFieldsComboLabel(mxBuilder->weld_label("select_field_label"))
+ , mxFieldsCombo(mxBuilder->weld_combo_box("multi_field_combo"))
, mxEdSearch(mxBuilder->weld_entry("search_edit"))
, mxBox(mxBuilder->weld_widget("box"))
, mxListChecks(mxBuilder->weld_tree_view("check_list_box"))
@@ -508,6 +531,7 @@ ScCheckListMenuControl::ScCheckListMenuControl(weld::Widget* pParent, ScViewData
, maOpenTimer(this)
, maCloseTimer(this)
, maSearchEditTimer("ScCheckListMenuControl maSearchEditTimer")
+ , mbIsMultiField(bIsMultiField)
{
mxTreeChecks->set_clicks_to_toggle(1);
mxListChecks->set_clicks_to_toggle(1);
@@ -556,6 +580,16 @@ ScCheckListMenuControl::ScCheckListMenuControl(weld::Widget* pParent, ScViewData
mxListChecks->enable_toggle_buttons(weld::ColumnToggleType::Check);
mxBox->show();
+ if (mbIsMultiField)
+ {
+ mxFieldsComboLabel->show();
+ mxFieldsCombo->show();
+ }
+ else
+ {
+ mxFieldsComboLabel->hide();
+ mxFieldsCombo->hide();
+ }
mxEdSearch->show();
mxButtonBox->show();
@@ -565,6 +599,8 @@ ScCheckListMenuControl::ScCheckListMenuControl(weld::Widget* pParent, ScViewData
mxBtnOk->connect_clicked(LINK(this, ScCheckListMenuControl, ButtonHdl));
mxBtnCancel->connect_clicked(LINK(this, ScCheckListMenuControl, ButtonHdl));
+ if (mbIsMultiField)
+ mxFieldsCombo->connect_changed(LINK(this, ScCheckListMenuControl, ComboChangedHdl));
mxEdSearch->connect_changed(LINK(this, ScCheckListMenuControl, EdModifyHdl));
mxEdSearch->connect_activate(LINK(this, ScCheckListMenuControl, EdActivateHdl));
mxTreeChecks->connect_toggled(LINK(this, ScCheckListMenuControl, CheckHdl));
@@ -744,6 +780,12 @@ namespace
}
}
+IMPL_LINK_NOARG(ScCheckListMenuControl, ComboChangedHdl, weld::ComboBox&, void)
+{
+ if (mbIsMultiField && mxFieldChangedAction)
+ mxFieldChangedAction->execute();
+}
+
IMPL_LINK_NOARG(ScCheckListMenuControl, SearchEditTimeoutHdl, Timer*, void)
{
OUString aSearchText = mxEdSearch->get_text();
@@ -1078,6 +1120,15 @@ void ScCheckListMenuControl::addMember(const OUString& rName, const double nVal,
maMembers.emplace_back(std::move(aMember));
}
+void ScCheckListMenuControl::clearMembers()
+{
+ maMembers.clear();
+
+ mpChecks->freeze();
+ mpChecks->clear();
+ mpChecks->thaw();
+}
+
std::unique_ptr<weld::TreeIter> ScCheckListMenuControl::FindEntry(const weld::TreeIter* pParent, std::u16string_view sNode)
{
std::unique_ptr<weld::TreeIter> xEntry = mpChecks->make_iterator(pParent);
@@ -1501,6 +1552,11 @@ void ScCheckListMenuControl::setPopupEndAction(Action* p)
mxPopupEndAction.reset(p);
}
+void ScCheckListMenuControl::setFieldChangedAction(Action* p)
+{
+ mxFieldChangedAction.reset(p);
+}
+
IMPL_LINK_NOARG(ScCheckListMenuControl, PopupModeEndHdl, weld::Popover&, void)
{
mbIsPoppedUp = false;
diff --git a/sc/source/ui/cctrl/dpcontrol.cxx b/sc/source/ui/cctrl/dpcontrol.cxx
index b7d61ce002c7..34eea011ee05 100644
--- a/sc/source/ui/cctrl/dpcontrol.cxx
+++ b/sc/source/ui/cctrl/dpcontrol.cxx
@@ -32,8 +32,12 @@ ScDPFieldButton::ScDPFieldButton(OutputDevice* pOutDev, const StyleSettings* pSt
mpDoc(pDoc),
mpOutDev(pOutDev),
mpStyle(pStyle),
+ mnToggleIndent(0),
mbBaseButton(true),
mbPopupButton(false),
+ mbPopupButtonMulti(false),
+ mbToggleButton(false),
+ mbToggleCollapse(false),
mbHasHiddenMember(false),
mbPopupPressed(false),
mbPopupLeft(false)
@@ -74,6 +78,18 @@ void ScDPFieldButton::setDrawPopupButton(bool b)
mbPopupButton = b;
}
+void ScDPFieldButton::setDrawPopupButtonMulti(bool b)
+{
+ mbPopupButtonMulti = b;
+}
+
+void ScDPFieldButton::setDrawToggleButton(bool b, bool bCollapse, sal_Int32 nIndent)
+{
+ mbToggleButton = b;
+ mbToggleCollapse = bCollapse;
+ mnToggleIndent = nIndent;
+}
+
void ScDPFieldButton::setHasHiddenMember(bool b)
{
mbHasHiddenMember = b;
@@ -140,9 +156,12 @@ void ScDPFieldButton::draw()
mpOutDev->Pop();
}
- if (mbPopupButton)
+ if (mbPopupButton || mbPopupButtonMulti)
drawPopupButton();
+ if (mbToggleButton)
+ drawToggleButton();
+
mpOutDev->EnableMapMode(bOldMapEnabled);
}
@@ -174,6 +193,34 @@ void ScDPFieldButton::getPopupBoundingBox(Point& rPos, Size& rSize) const
rSize.setHeight(nH);
}
+void ScDPFieldButton::getToggleBoundingBox(Point& rPos, Size& rSize) const
+{
+ const float fScaleFactor = mpOutDev->GetDPIScaleFactor();
+
+ tools::Long nMaxSize = 13 * fScaleFactor; // Button max size in either dimension
+ tools::Long nMargin = 3 * fScaleFactor;
+
+ tools::Long nIndent = fScaleFactor * o3tl::convert(mnToggleIndent, o3tl::Length::twip, o3tl::Length::px);
+ tools::Long nW = std::min(maSize.getWidth() / 2, nMaxSize);
+ tools::Long nH = std::min(maSize.getHeight(), nMaxSize);
+ nIndent = std::min(nIndent, maSize.getWidth());
+
+ double fZoom = static_cast<double>(maZoomY) > 1.0 ? static_cast<double>(maZoomY) : 1.0;
+ if (fZoom > 1.0)
+ {
+ nW = fZoom * (nW - 1);
+ nH = fZoom * (nH - 1);
+ nIndent = fZoom * (nIndent -1);
+ nMargin = fZoom * (nMargin - 1);
+ }
+
+ // FIXME: RTL case ?
+ rPos.setX(maPos.getX() + nIndent - nW + nMargin);
+ rPos.setY(maPos.getY() + maSize.getHeight() / 2 - nH / 2 + nMargin);
+ rSize.setWidth(nW - nMargin - 1);
+ rSize.setHeight(nH - nMargin - 1);
+}
+
void ScDPFieldButton::drawPopupButton()
{
Point aPos;
@@ -226,4 +273,29 @@ void ScDPFieldButton::drawPopupButton()
}
}
+void ScDPFieldButton::drawToggleButton()
+{
+ Point aPos;
+ Size aSize;
+ getToggleBoundingBox(aPos, aSize);
+
+ // Background & outer black border
+ mpOutDev->SetLineColor(COL_BLACK);
+ mpOutDev->SetFillColor();
+ mpOutDev->DrawRect(tools::Rectangle(aPos, aSize));
+
+ Point aCenter(aPos.X() + aSize.getWidth() / 2, aPos.Y() + aSize.getHeight() / 2);
+
+ mpOutDev->DrawLine(
+ Point(aPos.X() + 2, aCenter.Y()),
+ Point(aPos.X() + aSize.getWidth() - 2, aCenter.Y()));
+
+ if (!mbToggleCollapse)
+ {
+ mpOutDev->DrawLine(
+ Point(aCenter.X(), aPos.Y() + 2),
+ Point(aCenter.X(), aPos.Y() + aSize.getHeight() - 2));
+ }
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/PivotLayoutDialog.cxx b/sc/source/ui/dbgui/PivotLayoutDialog.cxx
index 42fe539ec0f1..17165eac6867 100644
--- a/sc/source/ui/dbgui/PivotLayoutDialog.cxx
+++ b/sc/source/ui/dbgui/PivotLayoutDialog.cxx
@@ -92,6 +92,7 @@ ScPivotLayoutDialog::ScPivotLayoutDialog(
, mxCheckIdentifyCategories(m_xBuilder->weld_check_button("check-identify-categories"))
, mxCheckTotalRows(m_xBuilder->weld_check_button("check-total-rows"))
, mxCheckDrillToDetail(m_xBuilder->weld_check_button("check-drill-to-details"))
+ , mxCheckExpandCollapse(m_xBuilder->weld_check_button("check-show-expand-collapse"))
, mxSourceRadioNamedRange(m_xBuilder->weld_radio_button("source-radio-named-range"))
, mxSourceRadioSelection(m_xBuilder->weld_radio_button("source-radio-selection"))
, mxSourceListBox(m_xBuilder->weld_combo_box("source-list"))
@@ -168,11 +169,13 @@ ScPivotLayoutDialog::ScPivotLayoutDialog(
{
mxCheckAddFilter->set_active(false);
mxCheckDrillToDetail->set_active(false);
+ mxCheckExpandCollapse->set_active(false);
}
else
{
mxCheckAddFilter->set_active(pSaveData->GetFilterButton());
mxCheckDrillToDetail->set_active(pSaveData->GetDrillDown());
+ mxCheckExpandCollapse->set_active(pSaveData->GetExpandCollapse());
}
mxCheckIgnoreEmptyRows->set_active(maPivotParameters.bIgnoreEmptyRows);
@@ -520,6 +523,7 @@ void ScPivotLayoutDialog::ApplySaveData(ScDPSaveData& rSaveData)
rSaveData.SetRowGrand(mxCheckTotalRows->get_active());
rSaveData.SetFilterButton(mxCheckAddFilter->get_active());
rSaveData.SetDrillDown(mxCheckDrillToDetail->get_active());
+ rSaveData.SetExpandCollapse(mxCheckExpandCollapse->get_active());
Reference<XDimensionsSupplier> xSource = maPivotTableObject.GetSource();
diff --git a/sc/source/ui/dbgui/pvfundlg.cxx b/sc/source/ui/dbgui/pvfundlg.cxx
index c6c92ac72611..f74bb4b7391d 100644
--- a/sc/source/ui/dbgui/pvfundlg.cxx
+++ b/sc/source/ui/dbgui/pvfundlg.cxx
@@ -600,6 +600,8 @@ namespace
return 1;
case DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_BOTTOM:
return 2;
+ case DataPilotFieldLayoutMode::COMPACT_LAYOUT:
+ return 3;
}
return -1;
}
@@ -614,6 +616,8 @@ namespace
return DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_TOP;
case 2:
return DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_BOTTOM;
+ case 3:
+ return DataPilotFieldLayoutMode::COMPACT_LAYOUT;
}
return DataPilotFieldLayoutMode::TABULAR_LAYOUT;
}
diff --git a/sc/source/ui/inc/PivotLayoutDialog.hxx b/sc/source/ui/inc/PivotLayoutDialog.hxx
index 8fd0c1e5b8de..c07693e3518f 100644
--- a/sc/source/ui/inc/PivotLayoutDialog.hxx
+++ b/sc/source/ui/inc/PivotLayoutDialog.hxx
@@ -60,6 +60,7 @@ private:
std::unique_ptr<weld::CheckButton> mxCheckIdentifyCategories;
std::unique_ptr<weld::CheckButton> mxCheckTotalRows;
std::unique_ptr<weld::CheckButton> mxCheckDrillToDetail;
+ std::unique_ptr<weld::CheckButton> mxCheckExpandCollapse;
std::unique_ptr<weld::RadioButton> mxSourceRadioNamedRange;
std::unique_ptr<weld::RadioButton> mxSourceRadioSelection;
diff --git a/sc/source/ui/inc/checklistmenu.hxx b/sc/source/ui/inc/checklistmenu.hxx
index 4ccd1574b621..1e85b17c48f2 100644
--- a/sc/source/ui/inc/checklistmenu.hxx
+++ b/sc/source/ui/inc/checklistmenu.hxx
@@ -124,7 +124,7 @@ public:
};
ScCheckListMenuControl(weld::Widget* pParent, ScViewData& rViewData,
- bool bTreeMode, int nWidth);
+ bool bTreeMode, int nWidth, bool bIsMultiField = false);
~ScCheckListMenuControl();
void addMenuItem(const OUString& rText, Action* pAction);
@@ -138,6 +138,7 @@ public:
void addDateMember(const OUString& rName, double nVal, bool bVisible, bool bHiddenByOtherFilter);
void addMember(const OUString& rName, const double nVal, bool bVisible, bool bHiddenByOtherFilter,
bool bValue = false);
+ void clearMembers();
size_t initMembers(int nMaxMemberWidth = -1);
void setConfig(const Config& rConfig);
@@ -172,6 +173,7 @@ public:
void setOKAction(Action* p);
void setPopupEndAction(Action* p);
+ void setFieldChangedAction(Action* p);
int GetTextWidth(const OUString& rsName) const;
int IncreaseWindowWidthToFitText(int nMaxTextWidth);
@@ -183,6 +185,9 @@ public:
void terminateAllPopupMenus();
void endSubMenu(ScListSubMenuControl& rSubMenu);
+
+ void addFields(const std::vector<OUString>& aFields);
+ tools::Long getField();
private:
std::vector<MenuItemData> maMenuItems;
@@ -234,6 +239,7 @@ private:
DECL_LINK(PopupModeEndHdl, weld::Popover&, void);
DECL_LINK(SearchEditTimeoutHdl, Timer*, void);
+ DECL_LINK(ComboChangedHdl, weld::ComboBox&, void);
DECL_LINK(EdModifyHdl, weld::Entry&, void);
DECL_LINK(EdActivateHdl, weld::Entry&, bool);
@@ -264,6 +270,8 @@ private:
std::unique_ptr<weld::TreeView> mxMenu;
std::unique_ptr<weld::TreeIter> mxScratchIter;
std::unique_ptr<weld::Widget> mxNonMenu;
+ std::unique_ptr<weld::Label> mxFieldsComboLabel;
+ std::unique_ptr<weld::ComboBox> mxFieldsCombo;
std::unique_ptr<weld::Entry> mxEdSearch;
std::unique_ptr<weld::Widget> mxBox;
std::unique_ptr<weld::TreeView> mxListChecks;
@@ -288,6 +296,7 @@ private:
std::unique_ptr<ExtendedData> mxExtendedData;
std::unique_ptr<Action> mxOKAction;
std::unique_ptr<Action> mxPopupEndAction;
+ std::unique_ptr<Action> mxFieldChangedAction;
Config maConfig;
Size maAllocatedSize;
@@ -325,6 +334,7 @@ private:
SubMenuItemData maCloseTimer;
Timer maSearchEditTimer;
+ bool mbIsMultiField;
};
class ScListSubMenuControl final
diff --git a/sc/source/ui/inc/dpcontrol.hxx b/sc/source/ui/inc/dpcontrol.hxx
index 2d656006e8d2..a447331cd7c3 100644
--- a/sc/source/ui/inc/dpcontrol.hxx
+++ b/sc/source/ui/inc/dpcontrol.hxx
@@ -43,15 +43,19 @@ public:
void setBoundingBox(const Point& rPos, const Size& rSize, bool bLayoutRTL);
void setDrawBaseButton(bool b);
void setDrawPopupButton(bool b);
+ void setDrawPopupButtonMulti(bool b);
+ void setDrawToggleButton(bool b, bool bCollapse, sal_Int32 nIndent);
void setHasHiddenMember(bool b);
void setPopupPressed(bool b);
void setPopupLeft(bool b);
void draw();
void getPopupBoundingBox(Point& rPos, Size& rSize) const;
+ void getToggleBoundingBox(Point& rPos, Size& rSize) const;
private:
void drawPopupButton();
+ void drawToggleButton();
private:
Point maPos;
@@ -61,8 +65,12 @@ private:
ScDocument* mpDoc;
VclPtr<OutputDevice> mpOutDev;
const StyleSettings* mpStyle;
+ sal_Int32 mnToggleIndent;
bool mbBaseButton;
bool mbPopupButton;
+ bool mbPopupButtonMulti;
+ bool mbToggleButton;
+ bool mbToggleCollapse;
bool mbHasHiddenMember;
bool mbPopupPressed;
bool mbPopupLeft;
diff --git a/sc/source/ui/inc/gridwin.hxx b/sc/source/ui/inc/gridwin.hxx
index 70a6efa616cc..635dd91dc393 100644
--- a/sc/source/ui/inc/gridwin.hxx
+++ b/sc/source/ui/inc/gridwin.hxx
@@ -85,6 +85,7 @@ class ScLokRTLContext;
namespace sdr::overlay { class OverlayObjectList; }
class ScFilterListBox;
+struct ScDPLabelData;
class SAL_DLLPUBLIC_RTTI ScGridWindow : public vcl::DocWindow, public DropTargetHelper, public DragSourceHelper
{
@@ -226,7 +227,8 @@ class SAL_DLLPUBLIC_RTTI ScGridWindow : public vcl::DocWindow, public DropTarget
bool DoPageFieldSelection( SCCOL nCol, SCROW nRow );
bool DoAutoFilterButton( SCCOL nCol, SCROW nRow, const MouseEvent& rMEvt );
- void DoPushPivotButton( SCCOL nCol, SCROW nRow, const MouseEvent& rMEvt, bool bButton, bool bPopup );
+ void DoPushPivotButton( SCCOL nCol, SCROW nRow, const MouseEvent& rMEvt, bool bButton, bool bPopup, bool bMultiField );
+ void DoPushPivotToggle( SCCOL nCol, SCROW nRow, const MouseEvent& rMEvt );
void DPMouseMove( const MouseEvent& rMEvt );
void DPMouseButtonUp( const MouseEvent& rMEvt );
@@ -239,8 +241,15 @@ class SAL_DLLPUBLIC_RTTI ScGridWindow : public vcl::DocWindow, public DropTarget
* mouse event handling is necessary, false otherwise.
*/
bool DPTestFieldPopupArrow(const MouseEvent& rMEvt, const ScAddress& rPos, const ScAddress& rDimPos, ScDPObject* pDPObj);
+ bool DPTestMultiFieldPopupArrow(const MouseEvent& rMEvt, const ScAddress& rPos, ScDPObject* pDPObj);
+ void DPPopulateFieldMembers(const ScDPLabelData& rLabelData);
+ void DPSetupFieldPopup(std::unique_ptr<ScCheckListMenuControl::ExtendedData> pDPData, bool bDimOrientNotPage,
+ ScDPObject* pDPObj, bool bMultiField = false);
+ void DPConfigFieldPopup();
void DPLaunchFieldPopupMenu(const Point& rScrPos, const Size& rScrSize, const ScAddress& rPos, ScDPObject* pDPObj);
+ void DPLaunchMultiFieldPopupMenu(const Point& rScrPos, const Size& rScrSize, ScDPObject* pDPObj,
+ css::sheet::DataPilotFieldOrientation nOrient);
void RFMouseMove( const MouseEvent& rMEvt, bool bUp );
@@ -444,6 +453,7 @@ public:
void CheckNeedsRepaint();
+ void UpdateDPPopupMenuForFieldChange();
void UpdateDPFromFieldPopupMenu();
bool UpdateVisibleRange();
diff --git a/sc/source/ui/view/gridwin.cxx b/sc/source/ui/view/gridwin.cxx
index 2ff49c0a5dfe..1465a7424032 100644
--- a/sc/source/ui/view/gridwin.cxx
+++ b/sc/source/ui/view/gridwin.cxx
@@ -1963,13 +1963,20 @@ void ScGridWindow::HandleMouseButtonDown( const MouseEvent& rMEvt, MouseEventSta
}
}
- if (pAttr->HasPivotButton() || pAttr->HasPivotPopupButton())
+ if (pAttr->HasPivotButton() || pAttr->HasPivotPopupButton() || pAttr->HasPivotMultiFieldPopupButton())
{
- DoPushPivotButton(nPosX, nPosY, rMEvt, pAttr->HasPivotButton(), pAttr->HasPivotPopupButton());
+ DoPushPivotButton(nPosX, nPosY, rMEvt, pAttr->HasPivotButton(),
+ pAttr->HasPivotPopupButton(), pAttr->HasPivotMultiFieldPopupButton());
rState.mbActivatePart = false;
return;
}
+ if (pAttr->HasPivotToggle())
+ {
+ DoPushPivotToggle(nPosX, nPosY, rMEvt);
+ rState.mbActivatePart = false;
+ }
+
// List Validity drop-down button
if ( bListValButton )
diff --git a/sc/source/ui/view/gridwin2.cxx b/sc/source/ui/view/gridwin2.cxx
index f8e83dc61864..94d90443da26 100644
--- a/sc/source/ui/view/gridwin2.cxx
+++ b/sc/source/ui/view/gridwin2.cxx
@@ -21,6 +21,7 @@
#include <vcl/settings.hxx>
#include <comphelper/lok.hxx>
+#include <attrib.hxx>
#include <gridwin.hxx>
#include <tabvwsh.hxx>
#include <docsh.hxx>
@@ -41,6 +42,7 @@
#include <scabstdlg.hxx>
#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
+#include <com/sun/star/sheet/DataPilotTableHeaderData.hpp>
#include <unordered_map>
#include <memory>
@@ -146,7 +148,7 @@ bool ScGridWindow::DoAutoFilterButton( SCCOL nCol, SCROW nRow, const MouseEvent&
return false;
}
-void ScGridWindow::DoPushPivotButton( SCCOL nCol, SCROW nRow, const MouseEvent& rMEvt, bool bButton, bool bPopup )
+void ScGridWindow::DoPushPivotButton( SCCOL nCol, SCROW nRow, const MouseEvent& rMEvt, bool bButton, bool bPopup, bool bMultiField )
{
ScDocument& rDoc = mrViewData.GetDocument();
SCTAB nTab = mrViewData.GetTabNo();
@@ -162,12 +164,20 @@ void ScGridWindow::DoPushPivotButton( SCCOL nCol, SCROW nRow, const MouseEvent&
// For page field selection cell, the real field position is to the left.
aDimPos.IncCol(-1);
+ if (bMultiField && DPTestMultiFieldPopupArrow(rMEvt, aPos, pDPObj))
+ {
+ // Multi-field pop up menu has been launched. Don't activate
+ // field move or regular popup.
+ return;
+ }
+
tools::Long nField = pDPObj->GetHeaderDim(aDimPos, nOrient);
if ( nField >= 0 )
{
bDPMouse = false;
nDPField = nField;
pDragDPObj = pDPObj;
+
if (bPopup && DPTestFieldPopupArrow(rMEvt, aPos, aDimPos, pDPObj))
{
// field name pop up menu has been launched. Don't activate
@@ -227,6 +237,51 @@ void ScGridWindow::DoPushPivotButton( SCCOL nCol, SCROW nRow, const MouseEvent&
}
}
+void ScGridWindow::DoPushPivotToggle( SCCOL nCol, SCROW nRow, const MouseEvent& rMEvt )
+{
+ bool bLayoutRTL = mrViewData.GetDocument().IsLayoutRTL( mrViewData.GetTabNo() );
+
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+
+ ScDPObject* pDPObj = rDoc.GetDPAtCursor(nCol, nRow, nTab);
+ if (!pDPObj)
+ return;
+
+ if (!pDPObj->GetSaveData()->GetDrillDown())
+ return;
+
+ // Get the geometry of the cell.
+ Point aScrPos = mrViewData.GetScrPos(nCol, nRow, eWhich);
+ tools::Long nSizeX, nSizeY;
+ mrViewData.GetMergeSizePixel(nCol, nRow, nSizeX, nSizeY);
+ Size aScrSize(nSizeX - 1, nSizeY - 1);
+
+ sal_uInt16 nIndent = 0;
+ if (const ScIndentItem* pIndentItem = rDoc.GetAttr(nCol, nRow, nTab, ATTR_INDENT))
+ nIndent = pIndentItem->GetValue();
+
+ // Check if the mouse cursor is clicking on the toggle +/- box.
+ ScDPFieldButton aBtn(GetOutDev(), &GetSettings().GetStyleSettings(), &GetMapMode().GetScaleY());
+ aBtn.setBoundingBox(aScrPos, aScrSize, bLayoutRTL);
+ aBtn.setDrawToggleButton(true, true, nIndent);
+ Point aPopupPos;
+ Size aPopupSize;
+ aBtn.getToggleBoundingBox(aPopupPos, aPopupSize);
+ tools::Rectangle aRect(aPopupPos, aPopupSize);
+ if (aRect.Contains(rMEvt.GetPosPixel()))
+ {
+ // Mouse cursor inside the toggle +/- box.
+ sheet::DataPilotTableHeaderData aData;
+ ScAddress aCellPos(nCol, nRow, nTab);
+ pDPObj->GetHeaderPositionData(aCellPos, aData);
+ ScDPObject aNewObj(*pDPObj);
+ pDPObj->ToggleDetails(aData, &aNewObj);
+ ScDBDocFunc aFunc(*mrViewData.GetDocShell());
+ aFunc.DataPilotUpdate(pDPObj, &aNewObj, true, false);
+ }
+}
+
// Data Pilot interaction
void ScGridWindow::DPTestMouse( const MouseEvent& rMEvt, bool bMove )
@@ -369,6 +424,39 @@ bool ScGridWindow::DPTestFieldPopupArrow(
return false;
}
+bool ScGridWindow::DPTestMultiFieldPopupArrow(
+ const MouseEvent& rMEvt, const ScAddress& rPos, ScDPObject* pDPObj)
+{
+ bool bLayoutRTL = mrViewData.GetDocument().IsLayoutRTL( mrViewData.GetTabNo() );
+ bool bLOK = comphelper::LibreOfficeKit::isActive();
+
+ // Get the geometry of the cell.
+ Point aScrPos = mrViewData.GetScrPos(rPos.Col(), rPos.Row(), eWhich);
+ tools::Long nSizeX, nSizeY;
+ mrViewData.GetMergeSizePixel(rPos.Col(), rPos.Row(), nSizeX, nSizeY);
+ Size aScrSize(nSizeX - 1, nSizeY - 1);
+
+ // Check if the mouse cursor is clicking on the popup arrow box.
+ ScDPFieldButton aBtn(GetOutDev(), &GetSettings().GetStyleSettings(), &GetMapMode().GetScaleY());
+ aBtn.setBoundingBox(aScrPos, aScrSize, bLayoutRTL);
+ aBtn.setPopupLeft(false); // DataPilot popup is always right-aligned for now
+ aBtn.setDrawPopupButtonMulti(true);
+ Point aPopupPos;
+ Size aPopupSize;
+ aBtn.getPopupBoundingBox(aPopupPos, aPopupSize);
+ tools::Rectangle aRect(aPopupPos, aPopupSize);
+ if (aRect.Contains(rMEvt.GetPosPixel()))
+ {
+ DataPilotFieldOrientation nOrient;
+ pDPObj->GetHeaderDim(rPos, nOrient);
+ // Mouse cursor inside the popup arrow box. Launch the multi-field menu.
+ DPLaunchMultiFieldPopupMenu(bLOK ? aScrPos : OutputToScreenPixel(aScrPos), aScrSize, pDPObj, nOrient);
+ return true;
+ }
+
+ return false;
+}
+
namespace {
struct DPFieldPopupData : public ScCheckListMenuControl::ExtendedData
@@ -378,6 +466,12 @@ struct DPFieldPopupData : public ScCheckListMenuControl::ExtendedData
tools::Long mnDim;
};
+struct DPMultiFieldPopupData : public DPFieldPopupData
+{
+ std::vector<tools::Long> maFieldIndices;
+ std::vector<OUString> maFieldNames;
+};
+
class DPFieldPopupOKAction : public ScCheckListMenuControl::Action
{
public:
@@ -393,6 +487,21 @@ private:
VclPtr<ScGridWindow> mpGridWindow;
};
+class DPFieldChangedAction : public ScCheckListMenuControl::Action
+{
+public:
+ explicit DPFieldChangedAction(ScGridWindow* p) :
+ mpGridWindow(p) {}
+
+ virtual bool execute() override
+ {
+ mpGridWindow->UpdateDPPopupMenuForFieldChange();
+ return true;
+ }
+private:
+ VclPtr<ScGridWindow> mpGridWindow;
+};
+
class PopupSortAction : public ScCheckListMenuControl::Action
{
public:
@@ -445,53 +554,104 @@ void ScGridWindow::DPLaunchFieldPopupMenu(const Point& rScreenPosition, const Si
DPLaunchFieldPopupMenu(rScreenPosition, rScreenSize, nDimIndex, pDPObject);
}
-void ScGridWindow::DPLaunchFieldPopupMenu(const Point& rScrPos, const Size& rScrSize,
- tools::Long nDimIndex, ScDPObject* pDPObj)
+bool lcl_FillDPFieldPopupData(tools::Long nDimIndex, ScDPObject* pDPObj,
+ DPFieldPopupData& rDPData, bool& bDimOrientNotPage)
{
- std::unique_ptr<DPFieldPopupData> pDPData(new DPFieldPopupData);
- pDPData->mnDim = nDimIndex;
+ if (!pDPObj)
+ return false;
+
+ rDPData.mnDim = nDimIndex;
pDPObj->GetSource();
bool bIsDataLayout;
- OUString aDimName = pDPObj->GetDimName(pDPData->mnDim, bIsDataLayout);
+ OUString aDimName = pDPObj->GetDimName(rDPData.mnDim, bIsDataLayout);
pDPObj->BuildAllDimensionMembers();
const ScDPSaveData* pSaveData = pDPObj->GetSaveData();
const ScDPSaveDimension* pDim = pSaveData->GetExistingDimensionByName(aDimName);
if (!pDim)
// This should never happen.
- return;
+ return false;
- bool bDimOrientNotPage = pDim->GetOrientation() != DataPilotFieldOrientation_PAGE;
+ bDimOrientNotPage = pDim->GetOrientation() != DataPilotFieldOrientation_PAGE;
// We need to get the list of field members.
- pDPObj->FillLabelData(pDPData->mnDim, pDPData->maLabels);
- pDPData->mpDPObj = pDPObj;
+ pDPObj->FillLabelData(rDPData.mnDim, rDPData.maLabels);
+ rDPData.mpDPObj = pDPObj;
- const ScDPLabelData& rLabelData = pDPData->maLabels;
+ return true;
+}
+
+void ScGridWindow::DPLaunchMultiFieldPopupMenu(const Point& rScreenPosition, const Size& rScreenSize,
+ ScDPObject* pDPObj, DataPilotFieldOrientation nOrient)
+{
+ if (!pDPObj)
+ return;
+
+ pDPObj->GetSource();
+
+ std::unique_ptr<DPMultiFieldPopupData> pDPData(new DPMultiFieldPopupData);
+ pDPObj->GetFieldIdsNames(nOrient, pDPData->maFieldIndices, pDPData->maFieldNames);
+
+ if (pDPData->maFieldIndices.empty())
+ return;
+
+ tools::Long nDimIndex = pDPData->maFieldIndices[0];
+
+ bool bDimOrientNotPage = true;
+ if (!lcl_FillDPFieldPopupData(nDimIndex, pDPObj, *pDPData, bDimOrientNotPage))
+ return;
mpDPFieldPopup.reset();
weld::Window* pPopupParent = GetFrameWeld();
mpDPFieldPopup.reset(new ScCheckListMenuControl(pPopupParent, mrViewData,
- false, -1));
+ false, -1, true));
- mpDPFieldPopup->setExtendedData(std::move(pDPData));
- mpDPFieldPopup->setOKAction(new DPFieldPopupOKAction(this));
+ mpDPFieldPopup->addFields(pDPData->maFieldNames);
+ DPSetupFieldPopup(std::move(pDPData), bDimOrientNotPage, pDPObj, true);
+
+ DPConfigFieldPopup();
+
+ if (IsMouseCaptured())
+ ReleaseMouse();
+
+ tools::Rectangle aCellRect(rScreenPosition, rScreenSize);
+ mpDPFieldPopup->launch(pPopupParent, aCellRect);
+}
+
+void ScGridWindow::DPPopulateFieldMembers(const ScDPLabelData& rLabelData)
+{
+ // Populate field members.
+ size_t n = rLabelData.maMembers.size();
+ mpDPFieldPopup->setMemberSize(n);
+ for (size_t i = 0; i < n; ++i)
{
- // Populate field members.
- size_t n = rLabelData.maMembers.size();
- mpDPFieldPopup->setMemberSize(n);
- for (size_t i = 0; i < n; ++i)
- {
- const ScDPLabelData::Member& rMem = rLabelData.maMembers[i];
- OUString aName = rMem.getDisplayName();
- if (aName.isEmpty())
- // Use special string for an empty name.
- mpDPFieldPopup->addMember(ScResId(STR_EMPTYDATA), 0.0, rMem.mbVisible, false);
- else
- mpDPFieldPopup->addMember(rMem.getDisplayName(), 0.0, rMem.mbVisible, false);
- }
+ const ScDPLabelData::Member& rMem = rLabelData.maMembers[i];
+ OUString aName = rMem.getDisplayName();
+ if (aName.isEmpty())
+ // Use special string for an empty name.
+ mpDPFieldPopup->addMember(ScResId(STR_EMPTYDATA), 0.0, rMem.mbVisible, false);
+ else
+ mpDPFieldPopup->addMember(rMem.getDisplayName(), 0.0, rMem.mbVisible, false);
}
+}
+
+void ScGridWindow::DPSetupFieldPopup(std::unique_ptr<ScCheckListMenuControl::ExtendedData> pDPData,
+ bool bDimOrientNotPage, ScDPObject* pDPObj,
+ bool bMultiField)
+{
+ if (!mpDPFieldPopup || !pDPObj)
+ return;
+
+ const ScDPLabelData& rLabelData = static_cast<DPFieldPopupData*>(pDPData.get())->maLabels;
+ const tools::Long nDimIndex = static_cast<DPFieldPopupData*>(pDPData.get())->mnDim;
+ mpDPFieldPopup->setExtendedData(std::move(pDPData));
+
+ if (bMultiField)
+ mpDPFieldPopup->setFieldChangedAction(new DPFieldChangedAction(this));
+
+ mpDPFieldPopup->setOKAction(new DPFieldPopupOKAction(this));
+ DPPopulateFieldMembers(rLabelData);
if (bDimOrientNotPage)
{
@@ -531,17 +691,76 @@ void ScGridWindow::DPLaunchFieldPopupMenu(const Point& rScrPos, const Size& rScr
}
mpDPFieldPopup->initMembers();
+}
+
+void ScGridWindow::DPConfigFieldPopup()
+{
+ if (!mpDPFieldPopup)
+ return;
- tools::Rectangle aCellRect(rScrPos, rScrSize);
ScCheckListMenuControl::Config aConfig;
aConfig.mbAllowEmptySet = false;
aConfig.mbRTL = mrViewData.GetDocument().IsLayoutRTL(mrViewData.GetTabNo());
mpDPFieldPopup->setConfig(aConfig);
+}
+
+void ScGridWindow::DPLaunchFieldPopupMenu(const Point& rScrPos, const Size& rScrSize,
+ tools::Long nDimIndex, ScDPObject* pDPObj)
+{
+ std::unique_ptr<DPFieldPopupData> pDPData(new DPFieldPopupData);
+ bool bDimOrientNotPage = true;
+ if (!lcl_FillDPFieldPopupData(nDimIndex, pDPObj, *pDPData, bDimOrientNotPage))
+ return;
+
+ mpDPFieldPopup.reset();
+
+ vcl::ILibreOfficeKitNotifier* pNotifier = nullptr;
+ if (comphelper::LibreOfficeKit::isActive())
+ pNotifier = SfxViewShell::Current();
+
+ weld::Window* pPopupParent = GetFrameWeld();
+ mpDPFieldPopup.reset(new ScCheckListMenuControl(pPopupParent, mrViewData,
+ false, -1, pNotifier));
+
+ DPSetupFieldPopup(std::move(pDPData), bDimOrientNotPage, pDPObj);
+
+ DPConfigFieldPopup();
+
if (IsMouseCaptured())
ReleaseMouse();
+
+ tools::Rectangle aCellRect(rScrPos, rScrSize);
mpDPFieldPopup->launch(pPopupParent, aCellRect);
}
+void ScGridWindow::UpdateDPPopupMenuForFieldChange()
+{
+ if (!mpDPFieldPopup)
+ return;
+
+ DPMultiFieldPopupData* pDPData = static_cast<DPMultiFieldPopupData*>(mpDPFieldPopup->getExtendedData());
+ if (!pDPData)
+ return;
+
+ if (pDPData->maFieldIndices.empty())
+ return;
+
+ tools::Long nIndex = mpDPFieldPopup->getField();
+ tools::Long nDimIndex = pDPData->maFieldIndices[nIndex];
+ if (nDimIndex == pDPData->mnDim)
+ return;
+
+ bool bDimOrientNotPage = true;
+ if (!lcl_FillDPFieldPopupData(nDimIndex, pDPData->mpDPObj, *pDPData, bDimOrientNotPage))
+ return;
+
+ mpDPFieldPopup->clearMembers();
+
+ DPPopulateFieldMembers(pDPData->maLabels);
+
+ mpDPFieldPopup->initMembers();
+}
+
void ScGridWindow::UpdateDPFromFieldPopupMenu()
{
typedef std::unordered_map<OUString, OUString> MemNameMapType;
diff --git a/sc/source/ui/view/gridwin4.cxx b/sc/source/ui/view/gridwin4.cxx
index fa78ad78312e..16d37447072d 100644
--- a/sc/source/ui/view/gridwin4.cxx
+++ b/sc/source/ui/view/gridwin4.cxx
@@ -2176,7 +2176,7 @@ void ScGridWindow::DrawButtons(SCCOL nX1, SCCOL nX2, const ScTableInfo& rTabInfo
}
}
- if ( pRowInfo[nArrY].bPivotButton && pRowInfo[nArrY].bChanged )
+ if ( (pRowInfo[nArrY].bPivotToggle || pRowInfo[nArrY].bPivotButton) && pRowInfo[nArrY].bChanged )
{
RowInfo* pThisRowInfo = &pRowInfo[nArrY];
nRow = pThisRowInfo->nRowNo;
@@ -2194,12 +2194,22 @@ void ScGridWindow::DrawButtons(SCCOL nX1, SCCOL nX2, const ScTableInfo& rTabInfo
tools::Long nPosY = aScrPos.Y();
// bLayoutRTL is handled in setBoundingBox
- OUString aStr = rDoc.GetString(nCol, nRow, nTab);
- aCellBtn.setText(aStr);
+ bool bDrawToggle = pInfo->bPivotCollapseButton || pInfo->bPivotExpandButton;
+ if (!bDrawToggle)
+ {
+ OUString aStr = rDoc.GetString(nCol, nRow, nTab);
+ aCellBtn.setText(aStr);
+ }
+
+ sal_uInt16 nIndent = 0;
+ if (const ScIndentItem* pIndentItem = rDoc.GetAttr(nCol, nRow, nTab, ATTR_INDENT))
+ nIndent = pIndentItem->GetValue();
aCellBtn.setBoundingBox(Point(nPosX, nPosY), Size(nSizeX-1, nSizeY-1), bLayoutRTL);
aCellBtn.setPopupLeft(false); // DataPilot popup is always right-aligned for now
aCellBtn.setDrawBaseButton(pInfo->bPivotButton);
aCellBtn.setDrawPopupButton(pInfo->bPivotPopupButton);
+ aCellBtn.setDrawPopupButtonMulti(pInfo->bPivotPopupButtonMulti);
+ aCellBtn.setDrawToggleButton(bDrawToggle, pInfo->bPivotCollapseButton, nIndent);
aCellBtn.setHasHiddenMember(pInfo->bFilterActive);
aCellBtn.draw();
}
diff --git a/sc/uiconfig/scalc/ui/datafieldoptionsdialog.ui b/sc/uiconfig/scalc/ui/datafieldoptionsdialog.ui
index 3dd16082e234..38ce3762834b 100644
--- a/sc/uiconfig/scalc/ui/datafieldoptionsdialog.ui
+++ b/sc/uiconfig/scalc/ui/datafieldoptionsdialog.ui
@@ -291,6 +291,7 @@
<item translatable="yes" context="datafieldoptionsdialog|layout">Tabular layout</item>
<item translatable="yes" context="datafieldoptionsdialog|layout">Outline layout with subtotals at the top</item>
<item translatable="yes" context="datafieldoptionsdialog|layout">Outline layout with subtotals at the bottom</item>
+ <item translatable="yes" context="datafieldoptionsdialog|layout">Compact layout</item>
</items>
<child internal-child="accessible">
<object class="AtkObject" id="layout-atkobject">
diff --git a/sc/uiconfig/scalc/ui/filterdropdown.ui b/sc/uiconfig/scalc/ui/filterdropdown.ui
index 726cab40de94..d7f081749ef5 100644
--- a/sc/uiconfig/scalc/ui/filterdropdown.ui
+++ b/sc/uiconfig/scalc/ui/filterdropdown.ui
@@ -150,6 +150,30 @@
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
+ <object class="GtkLabel" id="select_field_label">
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="filterdropdown|select_field_label">Select Field</property>
+ <property name="mnemonic_widget">multi_field_combo</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="multi_field_combo">
+ <property name="can-focus">True</property>
+ <property name="no-show-all">True</property>
+ <property name="tooltip-text" translatable="yes" context="filterdropdown|multi_field_combo">Select Field</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
<object class="GtkEntry" id="search_edit">
<property name="can-focus">True</property>
<property name="no-show-all">True</property>
@@ -160,7 +184,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
- <property name="position">0</property>
+ <property name="position">2</property>
</packing>
</child>
<child>
@@ -359,7 +383,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
- <property name="position">1</property>
+ <property name="position">3</property>
</packing>
</child>
<child>
@@ -402,7 +426,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
- <property name="position">2</property>
+ <property name="position">4</property>
</packing>
</child>
</object>
diff --git a/sc/uiconfig/scalc/ui/pivottablelayoutdialog.ui b/sc/uiconfig/scalc/ui/pivottablelayoutdialog.ui
index e77bc24f2f10..3f1e626d1b66 100644
--- a/sc/uiconfig/scalc/ui/pivottablelayoutdialog.ui
+++ b/sc/uiconfig/scalc/ui/pivottablelayoutdialog.ui
@@ -735,6 +735,25 @@
<property name="top_attach">2</property>
</packing>
</child>
+ <child>
+ <object class="GtkCheckButton" id="check-show-expand-collapse">
+ <property name="label" translatable="yes" context="pivottablelayoutdialog|check-show-expand-collapse">Show expand/collapse buttons</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="check-show-expand-collapse-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="pivottablelayoutdialog|extended_tip|check-show-expand-collapse">Select this check box to show expand/collapse buttons for field members</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">3</property>
+ </packing>
+ </child>
</object>
</child>
<child type="label">
diff --git a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng
index fa38694f9cff..f772ce223f35 100644
--- a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng
+++ b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng
@@ -2525,6 +2525,29 @@ xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.
</rng:optional>
</rng:define>
+ <!-- TODO no proposal -->
+ <rng:define name="table-data-pilot-table-attlist" combine="interleave">
+ <rng:optional>
+ <rng:attribute name="loext:show-drill-down-buttons">
+ <rng:ref name="boolean"/>
+ </rng:attribute>
+ </rng:optional>
+ </rng:define>
+
+ <!-- TODO no proposal for pivot table compact layout-->
+ <rng:define name="table-data-pilot-layout-info-attlist" combine="interleave">
+ <rng:optional>
+ <rng:attribute name="loext:layout-mode">
+ <rng:choice>
+ <rng:value>tabular-layout</rng:value>
+ <rng:value>outline-subtotals-top</rng:value>
+ <rng:value>outline-subtotals-bottom</rng:value>
+ <rng:value>compact-layout</rng:value>
+ </rng:choice>
+ </rng:attribute>
+ </rng:optional>
+ </rng:define>
+
<!-- TODO no proposal, 9009663d -->
<rng:define name="chart-chart-attlist" combine="interleave">
<rng:optional>
diff --git a/xmloff/source/core/xmltoken.cxx b/xmloff/source/core/xmltoken.cxx
index 43d47aec8cae..b42aa9c43f42 100644
--- a/xmloff/source/core/xmltoken.cxx
+++ b/xmloff/source/core/xmltoken.cxx
@@ -2588,6 +2588,7 @@ namespace xmloff::token {
TOKEN( "tabular-layout", XML_TABULAR_LAYOUT ),
TOKEN( "outline-subtotals-top", XML_OUTLINE_SUBTOTALS_TOP ),
TOKEN( "outline-subtotals-bottom", XML_OUTLINE_SUBTOTALS_BOTTOM ),
+ TOKEN( "compact-layout", XML_COMPACT_LAYOUT ),
TOKEN( "layout-mode", XML_LAYOUT_MODE ),
TOKEN( "data-pilot-layout-info", XML_DATA_PILOT_LAYOUT_INFO ),
TOKEN( "symbol-color", XML_SYMBOL_COLOR ),
@@ -2770,6 +2771,7 @@ namespace xmloff::token {
TOKEN( "show-filter-button", XML_SHOW_FILTER_BUTTON ),
TOKEN( "drill-down-on-double-click", XML_DRILL_DOWN_ON_DOUBLE_CLICK ),
+ TOKEN( "show-drill-down-buttons", XML_SHOW_DRILL_DOWN_BUTTONS ),
TOKEN( "header-grid-layout", XML_HEADER_GRID_LAYOUT ),
TOKEN( "grouped-by", XML_GROUPED_BY ),
TOKEN( "days", XML_DAYS ),
diff --git a/xmloff/source/token/tokens.txt b/xmloff/source/token/tokens.txt
index 5e60e58b2b73..01b9515172e2 100644
--- a/xmloff/source/token/tokens.txt
+++ b/xmloff/source/token/tokens.txt
@@ -2435,6 +2435,7 @@ add-empty-lines
tabular-layout
outline-subtotals-top
outline-subtotals-bottom
+compact-layout
layout-mode
data-pilot-layout-info
symbol-color
@@ -2596,6 +2597,7 @@ GNM_STEP_CENTER_Y_DUMMY
N_DB_OASIS_DUMMY
show-filter-button
drill-down-on-double-click
+show-drill-down-buttons
header-grid-layout
grouped-by
days