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 
12 #include <memory>
13 #include <string_view>
14 
15 #include <PivotLayoutTreeListData.hxx>
16 #include <PivotLayoutDialog.hxx>
17 
18 #include <vcl/event.hxx>
19 #include <pivot.hxx>
20 #include <globstr.hrc>
21 #include <scresid.hxx>
22 
23 namespace
24 {
25 
lclGetFunctionMaskName(const PivotFunc nFunctionMask)26 OUString lclGetFunctionMaskName(const PivotFunc nFunctionMask)
27 {
28     TranslateId pStrId;
29     switch (nFunctionMask)
30     {
31         case PivotFunc::Sum:        pStrId = STR_FUN_TEXT_SUM;      break;
32         case PivotFunc::Count:      pStrId = STR_FUN_TEXT_COUNT;    break;
33         case PivotFunc::Average:    pStrId = STR_FUN_TEXT_AVG;      break;
34         case PivotFunc::Median:     pStrId = STR_FUN_TEXT_MEDIAN;   break;
35         case PivotFunc::Max:        pStrId = STR_FUN_TEXT_MAX;      break;
36         case PivotFunc::Min:        pStrId = STR_FUN_TEXT_MIN;      break;
37         case PivotFunc::Product:    pStrId = STR_FUN_TEXT_PRODUCT;  break;
38         case PivotFunc::CountNum:   pStrId = STR_FUN_TEXT_COUNT;    break;
39         case PivotFunc::StdDev:     pStrId = STR_FUN_TEXT_STDDEV;   break;
40         case PivotFunc::StdDevP:    pStrId = STR_FUN_TEXT_STDDEV;   break;
41         case PivotFunc::StdVar:     pStrId = STR_FUN_TEXT_VAR;      break;
42         case PivotFunc::StdVarP:    pStrId = STR_FUN_TEXT_VAR;      break;
43         default:
44             assert(false);
45             break;
46     }
47     if (pStrId)
48         return ScResId(pStrId);
49     else
50         return OUString();
51 }
52 
lclCreateDataItemName(const PivotFunc nFunctionMask,std::u16string_view rName,const sal_uInt8 nDuplicationCount)53 OUString lclCreateDataItemName(const PivotFunc nFunctionMask, std::u16string_view rName, const sal_uInt8 nDuplicationCount)
54 {
55     OUString aBuffer = lclGetFunctionMaskName(nFunctionMask) + " - " + rName;
56     if(nDuplicationCount > 0)
57     {
58         aBuffer += " " + OUString::number(nDuplicationCount);
59     }
60     return aBuffer;
61 }
62 
63 } // anonymous namespace
64 
ScPivotLayoutTreeListData(std::unique_ptr<weld::TreeView> xControl)65 ScPivotLayoutTreeListData::ScPivotLayoutTreeListData(std::unique_ptr<weld::TreeView> xControl)
66     : ScPivotLayoutTreeListBase(std::move(xControl))
67 {
68     mxControl->connect_key_press(LINK(this, ScPivotLayoutTreeListData, KeyInputHdl));
69     mxControl->connect_row_activated(LINK(this, ScPivotLayoutTreeListData, DoubleClickHdl));
70 }
71 
~ScPivotLayoutTreeListData()72 ScPivotLayoutTreeListData::~ScPivotLayoutTreeListData()
73 {
74     if (mpFunctionDlg)
75     {
76         mpFunctionDlg->Response(RET_CANCEL);
77         mpFunctionDlg.clear();
78     }
79 }
80 
IMPL_LINK_NOARG(ScPivotLayoutTreeListData,DoubleClickHdl,weld::TreeView &,bool)81 IMPL_LINK_NOARG(ScPivotLayoutTreeListData, DoubleClickHdl, weld::TreeView&, bool)
82 {
83     int nEntry = mxControl->get_cursor_index();
84     if (nEntry == -1)
85         return true;
86 
87     ScItemValue* pCurrentItemValue = weld::fromId<ScItemValue*>(mxControl->get_id(nEntry));
88     ScPivotFuncData& rCurrentFunctionData = pCurrentItemValue->maFunctionData;
89 
90     SCCOL nCurrentColumn = rCurrentFunctionData.mnCol;
91     ScDPLabelData& rCurrentLabelData = mpParent->GetLabelData(nCurrentColumn);
92 
93     ScAbstractDialogFactory* pFactory = ScAbstractDialogFactory::Create();
94 
95     mpFunctionDlg = pFactory->CreateScDPFunctionDlg(mxControl.get(), mpParent->GetLabelDataVector(), rCurrentLabelData, rCurrentFunctionData);
96 
97     mpFunctionDlg->StartExecuteAsync([this, pCurrentItemValue, nEntry](int nResult) mutable {
98         if (nResult == RET_OK)
99         {
100             ScPivotFuncData& rFunctionData = pCurrentItemValue->maFunctionData;
101             rFunctionData.mnFuncMask = mpFunctionDlg->GetFuncMask();
102             ScDPLabelData& rLabelData = mpParent->GetLabelData(rFunctionData.mnCol);
103             rLabelData.mnFuncMask = mpFunctionDlg->GetFuncMask();
104 
105             rFunctionData.maFieldRef = mpFunctionDlg->GetFieldRef();
106 
107             ScDPLabelData& rDFData = mpParent->GetLabelData(rFunctionData.mnCol);
108 
109             AdjustDuplicateCount(pCurrentItemValue);
110 
111             OUString sDataItemName = lclCreateDataItemName(
112                                         rFunctionData.mnFuncMask,
113                                         rDFData.maName,
114                                         rFunctionData.mnDupCount);
115 
116             mxControl->set_text(nEntry, sDataItemName);
117         }
118 
119         mpFunctionDlg->disposeOnce();
120     });
121 
122     return true;
123 }
124 
FillDataField(ScPivotFieldVector & rDataFields)125 void ScPivotLayoutTreeListData::FillDataField(ScPivotFieldVector& rDataFields)
126 {
127     mxControl->clear();
128     maDataItemValues.clear();
129 
130     for (const ScPivotField& rField : rDataFields)
131     {
132         if (rField.nCol == PIVOT_DATA_FIELD)
133             continue;
134 
135         SCCOL nColumn;
136         if (rField.mnOriginalDim >= 0)
137             nColumn = rField.mnOriginalDim;
138         else
139             nColumn = rField.nCol;
140 
141         ScItemValue* pOriginalItemValue = mpParent->GetItem(nColumn);
142         ScItemValue* pItemValue = new ScItemValue(pOriginalItemValue->maName, nColumn, rField.nFuncMask);
143 
144         pItemValue->mpOriginalItemValue = pOriginalItemValue;
145         pItemValue->maFunctionData.mnOriginalDim = rField.mnOriginalDim;
146         pItemValue->maFunctionData.maFieldRef = rField.maFieldRef;
147 
148         AdjustDuplicateCount(pItemValue);
149         OUString sDataItemName = lclCreateDataItemName(pItemValue->maFunctionData.mnFuncMask,
150                                                        pItemValue->maName,
151                                                        pItemValue->maFunctionData.mnDupCount);
152 
153         maDataItemValues.push_back(std::unique_ptr<ScItemValue>(pItemValue));
154         OUString sId(weld::toId(pItemValue));
155         mxControl->append(sId, sDataItemName);
156     }
157 }
158 
PushDataFieldNames(std::vector<ScDPName> & rDataFieldNames)159 void ScPivotLayoutTreeListData::PushDataFieldNames(std::vector<ScDPName>& rDataFieldNames)
160 {
161     std::unique_ptr<weld::TreeIter> xLoopEntry(mxControl->make_iterator());
162     if (!mxControl->get_iter_first(*xLoopEntry))
163         return;
164 
165     do
166     {
167         ScItemValue* pEachItemValue = weld::fromId<ScItemValue*>(mxControl->get_id(*xLoopEntry));
168         SCCOL nColumn = pEachItemValue->maFunctionData.mnCol;
169 
170         ScDPLabelData& rLabelData = mpParent->GetLabelData(nColumn);
171 
172         if (rLabelData.maName.isEmpty())
173             continue;
174 
175         OUString sLayoutName = rLabelData.maLayoutName;
176         if (sLayoutName.isEmpty())
177         {
178             sLayoutName = lclCreateDataItemName(
179                             pEachItemValue->maFunctionData.mnFuncMask,
180                             pEachItemValue->maName,
181                             pEachItemValue->maFunctionData.mnDupCount);
182         }
183 
184         rDataFieldNames.emplace_back(rLabelData.maName, sLayoutName, rLabelData.mnDupCount);
185     } while (mxControl->iter_next(*xLoopEntry));
186 }
187 
InsertEntryForSourceTarget(weld::TreeView & rSource,int nTarget)188 void ScPivotLayoutTreeListData::InsertEntryForSourceTarget(weld::TreeView& rSource, int nTarget)
189 {
190     if (rSource.count_selected_rows() <=0)
191         return;
192 
193     ScItemValue* pItemValue = weld::fromId<ScItemValue*>(rSource.get_selected_id());
194 
195     if (mpParent->IsDataElement(pItemValue->maFunctionData.mnCol))
196         return;
197 
198     if (&rSource == mxControl.get())
199     {
200         OUString sText = mxControl->get_selected_text();
201         OUString sId(weld::toId(pItemValue));
202         mxControl->remove_id(sId);
203         mxControl->insert(nullptr, nTarget, &sText, &sId, nullptr, nullptr, false, nullptr);
204     }
205     else
206     {
207         InsertEntryForItem(pItemValue->mpOriginalItemValue, nTarget);
208     }
209 }
210 
InsertEntryForItem(ScItemValue * pItemValue,int nPosition)211 void ScPivotLayoutTreeListData::InsertEntryForItem(ScItemValue* pItemValue, int nPosition)
212 {
213     ScItemValue* pDataItemValue = new ScItemValue(pItemValue);
214     pDataItemValue->mpOriginalItemValue = pItemValue;
215     maDataItemValues.push_back(std::unique_ptr<ScItemValue>(pDataItemValue));
216 
217     ScPivotFuncData& rFunctionData = pDataItemValue->maFunctionData;
218 
219     if (rFunctionData.mnFuncMask == PivotFunc::NONE ||
220         rFunctionData.mnFuncMask == PivotFunc::Auto)
221     {
222         rFunctionData.mnFuncMask = PivotFunc::Sum;
223     }
224 
225     AdjustDuplicateCount(pDataItemValue);
226 
227     OUString sDataName = lclCreateDataItemName(
228                             rFunctionData.mnFuncMask,
229                             pDataItemValue->maName,
230                             rFunctionData.mnDupCount);
231 
232     OUString sId(weld::toId(pDataItemValue));
233     mxControl->insert(nullptr, nPosition, &sDataName, &sId, nullptr, nullptr, false, nullptr);
234 }
235 
AdjustDuplicateCount(ScItemValue * pInputItemValue)236 void ScPivotLayoutTreeListData::AdjustDuplicateCount(ScItemValue* pInputItemValue)
237 {
238     ScPivotFuncData& rInputFunctionData = pInputItemValue->maFunctionData;
239 
240     bool bFoundDuplicate = false;
241 
242     rInputFunctionData.mnDupCount = 0;
243     sal_uInt8 nMaxDuplicateCount = 0;
244 
245     std::unique_ptr<weld::TreeIter> xEachEntry(mxControl->make_iterator());
246     if (!mxControl->get_iter_first(*xEachEntry))
247         return;
248     do
249     {
250         ScItemValue* pItemValue = weld::fromId<ScItemValue*>(mxControl->get_id(*xEachEntry));
251         if (pItemValue == pInputItemValue)
252             continue;
253 
254         ScPivotFuncData& rFunctionData = pItemValue->maFunctionData;
255 
256         if (rFunctionData.mnCol      == rInputFunctionData.mnCol &&
257             rFunctionData.mnFuncMask == rInputFunctionData.mnFuncMask)
258         {
259             bFoundDuplicate = true;
260             if(rFunctionData.mnDupCount > nMaxDuplicateCount)
261                 nMaxDuplicateCount = rFunctionData.mnDupCount;
262         }
263     } while (mxControl->iter_next(*xEachEntry));
264 
265     if(bFoundDuplicate)
266     {
267         rInputFunctionData.mnDupCount = nMaxDuplicateCount + 1;
268     }
269 }
270 
IMPL_LINK(ScPivotLayoutTreeListData,KeyInputHdl,const KeyEvent &,rKeyEvent,bool)271 IMPL_LINK(ScPivotLayoutTreeListData, KeyInputHdl, const KeyEvent&, rKeyEvent, bool)
272 {
273     vcl::KeyCode aCode = rKeyEvent.GetKeyCode();
274     sal_uInt16 nCode = aCode.GetCode();
275 
276     if (nCode == KEY_DELETE)
277     {
278         int nEntry = mxControl->get_cursor_index();
279         if (nEntry != -1)
280             mxControl->remove(nEntry);
281         return true;
282     }
283 
284     return false;
285 }
286 
287 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
288