/src/libreoffice/sc/source/ui/docshell/dbdocfun.cxx
Line | Count | Source |
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 | | #include <sfx2/app.hxx> |
21 | | #include <vcl/svapp.hxx> |
22 | | #include <vcl/weld/MessageDialog.hxx> |
23 | | #include <vcl/weld/weld.hxx> |
24 | | #include <svx/dataaccessdescriptor.hxx> |
25 | | #include <svx/svdpage.hxx> |
26 | | #include <svx/svdoole2.hxx> |
27 | | #include <com/sun/star/sdb/CommandType.hpp> |
28 | | #include <unotools/charclass.hxx> |
29 | | #include <comphelper/lok.hxx> |
30 | | #include <osl/diagnose.h> |
31 | | |
32 | | #include <dbdocfun.hxx> |
33 | | #include <dbdata.hxx> |
34 | | #include <undodat.hxx> |
35 | | #include <docsh.hxx> |
36 | | #include <docfunc.hxx> |
37 | | #include <globstr.hrc> |
38 | | #include <scresid.hxx> |
39 | | #include <globalnames.hxx> |
40 | | #include <tabvwsh.hxx> |
41 | | #include <patattr.hxx> |
42 | | #include <rangenam.hxx> |
43 | | #include <olinetab.hxx> |
44 | | #include <dpobject.hxx> |
45 | | #include <dpsave.hxx> |
46 | | #include <dociter.hxx> |
47 | | #include <editable.hxx> |
48 | | #include <attrib.hxx> |
49 | | #include <drwlayer.hxx> |
50 | | #include <dpshttab.hxx> |
51 | | #include <hints.hxx> |
52 | | #include <queryentry.hxx> |
53 | | #include <markdata.hxx> |
54 | | #include <progress.hxx> |
55 | | #include <undosort.hxx> |
56 | | #include <inputopt.hxx> |
57 | | #include <scmod.hxx> |
58 | | |
59 | | #include <chartlis.hxx> |
60 | | #include <ChartTools.hxx> |
61 | | #include <SheetViewOperationsTester.hxx> |
62 | | |
63 | | #include <memory> |
64 | | |
65 | | using namespace ::com::sun::star; |
66 | | |
67 | | bool ScDBDocFunc::CheckSheetViewProtection(sc::Operation eOperation) |
68 | 310 | { |
69 | 310 | sc::SheetViewOperationsTester aSheetViewTester(ScDocShell::GetViewData()); |
70 | 310 | return aSheetViewTester.check(eOperation); |
71 | 310 | } |
72 | | |
73 | | bool ScDBDocFunc::AddDBTable(const OUString& rName, const ScRange& rRange, bool bHeader, |
74 | | bool bRecord, bool bApi, const OUString& rStyleName) |
75 | 0 | { |
76 | 0 | ScDocShellModificator aModificator(rDocShell); |
77 | |
|
78 | 0 | ScDocument& rDoc = rDocShell.GetDocument(); |
79 | 0 | ScDBCollection* pDocColl = rDoc.GetDBCollection(); |
80 | 0 | if (bRecord && !rDoc.IsUndoEnabled()) |
81 | 0 | bRecord = false; |
82 | |
|
83 | 0 | if (rStyleName.isEmpty()) |
84 | 0 | { |
85 | 0 | if (!bApi) |
86 | 0 | rDocShell.ErrorMessage(STR_TABLE_ERR_ADD); |
87 | 0 | return false; |
88 | 0 | } |
89 | | |
90 | 0 | std::unique_ptr<ScDBCollection> pUndoColl; |
91 | 0 | if (bRecord) |
92 | 0 | pUndoColl.reset(new ScDBCollection(*pDocColl)); |
93 | |
|
94 | 0 | std::unique_ptr<ScDBData> pNew(new ScDBData(rName, rRange.aStart.Tab(), rRange.aStart.Col(), |
95 | 0 | rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row(), |
96 | 0 | true, bHeader, false, u""_ustr, rStyleName)); |
97 | 0 | if (pNew) |
98 | 0 | { |
99 | 0 | pNew->SetAutoFilter(true); |
100 | 0 | rDoc.ApplyFlagsTab(rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), |
101 | 0 | rRange.aStart.Row(), rRange.aStart.Tab(), ScMF::Auto); |
102 | 0 | } |
103 | |
|
104 | 0 | bool bCompile = !rDoc.IsImportingXML(); |
105 | 0 | if (bCompile) |
106 | 0 | rDoc.PreprocessDBDataUpdate(); |
107 | |
|
108 | 0 | bool bOk = pDocColl->getNamedDBs().insert(std::move(pNew)); |
109 | 0 | if (bCompile) |
110 | 0 | rDoc.CompileHybridFormula(); |
111 | |
|
112 | 0 | if (!bOk && !bApi) |
113 | 0 | { |
114 | 0 | rDocShell.ErrorMessage(STR_TABLE_ERR_ADD); |
115 | 0 | return false; |
116 | 0 | } |
117 | | |
118 | 0 | rDocShell.PostPaint(rRange, PaintPartFlags::Grid | PaintPartFlags::Left | PaintPartFlags::Top | PaintPartFlags::Size); |
119 | |
|
120 | 0 | if (bRecord) |
121 | 0 | { |
122 | 0 | rDocShell.GetUndoManager()->AddUndoAction( |
123 | 0 | std::make_unique<ScUndoDBTable>(rDocShell, rName, true/*bInsert*/, std::move(pUndoColl), |
124 | 0 | std::make_unique<ScDBCollection>(*pDocColl))); |
125 | 0 | } |
126 | |
|
127 | 0 | aModificator.SetDocumentModified(); |
128 | 0 | SfxGetpApp()->Broadcast(SfxHint(SfxHintId::ScDbAreasChanged)); |
129 | 0 | return true; |
130 | 0 | } |
131 | | |
132 | | bool ScDBDocFunc::DeleteDBTable(const ScDBData* pDBObj, bool bRecord, bool bApi) |
133 | 0 | { |
134 | 0 | bool bDone = false; |
135 | 0 | ScDocument& rDoc = rDocShell.GetDocument(); |
136 | 0 | ScDBCollection* pDocColl = rDoc.GetDBCollection(); |
137 | 0 | if (bRecord && !rDoc.IsUndoEnabled()) |
138 | 0 | bRecord = false; |
139 | |
|
140 | 0 | ScDBCollection::NamedDBs& rDBs = pDocColl->getNamedDBs(); |
141 | 0 | auto const iter = rDBs.findByPointer(pDBObj); |
142 | 0 | if (iter != rDBs.end()) |
143 | 0 | { |
144 | 0 | ScDocShellModificator aModificator(rDocShell); |
145 | 0 | std::unique_ptr<ScDBCollection> pUndoColl; |
146 | 0 | if (bRecord) |
147 | 0 | pUndoColl.reset(new ScDBCollection(*pDocColl)); |
148 | |
|
149 | 0 | OUString aTableName = iter->get()->GetName(); |
150 | 0 | ScRange aRange; |
151 | 0 | iter->get()->GetArea(aRange); |
152 | |
|
153 | 0 | rDoc.PreprocessDBDataUpdate(); |
154 | 0 | rDBs.erase(iter); |
155 | 0 | rDoc.CompileHybridFormula(); |
156 | |
|
157 | 0 | if (aRange.IsValid()) |
158 | 0 | { |
159 | 0 | rDoc.RemoveFlagsTab(aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(), |
160 | 0 | aRange.aStart.Row(), aRange.aStart.Tab(), ScMF::Auto); |
161 | 0 | rDocShell.PostPaint(aRange, PaintPartFlags::Grid | PaintPartFlags::Left |
162 | 0 | | PaintPartFlags::Top | PaintPartFlags::Size); |
163 | 0 | } |
164 | |
|
165 | 0 | if (bRecord) |
166 | 0 | { |
167 | 0 | rDocShell.GetUndoManager()->AddUndoAction( |
168 | 0 | std::make_unique<ScUndoDBTable>(rDocShell, aTableName, false/*bInsert*/, std::move(pUndoColl), |
169 | 0 | std::make_unique<ScDBCollection>(*pDocColl))); |
170 | 0 | } |
171 | |
|
172 | 0 | aModificator.SetDocumentModified(); |
173 | 0 | SfxGetpApp()->Broadcast(SfxHint(SfxHintId::ScDbAreasChanged)); |
174 | 0 | bDone = true; |
175 | 0 | } |
176 | 0 | else if (!bApi) |
177 | 0 | { |
178 | 0 | rDocShell.ErrorMessage(STR_TABLE_ERR_DEL); |
179 | 0 | } |
180 | |
|
181 | 0 | return bDone; |
182 | 0 | } |
183 | | |
184 | | bool ScDBDocFunc::AddDBRange( const OUString& rName, const ScRange& rRange ) |
185 | 232 | { |
186 | | |
187 | 232 | ScDocShellModificator aModificator( rDocShell ); |
188 | | |
189 | 232 | ScDocument& rDoc = rDocShell.GetDocument(); |
190 | 232 | ScDBCollection* pDocColl = rDoc.GetDBCollection(); |
191 | 232 | bool bUndo (rDoc.IsUndoEnabled()); |
192 | | |
193 | 232 | std::unique_ptr<ScDBCollection> pUndoColl; |
194 | 232 | if (bUndo) |
195 | 0 | pUndoColl.reset( new ScDBCollection( *pDocColl ) ); |
196 | | |
197 | 232 | std::unique_ptr<ScDBData> pNew(new ScDBData( rName, rRange.aStart.Tab(), |
198 | 232 | rRange.aStart.Col(), rRange.aStart.Row(), |
199 | 232 | rRange.aEnd.Col(), rRange.aEnd.Row() )); |
200 | | |
201 | | // #i55926# While loading XML, formula cells only have a single string token, |
202 | | // so CompileDBFormula would never find any name (index) tokens, and would |
203 | | // unnecessarily loop through all cells. |
204 | 232 | bool bCompile = !rDoc.IsImportingXML(); |
205 | 232 | bool bOk; |
206 | 232 | if ( bCompile ) |
207 | 232 | rDoc.PreprocessDBDataUpdate(); |
208 | 232 | if ( rName == STR_DB_LOCAL_NONAME ) |
209 | 0 | { |
210 | 0 | rDoc.SetAnonymousDBData(rRange.aStart.Tab(), std::move(pNew)); |
211 | 0 | bOk = true; |
212 | 0 | } |
213 | 232 | else |
214 | 232 | { |
215 | 232 | bOk = pDocColl->getNamedDBs().insert(std::move(pNew)); |
216 | 232 | } |
217 | 232 | if ( bCompile ) |
218 | 232 | rDoc.CompileHybridFormula(); |
219 | | |
220 | 232 | if (!bOk) |
221 | 0 | { |
222 | 0 | return false; |
223 | 0 | } |
224 | | |
225 | 232 | if (bUndo) |
226 | 0 | { |
227 | 0 | rDocShell.GetUndoManager()->AddUndoAction( |
228 | 0 | std::make_unique<ScUndoDBData>( rDocShell, rName, std::move(pUndoColl), |
229 | 0 | rName, std::make_unique<ScDBCollection>( *pDocColl ) ) ); |
230 | 0 | } |
231 | | |
232 | 232 | aModificator.SetDocumentModified(); |
233 | 232 | SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) ); |
234 | 232 | return true; |
235 | 232 | } |
236 | | |
237 | | bool ScDBDocFunc::DeleteDBRange(const OUString& rName) |
238 | 0 | { |
239 | 0 | bool bDone = false; |
240 | 0 | ScDocument& rDoc = rDocShell.GetDocument(); |
241 | 0 | ScDBCollection* pDocColl = rDoc.GetDBCollection(); |
242 | 0 | bool bUndo = rDoc.IsUndoEnabled(); |
243 | |
|
244 | 0 | ScDBCollection::NamedDBs& rDBs = pDocColl->getNamedDBs(); |
245 | 0 | auto const iter = rDBs.findByUpperName2(ScGlobal::getCharClass().uppercase(rName)); |
246 | 0 | if (iter != rDBs.end()) |
247 | 0 | { |
248 | 0 | ScDocShellModificator aModificator( rDocShell ); |
249 | |
|
250 | 0 | std::unique_ptr<ScDBCollection> pUndoColl; |
251 | 0 | if (bUndo) |
252 | 0 | pUndoColl.reset( new ScDBCollection( *pDocColl ) ); |
253 | |
|
254 | 0 | ScRange aRange; |
255 | 0 | iter->get()->GetArea(aRange); |
256 | |
|
257 | 0 | rDoc.PreprocessDBDataUpdate(); |
258 | 0 | rDBs.erase(iter); |
259 | 0 | rDoc.CompileHybridFormula(); |
260 | |
|
261 | 0 | if (bUndo) |
262 | 0 | { |
263 | 0 | rDocShell.GetUndoManager()->AddUndoAction( |
264 | 0 | std::make_unique<ScUndoDBData>( rDocShell, rName, std::move(pUndoColl), |
265 | 0 | rName, std::make_unique<ScDBCollection>( *pDocColl ) ) ); |
266 | 0 | } |
267 | |
|
268 | 0 | aModificator.SetDocumentModified(); |
269 | 0 | SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) ); |
270 | 0 | bDone = true; |
271 | 0 | } |
272 | |
|
273 | 0 | return bDone; |
274 | 0 | } |
275 | | |
276 | | bool ScDBDocFunc::RenameDBRange( const OUString& rOld, const OUString& rNew ) |
277 | 0 | { |
278 | 0 | bool bDone = false; |
279 | 0 | ScDocument& rDoc = rDocShell.GetDocument(); |
280 | 0 | ScDBCollection* pDocColl = rDoc.GetDBCollection(); |
281 | 0 | bool bUndo = rDoc.IsUndoEnabled(); |
282 | 0 | ScDBCollection::NamedDBs& rDBs = pDocColl->getNamedDBs(); |
283 | 0 | auto const iterOld = rDBs.findByUpperName2(ScGlobal::getCharClass().uppercase(rOld)); |
284 | 0 | const ScDBData* pNew = rDBs.findByUpperName(ScGlobal::getCharClass().uppercase(rNew)); |
285 | 0 | if (iterOld != rDBs.end() && !pNew) |
286 | 0 | { |
287 | 0 | ScDocShellModificator aModificator( rDocShell ); |
288 | |
|
289 | 0 | std::unique_ptr<ScDBData> pNewData(new ScDBData(rNew, **iterOld)); |
290 | 0 | OUString aUndoName = rOld; |
291 | 0 | std::unique_ptr<ScDBCollection> pUndoColl( new ScDBCollection( *pDocColl ) ); |
292 | |
|
293 | 0 | rDoc.PreprocessDBDataUpdate(); |
294 | 0 | rDBs.erase(iterOld); |
295 | 0 | bool bInserted = rDBs.insert(std::move(pNewData)); |
296 | 0 | if (!bInserted) // error -> restore old state |
297 | 0 | { |
298 | 0 | rDoc.SetDBCollection(std::move(pUndoColl)); // belongs to the document then |
299 | 0 | } |
300 | |
|
301 | 0 | rDoc.CompileHybridFormula(); |
302 | |
|
303 | 0 | if (bInserted) // insertion worked |
304 | 0 | { |
305 | 0 | if (bUndo) |
306 | 0 | { |
307 | 0 | rDocShell.GetUndoManager()->AddUndoAction( |
308 | 0 | std::make_unique<ScUndoDBData>( rDocShell, aUndoName, std::move(pUndoColl), |
309 | 0 | rNew, std::make_unique<ScDBCollection>( *pDocColl ) ) ); |
310 | 0 | } |
311 | 0 | else |
312 | 0 | pUndoColl.reset(); |
313 | |
|
314 | 0 | aModificator.SetDocumentModified(); |
315 | 0 | SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) ); |
316 | 0 | bDone = true; |
317 | 0 | } |
318 | 0 | } |
319 | |
|
320 | 0 | return bDone; |
321 | 0 | } |
322 | | |
323 | | void ScDBDocFunc::ModifyDBData( const ScDBData& rNewData ) |
324 | 2.95k | { |
325 | 2.95k | ScDocument& rDoc = rDocShell.GetDocument(); |
326 | 2.95k | ScDBCollection* pDocColl = rDoc.GetDBCollection(); |
327 | 2.95k | bool bUndo = rDoc.IsUndoEnabled(); |
328 | | |
329 | 2.95k | ScDBData* pData = nullptr; |
330 | 2.95k | if (rNewData.GetName() == STR_DB_LOCAL_NONAME) |
331 | 746 | { |
332 | 746 | ScRange aRange; |
333 | 746 | rNewData.GetArea(aRange); |
334 | 746 | SCTAB nTab = aRange.aStart.Tab(); |
335 | 746 | pData = rDoc.GetAnonymousDBData(nTab); |
336 | 746 | } |
337 | 2.21k | else |
338 | 2.21k | pData = pDocColl->getNamedDBs().findByUpperName(rNewData.GetUpperName()); |
339 | | |
340 | 2.95k | if (!pData) |
341 | 0 | return; |
342 | | |
343 | 2.95k | ScDocShellModificator aModificator( rDocShell ); |
344 | 2.95k | ScRange aOldRange, aNewRange; |
345 | 2.95k | pData->GetArea(aOldRange); |
346 | 2.95k | rNewData.GetArea(aNewRange); |
347 | 2.95k | bool bAreaChanged = ( aOldRange != aNewRange ); // then a recompilation is needed |
348 | 2.95k | bool bOldAutoFilter = pData->HasAutoFilter(); |
349 | 2.95k | bool bNewAutoFilter = rNewData.HasAutoFilter(); |
350 | | |
351 | 2.95k | std::unique_ptr<ScDBCollection> pUndoColl; |
352 | 2.95k | OUString sOldName; |
353 | 2.95k | if (bUndo) |
354 | 0 | { |
355 | 0 | pUndoColl.reset( new ScDBCollection( *pDocColl ) ); |
356 | 0 | sOldName = pData->GetName(); |
357 | 0 | } |
358 | | |
359 | 2.95k | *pData = rNewData; |
360 | 2.95k | if (bAreaChanged || bOldAutoFilter != bNewAutoFilter) { |
361 | 444 | if (bAreaChanged) |
362 | 0 | rDoc.CompileDBFormula(); |
363 | 444 | if (bOldAutoFilter && !bNewAutoFilter) |
364 | 0 | { |
365 | 0 | rDoc.RemoveFlagsTab(aOldRange.aStart.Col(), aOldRange.aStart.Row(), aOldRange.aEnd.Col(), aOldRange.aEnd.Row(), aOldRange.aStart.Tab(), ScMF::Auto); |
366 | 0 | } |
367 | 444 | else if (bOldAutoFilter && bNewAutoFilter) |
368 | 0 | { |
369 | 0 | rDoc.RemoveFlagsTab(aOldRange.aStart.Col(), aOldRange.aStart.Row(), aOldRange.aEnd.Col(), aOldRange.aEnd.Row(), aOldRange.aStart.Tab(), ScMF::Auto); |
370 | 0 | rDoc.ApplyFlagsTab(aNewRange.aStart.Col(), aNewRange.aStart.Row(), aNewRange.aEnd.Col(), aNewRange.aStart.Row(), aNewRange.aStart.Tab(), ScMF::Auto); |
371 | 0 | } |
372 | 444 | else if (!bOldAutoFilter && bNewAutoFilter) |
373 | 444 | { |
374 | 444 | rDoc.ApplyFlagsTab(aNewRange.aStart.Col(), aNewRange.aStart.Row(), aNewRange.aEnd.Col(), aNewRange.aStart.Row(), aNewRange.aStart.Tab(), ScMF::Auto); |
375 | 444 | } |
376 | 444 | } |
377 | | |
378 | 2.95k | rDocShell.PostPaint(ScRange(0, 0, aOldRange.aStart.Tab(), rDoc.MaxCol(), rDoc.MaxRow(), aOldRange.aEnd.Tab()), |
379 | 2.95k | PaintPartFlags::Grid | PaintPartFlags::Left | PaintPartFlags::Top | PaintPartFlags::Size); |
380 | | |
381 | 2.95k | if (bUndo) |
382 | 0 | { |
383 | 0 | rDocShell.GetUndoManager()->AddUndoAction( |
384 | 0 | std::make_unique<ScUndoDBData>( rDocShell, sOldName, std::move(pUndoColl), |
385 | 0 | rNewData.GetName(), std::make_unique<ScDBCollection>( *pDocColl ) ) ); |
386 | 0 | } |
387 | | |
388 | 2.95k | aModificator.SetDocumentModified(); |
389 | 2.95k | } |
390 | | |
391 | | void ScDBDocFunc::ModifyAllDBData( const ScDBCollection& rNewColl, const std::vector<ScRange>& rDelAreaList ) |
392 | 0 | { |
393 | 0 | ScDocShellModificator aModificator(rDocShell); |
394 | 0 | ScDocument& rDoc = rDocShell.GetDocument(); |
395 | 0 | ScDBCollection* pOldColl = rDoc.GetDBCollection(); |
396 | 0 | std::unique_ptr<ScDBCollection> pUndoColl; |
397 | 0 | bool bRecord = rDoc.IsUndoEnabled(); |
398 | |
|
399 | 0 | for (const auto& rDelArea : rDelAreaList) |
400 | 0 | { |
401 | | // unregistering target in SBA no longer necessary |
402 | 0 | const ScAddress& rStart = rDelArea.aStart; |
403 | 0 | const ScAddress& rEnd = rDelArea.aEnd; |
404 | 0 | rDocShell.DBAreaDeleted( |
405 | 0 | rStart.Tab(), rStart.Col(), rStart.Row(), rEnd.Col()); |
406 | 0 | } |
407 | |
|
408 | 0 | if (bRecord) |
409 | 0 | pUndoColl.reset( new ScDBCollection( *pOldColl ) ); |
410 | | |
411 | | // register target in SBA no longer necessary |
412 | |
|
413 | 0 | rDoc.PreprocessDBDataUpdate(); |
414 | 0 | rDoc.SetDBCollection( std::unique_ptr<ScDBCollection>(new ScDBCollection( rNewColl )) ); |
415 | 0 | rDoc.CompileHybridFormula(); |
416 | 0 | pOldColl = nullptr; |
417 | 0 | rDocShell.PostPaint(ScRange(0, 0, 0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB), PaintPartFlags::Grid); |
418 | 0 | aModificator.SetDocumentModified(); |
419 | 0 | SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) ); |
420 | |
|
421 | 0 | if (bRecord) |
422 | 0 | { |
423 | 0 | rDocShell.GetUndoManager()->AddUndoAction( |
424 | 0 | std::make_unique<ScUndoDBData>(rDocShell, u""_ustr, std::move(pUndoColl), |
425 | 0 | u""_ustr, std::make_unique<ScDBCollection>(rNewColl))); |
426 | 0 | } |
427 | 0 | } |
428 | | |
429 | | bool ScDBDocFunc::RepeatDB( const OUString& rDBName, bool bApi, bool bIsUnnamed, SCTAB aTab ) |
430 | 0 | { |
431 | | //! use also for ScDBFunc::RepeatDB ! |
432 | |
|
433 | 0 | bool bDone = false; |
434 | 0 | ScDocument& rDoc = rDocShell.GetDocument(); |
435 | 0 | bool bRecord = true; |
436 | 0 | if (!rDoc.IsUndoEnabled()) |
437 | 0 | bRecord = false; |
438 | 0 | ScDBData* pDBData = nullptr; |
439 | 0 | if (bIsUnnamed) |
440 | 0 | { |
441 | 0 | pDBData = rDoc.GetAnonymousDBData( aTab ); |
442 | 0 | } |
443 | 0 | else |
444 | 0 | { |
445 | 0 | ScDBCollection* pColl = rDoc.GetDBCollection(); |
446 | 0 | if (pColl) |
447 | 0 | pDBData = pColl->getNamedDBs().findByUpperName(ScGlobal::getCharClass().uppercase(rDBName)); |
448 | 0 | } |
449 | |
|
450 | 0 | if ( pDBData ) |
451 | 0 | { |
452 | 0 | ScQueryParam aQueryParam; |
453 | 0 | pDBData->GetQueryParam( aQueryParam ); |
454 | 0 | bool bQuery = aQueryParam.GetEntry(0).bDoQuery; |
455 | |
|
456 | 0 | ScSortParam aSortParam; |
457 | 0 | pDBData->GetSortParam( aSortParam ); |
458 | 0 | bool bSort = aSortParam.maKeyState[0].bDoSort; |
459 | |
|
460 | 0 | ScSubTotalParam aSubTotalParam; |
461 | 0 | pDBData->GetSubTotalParam( aSubTotalParam ); |
462 | 0 | bool bSubTotal = aSubTotalParam.aGroups[0].bActive && !aSubTotalParam.bRemoveOnly; |
463 | |
|
464 | 0 | if ( bQuery || bSort || bSubTotal ) |
465 | 0 | { |
466 | 0 | bool bQuerySize = false; |
467 | 0 | ScRange aOldQuery; |
468 | 0 | ScRange aNewQuery; |
469 | 0 | if (bQuery && !aQueryParam.bInplace) |
470 | 0 | { |
471 | 0 | ScDBData* pDest = rDoc.GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow, |
472 | 0 | aQueryParam.nDestTab, ScDBDataPortion::TOP_LEFT ); |
473 | 0 | if (pDest && pDest->IsDoSize()) |
474 | 0 | { |
475 | 0 | pDest->GetArea( aOldQuery ); |
476 | 0 | bQuerySize = true; |
477 | 0 | } |
478 | 0 | } |
479 | |
|
480 | 0 | SCTAB nTab; |
481 | 0 | SCCOL nStartCol; |
482 | 0 | SCROW nStartRow; |
483 | 0 | SCCOL nEndCol; |
484 | 0 | SCROW nEndRow; |
485 | 0 | pDBData->GetArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow ); |
486 | | |
487 | | //! Undo needed data only ? |
488 | |
|
489 | 0 | ScDocumentUniquePtr pUndoDoc; |
490 | 0 | std::unique_ptr<ScOutlineTable> pUndoTab; |
491 | 0 | std::unique_ptr<ScRangeName> pUndoRange; |
492 | 0 | std::unique_ptr<ScDBCollection> pUndoDB; |
493 | |
|
494 | 0 | if (bRecord) |
495 | 0 | { |
496 | 0 | SCTAB nTabCount = rDoc.GetTableCount(); |
497 | 0 | pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO )); |
498 | 0 | ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab ); |
499 | 0 | if (pTable) |
500 | 0 | { |
501 | 0 | pUndoTab.reset(new ScOutlineTable( *pTable )); |
502 | | |
503 | | // column/row state |
504 | 0 | SCCOLROW nOutStartCol, nOutEndCol; |
505 | 0 | SCCOLROW nOutStartRow, nOutEndRow; |
506 | 0 | pTable->GetColArray().GetRange( nOutStartCol, nOutEndCol ); |
507 | 0 | pTable->GetRowArray().GetRange( nOutStartRow, nOutEndRow ); |
508 | |
|
509 | 0 | pUndoDoc->InitUndo( rDoc, nTab, nTab, true, true ); |
510 | 0 | rDoc.CopyToDocument(static_cast<SCCOL>(nOutStartCol), 0, |
511 | 0 | nTab, static_cast<SCCOL>(nOutEndCol), rDoc.MaxRow(), nTab, |
512 | 0 | InsertDeleteFlags::NONE, false, *pUndoDoc); |
513 | 0 | rDoc.CopyToDocument(0, static_cast<SCROW>(nOutStartRow), |
514 | 0 | nTab, rDoc.MaxCol(), static_cast<SCROW>(nOutEndRow), nTab, |
515 | 0 | InsertDeleteFlags::NONE, false, *pUndoDoc); |
516 | 0 | } |
517 | 0 | else |
518 | 0 | pUndoDoc->InitUndo( rDoc, nTab, nTab, false, true ); |
519 | | |
520 | | // secure data range - incl. filtering result |
521 | 0 | rDoc.CopyToDocument(0, nStartRow, nTab, rDoc.MaxCol(), nEndRow, nTab, InsertDeleteFlags::ALL, false, *pUndoDoc); |
522 | | |
523 | | // all formulas because of references |
524 | 0 | rDoc.CopyToDocument(0, 0, 0, rDoc.MaxCol(), rDoc.MaxRow(), nTabCount-1, InsertDeleteFlags::FORMULA, false, *pUndoDoc); |
525 | | |
526 | | // ranges of DB and other |
527 | 0 | ScRangeName* pDocRange = rDoc.GetRangeName(); |
528 | 0 | if (!pDocRange->empty()) |
529 | 0 | pUndoRange.reset(new ScRangeName( *pDocRange )); |
530 | 0 | ScDBCollection* pDocDB = rDoc.GetDBCollection(); |
531 | 0 | if (!pDocDB->empty()) |
532 | 0 | pUndoDB.reset(new ScDBCollection( *pDocDB )); |
533 | 0 | } |
534 | |
|
535 | 0 | if (bSort && bSubTotal) |
536 | 0 | { |
537 | | // sort without SubTotals |
538 | |
|
539 | 0 | aSubTotalParam.bRemoveOnly = true; // will be reset again further down |
540 | 0 | DoSubTotals( nTab, aSubTotalParam, false, bApi ); |
541 | 0 | } |
542 | |
|
543 | 0 | if (bSort) |
544 | 0 | { |
545 | 0 | pDBData->GetSortParam( aSortParam ); // range may have changed |
546 | 0 | (void)Sort( nTab, aSortParam, false, false, bApi ); |
547 | 0 | } |
548 | 0 | if (bQuery) |
549 | 0 | { |
550 | 0 | pDBData->GetQueryParam( aQueryParam ); // range may have changed |
551 | 0 | ScRange aAdvSource; |
552 | 0 | if (pDBData->GetAdvancedQuerySource(aAdvSource)) |
553 | 0 | Query( nTab, aQueryParam, &aAdvSource, false, bApi ); |
554 | 0 | else |
555 | 0 | Query( nTab, aQueryParam, nullptr, false, bApi ); |
556 | | |
557 | | // at not-inplace the table may have been converted |
558 | | // if ( !aQueryParam.bInplace && aQueryParam.nDestTab != nTab ) |
559 | | // SetTabNo( nTab ); |
560 | 0 | } |
561 | 0 | if (bSubTotal) |
562 | 0 | { |
563 | 0 | pDBData->GetSubTotalParam( aSubTotalParam ); // range may have changed |
564 | 0 | aSubTotalParam.bRemoveOnly = false; |
565 | 0 | DoSubTotals( nTab, aSubTotalParam, false, bApi ); |
566 | 0 | } |
567 | |
|
568 | 0 | if (bRecord) |
569 | 0 | { |
570 | 0 | SCTAB nDummyTab; |
571 | 0 | SCCOL nDummyCol; |
572 | 0 | SCROW nDummyRow; |
573 | 0 | SCROW nNewEndRow; |
574 | 0 | pDBData->GetArea( nDummyTab, nDummyCol,nDummyRow, nDummyCol,nNewEndRow ); |
575 | |
|
576 | 0 | const ScRange* pOld = nullptr; |
577 | 0 | const ScRange* pNew = nullptr; |
578 | 0 | if (bQuerySize) |
579 | 0 | { |
580 | 0 | ScDBData* pDest = rDoc.GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow, |
581 | 0 | aQueryParam.nDestTab, ScDBDataPortion::TOP_LEFT ); |
582 | 0 | if (pDest) |
583 | 0 | { |
584 | 0 | pDest->GetArea( aNewQuery ); |
585 | 0 | pOld = &aOldQuery; |
586 | 0 | pNew = &aNewQuery; |
587 | 0 | } |
588 | 0 | } |
589 | |
|
590 | 0 | rDocShell.GetUndoManager()->AddUndoAction( |
591 | 0 | std::make_unique<ScUndoRepeatDB>( rDocShell, nTab, |
592 | 0 | nStartCol, nStartRow, nEndCol, nEndRow, |
593 | 0 | nNewEndRow, |
594 | | //nCurX, nCurY, |
595 | 0 | nStartCol, nStartRow, |
596 | 0 | std::move(pUndoDoc), std::move(pUndoTab), |
597 | 0 | std::move(pUndoRange), std::move(pUndoDB), |
598 | 0 | pOld, pNew ) ); |
599 | 0 | } |
600 | |
|
601 | 0 | rDocShell.PostPaint(ScRange(0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab), |
602 | 0 | PaintPartFlags::Grid | PaintPartFlags::Left | PaintPartFlags::Top | PaintPartFlags::Size); |
603 | 0 | bDone = true; |
604 | 0 | } |
605 | 0 | else if (!bApi) // "Don't execute any operations" |
606 | 0 | rDocShell.ErrorMessage(STR_MSSG_REPEATDB_0); |
607 | 0 | } |
608 | |
|
609 | 0 | return bDone; |
610 | 0 | } |
611 | | |
612 | | bool ScDBDocFunc::Sort( SCTAB nTab, const ScSortParam& rSortParam, |
613 | | bool bRecord, bool bPaint, bool bApi ) |
614 | 0 | { |
615 | 0 | ScDocShellModificator aModificator( rDocShell ); |
616 | |
|
617 | 0 | ScDocument& rDoc = rDocShell.GetDocument(); |
618 | 0 | if (bRecord && !rDoc.IsUndoEnabled()) |
619 | 0 | bRecord = false; |
620 | |
|
621 | 0 | ScDBData* pDBData = rDoc.GetDBAtArea( nTab, rSortParam.nCol1, rSortParam.nRow1, |
622 | 0 | rSortParam.nCol2, rSortParam.nRow2 ); |
623 | 0 | if (!pDBData) |
624 | 0 | { |
625 | 0 | OSL_FAIL( "Sort: no DBData" ); |
626 | 0 | return false; |
627 | 0 | } |
628 | | |
629 | 0 | bool bCopy = !rSortParam.bInplace; |
630 | 0 | if ( bCopy && rSortParam.nDestCol == rSortParam.nCol1 && |
631 | 0 | rSortParam.nDestRow == rSortParam.nRow1 && rSortParam.nDestTab == nTab ) |
632 | 0 | bCopy = false; |
633 | |
|
634 | 0 | ScSortParam aLocalParam( rSortParam ); |
635 | 0 | if ( bCopy ) |
636 | 0 | { |
637 | | // Copy the data range to the destination then move the sort range to it. |
638 | 0 | ScRange aSrcRange(rSortParam.nCol1, rSortParam.nRow1, nTab, rSortParam.nCol2, rSortParam.nRow2, nTab); |
639 | 0 | ScAddress aDestPos(rSortParam.nDestCol,rSortParam.nDestRow,rSortParam.nDestTab); |
640 | |
|
641 | 0 | ScDocFunc& rDocFunc = rDocShell.GetDocFunc(); |
642 | 0 | bool bRet = rDocFunc.MoveBlock(aSrcRange, aDestPos, false, bRecord, bPaint, bApi); |
643 | |
|
644 | 0 | if (!bRet) |
645 | 0 | return false; |
646 | | |
647 | 0 | aLocalParam.MoveToDest(); |
648 | 0 | nTab = aLocalParam.nDestTab; |
649 | 0 | } |
650 | | |
651 | | // tdf#119804: If there is a header row/column, it won't be affected by |
652 | | // sorting; so we can exclude it from the test. |
653 | 0 | SCROW nStartingRowToEdit = aLocalParam.nRow1; |
654 | 0 | SCCOL nStartingColToEdit = aLocalParam.nCol1; |
655 | 0 | if ( aLocalParam.bHasHeader ) |
656 | 0 | { |
657 | 0 | if ( aLocalParam.bByRow ) |
658 | 0 | nStartingRowToEdit++; |
659 | 0 | else |
660 | 0 | nStartingColToEdit++; |
661 | 0 | } |
662 | |
|
663 | 0 | if (!CheckSheetViewProtection(sc::Operation::Sort)) |
664 | 0 | return false; |
665 | | |
666 | 0 | ScEditableTester aTester = ScEditableTester::CreateAndTestBlock( |
667 | 0 | rDoc, nTab, nStartingColToEdit, nStartingRowToEdit, |
668 | 0 | aLocalParam.nCol2, aLocalParam.nRow2, true /*bNoMatrixAtAll*/); |
669 | 0 | if (!aTester.IsEditable()) |
670 | 0 | { |
671 | 0 | if (!bApi) |
672 | 0 | rDocShell.ErrorMessage(aTester.GetMessageId()); |
673 | 0 | return false; |
674 | 0 | } |
675 | | |
676 | 0 | const ScInputOptions aInputOption = ScModule::get()->GetInputOptions(); |
677 | 0 | const bool bUpdateRefs = aInputOption.GetSortRefUpdate(); |
678 | | |
679 | | // Adjust aLocalParam cols/rows to used data area. Keep sticky top row or |
680 | | // column (depending on direction) in any case, not just if it has headers, |
681 | | // so empty leading cells will be sorted to the end. |
682 | | // aLocalParam.nCol/Row will encompass data content only, extras in |
683 | | // aLocalParam.aDataAreaExtras. |
684 | 0 | bool bShrunk = false; |
685 | 0 | aLocalParam.aDataAreaExtras.resetArea(); |
686 | 0 | rDoc.ShrinkToUsedDataArea(bShrunk, nTab, aLocalParam.nCol1, aLocalParam.nRow1, |
687 | 0 | aLocalParam.nCol2, aLocalParam.nRow2, false, aLocalParam.bByRow, |
688 | 0 | !aLocalParam.bByRow, |
689 | 0 | (aLocalParam.aDataAreaExtras.anyExtrasWanted() ? |
690 | 0 | &aLocalParam.aDataAreaExtras : nullptr)); |
691 | |
|
692 | 0 | SCROW nStartRow = aLocalParam.nRow1; |
693 | 0 | if (aLocalParam.bByRow && aLocalParam.bHasHeader && nStartRow < aLocalParam.nRow2) |
694 | 0 | ++nStartRow; |
695 | |
|
696 | 0 | SCCOL nOverallCol1 = aLocalParam.nCol1; |
697 | 0 | SCROW nOverallRow1 = aLocalParam.nRow1; |
698 | 0 | SCCOL nOverallCol2 = aLocalParam.nCol2; |
699 | 0 | SCROW nOverallRow2 = aLocalParam.nRow2; |
700 | 0 | if (aLocalParam.aDataAreaExtras.anyExtrasWanted()) |
701 | 0 | { |
702 | | // Trailing empty excess columns/rows are excluded from being sorted, |
703 | | // they stick at the end. Clip them. |
704 | 0 | const ScDataAreaExtras::Clip eClip = (aLocalParam.bByRow ? |
705 | 0 | ScDataAreaExtras::Clip::Row : ScDataAreaExtras::Clip::Col); |
706 | 0 | aLocalParam.aDataAreaExtras.GetOverallRange( nOverallCol1, nOverallRow1, nOverallCol2, nOverallRow2, eClip); |
707 | | // Make it permanent. |
708 | 0 | aLocalParam.aDataAreaExtras.SetOverallRange( nOverallCol1, nOverallRow1, nOverallCol2, nOverallRow2); |
709 | |
|
710 | 0 | if (bUpdateRefs) |
711 | 0 | { |
712 | | // With update references the entire range needs to be handled as |
713 | | // one entity for references pointing within to be moved along, |
714 | | // even when there's no data content. For huge ranges we may be |
715 | | // DOOMed then. |
716 | 0 | aLocalParam.nCol1 = nOverallCol1; |
717 | 0 | aLocalParam.nRow1 = nOverallRow1; |
718 | 0 | aLocalParam.nCol2 = nOverallCol2; |
719 | 0 | aLocalParam.nRow2 = nOverallRow2; |
720 | 0 | } |
721 | 0 | } |
722 | |
|
723 | 0 | if (aLocalParam.aDataAreaExtras.mbCellFormats |
724 | 0 | && rDoc.HasAttrib( nOverallCol1, nStartRow, nTab, nOverallCol2, nOverallRow2, nTab, |
725 | 0 | HasAttrFlags::Merged | HasAttrFlags::Overlapped)) |
726 | 0 | { |
727 | | // Merge attributes would be mixed up during sorting. |
728 | 0 | if (!bApi) |
729 | 0 | rDocShell.ErrorMessage(STR_SORT_ERR_MERGED); |
730 | 0 | return false; |
731 | 0 | } |
732 | | |
733 | | // execute |
734 | | |
735 | 0 | weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); |
736 | | |
737 | | // Calculate the script types for all cells in the sort range beforehand. |
738 | | // This will speed up the row height adjustment that takes place after the |
739 | | // sort. |
740 | 0 | rDoc.UpdateScriptTypes( |
741 | 0 | ScAddress(aLocalParam.nCol1,nStartRow,nTab), |
742 | 0 | aLocalParam.nCol2-aLocalParam.nCol1+1, |
743 | 0 | aLocalParam.nRow2-nStartRow+1); |
744 | | |
745 | | // No point adjusting row heights after the sort when all rows have the same height. |
746 | 0 | bool bUniformRowHeight = rDoc.HasUniformRowHeight(nTab, nStartRow, nOverallRow2); |
747 | |
|
748 | 0 | bool bRepeatQuery = false; // repeat existing filter? |
749 | 0 | ScQueryParam aQueryParam; |
750 | 0 | pDBData->GetQueryParam( aQueryParam ); |
751 | 0 | if ( aQueryParam.GetEntry(0).bDoQuery ) |
752 | 0 | bRepeatQuery = true; |
753 | |
|
754 | 0 | sc::ReorderParam aUndoParam; |
755 | | |
756 | | // don't call ScDocument::Sort with an empty SortParam (may be empty here if bCopy is set), |
757 | | // but always call it for random shuffle which doesn't need sort keys |
758 | 0 | if (aLocalParam.meSortOrderType == SortOrderType::Random |
759 | 0 | || (aLocalParam.GetSortKeyCount() && aLocalParam.maKeyState[0].bDoSort)) |
760 | 0 | { |
761 | 0 | ScProgress aProgress(&rDocShell, ScResId(STR_PROGRESS_SORTING), 0, true); |
762 | 0 | if (!bRepeatQuery) |
763 | 0 | bRepeatQuery = rDoc.HasHiddenRows(aLocalParam.nRow1, aLocalParam.nRow2, nTab); |
764 | 0 | rDoc.Sort(nTab, aLocalParam, bRepeatQuery, bUpdateRefs, &aProgress, &aUndoParam); |
765 | 0 | } |
766 | |
|
767 | 0 | if (bRecord) |
768 | 0 | { |
769 | | // Set up an undo object. |
770 | 0 | rDocShell.GetUndoManager()->AddUndoAction( |
771 | 0 | std::make_unique<sc::UndoSort>(rDocShell, aUndoParam)); |
772 | 0 | } |
773 | |
|
774 | 0 | ScSortParam aSortParamData(rSortParam); |
775 | 0 | aSortParamData.meSortOrderType = SortOrderType::Ordered; |
776 | 0 | pDBData->SetSortParam(aSortParamData); |
777 | | // Remember additional settings on anonymous database ranges. |
778 | 0 | if (pDBData == rDoc.GetAnonymousDBData( nTab) || rDoc.GetDBCollection()->getAnonDBs().has( pDBData)) |
779 | 0 | pDBData->UpdateFromSortParam(aSortParamData); |
780 | |
|
781 | 0 | if (SfxViewShell* pKitSomeViewForThisDoc = comphelper::LibreOfficeKit::isActive() ? |
782 | 0 | rDocShell.GetBestViewShell(false) : nullptr) |
783 | 0 | { |
784 | 0 | SfxViewShell* pViewShell = SfxViewShell::GetFirst(); |
785 | 0 | while (pViewShell) |
786 | 0 | { |
787 | 0 | ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell); |
788 | 0 | if (pTabViewShell && pTabViewShell->GetDocId() == pKitSomeViewForThisDoc->GetDocId()) |
789 | 0 | { |
790 | 0 | if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKHeightHelper(nTab)) |
791 | 0 | pPosHelper->invalidateByIndex(nStartRow); |
792 | 0 | } |
793 | 0 | pViewShell = SfxViewShell::GetNext(*pViewShell); |
794 | 0 | } |
795 | |
|
796 | 0 | ScTabViewShell::notifyAllViewsSheetGeomInvalidation( |
797 | 0 | pKitSomeViewForThisDoc, false /* bColumns */, true /* bRows */, true /* bSizes*/, |
798 | 0 | true /* bHidden */, true /* bFiltered */, true /* bGroups */, nTab); |
799 | 0 | } |
800 | |
|
801 | 0 | if (nStartRow <= aLocalParam.nRow2) |
802 | 0 | { |
803 | 0 | ScRange aDirtyRange( |
804 | 0 | aLocalParam.nCol1, nStartRow, nTab, |
805 | 0 | aLocalParam.nCol2, aLocalParam.nRow2, nTab); |
806 | 0 | rDoc.SetDirty( aDirtyRange, true ); |
807 | 0 | } |
808 | |
|
809 | 0 | if (bPaint) |
810 | 0 | { |
811 | 0 | PaintPartFlags nPaint = PaintPartFlags::Grid; |
812 | 0 | SCCOL nStartX = nOverallCol1; |
813 | 0 | SCROW nStartY = nOverallRow1; |
814 | 0 | SCCOL nEndX = nOverallCol2; |
815 | 0 | SCROW nEndY = nOverallRow2; |
816 | 0 | if ( bRepeatQuery ) |
817 | 0 | { |
818 | 0 | nPaint |= PaintPartFlags::Left; |
819 | 0 | nStartX = 0; |
820 | 0 | nEndX = rDoc.MaxCol(); |
821 | 0 | } |
822 | 0 | rDocShell.PostPaint(ScRange(nStartX, nStartY, nTab, nEndX, nEndY, nTab), nPaint); |
823 | 0 | } |
824 | |
|
825 | 0 | if (!bUniformRowHeight && nStartRow <= nOverallRow2) |
826 | 0 | rDocShell.AdjustRowHeight(nStartRow, nOverallRow2, nTab); |
827 | |
|
828 | 0 | aModificator.SetDocumentModified(); |
829 | |
|
830 | 0 | return true; |
831 | 0 | } |
832 | | |
833 | | bool ScDBDocFunc::Query( SCTAB nTab, const ScQueryParam& rQueryParam, |
834 | | const ScRange* pAdvSource, bool bRecord, bool bApi ) |
835 | 0 | { |
836 | 0 | ScDocShellModificator aModificator( rDocShell ); |
837 | |
|
838 | 0 | ScDocument& rDoc = rDocShell.GetDocument(); |
839 | |
|
840 | 0 | ScTabViewShell* pViewSh = rDocShell.GetBestViewShell(); |
841 | 0 | if (pViewSh && ScTabViewShell::isAnyEditViewInRange(pViewSh, /*bColumns*/ false, rQueryParam.nRow1, rQueryParam.nRow2)) |
842 | 0 | { |
843 | 0 | return false; |
844 | 0 | } |
845 | | |
846 | 0 | if (bRecord && !rDoc.IsUndoEnabled()) |
847 | 0 | bRecord = false; |
848 | 0 | ScDBData* pDBData = rDoc.GetDBAtArea( nTab, rQueryParam.nCol1, rQueryParam.nRow1, |
849 | 0 | rQueryParam.nCol2, rQueryParam.nRow2 ); |
850 | 0 | if (!pDBData) |
851 | 0 | { |
852 | 0 | OSL_FAIL( "Query: no DBData" ); |
853 | 0 | return false; |
854 | 0 | } |
855 | | |
856 | | // Change from Inplace to non-Inplace, only then cancel Inplace: |
857 | | // (only if "Persistent" is selected in the dialog) |
858 | | |
859 | 0 | if ( !rQueryParam.bInplace && pDBData->HasQueryParam() && rQueryParam.bDestPers ) |
860 | 0 | { |
861 | 0 | ScQueryParam aOldQuery; |
862 | 0 | pDBData->GetQueryParam(aOldQuery); |
863 | 0 | if (aOldQuery.bInplace) |
864 | 0 | { |
865 | | // cancel old filtering |
866 | |
|
867 | 0 | SCSIZE nEC = aOldQuery.GetEntryCount(); |
868 | 0 | for (SCSIZE i=0; i<nEC; i++) |
869 | 0 | aOldQuery.GetEntry(i).bDoQuery = false; |
870 | 0 | aOldQuery.bDuplicate = true; |
871 | 0 | Query( nTab, aOldQuery, nullptr, bRecord, bApi ); |
872 | 0 | } |
873 | 0 | } |
874 | |
|
875 | 0 | ScQueryParam aLocalParam( rQueryParam ); // for Paint / destination range |
876 | 0 | bool bCopy = !rQueryParam.bInplace; // copied in Table::Query |
877 | 0 | ScDBData* pDestData = nullptr; // range to be copied to |
878 | 0 | bool bDoSize = false; // adjust destination size (insert/delete) |
879 | 0 | SCCOL nFormulaCols = 0; // only at bDoSize |
880 | 0 | bool bKeepFmt = false; |
881 | 0 | ScRange aOldDest; |
882 | 0 | ScRange aDestTotal; |
883 | 0 | if ( bCopy && rQueryParam.nDestCol == rQueryParam.nCol1 && |
884 | 0 | rQueryParam.nDestRow == rQueryParam.nRow1 && rQueryParam.nDestTab == nTab ) |
885 | 0 | bCopy = false; |
886 | 0 | SCTAB nDestTab = nTab; |
887 | 0 | if ( bCopy ) |
888 | 0 | { |
889 | 0 | aLocalParam.MoveToDest(); |
890 | 0 | nDestTab = rQueryParam.nDestTab; |
891 | 0 | if ( !rDoc.ValidColRow( aLocalParam.nCol2, aLocalParam.nRow2 ) ) |
892 | 0 | { |
893 | 0 | if (!bApi) |
894 | 0 | rDocShell.ErrorMessage(STR_PASTE_FULL); |
895 | 0 | return false; |
896 | 0 | } |
897 | | |
898 | 0 | if (!CheckSheetViewProtection(sc::Operation::Query)) |
899 | 0 | return false; |
900 | | |
901 | 0 | ScEditableTester aTester = ScEditableTester::CreateAndTestBlock(rDoc, nDestTab, |
902 | 0 | aLocalParam.nCol1, aLocalParam.nRow1, |
903 | 0 | aLocalParam.nCol2,aLocalParam.nRow2); |
904 | 0 | if (!aTester.IsEditable()) |
905 | 0 | { |
906 | 0 | if (!bApi) |
907 | 0 | rDocShell.ErrorMessage(aTester.GetMessageId()); |
908 | 0 | return false; |
909 | 0 | } |
910 | | |
911 | 0 | pDestData = rDoc.GetDBAtCursor( rQueryParam.nDestCol, rQueryParam.nDestRow, |
912 | 0 | rQueryParam.nDestTab, ScDBDataPortion::TOP_LEFT ); |
913 | 0 | if (pDestData) |
914 | 0 | { |
915 | 0 | pDestData->GetArea( aOldDest ); |
916 | 0 | aDestTotal=ScRange( rQueryParam.nDestCol, |
917 | 0 | rQueryParam.nDestRow, |
918 | 0 | nDestTab, |
919 | 0 | rQueryParam.nDestCol + rQueryParam.nCol2 - rQueryParam.nCol1, |
920 | 0 | rQueryParam.nDestRow + rQueryParam.nRow2 - rQueryParam.nRow1, |
921 | 0 | nDestTab ); |
922 | |
|
923 | 0 | bDoSize = pDestData->IsDoSize(); |
924 | | // test if formulas need to be filled in (nFormulaCols): |
925 | 0 | if ( bDoSize && aOldDest.aEnd.Col() == aDestTotal.aEnd.Col() ) |
926 | 0 | { |
927 | 0 | SCCOL nTestCol = aOldDest.aEnd.Col() + 1; // next to the range |
928 | 0 | SCROW nTestRow = rQueryParam.nDestRow + |
929 | 0 | ( aLocalParam.bHasHeader ? 1 : 0 ); |
930 | 0 | while ( nTestCol <= rDoc.MaxCol() && |
931 | 0 | rDoc.GetCellType(ScAddress( nTestCol, nTestRow, nTab )) == CELLTYPE_FORMULA ) |
932 | 0 | { |
933 | 0 | ++nTestCol; |
934 | 0 | ++nFormulaCols; |
935 | 0 | } |
936 | 0 | } |
937 | |
|
938 | 0 | bKeepFmt = pDestData->IsKeepFmt(); |
939 | 0 | if ( bDoSize && !rDoc.CanFitBlock( aOldDest, aDestTotal ) ) |
940 | 0 | { |
941 | 0 | if (!bApi) |
942 | 0 | rDocShell.ErrorMessage(STR_MSSG_DOSUBTOTALS_2); // cannot insert rows |
943 | 0 | return false; |
944 | 0 | } |
945 | 0 | } |
946 | 0 | } |
947 | | |
948 | | // execute |
949 | | |
950 | 0 | weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); |
951 | |
|
952 | 0 | bool bKeepSub = false; // repeat existing partial results? |
953 | 0 | bool bKeepTotals = false; |
954 | 0 | if (rQueryParam.GetEntry(0).bDoQuery) // not at cancellation |
955 | 0 | { |
956 | 0 | ScSubTotalParam aSubTotalParam; |
957 | 0 | pDBData->GetSubTotalParam( aSubTotalParam ); // partial results exist? |
958 | |
|
959 | 0 | if (aSubTotalParam.aGroups[0].bActive && !aSubTotalParam.bRemoveOnly) |
960 | 0 | bKeepSub = true; |
961 | |
|
962 | 0 | if (pDBData->HasTotals() && pDBData->GetTableStyleInfo()) |
963 | 0 | bKeepTotals = true; |
964 | 0 | } |
965 | |
|
966 | 0 | ScDocumentUniquePtr pUndoDoc; |
967 | 0 | std::unique_ptr<ScDBCollection> pUndoDB; |
968 | 0 | const ScRange* pOld = nullptr; |
969 | |
|
970 | 0 | if ( bRecord ) |
971 | 0 | { |
972 | 0 | pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO )); |
973 | 0 | if (bCopy) |
974 | 0 | { |
975 | 0 | pUndoDoc->InitUndo( rDoc, nDestTab, nDestTab, false, true ); |
976 | 0 | rDoc.CopyToDocument(aLocalParam.nCol1, aLocalParam.nRow1, nDestTab, |
977 | 0 | aLocalParam.nCol2, aLocalParam.nRow2, nDestTab, |
978 | 0 | InsertDeleteFlags::ALL, false, *pUndoDoc); |
979 | | // secure attributes in case they were copied along |
980 | |
|
981 | 0 | if (pDestData) |
982 | 0 | { |
983 | 0 | rDoc.CopyToDocument(aOldDest, InsertDeleteFlags::ALL, false, *pUndoDoc); |
984 | 0 | pOld = &aOldDest; |
985 | 0 | } |
986 | 0 | } |
987 | 0 | else |
988 | 0 | { |
989 | 0 | pUndoDoc->InitUndo( rDoc, nTab, nTab, false, true ); |
990 | 0 | rDoc.CopyToDocument(0, rQueryParam.nRow1, nTab, rDoc.MaxCol(), rQueryParam.nRow2, nTab, |
991 | 0 | InsertDeleteFlags::NONE, false, *pUndoDoc); |
992 | 0 | } |
993 | |
|
994 | 0 | ScDBCollection* pDocDB = rDoc.GetDBCollection(); |
995 | 0 | if (!pDocDB->empty()) |
996 | 0 | pUndoDB.reset(new ScDBCollection( *pDocDB )); |
997 | |
|
998 | 0 | rDoc.BeginDrawUndo(); |
999 | 0 | } |
1000 | |
|
1001 | 0 | std::unique_ptr<ScDocument> pAttribDoc; |
1002 | 0 | ScRange aAttribRange; |
1003 | 0 | if (pDestData) // delete destination range |
1004 | 0 | { |
1005 | 0 | if ( bKeepFmt ) |
1006 | 0 | { |
1007 | | // smaller of the end columns, header+1 row |
1008 | 0 | aAttribRange = aOldDest; |
1009 | 0 | if ( aAttribRange.aEnd.Col() > aDestTotal.aEnd.Col() ) |
1010 | 0 | aAttribRange.aEnd.SetCol( aDestTotal.aEnd.Col() ); |
1011 | 0 | aAttribRange.aEnd.SetRow( aAttribRange.aStart.Row() + |
1012 | 0 | ( aLocalParam.bHasHeader ? 1 : 0 ) ); |
1013 | | |
1014 | | // also for filled-in formulas |
1015 | 0 | aAttribRange.aEnd.SetCol( aAttribRange.aEnd.Col() + nFormulaCols ); |
1016 | |
|
1017 | 0 | pAttribDoc.reset(new ScDocument( SCDOCMODE_UNDO )); |
1018 | 0 | pAttribDoc->InitUndo( rDoc, nDestTab, nDestTab, false, true ); |
1019 | 0 | rDoc.CopyToDocument(aAttribRange, InsertDeleteFlags::ATTRIB, false, *pAttribDoc); |
1020 | 0 | } |
1021 | |
|
1022 | 0 | if ( bDoSize ) |
1023 | 0 | rDoc.FitBlock( aOldDest, aDestTotal ); |
1024 | 0 | else |
1025 | 0 | rDoc.DeleteAreaTab(aOldDest, InsertDeleteFlags::ALL); // simply delete |
1026 | 0 | } |
1027 | | |
1028 | | // execute filtering on the document |
1029 | 0 | SCSIZE nCount = rDoc.Query( nTab, rQueryParam, bKeepSub, bKeepTotals ); |
1030 | 0 | pDBData->CalcSaveFilteredCount( nCount ); |
1031 | 0 | if (bCopy) |
1032 | 0 | { |
1033 | 0 | aLocalParam.nRow2 = aLocalParam.nRow1 + nCount; |
1034 | 0 | if (!aLocalParam.bHasHeader && nCount > 0) |
1035 | 0 | --aLocalParam.nRow2; |
1036 | |
|
1037 | 0 | if ( bDoSize ) |
1038 | 0 | { |
1039 | | // adjust to the real result range |
1040 | | // (this here is always a reduction) |
1041 | |
|
1042 | 0 | ScRange aNewDest( aLocalParam.nCol1, aLocalParam.nRow1, nDestTab, |
1043 | 0 | aLocalParam.nCol2, aLocalParam.nRow2, nDestTab ); |
1044 | 0 | rDoc.FitBlock( aDestTotal, aNewDest, false ); // sal_False - don't delete |
1045 | |
|
1046 | 0 | if ( nFormulaCols > 0 ) |
1047 | 0 | { |
1048 | | // fill in formulas |
1049 | | //! Undo (Query and Repeat) !!! |
1050 | |
|
1051 | 0 | ScRange aNewForm( aLocalParam.nCol2+1, aLocalParam.nRow1, nDestTab, |
1052 | 0 | aLocalParam.nCol2+nFormulaCols, aLocalParam.nRow2, nDestTab ); |
1053 | 0 | ScRange aOldForm = aNewForm; |
1054 | 0 | aOldForm.aEnd.SetRow( aOldDest.aEnd.Row() ); |
1055 | 0 | rDoc.FitBlock( aOldForm, aNewForm, false ); |
1056 | |
|
1057 | 0 | ScMarkData aMark(rDoc.GetSheetLimits()); |
1058 | 0 | aMark.SelectOneTable(nDestTab); |
1059 | 0 | SCROW nFStartY = aLocalParam.nRow1 + ( aLocalParam.bHasHeader ? 1 : 0 ); |
1060 | |
|
1061 | 0 | sal_uLong nProgCount = nFormulaCols; |
1062 | 0 | nProgCount *= aLocalParam.nRow2 - nFStartY; |
1063 | 0 | ScProgress aProgress( rDoc.GetDocumentShell(), |
1064 | 0 | ScResId(STR_FILL_SERIES_PROGRESS), nProgCount, true ); |
1065 | |
|
1066 | 0 | rDoc.Fill( aLocalParam.nCol2+1, nFStartY, |
1067 | 0 | aLocalParam.nCol2+nFormulaCols, nFStartY, &aProgress, aMark, |
1068 | 0 | aLocalParam.nRow2 - nFStartY, |
1069 | 0 | FILL_TO_BOTTOM, FILL_SIMPLE ); |
1070 | 0 | } |
1071 | 0 | } |
1072 | |
|
1073 | 0 | if ( pAttribDoc ) // copy back the memorized attributes |
1074 | 0 | { |
1075 | | // Header |
1076 | 0 | if (aLocalParam.bHasHeader) |
1077 | 0 | { |
1078 | 0 | ScRange aHdrRange = aAttribRange; |
1079 | 0 | aHdrRange.aEnd.SetRow( aHdrRange.aStart.Row() ); |
1080 | 0 | pAttribDoc->CopyToDocument(aHdrRange, InsertDeleteFlags::ATTRIB, false, rDoc); |
1081 | 0 | } |
1082 | | |
1083 | | // Data |
1084 | 0 | SCCOL nAttrEndCol = aAttribRange.aEnd.Col(); |
1085 | 0 | SCROW nAttrRow = aAttribRange.aStart.Row() + ( aLocalParam.bHasHeader ? 1 : 0 ); |
1086 | 0 | for (SCCOL nCol = aAttribRange.aStart.Col(); nCol<=nAttrEndCol; nCol++) |
1087 | 0 | { |
1088 | 0 | const ScPatternAttr* pSrcPattern = pAttribDoc->GetPattern( |
1089 | 0 | nCol, nAttrRow, nDestTab ); |
1090 | 0 | OSL_ENSURE(pSrcPattern,"Pattern is 0"); |
1091 | 0 | if (pSrcPattern) |
1092 | 0 | { |
1093 | 0 | rDoc.ApplyPatternAreaTab( nCol, nAttrRow, nCol, aLocalParam.nRow2, |
1094 | 0 | nDestTab, *pSrcPattern ); |
1095 | 0 | const ScStyleSheet* pStyle = pSrcPattern->GetStyleSheet(); |
1096 | 0 | if (pStyle) |
1097 | 0 | rDoc.ApplyStyleAreaTab( nCol, nAttrRow, nCol, aLocalParam.nRow2, |
1098 | 0 | nDestTab, *pStyle ); |
1099 | 0 | } |
1100 | 0 | } |
1101 | 0 | } |
1102 | 0 | } |
1103 | | |
1104 | | // saving: Inplace always, otherwise depending on setting |
1105 | | // old Inplace-Filter may have already been removed |
1106 | |
|
1107 | 0 | bool bSave = rQueryParam.bInplace || rQueryParam.bDestPers; |
1108 | 0 | if (bSave) // memorize |
1109 | 0 | { |
1110 | 0 | pDBData->SetQueryParam( rQueryParam ); |
1111 | 0 | pDBData->SetHeader( rQueryParam.bHasHeader ); //! ??? |
1112 | 0 | pDBData->SetAdvancedQuerySource( pAdvSource ); // after SetQueryParam |
1113 | 0 | } |
1114 | |
|
1115 | 0 | if (bCopy) // memorize new DB range |
1116 | 0 | { |
1117 | | // Selection is done afterwards from outside (dbfunc). |
1118 | | // Currently through the DB area at the destination position, |
1119 | | // so a range must be created there in any case. |
1120 | |
|
1121 | 0 | ScDBData* pNewData; |
1122 | 0 | if (pDestData) |
1123 | 0 | pNewData = pDestData; // range exists -> adjust (always!) |
1124 | 0 | else // create range |
1125 | 0 | pNewData = rDocShell.GetDBData( |
1126 | 0 | ScRange( aLocalParam.nCol1, aLocalParam.nRow1, nDestTab, |
1127 | 0 | aLocalParam.nCol2, aLocalParam.nRow2, nDestTab ), |
1128 | 0 | SC_DB_MAKE, ScGetDBSelection::ForceMark ); |
1129 | |
|
1130 | 0 | if (pNewData) |
1131 | 0 | { |
1132 | 0 | pNewData->SetArea( nDestTab, aLocalParam.nCol1, aLocalParam.nRow1, |
1133 | 0 | aLocalParam.nCol2, aLocalParam.nRow2 ); |
1134 | | |
1135 | | // query parameter is no longer set at the destination, only leads to confusion |
1136 | | // and mistakes with the query parameter at the source range (#37187#) |
1137 | 0 | } |
1138 | 0 | else |
1139 | 0 | { |
1140 | 0 | OSL_FAIL("Target are not available"); |
1141 | 0 | } |
1142 | 0 | } |
1143 | |
|
1144 | 0 | if (!bCopy) |
1145 | 0 | { |
1146 | 0 | rDoc.InvalidatePageBreaks(nTab); |
1147 | 0 | rDoc.UpdatePageBreaks( nTab ); |
1148 | 0 | } |
1149 | | |
1150 | | // #i23299# Subtotal functions depend on cell's filtered states. |
1151 | 0 | ScRange aDirtyRange(0 , aLocalParam.nRow1, nDestTab, rDoc.MaxCol(), aLocalParam.nRow2, nDestTab); |
1152 | 0 | rDoc.SetSubTotalCellsDirty(aDirtyRange); |
1153 | |
|
1154 | 0 | if ( bRecord ) |
1155 | 0 | { |
1156 | | // create undo action after executing, because of drawing layer undo |
1157 | 0 | rDocShell.GetUndoManager()->AddUndoAction( |
1158 | 0 | std::make_unique<ScUndoQuery>( rDocShell, nTab, rQueryParam, std::move(pUndoDoc), std::move(pUndoDB), |
1159 | 0 | pOld, bDoSize, pAdvSource ) ); |
1160 | 0 | } |
1161 | |
|
1162 | 0 | if ( pViewSh ) |
1163 | 0 | { |
1164 | | // could there be horizontal autofilter ? |
1165 | | // maybe it would be better to set bColumns to !rQueryParam.bByRow ? |
1166 | | // anyway at the beginning the value of bByRow is 'false' |
1167 | | // then after the first sort action it becomes 'true' |
1168 | 0 | pViewSh->OnLOKShowHideColRow(/*bColumns*/ false, rQueryParam.nRow1 - 1); |
1169 | 0 | } |
1170 | |
|
1171 | 0 | if (bCopy) |
1172 | 0 | { |
1173 | 0 | SCCOL nEndX = aLocalParam.nCol2; |
1174 | 0 | SCROW nEndY = aLocalParam.nRow2; |
1175 | 0 | if (pDestData) |
1176 | 0 | { |
1177 | 0 | if ( aOldDest.aEnd.Col() > nEndX ) |
1178 | 0 | nEndX = aOldDest.aEnd.Col(); |
1179 | 0 | if ( aOldDest.aEnd.Row() > nEndY ) |
1180 | 0 | nEndY = aOldDest.aEnd.Row(); |
1181 | 0 | } |
1182 | 0 | if (bDoSize) |
1183 | 0 | nEndY = rDoc.MaxRow(); |
1184 | | |
1185 | | // remove AutoFilter button flags |
1186 | 0 | rDocShell.DBAreaDeleted(nDestTab, aLocalParam.nCol1, aLocalParam.nRow1, aLocalParam.nCol2); |
1187 | |
|
1188 | 0 | rDocShell.PostPaint( |
1189 | 0 | ScRange(aLocalParam.nCol1, aLocalParam.nRow1, nDestTab, nEndX, nEndY, nDestTab), |
1190 | 0 | PaintPartFlags::Grid); |
1191 | 0 | } |
1192 | 0 | else |
1193 | 0 | rDocShell.PostPaint( |
1194 | 0 | ScRange(0, rQueryParam.nRow1, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab), |
1195 | 0 | PaintPartFlags::Grid | PaintPartFlags::Left); |
1196 | 0 | aModificator.SetDocumentModified(); |
1197 | |
|
1198 | 0 | return true; |
1199 | 0 | } |
1200 | | |
1201 | | void ScDBDocFunc::DoSubTotals( SCTAB nTab, const ScSubTotalParam& rParam, |
1202 | | bool bRecord, bool bApi ) |
1203 | 0 | { |
1204 | | //! use also for ScDBFunc::DoSubTotals ! |
1205 | | // then stays outside: |
1206 | | // - mark new range (from DBData) |
1207 | | // - SelectionChanged (?) |
1208 | |
|
1209 | 0 | bool bDo = !rParam.bRemoveOnly; // sal_False = only delete |
1210 | |
|
1211 | 0 | ScDocument& rDoc = rDocShell.GetDocument(); |
1212 | 0 | if (bRecord && !rDoc.IsUndoEnabled()) |
1213 | 0 | bRecord = false; |
1214 | 0 | ScDBData* pDBData = rDoc.GetDBAtArea( nTab, rParam.nCol1, rParam.nRow1, |
1215 | 0 | rParam.nCol2, rParam.nRow2 ); |
1216 | 0 | if (!pDBData) |
1217 | 0 | { |
1218 | 0 | OSL_FAIL( "SubTotals: no DBData" ); |
1219 | 0 | return; |
1220 | 0 | } |
1221 | | |
1222 | 0 | if (!CheckSheetViewProtection(sc::Operation::SubTotals)) |
1223 | 0 | return; |
1224 | | |
1225 | 0 | ScEditableTester aTester = ScEditableTester::CreateAndTestBlock(rDoc, nTab, 0, rParam.nRow1 + 1, rDoc.MaxCol(), rDoc.MaxRow()); |
1226 | 0 | if (!aTester.IsEditable()) |
1227 | 0 | { |
1228 | 0 | if (!bApi) |
1229 | 0 | rDocShell.ErrorMessage(aTester.GetMessageId()); |
1230 | 0 | return; |
1231 | 0 | } |
1232 | | |
1233 | 0 | if (rDoc.HasAttrib( rParam.nCol1, rParam.nRow1+1, nTab, |
1234 | 0 | rParam.nCol2, rParam.nRow2, nTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped )) |
1235 | 0 | { |
1236 | 0 | if (!bApi) |
1237 | 0 | rDocShell.ErrorMessage(STR_MSSG_INSERTCELLS_0); // don't insert into merged |
1238 | 0 | return; |
1239 | 0 | } |
1240 | | |
1241 | 0 | bool bOk = true; |
1242 | 0 | if (rParam.bReplace) |
1243 | 0 | { |
1244 | 0 | if (rDoc.TestRemoveSubTotals( nTab, rParam )) |
1245 | 0 | { |
1246 | 0 | std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(), |
1247 | 0 | VclMessageType::Question, |
1248 | 0 | VclButtonsType::YesNo, ScResId(STR_MSSG_DOSUBTOTALS_1))); // "Delete Data?" |
1249 | 0 | xBox->set_title(ScResId(STR_MSSG_DOSUBTOTALS_0)); // "StarCalc" |
1250 | 0 | bOk = xBox->run() == RET_YES; |
1251 | 0 | } |
1252 | 0 | } |
1253 | |
|
1254 | 0 | if (!bOk) |
1255 | 0 | return; |
1256 | | |
1257 | 0 | weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); |
1258 | 0 | ScDocShellModificator aModificator( rDocShell ); |
1259 | |
|
1260 | 0 | ScSubTotalParam aNewParam( rParam ); // end of range is being changed |
1261 | 0 | ScDocumentUniquePtr pUndoDoc; |
1262 | 0 | std::unique_ptr<ScOutlineTable> pUndoTab; |
1263 | 0 | std::unique_ptr<ScRangeName> pUndoRange; |
1264 | 0 | std::unique_ptr<ScDBCollection> pUndoDB; |
1265 | |
|
1266 | 0 | if (bRecord) // secure old data |
1267 | 0 | { |
1268 | 0 | bool bOldFilter = bDo && rParam.bDoSort; |
1269 | |
|
1270 | 0 | SCTAB nTabCount = rDoc.GetTableCount(); |
1271 | 0 | pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO )); |
1272 | 0 | ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab ); |
1273 | 0 | if (pTable) |
1274 | 0 | { |
1275 | 0 | pUndoTab.reset(new ScOutlineTable( *pTable )); |
1276 | | |
1277 | | // column/row state |
1278 | 0 | SCCOLROW nOutStartCol, nOutEndCol; |
1279 | 0 | SCCOLROW nOutStartRow, nOutEndRow; |
1280 | 0 | pTable->GetColArray().GetRange( nOutStartCol, nOutEndCol ); |
1281 | 0 | pTable->GetRowArray().GetRange( nOutStartRow, nOutEndRow ); |
1282 | |
|
1283 | 0 | pUndoDoc->InitUndo( rDoc, nTab, nTab, true, true ); |
1284 | 0 | rDoc.CopyToDocument(static_cast<SCCOL>(nOutStartCol), 0, nTab, static_cast<SCCOL>(nOutEndCol), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false, *pUndoDoc); |
1285 | 0 | rDoc.CopyToDocument(0, nOutStartRow, nTab, rDoc.MaxCol(), nOutEndRow, nTab, InsertDeleteFlags::NONE, false, *pUndoDoc); |
1286 | 0 | } |
1287 | 0 | else |
1288 | 0 | pUndoDoc->InitUndo( rDoc, nTab, nTab, false, bOldFilter ); |
1289 | | |
1290 | | // secure data range - incl. filtering result |
1291 | 0 | rDoc.CopyToDocument(0, rParam.nRow1+1,nTab, rDoc.MaxCol(),rParam.nRow2,nTab, |
1292 | 0 | InsertDeleteFlags::ALL, false, *pUndoDoc); |
1293 | | |
1294 | | // all formulas because of references |
1295 | 0 | rDoc.CopyToDocument(0, 0, 0, rDoc.MaxCol(),rDoc.MaxRow(),nTabCount-1, |
1296 | 0 | InsertDeleteFlags::FORMULA, false, *pUndoDoc); |
1297 | | |
1298 | | // ranges of DB and other |
1299 | 0 | ScRangeName* pDocRange = rDoc.GetRangeName(); |
1300 | 0 | if (!pDocRange->empty()) |
1301 | 0 | pUndoRange.reset(new ScRangeName( *pDocRange )); |
1302 | 0 | ScDBCollection* pDocDB = rDoc.GetDBCollection(); |
1303 | 0 | if (!pDocDB->empty()) |
1304 | 0 | pUndoDB.reset(new ScDBCollection( *pDocDB )); |
1305 | 0 | } |
1306 | | |
1307 | | // rDoc.SetOutlineTable( nTab, NULL ); |
1308 | 0 | ScOutlineTable* pOut = rDoc.GetOutlineTable( nTab ); |
1309 | 0 | if (pOut) |
1310 | 0 | pOut->GetRowArray().RemoveAll(); // only delete row outlines |
1311 | |
|
1312 | 0 | if (rParam.bReplace) |
1313 | 0 | rDoc.RemoveSubTotals( nTab, aNewParam ); |
1314 | 0 | bool bSuccess = true; |
1315 | 0 | if (bDo) |
1316 | 0 | { |
1317 | | // sort |
1318 | 0 | if ( rParam.bDoSort ) |
1319 | 0 | { |
1320 | 0 | pDBData->SetArea( nTab, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 ); |
1321 | | |
1322 | | // set partial result field to before the sorting |
1323 | | // (Duplicates are omitted, so can be called again) |
1324 | |
|
1325 | 0 | ScSortParam aOldSort; |
1326 | 0 | pDBData->GetSortParam( aOldSort ); |
1327 | 0 | ScSortParam aSortParam( aNewParam, aOldSort ); |
1328 | 0 | Sort( nTab, aSortParam, false, false, bApi ); |
1329 | 0 | } |
1330 | |
|
1331 | 0 | bSuccess = rDoc.DoSubTotals( nTab, aNewParam ); |
1332 | 0 | rDoc.SetDrawPageSize(nTab); |
1333 | 0 | } |
1334 | 0 | ScRange aDirtyRange( aNewParam.nCol1, aNewParam.nRow1, nTab, |
1335 | 0 | aNewParam.nCol2, aNewParam.nRow2, nTab ); |
1336 | 0 | rDoc.SetDirty( aDirtyRange, true ); |
1337 | |
|
1338 | 0 | if (bRecord) |
1339 | 0 | { |
1340 | | // ScDBData* pUndoDBData = pDBData ? new ScDBData( *pDBData ) : NULL; |
1341 | 0 | rDocShell.GetUndoManager()->AddUndoAction( |
1342 | 0 | std::make_unique<ScUndoSubTotals>( rDocShell, nTab, |
1343 | 0 | rParam, aNewParam.nRow2, |
1344 | 0 | std::move(pUndoDoc), std::move(pUndoTab), // pUndoDBData, |
1345 | 0 | std::move(pUndoRange), std::move(pUndoDB) ) ); |
1346 | 0 | } |
1347 | |
|
1348 | 0 | if (!bSuccess) |
1349 | 0 | { |
1350 | | // "Cannot insert rows" |
1351 | 0 | if (!bApi) |
1352 | 0 | rDocShell.ErrorMessage(STR_MSSG_DOSUBTOTALS_2); |
1353 | 0 | } |
1354 | | |
1355 | | // memorize |
1356 | 0 | pDBData->SetSubTotalParam( aNewParam ); |
1357 | 0 | pDBData->SetArea( nTab, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 ); |
1358 | 0 | rDoc.CompileDBFormula(); |
1359 | |
|
1360 | 0 | rDocShell.PostPaint(ScRange(0, 0, nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab), |
1361 | 0 | PaintPartFlags::Grid | PaintPartFlags::Left | PaintPartFlags::Top | PaintPartFlags::Size); |
1362 | 0 | aModificator.SetDocumentModified(); |
1363 | 0 | } |
1364 | | |
1365 | | void ScDBDocFunc::DoTableSubTotals( SCTAB nTab, const ScDBData& rNewData, const ScSubTotalParam& rParam, |
1366 | | bool bRecord, bool bApi ) |
1367 | 0 | { |
1368 | 0 | bool bDo = !rParam.bRemoveOnly; // sal_False = only delete |
1369 | |
|
1370 | 0 | ScDocument& rDoc = rDocShell.GetDocument(); |
1371 | 0 | if (bRecord && !rDoc.IsUndoEnabled()) |
1372 | 0 | bRecord = false; |
1373 | |
|
1374 | 0 | ScDBData* pDBData = rDoc.GetDBAtArea(nTab, rParam.nCol1, rParam.nRow1, rParam.nCol2, rParam.nRow2); |
1375 | 0 | if (!pDBData) |
1376 | 0 | { |
1377 | 0 | OSL_FAIL("SubTotals: no DBData"); |
1378 | 0 | return; |
1379 | 0 | } |
1380 | | |
1381 | 0 | ScEditableTester aTester = ScEditableTester::CreateAndTestBlock(rDoc, nTab, 0, rParam.nRow1 + 1, rDoc.MaxCol(), rDoc.MaxRow()); |
1382 | 0 | if (!aTester.IsEditable()) |
1383 | 0 | { |
1384 | 0 | if (!bApi) |
1385 | 0 | rDocShell.ErrorMessage(aTester.GetMessageId()); |
1386 | 0 | return; |
1387 | 0 | } |
1388 | | |
1389 | 0 | if (rDoc.HasAttrib(rParam.nCol1, rParam.nRow1 + 1, nTab, rParam.nCol2, rParam.nRow2, nTab, |
1390 | 0 | HasAttrFlags::Merged | HasAttrFlags::Overlapped)) |
1391 | 0 | { |
1392 | 0 | if (!bApi) |
1393 | 0 | rDocShell.ErrorMessage(STR_MSSG_INSERTCELLS_0); // don't insert into merged |
1394 | 0 | return; |
1395 | 0 | } |
1396 | | |
1397 | 0 | weld::WaitObject aWait(ScDocShell::GetActiveDialogParent()); |
1398 | 0 | ScDocShellModificator aModificator(rDocShell); |
1399 | |
|
1400 | 0 | ScSubTotalParam aNewParam; |
1401 | 0 | rNewData.GetSubTotalParam(aNewParam); // end of range is being changed |
1402 | 0 | ScDocumentUniquePtr pUndoDoc; |
1403 | 0 | std::unique_ptr<ScDBCollection> pUndoDB; |
1404 | |
|
1405 | 0 | if (bRecord) // secure old data |
1406 | 0 | { |
1407 | 0 | bool bOldFilter = bDo && rParam.bDoSort; |
1408 | |
|
1409 | 0 | SCTAB nTabCount = rDoc.GetTableCount(); |
1410 | 0 | pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO)); |
1411 | 0 | pUndoDoc->InitUndo(rDoc, nTab, nTab, false, bOldFilter); |
1412 | | |
1413 | | // secure data range - incl. filtering result |
1414 | 0 | rDoc.CopyToDocument(rParam.nCol1, rParam.nRow1 + 1, nTab, rParam.nCol2, rParam.nRow2, nTab, |
1415 | 0 | InsertDeleteFlags::ALL, false, *pUndoDoc); |
1416 | | |
1417 | | // all formulas because of references |
1418 | 0 | rDoc.CopyToDocument(0, 0, 0, rDoc.MaxCol(), rDoc.MaxRow(), nTabCount - 1, |
1419 | 0 | InsertDeleteFlags::FORMULA, false, *pUndoDoc); |
1420 | | |
1421 | | // ranges of DB |
1422 | 0 | ScDBCollection* pDocDB = rDoc.GetDBCollection(); |
1423 | 0 | if (!pDocDB->empty()) |
1424 | 0 | pUndoDB.reset(new ScDBCollection(*pDocDB)); |
1425 | 0 | } |
1426 | |
|
1427 | 0 | if (rParam.bReplace) |
1428 | 0 | rDoc.RemoveTableSubTotals(nTab, aNewParam, rParam); |
1429 | 0 | bool bSuccess = true; |
1430 | 0 | if (bDo) |
1431 | 0 | { |
1432 | 0 | bSuccess = rDoc.DoTableSubTotals(nTab, aNewParam); |
1433 | 0 | rDoc.SetDrawPageSize(nTab); |
1434 | 0 | } |
1435 | 0 | ScRange aDirtyRange(aNewParam.nCol1, aNewParam.nRow1, nTab, aNewParam.nCol2, aNewParam.nRow2, |
1436 | 0 | nTab); |
1437 | 0 | rDoc.SetDirty(aDirtyRange, true); |
1438 | | |
1439 | | // Need to store with the new values |
1440 | 0 | *pDBData = rNewData; |
1441 | 0 | if (bRecord) |
1442 | 0 | { |
1443 | 0 | ScDBCollection* pDocDB = rDoc.GetDBCollection(); |
1444 | 0 | rDocShell.GetUndoManager()->AddUndoAction(std::make_unique<ScUndoTableTotals>( |
1445 | 0 | rDocShell, nTab, rParam, aNewParam.nRow2, std::move(pUndoDoc), |
1446 | 0 | std::move(pUndoDB), std::make_unique<ScDBCollection>(*pDocDB))); |
1447 | 0 | } |
1448 | |
|
1449 | 0 | if (!bSuccess) |
1450 | 0 | { |
1451 | | // "Cannot insert rows" |
1452 | 0 | if (!bApi) |
1453 | 0 | rDocShell.ErrorMessage(STR_MSSG_DOSUBTOTALS_2); |
1454 | 0 | } |
1455 | | |
1456 | | // store |
1457 | 0 | pDBData->SetSubTotalParam(aNewParam); |
1458 | 0 | pDBData->SetArea(nTab, aNewParam.nCol1, aNewParam.nRow1, aNewParam.nCol2, aNewParam.nRow2); |
1459 | 0 | rDoc.CompileDBFormula(); |
1460 | |
|
1461 | 0 | rDocShell.PostPaint(ScRange(0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab), |
1462 | 0 | PaintPartFlags::Grid | PaintPartFlags::Left | PaintPartFlags::Top |
1463 | 0 | | PaintPartFlags::Size); |
1464 | 0 | aModificator.SetDocumentModified(); |
1465 | 0 | } |
1466 | | |
1467 | | namespace { |
1468 | | |
1469 | | bool lcl_EmptyExcept( ScDocument& rDoc, const ScRange& rRange, const ScRange& rExcept ) |
1470 | 0 | { |
1471 | 0 | ScCellIterator aIter( rDoc, rRange ); |
1472 | 0 | for (bool bHasCell = aIter.first(); bHasCell; bHasCell = aIter.next()) |
1473 | 0 | { |
1474 | 0 | if (!aIter.isEmpty()) // real content? |
1475 | 0 | { |
1476 | 0 | if (!rExcept.Contains(aIter.GetPos())) |
1477 | 0 | return false; // cell found |
1478 | 0 | } |
1479 | 0 | } |
1480 | | |
1481 | 0 | return true; // nothing found - empty |
1482 | 0 | } |
1483 | | |
1484 | | bool isEditable(ScDocShell& rDocShell, const ScRangeList& rRanges, bool bApi, |
1485 | | sc::EditAction eAction = sc::EditAction::Unknown) |
1486 | 0 | { |
1487 | 0 | ScDocument& rDoc = rDocShell.GetDocument(); |
1488 | 0 | if (!rDocShell.IsEditable() || rDoc.GetChangeTrack()) |
1489 | 0 | { |
1490 | | // not recorded -> disallow |
1491 | 0 | if (!bApi) |
1492 | 0 | rDocShell.ErrorMessage(STR_PROTECTIONERR); |
1493 | |
|
1494 | 0 | return false; |
1495 | 0 | } |
1496 | | |
1497 | 0 | for (size_t i = 0, n = rRanges.size(); i < n; ++i) |
1498 | 0 | { |
1499 | 0 | const ScRange & r = rRanges[i]; |
1500 | 0 | ScEditableTester aTester = ScEditableTester::CreateAndTestRange(rDoc, r, eAction); |
1501 | 0 | if (!aTester.IsEditable()) |
1502 | 0 | { |
1503 | 0 | if (!bApi) |
1504 | 0 | rDocShell.ErrorMessage(aTester.GetMessageId()); |
1505 | |
|
1506 | 0 | return false; |
1507 | 0 | } |
1508 | 0 | } |
1509 | | |
1510 | 0 | return true; |
1511 | 0 | } |
1512 | | |
1513 | | void createUndoDoc(ScDocumentUniquePtr& pUndoDoc, ScDocument& rDoc, const ScRange& rRange) |
1514 | 0 | { |
1515 | 0 | SCTAB nTab = rRange.aStart.Tab(); |
1516 | 0 | pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO)); |
1517 | 0 | pUndoDoc->InitUndo(rDoc, nTab, nTab); |
1518 | 0 | rDoc.CopyToDocument(rRange, InsertDeleteFlags::ALL, false, *pUndoDoc); |
1519 | 0 | } |
1520 | | |
1521 | | bool checkNewOutputRange(ScDPObject& rDPObj, ScDocShell& rDocShell, ScRange& rNewOut, bool bApi, |
1522 | | sc::EditAction eAction = sc::EditAction::Unknown) |
1523 | 0 | { |
1524 | 0 | ScDocument& rDoc = rDocShell.GetDocument(); |
1525 | |
|
1526 | 0 | bool bOverflow = false; |
1527 | 0 | rNewOut = rDPObj.GetNewOutputRange(bOverflow); |
1528 | | |
1529 | | // Test for overlap with source data range. |
1530 | | // TODO: Check with other pivot tables as well. |
1531 | 0 | const ScSheetSourceDesc* pSheetDesc = rDPObj.GetSheetDesc(); |
1532 | 0 | if (pSheetDesc && pSheetDesc->GetSourceRange().Intersects(rNewOut)) |
1533 | 0 | { |
1534 | | // New output range intersteps with the source data. Move it up to |
1535 | | // where the old range is and see if that works. |
1536 | 0 | ScRange aOldRange = rDPObj.GetOutRange(); |
1537 | 0 | SCROW nDiff = aOldRange.aStart.Row() - rNewOut.aStart.Row(); |
1538 | 0 | rNewOut.aStart.SetRow(aOldRange.aStart.Row()); |
1539 | 0 | rNewOut.aEnd.IncRow(nDiff); |
1540 | 0 | if (!rDoc.ValidRow(rNewOut.aStart.Row()) || !rDoc.ValidRow(rNewOut.aEnd.Row())) |
1541 | 0 | bOverflow = true; |
1542 | 0 | } |
1543 | |
|
1544 | 0 | if (bOverflow) |
1545 | 0 | { |
1546 | 0 | if (!bApi) |
1547 | 0 | rDocShell.ErrorMessage(STR_PIVOT_ERROR); |
1548 | |
|
1549 | 0 | return false; |
1550 | 0 | } |
1551 | | |
1552 | 0 | if (!rDoc.IsImportingXML()) |
1553 | 0 | { |
1554 | 0 | ScEditableTester aTester = ScEditableTester::CreateAndTestRange(rDoc, rNewOut, eAction); |
1555 | 0 | if (!aTester.IsEditable()) |
1556 | 0 | { |
1557 | | // destination area isn't editable |
1558 | 0 | if (!bApi) |
1559 | 0 | rDocShell.ErrorMessage(aTester.GetMessageId()); |
1560 | |
|
1561 | 0 | return false; |
1562 | 0 | } |
1563 | 0 | } |
1564 | | |
1565 | 0 | return true; |
1566 | 0 | } |
1567 | | |
1568 | | } |
1569 | | |
1570 | | bool ScDBDocFunc::DataPilotUpdate( ScDPObject* pOldObj, const ScDPObject* pNewObj, |
1571 | | bool bRecord, bool bApi, bool bAllowMove ) |
1572 | 0 | { |
1573 | 0 | if (!pOldObj) |
1574 | 0 | { |
1575 | 0 | if (!pNewObj) |
1576 | 0 | return false; |
1577 | | |
1578 | 0 | return CreatePivotTable(*pNewObj, bRecord, bApi); |
1579 | 0 | } |
1580 | | |
1581 | 0 | if (!pNewObj) |
1582 | 0 | return RemovePivotTable(*pOldObj, bRecord, bApi); |
1583 | | |
1584 | 0 | if (pOldObj == pNewObj) |
1585 | 0 | return UpdatePivotTable(*pOldObj, bRecord, bApi); |
1586 | | |
1587 | 0 | OSL_ASSERT(pOldObj && pNewObj && pOldObj != pNewObj); |
1588 | |
|
1589 | 0 | ScDocShellModificator aModificator( rDocShell ); |
1590 | 0 | weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); |
1591 | |
|
1592 | 0 | ScRangeList aRanges; |
1593 | 0 | aRanges.push_back(pOldObj->GetOutRange()); |
1594 | 0 | aRanges.push_back(ScRange(pNewObj->GetOutRange().aStart)); // at least one cell in the output position must be editable. |
1595 | |
|
1596 | 0 | if (!CheckSheetViewProtection(sc::Operation::PivotTableUpdate)) |
1597 | 0 | return false; |
1598 | | |
1599 | 0 | if (!isEditable(rDocShell, aRanges, bApi)) |
1600 | 0 | return false; |
1601 | | |
1602 | 0 | ScDocumentUniquePtr pOldUndoDoc; |
1603 | 0 | ScDocumentUniquePtr pNewUndoDoc; |
1604 | |
|
1605 | 0 | ScDPObject aUndoDPObj(*pOldObj); // for undo or revert on failure |
1606 | |
|
1607 | 0 | ScDocument& rDoc = rDocShell.GetDocument(); |
1608 | 0 | if (bRecord && !rDoc.IsUndoEnabled()) |
1609 | 0 | bRecord = false; |
1610 | |
|
1611 | 0 | if (bRecord) |
1612 | 0 | createUndoDoc(pOldUndoDoc, rDoc, pOldObj->GetOutRange()); |
1613 | |
|
1614 | 0 | pNewObj->WriteSourceDataTo(*pOldObj); // copy source data |
1615 | |
|
1616 | 0 | ScDPSaveData* pData = pNewObj->GetSaveData(); |
1617 | 0 | OSL_ENSURE( pData, "no SaveData from living DPObject" ); |
1618 | 0 | if (pData) |
1619 | 0 | pOldObj->SetSaveData(*pData); // copy SaveData |
1620 | |
|
1621 | 0 | pOldObj->SetAllowMove(bAllowMove); |
1622 | 0 | pOldObj->ReloadGroupTableData(); |
1623 | 0 | pOldObj->SyncAllDimensionMembers(); |
1624 | 0 | pOldObj->InvalidateData(); // before getting the new output area |
1625 | | |
1626 | | // make sure the table has a name (not set by dialog) |
1627 | 0 | if (pOldObj->GetName().isEmpty()) |
1628 | 0 | pOldObj->SetName( rDoc.GetDPCollection()->CreateNewName() ); |
1629 | |
|
1630 | 0 | ScRange aNewOut; |
1631 | 0 | if (!checkNewOutputRange(*pOldObj, rDocShell, aNewOut, bApi)) |
1632 | 0 | { |
1633 | 0 | *pOldObj = aUndoDPObj; |
1634 | 0 | return false; |
1635 | 0 | } |
1636 | | |
1637 | | // test if new output area is empty except for old area |
1638 | 0 | if (!bApi) |
1639 | 0 | { |
1640 | | // OutRange of pOldObj (pDestObj) is still old area |
1641 | 0 | if (!lcl_EmptyExcept(rDoc, aNewOut, pOldObj->GetOutRange())) |
1642 | 0 | { |
1643 | 0 | std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(), |
1644 | 0 | VclMessageType::Question, VclButtonsType::YesNo, |
1645 | 0 | ScResId(STR_PIVOT_NOTEMPTY))); |
1646 | 0 | xQueryBox->set_default_response(RET_YES); |
1647 | 0 | if (xQueryBox->run() == RET_NO) |
1648 | 0 | { |
1649 | | //! like above (not editable) |
1650 | 0 | *pOldObj = aUndoDPObj; |
1651 | 0 | return false; |
1652 | 0 | } |
1653 | 0 | } |
1654 | 0 | } |
1655 | | |
1656 | 0 | if (bRecord) |
1657 | 0 | createUndoDoc(pNewUndoDoc, rDoc, aNewOut); |
1658 | |
|
1659 | 0 | pOldObj->Output(aNewOut.aStart); |
1660 | 0 | rDocShell.PostPaintGridAll(); //! only necessary parts |
1661 | |
|
1662 | 0 | if (bRecord) |
1663 | 0 | { |
1664 | 0 | rDocShell.GetUndoManager()->AddUndoAction( |
1665 | 0 | std::make_unique<ScUndoDataPilot>( |
1666 | 0 | rDocShell, std::move(pOldUndoDoc), std::move(pNewUndoDoc), &aUndoDPObj, pOldObj, bAllowMove)); |
1667 | 0 | } |
1668 | | |
1669 | | // notify API objects |
1670 | 0 | rDoc.BroadcastUno( ScDataPilotModifiedHint(pOldObj->GetName()) ); |
1671 | 0 | aModificator.SetDocumentModified(); |
1672 | |
|
1673 | 0 | return true; |
1674 | 0 | } |
1675 | | |
1676 | | bool ScDBDocFunc::RemovePivotTable(const ScDPObject& rDPObj, bool bRecord, bool bApi) |
1677 | 0 | { |
1678 | 0 | ScDocShellModificator aModificator(rDocShell); |
1679 | 0 | weld::WaitObject aWait(ScDocShell::GetActiveDialogParent()); |
1680 | |
|
1681 | 0 | if (!CheckSheetViewProtection(sc::Operation::PivotTableRemove)) |
1682 | 0 | return false; |
1683 | | |
1684 | 0 | if (!isEditable(rDocShell, rDPObj.GetOutRange(), bApi)) |
1685 | 0 | return false; |
1686 | | |
1687 | 0 | ScDocument& rDoc = rDocShell.GetDocument(); |
1688 | |
|
1689 | 0 | if (!bApi) |
1690 | 0 | { |
1691 | | // If we come from GUI - ask to delete the associated pivot charts too... |
1692 | 0 | std::vector<SdrOle2Obj*> aListOfObjects = |
1693 | 0 | sctools::getAllPivotChartsConnectedTo(rDPObj.GetName(), rDocShell); |
1694 | |
|
1695 | 0 | ScDrawLayer* pModel = rDoc.GetDrawLayer(); |
1696 | |
|
1697 | 0 | if (pModel && !aListOfObjects.empty()) |
1698 | 0 | { |
1699 | 0 | std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(), |
1700 | 0 | VclMessageType::Question, VclButtonsType::YesNo, |
1701 | 0 | ScResId(STR_PIVOT_REMOVE_PIVOTCHART))); |
1702 | 0 | xQueryBox->set_default_response(RET_YES); |
1703 | 0 | if (xQueryBox->run() == RET_NO) |
1704 | 0 | { |
1705 | 0 | return false; |
1706 | 0 | } |
1707 | 0 | else |
1708 | 0 | { |
1709 | 0 | for (SdrOle2Obj* pChartObject : aListOfObjects) |
1710 | 0 | { |
1711 | 0 | rDoc.GetChartListenerCollection()->removeByName(pChartObject->GetName()); |
1712 | 0 | pModel->AddUndo(std::make_unique<SdrUndoDelObj>(*pChartObject)); |
1713 | 0 | pChartObject->getSdrPageFromSdrObject()->RemoveObject(pChartObject->GetOrdNum()); |
1714 | 0 | } |
1715 | 0 | } |
1716 | 0 | } |
1717 | 0 | } |
1718 | | |
1719 | 0 | ScDocumentUniquePtr pOldUndoDoc; |
1720 | 0 | std::unique_ptr<ScDPObject> pUndoDPObj; |
1721 | |
|
1722 | 0 | if (bRecord) |
1723 | 0 | pUndoDPObj.reset(new ScDPObject(rDPObj)); // copy old settings for undo |
1724 | |
|
1725 | 0 | if (bRecord && !rDoc.IsUndoEnabled()) |
1726 | 0 | bRecord = false; |
1727 | | |
1728 | | // delete table |
1729 | |
|
1730 | 0 | ScRange aRange = rDPObj.GetOutRange(); |
1731 | 0 | SCTAB nTab = aRange.aStart.Tab(); |
1732 | |
|
1733 | 0 | if (bRecord) |
1734 | 0 | createUndoDoc(pOldUndoDoc, rDoc, aRange); |
1735 | |
|
1736 | 0 | rDoc.DeleteAreaTab( aRange.aStart.Col(), aRange.aStart.Row(), |
1737 | 0 | aRange.aEnd.Col(), aRange.aEnd.Row(), |
1738 | 0 | nTab, InsertDeleteFlags::ALL ); |
1739 | 0 | rDoc.RemoveFlagsTab( aRange.aStart.Col(), aRange.aStart.Row(), |
1740 | 0 | aRange.aEnd.Col(), aRange.aEnd.Row(), |
1741 | 0 | nTab, ScMF::Auto ); |
1742 | |
|
1743 | 0 | rDoc.GetDPCollection()->FreeTable(&rDPObj); // object is deleted here |
1744 | |
|
1745 | 0 | rDocShell.PostPaintGridAll(); //! only necessary parts |
1746 | 0 | rDocShell.PostPaint(aRange, PaintPartFlags::Grid); |
1747 | |
|
1748 | 0 | if (bRecord) |
1749 | 0 | { |
1750 | 0 | rDocShell.GetUndoManager()->AddUndoAction( |
1751 | 0 | std::make_unique<ScUndoDataPilot>( |
1752 | 0 | rDocShell, std::move(pOldUndoDoc), nullptr, pUndoDPObj.get(), nullptr, false)); |
1753 | | |
1754 | | // pUndoDPObj is copied |
1755 | 0 | } |
1756 | |
|
1757 | 0 | aModificator.SetDocumentModified(); |
1758 | 0 | return true; |
1759 | 0 | } |
1760 | | |
1761 | | bool ScDBDocFunc::CreatePivotTable(const ScDPObject& rDPObj, bool bRecord, bool bApi) |
1762 | 310 | { |
1763 | 310 | ScDocShellModificator aModificator(rDocShell); |
1764 | 310 | weld::WaitObject aWait(ScDocShell::GetActiveDialogParent()); |
1765 | | |
1766 | 310 | if (!CheckSheetViewProtection(sc::Operation::PivotTableCreate)) |
1767 | 0 | return false; |
1768 | | |
1769 | | // At least one cell in the output range should be editable. Check in advance. |
1770 | 310 | ScDocument& rDoc = rDocShell.GetDocument(); |
1771 | 310 | if (!rDoc.IsImportingXML() && !isEditable(rDocShell, ScRange(rDPObj.GetOutRange().aStart), bApi)) |
1772 | 0 | return false; |
1773 | | |
1774 | 310 | ScDocumentUniquePtr pNewUndoDoc; |
1775 | | |
1776 | 310 | if (bRecord && !rDoc.IsUndoEnabled()) |
1777 | 310 | bRecord = false; |
1778 | | |
1779 | | // output range must be set at pNewObj |
1780 | 310 | std::unique_ptr<ScDPObject> pDestObj(new ScDPObject(rDPObj)); |
1781 | | |
1782 | 310 | ScDPObject& rDestObj = *pDestObj; |
1783 | | |
1784 | | // #i94570# When changing the output position in the dialog, a new table is created |
1785 | | // with the settings from the old table, including the name. |
1786 | | // So we have to check for duplicate names here (before inserting). |
1787 | 310 | if (rDoc.GetDPCollection()->GetByName(rDestObj.GetName())) |
1788 | 14 | rDestObj.SetName(OUString()); // ignore the invalid name, create a new name below |
1789 | | |
1790 | | // Synchronize groups between linked tables |
1791 | 310 | { |
1792 | 310 | const ScDPDimensionSaveData* pGroups = nullptr; |
1793 | 310 | bool bRefFound = rDoc.GetDPCollection()->GetReferenceGroups(rDestObj, &pGroups); |
1794 | 310 | if (bRefFound) |
1795 | 73 | { |
1796 | 73 | ScDPSaveData* pSaveData = rDestObj.GetSaveData(); |
1797 | 73 | if (pSaveData) |
1798 | 73 | pSaveData->SetDimensionData(pGroups); |
1799 | 73 | } |
1800 | 310 | } |
1801 | | |
1802 | 310 | rDoc.GetDPCollection()->InsertNewTable(std::move(pDestObj)); |
1803 | | |
1804 | 310 | rDestObj.ReloadGroupTableData(); |
1805 | 310 | rDestObj.SyncAllDimensionMembers(); |
1806 | 310 | rDestObj.InvalidateData(); // before getting the new output area |
1807 | | |
1808 | | // make sure the table has a name (not set by dialog) |
1809 | 310 | if (rDestObj.GetName().isEmpty()) |
1810 | 14 | rDestObj.SetName(rDoc.GetDPCollection()->CreateNewName()); |
1811 | | |
1812 | 310 | bool bOverflow = false; |
1813 | 310 | ScRange aNewOut = rDestObj.GetNewOutputRange(bOverflow); |
1814 | | |
1815 | 310 | if (bOverflow) |
1816 | 0 | { |
1817 | 0 | if (!bApi) |
1818 | 0 | rDocShell.ErrorMessage(STR_PIVOT_ERROR); |
1819 | |
|
1820 | 0 | return false; |
1821 | 0 | } |
1822 | | |
1823 | 310 | if (!rDoc.IsImportingXML()) |
1824 | 0 | { |
1825 | 0 | ScEditableTester aTester = ScEditableTester::CreateAndTestRange(rDoc, aNewOut, sc::EditAction::Unknown); |
1826 | 0 | if (!aTester.IsEditable()) |
1827 | 0 | { |
1828 | | // destination area isn't editable |
1829 | 0 | if (!bApi) |
1830 | 0 | rDocShell.ErrorMessage(aTester.GetMessageId()); |
1831 | |
|
1832 | 0 | return false; |
1833 | 0 | } |
1834 | 0 | } |
1835 | | |
1836 | | // test if new output area is empty except for old area |
1837 | 310 | if (!bApi) |
1838 | 0 | { |
1839 | 0 | bool bEmpty = rDoc.IsBlockEmpty( |
1840 | 0 | aNewOut.aStart.Col(), aNewOut.aStart.Row(), |
1841 | 0 | aNewOut.aEnd.Col(), aNewOut.aEnd.Row(), aNewOut.aStart.Tab() ); |
1842 | |
|
1843 | 0 | if (!bEmpty) |
1844 | 0 | { |
1845 | 0 | std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(), |
1846 | 0 | VclMessageType::Question, VclButtonsType::YesNo, |
1847 | 0 | ScResId(STR_PIVOT_NOTEMPTY))); |
1848 | 0 | xQueryBox->set_default_response(RET_YES); |
1849 | 0 | if (xQueryBox->run() == RET_NO) |
1850 | 0 | { |
1851 | | //! like above (not editable) |
1852 | 0 | return false; |
1853 | 0 | } |
1854 | 0 | } |
1855 | 0 | } |
1856 | | |
1857 | 310 | if (bRecord) |
1858 | 0 | createUndoDoc(pNewUndoDoc, rDoc, aNewOut); |
1859 | | |
1860 | 310 | rDestObj.Output(aNewOut.aStart); |
1861 | 310 | rDocShell.PostPaintGridAll(); //! only necessary parts |
1862 | | |
1863 | 310 | if (bRecord) |
1864 | 0 | { |
1865 | 0 | rDocShell.GetUndoManager()->AddUndoAction( |
1866 | 0 | std::make_unique<ScUndoDataPilot>(rDocShell, nullptr, std::move(pNewUndoDoc), nullptr, &rDestObj, false)); |
1867 | 0 | } |
1868 | | |
1869 | | // notify API objects |
1870 | 310 | rDoc.BroadcastUno(ScDataPilotModifiedHint(rDestObj.GetName())); |
1871 | 310 | aModificator.SetDocumentModified(); |
1872 | | |
1873 | 310 | return true; |
1874 | 310 | } |
1875 | | |
1876 | | bool ScDBDocFunc::UpdatePivotTable(ScDPObject& rDPObj, bool bRecord, bool bApi) |
1877 | 0 | { |
1878 | 0 | ScDocShellModificator aModificator( rDocShell ); |
1879 | 0 | weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); |
1880 | |
|
1881 | 0 | if (!CheckSheetViewProtection(sc::Operation::PivotTableUpdate)) |
1882 | 0 | return false; |
1883 | | |
1884 | 0 | if (!isEditable(rDocShell, rDPObj.GetOutRange(), bApi, sc::EditAction::UpdatePivotTable)) |
1885 | 0 | return false; |
1886 | | |
1887 | 0 | ScDocumentUniquePtr pOldUndoDoc; |
1888 | 0 | ScDocumentUniquePtr pNewUndoDoc; |
1889 | |
|
1890 | 0 | ScDPObject aUndoDPObj(rDPObj); // For undo or revert on failure. |
1891 | |
|
1892 | 0 | ScDocument& rDoc = rDocShell.GetDocument(); |
1893 | 0 | if (bRecord && !rDoc.IsUndoEnabled()) |
1894 | 0 | bRecord = false; |
1895 | |
|
1896 | 0 | if (bRecord) |
1897 | 0 | createUndoDoc(pOldUndoDoc, rDoc, rDPObj.GetOutRange()); |
1898 | |
|
1899 | 0 | rDPObj.SetAllowMove(false); |
1900 | 0 | rDPObj.ReloadGroupTableData(); |
1901 | 0 | if (!rDPObj.SyncAllDimensionMembers()) |
1902 | 0 | return false; |
1903 | | |
1904 | 0 | rDPObj.InvalidateData(); // before getting the new output area |
1905 | | |
1906 | | // make sure the table has a name (not set by dialog) |
1907 | 0 | if (rDPObj.GetName().isEmpty()) |
1908 | 0 | rDPObj.SetName( rDoc.GetDPCollection()->CreateNewName() ); |
1909 | |
|
1910 | 0 | ScRange aNewOut; |
1911 | 0 | if (!checkNewOutputRange(rDPObj, rDocShell, aNewOut, bApi, sc::EditAction::UpdatePivotTable)) |
1912 | 0 | { |
1913 | 0 | rDPObj = aUndoDPObj; |
1914 | 0 | return false; |
1915 | 0 | } |
1916 | | |
1917 | | // test if new output area is empty except for old area |
1918 | 0 | if (!bApi) |
1919 | 0 | { |
1920 | 0 | if (!lcl_EmptyExcept(rDoc, aNewOut, rDPObj.GetOutRange())) |
1921 | 0 | { |
1922 | 0 | std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(), |
1923 | 0 | VclMessageType::Question, VclButtonsType::YesNo, |
1924 | 0 | ScResId(STR_PIVOT_NOTEMPTY))); |
1925 | 0 | xQueryBox->set_default_response(RET_YES); |
1926 | 0 | if (xQueryBox->run() == RET_NO) |
1927 | 0 | { |
1928 | 0 | rDPObj = aUndoDPObj; |
1929 | 0 | return false; |
1930 | 0 | } |
1931 | 0 | } |
1932 | 0 | } |
1933 | | |
1934 | 0 | if (bRecord) |
1935 | 0 | createUndoDoc(pNewUndoDoc, rDoc, aNewOut); |
1936 | |
|
1937 | 0 | rDPObj.Output(aNewOut.aStart); |
1938 | 0 | rDocShell.PostPaintGridAll(); //! only necessary parts |
1939 | |
|
1940 | 0 | if (bRecord) |
1941 | 0 | { |
1942 | 0 | rDocShell.GetUndoManager()->AddUndoAction( |
1943 | 0 | std::make_unique<ScUndoDataPilot>( |
1944 | 0 | rDocShell, std::move(pOldUndoDoc), std::move(pNewUndoDoc), &aUndoDPObj, &rDPObj, false)); |
1945 | 0 | } |
1946 | | |
1947 | | // notify API objects |
1948 | 0 | rDoc.BroadcastUno( ScDataPilotModifiedHint(rDPObj.GetName()) ); |
1949 | 0 | aModificator.SetDocumentModified(); |
1950 | 0 | return true; |
1951 | 0 | } |
1952 | | |
1953 | | void ScDBDocFunc::RefreshPivotTables(const ScDPObject* pDPObj, bool bApi) |
1954 | 0 | { |
1955 | 0 | ScDPCollection* pDPs = rDocShell.GetDocument().GetDPCollection(); |
1956 | 0 | if (!pDPs) |
1957 | 0 | return; |
1958 | | |
1959 | 0 | o3tl::sorted_vector<ScDPObject*> aRefs; |
1960 | 0 | TranslateId pErrId = pDPs->ReloadCache(pDPObj, aRefs); |
1961 | 0 | if (pErrId) |
1962 | 0 | return; |
1963 | | |
1964 | 0 | for (ScDPObject* pObj : aRefs) |
1965 | 0 | { |
1966 | | // This action is intentionally not undoable since it modifies cache. |
1967 | 0 | UpdatePivotTable(*pObj, false, bApi); |
1968 | 0 | } |
1969 | 0 | } |
1970 | | |
1971 | | void ScDBDocFunc::RefreshPivotTableGroups(ScDPObject* pDPObj) |
1972 | 18 | { |
1973 | 18 | if (!pDPObj) |
1974 | 0 | return; |
1975 | | |
1976 | 18 | ScDPCollection* pDPs = rDocShell.GetDocument().GetDPCollection(); |
1977 | 18 | if (!pDPs) |
1978 | 0 | return; |
1979 | | |
1980 | 18 | ScDPSaveData* pSaveData = pDPObj->GetSaveData(); |
1981 | 18 | if (!pSaveData) |
1982 | 0 | return; |
1983 | | |
1984 | 18 | if (!pDPs->HasTable(pDPObj)) |
1985 | 18 | { |
1986 | | // This table is under construction so no need for a whole update (UpdatePivotTable()). |
1987 | 18 | pDPObj->ReloadGroupTableData(); |
1988 | 18 | return; |
1989 | 18 | } |
1990 | | |
1991 | | // Update all linked tables, if this table is part of the cache (ScDPCollection) |
1992 | 0 | o3tl::sorted_vector<ScDPObject*> aRefs; |
1993 | 0 | if (!pDPs->ReloadGroupsInCache(pDPObj, aRefs)) |
1994 | 0 | return; |
1995 | | |
1996 | | // We allow pDimData being NULL. |
1997 | 0 | const ScDPDimensionSaveData* pDimData = pSaveData->GetExistingDimensionData(); |
1998 | 0 | for (ScDPObject* pObj : aRefs) |
1999 | 0 | { |
2000 | 0 | if (pObj != pDPObj) |
2001 | 0 | { |
2002 | 0 | pSaveData = pObj->GetSaveData(); |
2003 | 0 | if (pSaveData) |
2004 | 0 | pSaveData->SetDimensionData(pDimData); |
2005 | 0 | } |
2006 | | |
2007 | | // This action is intentionally not undoable since it modifies cache. |
2008 | 0 | UpdatePivotTable(*pObj, false, false); |
2009 | 0 | } |
2010 | 0 | } |
2011 | | |
2012 | | // database import |
2013 | | |
2014 | | void ScDBDocFunc::UpdateImport( const OUString& rTarget, const svx::ODataAccessDescriptor& rDescriptor ) |
2015 | 0 | { |
2016 | | // rTarget is the name of a database range |
2017 | |
|
2018 | 0 | ScDocument& rDoc = rDocShell.GetDocument(); |
2019 | 0 | ScDBCollection& rDBColl = *rDoc.GetDBCollection(); |
2020 | 0 | const ScDBData* pData = rDBColl.getNamedDBs().findByUpperName(ScGlobal::getCharClass().uppercase(rTarget)); |
2021 | 0 | if (!pData) |
2022 | 0 | { |
2023 | 0 | std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(), |
2024 | 0 | VclMessageType::Info, VclButtonsType::Ok, |
2025 | 0 | ScResId(STR_TARGETNOTFOUND))); |
2026 | 0 | xInfoBox->run(); |
2027 | 0 | return; |
2028 | 0 | } |
2029 | | |
2030 | 0 | SCTAB nTab; |
2031 | 0 | SCCOL nDummyCol; |
2032 | 0 | SCROW nDummyRow; |
2033 | 0 | pData->GetArea( nTab, nDummyCol,nDummyRow,nDummyCol,nDummyRow ); |
2034 | |
|
2035 | 0 | ScImportParam aImportParam; |
2036 | 0 | pData->GetImportParam( aImportParam ); |
2037 | |
|
2038 | 0 | OUString sDBName; |
2039 | 0 | OUString sDBTable; |
2040 | 0 | sal_Int32 nCommandType = 0; |
2041 | 0 | sDBName = rDescriptor.getDataSource(); |
2042 | 0 | rDescriptor[svx::DataAccessDescriptorProperty::Command] >>= sDBTable; |
2043 | 0 | rDescriptor[svx::DataAccessDescriptorProperty::CommandType] >>= nCommandType; |
2044 | |
|
2045 | 0 | aImportParam.aDBName = sDBName; |
2046 | 0 | aImportParam.bSql = ( nCommandType == sdb::CommandType::COMMAND ); |
2047 | 0 | aImportParam.aStatement = sDBTable; |
2048 | 0 | aImportParam.bNative = false; |
2049 | 0 | aImportParam.nType = static_cast<sal_uInt8>( ( nCommandType == sdb::CommandType::QUERY ) ? ScDbQuery : ScDbTable ); |
2050 | 0 | aImportParam.bImport = true; |
2051 | |
|
2052 | 0 | bool bContinue = DoImport( nTab, aImportParam, &rDescriptor ); |
2053 | | |
2054 | | // repeat DB operations |
2055 | |
|
2056 | 0 | ScTabViewShell* pViewSh = rDocShell.GetBestViewShell(); |
2057 | 0 | if (!pViewSh) |
2058 | 0 | return; |
2059 | | |
2060 | 0 | ScRange aRange; |
2061 | 0 | pData->GetArea(aRange); |
2062 | 0 | pViewSh->MarkRange(aRange); // select |
2063 | |
|
2064 | 0 | if ( bContinue ) // error at import -> abort |
2065 | 0 | { |
2066 | | // internal operations, if some are saved |
2067 | |
|
2068 | 0 | if ( pData->HasQueryParam() || pData->HasSortParam() || pData->HasSubTotalParam() ) |
2069 | 0 | pViewSh->RepeatDB(); |
2070 | | |
2071 | | // pivot tables which have the range as source data |
2072 | |
|
2073 | 0 | rDocShell.RefreshPivotTables(aRange); |
2074 | 0 | } |
2075 | 0 | } |
2076 | | |
2077 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |