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