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