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