diff options
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 Binary files differnew file mode 100644 index 000000000000..673ba7e0b60a --- /dev/null +++ b/sc/qa/unit/data/xlsx/pivot-table/pivotcompact.xlsx 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 |