1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sc.hxx"
26
27
28
29 // INCLUDE ---------------------------------------------------------------
30
31 #include <sfx2/app.hxx>
32 #include <vcl/msgbox.hxx>
33 #include <vcl/waitobj.hxx>
34 #include <svx/dataaccessdescriptor.hxx>
35
36 #include <com/sun/star/sdb/CommandType.hpp>
37
38 #include "dbdocfun.hxx"
39 #include "sc.hrc"
40 #include "dbcolect.hxx"
41 #include "undodat.hxx"
42 #include "docsh.hxx"
43 #include "docfunc.hxx"
44 #include "globstr.hrc"
45 #include "tabvwsh.hxx"
46 #include "patattr.hxx"
47 #include "rangenam.hxx"
48 #include "olinetab.hxx"
49 #include "dpobject.hxx"
50 #include "dociter.hxx" // for lcl_EmptyExcept
51 #include "cell.hxx" // for lcl_EmptyExcept
52 #include "editable.hxx"
53 #include "attrib.hxx"
54 #include "drwlayer.hxx"
55 #include "dpshttab.hxx"
56 #include "hints.hxx"
57
58 using namespace ::com::sun::star;
59
60 // -----------------------------------------------------------------
61
AddDBRange(const String & rName,const ScRange & rRange,sal_Bool)62 sal_Bool ScDBDocFunc::AddDBRange( const String& rName, const ScRange& rRange, sal_Bool /* bApi */ )
63 {
64
65 ScDocShellModificator aModificator( rDocShell );
66
67 ScDocument* pDoc = rDocShell.GetDocument();
68 ScDBCollection* pDocColl = pDoc->GetDBCollection();
69 sal_Bool bUndo (pDoc->IsUndoEnabled());
70
71 ScDBCollection* pUndoColl = NULL;
72 if (bUndo)
73 pUndoColl = new ScDBCollection( *pDocColl );
74
75 ScDBData* pNew = new ScDBData( rName, rRange.aStart.Tab(),
76 rRange.aStart.Col(), rRange.aStart.Row(),
77 rRange.aEnd.Col(), rRange.aEnd.Row() );
78
79 // #i55926# While loading XML, formula cells only have a single string token,
80 // so CompileDBFormula would never find any name (index) tokens, and would
81 // unnecessarily loop through all cells.
82 sal_Bool bCompile = !pDoc->IsImportingXML();
83
84 if ( bCompile )
85 pDoc->CompileDBFormula( sal_True ); // CreateFormulaString
86 sal_Bool bOk = pDocColl->Insert( pNew );
87 if ( bCompile )
88 pDoc->CompileDBFormula( sal_False ); // CompileFormulaString
89
90 if (!bOk)
91 {
92 delete pNew;
93 delete pUndoColl;
94 return sal_False;
95 }
96
97 if (bUndo)
98 {
99 ScDBCollection* pRedoColl = new ScDBCollection( *pDocColl );
100 rDocShell.GetUndoManager()->AddUndoAction(
101 new ScUndoDBData( &rDocShell, pUndoColl, pRedoColl ) );
102 }
103
104 aModificator.SetDocumentModified();
105 SFX_APP()->Broadcast( SfxSimpleHint( SC_HINT_DBAREAS_CHANGED ) );
106 return sal_True;
107 }
108
DeleteDBRange(const String & rName,sal_Bool)109 sal_Bool ScDBDocFunc::DeleteDBRange( const String& rName, sal_Bool /* bApi */ )
110 {
111 sal_Bool bDone = sal_False;
112 ScDocument* pDoc = rDocShell.GetDocument();
113 ScDBCollection* pDocColl = pDoc->GetDBCollection();
114 sal_Bool bUndo (pDoc->IsUndoEnabled());
115
116 sal_uInt16 nPos = 0;
117 if (pDocColl->SearchName( rName, nPos ))
118 {
119 ScDocShellModificator aModificator( rDocShell );
120
121 ScDBCollection* pUndoColl = NULL;
122 if (bUndo)
123 pUndoColl = new ScDBCollection( *pDocColl );
124
125 pDoc->CompileDBFormula( sal_True ); // CreateFormulaString
126 pDocColl->AtFree( nPos );
127 pDoc->CompileDBFormula( sal_False ); // CompileFormulaString
128
129 if (bUndo)
130 {
131 ScDBCollection* pRedoColl = new ScDBCollection( *pDocColl );
132 rDocShell.GetUndoManager()->AddUndoAction(
133 new ScUndoDBData( &rDocShell, pUndoColl, pRedoColl ) );
134 }
135
136 aModificator.SetDocumentModified();
137 SFX_APP()->Broadcast( SfxSimpleHint( SC_HINT_DBAREAS_CHANGED ) );
138 bDone = sal_True;
139 }
140
141 return bDone;
142 }
143
RenameDBRange(const String & rOld,const String & rNew,sal_Bool)144 sal_Bool ScDBDocFunc::RenameDBRange( const String& rOld, const String& rNew, sal_Bool /* bApi */ )
145 {
146 sal_Bool bDone = sal_False;
147 ScDocument* pDoc = rDocShell.GetDocument();
148 ScDBCollection* pDocColl = pDoc->GetDBCollection();
149 sal_Bool bUndo (pDoc->IsUndoEnabled());
150
151 sal_uInt16 nPos = 0;
152 sal_uInt16 nDummy = 0;
153 if ( pDocColl->SearchName( rOld, nPos ) &&
154 !pDocColl->SearchName( rNew, nDummy ) )
155 {
156 ScDocShellModificator aModificator( rDocShell );
157
158 ScDBData* pData = (*pDocColl)[nPos];
159 ScDBData* pNewData = new ScDBData(*pData);
160 pNewData->SetName(rNew);
161
162 ScDBCollection* pUndoColl = new ScDBCollection( *pDocColl );
163
164 pDoc->CompileDBFormula( sal_True ); // CreateFormulaString
165 pDocColl->AtFree( nPos );
166 sal_Bool bInserted = pDocColl->Insert( pNewData );
167 if (!bInserted) // Fehler -> alten Zustand wiederherstellen
168 {
169 delete pNewData;
170 pDoc->SetDBCollection( pUndoColl ); // gehoert dann dem Dokument
171 }
172 pDoc->CompileDBFormula( sal_False ); // CompileFormulaString
173
174 if (bInserted) // Einfuegen hat geklappt
175 {
176 if (bUndo)
177 {
178 ScDBCollection* pRedoColl = new ScDBCollection( *pDocColl );
179 rDocShell.GetUndoManager()->AddUndoAction(
180 new ScUndoDBData( &rDocShell, pUndoColl, pRedoColl ) );
181 }
182 else
183 delete pUndoColl;
184
185 aModificator.SetDocumentModified();
186 SFX_APP()->Broadcast( SfxSimpleHint( SC_HINT_DBAREAS_CHANGED ) );
187 bDone = sal_True;
188 }
189 }
190
191 return bDone;
192 }
193
ModifyDBData(const ScDBData & rNewData,sal_Bool)194 sal_Bool ScDBDocFunc::ModifyDBData( const ScDBData& rNewData, sal_Bool /* bApi */ )
195 {
196 sal_Bool bDone = sal_False;
197 ScDocument* pDoc = rDocShell.GetDocument();
198 ScDBCollection* pDocColl = pDoc->GetDBCollection();
199 sal_Bool bUndo (pDoc->IsUndoEnabled());
200
201 sal_uInt16 nPos = 0;
202 if (pDocColl->SearchName( rNewData.GetName(), nPos ))
203 {
204 ScDocShellModificator aModificator( rDocShell );
205
206 ScDBData* pData = (*pDocColl)[nPos];
207
208 ScRange aOldRange, aNewRange;
209 pData->GetArea(aOldRange);
210 rNewData.GetArea(aNewRange);
211 sal_Bool bAreaChanged = ( aOldRange != aNewRange ); // dann muss neu compiliert werden
212
213 ScDBCollection* pUndoColl = NULL;
214 if (bUndo)
215 pUndoColl = new ScDBCollection( *pDocColl );
216
217 *pData = rNewData;
218 if (bAreaChanged)
219 pDoc->CompileDBFormula();
220
221 if (bUndo)
222 {
223 ScDBCollection* pRedoColl = new ScDBCollection( *pDocColl );
224 rDocShell.GetUndoManager()->AddUndoAction(
225 new ScUndoDBData( &rDocShell, pUndoColl, pRedoColl ) );
226 }
227
228 aModificator.SetDocumentModified();
229 bDone = sal_True;
230 }
231
232 return bDone;
233 }
234
235 // -----------------------------------------------------------------
236
RepeatDB(const String & rDBName,sal_Bool bRecord,sal_Bool bApi)237 sal_Bool ScDBDocFunc::RepeatDB( const String& rDBName, sal_Bool bRecord, sal_Bool bApi )
238 {
239 //! auch fuer ScDBFunc::RepeatDB benutzen!
240
241 sal_Bool bDone = sal_False;
242 ScDocument* pDoc = rDocShell.GetDocument();
243 if (bRecord && !pDoc->IsUndoEnabled())
244 bRecord = sal_False;
245 ScDBCollection* pColl = pDoc->GetDBCollection();
246 sal_uInt16 nIndex;
247 if ( pColl && pColl->SearchName( rDBName, nIndex ) )
248 {
249 ScDBData* pDBData = (*pColl)[nIndex];
250
251 ScQueryParam aQueryParam;
252 pDBData->GetQueryParam( aQueryParam );
253 sal_Bool bQuery = aQueryParam.GetEntry(0).bDoQuery;
254
255 ScSortParam aSortParam;
256 pDBData->GetSortParam( aSortParam );
257 sal_Bool bSort = aSortParam.bDoSort[0];
258
259 ScSubTotalParam aSubTotalParam;
260 pDBData->GetSubTotalParam( aSubTotalParam );
261 sal_Bool bSubTotal = aSubTotalParam.bGroupActive[0] && !aSubTotalParam.bRemoveOnly;
262
263 if ( bQuery || bSort || bSubTotal )
264 {
265 sal_Bool bQuerySize = sal_False;
266 ScRange aOldQuery;
267 ScRange aNewQuery;
268 if (bQuery && !aQueryParam.bInplace)
269 {
270 ScDBData* pDest = pDoc->GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow,
271 aQueryParam.nDestTab, sal_True );
272 if (pDest && pDest->IsDoSize())
273 {
274 pDest->GetArea( aOldQuery );
275 bQuerySize = sal_True;
276 }
277 }
278
279 SCTAB nTab;
280 SCCOL nStartCol;
281 SCROW nStartRow;
282 SCCOL nEndCol;
283 SCROW nEndRow;
284 pDBData->GetArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow );
285
286 //! Undo nur benoetigte Daten ?
287
288 ScDocument* pUndoDoc = NULL;
289 ScOutlineTable* pUndoTab = NULL;
290 ScRangeName* pUndoRange = NULL;
291 ScDBCollection* pUndoDB = NULL;
292
293 if (bRecord)
294 {
295 SCTAB nTabCount = pDoc->GetTableCount();
296 pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
297 ScOutlineTable* pTable = pDoc->GetOutlineTable( nTab );
298 if (pTable)
299 {
300 pUndoTab = new ScOutlineTable( *pTable );
301
302 // column/row state
303 SCCOLROW nOutStartCol, nOutEndCol;
304 SCCOLROW nOutStartRow, nOutEndRow;
305 pTable->GetColArray()->GetRange( nOutStartCol, nOutEndCol );
306 pTable->GetRowArray()->GetRange( nOutStartRow, nOutEndRow );
307
308 pUndoDoc->InitUndo( pDoc, nTab, nTab, sal_True, sal_True );
309 pDoc->CopyToDocument( static_cast<SCCOL>(nOutStartCol), 0,
310 nTab, static_cast<SCCOL>(nOutEndCol), MAXROW, nTab,
311 IDF_NONE, sal_False, pUndoDoc );
312 pDoc->CopyToDocument( 0, static_cast<SCROW>(nOutStartRow),
313 nTab, MAXCOL, static_cast<SCROW>(nOutEndRow), nTab,
314 IDF_NONE, sal_False, pUndoDoc );
315 }
316 else
317 pUndoDoc->InitUndo( pDoc, nTab, nTab, sal_False, sal_True );
318
319 // Datenbereich sichern - incl. Filter-Ergebnis
320 pDoc->CopyToDocument( 0,nStartRow,nTab, MAXCOL,nEndRow,nTab, IDF_ALL, sal_False, pUndoDoc );
321
322 // alle Formeln wegen Referenzen
323 pDoc->CopyToDocument( 0,0,0, MAXCOL,MAXROW,nTabCount-1, IDF_FORMULA, sal_False, pUndoDoc );
324
325 // DB- und andere Bereiche
326 ScRangeName* pDocRange = pDoc->GetRangeName();
327 if (pDocRange->GetCount())
328 pUndoRange = new ScRangeName( *pDocRange );
329 ScDBCollection* pDocDB = pDoc->GetDBCollection();
330 if (pDocDB->GetCount())
331 pUndoDB = new ScDBCollection( *pDocDB );
332 }
333
334 if (bSort && bSubTotal)
335 {
336 // Sortieren ohne SubTotals
337
338 aSubTotalParam.bRemoveOnly = sal_True; // wird unten wieder zurueckgesetzt
339 DoSubTotals( nTab, aSubTotalParam, NULL, sal_False, bApi );
340 }
341
342 if (bSort)
343 {
344 pDBData->GetSortParam( aSortParam ); // Bereich kann sich geaendert haben
345 Sort( nTab, aSortParam, sal_False, sal_False, bApi );
346 }
347 if (bQuery)
348 {
349 pDBData->GetQueryParam( aQueryParam ); // Bereich kann sich geaendert haben
350 ScRange aAdvSource;
351 if (pDBData->GetAdvancedQuerySource(aAdvSource))
352 Query( nTab, aQueryParam, &aAdvSource, sal_False, bApi );
353 else
354 Query( nTab, aQueryParam, NULL, sal_False, bApi );
355
356 // bei nicht-inplace kann die Tabelle umgestellt worden sein
357 // if ( !aQueryParam.bInplace && aQueryParam.nDestTab != nTab )
358 // SetTabNo( nTab );
359 }
360 if (bSubTotal)
361 {
362 pDBData->GetSubTotalParam( aSubTotalParam ); // Bereich kann sich geaendert haben
363 aSubTotalParam.bRemoveOnly = sal_False;
364 DoSubTotals( nTab, aSubTotalParam, NULL, sal_False, bApi );
365 }
366
367 if (bRecord)
368 {
369 SCTAB nDummyTab;
370 SCCOL nDummyCol;
371 SCROW nDummyRow;
372 SCROW nNewEndRow;
373 pDBData->GetArea( nDummyTab, nDummyCol,nDummyRow, nDummyCol,nNewEndRow );
374
375 const ScRange* pOld = NULL;
376 const ScRange* pNew = NULL;
377 if (bQuerySize)
378 {
379 ScDBData* pDest = pDoc->GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow,
380 aQueryParam.nDestTab, sal_True );
381 if (pDest)
382 {
383 pDest->GetArea( aNewQuery );
384 pOld = &aOldQuery;
385 pNew = &aNewQuery;
386 }
387 }
388
389 rDocShell.GetUndoManager()->AddUndoAction(
390 new ScUndoRepeatDB( &rDocShell, nTab,
391 nStartCol, nStartRow, nEndCol, nEndRow,
392 nNewEndRow,
393 //nCurX, nCurY,
394 nStartCol, nStartRow,
395 pUndoDoc, pUndoTab,
396 pUndoRange, pUndoDB,
397 pOld, pNew ) );
398 }
399
400 rDocShell.PostPaint( 0,0,nTab, MAXCOL,MAXROW,nTab,
401 PAINT_GRID | PAINT_LEFT | PAINT_TOP | PAINT_SIZE );
402 bDone = sal_True;
403 }
404 else if (!bApi) // "Keine Operationen auszufuehren"
405 rDocShell.ErrorMessage(STR_MSSG_REPEATDB_0);
406 }
407
408 return bDone;
409 }
410
411 // -----------------------------------------------------------------
412
Sort(SCTAB nTab,const ScSortParam & rSortParam,sal_Bool bRecord,sal_Bool bPaint,sal_Bool bApi)413 sal_Bool ScDBDocFunc::Sort( SCTAB nTab, const ScSortParam& rSortParam,
414 sal_Bool bRecord, sal_Bool bPaint, sal_Bool bApi )
415 {
416 ScDocShellModificator aModificator( rDocShell );
417
418 ScDocument* pDoc = rDocShell.GetDocument();
419 if (bRecord && !pDoc->IsUndoEnabled())
420 bRecord = sal_False;
421 SCTAB nSrcTab = nTab;
422 ScDrawLayer* pDrawLayer = pDoc->GetDrawLayer();
423
424 ScDBData* pDBData = pDoc->GetDBAtArea( nTab, rSortParam.nCol1, rSortParam.nRow1,
425 rSortParam.nCol2, rSortParam.nRow2 );
426 if (!pDBData)
427 {
428 DBG_ERROR( "Sort: keine DBData" );
429 return sal_False;
430 }
431
432 ScDBData* pDestData = NULL;
433 ScRange aOldDest;
434 sal_Bool bCopy = !rSortParam.bInplace;
435 if ( bCopy && rSortParam.nDestCol == rSortParam.nCol1 &&
436 rSortParam.nDestRow == rSortParam.nRow1 && rSortParam.nDestTab == nTab )
437 bCopy = sal_False;
438 ScSortParam aLocalParam( rSortParam );
439 if ( bCopy )
440 {
441 aLocalParam.MoveToDest();
442 if ( !ValidColRow( aLocalParam.nCol2, aLocalParam.nRow2 ) )
443 {
444 if (!bApi)
445 rDocShell.ErrorMessage(STR_PASTE_FULL);
446 return sal_False;
447 }
448
449 nTab = rSortParam.nDestTab;
450 pDestData = pDoc->GetDBAtCursor( rSortParam.nDestCol, rSortParam.nDestRow,
451 rSortParam.nDestTab, sal_True );
452 if (pDestData)
453 pDestData->GetArea(aOldDest);
454 }
455
456 ScEditableTester aTester( pDoc, nTab, aLocalParam.nCol1,aLocalParam.nRow1,
457 aLocalParam.nCol2,aLocalParam.nRow2 );
458 if (!aTester.IsEditable())
459 {
460 if (!bApi)
461 rDocShell.ErrorMessage(aTester.GetMessageId());
462 return sal_False;
463 }
464
465 if ( aLocalParam.bIncludePattern && pDoc->HasAttrib(
466 aLocalParam.nCol1, aLocalParam.nRow1, nTab,
467 aLocalParam.nCol2, aLocalParam.nRow2, nTab,
468 HASATTR_MERGED | HASATTR_OVERLAPPED ) )
469 {
470 // Merge-Attribute wuerden beim Sortieren durcheinanderkommen
471 if (!bApi)
472 rDocShell.ErrorMessage(STR_SORT_ERR_MERGED);
473 return sal_False;
474 }
475
476
477 // ausfuehren
478
479 WaitObject aWait( rDocShell.GetActiveDialogParent() );
480
481 sal_Bool bRepeatQuery = sal_False; // bestehenden Filter wiederholen?
482 ScQueryParam aQueryParam;
483 pDBData->GetQueryParam( aQueryParam );
484 if ( aQueryParam.GetEntry(0).bDoQuery )
485 bRepeatQuery = sal_True;
486
487 if (bRepeatQuery && bCopy)
488 {
489 if ( aQueryParam.bInplace ||
490 aQueryParam.nDestCol != rSortParam.nDestCol ||
491 aQueryParam.nDestRow != rSortParam.nDestRow ||
492 aQueryParam.nDestTab != rSortParam.nDestTab ) // Query auf selben Zielbereich?
493 bRepeatQuery = sal_False;
494 }
495
496 ScUndoSort* pUndoAction = 0;
497 if ( bRecord )
498 {
499 // Referenzen ausserhalb des Bereichs werden nicht veraendert !
500
501 ScDocument* pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
502 // Zeilenhoehen immer (wegen automatischer Anpassung)
503 //! auf ScBlockUndo umstellen
504 pUndoDoc->InitUndo( pDoc, nTab, nTab, sal_False, sal_True );
505
506 /* #i59745# Do not copy note captions to undo document. All existing
507 caption objects will be repositioned while sorting which is tracked
508 in drawing undo. When undo is executed, the old positions will be
509 restored, and the cells with the old notes (which still refer to the
510 existing captions) will be copied back into the source document. */
511 pDoc->CopyToDocument( aLocalParam.nCol1, aLocalParam.nRow1, nTab,
512 aLocalParam.nCol2, aLocalParam.nRow2, nTab,
513 IDF_ALL|IDF_NOCAPTIONS, sal_False, pUndoDoc );
514
515 const ScRange* pR = 0;
516 if (pDestData)
517 {
518 /* #i59745# Do not copy note captions from destination range to
519 undo document. All existing caption objects will be removed
520 which is tracked in drawing undo. When undo is executed, the
521 caption objects are reinserted with drawing undo, and the cells
522 with the old notes (which still refer to the existing captions)
523 will be copied back into the source document. */
524 pDoc->CopyToDocument( aOldDest, IDF_ALL|IDF_NOCAPTIONS, sal_False, pUndoDoc );
525 pR = &aOldDest;
526 }
527
528 // Zeilenhoehen immer (wegen automatischer Anpassung)
529 //! auf ScBlockUndo umstellen
530 // if (bRepeatQuery)
531 pDoc->CopyToDocument( 0, aLocalParam.nRow1, nTab, MAXCOL, aLocalParam.nRow2, nTab,
532 IDF_NONE, sal_False, pUndoDoc );
533
534 ScDBCollection* pUndoDB = NULL;
535 ScDBCollection* pDocDB = pDoc->GetDBCollection();
536 if (pDocDB->GetCount())
537 pUndoDB = new ScDBCollection( *pDocDB );
538
539 pUndoAction = new ScUndoSort( &rDocShell, nTab, rSortParam, bRepeatQuery, pUndoDoc, pUndoDB, pR );
540 rDocShell.GetUndoManager()->AddUndoAction( pUndoAction );
541
542 // #i59745# collect all drawing undo actions affecting cell note captions
543 if( pDrawLayer )
544 pDrawLayer->BeginCalcUndo(false);
545 }
546
547 if ( bCopy )
548 {
549 if (pDestData)
550 pDoc->DeleteAreaTab(aOldDest, IDF_CONTENTS); // Zielbereich vorher loeschen
551
552 ScRange aSource( rSortParam.nCol1,rSortParam.nRow1,nSrcTab,
553 rSortParam.nCol2,rSortParam.nRow2,nSrcTab );
554 ScAddress aDest( rSortParam.nDestCol, rSortParam.nDestRow, rSortParam.nDestTab );
555
556 rDocShell.GetDocFunc().MoveBlock( aSource, aDest, sal_False, sal_False, sal_False, sal_True );
557 }
558
559 // #105780# don't call ScDocument::Sort with an empty SortParam (may be empty here if bCopy is set)
560 if ( aLocalParam.bDoSort[0] )
561 pDoc->Sort( nTab, aLocalParam, bRepeatQuery );
562
563 sal_Bool bSave = sal_True;
564 if (bCopy)
565 {
566 ScSortParam aOldSortParam;
567 pDBData->GetSortParam( aOldSortParam );
568 if ( aOldSortParam.bDoSort[0] && aOldSortParam.bInplace ) // Inplace-Sortierung gemerkt?
569 {
570 bSave = sal_False;
571 aOldSortParam.nDestCol = rSortParam.nDestCol;
572 aOldSortParam.nDestRow = rSortParam.nDestRow;
573 aOldSortParam.nDestTab = rSortParam.nDestTab;
574 pDBData->SetSortParam( aOldSortParam ); // dann nur DestPos merken
575 }
576 }
577 if (bSave) // Parameter merken
578 {
579 pDBData->SetSortParam( rSortParam );
580 pDBData->SetHeader( rSortParam.bHasHeader ); //! ???
581 pDBData->SetByRow( rSortParam.bByRow ); //! ???
582 }
583
584 if (bCopy) // neuen DB-Bereich merken
585 {
586 // Tabelle umschalten von aussen (View)
587 //! SetCursor ??!?!
588
589 ScRange aDestPos( aLocalParam.nCol1, aLocalParam.nRow1, nTab,
590 aLocalParam.nCol2, aLocalParam.nRow2, nTab );
591 ScDBData* pNewData;
592 if (pDestData)
593 pNewData = pDestData; // Bereich vorhanden -> anpassen
594 else // Bereich ab Cursor/Markierung wird angelegt
595 pNewData = rDocShell.GetDBData(aDestPos, SC_DB_MAKE, SC_DBSEL_FORCE_MARK );
596 if (pNewData)
597 {
598 pNewData->SetArea( nTab,
599 aLocalParam.nCol1,aLocalParam.nRow1,
600 aLocalParam.nCol2,aLocalParam.nRow2 );
601 pNewData->SetSortParam( aLocalParam );
602 pNewData->SetHeader( aLocalParam.bHasHeader ); //! ???
603 pNewData->SetByRow( aLocalParam.bByRow );
604 }
605 else
606 {
607 DBG_ERROR("Zielbereich nicht da");
608 }
609 }
610
611 ScRange aDirtyRange( aLocalParam.nCol1, aLocalParam.nRow1, nTab,
612 aLocalParam.nCol2, aLocalParam.nRow2, nTab );
613 pDoc->SetDirty( aDirtyRange );
614
615 if (bPaint)
616 {
617 sal_uInt16 nPaint = PAINT_GRID;
618 SCCOL nStartX = aLocalParam.nCol1;
619 SCROW nStartY = aLocalParam.nRow1;
620 SCCOL nEndX = aLocalParam.nCol2;
621 SCROW nEndY = aLocalParam.nRow2;
622 if ( bRepeatQuery )
623 {
624 nPaint |= PAINT_LEFT;
625 nStartX = 0;
626 nEndX = MAXCOL;
627 }
628 if (pDestData)
629 {
630 if ( nEndX < aOldDest.aEnd.Col() )
631 nEndX = aOldDest.aEnd.Col();
632 if ( nEndY < aOldDest.aEnd.Row() )
633 nEndY = aOldDest.aEnd.Row();
634 }
635 rDocShell.PostPaint( nStartX, nStartY, nTab, nEndX, nEndY, nTab, nPaint );
636 }
637
638 // AdjustRowHeight( aLocalParam.nRow1, aLocalParam.nRow2, bPaint );
639 rDocShell.AdjustRowHeight( aLocalParam.nRow1, aLocalParam.nRow2, nTab );
640
641 // #i59745# set collected drawing undo actions at sorting undo action
642 if( pUndoAction && pDrawLayer )
643 pUndoAction->SetDrawUndoAction( pDrawLayer->GetCalcUndo() );
644
645 aModificator.SetDocumentModified();
646
647 return sal_True;
648 }
649
650 // -----------------------------------------------------------------
651
Query(SCTAB nTab,const ScQueryParam & rQueryParam,const ScRange * pAdvSource,sal_Bool bRecord,sal_Bool bApi)652 sal_Bool ScDBDocFunc::Query( SCTAB nTab, const ScQueryParam& rQueryParam,
653 const ScRange* pAdvSource, sal_Bool bRecord, sal_Bool bApi )
654 {
655 ScDocShellModificator aModificator( rDocShell );
656
657 ScDocument* pDoc = rDocShell.GetDocument();
658 if (bRecord && !pDoc->IsUndoEnabled())
659 bRecord = sal_False;
660 ScDBData* pDBData = pDoc->GetDBAtArea( nTab, rQueryParam.nCol1, rQueryParam.nRow1,
661 rQueryParam.nCol2, rQueryParam.nRow2 );
662 if (!pDBData)
663 {
664 DBG_ERROR( "Query: keine DBData" );
665 return sal_False;
666 }
667
668 // Wechsel von Inplace auf nicht-Inplace, dann erst Inplace aufheben:
669 // (nur, wenn im Dialog "Persistent" ausgewaehlt ist)
670
671 if ( !rQueryParam.bInplace && pDBData->HasQueryParam() && rQueryParam.bDestPers )
672 {
673 ScQueryParam aOldQuery;
674 pDBData->GetQueryParam(aOldQuery);
675 if (aOldQuery.bInplace)
676 {
677 // alte Filterung aufheben
678
679 SCSIZE nEC = aOldQuery.GetEntryCount();
680 for (SCSIZE i=0; i<nEC; i++)
681 aOldQuery.GetEntry(i).bDoQuery = sal_False;
682 aOldQuery.bDuplicate = sal_True;
683 Query( nTab, aOldQuery, NULL, bRecord, bApi );
684 }
685 }
686
687 ScQueryParam aLocalParam( rQueryParam ); // fuer Paint / Zielbereich
688 sal_Bool bCopy = !rQueryParam.bInplace; // kopiert wird in Table::Query
689 ScDBData* pDestData = NULL; // Bereich, in den kopiert wird
690 sal_Bool bDoSize = sal_False; // Zielgroesse anpassen (einf./loeschen)
691 SCCOL nFormulaCols = 0; // nur bei bDoSize
692 sal_Bool bKeepFmt = sal_False;
693 ScRange aOldDest;
694 ScRange aDestTotal;
695 if ( bCopy && rQueryParam.nDestCol == rQueryParam.nCol1 &&
696 rQueryParam.nDestRow == rQueryParam.nRow1 && rQueryParam.nDestTab == nTab )
697 bCopy = sal_False;
698 SCTAB nDestTab = nTab;
699 if ( bCopy )
700 {
701 aLocalParam.MoveToDest();
702 nDestTab = rQueryParam.nDestTab;
703 if ( !ValidColRow( aLocalParam.nCol2, aLocalParam.nRow2 ) )
704 {
705 if (!bApi)
706 rDocShell.ErrorMessage(STR_PASTE_FULL);
707 return sal_False;
708 }
709
710 ScEditableTester aTester( pDoc, nDestTab, aLocalParam.nCol1,aLocalParam.nRow1,
711 aLocalParam.nCol2,aLocalParam.nRow2);
712 if (!aTester.IsEditable())
713 {
714 if (!bApi)
715 rDocShell.ErrorMessage(aTester.GetMessageId());
716 return sal_False;
717 }
718
719 pDestData = pDoc->GetDBAtCursor( rQueryParam.nDestCol, rQueryParam.nDestRow,
720 rQueryParam.nDestTab, sal_True );
721 if (pDestData)
722 {
723 pDestData->GetArea( aOldDest );
724 aDestTotal=ScRange( rQueryParam.nDestCol,
725 rQueryParam.nDestRow,
726 nDestTab,
727 rQueryParam.nDestCol + rQueryParam.nCol2 - rQueryParam.nCol1,
728 rQueryParam.nDestRow + rQueryParam.nRow2 - rQueryParam.nRow1,
729 nDestTab );
730
731 bDoSize = pDestData->IsDoSize();
732 // Test, ob Formeln aufgefuellt werden muessen (nFormulaCols):
733 if ( bDoSize && aOldDest.aEnd.Col() == aDestTotal.aEnd.Col() )
734 {
735 SCCOL nTestCol = aOldDest.aEnd.Col() + 1; // neben dem Bereich
736 SCROW nTestRow = rQueryParam.nDestRow +
737 ( aLocalParam.bHasHeader ? 1 : 0 );
738 while ( nTestCol <= MAXCOL &&
739 pDoc->GetCellType(ScAddress( nTestCol, nTestRow, nTab )) == CELLTYPE_FORMULA )
740 ++nTestCol, ++nFormulaCols;
741 }
742
743 bKeepFmt = pDestData->IsKeepFmt();
744 if ( bDoSize && !pDoc->CanFitBlock( aOldDest, aDestTotal ) )
745 {
746 if (!bApi)
747 rDocShell.ErrorMessage(STR_MSSG_DOSUBTOTALS_2); // kann keine Zeilen einfuegen
748 return sal_False;
749 }
750 }
751 }
752
753 // ausfuehren
754
755 WaitObject aWait( rDocShell.GetActiveDialogParent() );
756
757 sal_Bool bKeepSub = sal_False; // bestehende Teilergebnisse wiederholen?
758 ScSubTotalParam aSubTotalParam;
759 if (rQueryParam.GetEntry(0).bDoQuery) // nicht beim Aufheben
760 {
761 pDBData->GetSubTotalParam( aSubTotalParam ); // Teilergebnisse vorhanden?
762
763 if ( aSubTotalParam.bGroupActive[0] && !aSubTotalParam.bRemoveOnly )
764 bKeepSub = sal_True;
765 }
766
767 ScDocument* pUndoDoc = NULL;
768 ScDBCollection* pUndoDB = NULL;
769 const ScRange* pOld = NULL;
770
771 if ( bRecord )
772 {
773 pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
774 if (bCopy)
775 {
776 pUndoDoc->InitUndo( pDoc, nDestTab, nDestTab, sal_False, sal_True );
777 pDoc->CopyToDocument( aLocalParam.nCol1, aLocalParam.nRow1, nDestTab,
778 aLocalParam.nCol2, aLocalParam.nRow2, nDestTab,
779 IDF_ALL, sal_False, pUndoDoc );
780 // Attribute sichern, falls beim Filtern mitkopiert
781
782 if (pDestData)
783 {
784 pDoc->CopyToDocument( aOldDest, IDF_ALL, sal_False, pUndoDoc );
785 pOld = &aOldDest;
786 }
787 }
788 else
789 {
790 pUndoDoc->InitUndo( pDoc, nTab, nTab, sal_False, sal_True );
791 pDoc->CopyToDocument( 0, rQueryParam.nRow1, nTab, MAXCOL, rQueryParam.nRow2, nTab,
792 IDF_NONE, sal_False, pUndoDoc );
793 }
794
795 ScDBCollection* pDocDB = pDoc->GetDBCollection();
796 if (pDocDB->GetCount())
797 pUndoDB = new ScDBCollection( *pDocDB );
798
799 pDoc->BeginDrawUndo();
800 }
801
802 ScDocument* pAttribDoc = NULL;
803 ScRange aAttribRange;
804 if (pDestData) // Zielbereich loeschen
805 {
806 if ( bKeepFmt )
807 {
808 // kleinere der End-Spalten, Header+1 Zeile
809 aAttribRange = aOldDest;
810 if ( aAttribRange.aEnd.Col() > aDestTotal.aEnd.Col() )
811 aAttribRange.aEnd.SetCol( aDestTotal.aEnd.Col() );
812 aAttribRange.aEnd.SetRow( aAttribRange.aStart.Row() +
813 ( aLocalParam.bHasHeader ? 1 : 0 ) );
814
815 // auch fuer aufgefuellte Formeln
816 aAttribRange.aEnd.SetCol( aAttribRange.aEnd.Col() + nFormulaCols );
817
818 pAttribDoc = new ScDocument( SCDOCMODE_UNDO );
819 pAttribDoc->InitUndo( pDoc, nDestTab, nDestTab, sal_False, sal_True );
820 pDoc->CopyToDocument( aAttribRange, IDF_ATTRIB, sal_False, pAttribDoc );
821 }
822
823 if ( bDoSize )
824 pDoc->FitBlock( aOldDest, aDestTotal );
825 else
826 pDoc->DeleteAreaTab(aOldDest, IDF_ALL); // einfach loeschen
827 }
828
829 // Filtern am Dokument ausfuehren
830 SCSIZE nCount = pDoc->Query( nTab, rQueryParam, bKeepSub );
831 if (bCopy)
832 {
833 aLocalParam.nRow2 = aLocalParam.nRow1 + nCount;
834 if (!aLocalParam.bHasHeader && nCount > 0)
835 --aLocalParam.nRow2;
836
837 if ( bDoSize )
838 {
839 // auf wirklichen Ergebnis-Bereich anpassen
840 // (das hier ist immer eine Verkleinerung)
841
842 ScRange aNewDest( aLocalParam.nCol1, aLocalParam.nRow1, nDestTab,
843 aLocalParam.nCol2, aLocalParam.nRow2, nDestTab );
844 pDoc->FitBlock( aDestTotal, aNewDest, sal_False ); // sal_False - nicht loeschen
845
846 if ( nFormulaCols > 0 )
847 {
848 // Formeln ausfuellen
849 //! Undo (Query und Repeat) !!!
850
851 ScRange aNewForm( aLocalParam.nCol2+1, aLocalParam.nRow1, nDestTab,
852 aLocalParam.nCol2+nFormulaCols, aLocalParam.nRow2, nDestTab );
853 ScRange aOldForm = aNewForm;
854 aOldForm.aEnd.SetRow( aOldDest.aEnd.Row() );
855 pDoc->FitBlock( aOldForm, aNewForm, sal_False );
856
857 ScMarkData aMark;
858 aMark.SelectOneTable(nDestTab);
859 SCROW nFStartY = aLocalParam.nRow1 + ( aLocalParam.bHasHeader ? 1 : 0 );
860 pDoc->Fill( aLocalParam.nCol2+1, nFStartY,
861 aLocalParam.nCol2+nFormulaCols, nFStartY, aMark,
862 aLocalParam.nRow2 - nFStartY,
863 FILL_TO_BOTTOM, FILL_SIMPLE );
864 }
865 }
866
867 if ( pAttribDoc ) // gemerkte Attribute zurueckkopieren
868 {
869 // Header
870 if (aLocalParam.bHasHeader)
871 {
872 ScRange aHdrRange = aAttribRange;
873 aHdrRange.aEnd.SetRow( aHdrRange.aStart.Row() );
874 pAttribDoc->CopyToDocument( aHdrRange, IDF_ATTRIB, sal_False, pDoc );
875 }
876
877 // Daten
878 SCCOL nAttrEndCol = aAttribRange.aEnd.Col();
879 SCROW nAttrRow = aAttribRange.aStart.Row() + ( aLocalParam.bHasHeader ? 1 : 0 );
880 for (SCCOL nCol = aAttribRange.aStart.Col(); nCol<=nAttrEndCol; nCol++)
881 {
882 const ScPatternAttr* pSrcPattern = pAttribDoc->GetPattern(
883 nCol, nAttrRow, nDestTab );
884 DBG_ASSERT(pSrcPattern,"Pattern ist 0");
885 if (pSrcPattern)
886 pDoc->ApplyPatternAreaTab( nCol, nAttrRow, nCol, aLocalParam.nRow2,
887 nDestTab, *pSrcPattern );
888 const ScStyleSheet* pStyle = pSrcPattern->GetStyleSheet();
889 if (pStyle)
890 pDoc->ApplyStyleAreaTab( nCol, nAttrRow, nCol, aLocalParam.nRow2,
891 nDestTab, *pStyle );
892 }
893
894 delete pAttribDoc;
895 }
896 }
897
898 // speichern: Inplace immer, sonst je nach Einstellung
899 // alter Inplace-Filter ist ggf. schon aufgehoben
900
901 sal_Bool bSave = rQueryParam.bInplace || rQueryParam.bDestPers;
902 if (bSave) // merken
903 {
904 pDBData->SetQueryParam( rQueryParam );
905 pDBData->SetHeader( rQueryParam.bHasHeader ); //! ???
906 pDBData->SetAdvancedQuerySource( pAdvSource ); // after SetQueryParam
907 }
908
909 if (bCopy) // neuen DB-Bereich merken
910 {
911 // selektieren wird hinterher von aussen (dbfunc)
912 // momentan ueber DB-Bereich an der Zielposition, darum muss dort
913 // auf jeden Fall ein Bereich angelegt werden.
914
915 ScDBData* pNewData;
916 if (pDestData)
917 pNewData = pDestData; // Bereich vorhanden -> anpassen (immer!)
918 else // Bereich anlegen
919 pNewData = rDocShell.GetDBData(
920 ScRange( aLocalParam.nCol1, aLocalParam.nRow1, nDestTab,
921 aLocalParam.nCol2, aLocalParam.nRow2, nDestTab ),
922 SC_DB_MAKE, SC_DBSEL_FORCE_MARK );
923
924 if (pNewData)
925 {
926 pNewData->SetArea( nDestTab, aLocalParam.nCol1, aLocalParam.nRow1,
927 aLocalParam.nCol2, aLocalParam.nRow2 );
928
929 // Query-Param wird am Ziel nicht mehr eingestellt, fuehrt nur zu Verwirrung
930 // und Verwechslung mit dem Query-Param am Quellbereich (#37187#)
931 }
932 else
933 {
934 DBG_ERROR("Zielbereich nicht da");
935 }
936 }
937
938 if (!bCopy)
939 {
940 pDoc->InvalidatePageBreaks(nTab);
941 pDoc->UpdatePageBreaks( nTab );
942 }
943
944 // #i23299# because of Subtotal functions, the whole rows must be set dirty
945 ScRange aDirtyRange( 0 , aLocalParam.nRow1, nDestTab,
946 MAXCOL, aLocalParam.nRow2, nDestTab );
947 pDoc->SetDirty( aDirtyRange );
948
949 if ( bRecord )
950 {
951 // create undo action after executing, because of drawing layer undo
952 rDocShell.GetUndoManager()->AddUndoAction(
953 new ScUndoQuery( &rDocShell, nTab, rQueryParam, pUndoDoc, pUndoDB,
954 pOld, bDoSize, pAdvSource ) );
955 }
956
957
958 if (bCopy)
959 {
960 SCCOL nEndX = aLocalParam.nCol2;
961 SCROW nEndY = aLocalParam.nRow2;
962 if (pDestData)
963 {
964 if ( aOldDest.aEnd.Col() > nEndX )
965 nEndX = aOldDest.aEnd.Col();
966 if ( aOldDest.aEnd.Row() > nEndY )
967 nEndY = aOldDest.aEnd.Row();
968 }
969 if (bDoSize)
970 nEndY = MAXROW;
971 rDocShell.PostPaint( aLocalParam.nCol1, aLocalParam.nRow1, nDestTab,
972 nEndX, nEndY, nDestTab, PAINT_GRID );
973 }
974 else
975 rDocShell.PostPaint( 0, rQueryParam.nRow1, nTab, MAXCOL, MAXROW, nTab,
976 PAINT_GRID | PAINT_LEFT );
977 aModificator.SetDocumentModified();
978
979 return sal_True;
980 }
981
982 // -----------------------------------------------------------------
983
DoSubTotals(SCTAB nTab,const ScSubTotalParam & rParam,const ScSortParam * pForceNewSort,sal_Bool bRecord,sal_Bool bApi)984 sal_Bool ScDBDocFunc::DoSubTotals( SCTAB nTab, const ScSubTotalParam& rParam,
985 const ScSortParam* pForceNewSort, sal_Bool bRecord, sal_Bool bApi )
986 {
987 //! auch fuer ScDBFunc::DoSubTotals benutzen!
988 // dann bleibt aussen:
989 // - neuen Bereich (aus DBData) markieren
990 // - SelectionChanged (?)
991
992 sal_Bool bDo = !rParam.bRemoveOnly; // sal_False = nur loeschen
993 sal_Bool bRet = sal_False;
994
995 ScDocument* pDoc = rDocShell.GetDocument();
996 if (bRecord && !pDoc->IsUndoEnabled())
997 bRecord = sal_False;
998 ScDBData* pDBData = pDoc->GetDBAtArea( nTab, rParam.nCol1, rParam.nRow1,
999 rParam.nCol2, rParam.nRow2 );
1000 if (!pDBData)
1001 {
1002 DBG_ERROR( "SubTotals: keine DBData" );
1003 return sal_False;
1004 }
1005
1006 ScEditableTester aTester( pDoc, nTab, 0,rParam.nRow1+1, MAXCOL,MAXROW );
1007 if (!aTester.IsEditable())
1008 {
1009 if (!bApi)
1010 rDocShell.ErrorMessage(aTester.GetMessageId());
1011 return sal_False;
1012 }
1013
1014 if (pDoc->HasAttrib( rParam.nCol1, rParam.nRow1+1, nTab,
1015 rParam.nCol2, rParam.nRow2, nTab, HASATTR_MERGED | HASATTR_OVERLAPPED ))
1016 {
1017 if (!bApi)
1018 rDocShell.ErrorMessage(STR_MSSG_INSERTCELLS_0); // nicht in zusammengefasste einfuegen
1019 return sal_False;
1020 }
1021
1022 sal_Bool bOk = sal_True;
1023 sal_Bool bDelete = sal_False;
1024 if (rParam.bReplace)
1025 if (pDoc->TestRemoveSubTotals( nTab, rParam ))
1026 {
1027 bDelete = sal_True;
1028 bOk = ( MessBox( rDocShell.GetActiveDialogParent(), WinBits(WB_YES_NO | WB_DEF_YES),
1029 // "StarCalc" "Daten loeschen?"
1030 ScGlobal::GetRscString( STR_MSSG_DOSUBTOTALS_0 ),
1031 ScGlobal::GetRscString( STR_MSSG_DOSUBTOTALS_1 ) ).Execute()
1032 == RET_YES );
1033 }
1034
1035 if (bOk)
1036 {
1037 WaitObject aWait( rDocShell.GetActiveDialogParent() );
1038 ScDocShellModificator aModificator( rDocShell );
1039
1040 ScSubTotalParam aNewParam( rParam ); // Bereichsende wird veraendert
1041 ScDocument* pUndoDoc = NULL;
1042 ScOutlineTable* pUndoTab = NULL;
1043 ScRangeName* pUndoRange = NULL;
1044 ScDBCollection* pUndoDB = NULL;
1045 SCTAB nTabCount = 0; // fuer Referenz-Undo
1046
1047 if (bRecord) // alte Daten sichern
1048 {
1049 sal_Bool bOldFilter = bDo && rParam.bDoSort;
1050
1051 nTabCount = pDoc->GetTableCount();
1052 pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
1053 ScOutlineTable* pTable = pDoc->GetOutlineTable( nTab );
1054 if (pTable)
1055 {
1056 pUndoTab = new ScOutlineTable( *pTable );
1057
1058 // column/row state
1059 SCCOLROW nOutStartCol, nOutEndCol;
1060 SCCOLROW nOutStartRow, nOutEndRow;
1061 pTable->GetColArray()->GetRange( nOutStartCol, nOutEndCol );
1062 pTable->GetRowArray()->GetRange( nOutStartRow, nOutEndRow );
1063
1064 pUndoDoc->InitUndo( pDoc, nTab, nTab, sal_True, sal_True );
1065 pDoc->CopyToDocument( static_cast<SCCOL>(nOutStartCol), 0, nTab, static_cast<SCCOL>(nOutEndCol), MAXROW, nTab, IDF_NONE, sal_False, pUndoDoc );
1066 pDoc->CopyToDocument( 0, nOutStartRow, nTab, MAXCOL, nOutEndRow, nTab, IDF_NONE, sal_False, pUndoDoc );
1067 }
1068 else
1069 pUndoDoc->InitUndo( pDoc, nTab, nTab, sal_False, bOldFilter );
1070
1071 // Datenbereich sichern - incl. Filter-Ergebnis
1072 pDoc->CopyToDocument( 0,rParam.nRow1+1,nTab, MAXCOL,rParam.nRow2,nTab,
1073 IDF_ALL, sal_False, pUndoDoc );
1074
1075 // alle Formeln wegen Referenzen
1076 pDoc->CopyToDocument( 0,0,0, MAXCOL,MAXROW,nTabCount-1,
1077 IDF_FORMULA, sal_False, pUndoDoc );
1078
1079 // DB- und andere Bereiche
1080 ScRangeName* pDocRange = pDoc->GetRangeName();
1081 if (pDocRange->GetCount())
1082 pUndoRange = new ScRangeName( *pDocRange );
1083 ScDBCollection* pDocDB = pDoc->GetDBCollection();
1084 if (pDocDB->GetCount())
1085 pUndoDB = new ScDBCollection( *pDocDB );
1086 }
1087
1088 // pDoc->SetOutlineTable( nTab, NULL );
1089 ScOutlineTable* pOut = pDoc->GetOutlineTable( nTab );
1090 if (pOut)
1091 pOut->GetRowArray()->RemoveAll(); // nur Zeilen-Outlines loeschen
1092
1093 if (rParam.bReplace)
1094 pDoc->RemoveSubTotals( nTab, aNewParam );
1095 sal_Bool bSuccess = sal_True;
1096 if (bDo)
1097 {
1098 // Sortieren
1099 if ( rParam.bDoSort || pForceNewSort )
1100 {
1101 pDBData->SetArea( nTab, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 );
1102
1103 // Teilergebnis-Felder vor die Sortierung setzen
1104 // (doppelte werden weggelassen, kann darum auch wieder aufgerufen werden)
1105
1106 ScSortParam aOldSort;
1107 pDBData->GetSortParam( aOldSort );
1108 ScSortParam aSortParam( aNewParam, pForceNewSort ? *pForceNewSort : aOldSort );
1109 Sort( nTab, aSortParam, sal_False, sal_False, bApi );
1110 }
1111
1112 bSuccess = pDoc->DoSubTotals( nTab, aNewParam );
1113 }
1114 ScRange aDirtyRange( aNewParam.nCol1, aNewParam.nRow1, nTab,
1115 aNewParam.nCol2, aNewParam.nRow2, nTab );
1116 pDoc->SetDirty( aDirtyRange );
1117
1118 if (bRecord)
1119 {
1120 // ScDBData* pUndoDBData = pDBData ? new ScDBData( *pDBData ) : NULL;
1121 rDocShell.GetUndoManager()->AddUndoAction(
1122 new ScUndoSubTotals( &rDocShell, nTab,
1123 rParam, aNewParam.nRow2,
1124 pUndoDoc, pUndoTab, // pUndoDBData,
1125 pUndoRange, pUndoDB ) );
1126 }
1127
1128 if (!bSuccess)
1129 {
1130 // "Kann keine Zeilen einfuegen"
1131 if (!bApi)
1132 rDocShell.ErrorMessage(STR_MSSG_DOSUBTOTALS_2);
1133 }
1134
1135 // merken
1136 pDBData->SetSubTotalParam( aNewParam );
1137 pDBData->SetArea( nTab, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 );
1138 pDoc->CompileDBFormula();
1139
1140 rDocShell.PostPaint( 0,0,nTab, MAXCOL,MAXROW,nTab,
1141 PAINT_GRID | PAINT_LEFT | PAINT_TOP | PAINT_SIZE );
1142 aModificator.SetDocumentModified();
1143
1144 bRet = bSuccess;
1145 }
1146 return bRet;
1147 }
1148
1149 //==================================================================
1150
lcl_EmptyExcept(ScDocument * pDoc,const ScRange & rRange,const ScRange & rExcept)1151 sal_Bool lcl_EmptyExcept( ScDocument* pDoc, const ScRange& rRange, const ScRange& rExcept )
1152 {
1153 ScCellIterator aIter( pDoc, rRange );
1154 ScBaseCell* pCell = aIter.GetFirst();
1155 while (pCell)
1156 {
1157 if ( !pCell->IsBlank() ) // real content?
1158 {
1159 if ( !rExcept.In( ScAddress( aIter.GetCol(), aIter.GetRow(), aIter.GetTab() ) ) )
1160 return sal_False; // cell found
1161 }
1162 pCell = aIter.GetNext();
1163 }
1164
1165 return sal_True; // nothing found - empty
1166 }
1167
DataPilotUpdate(ScDPObject * pOldObj,const ScDPObject * pNewObj,sal_Bool bRecord,sal_Bool bApi,sal_Bool bAllowMove)1168 sal_Bool ScDBDocFunc::DataPilotUpdate( ScDPObject* pOldObj, const ScDPObject* pNewObj,
1169 sal_Bool bRecord, sal_Bool bApi, sal_Bool bAllowMove )
1170 {
1171 ScDocShellModificator aModificator( rDocShell );
1172 WaitObject aWait( rDocShell.GetActiveDialogParent() );
1173
1174 sal_Bool bDone = sal_False;
1175 sal_Bool bUndoSelf = sal_False;
1176 sal_uInt16 nErrId = 0;
1177
1178 ScDocument* pOldUndoDoc = NULL;
1179 ScDocument* pNewUndoDoc = NULL;
1180 ScDPObject* pUndoDPObj = NULL;
1181 if ( bRecord && pOldObj )
1182 pUndoDPObj = new ScDPObject( *pOldObj ); // copy old settings for undo
1183
1184 ScDocument* pDoc = rDocShell.GetDocument();
1185 if (bRecord && !pDoc->IsUndoEnabled())
1186 bRecord = sal_False;
1187 if ( !rDocShell.IsEditable() || pDoc->GetChangeTrack() )
1188 {
1189 // not recorded -> disallow
1190 //! different error messages?
1191
1192 nErrId = STR_PROTECTIONERR;
1193 }
1194 if ( pOldObj && !nErrId )
1195 {
1196 ScRange aOldOut = pOldObj->GetOutRange();
1197 ScEditableTester aTester( pDoc, aOldOut );
1198 if ( !aTester.IsEditable() )
1199 nErrId = aTester.GetMessageId();
1200 }
1201 if ( pNewObj && !nErrId )
1202 {
1203 // at least one cell at the output position must be editable
1204 // -> check in advance
1205 // (start of output range in pNewObj is valid)
1206
1207 ScRange aNewStart( pNewObj->GetOutRange().aStart );
1208 ScEditableTester aTester( pDoc, aNewStart );
1209 if ( !aTester.IsEditable() )
1210 nErrId = aTester.GetMessageId();
1211 }
1212
1213 ScDPObject* pDestObj = NULL;
1214 if ( !nErrId )
1215 {
1216 if ( pOldObj && !pNewObj )
1217 {
1218 // delete table
1219
1220 ScRange aRange = pOldObj->GetOutRange();
1221 SCTAB nTab = aRange.aStart.Tab();
1222
1223 if ( bRecord )
1224 {
1225 pOldUndoDoc = new ScDocument( SCDOCMODE_UNDO );
1226 pOldUndoDoc->InitUndo( pDoc, nTab, nTab );
1227 pDoc->CopyToDocument( aRange, IDF_ALL, sal_False, pOldUndoDoc );
1228 }
1229
1230 pDoc->DeleteAreaTab( aRange.aStart.Col(), aRange.aStart.Row(),
1231 aRange.aEnd.Col(), aRange.aEnd.Row(),
1232 nTab, IDF_ALL );
1233 pDoc->RemoveFlagsTab( aRange.aStart.Col(), aRange.aStart.Row(),
1234 aRange.aEnd.Col(), aRange.aEnd.Row(),
1235 nTab, SC_MF_AUTO );
1236
1237 pDoc->GetDPCollection()->FreeTable( pOldObj ); // object is deleted here
1238
1239 rDocShell.PostPaintGridAll(); //! only necessary parts
1240 rDocShell.PostPaint( aRange.aStart.Col(), aRange.aStart.Row(), nTab,
1241 aRange.aEnd.Col(), aRange.aEnd.Row(), nTab,
1242 PAINT_GRID );
1243 bDone = sal_True;
1244 }
1245 else if ( pNewObj )
1246 {
1247 if ( pOldObj )
1248 {
1249 if ( bRecord )
1250 {
1251 ScRange aRange = pOldObj->GetOutRange();
1252 SCTAB nTab = aRange.aStart.Tab();
1253 pOldUndoDoc = new ScDocument( SCDOCMODE_UNDO );
1254 pOldUndoDoc->InitUndo( pDoc, nTab, nTab );
1255 pDoc->CopyToDocument( aRange, IDF_ALL, sal_False, pOldUndoDoc );
1256 }
1257
1258 if ( pNewObj == pOldObj )
1259 {
1260 // refresh only - no settings modified
1261 }
1262 else
1263 {
1264 pNewObj->WriteSourceDataTo( *pOldObj ); // copy source data
1265
1266 ScDPSaveData* pData = pNewObj->GetSaveData();
1267 DBG_ASSERT( pData, "no SaveData from living DPObject" );
1268 if ( pData )
1269 pOldObj->SetSaveData( *pData ); // copy SaveData
1270 }
1271
1272 pDestObj = pOldObj;
1273 pDestObj->SetAllowMove( bAllowMove );
1274 }
1275 else
1276 {
1277 // output range must be set at pNewObj
1278
1279 pDestObj = new ScDPObject( *pNewObj );
1280
1281 // #i94570# When changing the output position in the dialog, a new table is created
1282 // with the settings from the old table, including the name.
1283 // So we have to check for duplicate names here (before inserting).
1284 if ( pDoc->GetDPCollection()->GetByName(pDestObj->GetName()) )
1285 pDestObj->SetName( String() ); // ignore the invalid name, create a new name below
1286
1287 pDestObj->SetAlive(sal_True);
1288 if ( !pDoc->GetDPCollection()->InsertNewTable(pDestObj) )
1289 {
1290 DBG_ERROR("cannot insert DPObject");
1291 DELETEZ( pDestObj );
1292 }
1293 }
1294 if ( pDestObj )
1295 {
1296 // #78541# create new database connection for "refresh"
1297 // (and re-read column entry collections)
1298 // so all changes take effect
1299 if ( pNewObj == pOldObj && pDestObj->IsImportData() )
1300 pDestObj->InvalidateSource();
1301
1302 pDestObj->InvalidateData(); // before getting the new output area
1303
1304 // make sure the table has a name (not set by dialog)
1305 if ( !pDestObj->GetName().Len() )
1306 pDestObj->SetName( pDoc->GetDPCollection()->CreateNewName() );
1307
1308 sal_Bool bOverflow = sal_False;
1309 ScRange aNewOut = pDestObj->GetNewOutputRange( bOverflow );
1310
1311 //! test for overlap with other data pilot tables
1312 if( pOldObj )
1313 {
1314 const ScSheetSourceDesc* pSheetDesc = pOldObj->GetSheetDesc();
1315 if( pSheetDesc && pSheetDesc->aSourceRange.Intersects( aNewOut ) )
1316 {
1317 ScRange aOldRange = pOldObj->GetOutRange();
1318 SCsROW nDiff = aOldRange.aStart.Row()-aNewOut.aStart.Row();
1319 aNewOut.aStart.SetRow( aOldRange.aStart.Row() );
1320 aNewOut.aEnd.SetRow( aNewOut.aEnd.Row()+nDiff );
1321 if( !ValidRow( aNewOut.aStart.Row() ) || !ValidRow( aNewOut.aEnd.Row() ) )
1322 bOverflow = sal_True;
1323 }
1324 }
1325
1326 if ( bOverflow )
1327 {
1328 // like with STR_PROTECTIONERR, use undo to reverse everything
1329 DBG_ASSERT( bRecord, "DataPilotUpdate: can't undo" );
1330 bUndoSelf = sal_True;
1331 nErrId = STR_PIVOT_ERROR;
1332 }
1333 else
1334 {
1335 ScEditableTester aTester( pDoc, aNewOut );
1336 if ( !aTester.IsEditable() )
1337 {
1338 // destination area isn't editable
1339 //! reverse everything done so far, don't proceed
1340
1341 // quick solution: proceed to end, use undo action
1342 // to reverse everything:
1343 DBG_ASSERT( bRecord, "DataPilotUpdate: can't undo" );
1344 bUndoSelf = sal_True;
1345 nErrId = aTester.GetMessageId();
1346 }
1347 }
1348
1349 // test if new output area is empty except for old area
1350 if ( !bApi )
1351 {
1352 sal_Bool bEmpty;
1353 if ( pOldObj ) // OutRange of pOldObj (pDestObj) is still old area
1354 bEmpty = lcl_EmptyExcept( pDoc, aNewOut, pOldObj->GetOutRange() );
1355 else
1356 bEmpty = pDoc->IsBlockEmpty( aNewOut.aStart.Tab(),
1357 aNewOut.aStart.Col(), aNewOut.aStart.Row(),
1358 aNewOut.aEnd.Col(), aNewOut.aEnd.Row() );
1359
1360 if ( !bEmpty )
1361 {
1362 QueryBox aBox( rDocShell.GetActiveDialogParent(), WinBits(WB_YES_NO | WB_DEF_YES),
1363 ScGlobal::GetRscString(STR_PIVOT_NOTEMPTY) );
1364 if (aBox.Execute() == RET_NO)
1365 {
1366 //! like above (not editable), use undo to reverse everything
1367 DBG_ASSERT( bRecord, "DataPilotUpdate: can't undo" );
1368 bUndoSelf = sal_True;
1369 }
1370 }
1371 }
1372
1373 if ( bRecord )
1374 {
1375 SCTAB nTab = aNewOut.aStart.Tab();
1376 pNewUndoDoc = new ScDocument( SCDOCMODE_UNDO );
1377 pNewUndoDoc->InitUndo( pDoc, nTab, nTab );
1378 pDoc->CopyToDocument( aNewOut, IDF_ALL, sal_False, pNewUndoDoc );
1379 }
1380
1381 pDestObj->Output( aNewOut.aStart );
1382
1383 rDocShell.PostPaintGridAll(); //! only necessary parts
1384 bDone = sal_True;
1385 }
1386 }
1387 // else nothing (no old, no new)
1388 }
1389
1390 if ( bRecord && bDone )
1391 {
1392 SfxUndoAction* pAction = new ScUndoDataPilot( &rDocShell,
1393 pOldUndoDoc, pNewUndoDoc, pUndoDPObj, pDestObj, bAllowMove );
1394 pOldUndoDoc = NULL;
1395 pNewUndoDoc = NULL; // pointers are used in undo action
1396 // pUndoDPObj is copied
1397
1398 if (bUndoSelf)
1399 {
1400 // use undo action to restore original state
1401 //! prevent setting the document modified? (ScDocShellModificator)
1402
1403 pAction->Undo();
1404 delete pAction;
1405 bDone = sal_False;
1406 }
1407 else
1408 rDocShell.GetUndoManager()->AddUndoAction( pAction );
1409 }
1410
1411 delete pOldUndoDoc; // if not used for undo
1412 delete pNewUndoDoc;
1413 delete pUndoDPObj;
1414
1415 if (bDone)
1416 {
1417 // notify API objects
1418 if (pDestObj)
1419 pDoc->BroadcastUno( ScDataPilotModifiedHint( pDestObj->GetName() ) );
1420 aModificator.SetDocumentModified();
1421 }
1422
1423 if ( nErrId && !bApi )
1424 rDocShell.ErrorMessage( nErrId );
1425
1426 return bDone;
1427 }
1428
1429 //==================================================================
1430 //
1431 // database import
1432
UpdateImport(const String & rTarget,const svx::ODataAccessDescriptor & rDescriptor)1433 void ScDBDocFunc::UpdateImport( const String& rTarget, const svx::ODataAccessDescriptor& rDescriptor )
1434 {
1435 // rTarget is the name of a database range
1436
1437 ScDocument* pDoc = rDocShell.GetDocument();
1438 ScDBCollection& rDBColl = *pDoc->GetDBCollection();
1439 ScDBData* pData = NULL;
1440 ScImportParam aImportParam;
1441 sal_Bool bFound = sal_False;
1442 sal_uInt16 nCount = rDBColl.GetCount();
1443 for (sal_uInt16 i=0; i<nCount && !bFound; i++)
1444 {
1445 pData = rDBColl[i];
1446 if (pData->GetName() == rTarget)
1447 bFound = sal_True;
1448 }
1449 if (!bFound)
1450 {
1451 InfoBox aInfoBox(rDocShell.GetActiveDialogParent(),
1452 ScGlobal::GetRscString( STR_TARGETNOTFOUND ) );
1453 aInfoBox.Execute();
1454 return;
1455 }
1456
1457 SCTAB nTab;
1458 SCCOL nDummyCol;
1459 SCROW nDummyRow;
1460 pData->GetArea( nTab, nDummyCol,nDummyRow,nDummyCol,nDummyRow );
1461 pData->GetImportParam( aImportParam );
1462
1463 rtl::OUString sDBName;
1464 rtl::OUString sDBTable;
1465 sal_Int32 nCommandType = 0;
1466 rDescriptor[svx::daDataSource] >>= sDBName;
1467 rDescriptor[svx::daCommand] >>= sDBTable;
1468 rDescriptor[svx::daCommandType] >>= nCommandType;
1469
1470 aImportParam.aDBName = sDBName;
1471 aImportParam.bSql = ( nCommandType == sdb::CommandType::COMMAND );
1472 aImportParam.aStatement = sDBTable;
1473 aImportParam.bNative = sal_False;
1474 aImportParam.nType = static_cast<sal_uInt8>( ( nCommandType == sdb::CommandType::QUERY ) ? ScDbQuery : ScDbTable );
1475 aImportParam.bImport = sal_True;
1476
1477 sal_Bool bContinue = DoImport( nTab, aImportParam, &rDescriptor, sal_True );
1478
1479 // DB-Operationen wiederholen
1480
1481 ScTabViewShell* pViewSh = rDocShell.GetBestViewShell();
1482 if (pViewSh)
1483 {
1484 ScRange aRange;
1485 pData->GetArea(aRange);
1486 pViewSh->MarkRange(aRange); // selektieren
1487
1488 if ( bContinue ) // #41905# Fehler beim Import -> Abbruch
1489 {
1490 // interne Operationen, wenn welche gespeichert
1491
1492 if ( pData->HasQueryParam() || pData->HasSortParam() || pData->HasSubTotalParam() )
1493 pViewSh->RepeatDB();
1494
1495 // Pivottabellen die den Bereich als Quelldaten haben
1496
1497 rDocShell.RefreshPivotTables(aRange);
1498 }
1499 }
1500 }
1501
1502
1503
1504
1505