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