1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #undef SC_DLLIMPLEMENTATION
21 
22 #include <pvfundlg.hxx>
23 
24 #include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp>
25 #include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp>
26 #include <com/sun/star/sheet/DataPilotFieldLayoutMode.hpp>
27 #include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
28 #include <com/sun/star/sheet/DataPilotFieldShowItemsMode.hpp>
29 
30 #include <osl/diagnose.h>
31 
32 #include <scresid.hxx>
33 #include <dpobject.hxx>
34 #include <dpsave.hxx>
35 #include <pvfundlg.hrc>
36 #include <globstr.hrc>
37 #include <dputil.hxx>
38 
39 #include <vector>
40 
41 using namespace ::com::sun::star::sheet;
42 
43 using ::com::sun::star::uno::Sequence;
44 using ::std::vector;
45 
46 namespace {
47 
48 /** Appends all strings from the Sequence to the list box.
49 
50     Empty strings are replaced by a localized "(empty)" entry and inserted at
51     the specified position.
52 
53     @return  true = The passed string list contains an empty string entry.
54  */
55 
lclFillListBox(weld::ComboBox & rLBox,const Sequence<OUString> & rStrings)56 bool lclFillListBox(weld::ComboBox& rLBox, const Sequence< OUString >& rStrings)
57 {
58     bool bEmpty = false;
59     for (const OUString& str : rStrings)
60     {
61         if (!str.isEmpty())
62             rLBox.append_text(str);
63         else
64         {
65             rLBox.append_text(ScResId(STR_EMPTYDATA));
66             bEmpty = true;
67         }
68     }
69     return bEmpty;
70 }
71 
lclFillListBox(weld::ComboBox & rLBox,const vector<ScDPLabelData::Member> & rMembers,int nEmptyPos)72 bool lclFillListBox(weld::ComboBox& rLBox, const vector<ScDPLabelData::Member>& rMembers, int nEmptyPos)
73 {
74     bool bEmpty = false;
75     vector<ScDPLabelData::Member>::const_iterator itr = rMembers.begin(), itrEnd = rMembers.end();
76     for (; itr != itrEnd; ++itr)
77     {
78         OUString aName = itr->getDisplayName();
79         if (!aName.isEmpty())
80             rLBox.append_text(aName);
81         else
82         {
83             rLBox.insert_text(nEmptyPos, ScResId(STR_EMPTYDATA));
84             bEmpty = true;
85         }
86     }
87     return bEmpty;
88 }
89 
lclFillListBox(weld::TreeView & rLBox,const vector<ScDPLabelData::Member> & rMembers)90 bool lclFillListBox(weld::TreeView& rLBox, const vector<ScDPLabelData::Member>& rMembers)
91 {
92     bool bEmpty = false;
93     for (const auto& rMember : rMembers)
94     {
95         rLBox.append();
96         int pos = rLBox.n_children() - 1;
97         rLBox.set_toggle(pos, TRISTATE_FALSE, 0);
98         OUString aName = rMember.getDisplayName();
99         if (!aName.isEmpty())
100             rLBox.set_text(pos, aName, 1);
101         else
102         {
103             rLBox.set_text(pos, ScResId(STR_EMPTYDATA), 1);
104             bEmpty = true;
105         }
106     }
107     return bEmpty;
108 }
109 
110 /** This table represents the order of the strings in the resource string array. */
111 static const PivotFunc spnFunctions[] =
112 {
113     PivotFunc::Sum,
114     PivotFunc::Count,
115     PivotFunc::Average,
116     PivotFunc::Median,
117     PivotFunc::Max,
118     PivotFunc::Min,
119     PivotFunc::Product,
120     PivotFunc::CountNum,
121     PivotFunc::StdDev,
122     PivotFunc::StdDevP,
123     PivotFunc::StdVar,
124     PivotFunc::StdVarP
125 };
126 
127 const sal_uInt16 SC_BASEITEM_PREV_POS = 0;
128 const sal_uInt16 SC_BASEITEM_NEXT_POS = 1;
129 const sal_uInt16 SC_BASEITEM_USER_POS = 2;
130 
131 const sal_uInt16 SC_SORTNAME_POS = 0;
132 const sal_uInt16 SC_SORTDATA_POS = 1;
133 
134 const long SC_SHOW_DEFAULT = 10;
135 
136 } // namespace
137 
ScDPFunctionListBox(std::unique_ptr<weld::TreeView> xControl)138 ScDPFunctionListBox::ScDPFunctionListBox(std::unique_ptr<weld::TreeView> xControl)
139     : m_xControl(std::move(xControl))
140 {
141     FillFunctionNames();
142 }
143 
SetSelection(PivotFunc nFuncMask)144 void ScDPFunctionListBox::SetSelection( PivotFunc nFuncMask )
145 {
146     if( (nFuncMask == PivotFunc::NONE) || (nFuncMask == PivotFunc::Auto) )
147         m_xControl->unselect_all();
148     else
149     {
150         for( sal_Int32 nEntry = 0, nCount = m_xControl->n_children(); nEntry < nCount; ++nEntry )
151         {
152             if (bool(nFuncMask & spnFunctions[ nEntry ]))
153                 m_xControl->select(nEntry);
154             else
155                 m_xControl->unselect(nEntry);
156         }
157     }
158 }
159 
GetSelection() const160 PivotFunc ScDPFunctionListBox::GetSelection() const
161 {
162     PivotFunc nFuncMask = PivotFunc::NONE;
163     std::vector<int> aRows = m_xControl->get_selected_rows();
164     for (int nSel : aRows)
165         nFuncMask |= spnFunctions[nSel];
166     return nFuncMask;
167 }
168 
FillFunctionNames()169 void ScDPFunctionListBox::FillFunctionNames()
170 {
171     OSL_ENSURE( !m_xControl->n_children(), "ScDPMultiFuncListBox::FillFunctionNames - do not add texts to resource" );
172     m_xControl->clear();
173     m_xControl->freeze();
174     for (size_t nIndex = 0; nIndex < SAL_N_ELEMENTS(SCSTR_DPFUNCLISTBOX); ++nIndex)
175         m_xControl->append_text(ScResId(SCSTR_DPFUNCLISTBOX[nIndex]));
176     m_xControl->thaw();
177     assert(m_xControl->n_children() == SAL_N_ELEMENTS(spnFunctions));
178 }
179 
180 namespace
181 {
FromDataPilotFieldReferenceType(int eMode)182     int FromDataPilotFieldReferenceType(int eMode)
183     {
184         switch (eMode)
185         {
186             case DataPilotFieldReferenceType::NONE:
187                 return 0;
188             case DataPilotFieldReferenceType::ITEM_DIFFERENCE:
189                 return 1;
190             case DataPilotFieldReferenceType::ITEM_PERCENTAGE:
191                 return 2;
192             case DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE:
193                 return 3;
194             case DataPilotFieldReferenceType::RUNNING_TOTAL:
195                 return 4;
196             case DataPilotFieldReferenceType::ROW_PERCENTAGE:
197                 return 5;
198             case DataPilotFieldReferenceType::COLUMN_PERCENTAGE:
199                 return 6;
200             case DataPilotFieldReferenceType::TOTAL_PERCENTAGE:
201                 return 7;
202             case DataPilotFieldReferenceType::INDEX:
203                 return 8;
204         }
205         return -1;
206     }
207 
ToDataPilotFieldReferenceType(int nPos)208     int ToDataPilotFieldReferenceType(int nPos)
209     {
210         switch (nPos)
211         {
212             case 0:
213                 return DataPilotFieldReferenceType::NONE;
214             case 1:
215                 return DataPilotFieldReferenceType::ITEM_DIFFERENCE;
216             case 2:
217                 return DataPilotFieldReferenceType::ITEM_PERCENTAGE;
218             case 3:
219                 return DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE;
220             case 4:
221                 return DataPilotFieldReferenceType::RUNNING_TOTAL;
222             case 5:
223                 return DataPilotFieldReferenceType::ROW_PERCENTAGE;
224             case 6:
225                 return DataPilotFieldReferenceType::COLUMN_PERCENTAGE;
226             case 7:
227                 return DataPilotFieldReferenceType::TOTAL_PERCENTAGE;
228             case 8:
229                 return DataPilotFieldReferenceType::INDEX;
230         }
231         return DataPilotFieldReferenceType::NONE;
232 
233     }
234 }
235 
ScDPFunctionDlg(weld::Widget * pParent,const ScDPLabelDataVector & rLabelVec,const ScDPLabelData & rLabelData,const ScPivotFuncData & rFuncData)236 ScDPFunctionDlg::ScDPFunctionDlg(
237         weld::Widget* pParent, const ScDPLabelDataVector& rLabelVec,
238         const ScDPLabelData& rLabelData, const ScPivotFuncData& rFuncData)
239     : GenericDialogController(pParent, "modules/scalc/ui/datafielddialog.ui", "DataFieldDialog")
240     , mxLbFunc(new ScDPFunctionListBox(m_xBuilder->weld_tree_view("functions")))
241     , mxFtName(m_xBuilder->weld_label("name"))
242     , mxLbType(m_xBuilder->weld_combo_box("type"))
243     , mxFtBaseField(m_xBuilder->weld_label("basefieldft"))
244     , mxLbBaseField(m_xBuilder->weld_combo_box("basefield"))
245     , mxFtBaseItem(m_xBuilder->weld_label("baseitemft"))
246     , mxLbBaseItem(m_xBuilder->weld_combo_box("baseitem"))
247     , mxBtnOk(m_xBuilder->weld_button("ok"))
248     , mrLabelVec(rLabelVec)
249     , mbEmptyItem(false)
250 {
251     mxLbFunc->set_size_request(-1, mxLbFunc->get_height_rows(8));
252 
253     Init(rLabelData, rFuncData);
254 }
255 
~ScDPFunctionDlg()256 ScDPFunctionDlg::~ScDPFunctionDlg()
257 {
258 }
259 
GetFuncMask() const260 PivotFunc ScDPFunctionDlg::GetFuncMask() const
261 {
262     return mxLbFunc->GetSelection();
263 }
264 
GetFieldRef() const265 DataPilotFieldReference ScDPFunctionDlg::GetFieldRef() const
266 {
267     DataPilotFieldReference aRef;
268 
269     aRef.ReferenceType = ToDataPilotFieldReferenceType(mxLbType->get_active());
270     aRef.ReferenceField = GetBaseFieldName(mxLbBaseField->get_active_text());
271 
272     sal_Int32 nBaseItemPos = mxLbBaseItem->get_active();
273     switch( nBaseItemPos )
274     {
275         case SC_BASEITEM_PREV_POS:
276             aRef.ReferenceItemType = DataPilotFieldReferenceItemType::PREVIOUS;
277         break;
278         case SC_BASEITEM_NEXT_POS:
279             aRef.ReferenceItemType = DataPilotFieldReferenceItemType::NEXT;
280         break;
281         default:
282         {
283             aRef.ReferenceItemType = DataPilotFieldReferenceItemType::NAMED;
284             if( !mbEmptyItem || (nBaseItemPos > SC_BASEITEM_USER_POS) )
285                 aRef.ReferenceItemName = GetBaseItemName(mxLbBaseItem->get_active_text());
286         }
287     }
288 
289     return aRef;
290 }
291 
Init(const ScDPLabelData & rLabelData,const ScPivotFuncData & rFuncData)292 void ScDPFunctionDlg::Init( const ScDPLabelData& rLabelData, const ScPivotFuncData& rFuncData )
293 {
294     // list box
295     PivotFunc nFuncMask = (rFuncData.mnFuncMask == PivotFunc::NONE) ? PivotFunc::Sum : rFuncData.mnFuncMask;
296     mxLbFunc->SetSelection( nFuncMask );
297 
298     // field name
299     mxFtName->set_label(rLabelData.getDisplayName());
300 
301     // handlers
302     mxLbFunc->connect_row_activated( LINK( this, ScDPFunctionDlg, DblClickHdl ) );
303     mxLbType->connect_changed( LINK( this, ScDPFunctionDlg, SelectHdl ) );
304     mxLbBaseField->connect_changed( LINK( this, ScDPFunctionDlg, SelectHdl ) );
305 
306     // base field list box
307     OUString aSelectedEntry;
308     for( const auto& rxLabel : mrLabelVec )
309     {
310         mxLbBaseField->append_text(rxLabel->getDisplayName());
311         maBaseFieldNameMap.emplace(rxLabel->getDisplayName(), rxLabel->maName);
312         if (rxLabel->maName == rFuncData.maFieldRef.ReferenceField)
313             aSelectedEntry = rxLabel->getDisplayName();
314     }
315 
316     // select field reference type
317     mxLbType->set_active(FromDataPilotFieldReferenceType(rFuncData.maFieldRef.ReferenceType));
318     SelectHdl( *mxLbType );         // enables base field/item list boxes
319 
320     // select base field
321     mxLbBaseField->set_active_text(aSelectedEntry);
322     if (mxLbBaseField->get_active() == -1)
323         mxLbBaseField->set_active(0);
324     SelectHdl( *mxLbBaseField );    // fills base item list, selects base item
325 
326     // select base item
327     switch( rFuncData.maFieldRef.ReferenceItemType )
328     {
329         case DataPilotFieldReferenceItemType::PREVIOUS:
330             mxLbBaseItem->set_active( SC_BASEITEM_PREV_POS );
331         break;
332         case DataPilotFieldReferenceItemType::NEXT:
333             mxLbBaseItem->set_active( SC_BASEITEM_NEXT_POS );
334         break;
335         default:
336         {
337             if( mbEmptyItem && rFuncData.maFieldRef.ReferenceItemName.isEmpty() )
338             {
339                 // select special "(empty)" entry added before other items
340                 mxLbBaseItem->set_active( SC_BASEITEM_USER_POS );
341             }
342             else
343             {
344                 sal_Int32 nStartPos = mbEmptyItem ? (SC_BASEITEM_USER_POS + 1) : SC_BASEITEM_USER_POS;
345                 sal_Int32 nPos = FindBaseItemPos( rFuncData.maFieldRef.ReferenceItemName, nStartPos );
346                 if( nPos == -1)
347                     nPos = (mxLbBaseItem->get_count() > SC_BASEITEM_USER_POS) ? SC_BASEITEM_USER_POS : SC_BASEITEM_PREV_POS;
348                 mxLbBaseItem->set_active( nPos );
349             }
350         }
351     }
352 }
353 
GetBaseFieldName(const OUString & rLayoutName) const354 const OUString& ScDPFunctionDlg::GetBaseFieldName(const OUString& rLayoutName) const
355 {
356     NameMapType::const_iterator itr = maBaseFieldNameMap.find(rLayoutName);
357     return itr == maBaseFieldNameMap.end() ? rLayoutName : itr->second;
358 }
359 
GetBaseItemName(const OUString & rLayoutName) const360 const OUString& ScDPFunctionDlg::GetBaseItemName(const OUString& rLayoutName) const
361 {
362     NameMapType::const_iterator itr = maBaseItemNameMap.find(rLayoutName);
363     return itr == maBaseItemNameMap.end() ? rLayoutName : itr->second;
364 }
365 
FindBaseItemPos(const OUString & rEntry,sal_Int32 nStartPos) const366 sal_Int32 ScDPFunctionDlg::FindBaseItemPos( const OUString& rEntry, sal_Int32 nStartPos ) const
367 {
368     sal_Int32 nPos = nStartPos;
369     bool bFound = false;
370     while (nPos < mxLbBaseItem->get_count())
371     {
372         // translate the displayed field name back to its original field name.
373         const OUString& rInName = mxLbBaseItem->get_text(nPos);
374         const OUString& rName = GetBaseItemName(rInName);
375         if (rName == rEntry)
376         {
377             bFound = true;
378             break;
379         }
380         ++nPos;
381     }
382     return bFound ? nPos : -1;
383 }
384 
IMPL_LINK(ScDPFunctionDlg,SelectHdl,weld::ComboBox &,rLBox,void)385 IMPL_LINK( ScDPFunctionDlg, SelectHdl, weld::ComboBox&, rLBox, void )
386 {
387     if (&rLBox == mxLbType.get())
388     {
389         bool bEnableField, bEnableItem;
390         switch (ToDataPilotFieldReferenceType(mxLbType->get_active()))
391         {
392             case DataPilotFieldReferenceType::ITEM_DIFFERENCE:
393             case DataPilotFieldReferenceType::ITEM_PERCENTAGE:
394             case DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE:
395                 bEnableField = bEnableItem = true;
396             break;
397 
398             case DataPilotFieldReferenceType::RUNNING_TOTAL:
399                 bEnableField = true;
400                 bEnableItem = false;
401             break;
402 
403             default:
404                 bEnableField = bEnableItem = false;
405         }
406 
407         bEnableField &= (mxLbBaseField->get_count() > 0);
408         mxFtBaseField->set_sensitive( bEnableField );
409         mxLbBaseField->set_sensitive( bEnableField );
410 
411         bEnableItem &= bEnableField;
412         mxFtBaseItem->set_sensitive( bEnableItem );
413         mxLbBaseItem->set_sensitive( bEnableItem );
414     }
415     else if (&rLBox == mxLbBaseField.get())
416     {
417         // keep "previous" and "next" entries
418         while (mxLbBaseItem->get_count() > SC_BASEITEM_USER_POS)
419              mxLbBaseItem->remove(SC_BASEITEM_USER_POS);
420 
421         // update item list for current base field
422         mbEmptyItem = false;
423         size_t nBasePos = mxLbBaseField->get_active();
424         if (nBasePos < mrLabelVec.size())
425         {
426             const vector<ScDPLabelData::Member>& rMembers = mrLabelVec[nBasePos]->maMembers;
427             mbEmptyItem = lclFillListBox(*mxLbBaseItem, rMembers, SC_BASEITEM_USER_POS);
428             // build cache for base names.
429             NameMapType aMap;
430             for (const auto& rMember : rMembers)
431                 aMap.emplace(rMember.getDisplayName(), rMember.maName);
432             maBaseItemNameMap.swap(aMap);
433         }
434 
435         // select base item
436         sal_uInt16 nItemPos = (mxLbBaseItem->get_count() > SC_BASEITEM_USER_POS) ? SC_BASEITEM_USER_POS : SC_BASEITEM_PREV_POS;
437         mxLbBaseItem->set_active( nItemPos );
438     }
439 }
440 
IMPL_LINK_NOARG(ScDPFunctionDlg,DblClickHdl,weld::TreeView &,bool)441 IMPL_LINK_NOARG(ScDPFunctionDlg, DblClickHdl, weld::TreeView&, bool)
442 {
443     m_xDialog->response(RET_OK);
444     return true;
445 }
446 
ScDPSubtotalDlg(weld::Widget * pParent,ScDPObject & rDPObj,const ScDPLabelData & rLabelData,const ScPivotFuncData & rFuncData,const ScDPNameVec & rDataFields,bool bEnableLayout)447 ScDPSubtotalDlg::ScDPSubtotalDlg(weld::Widget* pParent, ScDPObject& rDPObj,
448         const ScDPLabelData& rLabelData, const ScPivotFuncData& rFuncData,
449         const ScDPNameVec& rDataFields, bool bEnableLayout)
450     : GenericDialogController(pParent, "modules/scalc/ui/pivotfielddialog.ui", "PivotFieldDialog")
451     , mrDPObj(rDPObj)
452     , mrDataFields(rDataFields)
453     , maLabelData(rLabelData)
454     , mbEnableLayout(bEnableLayout)
455     , mxRbNone(m_xBuilder->weld_radio_button("none"))
456     , mxRbAuto(m_xBuilder->weld_radio_button("auto"))
457     , mxRbUser(m_xBuilder->weld_radio_button("user"))
458     , mxLbFunc(new ScDPFunctionListBox(m_xBuilder->weld_tree_view("functions")))
459     , mxFtName(m_xBuilder->weld_label("name"))
460     , mxCbShowAll(m_xBuilder->weld_check_button("showall"))
461     , mxBtnOk(m_xBuilder->weld_button("ok"))
462     , mxBtnOptions(m_xBuilder->weld_button("options"))
463 {
464     mxLbFunc->set_selection_mode(SelectionMode::Multiple);
465     mxLbFunc->set_size_request(-1, mxLbFunc->get_height_rows(8));
466     Init(rLabelData, rFuncData);
467 }
468 
~ScDPSubtotalDlg()469 ScDPSubtotalDlg::~ScDPSubtotalDlg()
470 {
471 }
472 
GetFuncMask() const473 PivotFunc ScDPSubtotalDlg::GetFuncMask() const
474 {
475     PivotFunc nFuncMask = PivotFunc::NONE;
476 
477     if (mxRbAuto->get_active())
478         nFuncMask = PivotFunc::Auto;
479     else if (mxRbUser->get_active())
480         nFuncMask = mxLbFunc->GetSelection();
481 
482     return nFuncMask;
483 }
484 
FillLabelData(ScDPLabelData & rLabelData) const485 void ScDPSubtotalDlg::FillLabelData( ScDPLabelData& rLabelData ) const
486 {
487     rLabelData.mnFuncMask = GetFuncMask();
488     rLabelData.mnUsedHier = maLabelData.mnUsedHier;
489     rLabelData.mbShowAll = mxCbShowAll->get_active();
490     rLabelData.maMembers = maLabelData.maMembers;
491     rLabelData.maSortInfo = maLabelData.maSortInfo;
492     rLabelData.maLayoutInfo = maLabelData.maLayoutInfo;
493     rLabelData.maShowInfo = maLabelData.maShowInfo;
494     rLabelData.mbRepeatItemLabels = maLabelData.mbRepeatItemLabels;
495 }
496 
Init(const ScDPLabelData & rLabelData,const ScPivotFuncData & rFuncData)497 void ScDPSubtotalDlg::Init( const ScDPLabelData& rLabelData, const ScPivotFuncData& rFuncData )
498 {
499     // field name
500     mxFtName->set_label(rLabelData.getDisplayName());
501 
502     // radio buttons
503     mxRbNone->connect_clicked( LINK( this, ScDPSubtotalDlg, RadioClickHdl ) );
504     mxRbAuto->connect_clicked( LINK( this, ScDPSubtotalDlg, RadioClickHdl ) );
505     mxRbUser->connect_clicked( LINK( this, ScDPSubtotalDlg, RadioClickHdl ) );
506 
507     weld::RadioButton* pRBtn = nullptr;
508     switch( rFuncData.mnFuncMask )
509     {
510         case PivotFunc::NONE:   pRBtn = mxRbNone.get();  break;
511         case PivotFunc::Auto:   pRBtn = mxRbAuto.get();  break;
512         default:                pRBtn = mxRbUser.get();
513     }
514     pRBtn->set_active(true);
515     RadioClickHdl(*pRBtn);
516 
517     // list box
518     mxLbFunc->SetSelection( rFuncData.mnFuncMask );
519     mxLbFunc->connect_row_activated( LINK( this, ScDPSubtotalDlg, DblClickHdl ) );
520 
521     // show all
522     mxCbShowAll->set_active( rLabelData.mbShowAll );
523 
524     // options
525     mxBtnOptions->connect_clicked( LINK( this, ScDPSubtotalDlg, ClickHdl ) );
526 }
527 
IMPL_LINK(ScDPSubtotalDlg,RadioClickHdl,weld::Button &,rBtn,void)528 IMPL_LINK(ScDPSubtotalDlg, RadioClickHdl, weld::Button&, rBtn, void)
529 {
530     mxLbFunc->set_sensitive(&rBtn == mxRbUser.get());
531 }
532 
IMPL_LINK_NOARG(ScDPSubtotalDlg,DblClickHdl,weld::TreeView &,bool)533 IMPL_LINK_NOARG(ScDPSubtotalDlg, DblClickHdl, weld::TreeView&, bool)
534 {
535     m_xDialog->response(RET_OK);
536     return true;
537 }
538 
IMPL_LINK(ScDPSubtotalDlg,ClickHdl,weld::Button &,rBtn,void)539 IMPL_LINK(ScDPSubtotalDlg, ClickHdl, weld::Button&, rBtn, void)
540 {
541     if (&rBtn == mxBtnOptions.get())
542     {
543         ScDPSubtotalOptDlg aDlg(m_xDialog.get(), mrDPObj, maLabelData, mrDataFields, mbEnableLayout);
544         if (aDlg.run() == RET_OK)
545             aDlg.FillLabelData(maLabelData);
546     }
547 }
548 
549 namespace
550 {
FromDataPilotFieldLayoutMode(int eMode)551     int FromDataPilotFieldLayoutMode(int eMode)
552     {
553         switch (eMode)
554         {
555             case DataPilotFieldLayoutMode::TABULAR_LAYOUT:
556                 return 0;
557             case DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_TOP:
558                 return 1;
559             case DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_BOTTOM:
560                 return 2;
561         }
562         return -1;
563     }
564 
ToDataPilotFieldLayoutMode(int nPos)565     int ToDataPilotFieldLayoutMode(int nPos)
566     {
567         switch (nPos)
568         {
569             case 0:
570                 return DataPilotFieldLayoutMode::TABULAR_LAYOUT;
571             case 1:
572                 return DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_TOP;
573             case 2:
574                 return DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_BOTTOM;
575         }
576         return DataPilotFieldLayoutMode::TABULAR_LAYOUT;
577     }
578 
FromDataPilotFieldShowItemsMode(int eMode)579     int FromDataPilotFieldShowItemsMode(int eMode)
580     {
581         switch (eMode)
582         {
583             case DataPilotFieldShowItemsMode::FROM_TOP:
584                 return 0;
585             case DataPilotFieldShowItemsMode::FROM_BOTTOM:
586                 return 1;
587         }
588         return -1;
589     }
590 
ToDataPilotFieldShowItemsMode(int nPos)591     int ToDataPilotFieldShowItemsMode(int nPos)
592     {
593         switch (nPos)
594         {
595             case 0:
596                 return DataPilotFieldShowItemsMode::FROM_TOP;
597             case 1:
598                 return DataPilotFieldShowItemsMode::FROM_BOTTOM;
599         }
600         return DataPilotFieldShowItemsMode::FROM_TOP;
601     }
602 }
603 
ScDPSubtotalOptDlg(weld::Window * pParent,ScDPObject & rDPObj,const ScDPLabelData & rLabelData,const ScDPNameVec & rDataFields,bool bEnableLayout)604 ScDPSubtotalOptDlg::ScDPSubtotalOptDlg(weld::Window* pParent, ScDPObject& rDPObj,
605         const ScDPLabelData& rLabelData, const ScDPNameVec& rDataFields,
606         bool bEnableLayout )
607     : GenericDialogController(pParent, "modules/scalc/ui/datafieldoptionsdialog.ui",
608                               "DataFieldOptionsDialog")
609     , m_xLbSortBy(m_xBuilder->weld_combo_box("sortby"))
610     , m_xRbSortAsc(m_xBuilder->weld_radio_button("ascending"))
611     , m_xRbSortDesc(m_xBuilder->weld_radio_button("descending"))
612     , m_xRbSortMan(m_xBuilder->weld_radio_button("manual"))
613     , m_xLayoutFrame(m_xBuilder->weld_widget("layoutframe"))
614     , m_xLbLayout(m_xBuilder->weld_combo_box("layout"))
615     , m_xCbLayoutEmpty(m_xBuilder->weld_check_button("emptyline"))
616     , m_xCbRepeatItemLabels(m_xBuilder->weld_check_button("repeatitemlabels"))
617     , m_xCbShow(m_xBuilder->weld_check_button("show"))
618     , m_xNfShow(m_xBuilder->weld_spin_button("items"))
619     , m_xFtShow(m_xBuilder->weld_label("showft"))
620     , m_xFtShowFrom(m_xBuilder->weld_label("showfromft"))
621     , m_xLbShowFrom(m_xBuilder->weld_combo_box("from"))
622     , m_xFtShowUsing(m_xBuilder->weld_label("usingft"))
623     , m_xLbShowUsing(m_xBuilder->weld_combo_box("using"))
624     , m_xHideFrame(m_xBuilder->weld_widget("hideframe"))
625     , m_xLbHide(m_xBuilder->weld_tree_view("hideitems"))
626     , m_xFtHierarchy(m_xBuilder->weld_label("hierarchyft"))
627     , m_xLbHierarchy(m_xBuilder->weld_combo_box("hierarchy"))
628     , mrDPObj(rDPObj)
629     , maLabelData(rLabelData)
630 {
631     std::vector<int> aWidths;
632     aWidths.push_back(m_xLbHide->get_checkbox_column_width());
633     m_xLbHide->set_column_fixed_widths(aWidths);
634 
635     m_xLbSortBy->set_size_request(m_xLbSortBy->get_approximate_digit_width() * 18, -1);
636     m_xLbHide->set_size_request(-1, m_xLbHide->get_height_rows(5));
637     Init(rDataFields, bEnableLayout);
638 }
639 
~ScDPSubtotalOptDlg()640 ScDPSubtotalOptDlg::~ScDPSubtotalOptDlg()
641 {
642 }
643 
FillLabelData(ScDPLabelData & rLabelData) const644 void ScDPSubtotalOptDlg::FillLabelData( ScDPLabelData& rLabelData ) const
645 {
646     // *** SORTING ***
647 
648     if (m_xRbSortMan->get_active())
649         rLabelData.maSortInfo.Mode = DataPilotFieldSortMode::MANUAL;
650     else if (m_xLbSortBy->get_active() == SC_SORTNAME_POS)
651         rLabelData.maSortInfo.Mode = DataPilotFieldSortMode::NAME;
652     else
653         rLabelData.maSortInfo.Mode = DataPilotFieldSortMode::DATA;
654 
655     ScDPName aFieldName = GetFieldName(m_xLbSortBy->get_active_text());
656     if (!aFieldName.maName.isEmpty())
657     {
658         rLabelData.maSortInfo.Field =
659             ScDPUtil::createDuplicateDimensionName(aFieldName.maName, aFieldName.mnDupCount);
660         rLabelData.maSortInfo.IsAscending = m_xRbSortAsc->get_active();
661     }
662 
663     // *** LAYOUT MODE ***
664 
665     rLabelData.maLayoutInfo.LayoutMode = ToDataPilotFieldLayoutMode(m_xLbLayout->get_active());
666     rLabelData.maLayoutInfo.AddEmptyLines = m_xCbLayoutEmpty->get_active();
667     rLabelData.mbRepeatItemLabels = m_xCbRepeatItemLabels->get_active();
668 
669     // *** AUTO SHOW ***
670 
671     aFieldName = GetFieldName(m_xLbShowUsing->get_active_text());
672     if (!aFieldName.maName.isEmpty())
673     {
674         rLabelData.maShowInfo.IsEnabled = m_xCbShow->get_active();
675         rLabelData.maShowInfo.ShowItemsMode = ToDataPilotFieldShowItemsMode(m_xLbShowFrom->get_active());
676         rLabelData.maShowInfo.ItemCount = sal::static_int_cast<sal_Int32>( m_xNfShow->get_value() );
677         rLabelData.maShowInfo.DataField =
678             ScDPUtil::createDuplicateDimensionName(aFieldName.maName, aFieldName.mnDupCount);
679     }
680 
681     // *** HIDDEN ITEMS ***
682 
683     rLabelData.maMembers = maLabelData.maMembers;
684     int nVisCount = m_xLbHide->n_children();
685     for (int nPos = 0; nPos < nVisCount; ++nPos)
686         rLabelData.maMembers[nPos].mbVisible = m_xLbHide->get_toggle(nPos, 0) == TRISTATE_FALSE;
687 
688     // *** HIERARCHY ***
689 
690     rLabelData.mnUsedHier = m_xLbHierarchy->get_active() != -1 ? m_xLbHierarchy->get_active() : 0;
691 }
692 
Init(const ScDPNameVec & rDataFields,bool bEnableLayout)693 void ScDPSubtotalOptDlg::Init( const ScDPNameVec& rDataFields, bool bEnableLayout )
694 {
695     // *** SORTING ***
696 
697     sal_Int32 nSortMode = maLabelData.maSortInfo.Mode;
698 
699     // sort fields list box
700     m_xLbSortBy->append_text(maLabelData.getDisplayName());
701 
702     for( const auto& rDataField : rDataFields )
703     {
704         // Cache names for later lookup.
705         maDataFieldNameMap.emplace(rDataField.maLayoutName, rDataField);
706 
707         m_xLbSortBy->append_text(rDataField.maLayoutName);
708         m_xLbShowUsing->append_text(rDataField.maLayoutName);  // for AutoShow
709     }
710 
711     sal_Int32 nSortPos = SC_SORTNAME_POS;
712     if( nSortMode == DataPilotFieldSortMode::DATA )
713     {
714         nSortPos = FindListBoxEntry( *m_xLbSortBy, maLabelData.maSortInfo.Field, SC_SORTDATA_POS );
715         if( nSortPos == -1 )
716         {
717             nSortPos = SC_SORTNAME_POS;
718             nSortMode = DataPilotFieldSortMode::MANUAL;
719         }
720     }
721     m_xLbSortBy->set_active(nSortPos);
722 
723     // sorting mode
724     m_xRbSortAsc->connect_clicked( LINK( this, ScDPSubtotalOptDlg, RadioClickHdl ) );
725     m_xRbSortDesc->connect_clicked( LINK( this, ScDPSubtotalOptDlg, RadioClickHdl ) );
726     m_xRbSortMan->connect_clicked( LINK( this, ScDPSubtotalOptDlg, RadioClickHdl ) );
727 
728     weld::RadioButton* pRBtn = nullptr;
729     switch( nSortMode )
730     {
731         case DataPilotFieldSortMode::NONE:
732         case DataPilotFieldSortMode::MANUAL:
733             pRBtn = m_xRbSortMan.get();
734         break;
735         default:
736             pRBtn = maLabelData.maSortInfo.IsAscending ? m_xRbSortAsc.get() : m_xRbSortDesc.get();
737     }
738     pRBtn->set_active(true);
739     RadioClickHdl(*pRBtn);
740 
741     // *** LAYOUT MODE ***
742 
743     m_xLayoutFrame->set_sensitive(bEnableLayout);
744 
745     m_xLbLayout->set_active(FromDataPilotFieldLayoutMode(maLabelData.maLayoutInfo.LayoutMode));
746     m_xCbLayoutEmpty->set_active( maLabelData.maLayoutInfo.AddEmptyLines );
747     m_xCbRepeatItemLabels->set_active( maLabelData.mbRepeatItemLabels );
748 
749     // *** AUTO SHOW ***
750 
751     m_xCbShow->set_active( maLabelData.maShowInfo.IsEnabled );
752     m_xCbShow->connect_clicked( LINK( this, ScDPSubtotalOptDlg, CheckHdl ) );
753 
754     m_xLbShowFrom->set_active(FromDataPilotFieldShowItemsMode(maLabelData.maShowInfo.ShowItemsMode));
755     long nCount = static_cast< long >( maLabelData.maShowInfo.ItemCount );
756     if( nCount < 1 )
757         nCount = SC_SHOW_DEFAULT;
758     m_xNfShow->set_value( nCount );
759 
760     // m_xLbShowUsing already filled above
761     m_xLbShowUsing->set_active_text(maLabelData.maShowInfo.DataField);
762     if (m_xLbShowUsing->get_active() == -1)
763         m_xLbShowUsing->set_active(0);
764 
765     CheckHdl(*m_xCbShow);      // enable/disable dependent controls
766 
767     // *** HIDDEN ITEMS ***
768 
769     InitHideListBox();
770 
771     // *** HIERARCHY ***
772 
773     if( maLabelData.maHiers.getLength() > 1 )
774     {
775         lclFillListBox(*m_xLbHierarchy, maLabelData.maHiers);
776         sal_Int32 nHier = maLabelData.mnUsedHier;
777         if( (nHier < 0) || (nHier >= maLabelData.maHiers.getLength()) ) nHier = 0;
778         m_xLbHierarchy->set_active( nHier );
779         m_xLbHierarchy->connect_changed( LINK( this, ScDPSubtotalOptDlg, SelectHdl ) );
780     }
781     else
782     {
783         m_xFtHierarchy->set_sensitive(false);
784         m_xLbHierarchy->set_sensitive(false);
785     }
786 }
787 
InitHideListBox()788 void ScDPSubtotalOptDlg::InitHideListBox()
789 {
790     m_xLbHide->clear();
791     lclFillListBox(*m_xLbHide, maLabelData.maMembers);
792     size_t n = maLabelData.maMembers.size();
793     for (size_t i = 0; i < n; ++i)
794         m_xLbHide->set_toggle(i, maLabelData.maMembers[i].mbVisible ? TRISTATE_FALSE : TRISTATE_TRUE, 0);
795     bool bEnable = m_xLbHide->n_children() > 0;
796     m_xHideFrame->set_sensitive(bEnable);
797 }
798 
GetFieldName(const OUString & rLayoutName) const799 ScDPName ScDPSubtotalOptDlg::GetFieldName(const OUString& rLayoutName) const
800 {
801     NameMapType::const_iterator itr = maDataFieldNameMap.find(rLayoutName);
802     return itr == maDataFieldNameMap.end() ? ScDPName() : itr->second;
803 }
804 
FindListBoxEntry(const weld::ComboBox & rLBox,const OUString & rEntry,sal_Int32 nStartPos) const805 sal_Int32 ScDPSubtotalOptDlg::FindListBoxEntry(
806     const weld::ComboBox& rLBox, const OUString& rEntry, sal_Int32 nStartPos ) const
807 {
808     sal_Int32 nPos = nStartPos;
809     bool bFound = false;
810     while (nPos < rLBox.get_count())
811     {
812         // translate the displayed field name back to its original field name.
813         ScDPName aName = GetFieldName(rLBox.get_text(nPos));
814         OUString aUnoName = ScDPUtil::createDuplicateDimensionName(aName.maName, aName.mnDupCount);
815         if (aUnoName == rEntry)
816         {
817             bFound = true;
818             break;
819         }
820         ++nPos;
821     }
822     return bFound ? nPos : -1;
823 }
824 
IMPL_LINK(ScDPSubtotalOptDlg,RadioClickHdl,weld::Button &,rBtn,void)825 IMPL_LINK(ScDPSubtotalOptDlg, RadioClickHdl, weld::Button&, rBtn, void)
826 {
827     m_xLbSortBy->set_sensitive(&rBtn != m_xRbSortMan.get());
828 }
829 
IMPL_LINK(ScDPSubtotalOptDlg,CheckHdl,weld::Button &,rCBox,void)830 IMPL_LINK(ScDPSubtotalOptDlg, CheckHdl, weld::Button&, rCBox, void)
831 {
832     if (&rCBox == m_xCbShow.get())
833     {
834         bool bEnable = m_xCbShow->get_active();
835         m_xNfShow->set_sensitive( bEnable );
836         m_xFtShow->set_sensitive( bEnable );
837         m_xFtShowFrom->set_sensitive( bEnable );
838         m_xLbShowFrom->set_sensitive( bEnable );
839 
840         bool bEnableUsing = bEnable && (m_xLbShowUsing->get_count() > 0);
841         m_xFtShowUsing->set_sensitive(bEnableUsing);
842         m_xLbShowUsing->set_sensitive(bEnableUsing);
843     }
844 }
845 
IMPL_LINK_NOARG(ScDPSubtotalOptDlg,SelectHdl,weld::ComboBox &,void)846 IMPL_LINK_NOARG(ScDPSubtotalOptDlg, SelectHdl, weld::ComboBox&, void)
847 {
848     mrDPObj.GetMembers(maLabelData.mnCol, m_xLbHierarchy->get_active(), maLabelData.maMembers);
849     InitHideListBox();
850 }
851 
ScDPShowDetailDlg(weld::Window * pParent,ScDPObject & rDPObj,css::sheet::DataPilotFieldOrientation nOrient)852 ScDPShowDetailDlg::ScDPShowDetailDlg(weld::Window* pParent, ScDPObject& rDPObj, css::sheet::DataPilotFieldOrientation nOrient)
853     : GenericDialogController(pParent, "modules/scalc/ui/showdetaildialog.ui", "ShowDetail")
854     , mrDPObj(rDPObj)
855     , mxLbDims(m_xBuilder->weld_tree_view("dimsTreeview"))
856 {
857     ScDPSaveData* pSaveData = rDPObj.GetSaveData();
858     long nDimCount = rDPObj.GetDimCount();
859     for (long nDim=0; nDim<nDimCount; nDim++)
860     {
861         bool bIsDataLayout;
862         sal_Int32 nDimFlags = 0;
863         OUString aName = rDPObj.GetDimName( nDim, bIsDataLayout, &nDimFlags );
864         if ( !bIsDataLayout && !rDPObj.IsDuplicated( nDim ) && ScDPObject::IsOrientationAllowed( nOrient, nDimFlags ) )
865         {
866             const ScDPSaveDimension* pDimension = pSaveData ? pSaveData->GetExistingDimensionByName(aName) : nullptr;
867             if ( !pDimension || (pDimension->GetOrientation() != nOrient) )
868             {
869                 if (pDimension)
870                 {
871                     const boost::optional<OUString> & pLayoutName = pDimension->GetLayoutName();
872                     if (pLayoutName)
873                         aName = *pLayoutName;
874                 }
875                 mxLbDims->append_text(aName);
876                 maNameIndexMap.emplace(aName, nDim);
877             }
878         }
879     }
880     if (mxLbDims->n_children())
881        mxLbDims->select(0);
882 
883     mxLbDims->connect_row_activated(LINK(this, ScDPShowDetailDlg, DblClickHdl));
884 }
885 
~ScDPShowDetailDlg()886 ScDPShowDetailDlg::~ScDPShowDetailDlg()
887 {
888 }
889 
run()890 short ScDPShowDetailDlg::run()
891 {
892     return mxLbDims->n_children() ? GenericDialogController::run() : static_cast<short>(RET_CANCEL);
893 }
894 
GetDimensionName() const895 OUString ScDPShowDetailDlg::GetDimensionName() const
896 {
897     // Look up the internal dimension name which may be different from the
898     // displayed field name.
899     OUString aSelectedName = mxLbDims->get_selected_text();
900     DimNameIndexMap::const_iterator itr = maNameIndexMap.find(aSelectedName);
901     if (itr == maNameIndexMap.end())
902         // This should never happen!
903         return aSelectedName;
904 
905     long nDim = itr->second;
906     bool bIsDataLayout = false;
907     return mrDPObj.GetDimName(nDim, bIsDataLayout);
908 }
909 
IMPL_LINK_NOARG(ScDPShowDetailDlg,DblClickHdl,weld::TreeView &,bool)910 IMPL_LINK_NOARG(ScDPShowDetailDlg, DblClickHdl, weld::TreeView&, bool)
911 {
912     m_xDialog->response(RET_OK);
913     return true;
914 }
915 
916 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
917