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 "dbfunc.hxx" 32 #include "scitems.hxx" 33 #include <sfx2/bindings.hxx> 34 #include <vcl/svapp.hxx> 35 #include <vcl/msgbox.hxx> 36 #include <vcl/sound.hxx> 37 #include <vcl/waitobj.hxx> 38 #include <svl/zforlist.hxx> 39 #include <sfx2/app.hxx> 40 #include <com/sun/star/beans/XPropertySet.hpp> 41 #include <com/sun/star/container/XNameAccess.hpp> 42 #include <com/sun/star/sheet/DataPilotFieldFilter.hpp> 43 #include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp> 44 #include <com/sun/star/sheet/DataPilotFieldOrientation.hpp> 45 #include <com/sun/star/sheet/DataPilotFieldSortMode.hpp> 46 #include <com/sun/star/sheet/DataPilotTableHeaderData.hpp> 47 #include <com/sun/star/sheet/GeneralFunction.hpp> 48 #include <com/sun/star/sheet/MemberResultFlags.hpp> 49 #include <com/sun/star/sheet/XDimensionsSupplier.hpp> 50 #include <com/sun/star/sheet/XDrillDownDataSupplier.hpp> 51 52 #include "global.hxx" 53 #include "globstr.hrc" 54 #include "sc.hrc" 55 #include "undotab.hxx" 56 #include "undodat.hxx" 57 #include "dbcolect.hxx" 58 #include "rangenam.hxx" 59 #include "rangeutl.hxx" 60 #include "docsh.hxx" 61 #include "olinetab.hxx" 62 #include "consoli.hxx" 63 #include "olinefun.hxx" 64 #include "dpobject.hxx" 65 #include "dpsave.hxx" 66 #include "dpdimsave.hxx" 67 #include "dbdocfun.hxx" 68 #include "dpoutput.hxx" 69 #include "dptabsrc.hxx" 70 #include "editable.hxx" 71 #include "docpool.hxx" 72 #include "patattr.hxx" 73 #include "unonames.hxx" 74 #include "cell.hxx" 75 #include "userlist.hxx" 76 77 #include <hash_set> 78 #include <hash_map> 79 #include <memory> 80 #include <list> 81 #include <vector> 82 83 using namespace com::sun::star; 84 using ::com::sun::star::uno::Any; 85 using ::com::sun::star::uno::Sequence; 86 using ::com::sun::star::uno::Reference; 87 using ::com::sun::star::uno::UNO_QUERY; 88 using ::com::sun::star::beans::XPropertySet; 89 using ::com::sun::star::container::XNameAccess; 90 using ::com::sun::star::sheet::XDimensionsSupplier; 91 using ::rtl::OUString; 92 using ::rtl::OUStringHash; 93 using ::rtl::OUStringBuffer; 94 using ::std::auto_ptr; 95 using ::std::list; 96 using ::std::vector; 97 using ::std::hash_map; 98 using ::std::hash_set; 99 100 // STATIC DATA ----------------------------------------------------------- 101 102 103 //================================================================== 104 105 // 106 // Outliner 107 // 108 109 // Outline-Gruppierung erzeugen 110 111 void ScDBFunc::MakeOutline( sal_Bool bColumns, sal_Bool bRecord ) 112 { 113 ScRange aRange; 114 if (GetViewData()->GetSimpleArea(aRange) == SC_MARK_SIMPLE) 115 { 116 ScDocShell* pDocSh = GetViewData()->GetDocShell(); 117 ScOutlineDocFunc aFunc(*pDocSh); 118 aFunc.MakeOutline( aRange, bColumns, bRecord, sal_False ); 119 } 120 else 121 ErrorMessage(STR_NOMULTISELECT); 122 } 123 124 // Outline-Gruppierung loeschen 125 126 void ScDBFunc::RemoveOutline( sal_Bool bColumns, sal_Bool bRecord ) 127 { 128 ScRange aRange; 129 if (GetViewData()->GetSimpleArea(aRange) == SC_MARK_SIMPLE) 130 { 131 ScDocShell* pDocSh = GetViewData()->GetDocShell(); 132 ScOutlineDocFunc aFunc(*pDocSh); 133 aFunc.RemoveOutline( aRange, bColumns, bRecord, sal_False ); 134 } 135 else 136 ErrorMessage(STR_NOMULTISELECT); 137 } 138 139 // Menue-Status: Outlines loeschen 140 141 void ScDBFunc::TestRemoveOutline( sal_Bool& rCol, sal_Bool& rRow ) 142 { 143 sal_Bool bColFound = sal_False; 144 sal_Bool bRowFound = sal_False; 145 146 SCCOL nStartCol, nEndCol; 147 SCROW nStartRow, nEndRow; 148 SCTAB nStartTab, nEndTab; 149 if (GetViewData()->GetSimpleArea(nStartCol,nStartRow,nStartTab,nEndCol,nEndRow,nEndTab) == SC_MARK_SIMPLE) 150 { 151 SCTAB nTab = nStartTab; 152 ScDocument* pDoc = GetViewData()->GetDocument(); 153 ScOutlineTable* pTable = pDoc->GetOutlineTable( nTab ); 154 if (pTable) 155 { 156 ScOutlineArray* pArray; 157 ScOutlineEntry* pEntry; 158 SCCOLROW nStart; 159 SCCOLROW nEnd; 160 sal_Bool bColMarked = ( nStartRow == 0 && nEndRow == MAXROW ); 161 sal_Bool bRowMarked = ( nStartCol == 0 && nEndCol == MAXCOL ); 162 163 // Spalten 164 165 if ( !bRowMarked || bColMarked ) // nicht wenn ganze Zeilen markiert 166 { 167 pArray = pTable->GetColArray(); 168 ScSubOutlineIterator aColIter( pArray ); 169 while ((pEntry=aColIter.GetNext()) != NULL && !bColFound) 170 { 171 nStart = pEntry->GetStart(); 172 nEnd = pEntry->GetEnd(); 173 if ( nStartCol<=static_cast<SCCOL>(nEnd) && nEndCol>=static_cast<SCCOL>(nStart) ) 174 bColFound = sal_True; 175 } 176 } 177 178 // Zeilen 179 180 if ( !bColMarked || bRowMarked ) // nicht wenn ganze Spalten markiert 181 { 182 pArray = pTable->GetRowArray(); 183 ScSubOutlineIterator aRowIter( pArray ); 184 while ((pEntry=aRowIter.GetNext()) != NULL && !bRowFound) 185 { 186 nStart = pEntry->GetStart(); 187 nEnd = pEntry->GetEnd(); 188 if ( nStartRow<=nEnd && nEndRow>=nStart ) 189 bRowFound = sal_True; 190 } 191 } 192 } 193 } 194 195 rCol = bColFound; 196 rRow = bRowFound; 197 } 198 199 void ScDBFunc::RemoveAllOutlines( sal_Bool bRecord ) 200 { 201 SCTAB nTab = GetViewData()->GetTabNo(); 202 ScDocShell* pDocSh = GetViewData()->GetDocShell(); 203 ScOutlineDocFunc aFunc(*pDocSh); 204 205 HideCursor(); 206 sal_Bool bOk = aFunc.RemoveAllOutlines( nTab, bRecord, sal_False ); 207 ShowCursor(); 208 209 if (bOk) 210 UpdateScrollBars(); 211 } 212 213 // Auto-Outlines 214 215 void ScDBFunc::AutoOutline( sal_Bool bRecord ) 216 { 217 SCTAB nTab = GetViewData()->GetTabNo(); 218 ScRange aRange( 0,0,nTab, MAXCOL,MAXROW,nTab ); // ganze Tabelle, wenn nichts markiert 219 ScMarkData& rMark = GetViewData()->GetMarkData(); 220 if ( rMark.IsMarked() || rMark.IsMultiMarked() ) 221 { 222 rMark.MarkToMulti(); 223 rMark.GetMultiMarkArea( aRange ); 224 } 225 226 ScDocShell* pDocSh = GetViewData()->GetDocShell(); 227 ScOutlineDocFunc aFunc(*pDocSh); 228 aFunc.AutoOutline( aRange, bRecord, sal_False ); 229 } 230 231 // Outline-Ebene auswaehlen 232 233 void ScDBFunc::SelectLevel( sal_Bool bColumns, sal_uInt16 nLevel, sal_Bool bRecord, sal_Bool bPaint ) 234 { 235 SCTAB nTab = GetViewData()->GetTabNo(); 236 ScDocShell* pDocSh = GetViewData()->GetDocShell(); 237 ScOutlineDocFunc aFunc(*pDocSh); 238 239 HideCursor(); 240 sal_Bool bOk = aFunc.SelectLevel( nTab, bColumns, nLevel, bRecord, bPaint, sal_False ); 241 ShowCursor(); 242 243 if (bOk) 244 UpdateScrollBars(); 245 } 246 247 // einzelne Outline-Gruppe einblenden 248 249 void ScDBFunc::ShowOutline( sal_Bool bColumns, sal_uInt16 nLevel, sal_uInt16 nEntry, sal_Bool bRecord, sal_Bool bPaint ) 250 { 251 SCTAB nTab = GetViewData()->GetTabNo(); 252 ScDocShell* pDocSh = GetViewData()->GetDocShell(); 253 ScOutlineDocFunc aFunc(*pDocSh); 254 255 HideCursor(); 256 sal_Bool bOk = aFunc.ShowOutline( nTab, bColumns, nLevel, nEntry, bRecord, bPaint, sal_False ); 257 ShowCursor(); 258 259 if ( bOk && bPaint ) 260 UpdateScrollBars(); 261 } 262 263 // einzelne Outline-Gruppe ausblenden 264 265 void ScDBFunc::HideOutline( sal_Bool bColumns, sal_uInt16 nLevel, sal_uInt16 nEntry, sal_Bool bRecord, sal_Bool bPaint ) 266 { 267 SCTAB nTab = GetViewData()->GetTabNo(); 268 ScDocShell* pDocSh = GetViewData()->GetDocShell(); 269 ScOutlineDocFunc aFunc(*pDocSh); 270 271 HideCursor(); 272 sal_Bool bOk = aFunc.HideOutline( nTab, bColumns, nLevel, nEntry, bRecord, bPaint, sal_False ); 273 ShowCursor(); 274 275 if ( bOk && bPaint ) 276 UpdateScrollBars(); 277 } 278 279 // Menue-Status: markierten Bereich ein-/ausblenden 280 281 sal_Bool ScDBFunc::OutlinePossible(sal_Bool bHide) 282 { 283 sal_Bool bEnable = sal_False; 284 285 SCCOL nStartCol; 286 SCROW nStartRow; 287 SCTAB nStartTab; 288 SCCOL nEndCol; 289 SCROW nEndRow; 290 SCTAB nEndTab; 291 292 if (GetViewData()->GetSimpleArea(nStartCol,nStartRow,nStartTab,nEndCol,nEndRow,nEndTab) == SC_MARK_SIMPLE) 293 { 294 ScDocument* pDoc = GetViewData()->GetDocument(); 295 SCTAB nTab = GetViewData()->GetTabNo(); 296 ScOutlineTable* pTable = pDoc->GetOutlineTable( nTab ); 297 if (pTable) 298 { 299 ScOutlineArray* pArray; 300 ScOutlineEntry* pEntry; 301 SCCOLROW nStart; 302 SCCOLROW nEnd; 303 304 // Spalten 305 306 pArray = pTable->GetColArray(); 307 ScSubOutlineIterator aColIter( pArray ); 308 while ((pEntry=aColIter.GetNext()) != NULL && !bEnable) 309 { 310 nStart = pEntry->GetStart(); 311 nEnd = pEntry->GetEnd(); 312 if ( bHide ) 313 { 314 if ( nStartCol<=static_cast<SCCOL>(nEnd) && nEndCol>=static_cast<SCCOL>(nStart) ) 315 if (!pEntry->IsHidden()) 316 bEnable = sal_True; 317 } 318 else 319 { 320 if ( nStart>=nStartCol && nEnd<=nEndCol ) 321 if (pEntry->IsHidden()) 322 bEnable = sal_True; 323 } 324 } 325 326 // Zeilen 327 328 pArray = pTable->GetRowArray(); 329 ScSubOutlineIterator aRowIter( pArray ); 330 while ((pEntry=aRowIter.GetNext()) != NULL) 331 { 332 nStart = pEntry->GetStart(); 333 nEnd = pEntry->GetEnd(); 334 if ( bHide ) 335 { 336 if ( nStartRow<=nEnd && nEndRow>=nStart ) 337 if (!pEntry->IsHidden()) 338 bEnable = sal_True; 339 } 340 else 341 { 342 if ( nStart>=nStartRow && nEnd<=nEndRow ) 343 if (pEntry->IsHidden()) 344 bEnable = sal_True; 345 } 346 } 347 } 348 } 349 350 return bEnable; 351 } 352 353 // markierten Bereich einblenden 354 355 void ScDBFunc::ShowMarkedOutlines( sal_Bool bRecord ) 356 { 357 ScRange aRange; 358 if (GetViewData()->GetSimpleArea(aRange) == SC_MARK_SIMPLE) 359 { 360 ScDocShell* pDocSh = GetViewData()->GetDocShell(); 361 ScOutlineDocFunc aFunc(*pDocSh); 362 HideCursor(); 363 sal_Bool bDone = aFunc.ShowMarkedOutlines( aRange, bRecord, sal_False ); 364 ShowCursor(); 365 if (bDone) 366 UpdateScrollBars(); 367 } 368 else 369 ErrorMessage(STR_NOMULTISELECT); 370 } 371 372 // markierten Bereich ausblenden 373 374 void ScDBFunc::HideMarkedOutlines( sal_Bool bRecord ) 375 { 376 ScRange aRange; 377 if (GetViewData()->GetSimpleArea(aRange) == SC_MARK_SIMPLE) 378 { 379 ScDocShell* pDocSh = GetViewData()->GetDocShell(); 380 ScOutlineDocFunc aFunc(*pDocSh); 381 HideCursor(); 382 sal_Bool bDone = aFunc.HideMarkedOutlines( aRange, bRecord, sal_False ); 383 ShowCursor(); 384 if (bDone) 385 UpdateScrollBars(); 386 } 387 else 388 ErrorMessage(STR_NOMULTISELECT); 389 } 390 391 // -------------------------------------------------------------------------- 392 393 // 394 // Teilergebnisse 395 // 396 397 void ScDBFunc::DoSubTotals( const ScSubTotalParam& rParam, sal_Bool bRecord, 398 const ScSortParam* pForceNewSort ) 399 { 400 sal_Bool bDo = !rParam.bRemoveOnly; // sal_False = nur loeschen 401 402 ScDocShell* pDocSh = GetViewData()->GetDocShell(); 403 ScDocument* pDoc = pDocSh->GetDocument(); 404 ScMarkData& rMark = GetViewData()->GetMarkData(); 405 SCTAB nTab = GetViewData()->GetTabNo(); 406 if (bRecord && !pDoc->IsUndoEnabled()) 407 bRecord = sal_False; 408 409 ScDBData* pDBData = pDoc->GetDBAtArea( nTab, rParam.nCol1, rParam.nRow1, 410 rParam.nCol2, rParam.nRow2 ); 411 if (!pDBData) 412 { 413 DBG_ERROR( "SubTotals: keine DBData" ); 414 return; 415 } 416 417 ScEditableTester aTester( pDoc, nTab, 0,rParam.nRow1+1, MAXCOL,MAXROW ); 418 if (!aTester.IsEditable()) 419 { 420 ErrorMessage(aTester.GetMessageId()); 421 return; 422 } 423 424 if (pDoc->HasAttrib( rParam.nCol1, rParam.nRow1+1, nTab, 425 rParam.nCol2, rParam.nRow2, nTab, HASATTR_MERGED | HASATTR_OVERLAPPED )) 426 { 427 ErrorMessage(STR_MSSG_INSERTCELLS_0); // nicht in zusammengefasste einfuegen 428 return; 429 } 430 431 WaitObject aWait( GetViewData()->GetDialogParent() ); 432 sal_Bool bOk = sal_True; 433 sal_Bool bDelete = sal_False; 434 if (rParam.bReplace) 435 if (pDoc->TestRemoveSubTotals( nTab, rParam )) 436 { 437 bDelete = sal_True; 438 bOk = ( MessBox( GetViewData()->GetDialogParent(), WinBits(WB_YES_NO | WB_DEF_YES), 439 // "StarCalc" "Daten loeschen?" 440 ScGlobal::GetRscString( STR_MSSG_DOSUBTOTALS_0 ), 441 ScGlobal::GetRscString( STR_MSSG_DOSUBTOTALS_1 ) ).Execute() 442 == RET_YES ); 443 } 444 445 if (bOk) 446 { 447 ScDocShellModificator aModificator( *pDocSh ); 448 449 ScSubTotalParam aNewParam( rParam ); // Bereichsende wird veraendert 450 ScDocument* pUndoDoc = NULL; 451 ScOutlineTable* pUndoTab = NULL; 452 ScRangeName* pUndoRange = NULL; 453 ScDBCollection* pUndoDB = NULL; 454 SCTAB nTabCount = 0; // fuer Referenz-Undo 455 456 if (bRecord) // alte Daten sichern 457 { 458 sal_Bool bOldFilter = bDo && rParam.bDoSort; 459 460 nTabCount = pDoc->GetTableCount(); 461 pUndoDoc = new ScDocument( SCDOCMODE_UNDO ); 462 ScOutlineTable* pTable = pDoc->GetOutlineTable( nTab ); 463 if (pTable) 464 { 465 pUndoTab = new ScOutlineTable( *pTable ); 466 467 SCCOLROW nOutStartCol; // Zeilen/Spaltenstatus 468 SCCOLROW nOutStartRow; 469 SCCOLROW nOutEndCol; 470 SCCOLROW nOutEndRow; 471 pTable->GetColArray()->GetRange( nOutStartCol, nOutEndCol ); 472 pTable->GetRowArray()->GetRange( nOutStartRow, nOutEndRow ); 473 474 pUndoDoc->InitUndo( pDoc, nTab, nTab, sal_True, sal_True ); 475 pDoc->CopyToDocument( static_cast<SCCOL>(nOutStartCol), 0, nTab, static_cast<SCCOL>(nOutEndCol), MAXROW, nTab, IDF_NONE, sal_False, pUndoDoc ); 476 pDoc->CopyToDocument( 0, nOutStartRow, nTab, MAXCOL, nOutEndRow, nTab, IDF_NONE, sal_False, pUndoDoc ); 477 } 478 else 479 pUndoDoc->InitUndo( pDoc, nTab, nTab, sal_False, bOldFilter ); 480 481 // Datenbereich sichern - incl. Filter-Ergebnis 482 pDoc->CopyToDocument( 0,rParam.nRow1+1,nTab, MAXCOL,rParam.nRow2,nTab, 483 IDF_ALL, sal_False, pUndoDoc ); 484 485 // alle Formeln wegen Referenzen 486 pDoc->CopyToDocument( 0,0,0, MAXCOL,MAXROW,nTabCount-1, 487 IDF_FORMULA, sal_False, pUndoDoc ); 488 489 // DB- und andere Bereiche 490 ScRangeName* pDocRange = pDoc->GetRangeName(); 491 if (pDocRange->GetCount()) 492 pUndoRange = new ScRangeName( *pDocRange ); 493 ScDBCollection* pDocDB = pDoc->GetDBCollection(); 494 if (pDocDB->GetCount()) 495 pUndoDB = new ScDBCollection( *pDocDB ); 496 } 497 498 // pDoc->SetOutlineTable( nTab, NULL ); 499 ScOutlineTable* pOut = pDoc->GetOutlineTable( nTab ); 500 if (pOut) 501 pOut->GetRowArray()->RemoveAll(); // nur Zeilen-Outlines loeschen 502 503 if (rParam.bReplace) 504 pDoc->RemoveSubTotals( nTab, aNewParam ); 505 sal_Bool bSuccess = sal_True; 506 if (bDo) 507 { 508 // Sortieren 509 if ( rParam.bDoSort || pForceNewSort ) 510 { 511 pDBData->SetArea( nTab, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 ); 512 513 // Teilergebnis-Felder vor die Sortierung setzen 514 // (doppelte werden weggelassen, kann darum auch wieder aufgerufen werden) 515 516 ScSortParam aOldSort; 517 pDBData->GetSortParam( aOldSort ); 518 ScSortParam aSortParam( aNewParam, pForceNewSort ? *pForceNewSort : aOldSort ); 519 Sort( aSortParam, sal_False, sal_False ); 520 } 521 522 bSuccess = pDoc->DoSubTotals( nTab, aNewParam ); 523 } 524 ScRange aDirtyRange( aNewParam.nCol1, aNewParam.nRow1, nTab, 525 aNewParam.nCol2, aNewParam.nRow2, nTab ); 526 pDoc->SetDirty( aDirtyRange ); 527 528 if (bRecord) 529 { 530 // ScDBData* pUndoDBData = pDBData ? new ScDBData( *pDBData ) : NULL; 531 pDocSh->GetUndoManager()->AddUndoAction( 532 new ScUndoSubTotals( pDocSh, nTab, 533 rParam, aNewParam.nRow2, 534 pUndoDoc, pUndoTab, // pUndoDBData, 535 pUndoRange, pUndoDB ) ); 536 } 537 538 if (!bSuccess) 539 { 540 // "Kann keine Zeilen einfuegen" 541 ErrorMessage(STR_MSSG_DOSUBTOTALS_2); 542 } 543 544 // merken 545 pDBData->SetSubTotalParam( aNewParam ); 546 pDBData->SetArea( nTab, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 ); 547 pDoc->CompileDBFormula(); 548 549 DoneBlockMode(); 550 InitOwnBlockMode(); 551 rMark.SetMarkArea( ScRange( aNewParam.nCol1,aNewParam.nRow1,nTab, 552 aNewParam.nCol2,aNewParam.nRow2,nTab ) ); 553 MarkDataChanged(); 554 555 pDocSh->PostPaint( 0,0,nTab, MAXCOL,MAXROW,nTab, 556 PAINT_GRID | PAINT_LEFT | PAINT_TOP | PAINT_SIZE ); 557 558 aModificator.SetDocumentModified(); 559 560 SelectionChanged(); 561 } 562 } 563 564 // 565 // Consolidate 566 // 567 568 void ScDBFunc::Consolidate( const ScConsolidateParam& rParam, sal_Bool bRecord ) 569 { 570 ScDocShell* pDocShell = GetViewData()->GetDocShell(); 571 pDocShell->DoConsolidate( rParam, bRecord ); 572 SetTabNo( rParam.nTab, sal_True ); 573 } 574 575 // 576 // Pivot 577 // 578 579 String lcl_MakePivotTabName( const String& rPrefix, SCTAB nNumber ) 580 { 581 String aName = rPrefix; 582 aName += String::CreateFromInt32( nNumber ); 583 return aName; 584 } 585 586 bool ScDBFunc::MakePivotTable( const ScDPSaveData& rData, const ScRange& rDest, sal_Bool bNewTable, 587 const ScDPObject& rSource, sal_Bool bApi ) 588 { 589 // #70096# error message if no fields are set 590 // this must be removed when drag&drop of fields from a toolbox is available 591 592 if ( rData.IsEmpty() && !bApi ) 593 { 594 ErrorMessage(STR_PIVOT_NODATA); 595 return false; 596 } 597 598 ScDocShell* pDocSh = GetViewData()->GetDocShell(); 599 ScDocument* pDoc = GetViewData()->GetDocument(); 600 sal_Bool bUndo(pDoc->IsUndoEnabled()); 601 602 ScRange aDestRange = rDest; 603 if ( bNewTable ) 604 { 605 SCTAB nSrcTab = GetViewData()->GetTabNo(); 606 607 String aName( ScGlobal::GetRscString(STR_PIVOT_TABLE) ); 608 String aStr; 609 610 pDoc->GetName( nSrcTab, aStr ); 611 aName += '_'; 612 aName += aStr; 613 aName += '_'; 614 615 SCTAB nNewTab = nSrcTab+1; 616 617 SCTAB i=1; 618 while ( !pDoc->InsertTab( nNewTab, lcl_MakePivotTabName( aName, i ) ) && i <= MAXTAB ) 619 i++; 620 621 sal_Bool bAppend = ( nNewTab+1 == pDoc->GetTableCount() ); 622 if (bUndo) 623 { 624 pDocSh->GetUndoManager()->AddUndoAction( 625 new ScUndoInsertTab( pDocSh, nNewTab, bAppend, lcl_MakePivotTabName( aName, i ) )); 626 } 627 628 GetViewData()->InsertTab( nNewTab ); 629 SetTabNo( nNewTab, sal_True ); 630 631 aDestRange = ScRange( 0, 0, nNewTab ); 632 } 633 634 ScDPObject* pDPObj = pDoc->GetDPAtCursor( 635 aDestRange.aStart.Col(), aDestRange.aStart.Row(), aDestRange.aStart.Tab() ); 636 637 ScDPObject aObj( rSource ); 638 aObj.SetOutRange( aDestRange ); 639 if ( pDPObj && !rData.GetExistingDimensionData() ) 640 { 641 // copy dimension data from old object - lost in the dialog 642 //! change the dialog to keep the dimension data 643 644 ScDPSaveData aNewData( rData ); 645 const ScDPSaveData* pOldData = pDPObj->GetSaveData(); 646 if ( pOldData ) 647 { 648 const ScDPDimensionSaveData* pDimSave = pOldData->GetExistingDimensionData(); 649 aNewData.SetDimensionData( pDimSave ); 650 } 651 aObj.SetSaveData( aNewData ); 652 } 653 else 654 aObj.SetSaveData( rData ); 655 656 sal_Bool bAllowMove = ( pDPObj != NULL ); // allow re-positioning when editing existing table 657 658 ScDBDocFunc aFunc( *pDocSh ); 659 bool bSuccess = aFunc.DataPilotUpdate( pDPObj, &aObj, sal_True, sal_False, bAllowMove ); 660 661 CursorPosChanged(); // shells may be switched 662 663 if ( bNewTable ) 664 { 665 pDocSh->PostPaintExtras(); 666 SFX_APP()->Broadcast( SfxSimpleHint( SC_HINT_TABLES_CHANGED ) ); 667 } 668 669 return bSuccess; 670 } 671 672 void ScDBFunc::DeletePivotTable() 673 { 674 ScDocShell* pDocSh = GetViewData()->GetDocShell(); 675 ScDocument* pDoc = pDocSh->GetDocument(); 676 ScDPObject* pDPObj = pDoc->GetDPAtCursor( GetViewData()->GetCurX(), 677 GetViewData()->GetCurY(), 678 GetViewData()->GetTabNo() ); 679 if ( pDPObj ) 680 { 681 ScDBDocFunc aFunc( *pDocSh ); 682 aFunc.DataPilotUpdate( pDPObj, NULL, sal_True, sal_False ); 683 CursorPosChanged(); // shells may be switched 684 } 685 else 686 ErrorMessage(STR_PIVOT_NOTFOUND); 687 } 688 sal_uLong RefreshDPObject( ScDPObject *pDPObj, ScDocument *pDoc, ScDocShell *pDocSh, sal_Bool bRecord, sal_Bool bApi ) 689 { 690 if( !pDPObj ) 691 return STR_PIVOT_NOTFOUND; 692 693 if ( pDocSh && !pDoc ) 694 pDoc = pDocSh->GetDocument(); 695 696 if( !pDoc ) 697 return static_cast<sal_uLong>(-1); 698 699 if( !pDocSh && ( pDocSh = PTR_CAST( ScDocShell, pDoc->GetDocumentShell() ) ) == NULL ) 700 return static_cast<sal_uLong>(-1); 701 702 if( sal_uLong nErrId = pDPObj->RefreshCache() ) 703 return nErrId; 704 else if ( nErrId == 0 ) 705 { 706 //Refresh all dpobjects 707 ScDPCollection* pDPCollection = pDoc->GetDPCollection(); 708 sal_uInt16 nCount = pDPCollection->GetCount(); 709 for (sal_uInt16 i=0; i<nCount; i++) 710 { 711 if ( (*pDPCollection)[i]->GetCacheId() == pDPObj->GetCacheId() ) 712 { 713 ScDBDocFunc aFunc( * pDocSh ); 714 if ( !aFunc.DataPilotUpdate( (*pDPCollection)[i], (*pDPCollection)[i], bRecord, bApi ) ) 715 break; 716 } 717 } 718 719 return nErrId; 720 } 721 722 return 0U; 723 } 724 725 sal_uLong ScDBFunc::RecalcPivotTable() 726 { 727 ScDocShell* pDocSh = GetViewData()->GetDocShell(); 728 ScDocument* pDoc = GetViewData()->GetDocument(); 729 730 // old pivot not used any more 731 732 ScDPObject* pDPObj = pDoc->GetDPAtCursor( GetViewData()->GetCurX(), 733 GetViewData()->GetCurY(), 734 GetViewData()->GetTabNo() ); 735 if ( pDPObj ) 736 { 737 // Wang Xu Ming -- 2009-6-17 738 // DataPilot Migration 739 //ScDBDocFunc aFunc( *pDocSh ); 740 //aFunc.DataPilotUpdate( pDPObj, pDPObj, sal_True, sal_False ); 741 //CursorPosChanged(); // shells may be switched 742 sal_uLong nErrId = RefreshDPObject( pDPObj, pDoc, pDocSh, sal_True, sal_False );//pDPObj->RefreshCache(); 743 if ( nErrId == 0 ) 744 { 745 // There is no undo for the refresh of the cache table, but the undo history for cell changes 746 // remains valid and should be preserved, so the history isn't cleared here. 747 //GetViewData()->GetDocShell()->GetUndoManager()->Clear(); 748 } 749 else if (nErrId <= USHRT_MAX) 750 ErrorMessage(static_cast<sal_uInt16>(nErrId)); 751 return nErrId; 752 // End Comments 753 } 754 else 755 ErrorMessage(STR_PIVOT_NOTFOUND); 756 return STR_PIVOT_NOTFOUND; 757 } 758 759 void ScDBFunc::GetSelectedMemberList( ScStrCollection& rEntries, long& rDimension ) 760 { 761 ScDPObject* pDPObj = GetViewData()->GetDocument()->GetDPAtCursor( GetViewData()->GetCurX(), 762 GetViewData()->GetCurY(), GetViewData()->GetTabNo() ); 763 if ( !pDPObj ) 764 return; 765 766 long nStartDimension = -1; 767 long nStartHierarchy = -1; 768 long nStartLevel = -1; 769 770 ScRangeListRef xRanges; 771 GetViewData()->GetMultiArea( xRanges ); // incl. cursor if nothing is selected 772 sal_uLong nRangeCount = xRanges->Count(); 773 sal_Bool bContinue = sal_True; 774 775 for (sal_uLong nRangePos=0; nRangePos<nRangeCount && bContinue; nRangePos++) 776 { 777 ScRange aRange = *xRanges->GetObject(nRangePos); 778 SCCOL nStartCol = aRange.aStart.Col(); 779 SCROW nStartRow = aRange.aStart.Row(); 780 SCCOL nEndCol = aRange.aEnd.Col(); 781 SCROW nEndRow = aRange.aEnd.Row(); 782 SCTAB nTab = aRange.aStart.Tab(); 783 784 for (SCROW nRow=nStartRow; nRow<=nEndRow && bContinue; nRow++) 785 for (SCCOL nCol=nStartCol; nCol<=nEndCol && bContinue; nCol++) 786 { 787 sheet::DataPilotTableHeaderData aData; 788 pDPObj->GetHeaderPositionData(ScAddress(nCol, nRow, nTab), aData); 789 if ( aData.Dimension < 0 ) 790 bContinue = sal_False; // not part of any dimension 791 else 792 { 793 if ( nStartDimension < 0 ) // first member? 794 { 795 nStartDimension = aData.Dimension; 796 nStartHierarchy = aData.Hierarchy; 797 nStartLevel = aData.Level; 798 } 799 if ( aData.Dimension != nStartDimension || 800 aData.Hierarchy != nStartHierarchy || 801 aData.Level != nStartLevel ) 802 { 803 bContinue = sal_False; // cannot mix dimensions 804 } 805 } 806 if ( bContinue ) 807 { 808 // accept any part of a member description, also subtotals, 809 // but don't stop if empty parts are contained 810 if ( aData.Flags & sheet::MemberResultFlags::HASMEMBER ) 811 { 812 StrData* pNew = new StrData( aData.MemberName ); 813 if ( !rEntries.Insert( pNew ) ) 814 delete pNew; 815 } 816 } 817 } 818 } 819 820 rDimension = nStartDimension; // dimension from which the found members came 821 if (!bContinue) 822 rEntries.FreeAll(); // remove all if not valid 823 } 824 825 sal_Bool ScDBFunc::HasSelectionForDateGroup( ScDPNumGroupInfo& rOldInfo, sal_Int32& rParts ) 826 { 827 // determine if the date group dialog has to be shown for the current selection 828 829 sal_Bool bFound = sal_False; 830 831 SCCOL nCurX = GetViewData()->GetCurX(); 832 SCROW nCurY = GetViewData()->GetCurY(); 833 SCTAB nTab = GetViewData()->GetTabNo(); 834 ScDocument* pDoc = GetViewData()->GetDocument(); 835 836 ScDPObject* pDPObj = pDoc->GetDPAtCursor( nCurX, nCurY, nTab ); 837 if ( pDPObj ) 838 { 839 ScStrCollection aEntries; 840 long nSelectDimension = -1; 841 GetSelectedMemberList( aEntries, nSelectDimension ); 842 843 if ( aEntries.GetCount() > 0 ) 844 { 845 sal_Bool bIsDataLayout; 846 String aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout ); 847 String aBaseDimName( aDimName ); 848 849 sal_Bool bInGroupDim = sal_False; 850 sal_Bool bFoundParts = sal_False; 851 852 ScDPDimensionSaveData* pDimData = 853 const_cast<ScDPDimensionSaveData*>( pDPObj->GetSaveData()->GetExistingDimensionData() ); 854 if ( pDimData ) 855 { 856 const ScDPSaveNumGroupDimension* pNumGroupDim = pDimData->GetNumGroupDim( aDimName ); 857 const ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDim( aDimName ); 858 if ( pNumGroupDim ) 859 { 860 // existing num group dimension 861 862 if ( pNumGroupDim->GetDatePart() != 0 ) 863 { 864 // dimension has date info -> edit settings of this dimension 865 // (parts are collected below) 866 867 rOldInfo = pNumGroupDim->GetDateInfo(); 868 bFound = sal_True; 869 } 870 else if ( pNumGroupDim->GetInfo().DateValues ) 871 { 872 // Numerical grouping with DateValues flag is used for grouping 873 // of days with a "Number of days" value. 874 875 rOldInfo = pNumGroupDim->GetInfo(); 876 rParts = com::sun::star::sheet::DataPilotFieldGroupBy::DAYS; // not found in CollectDateParts 877 bFoundParts = sal_True; 878 bFound = sal_True; 879 } 880 bInGroupDim = sal_True; 881 } 882 else if ( pGroupDim ) 883 { 884 // existing additional group dimension 885 886 if ( pGroupDim->GetDatePart() != 0 ) 887 { 888 // dimension has date info -> edit settings of this dimension 889 // (parts are collected below) 890 891 rOldInfo = pGroupDim->GetDateInfo(); 892 aBaseDimName = pGroupDim->GetSourceDimName(); 893 bFound = sal_True; 894 } 895 bInGroupDim = sal_True; 896 } 897 } 898 if ( bFound && !bFoundParts ) 899 { 900 // collect date parts from all group dimensions 901 rParts = pDimData->CollectDateParts( aBaseDimName ); 902 } 903 if ( !bFound && !bInGroupDim ) 904 { 905 // create new date group dimensions if the selection is a single cell 906 // in a normal dimension with date content 907 908 ScRange aSelRange; 909 if ( (GetViewData()->GetSimpleArea( aSelRange ) == SC_MARK_SIMPLE) && 910 aSelRange.aStart == aSelRange.aEnd ) 911 { 912 SCCOL nSelCol = aSelRange.aStart.Col(); 913 SCROW nSelRow = aSelRange.aStart.Row(); 914 SCTAB nSelTab = aSelRange.aStart.Tab(); 915 if ( pDoc->HasValueData( nSelCol, nSelRow, nSelTab ) ) 916 { 917 sal_uLong nIndex = static_cast<const SfxUInt32Item*>(pDoc->GetAttr( 918 nSelCol, nSelRow, nSelTab, ATTR_VALUE_FORMAT))->GetValue(); 919 short nType = pDoc->GetFormatTable()->GetType(nIndex); 920 if ( nType == NUMBERFORMAT_DATE || nType == NUMBERFORMAT_TIME || nType == NUMBERFORMAT_DATETIME ) 921 { 922 bFound = sal_True; 923 // use currently selected value for automatic limits 924 if( rOldInfo.AutoStart ) 925 rOldInfo.Start = pDoc->GetValue( aSelRange.aStart ); 926 if( rOldInfo.AutoEnd ) 927 rOldInfo.End = pDoc->GetValue( aSelRange.aStart ); 928 } 929 } 930 } 931 } 932 } 933 } 934 935 return bFound; 936 } 937 938 sal_Bool ScDBFunc::HasSelectionForNumGroup( ScDPNumGroupInfo& rOldInfo ) 939 { 940 // determine if the numeric group dialog has to be shown for the current selection 941 942 sal_Bool bFound = sal_False; 943 944 SCCOL nCurX = GetViewData()->GetCurX(); 945 SCROW nCurY = GetViewData()->GetCurY(); 946 SCTAB nTab = GetViewData()->GetTabNo(); 947 ScDocument* pDoc = GetViewData()->GetDocument(); 948 949 ScDPObject* pDPObj = pDoc->GetDPAtCursor( nCurX, nCurY, nTab ); 950 if ( pDPObj ) 951 { 952 ScStrCollection aEntries; 953 long nSelectDimension = -1; 954 GetSelectedMemberList( aEntries, nSelectDimension ); 955 956 if ( aEntries.GetCount() > 0 ) 957 { 958 sal_Bool bIsDataLayout; 959 String aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout ); 960 961 sal_Bool bInGroupDim = sal_False; 962 963 ScDPDimensionSaveData* pDimData = 964 const_cast<ScDPDimensionSaveData*>( pDPObj->GetSaveData()->GetExistingDimensionData() ); 965 if ( pDimData ) 966 { 967 const ScDPSaveNumGroupDimension* pNumGroupDim = pDimData->GetNumGroupDim( aDimName ); 968 if ( pNumGroupDim ) 969 { 970 // existing num group dimension 971 // -> edit settings of this dimension 972 973 rOldInfo = pNumGroupDim->GetInfo(); 974 bFound = sal_True; 975 } 976 else if ( pDimData->GetNamedGroupDim( aDimName ) ) 977 bInGroupDim = sal_True; // in a group dimension 978 } 979 if ( !bFound && !bInGroupDim ) 980 { 981 // create a new num group dimension if the selection is a single cell 982 // in a normal dimension with numeric content 983 984 ScRange aSelRange; 985 if ( (GetViewData()->GetSimpleArea( aSelRange ) == SC_MARK_SIMPLE) && 986 aSelRange.aStart == aSelRange.aEnd ) 987 { 988 if ( pDoc->HasValueData( aSelRange.aStart.Col(), aSelRange.aStart.Row(), 989 aSelRange.aStart.Tab() ) ) 990 { 991 bFound = sal_True; 992 // use currently selected value for automatic limits 993 if( rOldInfo.AutoStart ) 994 rOldInfo.Start = pDoc->GetValue( aSelRange.aStart ); 995 if( rOldInfo.AutoEnd ) 996 rOldInfo.End = pDoc->GetValue( aSelRange.aStart ); 997 } 998 } 999 } 1000 } 1001 } 1002 1003 return bFound; 1004 } 1005 1006 void ScDBFunc::DateGroupDataPilot( const ScDPNumGroupInfo& rInfo, sal_Int32 nParts ) 1007 { 1008 ScDPObject* pDPObj = GetViewData()->GetDocument()->GetDPAtCursor( GetViewData()->GetCurX(), 1009 GetViewData()->GetCurY(), GetViewData()->GetTabNo() ); 1010 if ( pDPObj ) 1011 { 1012 ScStrCollection aEntries; 1013 long nSelectDimension = -1; 1014 GetSelectedMemberList( aEntries, nSelectDimension ); 1015 1016 if ( aEntries.GetCount() > 0 ) 1017 { 1018 sal_Bool bIsDataLayout; 1019 String aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout ); 1020 1021 ScDPSaveData aData( *pDPObj->GetSaveData() ); 1022 ScDPDimensionSaveData* pDimData = aData.GetDimensionData(); // created if not there 1023 1024 // find original base 1025 String aBaseDimName = aDimName; 1026 if( const ScDPSaveGroupDimension* pBaseGroupDim = pDimData->GetNamedGroupDim( aDimName ) ) 1027 aBaseDimName = pBaseGroupDim->GetSourceDimName(); 1028 1029 // remove all existing parts (the grouping is built completely new) 1030 1031 /* Remove numeric group dimension (exists once at most). No need 1032 to delete anything in save data (grouping was done inplace in 1033 an existing base dimension). */ 1034 pDimData->RemoveNumGroupDimension( aBaseDimName ); 1035 1036 /* Remove named group dimension(s). Collect deleted dimension 1037 names which may be reused while recreating the groups. 1038 Dimensions have to be removed from dimension save data and from 1039 save data too. */ 1040 std::vector< String > aDeletedNames; 1041 const ScDPSaveGroupDimension* pExistingGroup = pDimData->GetGroupDimForBase( aBaseDimName ); 1042 while ( pExistingGroup ) 1043 { 1044 String aGroupDimName = pExistingGroup->GetGroupDimName(); 1045 pDimData->RemoveGroupDimension( aGroupDimName ); // pExistingGroup is deleted 1046 1047 // also remove SaveData settings for the dimension that no longer exists 1048 aData.RemoveDimensionByName( aGroupDimName ); 1049 1050 /* The name can be used for the new group dimensions, although 1051 it is still in use with the DataPilotSource. */ 1052 aDeletedNames.push_back( aGroupDimName ); 1053 1054 // see if there are more group dimensions 1055 pExistingGroup = pDimData->GetGroupDimForBase( aBaseDimName ); 1056 1057 if ( pExistingGroup && pExistingGroup->GetGroupDimName() == aGroupDimName ) 1058 { 1059 // still get the same group dimension? 1060 DBG_ERROR("couldn't remove group dimension"); 1061 pExistingGroup = NULL; // avoid endless loop 1062 } 1063 } 1064 1065 if ( nParts ) 1066 { 1067 // create date group dimensions 1068 1069 ScDPNumGroupInfo aEmpty; 1070 bool bFirst = true; 1071 sal_Int32 nMask = 1; 1072 for (sal_uInt16 nBit=0; nBit<32; nBit++) 1073 { 1074 if ( nParts & nMask ) 1075 { 1076 if ( bFirst ) 1077 { 1078 // innermost part: create NumGroupDimension (replacing original values) 1079 // Dimension name is left unchanged 1080 1081 if ( (nParts == sheet::DataPilotFieldGroupBy::DAYS) && (rInfo.Step >= 1.0) ) 1082 { 1083 // only days, and a step value specified: use numerical grouping 1084 // with DateValues flag, not date grouping 1085 1086 ScDPNumGroupInfo aNumInfo( rInfo ); 1087 aNumInfo.DateValues = sal_True; 1088 1089 ScDPSaveNumGroupDimension aNumGroupDim( aBaseDimName, aNumInfo ); 1090 pDimData->AddNumGroupDimension( aNumGroupDim ); 1091 } 1092 else 1093 { 1094 ScDPSaveNumGroupDimension aNumGroupDim( aBaseDimName, rInfo, nMask ); 1095 pDimData->AddNumGroupDimension( aNumGroupDim ); 1096 } 1097 1098 bFirst = false; 1099 } 1100 else 1101 { 1102 // additional parts: create GroupDimension (shown as additional dimensions) 1103 String aGroupDimName = pDimData->CreateDateGroupDimName( nMask, *pDPObj, true, &aDeletedNames ); 1104 ScDPSaveGroupDimension aGroupDim( aBaseDimName, aGroupDimName ); 1105 aGroupDim.SetDateInfo( rInfo, nMask ); 1106 pDimData->AddGroupDimension( aGroupDim ); 1107 1108 // set orientation 1109 ScDPSaveDimension* pSaveDimension = aData.GetDimensionByName( aGroupDimName ); 1110 if ( pSaveDimension->GetOrientation() == sheet::DataPilotFieldOrientation_HIDDEN ) 1111 { 1112 ScDPSaveDimension* pOldDimension = aData.GetDimensionByName( aBaseDimName ); 1113 pSaveDimension->SetOrientation( pOldDimension->GetOrientation() ); 1114 long nPosition = 0; //! before (immediate) base 1115 aData.SetPosition( pSaveDimension, nPosition ); 1116 } 1117 } 1118 } 1119 nMask *= 2; 1120 } 1121 } 1122 1123 // apply changes 1124 ScDBDocFunc aFunc( *GetViewData()->GetDocShell() ); 1125 ScDPObject* pNewObj = new ScDPObject( *pDPObj ); 1126 pNewObj->SetSaveData( aData ); 1127 aFunc.DataPilotUpdate( pDPObj, pNewObj, sal_True, sal_False ); 1128 delete pNewObj; 1129 1130 // unmark cell selection 1131 Unmark(); 1132 } 1133 } 1134 } 1135 1136 void ScDBFunc::NumGroupDataPilot( const ScDPNumGroupInfo& rInfo ) 1137 { 1138 ScDPObject* pDPObj = GetViewData()->GetDocument()->GetDPAtCursor( GetViewData()->GetCurX(), 1139 GetViewData()->GetCurY(), GetViewData()->GetTabNo() ); 1140 if ( pDPObj ) 1141 { 1142 ScStrCollection aEntries; 1143 long nSelectDimension = -1; 1144 GetSelectedMemberList( aEntries, nSelectDimension ); 1145 1146 if ( aEntries.GetCount() > 0 ) 1147 { 1148 sal_Bool bIsDataLayout; 1149 String aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout ); 1150 1151 ScDPSaveData aData( *pDPObj->GetSaveData() ); 1152 ScDPDimensionSaveData* pDimData = aData.GetDimensionData(); // created if not there 1153 1154 ScDPSaveNumGroupDimension* pExisting = pDimData->GetNumGroupDimAcc( aDimName ); 1155 if ( pExisting ) 1156 { 1157 // modify existing group dimension 1158 pExisting->SetGroupInfo( rInfo ); 1159 } 1160 else 1161 { 1162 // create new group dimension 1163 ScDPSaveNumGroupDimension aNumGroupDim( aDimName, rInfo ); 1164 pDimData->AddNumGroupDimension( aNumGroupDim ); 1165 } 1166 1167 // apply changes 1168 ScDBDocFunc aFunc( *GetViewData()->GetDocShell() ); 1169 ScDPObject* pNewObj = new ScDPObject( *pDPObj ); 1170 pNewObj->SetSaveData( aData ); 1171 aFunc.DataPilotUpdate( pDPObj, pNewObj, sal_True, sal_False ); 1172 delete pNewObj; 1173 1174 // unmark cell selection 1175 Unmark(); 1176 } 1177 } 1178 } 1179 1180 void ScDBFunc::GroupDataPilot() 1181 { 1182 ScDPObject* pDPObj = GetViewData()->GetDocument()->GetDPAtCursor( GetViewData()->GetCurX(), 1183 GetViewData()->GetCurY(), GetViewData()->GetTabNo() ); 1184 if ( pDPObj ) 1185 { 1186 ScStrCollection aEntries; 1187 long nSelectDimension = -1; 1188 GetSelectedMemberList( aEntries, nSelectDimension ); 1189 1190 if ( aEntries.GetCount() > 0 ) 1191 { 1192 sal_Bool bIsDataLayout; 1193 String aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout ); 1194 1195 ScDPSaveData aData( *pDPObj->GetSaveData() ); 1196 ScDPDimensionSaveData* pDimData = aData.GetDimensionData(); // created if not there 1197 1198 // find original base 1199 String aBaseDimName( aDimName ); 1200 const ScDPSaveGroupDimension* pBaseGroupDim = pDimData->GetNamedGroupDim( aDimName ); 1201 if ( pBaseGroupDim ) 1202 { 1203 // any entry's SourceDimName is the original base 1204 aBaseDimName = pBaseGroupDim->GetSourceDimName(); 1205 } 1206 1207 // find existing group dimension 1208 // (using the selected dim, can be intermediate group dim) 1209 ScDPSaveGroupDimension* pGroupDimension = pDimData->GetGroupDimAccForBase( aDimName ); 1210 1211 // remove the selected items from their groups 1212 // (empty groups are removed, too) 1213 sal_uInt16 nEntryCount = aEntries.GetCount(); 1214 sal_uInt16 nEntry; 1215 if ( pGroupDimension ) 1216 { 1217 for (nEntry=0; nEntry<nEntryCount; nEntry++) 1218 { 1219 String aEntryName = aEntries[nEntry]->GetString(); 1220 if ( pBaseGroupDim ) 1221 { 1222 // for each selected (intermediate) group, remove all its items 1223 // (same logic as for adding, below) 1224 const ScDPSaveGroupItem* pBaseGroup = pBaseGroupDim->GetNamedGroup( aEntryName ); 1225 if ( pBaseGroup ) 1226 pBaseGroup->RemoveElementsFromGroups( *pGroupDimension ); // remove all elements 1227 else 1228 pGroupDimension->RemoveFromGroups( aEntryName ); 1229 } 1230 else 1231 pGroupDimension->RemoveFromGroups( aEntryName ); 1232 } 1233 } 1234 1235 ScDPSaveGroupDimension* pNewGroupDim = NULL; 1236 if ( !pGroupDimension ) 1237 { 1238 // create a new group dimension 1239 String aGroupDimName = pDimData->CreateGroupDimName( aBaseDimName, *pDPObj, false, NULL ); 1240 pNewGroupDim = new ScDPSaveGroupDimension( aBaseDimName, aGroupDimName ); 1241 1242 pGroupDimension = pNewGroupDim; // make changes to the new dim if none existed 1243 1244 if ( pBaseGroupDim ) 1245 { 1246 // If it's a higher-order group dimension, pre-allocate groups for all 1247 // non-selected original groups, so the individual base members aren't 1248 // used for automatic groups (this would make the original groups hard 1249 // to find). 1250 //! Also do this when removing groups? 1251 //! Handle this case dynamically with automatic groups? 1252 1253 long nGroupCount = pBaseGroupDim->GetGroupCount(); 1254 for ( long nGroup = 0; nGroup < nGroupCount; nGroup++ ) 1255 { 1256 const ScDPSaveGroupItem* pBaseGroup = pBaseGroupDim->GetGroupByIndex( nGroup ); 1257 1258 StrData aStrData( pBaseGroup->GetGroupName() ); 1259 sal_uInt16 nCollIndex; 1260 if ( !aEntries.Search( &aStrData, nCollIndex ) ) //! ignore case? 1261 { 1262 // add an additional group for each item that is not in the selection 1263 ScDPSaveGroupItem aGroup( pBaseGroup->GetGroupName() ); 1264 aGroup.AddElementsFromGroup( *pBaseGroup ); 1265 pGroupDimension->AddGroupItem( aGroup ); 1266 } 1267 } 1268 } 1269 } 1270 String aGroupDimName = pGroupDimension->GetGroupDimName(); 1271 1272 //! localized prefix string 1273 String aGroupName = pGroupDimension->CreateGroupName( String::CreateFromAscii("Group") ); 1274 ScDPSaveGroupItem aGroup( aGroupName ); 1275 for (nEntry=0; nEntry<nEntryCount; nEntry++) 1276 { 1277 String aEntryName = aEntries[nEntry]->GetString(); 1278 if ( pBaseGroupDim ) 1279 { 1280 // for each selected (intermediate) group, add all its items 1281 const ScDPSaveGroupItem* pBaseGroup = pBaseGroupDim->GetNamedGroup( aEntryName ); 1282 if ( pBaseGroup ) 1283 aGroup.AddElementsFromGroup( *pBaseGroup ); 1284 else 1285 aGroup.AddElement( aEntryName ); // no group found -> automatic group, add the item itself 1286 } 1287 else 1288 aGroup.AddElement( aEntryName ); // no group dimension, add all items directly 1289 } 1290 1291 pGroupDimension->AddGroupItem( aGroup ); 1292 1293 if ( pNewGroupDim ) 1294 { 1295 pDimData->AddGroupDimension( *pNewGroupDim ); 1296 delete pNewGroupDim; // AddGroupDimension copies the object 1297 // don't access pGroupDimension after here 1298 } 1299 pGroupDimension = pNewGroupDim = NULL; 1300 1301 // set orientation 1302 ScDPSaveDimension* pSaveDimension = aData.GetDimensionByName( aGroupDimName ); 1303 if ( pSaveDimension->GetOrientation() == sheet::DataPilotFieldOrientation_HIDDEN ) 1304 { 1305 ScDPSaveDimension* pOldDimension = aData.GetDimensionByName( aDimName ); 1306 pSaveDimension->SetOrientation( pOldDimension->GetOrientation() ); 1307 long nPosition = 0; //! before (immediate) base 1308 aData.SetPosition( pSaveDimension, nPosition ); 1309 } 1310 1311 // apply changes 1312 ScDBDocFunc aFunc( *GetViewData()->GetDocShell() ); 1313 ScDPObject* pNewObj = new ScDPObject( *pDPObj ); 1314 pNewObj->SetSaveData( aData ); 1315 aFunc.DataPilotUpdate( pDPObj, pNewObj, sal_True, sal_False ); 1316 delete pNewObj; 1317 1318 // unmark cell selection 1319 Unmark(); 1320 } 1321 } 1322 } 1323 1324 void ScDBFunc::UngroupDataPilot() 1325 { 1326 ScDPObject* pDPObj = GetViewData()->GetDocument()->GetDPAtCursor( GetViewData()->GetCurX(), 1327 GetViewData()->GetCurY(), GetViewData()->GetTabNo() ); 1328 if ( pDPObj ) 1329 { 1330 ScStrCollection aEntries; 1331 long nSelectDimension = -1; 1332 GetSelectedMemberList( aEntries, nSelectDimension ); 1333 1334 if ( aEntries.GetCount() > 0 ) 1335 { 1336 sal_Bool bIsDataLayout; 1337 String aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout ); 1338 1339 ScDPSaveData aData( *pDPObj->GetSaveData() ); 1340 ScDPDimensionSaveData* pDimData = aData.GetDimensionData(); // created if not there 1341 //! test first if DimensionData exists? 1342 1343 sal_Bool bApply = sal_False; 1344 1345 ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDimAcc( aDimName ); 1346 const ScDPSaveNumGroupDimension* pNumGroupDim = pDimData->GetNumGroupDim( aDimName ); 1347 if ( ( pGroupDim && pGroupDim->GetDatePart() != 0 ) || 1348 ( pNumGroupDim && pNumGroupDim->GetDatePart() != 0 ) ) 1349 { 1350 // Date grouping: need to remove all affected group dimensions. 1351 // This is done using DateGroupDataPilot with nParts=0. 1352 1353 DateGroupDataPilot( ScDPNumGroupInfo(), 0 ); 1354 // bApply remains FALSE 1355 // dimension pointers become invalid 1356 } 1357 else if ( pGroupDim ) 1358 { 1359 sal_uInt16 nEntryCount = aEntries.GetCount(); 1360 for (sal_uInt16 nEntry=0; nEntry<nEntryCount; nEntry++) 1361 { 1362 String aEntryName = aEntries[nEntry]->GetString(); 1363 pGroupDim->RemoveGroup( aEntryName ); 1364 } 1365 // remove group dimension if empty 1366 bool bEmptyDim = pGroupDim->IsEmpty(); 1367 if ( !bEmptyDim ) 1368 { 1369 // If all remaining groups in the dimension aren't shown, remove 1370 // the dimension too, as if it was completely empty. 1371 ScStrCollection aVisibleEntries; 1372 pDPObj->GetMemberResultNames( aVisibleEntries, nSelectDimension ); 1373 bEmptyDim = pGroupDim->HasOnlyHidden( aVisibleEntries ); 1374 } 1375 if ( bEmptyDim ) 1376 { 1377 pDimData->RemoveGroupDimension( aDimName ); // pGroupDim is deleted 1378 1379 // also remove SaveData settings for the dimension that no longer exists 1380 aData.RemoveDimensionByName( aDimName ); 1381 } 1382 bApply = sal_True; 1383 } 1384 else if ( pNumGroupDim ) 1385 { 1386 // remove the numerical grouping 1387 pDimData->RemoveNumGroupDimension( aDimName ); 1388 // SaveData settings can remain unchanged - the same dimension still exists 1389 bApply = sal_True; 1390 } 1391 1392 if ( bApply ) 1393 { 1394 // apply changes 1395 ScDBDocFunc aFunc( *GetViewData()->GetDocShell() ); 1396 ScDPObject* pNewObj = new ScDPObject( *pDPObj ); 1397 pNewObj->SetSaveData( aData ); 1398 aFunc.DataPilotUpdate( pDPObj, pNewObj, sal_True, sal_False ); 1399 delete pNewObj; 1400 1401 // unmark cell selection 1402 Unmark(); 1403 } 1404 } 1405 } 1406 } 1407 1408 static OUString lcl_replaceMemberNameInSubtotal(const OUString& rSubtotal, const OUString& rMemberName) 1409 { 1410 sal_Int32 n = rSubtotal.getLength(); 1411 const sal_Unicode* p = rSubtotal.getStr(); 1412 OUStringBuffer aBuf, aWordBuf; 1413 for (sal_Int32 i = 0; i < n; ++i) 1414 { 1415 sal_Unicode c = p[i]; 1416 if (c == sal_Unicode(' ')) 1417 { 1418 OUString aWord = aWordBuf.makeStringAndClear(); 1419 if (aWord.equals(rMemberName)) 1420 aBuf.append(sal_Unicode('?')); 1421 else 1422 aBuf.append(aWord); 1423 aBuf.append(c); 1424 } 1425 else if (c == sal_Unicode('\\')) 1426 { 1427 // Escape a backslash character. 1428 aWordBuf.append(c); 1429 aWordBuf.append(c); 1430 } 1431 else if (c == sal_Unicode('?')) 1432 { 1433 // A literal '?' must be escaped with a backslash ('\'); 1434 aWordBuf.append(sal_Unicode('\\')); 1435 aWordBuf.append(c); 1436 } 1437 else 1438 aWordBuf.append(c); 1439 } 1440 1441 if (aWordBuf.getLength() > 0) 1442 { 1443 OUString aWord = aWordBuf.makeStringAndClear(); 1444 if (aWord.equals(rMemberName)) 1445 aBuf.append(sal_Unicode('?')); 1446 else 1447 aBuf.append(aWord); 1448 } 1449 1450 return aBuf.makeStringAndClear(); 1451 } 1452 1453 void ScDBFunc::DataPilotInput( const ScAddress& rPos, const String& rString ) 1454 { 1455 using namespace ::com::sun::star::sheet; 1456 1457 String aNewName( rString ); 1458 1459 ScDocument* pDoc = GetViewData()->GetDocument(); 1460 ScDPObject* pDPObj = pDoc->GetDPAtCursor( rPos.Col(), rPos.Row(), rPos.Tab() ); 1461 if (!pDPObj) 1462 return; 1463 1464 String aOldText; 1465 pDoc->GetString( rPos.Col(), rPos.Row(), rPos.Tab(), aOldText ); 1466 1467 if ( aOldText == rString ) 1468 { 1469 // nothing to do: silently exit 1470 return; 1471 } 1472 1473 sal_uInt16 nErrorId = 0; 1474 1475 pDPObj->BuildAllDimensionMembers(); 1476 ScDPSaveData aData( *pDPObj->GetSaveData() ); 1477 sal_Bool bChange = sal_False; 1478 1479 sal_uInt16 nOrient = DataPilotFieldOrientation_HIDDEN; 1480 long nField = pDPObj->GetHeaderDim( rPos, nOrient ); 1481 if ( nField >= 0 ) 1482 { 1483 // changing a field title 1484 if ( aData.GetExistingDimensionData() ) 1485 { 1486 // only group dimensions can be renamed 1487 1488 ScDPDimensionSaveData* pDimData = aData.GetDimensionData(); 1489 ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDimAcc( aOldText ); 1490 if ( pGroupDim ) 1491 { 1492 // valid name: not empty, no existing dimension (group or other) 1493 if ( rString.Len() && !pDPObj->IsDimNameInUse(rString) ) 1494 { 1495 pGroupDim->Rename( aNewName ); 1496 1497 // also rename in SaveData to preserve the field settings 1498 ScDPSaveDimension* pSaveDim = aData.GetDimensionByName( aOldText ); 1499 pSaveDim->SetName( aNewName ); 1500 1501 bChange = sal_True; 1502 } 1503 else 1504 nErrorId = STR_INVALIDNAME; 1505 } 1506 } 1507 else if (nOrient == DataPilotFieldOrientation_COLUMN || nOrient == DataPilotFieldOrientation_ROW) 1508 { 1509 sal_Bool bDataLayout = false; 1510 String aDimName = pDPObj->GetDimName(nField, bDataLayout); 1511 ScDPSaveDimension* pDim = bDataLayout ? aData.GetDataLayoutDimension() : aData.GetDimensionByName(aDimName); 1512 if (pDim) 1513 { 1514 if (rString.Len()) 1515 { 1516 if (rString.EqualsIgnoreCaseAscii(aDimName)) 1517 { 1518 pDim->RemoveLayoutName(); 1519 bChange = true; 1520 } 1521 else if (!pDPObj->IsDimNameInUse(rString)) 1522 { 1523 pDim->SetLayoutName(rString); 1524 bChange = true; 1525 } 1526 else 1527 nErrorId = STR_INVALIDNAME; 1528 } 1529 else 1530 nErrorId = STR_INVALIDNAME; 1531 } 1532 } 1533 } 1534 else if (pDPObj->IsDataDescriptionCell(rPos)) 1535 { 1536 // There is only one data dimension. 1537 ScDPSaveDimension* pDim = aData.GetFirstDimension(sheet::DataPilotFieldOrientation_DATA); 1538 if (pDim) 1539 { 1540 if (rString.Len()) 1541 { 1542 if (rString.EqualsIgnoreCaseAscii(pDim->GetName())) 1543 { 1544 pDim->RemoveLayoutName(); 1545 bChange = true; 1546 } 1547 else if (!pDPObj->IsDimNameInUse(rString)) 1548 { 1549 pDim->SetLayoutName(rString); 1550 bChange = true; 1551 } 1552 else 1553 nErrorId = STR_INVALIDNAME; 1554 } 1555 else 1556 nErrorId = STR_INVALIDNAME; 1557 } 1558 } 1559 else 1560 { 1561 // This is not a field header. 1562 sheet::DataPilotTableHeaderData aPosData; 1563 pDPObj->GetHeaderPositionData(rPos, aPosData); 1564 1565 if ( (aPosData.Flags & MemberResultFlags::HASMEMBER) && aOldText.Len() ) 1566 { 1567 if ( aData.GetExistingDimensionData() && !(aPosData.Flags & MemberResultFlags::SUBTOTAL)) 1568 { 1569 sal_Bool bIsDataLayout; 1570 String aDimName = pDPObj->GetDimName( aPosData.Dimension, bIsDataLayout ); 1571 1572 ScDPDimensionSaveData* pDimData = aData.GetDimensionData(); 1573 ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDimAcc( aDimName ); 1574 if ( pGroupDim ) 1575 { 1576 // valid name: not empty, no existing group in this dimension 1577 //! ignore case? 1578 if ( aNewName.Len() && !pGroupDim->GetNamedGroup( aNewName ) ) 1579 { 1580 ScDPSaveGroupItem* pGroup = pGroupDim->GetNamedGroupAcc( aOldText ); 1581 if ( pGroup ) 1582 pGroup->Rename( aNewName ); // rename the existing group 1583 else 1584 { 1585 // create a new group to replace the automatic group 1586 ScDPSaveGroupItem aGroup( aNewName ); 1587 aGroup.AddElement( aOldText ); 1588 pGroupDim->AddGroupItem( aGroup ); 1589 } 1590 1591 // in both cases also adjust savedata, to preserve member settings (show details) 1592 ScDPSaveDimension* pSaveDim = aData.GetDimensionByName( aDimName ); 1593 ScDPSaveMember* pSaveMember = pSaveDim->GetExistingMemberByName( aOldText ); 1594 if ( pSaveMember ) 1595 pSaveMember->SetName( aNewName ); 1596 1597 bChange = sal_True; 1598 } 1599 else 1600 nErrorId = STR_INVALIDNAME; 1601 } 1602 } 1603 else if ((aPosData.Flags & MemberResultFlags::GRANDTOTAL)) 1604 { 1605 aData.SetGrandTotalName(rString); 1606 bChange = true; 1607 } 1608 else if (aPosData.Dimension >= 0 && aPosData.MemberName.getLength() > 0) 1609 { 1610 sal_Bool bDataLayout = false; 1611 String aDimName = pDPObj->GetDimName(static_cast<long>(aPosData.Dimension), bDataLayout); 1612 if (bDataLayout) 1613 { 1614 // data dimension 1615 do 1616 { 1617 if ((aPosData.Flags & MemberResultFlags::SUBTOTAL)) 1618 break; 1619 1620 ScDPSaveDimension* pDim = aData.GetDimensionByName(aPosData.MemberName); 1621 if (!pDim) 1622 break; 1623 1624 if (!rString.Len()) 1625 { 1626 nErrorId = STR_INVALIDNAME; 1627 break; 1628 } 1629 1630 if (aPosData.MemberName.equalsIgnoreAsciiCase(rString)) 1631 { 1632 pDim->RemoveLayoutName(); 1633 bChange = true; 1634 } 1635 else if (!pDPObj->IsDimNameInUse(rString)) 1636 { 1637 pDim->SetLayoutName(rString); 1638 bChange = true; 1639 } 1640 else 1641 nErrorId = STR_INVALIDNAME; 1642 } 1643 while (false); 1644 } 1645 else 1646 { 1647 // field member 1648 do 1649 { 1650 ScDPSaveDimension* pDim = aData.GetDimensionByName(aDimName); 1651 if (!pDim) 1652 break; 1653 1654 ScDPSaveMember* pMem = pDim->GetExistingMemberByName(aPosData.MemberName); 1655 if (!pMem) 1656 break; 1657 1658 if ((aPosData.Flags & MemberResultFlags::SUBTOTAL)) 1659 { 1660 // Change subtotal only when the table has one data dimension. 1661 if (aData.GetDataDimensionCount() > 1) 1662 break; 1663 1664 // display name for subtotal is allowed only if the subtotal type is 'Automatic'. 1665 if (pDim->GetSubTotalsCount() != 1) 1666 break; 1667 1668 if (pDim->GetSubTotalFunc(0) != sheet::GeneralFunction_AUTO) 1669 break; 1670 1671 const OUString* pLayoutName = pMem->GetLayoutName(); 1672 String aMemberName; 1673 if (pLayoutName) 1674 aMemberName = *pLayoutName; 1675 else 1676 aMemberName = aPosData.MemberName; 1677 1678 String aNew = lcl_replaceMemberNameInSubtotal(rString, aMemberName); 1679 pDim->SetSubtotalName(aNew); 1680 bChange = true; 1681 } 1682 else 1683 { 1684 // Check to make sure the member name isn't 1685 // already used. 1686 if (rString.Len()) 1687 { 1688 if (rString.EqualsIgnoreCaseAscii(pMem->GetName())) 1689 { 1690 pMem->RemoveLayoutName(); 1691 bChange = true; 1692 } 1693 else if (!pDim->IsMemberNameInUse(rString)) 1694 { 1695 pMem->SetLayoutName(rString); 1696 bChange = true; 1697 } 1698 else 1699 nErrorId = STR_INVALIDNAME; 1700 } 1701 else 1702 nErrorId = STR_INVALIDNAME; 1703 } 1704 } 1705 while (false); 1706 } 1707 } 1708 } 1709 } 1710 1711 if ( bChange ) 1712 { 1713 // apply changes 1714 ScDBDocFunc aFunc( *GetViewData()->GetDocShell() ); 1715 ScDPObject* pNewObj = new ScDPObject( *pDPObj ); 1716 pNewObj->SetSaveData( aData ); 1717 aFunc.DataPilotUpdate( pDPObj, pNewObj, sal_True, sal_False ); 1718 delete pNewObj; 1719 } 1720 else 1721 { 1722 if ( !nErrorId ) 1723 nErrorId = STR_ERR_DATAPILOT_INPUT; 1724 ErrorMessage( nErrorId ); 1725 } 1726 } 1727 1728 void lcl_MoveToEnd( ScDPSaveDimension& rDim, const String& rItemName ) 1729 { 1730 ScDPSaveMember* pNewMember = NULL; 1731 const ScDPSaveMember* pOldMember = rDim.GetExistingMemberByName( rItemName ); 1732 if ( pOldMember ) 1733 pNewMember = new ScDPSaveMember( *pOldMember ); 1734 else 1735 pNewMember = new ScDPSaveMember( rItemName ); 1736 rDim.AddMember( pNewMember ); 1737 // AddMember takes ownership of the new pointer, 1738 // puts it to the end of the list even if it was in the list before. 1739 } 1740 1741 struct ScOUStringCollate 1742 { 1743 CollatorWrapper* mpCollator; 1744 1745 ScOUStringCollate(CollatorWrapper* pColl) : mpCollator(pColl) {} 1746 1747 bool operator()(const rtl::OUString& rStr1, const rtl::OUString& rStr2) const 1748 { 1749 return ( mpCollator->compareString(rStr1, rStr2) < 0 ); 1750 } 1751 }; 1752 1753 bool ScDBFunc::DataPilotSort( const ScAddress& rPos, bool bAscending, sal_uInt16* pUserListId ) 1754 { 1755 ScDocument* pDoc = GetViewData()->GetDocument(); 1756 ScDPObject* pDPObj = pDoc->GetDPAtCursor(rPos.Col(), rPos.Row(), rPos.Tab()); 1757 if (!pDPObj) 1758 return false; 1759 1760 // We need to run this to get all members later. 1761 if ( pUserListId ) 1762 pDPObj->BuildAllDimensionMembers(); 1763 1764 sal_uInt16 nOrientation; 1765 long nDimIndex = pDPObj->GetHeaderDim(rPos, nOrientation); 1766 if (nDimIndex < 0) 1767 // Invalid dimension index. Bail out. 1768 return false; 1769 1770 sal_Bool bDataLayout; 1771 ScDPSaveData* pSaveData = pDPObj->GetSaveData(); 1772 if (!pSaveData) 1773 return false; 1774 1775 ScDPSaveData aNewSaveData(*pSaveData); 1776 String aDimName = pDPObj->GetDimName(nDimIndex, bDataLayout); 1777 ScDPSaveDimension* pSaveDim = aNewSaveData.GetDimensionByName(aDimName); 1778 if (!pSaveDim) 1779 return false; 1780 1781 // manual evaluation of sort order is only needed if a user list id is given 1782 if ( pUserListId ) 1783 { 1784 typedef ScDPSaveDimension::MemberList MemList; 1785 const MemList& rDimMembers = pSaveDim->GetMembers(); 1786 list<OUString> aMembers; 1787 hash_set<OUString, ::rtl::OUStringHash> aMemberSet; 1788 size_t nMemberCount = 0; 1789 for (MemList::const_iterator itr = rDimMembers.begin(), itrEnd = rDimMembers.end(); 1790 itr != itrEnd; ++itr) 1791 { 1792 ScDPSaveMember* pMem = *itr; 1793 aMembers.push_back(pMem->GetName()); 1794 aMemberSet.insert(pMem->GetName()); 1795 ++nMemberCount; 1796 } 1797 1798 // Sort the member list in ascending order. 1799 ScOUStringCollate aCollate( ScGlobal::GetCollator() ); 1800 aMembers.sort(aCollate); 1801 1802 // Collect and rank those custom sort strings that also exist in the member name list. 1803 1804 typedef hash_map<OUString, sal_uInt16, OUStringHash> UserSortMap; 1805 UserSortMap aSubStrs; 1806 sal_uInt16 nSubCount = 0; 1807 if (pUserListId) 1808 { 1809 ScUserList* pUserList = ScGlobal::GetUserList(); 1810 if (!pUserList) 1811 return false; 1812 1813 { 1814 sal_uInt16 n = pUserList->GetCount(); 1815 if (!n || *pUserListId >= n) 1816 return false; 1817 } 1818 1819 ScUserListData* pData = static_cast<ScUserListData*>((*pUserList)[*pUserListId]); 1820 if (pData) 1821 { 1822 sal_uInt16 n = pData->GetSubCount(); 1823 for (sal_uInt16 i = 0; i < n; ++i) 1824 { 1825 OUString aSub = pData->GetSubStr(i); 1826 if (!aMemberSet.count(aSub)) 1827 // This string doesn't exist in the member name set. Don't add this. 1828 continue; 1829 1830 aSubStrs.insert(UserSortMap::value_type(aSub, nSubCount++)); 1831 } 1832 } 1833 } 1834 1835 // Rank all members. 1836 1837 vector<OUString> aRankedNames(nMemberCount); 1838 sal_uInt16 nCurStrId = 0; 1839 for (list<OUString>::const_iterator itr = aMembers.begin(), itrEnd = aMembers.end(); 1840 itr != itrEnd; ++itr) 1841 { 1842 OUString aName = *itr; 1843 sal_uInt16 nRank = 0; 1844 UserSortMap::const_iterator itrSub = aSubStrs.find(aName); 1845 if (itrSub == aSubStrs.end()) 1846 nRank = nSubCount + nCurStrId++; 1847 else 1848 nRank = itrSub->second; 1849 1850 if (!bAscending) 1851 nRank = static_cast< sal_uInt16 >( nMemberCount - nRank - 1 ); 1852 1853 aRankedNames[nRank] = aName; 1854 } 1855 1856 // Re-order ScDPSaveMember instances with the new ranks. 1857 1858 for (vector<OUString>::const_iterator itr = aRankedNames.begin(), itrEnd = aRankedNames.end(); 1859 itr != itrEnd; ++itr) 1860 { 1861 const ScDPSaveMember* pOldMem = pSaveDim->GetExistingMemberByName(*itr); 1862 if (!pOldMem) 1863 // All members are supposed to be present. 1864 continue; 1865 1866 ScDPSaveMember* pNewMem = new ScDPSaveMember(*pOldMem); 1867 pSaveDim->AddMember(pNewMem); 1868 } 1869 1870 // Set the sorting mode to manual for now. We may introduce a new sorting 1871 // mode later on. 1872 1873 sheet::DataPilotFieldSortInfo aSortInfo; 1874 aSortInfo.Mode = sheet::DataPilotFieldSortMode::MANUAL; 1875 pSaveDim->SetSortInfo(&aSortInfo); 1876 } 1877 else 1878 { 1879 // without user list id, just apply sorting mode 1880 1881 sheet::DataPilotFieldSortInfo aSortInfo; 1882 aSortInfo.Mode = sheet::DataPilotFieldSortMode::NAME; 1883 aSortInfo.IsAscending = bAscending; 1884 pSaveDim->SetSortInfo(&aSortInfo); 1885 } 1886 1887 // Update the datapilot with the newly sorted field members. 1888 1889 auto_ptr<ScDPObject> pNewObj(new ScDPObject(*pDPObj)); 1890 pNewObj->SetSaveData(aNewSaveData); 1891 ScDBDocFunc aFunc(*GetViewData()->GetDocShell()); 1892 1893 return aFunc.DataPilotUpdate(pDPObj, pNewObj.get(), true, false); 1894 } 1895 1896 sal_Bool ScDBFunc::DataPilotMove( const ScRange& rSource, const ScAddress& rDest ) 1897 { 1898 sal_Bool bRet = sal_False; 1899 ScDocument* pDoc = GetViewData()->GetDocument(); 1900 ScDPObject* pDPObj = pDoc->GetDPAtCursor( rSource.aStart.Col(), rSource.aStart.Row(), rSource.aStart.Tab() ); 1901 if ( pDPObj && pDPObj == pDoc->GetDPAtCursor( rDest.Col(), rDest.Row(), rDest.Tab() ) ) 1902 { 1903 sheet::DataPilotTableHeaderData aDestData; 1904 pDPObj->GetHeaderPositionData( rDest, aDestData ); 1905 bool bValid = ( aDestData.Dimension >= 0 ); // dropping onto a field 1906 1907 // look through the source range 1908 std::hash_set< rtl::OUString, rtl::OUStringHash, std::equal_to<rtl::OUString> > aMembersSet; // for lookup 1909 std::vector< rtl::OUString > aMembersVector; // members in original order, for inserting 1910 aMembersVector.reserve( std::max( static_cast<SCSIZE>( rSource.aEnd.Col() - rSource.aStart.Col() + 1 ), 1911 static_cast<SCSIZE>( rSource.aEnd.Row() - rSource.aStart.Row() + 1 ) ) ); 1912 for (SCROW nRow = rSource.aStart.Row(); bValid && nRow <= rSource.aEnd.Row(); ++nRow ) 1913 for (SCCOL nCol = rSource.aStart.Col(); bValid && nCol <= rSource.aEnd.Col(); ++nCol ) 1914 { 1915 sheet::DataPilotTableHeaderData aSourceData; 1916 pDPObj->GetHeaderPositionData( ScAddress( nCol, nRow, rSource.aStart.Tab() ), aSourceData ); 1917 if ( aSourceData.Dimension == aDestData.Dimension && aSourceData.MemberName.getLength() ) 1918 { 1919 if ( aMembersSet.find( aSourceData.MemberName ) == aMembersSet.end() ) 1920 { 1921 aMembersSet.insert( aSourceData.MemberName ); 1922 aMembersVector.push_back( aSourceData.MemberName ); 1923 } 1924 // duplicates are ignored 1925 } 1926 else 1927 bValid = false; // empty (subtotal) or different field 1928 } 1929 1930 if ( bValid ) 1931 { 1932 sal_Bool bIsDataLayout; 1933 String aDimName = pDPObj->GetDimName( aDestData.Dimension, bIsDataLayout ); 1934 if ( !bIsDataLayout ) 1935 { 1936 ScDPSaveData aData( *pDPObj->GetSaveData() ); 1937 ScDPSaveDimension* pDim = aData.GetDimensionByName( aDimName ); 1938 1939 // get all member names in source order 1940 uno::Sequence<rtl::OUString> aMemberNames; 1941 pDPObj->GetMemberNames( aDestData.Dimension, aMemberNames ); 1942 1943 bool bInserted = false; 1944 1945 sal_Int32 nMemberCount = aMemberNames.getLength(); 1946 for (sal_Int32 nMemberPos=0; nMemberPos<nMemberCount; ++nMemberPos) 1947 { 1948 String aMemberStr( aMemberNames[nMemberPos] ); 1949 1950 if ( !bInserted && aMemberNames[nMemberPos] == aDestData.MemberName ) 1951 { 1952 // insert dragged items before this item 1953 for ( std::vector<rtl::OUString>::const_iterator aIter = aMembersVector.begin(); 1954 aIter != aMembersVector.end(); ++aIter ) 1955 lcl_MoveToEnd( *pDim, *aIter ); 1956 bInserted = true; 1957 } 1958 1959 if ( aMembersSet.find( aMemberStr ) == aMembersSet.end() ) // skip dragged items 1960 lcl_MoveToEnd( *pDim, aMemberStr ); 1961 } 1962 // insert dragged item at end if dest wasn't found (for example, empty) 1963 if ( !bInserted ) 1964 for ( std::vector<rtl::OUString>::const_iterator aIter = aMembersVector.begin(); 1965 aIter != aMembersVector.end(); ++aIter ) 1966 lcl_MoveToEnd( *pDim, *aIter ); 1967 1968 // Items that were in SaveData, but not in the source, end up at the start of the list. 1969 1970 // set flag for manual sorting 1971 sheet::DataPilotFieldSortInfo aSortInfo; 1972 aSortInfo.Mode = sheet::DataPilotFieldSortMode::MANUAL; 1973 pDim->SetSortInfo( &aSortInfo ); 1974 1975 // apply changes 1976 ScDBDocFunc aFunc( *GetViewData()->GetDocShell() ); 1977 ScDPObject* pNewObj = new ScDPObject( *pDPObj ); 1978 pNewObj->SetSaveData( aData ); 1979 aFunc.DataPilotUpdate( pDPObj, pNewObj, sal_True, sal_False ); //! bApi for drag&drop? 1980 delete pNewObj; 1981 1982 Unmark(); // entry was moved - no use in leaving the old cell selected 1983 1984 bRet = sal_True; 1985 } 1986 } 1987 } 1988 1989 return bRet; 1990 } 1991 1992 sal_Bool ScDBFunc::HasSelectionForDrillDown( sal_uInt16& rOrientation ) 1993 { 1994 sal_Bool bRet = sal_False; 1995 1996 ScDPObject* pDPObj = GetViewData()->GetDocument()->GetDPAtCursor( GetViewData()->GetCurX(), 1997 GetViewData()->GetCurY(), GetViewData()->GetTabNo() ); 1998 if ( pDPObj ) 1999 { 2000 ScStrCollection aEntries; 2001 long nSelectDimension = -1; 2002 GetSelectedMemberList( aEntries, nSelectDimension ); 2003 2004 if ( aEntries.GetCount() > 0 ) 2005 { 2006 sal_Bool bIsDataLayout; 2007 String aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout ); 2008 if ( !bIsDataLayout ) 2009 { 2010 ScDPSaveData* pSaveData = pDPObj->GetSaveData(); 2011 ScDPSaveDimension* pDim = pSaveData->GetExistingDimensionByName( aDimName ); 2012 if ( pDim ) 2013 { 2014 sal_uInt16 nDimOrient = pDim->GetOrientation(); 2015 ScDPSaveDimension* pInner = pSaveData->GetInnermostDimension( nDimOrient ); 2016 if ( pDim == pInner ) 2017 { 2018 rOrientation = nDimOrient; 2019 bRet = sal_True; 2020 } 2021 } 2022 } 2023 } 2024 } 2025 2026 return bRet; 2027 } 2028 2029 void ScDBFunc::SetDataPilotDetails( sal_Bool bShow, const String* pNewDimensionName ) 2030 { 2031 ScDPObject* pDPObj = GetViewData()->GetDocument()->GetDPAtCursor( GetViewData()->GetCurX(), 2032 GetViewData()->GetCurY(), GetViewData()->GetTabNo() ); 2033 if ( pDPObj ) 2034 { 2035 ScStrCollection aEntries; 2036 long nSelectDimension = -1; 2037 GetSelectedMemberList( aEntries, nSelectDimension ); 2038 2039 if ( aEntries.GetCount() > 0 ) 2040 { 2041 sal_Bool bIsDataLayout; 2042 String aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout ); 2043 if ( !bIsDataLayout ) 2044 { 2045 ScDPSaveData aData( *pDPObj->GetSaveData() ); 2046 ScDPSaveDimension* pDim = aData.GetDimensionByName( aDimName ); 2047 2048 if ( bShow && pNewDimensionName ) 2049 { 2050 // add the new dimension with the same orientation, at the end 2051 2052 ScDPSaveDimension* pNewDim = aData.GetDimensionByName( *pNewDimensionName ); 2053 ScDPSaveDimension* pDuplicated = NULL; 2054 if ( pNewDim->GetOrientation() == sheet::DataPilotFieldOrientation_DATA ) 2055 { 2056 // Need to duplicate the dimension, create column/row in addition to data: 2057 // The duplicated dimension inherits the existing settings, pNewDim is modified below. 2058 pDuplicated = aData.DuplicateDimension( *pNewDimensionName ); 2059 } 2060 2061 sal_uInt16 nOrientation = pDim->GetOrientation(); 2062 pNewDim->SetOrientation( nOrientation ); 2063 2064 long nPosition = LONG_MAX; 2065 aData.SetPosition( pNewDim, nPosition ); 2066 2067 ScDPSaveDimension* pDataLayout = aData.GetDataLayoutDimension(); 2068 if ( pDataLayout->GetOrientation() == nOrientation && 2069 aData.GetDataDimensionCount() <= 1 ) 2070 { 2071 // If there is only one data dimension, the data layout dimension 2072 // must still be the last one in its orientation. 2073 aData.SetPosition( pDataLayout, nPosition ); 2074 } 2075 2076 if ( pDuplicated ) 2077 { 2078 // The duplicated (data) dimension needs to be behind the original dimension 2079 aData.SetPosition( pDuplicated, nPosition ); 2080 } 2081 2082 // Hide details for all visible members (selected are changed below). 2083 //! Use all members from source level instead (including non-visible)? 2084 2085 ScStrCollection aVisibleEntries; 2086 pDPObj->GetMemberResultNames( aVisibleEntries, nSelectDimension ); 2087 2088 sal_uInt16 nVisCount = aVisibleEntries.GetCount(); 2089 for (sal_uInt16 nVisPos=0; nVisPos<nVisCount; nVisPos++) 2090 { 2091 String aVisName = aVisibleEntries[nVisPos]->GetString(); 2092 ScDPSaveMember* pMember = pDim->GetMemberByName( aVisName ); 2093 pMember->SetShowDetails( sal_False ); 2094 } 2095 } 2096 2097 sal_uInt16 nEntryCount = aEntries.GetCount(); 2098 for (sal_uInt16 nEntry=0; nEntry<nEntryCount; nEntry++) 2099 { 2100 String aEntryName = aEntries[nEntry]->GetString(); 2101 ScDPSaveMember* pMember = pDim->GetMemberByName( aEntryName ); 2102 pMember->SetShowDetails( bShow ); 2103 } 2104 2105 // apply changes 2106 ScDBDocFunc aFunc( *GetViewData()->GetDocShell() ); 2107 ScDPObject* pNewObj = new ScDPObject( *pDPObj ); 2108 pNewObj->SetSaveData( aData ); 2109 aFunc.DataPilotUpdate( pDPObj, pNewObj, sal_True, sal_False ); 2110 delete pNewObj; 2111 2112 // unmark cell selection 2113 Unmark(); 2114 } 2115 } 2116 } 2117 } 2118 2119 void ScDBFunc::ShowDataPilotSourceData( ScDPObject& rDPObj, const Sequence<sheet::DataPilotFieldFilter>& rFilters ) 2120 { 2121 ScDocument* pDoc = GetViewData()->GetDocument(); 2122 if (pDoc->GetDocumentShell()->IsReadOnly()) 2123 { 2124 ErrorMessage(STR_READONLYERR); 2125 return; 2126 } 2127 2128 Reference<sheet::XDimensionsSupplier> xDimSupplier = rDPObj.GetSource(); 2129 Reference<container::XNameAccess> xDims = xDimSupplier->getDimensions(); 2130 Reference<sheet::XDrillDownDataSupplier> xDDSupplier(xDimSupplier, UNO_QUERY); 2131 if (!xDDSupplier.is()) 2132 return; 2133 2134 Sequence< Sequence<Any> > aTabData = xDDSupplier->getDrillDownData(rFilters); 2135 sal_Int32 nRowSize = aTabData.getLength(); 2136 if (nRowSize <= 1) 2137 // There is no data to show. Bail out. 2138 return; 2139 2140 sal_Int32 nColSize = aTabData[0].getLength(); 2141 2142 SCTAB nNewTab = GetViewData()->GetTabNo(); 2143 2144 auto_ptr<ScDocument> pInsDoc(new ScDocument(SCDOCMODE_CLIP)); 2145 pInsDoc->ResetClip( pDoc, nNewTab ); 2146 for (SCROW nRow = 0; nRow < nRowSize; ++nRow) 2147 { 2148 for (SCCOL nCol = 0; nCol < nColSize; ++nCol) 2149 { 2150 const Any& rAny = aTabData[nRow][nCol]; 2151 rtl::OUString aStr; 2152 double fVal; 2153 if (rAny >>= aStr) 2154 pInsDoc->PutCell( ScAddress(nCol, nRow, nNewTab), new ScStringCell(String(aStr)) ); 2155 else if (rAny >>= fVal) 2156 pInsDoc->SetValue(nCol, nRow, nNewTab, fVal); 2157 } 2158 } 2159 2160 // set number format (important for dates) 2161 for (SCCOL nCol = 0; nCol < nColSize; ++nCol) 2162 { 2163 rtl::OUString aStr; 2164 if (!(aTabData[0][nCol] >>= aStr)) 2165 continue; 2166 2167 Reference<XPropertySet> xPropSet(xDims->getByName(aStr), UNO_QUERY); 2168 if (!xPropSet.is()) 2169 continue; 2170 2171 Any any = xPropSet->getPropertyValue( rtl::OUString::createFromAscii(SC_UNO_NUMBERFO) ); 2172 sal_Int32 nNumFmt = 0; 2173 if (!(any >>= nNumFmt)) 2174 continue; 2175 2176 ScPatternAttr aPattern( pInsDoc->GetPool() ); 2177 aPattern.GetItemSet().Put( SfxUInt32Item(ATTR_VALUE_FORMAT, static_cast<sal_uInt32>(nNumFmt)) ); 2178 pInsDoc->ApplyPatternAreaTab(nCol, 1, nCol, nRowSize-1, nNewTab, aPattern); 2179 } 2180 2181 SCCOL nEndCol = 0; 2182 SCROW nEndRow = 0; 2183 pInsDoc->GetCellArea( nNewTab, nEndCol, nEndRow ); 2184 pInsDoc->SetClipArea( ScRange( 0, 0, nNewTab, nEndCol, nEndRow, nNewTab ) ); 2185 2186 ::svl::IUndoManager* pMgr = GetViewData()->GetDocShell()->GetUndoManager(); 2187 String aUndo = ScGlobal::GetRscString( STR_UNDO_DOOUTLINE ); 2188 pMgr->EnterListAction( aUndo, aUndo ); 2189 2190 String aNewTabName; 2191 pDoc->CreateValidTabName(aNewTabName); 2192 if ( InsertTable(aNewTabName, nNewTab) ) 2193 PasteFromClip( IDF_ALL, pInsDoc.get() ); 2194 2195 pMgr->LeaveListAction(); 2196 } 2197 2198 // 2199 // DB-Operationen (Sortieren, Filtern, Teilergebnisse) wiederholen 2200 // 2201 2202 void ScDBFunc::RepeatDB( sal_Bool bRecord ) 2203 { 2204 SCCOL nCurX = GetViewData()->GetCurX(); 2205 SCROW nCurY = GetViewData()->GetCurY(); 2206 SCTAB nTab = GetViewData()->GetTabNo(); 2207 ScDocument* pDoc = GetViewData()->GetDocument(); 2208 ScDBData* pDBData = GetDBData(); 2209 if (bRecord && !pDoc->IsUndoEnabled()) 2210 bRecord = sal_False; 2211 2212 ScQueryParam aQueryParam; 2213 pDBData->GetQueryParam( aQueryParam ); 2214 sal_Bool bQuery = aQueryParam.GetEntry(0).bDoQuery; 2215 2216 ScSortParam aSortParam; 2217 pDBData->GetSortParam( aSortParam ); 2218 sal_Bool bSort = aSortParam.bDoSort[0]; 2219 2220 ScSubTotalParam aSubTotalParam; 2221 pDBData->GetSubTotalParam( aSubTotalParam ); 2222 sal_Bool bSubTotal = aSubTotalParam.bGroupActive[0] && !aSubTotalParam.bRemoveOnly; 2223 2224 if ( bQuery || bSort || bSubTotal ) 2225 { 2226 sal_Bool bQuerySize = sal_False; 2227 ScRange aOldQuery; 2228 ScRange aNewQuery; 2229 if (bQuery && !aQueryParam.bInplace) 2230 { 2231 ScDBData* pDest = pDoc->GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow, 2232 aQueryParam.nDestTab, sal_True ); 2233 if (pDest && pDest->IsDoSize()) 2234 { 2235 pDest->GetArea( aOldQuery ); 2236 bQuerySize = sal_True; 2237 } 2238 } 2239 2240 SCTAB nDummy; 2241 SCCOL nStartCol; 2242 SCROW nStartRow; 2243 SCCOL nEndCol; 2244 SCROW nEndRow; 2245 pDBData->GetArea( nDummy, nStartCol, nStartRow, nEndCol, nEndRow ); 2246 2247 //! Undo nur benoetigte Daten ? 2248 2249 ScDocument* pUndoDoc = NULL; 2250 ScOutlineTable* pUndoTab = NULL; 2251 ScRangeName* pUndoRange = NULL; 2252 ScDBCollection* pUndoDB = NULL; 2253 2254 if (bRecord) 2255 { 2256 SCTAB nTabCount = pDoc->GetTableCount(); 2257 pUndoDoc = new ScDocument( SCDOCMODE_UNDO ); 2258 ScOutlineTable* pTable = pDoc->GetOutlineTable( nTab ); 2259 if (pTable) 2260 { 2261 pUndoTab = new ScOutlineTable( *pTable ); 2262 2263 SCCOLROW nOutStartCol; // Zeilen/Spaltenstatus 2264 SCCOLROW nOutStartRow; 2265 SCCOLROW nOutEndCol; 2266 SCCOLROW nOutEndRow; 2267 pTable->GetColArray()->GetRange( nOutStartCol, nOutEndCol ); 2268 pTable->GetRowArray()->GetRange( nOutStartRow, nOutEndRow ); 2269 2270 pUndoDoc->InitUndo( pDoc, nTab, nTab, sal_True, sal_True ); 2271 pDoc->CopyToDocument( static_cast<SCCOL>(nOutStartCol), 0, nTab, static_cast<SCCOL>(nOutEndCol), MAXROW, nTab, IDF_NONE, sal_False, pUndoDoc ); 2272 pDoc->CopyToDocument( 0, nOutStartRow, nTab, MAXCOL, nOutEndRow, nTab, IDF_NONE, sal_False, pUndoDoc ); 2273 } 2274 else 2275 pUndoDoc->InitUndo( pDoc, nTab, nTab, sal_False, sal_True ); 2276 2277 // Datenbereich sichern - incl. Filter-Ergebnis 2278 pDoc->CopyToDocument( 0,nStartRow,nTab, MAXCOL,nEndRow,nTab, IDF_ALL, sal_False, pUndoDoc ); 2279 2280 // alle Formeln wegen Referenzen 2281 pDoc->CopyToDocument( 0,0,0, MAXCOL,MAXROW,nTabCount-1, IDF_FORMULA, sal_False, pUndoDoc ); 2282 2283 // DB- und andere Bereiche 2284 ScRangeName* pDocRange = pDoc->GetRangeName(); 2285 if (pDocRange->GetCount()) 2286 pUndoRange = new ScRangeName( *pDocRange ); 2287 ScDBCollection* pDocDB = pDoc->GetDBCollection(); 2288 if (pDocDB->GetCount()) 2289 pUndoDB = new ScDBCollection( *pDocDB ); 2290 } 2291 2292 if (bSort && bSubTotal) 2293 { 2294 // Sortieren ohne SubTotals 2295 2296 aSubTotalParam.bRemoveOnly = sal_True; // wird unten wieder zurueckgesetzt 2297 DoSubTotals( aSubTotalParam, sal_False ); 2298 } 2299 2300 if (bSort) 2301 { 2302 pDBData->GetSortParam( aSortParam ); // Bereich kann sich geaendert haben 2303 Sort( aSortParam, sal_False, sal_False); 2304 } 2305 if (bQuery) 2306 { 2307 pDBData->GetQueryParam( aQueryParam ); // Bereich kann sich geaendert haben 2308 ScRange aAdvSource; 2309 if (pDBData->GetAdvancedQuerySource(aAdvSource)) 2310 { 2311 pDoc->CreateQueryParam( 2312 aAdvSource.aStart.Col(), aAdvSource.aStart.Row(), 2313 aAdvSource.aEnd.Col(), aAdvSource.aEnd.Row(), 2314 aAdvSource.aStart.Tab(), aQueryParam ); 2315 Query( aQueryParam, &aAdvSource, sal_False ); 2316 } 2317 else 2318 Query( aQueryParam, NULL, sal_False ); 2319 2320 // bei nicht-inplace kann die Tabelle umgestellt worden sein 2321 if ( !aQueryParam.bInplace && aQueryParam.nDestTab != nTab ) 2322 SetTabNo( nTab ); 2323 } 2324 if (bSubTotal) 2325 { 2326 pDBData->GetSubTotalParam( aSubTotalParam ); // Bereich kann sich geaendert haben 2327 aSubTotalParam.bRemoveOnly = sal_False; 2328 DoSubTotals( aSubTotalParam, sal_False ); 2329 } 2330 2331 if (bRecord) 2332 { 2333 SCTAB nDummyTab; 2334 SCCOL nDummyCol; 2335 SCROW nDummyRow, nNewEndRow; 2336 pDBData->GetArea( nDummyTab, nDummyCol,nDummyRow, nDummyCol,nNewEndRow ); 2337 2338 const ScRange* pOld = NULL; 2339 const ScRange* pNew = NULL; 2340 if (bQuerySize) 2341 { 2342 ScDBData* pDest = pDoc->GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow, 2343 aQueryParam.nDestTab, sal_True ); 2344 if (pDest) 2345 { 2346 pDest->GetArea( aNewQuery ); 2347 pOld = &aOldQuery; 2348 pNew = &aNewQuery; 2349 } 2350 } 2351 2352 GetViewData()->GetDocShell()->GetUndoManager()->AddUndoAction( 2353 new ScUndoRepeatDB( GetViewData()->GetDocShell(), nTab, 2354 nStartCol, nStartRow, nEndCol, nEndRow, 2355 nNewEndRow, 2356 nCurX, nCurY, 2357 pUndoDoc, pUndoTab, 2358 pUndoRange, pUndoDB, 2359 pOld, pNew ) ); 2360 } 2361 2362 GetViewData()->GetDocShell()->PostPaint( 0,0,nTab, MAXCOL,MAXROW,nTab, 2363 PAINT_GRID | PAINT_LEFT | PAINT_TOP | PAINT_SIZE ); 2364 } 2365 else // "Keine Operationen auszufuehren" 2366 ErrorMessage(STR_MSSG_REPEATDB_0); 2367 } 2368 2369 2370 2371 2372