1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_sc.hxx" 30 31 32 33 // INCLUDE --------------------------------------------------------------- 34 35 #include "scitems.hxx" 36 #include <editeng/eeitem.hxx> 37 38 39 #include <svtools/colorcfg.hxx> 40 #include <editeng/colritem.hxx> 41 #include <editeng/editview.hxx> 42 #include <editeng/fhgtitem.hxx> 43 #include <editeng/scripttypeitem.hxx> 44 #include <sfx2/bindings.hxx> 45 #include <sfx2/printer.hxx> 46 47 #include <svx/svdview.hxx> 48 #include "tabvwsh.hxx" 49 50 #include "gridwin.hxx" 51 #include "viewdata.hxx" 52 #include "output.hxx" 53 #include "document.hxx" 54 #include "attrib.hxx" 55 #include "patattr.hxx" // InvertSimple 56 #include "dbcolect.hxx" 57 #include "docoptio.hxx" 58 #include "notemark.hxx" 59 #include "dbfunc.hxx" // oder GetPageBreakData an die ViewData 60 #include "scmod.hxx" 61 #include "inputhdl.hxx" 62 #include "rfindlst.hxx" 63 #include "hiranges.hxx" 64 #include "pagedata.hxx" 65 #include "docpool.hxx" 66 #include "globstr.hrc" 67 #include "docsh.hxx" // oder GetSfxInPlaceObject 68 #include "cbutton.hxx" 69 #include "invmerge.hxx" 70 #include "editutil.hxx" 71 #include "inputopt.hxx" 72 #include "fillinfo.hxx" 73 #include "dpcontrol.hxx" 74 #include "queryparam.hxx" 75 #include "sc.hrc" 76 #include <vcl/virdev.hxx> 77 78 // #i74769# 79 #include <svx/sdrpaintwindow.hxx> 80 81 //#include "tabvwsh.hxx" //! Test !!!! 82 83 //------------------------------------------------------------------------ 84 85 void lcl_LimitRect( Rectangle& rRect, const Rectangle& rVisible ) 86 { 87 if ( rRect.Top() < rVisible.Top()-1 ) rRect.Top() = rVisible.Top()-1; 88 // if ( rRect.Left() < rVisible.Left()-1 ) rRect.Left() = rVisible.Left()-1; 89 if ( rRect.Bottom() > rVisible.Bottom()+1 ) rRect.Bottom() = rVisible.Bottom()+1; 90 // if ( rRect.Right() > rVisible.Right()+1 ) rRect.Right() = rVisible.Right()+1; 91 92 // #51122# auch wenn das inner-Rectangle nicht sichtbar ist, muss evtl. 93 // die Titelzeile gezeichnet werden, darum kein Rueckgabewert mehr. 94 // Wenn's weit daneben liegt, wird lcl_DrawOneFrame erst gar nicht gerufen. 95 } 96 97 void lcl_DrawOneFrame( OutputDevice* pDev, const Rectangle& rInnerPixel, 98 const String& rTitle, const Color& rColor, sal_Bool bTextBelow, 99 double nPPTX, double nPPTY, const Fraction& rZoomY, 100 ScDocument* pDoc, ScViewData* pButtonViewData, sal_Bool bLayoutRTL ) 101 { 102 // pButtonViewData wird nur benutzt, um die Button-Groesse zu setzen, 103 // darf ansonsten NULL sein! 104 105 Rectangle aInner = rInnerPixel; 106 if ( bLayoutRTL ) 107 { 108 aInner.Left() = rInnerPixel.Right(); 109 aInner.Right() = rInnerPixel.Left(); 110 } 111 112 Rectangle aVisible( Point(0,0), pDev->GetOutputSizePixel() ); 113 lcl_LimitRect( aInner, aVisible ); 114 115 Rectangle aOuter = aInner; 116 long nHor = (long) ( SC_SCENARIO_HSPACE * nPPTX ); 117 long nVer = (long) ( SC_SCENARIO_VSPACE * nPPTY ); 118 aOuter.Left() -= nHor; 119 aOuter.Right() += nHor; 120 aOuter.Top() -= nVer; 121 aOuter.Bottom() += nVer; 122 123 // use ScPatternAttr::GetFont only for font size 124 Font aAttrFont; 125 ((const ScPatternAttr&)pDoc->GetPool()->GetDefaultItem(ATTR_PATTERN)). 126 GetFont(aAttrFont,SC_AUTOCOL_BLACK,pDev,&rZoomY); 127 128 // everything else from application font 129 Font aAppFont = pDev->GetSettings().GetStyleSettings().GetAppFont(); 130 aAppFont.SetSize( aAttrFont.GetSize() ); 131 132 aAppFont.SetAlign( ALIGN_TOP ); 133 pDev->SetFont( aAppFont ); 134 135 Size aTextSize( pDev->GetTextWidth( rTitle ), pDev->GetTextHeight() ); 136 137 if ( bTextBelow ) 138 aOuter.Bottom() += aTextSize.Height(); 139 else 140 aOuter.Top() -= aTextSize.Height(); 141 142 pDev->SetLineColor(); 143 pDev->SetFillColor( rColor ); 144 // links, oben, rechts, unten 145 pDev->DrawRect( Rectangle( aOuter.Left(), aOuter.Top(), aInner.Left(), aOuter.Bottom() ) ); 146 pDev->DrawRect( Rectangle( aOuter.Left(), aOuter.Top(), aOuter.Right(), aInner.Top() ) ); 147 pDev->DrawRect( Rectangle( aInner.Right(), aOuter.Top(), aOuter.Right(), aOuter.Bottom() ) ); 148 pDev->DrawRect( Rectangle( aOuter.Left(), aInner.Bottom(), aOuter.Right(), aOuter.Bottom() ) ); 149 150 long nButtonY = bTextBelow ? aInner.Bottom() : aOuter.Top(); 151 152 ScDDComboBoxButton aComboButton((Window*)pDev); 153 aComboButton.SetOptSizePixel(); 154 long nBWidth = ( aComboButton.GetSizePixel().Width() * rZoomY.GetNumerator() ) 155 / rZoomY.GetDenominator(); 156 long nBHeight = nVer + aTextSize.Height() + 1; 157 Size aButSize( nBWidth, nBHeight ); 158 long nButtonPos = bLayoutRTL ? aOuter.Left() : aOuter.Right()-nBWidth+1; 159 aComboButton.Draw( Point(nButtonPos, nButtonY), aButSize, sal_False ); 160 if (pButtonViewData) 161 pButtonViewData->SetScenButSize( aButSize ); 162 163 long nTextStart = bLayoutRTL ? aInner.Right() - aTextSize.Width() + 1 : aInner.Left(); 164 165 sal_Bool bWasClip = sal_False; 166 Region aOldClip; 167 sal_Bool bClip = ( aTextSize.Width() > aOuter.Right() - nBWidth - aInner.Left() ); 168 if ( bClip ) 169 { 170 if (pDev->IsClipRegion()) 171 { 172 bWasClip = sal_True; 173 aOldClip = pDev->GetActiveClipRegion(); 174 } 175 long nClipStartX = bLayoutRTL ? aOuter.Left() + nBWidth : aInner.Left(); 176 long nClipEndX = bLayoutRTL ? aInner.Right() : aOuter.Right() - nBWidth; 177 pDev->SetClipRegion( Rectangle( nClipStartX, nButtonY + nVer/2, 178 nClipEndX, nButtonY + nVer/2 + aTextSize.Height() ) ); 179 } 180 181 pDev->DrawText( Point( nTextStart, nButtonY + nVer/2 ), rTitle ); 182 183 if ( bClip ) 184 { 185 if ( bWasClip ) 186 pDev->SetClipRegion(aOldClip); 187 else 188 pDev->SetClipRegion(); 189 } 190 191 pDev->SetFillColor(); 192 pDev->SetLineColor( COL_BLACK ); 193 pDev->DrawRect( aInner ); 194 pDev->DrawRect( aOuter ); 195 } 196 197 void lcl_DrawScenarioFrames( OutputDevice* pDev, ScViewData* pViewData, ScSplitPos eWhich, 198 SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2 ) 199 { 200 ScDocument* pDoc = pViewData->GetDocument(); 201 SCTAB nTab = pViewData->GetTabNo(); 202 SCTAB nTabCount = pDoc->GetTableCount(); 203 if ( nTab+1<nTabCount && pDoc->IsScenario(nTab+1) && !pDoc->IsScenario(nTab) ) 204 { 205 if ( nX1 > 0 ) --nX1; 206 if ( nY1>=2 ) nY1 -= 2; // Hack: Titelzeile beruehrt zwei Zellen 207 else if ( nY1 > 0 ) --nY1; 208 if ( nX2 < MAXCOL ) ++nX2; 209 if ( nY2 < MAXROW-1 ) nY2 += 2; // Hack: Titelzeile beruehrt zwei Zellen 210 else if ( nY2 < MAXROW ) ++nY2; 211 ScRange aViewRange( nX1,nY1,nTab, nX2,nY2,nTab ); 212 213 //! Ranges an der Table cachen!!!! 214 215 ScMarkData aMarks; 216 for (SCTAB i=nTab+1; i<nTabCount && pDoc->IsScenario(i); i++) 217 pDoc->MarkScenario( i, nTab, aMarks, sal_False, SC_SCENARIO_SHOWFRAME ); 218 ScRangeListRef xRanges = new ScRangeList; 219 aMarks.FillRangeListWithMarks( xRanges, sal_False ); 220 221 sal_Bool bLayoutRTL = pDoc->IsLayoutRTL( nTab ); 222 long nLayoutSign = bLayoutRTL ? -1 : 1; 223 224 sal_uInt16 nRangeCount = (sal_uInt16)xRanges->Count(); 225 for (sal_uInt16 j=0; j<nRangeCount; j++) 226 { 227 ScRange aRange = *xRanges->GetObject(j); 228 // Szenario-Rahmen immer dann auf zusammengefasste Zellen erweitern, wenn 229 // dadurch keine neuen nicht-ueberdeckten Zellen mit umrandet werden 230 pDoc->ExtendTotalMerge( aRange ); 231 232 //! -> Repaint beim Zusammenfassen erweitern !!! 233 234 if ( aRange.Intersects( aViewRange ) ) //! Platz fuer Text/Button? 235 { 236 Point aStartPos = pViewData->GetScrPos( 237 aRange.aStart.Col(), aRange.aStart.Row(), eWhich, sal_True ); 238 Point aEndPos = pViewData->GetScrPos( 239 aRange.aEnd.Col()+1, aRange.aEnd.Row()+1, eWhich, sal_True ); 240 // on the grid: 241 aStartPos.X() -= nLayoutSign; 242 aStartPos.Y() -= 1; 243 aEndPos.X() -= nLayoutSign; 244 aEndPos.Y() -= 1; 245 246 sal_Bool bTextBelow = ( aRange.aStart.Row() == 0 ); 247 248 String aCurrent; 249 Color aColor( COL_LIGHTGRAY ); 250 for (SCTAB nAct=nTab+1; nAct<nTabCount && pDoc->IsScenario(nAct); nAct++) 251 if ( pDoc->IsActiveScenario(nAct) && pDoc->HasScenarioRange(nAct,aRange) ) 252 { 253 String aDummyComment; 254 sal_uInt16 nDummyFlags; 255 pDoc->GetName( nAct, aCurrent ); 256 pDoc->GetScenarioData( nAct, aDummyComment, aColor, nDummyFlags ); 257 } 258 259 if (!aCurrent.Len()) 260 aCurrent = ScGlobal::GetRscString( STR_EMPTYDATA ); 261 262 //! eigener Text "(keins)" statt "(leer)" ??? 263 264 lcl_DrawOneFrame( pDev, Rectangle( aStartPos, aEndPos ), 265 aCurrent, aColor, bTextBelow, 266 pViewData->GetPPTX(), pViewData->GetPPTY(), pViewData->GetZoomY(), 267 pDoc, pViewData, bLayoutRTL ); 268 } 269 } 270 } 271 } 272 273 //------------------------------------------------------------------------ 274 275 void lcl_DrawHighlight( ScOutputData& rOutputData, ScViewData* pViewData, 276 ScHighlightRanges& rHighlightRanges ) 277 { 278 SCTAB nTab = pViewData->GetTabNo(); 279 sal_uLong nCount = rHighlightRanges.Count(); 280 for (sal_uLong i=0; i<nCount; i++) 281 { 282 ScHighlightEntry* pEntry = rHighlightRanges.GetObject( i ); 283 if (pEntry) 284 { 285 ScRange aRange = pEntry->aRef; 286 if ( nTab >= aRange.aStart.Tab() && nTab <= aRange.aEnd.Tab() ) 287 { 288 rOutputData.DrawRefMark( 289 aRange.aStart.Col(), aRange.aStart.Row(), 290 aRange.aEnd.Col(), aRange.aEnd.Row(), 291 pEntry->aColor, sal_False ); 292 } 293 } 294 } 295 } 296 297 //------------------------------------------------------------------------ 298 299 void ScGridWindow::DoInvertRect( const Rectangle& rPixel ) 300 { 301 // Invert( PixelToLogic(rPixel) ); 302 303 if ( rPixel == aInvertRect ) 304 aInvertRect = Rectangle(); // aufheben 305 else 306 { 307 DBG_ASSERT( aInvertRect.IsEmpty(), "DoInvertRect nicht paarig" ); 308 309 aInvertRect = rPixel; // neues Rechteck merken 310 } 311 312 UpdateHeaderOverlay(); // uses aInvertRect 313 } 314 315 //------------------------------------------------------------------------ 316 317 void __EXPORT ScGridWindow::PrePaint() 318 { 319 // forward PrePaint to DrawingLayer 320 ScTabViewShell* pTabViewShell = pViewData->GetViewShell(); 321 322 if(pTabViewShell) 323 { 324 SdrView* pDrawView = pTabViewShell->GetSdrView(); 325 326 if(pDrawView) 327 { 328 pDrawView->PrePaint(); 329 } 330 } 331 } 332 333 //------------------------------------------------------------------------ 334 335 void __EXPORT ScGridWindow::Paint( const Rectangle& rRect ) 336 { 337 //TODO/LATER: how to get environment? Do we need that?! 338 /* 339 ScDocShell* pDocSh = pViewData->GetDocShell(); 340 SvInPlaceEnvironment* pEnv = pDocSh->GetIPEnv(); 341 if (pEnv && pEnv->GetRectsChangedLockCount()) 342 { 343 Invalidate(rRect); 344 return; 345 }*/ 346 347 ScDocument* pDoc = pViewData->GetDocument(); 348 if ( pDoc->IsInInterpreter() ) 349 { 350 // via Reschedule, interpretierende Zellen nicht nochmal anstossen 351 // hier kein Invalidate, sonst kommt z.B. eine Error-Box nie an die Reihe 352 // (Bug 36381). Durch bNeedsRepaint wird spaeter alles nochmal gemalt. 353 354 if ( bNeedsRepaint ) 355 { 356 //! Rechtecke zusammenfassen? 357 aRepaintPixel = Rectangle(); // mehrfach -> alles painten 358 } 359 else 360 { 361 bNeedsRepaint = sal_True; 362 aRepaintPixel = LogicToPixel(rRect); // nur betroffenen Bereich 363 } 364 return; 365 } 366 367 // #i117893# If GetSizePixel needs to call the resize handler, the resulting nested Paint call 368 // (possibly for a larger rectangle) has to be allowed. Call GetSizePixel before setting bIsInPaint. 369 GetSizePixel(); 370 371 if (bIsInPaint) 372 return; 373 374 bIsInPaint = sal_True; 375 376 Rectangle aPixRect = LogicToPixel( rRect ); 377 378 SCCOL nX1 = pViewData->GetPosX(eHWhich); 379 SCROW nY1 = pViewData->GetPosY(eVWhich); 380 381 SCTAB nTab = pViewData->GetTabNo(); 382 383 double nPPTX = pViewData->GetPPTX(); 384 double nPPTY = pViewData->GetPPTY(); 385 386 Rectangle aMirroredPixel = aPixRect; 387 if ( pDoc->IsLayoutRTL( nTab ) ) 388 { 389 // mirror and swap 390 long nWidth = GetSizePixel().Width(); 391 aMirroredPixel.Left() = nWidth - 1 - aPixRect.Right(); 392 aMirroredPixel.Right() = nWidth - 1 - aPixRect.Left(); 393 } 394 395 long nScrX = ScViewData::ToPixel( pDoc->GetColWidth( nX1, nTab ), nPPTX ); 396 while ( nScrX <= aMirroredPixel.Left() && nX1 < MAXCOL ) 397 { 398 ++nX1; 399 nScrX += ScViewData::ToPixel( pDoc->GetColWidth( nX1, nTab ), nPPTX ); 400 } 401 SCCOL nX2 = nX1; 402 while ( nScrX <= aMirroredPixel.Right() && nX2 < MAXCOL ) 403 { 404 ++nX2; 405 nScrX += ScViewData::ToPixel( pDoc->GetColWidth( nX2, nTab ), nPPTX ); 406 } 407 408 long nScrY = 0; 409 ScViewData::AddPixelsWhile( nScrY, aPixRect.Top(), nY1, MAXROW, nPPTY, pDoc, nTab); 410 SCROW nY2 = nY1; 411 if (nScrY <= aPixRect.Bottom() && nY2 < MAXROW) 412 { 413 ++nY2; 414 ScViewData::AddPixelsWhile( nScrY, aPixRect.Bottom(), nY2, MAXROW, nPPTY, pDoc, nTab); 415 } 416 417 Draw( nX1,nY1,nX2,nY2, SC_UPDATE_MARKS ); // nicht weiterzeichnen 418 419 bIsInPaint = sal_False; 420 } 421 422 // 423 // Draw ---------------------------------------------------------------- 424 // 425 426 void ScGridWindow::Draw( SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2, ScUpdateMode eMode ) 427 { 428 ScModule* pScMod = SC_MOD(); 429 sal_Bool bTextWysiwyg = pScMod->GetInputOptions().GetTextWysiwyg(); 430 sal_Bool bGridFirst = sal_True; //! entscheiden!!! 431 432 if (pViewData->IsMinimized()) 433 return; 434 435 PutInOrder( nX1, nX2 ); 436 PutInOrder( nY1, nY2 ); 437 438 DBG_ASSERT( ValidCol(nX2) && ValidRow(nY2), "GridWin Draw Bereich zu gross" ); 439 440 SCCOL nPosX = pViewData->GetPosX( eHWhich ); 441 SCROW nPosY = pViewData->GetPosY( eVWhich ); 442 if (nX2 < nPosX || nY2 < nPosY) 443 return; // unsichtbar 444 if (nX1 < nPosX) nX1 = nPosX; 445 if (nY1 < nPosY) nY1 = nPosY; 446 447 SCCOL nXRight = nPosX + pViewData->VisibleCellsX(eHWhich); 448 if (nXRight > MAXCOL) nXRight = MAXCOL; 449 SCROW nYBottom = nPosY + pViewData->VisibleCellsY(eVWhich); 450 if (nYBottom > MAXROW) nYBottom = MAXROW; 451 452 // Store the current visible range. 453 maVisibleRange.mnCol1 = nPosX; 454 maVisibleRange.mnCol2 = nXRight; 455 maVisibleRange.mnRow1 = nPosY; 456 maVisibleRange.mnRow2 = nYBottom; 457 458 if (nX1 > nXRight || nY1 > nYBottom) 459 return; // unsichtbar 460 if (nX2 > nXRight) nX2 = nXRight; 461 if (nY2 > nYBottom) nY2 = nYBottom; 462 463 if ( eMode != SC_UPDATE_MARKS ) 464 if (nX2 < nXRight) 465 nX2 = nXRight; // zum Weiterzeichnen 466 467 // ab hier kein return mehr 468 469 ++nPaintCount; // merken, dass gemalt wird (wichtig beim Invertieren) 470 471 ScDocShell* pDocSh = pViewData->GetDocShell(); 472 ScDocument* pDoc = pDocSh->GetDocument(); 473 SCTAB nTab = pViewData->GetTabNo(); 474 475 pDoc->ExtendHidden( nX1, nY1, nX2, nY2, nTab ); 476 477 Point aScrPos = pViewData->GetScrPos( nX1, nY1, eWhich ); 478 long nMirrorWidth = GetSizePixel().Width(); 479 sal_Bool bLayoutRTL = pDoc->IsLayoutRTL( nTab ); 480 long nLayoutSign = bLayoutRTL ? -1 : 1; 481 if ( bLayoutRTL ) 482 { 483 long nEndPixel = pViewData->GetScrPos( nX2+1, nPosY, eWhich ).X(); 484 nMirrorWidth = aScrPos.X() - nEndPixel; 485 aScrPos.X() = nEndPixel + 1; 486 } 487 488 long nScrX = aScrPos.X(); 489 long nScrY = aScrPos.Y(); 490 491 SCCOL nCurX = pViewData->GetCurX(); 492 SCROW nCurY = pViewData->GetCurY(); 493 SCCOL nCurEndX = nCurX; 494 SCROW nCurEndY = nCurY; 495 pDoc->ExtendMerge( nCurX, nCurY, nCurEndX, nCurEndY, nTab ); 496 sal_Bool bCurVis = nCursorHideCount==0 && 497 ( nCurEndX+1 >= nX1 && nCurX <= nX2+1 && nCurEndY+1 >= nY1 && nCurY <= nY2+1 ); 498 499 // AutoFill-Anfasser 500 if ( !bCurVis && nCursorHideCount==0 && bAutoMarkVisible && aAutoMarkPos.Tab() == nTab && 501 ( aAutoMarkPos.Col() != nCurX || aAutoMarkPos.Row() != nCurY ) ) 502 { 503 SCCOL nHdlX = aAutoMarkPos.Col(); 504 SCROW nHdlY = aAutoMarkPos.Row(); 505 pDoc->ExtendMerge( nHdlX, nHdlY, nHdlX, nHdlY, nTab ); 506 bCurVis = ( nHdlX+1 >= nX1 && nHdlX <= nX2 && nHdlY+1 >= nY1 && nHdlY <= nY2 ); 507 // links und oben ist nicht betroffen 508 509 //! AutoFill-Anfasser alleine (ohne Cursor) zeichnen ??? 510 } 511 512 double nPPTX = pViewData->GetPPTX(); 513 double nPPTY = pViewData->GetPPTY(); 514 515 const ScViewOptions& rOpts = pViewData->GetOptions(); 516 sal_Bool bFormulaMode = rOpts.GetOption( VOPT_FORMULAS ); 517 sal_Bool bMarkClipped = rOpts.GetOption( VOPT_CLIPMARKS ); 518 519 // Datenblock 520 521 ScTableInfo aTabInfo; 522 pDoc->FillInfo( aTabInfo, nX1, nY1, nX2, nY2, nTab, 523 nPPTX, nPPTY, sal_False, bFormulaMode, 524 &pViewData->GetMarkData() ); 525 526 //-------------------------------------------------------------------- 527 528 Fraction aZoomX = pViewData->GetZoomX(); 529 Fraction aZoomY = pViewData->GetZoomY(); 530 ScOutputData aOutputData( this, OUTTYPE_WINDOW, aTabInfo, pDoc, nTab, 531 nScrX, nScrY, nX1, nY1, nX2, nY2, nPPTX, nPPTY, 532 &aZoomX, &aZoomY ); 533 534 aOutputData.SetMirrorWidth( nMirrorWidth ); // needed for RTL 535 536 std::auto_ptr< VirtualDevice > xFmtVirtDev; 537 sal_Bool bLogicText = bTextWysiwyg; // call DrawStrings in logic MapMode? 538 539 if ( bTextWysiwyg ) 540 { 541 // use printer for text formatting 542 543 OutputDevice* pFmtDev = pDoc->GetPrinter(); 544 pFmtDev->SetMapMode( pViewData->GetLogicMode(eWhich) ); 545 aOutputData.SetFmtDevice( pFmtDev ); 546 } 547 else if ( aZoomX != aZoomY && pViewData->IsOle() ) 548 { 549 // #i45033# For OLE inplace editing with different zoom factors, 550 // use a virtual device with 1/100th mm as text formatting reference 551 552 xFmtVirtDev.reset( new VirtualDevice ); 553 xFmtVirtDev->SetMapMode( MAP_100TH_MM ); 554 aOutputData.SetFmtDevice( xFmtVirtDev.get() ); 555 556 bLogicText = sal_True; // use logic MapMode 557 } 558 559 const svtools::ColorConfig& rColorCfg = pScMod->GetColorConfig(); 560 Color aGridColor( rColorCfg.GetColorValue( svtools::CALCGRID, sal_False ).nColor ); 561 if ( aGridColor.GetColor() == COL_TRANSPARENT ) 562 { 563 // use view options' grid color only if color config has "automatic" color 564 aGridColor = rOpts.GetGridColor(); 565 } 566 567 aOutputData.SetSyntaxMode ( pViewData->IsSyntaxMode() ); 568 aOutputData.SetGridColor ( aGridColor ); 569 aOutputData.SetShowNullValues ( rOpts.GetOption( VOPT_NULLVALS ) ); 570 aOutputData.SetShowFormulas ( bFormulaMode ); 571 aOutputData.SetShowSpellErrors ( pDoc->GetDocOptions().IsAutoSpell() ); 572 aOutputData.SetMarkClipped ( bMarkClipped ); 573 574 aOutputData.SetUseStyleColor( sal_True ); // always set in table view 575 576 aOutputData.SetEditObject( GetEditObject() ); 577 aOutputData.SetViewShell( pViewData->GetViewShell() ); 578 579 sal_Bool bGrid = rOpts.GetOption( VOPT_GRID ); 580 sal_Bool bPage = rOpts.GetOption( VOPT_PAGEBREAKS ); 581 582 if ( eMode == SC_UPDATE_CHANGED ) 583 { 584 aOutputData.FindChanged(); 585 aOutputData.SetSingleGrid(sal_True); 586 } 587 588 sal_Bool bPageMode = pViewData->IsPagebreakMode(); 589 if (bPageMode) // nach FindChanged 590 { 591 // SetPagebreakMode initialisiert auch bPrinted Flags 592 aOutputData.SetPagebreakMode( pViewData->GetView()->GetPageBreakData() ); 593 } 594 595 EditView* pEditView = NULL; 596 sal_Bool bEditMode = pViewData->HasEditView(eWhich); 597 if ( bEditMode && pViewData->GetRefTabNo() == nTab ) 598 { 599 SCCOL nEditCol; 600 SCROW nEditRow; 601 pViewData->GetEditView( eWhich, pEditView, nEditCol, nEditRow ); 602 SCCOL nEditEndCol = pViewData->GetEditEndCol(); 603 SCROW nEditEndRow = pViewData->GetEditEndRow(); 604 605 if ( nEditEndCol >= nX1 && nEditCol <= nX2 && nEditEndRow >= nY1 && nEditRow <= nY2 ) 606 aOutputData.SetEditCell( nEditCol, nEditRow ); 607 else 608 bEditMode = sal_False; 609 610 // nur Edit-Area zu zeichnen? 611 //! dann muss trotzdem noch der Rand / das Gitter gemalt werden! 612 613 // if ( nEditCol <= nX1 && nEditEndCol >= nX2 && nEditRow <= nY1 && nEditEndRow >= nY2 ) 614 // bOnlyEdit = sal_True; 615 } 616 617 // define drawing layer map mode and paint rectangle 618 const MapMode aDrawMode = GetDrawMapMode(); 619 Rectangle aDrawingRectLogic; 620 621 { 622 // get drawing pixel rect 623 Rectangle aDrawingRectPixel(Point(nScrX, nScrY), Size(aOutputData.GetScrW(), aOutputData.GetScrH())); 624 625 // correct for border (left/right) 626 if(MAXCOL == nX2) 627 { 628 if(bLayoutRTL) 629 { 630 aDrawingRectPixel.Left() = 0L; 631 } 632 else 633 { 634 aDrawingRectPixel.Right() = GetOutputSizePixel().getWidth(); 635 } 636 } 637 638 // correct for border (bottom) 639 if(MAXROW == nY2) 640 { 641 aDrawingRectPixel.Bottom() = GetOutputSizePixel().getHeight(); 642 } 643 644 // get logic positions 645 aDrawingRectLogic = PixelToLogic(aDrawingRectPixel, aDrawMode); 646 } 647 648 // not necessary with overlay 649 // if (bCurVis) 650 // HideCursor(); 651 652 OutputDevice* pContentDev = this; // device for document content, used by overlay manager 653 SdrPaintWindow* pTargetPaintWindow = 0; // #i74769# work with SdrPaintWindow directly 654 655 { 656 // init redraw 657 ScTabViewShell* pTabViewShell = pViewData->GetViewShell(); 658 659 if(pTabViewShell) 660 { 661 MapMode aCurrentMapMode(pContentDev->GetMapMode()); 662 pContentDev->SetMapMode(aDrawMode); 663 SdrView* pDrawView = pTabViewShell->GetSdrView(); 664 665 if(pDrawView) 666 { 667 // #i74769# Use new BeginDrawLayers() interface 668 Region aDrawingRegion(aDrawingRectLogic); 669 pTargetPaintWindow = pDrawView->BeginDrawLayers(this, aDrawingRegion); 670 OSL_ENSURE(pTargetPaintWindow, "BeginDrawLayers: Got no SdrPaintWindow (!)"); 671 672 // #i74769# get target device from SdrPaintWindow, this may be the prerender 673 // device now, too. 674 pContentDev = &(pTargetPaintWindow->GetTargetOutputDevice()); 675 aOutputData.SetContentDevice( pContentDev ); 676 } 677 678 pContentDev->SetMapMode(aCurrentMapMode); 679 } 680 } 681 682 // Rand (Wiese) (Pixel) 683 if ( nX2==MAXCOL || nY2==MAXROW ) 684 { 685 // save MapMode and set to pixel 686 MapMode aCurrentMapMode(pContentDev->GetMapMode()); 687 pContentDev->SetMapMode(MAP_PIXEL); 688 689 Rectangle aPixRect = Rectangle( Point(), GetOutputSizePixel() ); 690 pContentDev->SetFillColor( rColorCfg.GetColorValue(svtools::APPBACKGROUND).nColor ); 691 pContentDev->SetLineColor(); 692 if ( nX2==MAXCOL ) 693 { 694 Rectangle aDrawRect( aPixRect ); 695 if ( bLayoutRTL ) 696 aDrawRect.Right() = nScrX - 1; 697 else 698 aDrawRect.Left() = nScrX + aOutputData.GetScrW(); 699 if (aDrawRect.Right() >= aDrawRect.Left()) 700 pContentDev->DrawRect( aDrawRect ); 701 } 702 if ( nY2==MAXROW ) 703 { 704 Rectangle aDrawRect( aPixRect ); 705 aDrawRect.Top() = nScrY + aOutputData.GetScrH(); 706 if ( nX2==MAXCOL ) 707 { 708 // no double painting of the corner 709 if ( bLayoutRTL ) 710 aDrawRect.Left() = nScrX; 711 else 712 aDrawRect.Right() = nScrX + aOutputData.GetScrW() - 1; 713 } 714 if (aDrawRect.Bottom() >= aDrawRect.Top()) 715 pContentDev->DrawRect( aDrawRect ); 716 } 717 718 // restore MapMode 719 pContentDev->SetMapMode(aCurrentMapMode); 720 } 721 722 if ( pDoc->HasBackgroundDraw( nTab, aDrawingRectLogic ) ) 723 { 724 pContentDev->SetMapMode(MAP_PIXEL); 725 aOutputData.DrawClear(); 726 727 // Drawing Hintergrund 728 729 pContentDev->SetMapMode(aDrawMode); 730 DrawRedraw( aOutputData, eMode, SC_LAYER_BACK ); 731 } 732 else 733 aOutputData.SetSolidBackground(sal_True); 734 735 pContentDev->SetMapMode(MAP_PIXEL); 736 aOutputData.DrawBackground(); 737 if ( bGridFirst && ( bGrid || bPage ) ) 738 aOutputData.DrawGrid( bGrid, bPage ); 739 if ( bPageMode ) 740 { 741 // #87655# DrawPagePreview draws complete lines/page numbers, must always be clipped 742 if ( aOutputData.SetChangedClip() ) 743 { 744 DrawPagePreview(nX1,nY1,nX2,nY2, pContentDev); 745 pContentDev->SetClipRegion(); 746 } 747 } 748 aOutputData.DrawShadow(); 749 aOutputData.DrawFrame(); 750 if ( !bLogicText ) 751 aOutputData.DrawStrings(sal_False); // in pixel MapMode 752 753 // edit cells and printer-metrics text must be before the buttons 754 // (DataPilot buttons contain labels in UI font) 755 756 pContentDev->SetMapMode(pViewData->GetLogicMode(eWhich)); 757 if ( bLogicText ) 758 aOutputData.DrawStrings(sal_True); // in logic MapMode if bTextWysiwyg is set 759 aOutputData.DrawEdit(sal_True); 760 pContentDev->SetMapMode(MAP_PIXEL); 761 762 // Autofilter- und Pivot-Buttons 763 764 DrawButtons( nX1, nY1, nX2, nY2, aTabInfo, pContentDev ); // Pixel 765 766 // Notiz-Anzeiger 767 768 if ( rOpts.GetOption( VOPT_NOTES ) ) 769 aOutputData.DrawNoteMarks(); 770 771 if ( !bGridFirst && ( bGrid || bPage ) ) 772 { 773 aOutputData.DrawGrid( bGrid, bPage ); 774 } 775 aOutputData.DrawClipMarks(); 776 777 // Szenario / ChangeTracking muss auf jeden Fall nach DrawGrid sein, auch bei !bGridFirst 778 779 //! Test, ob ChangeTrack-Anzeige aktiv ist 780 //! Szenario-Rahmen per View-Optionen abschaltbar? 781 782 SCTAB nTabCount = pDoc->GetTableCount(); 783 ScHighlightRanges* pHigh = pViewData->GetView()->GetHighlightRanges(); 784 sal_Bool bHasScenario = ( nTab+1<nTabCount && pDoc->IsScenario(nTab+1) && !pDoc->IsScenario(nTab) ); 785 sal_Bool bHasChange = ( pDoc->GetChangeTrack() != NULL ); 786 787 if ( bHasChange || bHasScenario || pHigh != NULL ) 788 { 789 790 //! SetChangedClip() mit DrawMarks() zusammenfassen?? (anderer MapMode!) 791 792 sal_Bool bAny = sal_True; 793 if (eMode == SC_UPDATE_CHANGED) 794 bAny = aOutputData.SetChangedClip(); 795 if (bAny) 796 { 797 if ( bHasChange ) 798 aOutputData.DrawChangeTrack(); 799 800 if ( bHasScenario ) 801 lcl_DrawScenarioFrames( pContentDev, pViewData, eWhich, nX1,nY1,nX2,nY2 ); 802 803 if ( pHigh ) 804 lcl_DrawHighlight( aOutputData, pViewData, *pHigh ); 805 806 if (eMode == SC_UPDATE_CHANGED) 807 pContentDev->SetClipRegion(); 808 } 809 } 810 811 // Drawing Vordergrund 812 813 pContentDev->SetMapMode(aDrawMode); 814 815 DrawRedraw( aOutputData, eMode, SC_LAYER_FRONT ); 816 DrawRedraw( aOutputData, eMode, SC_LAYER_INTERN ); 817 DrawSdrGrid( aDrawingRectLogic, pContentDev ); 818 819 if (!bIsInScroll) // Drawing Markierungen 820 { 821 if(eMode == SC_UPDATE_CHANGED && aOutputData.SetChangedClip()) 822 { 823 pContentDev->SetClipRegion(); 824 } 825 826 //sal_Bool bDraw = sal_True; 827 //if (eMode == SC_UPDATE_CHANGED) 828 // bDraw = NeedDrawMarks() && aOutputData.SetChangedClip(); 829 //if (bDraw) 830 //{ 831 // DrawMarks(); 832 // if (eMode == SC_UPDATE_CHANGED) 833 // pContentDev->SetClipRegion(); 834 //} 835 } 836 837 pContentDev->SetMapMode(MAP_PIXEL); 838 839 #ifdef OLD_SELECTION_PAINT 840 if (pViewData->IsActive()) 841 aOutputData.DrawMark( this ); 842 #endif 843 844 if ( pViewData->IsRefMode() && nTab >= pViewData->GetRefStartZ() && nTab <= pViewData->GetRefEndZ() ) 845 { 846 // The AutoFill shrink area has an own overlay now 847 #if 0 848 // Schraffur beim Loeschen per AutoFill 849 if ( pViewData->GetRefType() == SC_REFTYPE_FILL ) 850 { 851 ScRange aRange; 852 if ( pViewData->GetDelMark( aRange ) ) 853 { 854 if ( aRange.aStart.Col() < nX1 ) aRange.aStart.SetCol(nX1); 855 if ( aRange.aEnd.Col() > nX2 ) aRange.aEnd.SetCol(nX2); 856 if ( aRange.aStart.Row() < nY1 ) aRange.aStart.SetRow(nY1); 857 if ( aRange.aEnd.Row() > nY2 ) aRange.aEnd.SetRow(nY2); 858 if ( aRange.aStart.Col() <= aRange.aEnd.Col() && 859 aRange.aStart.Row() <= aRange.aEnd.Row() ) 860 { 861 Point aStart = pViewData->GetScrPos( aRange.aStart.Col(), 862 aRange.aStart.Row(), eWhich ); 863 Point aEnd = pViewData->GetScrPos( aRange.aEnd.Col()+1, 864 aRange.aEnd.Row()+1, eWhich ); 865 aEnd.X() -= 1; 866 aEnd.Y() -= 1; 867 868 // Markierung aufheben - roter Rahmen bleibt stehen 869 Rectangle aRect( aStart,aEnd ); 870 Invert( aRect, INVERT_HIGHLIGHT ); 871 872 //! Delete-Bereich extra kennzeichnen?!?!? 873 } 874 } 875 } 876 #endif 877 878 Color aRefColor( rColorCfg.GetColorValue(svtools::CALCREFERENCE).nColor ); 879 aOutputData.DrawRefMark( pViewData->GetRefStartX(), pViewData->GetRefStartY(), 880 pViewData->GetRefEndX(), pViewData->GetRefEndY(), 881 aRefColor, sal_False ); 882 } 883 884 // Range-Finder 885 886 ScInputHandler* pHdl = pScMod->GetInputHdl( pViewData->GetViewShell() ); 887 if (pHdl) 888 { 889 ScRangeFindList* pRangeFinder = pHdl->GetRangeFindList(); 890 if ( pRangeFinder && !pRangeFinder->IsHidden() && 891 pRangeFinder->GetDocName() == pDocSh->GetTitle() ) 892 { 893 sal_uInt16 nCount = (sal_uInt16)pRangeFinder->Count(); 894 for (sal_uInt16 i=0; i<nCount; i++) 895 { 896 ScRangeFindData* pData = pRangeFinder->GetObject(i); 897 if (pData) 898 { 899 ScRange aRef = pData->aRef; 900 aRef.Justify(); 901 if ( aRef.aStart.Tab() >= nTab && aRef.aEnd.Tab() <= nTab ) 902 aOutputData.DrawRefMark( aRef.aStart.Col(), aRef.aStart.Row(), 903 aRef.aEnd.Col(), aRef.aEnd.Row(), 904 Color( ScRangeFindList::GetColorName( i ) ), 905 sal_True ); 906 } 907 } 908 } 909 } 910 911 { 912 // end redraw 913 ScTabViewShell* pTabViewShell = pViewData->GetViewShell(); 914 915 if(pTabViewShell) 916 { 917 MapMode aCurrentMapMode(pContentDev->GetMapMode()); 918 pContentDev->SetMapMode(aDrawMode); 919 SdrView* pDrawView = pTabViewShell->GetSdrView(); 920 921 if(pDrawView) 922 { 923 // #i74769# work with SdrPaintWindow directly 924 pDrawView->EndDrawLayers(*pTargetPaintWindow, true); 925 } 926 927 pContentDev->SetMapMode(aCurrentMapMode); 928 } 929 } 930 931 // InPlace Edit-View 932 // moved after EndDrawLayers() to get it outside the overlay buffer and 933 // on top of everything 934 if ( bEditMode && (pViewData->GetRefTabNo() == pViewData->GetTabNo()) ) 935 { 936 //! use pContentDev for EditView? 937 SetMapMode(MAP_PIXEL); 938 SCCOL nCol1 = pViewData->GetEditStartCol(); 939 SCROW nRow1 = pViewData->GetEditStartRow(); 940 SCCOL nCol2 = pViewData->GetEditEndCol(); 941 SCROW nRow2 = pViewData->GetEditEndRow(); 942 SetLineColor(); 943 SetFillColor( pEditView->GetBackgroundColor() ); 944 Point aStart = pViewData->GetScrPos( nCol1, nRow1, eWhich ); 945 Point aEnd = pViewData->GetScrPos( nCol2+1, nRow2+1, eWhich ); 946 aEnd.X() -= 2 * nLayoutSign; // don't overwrite grid 947 aEnd.Y() -= 2; 948 DrawRect( Rectangle( aStart,aEnd ) ); 949 950 SetMapMode(pViewData->GetLogicMode()); 951 pEditView->Paint( PixelToLogic( Rectangle( Point( nScrX, nScrY ), 952 Size( aOutputData.GetScrW(), aOutputData.GetScrH() ) ) ) ); 953 SetMapMode(MAP_PIXEL); 954 } 955 956 if (pViewData->HasEditView(eWhich)) 957 { 958 // flush OverlayManager before changing the MapMode 959 flushOverlayManager(); 960 961 // set MapMode for text edit 962 SetMapMode(pViewData->GetLogicMode()); 963 } 964 else 965 SetMapMode(aDrawMode); 966 967 if ( pNoteMarker ) 968 pNoteMarker->Draw(); // ueber den Cursor, im Drawing-MapMode 969 970 //DrawStartTimer(); // fuer bunte Handles ohne System-Clipping 971 972 // 973 // Wenn waehrend des Paint etwas invertiert wurde (Selektion geaendert aus Basic-Macro), 974 // ist das jetzt durcheinandergekommen und es muss neu gemalt werden 975 // 976 977 DBG_ASSERT(nPaintCount, "nPaintCount falsch"); 978 --nPaintCount; 979 if (!nPaintCount) 980 CheckNeedsRepaint(); 981 } 982 983 void ScGridWindow::CheckNeedsRepaint() 984 { 985 // called at the end of painting, and from timer after background text width calculation 986 987 if (bNeedsRepaint) 988 { 989 bNeedsRepaint = sal_False; 990 if (aRepaintPixel.IsEmpty()) 991 Invalidate(); 992 else 993 Invalidate(PixelToLogic(aRepaintPixel)); 994 aRepaintPixel = Rectangle(); 995 996 // selection function in status bar might also be invalid 997 SfxBindings& rBindings = pViewData->GetBindings(); 998 rBindings.Invalidate( SID_STATUS_SUM ); 999 rBindings.Invalidate( SID_ATTR_SIZE ); 1000 rBindings.Invalidate( SID_TABLE_CELL ); 1001 } 1002 } 1003 1004 void ScGridWindow::DrawPagePreview( SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2, OutputDevice* pContentDev ) 1005 { 1006 ScPageBreakData* pPageData = pViewData->GetView()->GetPageBreakData(); 1007 if (pPageData) 1008 { 1009 ScDocument* pDoc = pViewData->GetDocument(); 1010 SCTAB nTab = pViewData->GetTabNo(); 1011 Size aWinSize = GetOutputSizePixel(); 1012 const svtools::ColorConfig& rColorCfg = SC_MOD()->GetColorConfig(); 1013 Color aManual( rColorCfg.GetColorValue(svtools::CALCPAGEBREAKMANUAL).nColor ); 1014 Color aAutomatic( rColorCfg.GetColorValue(svtools::CALCPAGEBREAK).nColor ); 1015 1016 String aPageText = ScGlobal::GetRscString( STR_PAGE ); 1017 if ( nPageScript == 0 ) 1018 { 1019 // get script type of translated "Page" string only once 1020 nPageScript = pDoc->GetStringScriptType( aPageText ); 1021 if (nPageScript == 0) 1022 nPageScript = ScGlobal::GetDefaultScriptType(); 1023 } 1024 aPageText += ' '; 1025 1026 Font aFont; 1027 ScEditEngineDefaulter* pEditEng = NULL; 1028 const ScPatternAttr& rDefPattern = ((const ScPatternAttr&)pDoc->GetPool()->GetDefaultItem(ATTR_PATTERN)); 1029 if ( nPageScript == SCRIPTTYPE_LATIN ) 1030 { 1031 // use single font and call DrawText directly 1032 rDefPattern.GetFont( aFont, SC_AUTOCOL_BLACK ); 1033 aFont.SetColor( Color( COL_LIGHTGRAY ) ); 1034 // font size is set as needed 1035 } 1036 else 1037 { 1038 // use EditEngine to draw mixed-script string 1039 pEditEng = new ScEditEngineDefaulter( EditEngine::CreatePool(), sal_True ); 1040 pEditEng->SetRefMapMode( pContentDev->GetMapMode() ); 1041 SfxItemSet* pEditDefaults = new SfxItemSet( pEditEng->GetEmptyItemSet() ); 1042 rDefPattern.FillEditItemSet( pEditDefaults ); 1043 pEditDefaults->Put( SvxColorItem( Color( COL_LIGHTGRAY ), EE_CHAR_COLOR ) ); 1044 pEditEng->SetDefaults( pEditDefaults ); 1045 } 1046 1047 sal_uInt16 nCount = sal::static_int_cast<sal_uInt16>( pPageData->GetCount() ); 1048 for (sal_uInt16 nPos=0; nPos<nCount; nPos++) 1049 { 1050 ScPrintRangeData& rData = pPageData->GetData(nPos); 1051 ScRange aRange = rData.GetPrintRange(); 1052 if ( aRange.aStart.Col() <= nX2+1 && aRange.aEnd.Col()+1 >= nX1 && 1053 aRange.aStart.Row() <= nY2+1 && aRange.aEnd.Row()+1 >= nY1 ) 1054 { 1055 // 3 Pixel Rahmen um den Druckbereich 1056 // (mittlerer Pixel auf den Gitterlinien) 1057 1058 pContentDev->SetLineColor(); 1059 if (rData.IsAutomatic()) 1060 pContentDev->SetFillColor( aAutomatic ); 1061 else 1062 pContentDev->SetFillColor( aManual ); 1063 1064 Point aStart = pViewData->GetScrPos( 1065 aRange.aStart.Col(), aRange.aStart.Row(), eWhich, sal_True ); 1066 Point aEnd = pViewData->GetScrPos( 1067 aRange.aEnd.Col() + 1, aRange.aEnd.Row() + 1, eWhich, sal_True ); 1068 aStart.X() -= 2; 1069 aStart.Y() -= 2; 1070 1071 // Ueberlaeufe verhindern: 1072 if ( aStart.X() < -10 ) aStart.X() = -10; 1073 if ( aStart.Y() < -10 ) aStart.Y() = -10; 1074 if ( aEnd.X() > aWinSize.Width() + 10 ) 1075 aEnd.X() = aWinSize.Width() + 10; 1076 if ( aEnd.Y() > aWinSize.Height() + 10 ) 1077 aEnd.Y() = aWinSize.Height() + 10; 1078 1079 pContentDev->DrawRect( Rectangle( aStart, Point(aEnd.X(),aStart.Y()+2) ) ); 1080 pContentDev->DrawRect( Rectangle( aStart, Point(aStart.X()+2,aEnd.Y()) ) ); 1081 pContentDev->DrawRect( Rectangle( Point(aStart.X(),aEnd.Y()-2), aEnd ) ); 1082 pContentDev->DrawRect( Rectangle( Point(aEnd.X()-2,aStart.Y()), aEnd ) ); 1083 1084 // Seitenumbrueche 1085 //! anders darstellen (gestrichelt ????) 1086 1087 size_t nColBreaks = rData.GetPagesX(); 1088 const SCCOL* pColEnd = rData.GetPageEndX(); 1089 size_t nColPos; 1090 for (nColPos=0; nColPos+1<nColBreaks; nColPos++) 1091 { 1092 SCCOL nBreak = pColEnd[nColPos]+1; 1093 if ( nBreak >= nX1 && nBreak <= nX2+1 ) 1094 { 1095 //! hidden suchen 1096 if (pDoc->HasColBreak(nBreak, nTab) & BREAK_MANUAL) 1097 pContentDev->SetFillColor( aManual ); 1098 else 1099 pContentDev->SetFillColor( aAutomatic ); 1100 Point aBreak = pViewData->GetScrPos( 1101 nBreak, aRange.aStart.Row(), eWhich, sal_True ); 1102 pContentDev->DrawRect( Rectangle( aBreak.X()-1, aStart.Y(), aBreak.X(), aEnd.Y() ) ); 1103 } 1104 } 1105 1106 size_t nRowBreaks = rData.GetPagesY(); 1107 const SCROW* pRowEnd = rData.GetPageEndY(); 1108 size_t nRowPos; 1109 for (nRowPos=0; nRowPos+1<nRowBreaks; nRowPos++) 1110 { 1111 SCROW nBreak = pRowEnd[nRowPos]+1; 1112 if ( nBreak >= nY1 && nBreak <= nY2+1 ) 1113 { 1114 //! hidden suchen 1115 if (pDoc->HasRowBreak(nBreak, nTab) & BREAK_MANUAL) 1116 pContentDev->SetFillColor( aManual ); 1117 else 1118 pContentDev->SetFillColor( aAutomatic ); 1119 Point aBreak = pViewData->GetScrPos( 1120 aRange.aStart.Col(), nBreak, eWhich, sal_True ); 1121 pContentDev->DrawRect( Rectangle( aStart.X(), aBreak.Y()-1, aEnd.X(), aBreak.Y() ) ); 1122 } 1123 } 1124 1125 // Seitenzahlen 1126 1127 SCROW nPrStartY = aRange.aStart.Row(); 1128 for (nRowPos=0; nRowPos<nRowBreaks; nRowPos++) 1129 { 1130 SCROW nPrEndY = pRowEnd[nRowPos]; 1131 if ( nPrEndY >= nY1 && nPrStartY <= nY2 ) 1132 { 1133 SCCOL nPrStartX = aRange.aStart.Col(); 1134 for (nColPos=0; nColPos<nColBreaks; nColPos++) 1135 { 1136 SCCOL nPrEndX = pColEnd[nColPos]; 1137 if ( nPrEndX >= nX1 && nPrStartX <= nX2 ) 1138 { 1139 Point aPageStart = pViewData->GetScrPos( 1140 nPrStartX, nPrStartY, eWhich, sal_True ); 1141 Point aPageEnd = pViewData->GetScrPos( 1142 nPrEndX+1,nPrEndY+1, eWhich, sal_True ); 1143 1144 long nPageNo = rData.GetFirstPage(); 1145 if ( rData.IsTopDown() ) 1146 nPageNo += ((long)nColPos)*nRowBreaks+nRowPos; 1147 else 1148 nPageNo += ((long)nRowPos)*nColBreaks+nColPos; 1149 String aPageStr = aPageText; 1150 aPageStr += String::CreateFromInt32(nPageNo); 1151 1152 if ( pEditEng ) 1153 { 1154 // find right font size with EditEngine 1155 long nHeight = 100; 1156 pEditEng->SetDefaultItem( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT ) ); 1157 pEditEng->SetDefaultItem( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT_CJK ) ); 1158 pEditEng->SetDefaultItem( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT_CTL ) ); 1159 pEditEng->SetText( aPageStr ); 1160 Size aSize100( pEditEng->CalcTextWidth(), pEditEng->GetTextHeight() ); 1161 1162 // 40% of width or 60% of height 1163 long nSizeX = 40 * ( aPageEnd.X() - aPageStart.X() ) / aSize100.Width(); 1164 long nSizeY = 60 * ( aPageEnd.Y() - aPageStart.Y() ) / aSize100.Height(); 1165 nHeight = Min(nSizeX,nSizeY); 1166 pEditEng->SetDefaultItem( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT ) ); 1167 pEditEng->SetDefaultItem( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT_CJK ) ); 1168 pEditEng->SetDefaultItem( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT_CTL ) ); 1169 1170 // centered output with EditEngine 1171 Size aTextSize( pEditEng->CalcTextWidth(), pEditEng->GetTextHeight() ); 1172 Point aPos( (aPageStart.X()+aPageEnd.X()-aTextSize.Width())/2, 1173 (aPageStart.Y()+aPageEnd.Y()-aTextSize.Height())/2 ); 1174 pEditEng->Draw( pContentDev, aPos ); 1175 } 1176 else 1177 { 1178 // find right font size for DrawText 1179 aFont.SetSize( Size( 0,100 ) ); 1180 pContentDev->SetFont( aFont ); 1181 Size aSize100( pContentDev->GetTextWidth( aPageStr ), pContentDev->GetTextHeight() ); 1182 1183 // 40% of width or 60% of height 1184 long nSizeX = 40 * ( aPageEnd.X() - aPageStart.X() ) / aSize100.Width(); 1185 long nSizeY = 60 * ( aPageEnd.Y() - aPageStart.Y() ) / aSize100.Height(); 1186 aFont.SetSize( Size( 0,Min(nSizeX,nSizeY) ) ); 1187 pContentDev->SetFont( aFont ); 1188 1189 // centered output with DrawText 1190 Size aTextSize( pContentDev->GetTextWidth( aPageStr ), pContentDev->GetTextHeight() ); 1191 Point aPos( (aPageStart.X()+aPageEnd.X()-aTextSize.Width())/2, 1192 (aPageStart.Y()+aPageEnd.Y()-aTextSize.Height())/2 ); 1193 pContentDev->DrawText( aPos, aPageStr ); 1194 } 1195 } 1196 nPrStartX = nPrEndX + 1; 1197 } 1198 } 1199 nPrStartY = nPrEndY + 1; 1200 } 1201 } 1202 } 1203 1204 delete pEditEng; 1205 } 1206 } 1207 1208 void ScGridWindow::DrawButtons( SCCOL nX1, SCROW /*nY1*/, SCCOL nX2, SCROW /*nY2*/, ScTableInfo& rTabInfo, OutputDevice* pContentDev ) 1209 { 1210 aComboButton.SetOutputDevice( pContentDev ); 1211 1212 ScDocument* pDoc = pViewData->GetDocument(); 1213 ScDPFieldButton aCellBtn(pContentDev, &GetSettings().GetStyleSettings(), &pViewData->GetZoomX(), &pViewData->GetZoomY(), pDoc); 1214 1215 SCCOL nCol; 1216 SCROW nRow; 1217 SCSIZE nArrY; 1218 SCSIZE nQuery; 1219 SCTAB nTab = pViewData->GetTabNo(); 1220 ScDBData* pDBData = NULL; 1221 ScQueryParam* pQueryParam = NULL; 1222 1223 RowInfo* pRowInfo = rTabInfo.mpRowInfo; 1224 sal_uInt16 nArrCount = rTabInfo.mnArrCount; 1225 1226 sal_Bool bLayoutRTL = pDoc->IsLayoutRTL( nTab ); 1227 1228 Point aOldPos = aComboButton.GetPosPixel(); // Zustand fuer MouseDown/Up 1229 Size aOldSize = aComboButton.GetSizePixel(); // merken 1230 1231 for (nArrY=1; nArrY+1<nArrCount; nArrY++) 1232 { 1233 if ( pRowInfo[nArrY].bAutoFilter && pRowInfo[nArrY].bChanged ) 1234 { 1235 RowInfo* pThisRowInfo = &pRowInfo[nArrY]; 1236 1237 nRow = pThisRowInfo->nRowNo; 1238 1239 1240 for (nCol=nX1; nCol<=nX2; nCol++) 1241 { 1242 CellInfo* pInfo = &pThisRowInfo->pCellInfo[nCol+1]; 1243 if ( pInfo->bAutoFilter && !pInfo->bHOverlapped && !pInfo->bVOverlapped ) 1244 { 1245 if (!pQueryParam) 1246 pQueryParam = new ScQueryParam; 1247 1248 sal_Bool bNewData = sal_True; 1249 if (pDBData) 1250 { 1251 SCCOL nStartCol; 1252 SCROW nStartRow; 1253 SCCOL nEndCol; 1254 SCROW nEndRow; 1255 SCTAB nAreaTab; 1256 pDBData->GetArea( nAreaTab, nStartCol, nStartRow, nEndCol, nEndRow ); 1257 if ( nCol >= nStartCol && nCol <= nEndCol && 1258 nRow >= nStartRow && nRow <= nEndRow ) 1259 bNewData = sal_False; 1260 } 1261 if (bNewData) 1262 { 1263 pDBData = pDoc->GetDBAtCursor( nCol, nRow, nTab ); 1264 if (pDBData) 1265 pDBData->GetQueryParam( *pQueryParam ); 1266 else 1267 { 1268 // can also be part of DataPilot table 1269 // DBG_ERROR("Auto-Filter-Button ohne DBData"); 1270 } 1271 } 1272 1273 // pQueryParam kann nur MAXQUERY Eintraege enthalten 1274 1275 sal_Bool bSimpleQuery = sal_True; 1276 sal_Bool bColumnFound = sal_False; 1277 if (!pQueryParam->bInplace) 1278 bSimpleQuery = sal_False; 1279 for (nQuery=0; nQuery<MAXQUERY && bSimpleQuery; nQuery++) 1280 if (pQueryParam->GetEntry(nQuery).bDoQuery) 1281 { 1282 // hier nicht auf EQUAL beschraenken 1283 // (auch bei ">1" soll der Spaltenkopf blau werden) 1284 1285 if (pQueryParam->GetEntry(nQuery).nField == nCol) 1286 bColumnFound = sal_True; 1287 if (nQuery > 0) 1288 if (pQueryParam->GetEntry(nQuery).eConnect != SC_AND) 1289 bSimpleQuery = sal_False; 1290 } 1291 1292 bool bArrowState = bSimpleQuery && bColumnFound; 1293 long nSizeX; 1294 long nSizeY; 1295 pViewData->GetMergeSizePixel( nCol, nRow, nSizeX, nSizeY ); 1296 Point aScrPos = pViewData->GetScrPos( nCol, nRow, eWhich ); 1297 1298 aCellBtn.setBoundingBox(aScrPos, Size(nSizeX-1, nSizeY-1), bLayoutRTL); 1299 aCellBtn.setPopupLeft(bLayoutRTL); // #i114944# AutoFilter button is left-aligned in RTL 1300 aCellBtn.setDrawBaseButton(false); 1301 aCellBtn.setDrawPopupButton(true); 1302 aCellBtn.setHasHiddenMember(bArrowState); 1303 aCellBtn.draw(); 1304 } 1305 } 1306 } 1307 1308 if ( pRowInfo[nArrY].bPushButton && pRowInfo[nArrY].bChanged ) 1309 { 1310 RowInfo* pThisRowInfo = &pRowInfo[nArrY]; 1311 nRow = pThisRowInfo->nRowNo; 1312 for (nCol=nX1; nCol<=nX2; nCol++) 1313 { 1314 CellInfo* pInfo = &pThisRowInfo->pCellInfo[nCol+1]; 1315 if ( pInfo->bPushButton && !pInfo->bHOverlapped && !pInfo->bVOverlapped ) 1316 { 1317 Point aScrPos = pViewData->GetScrPos( nCol, nRow, eWhich ); 1318 long nSizeX; 1319 long nSizeY; 1320 pViewData->GetMergeSizePixel( nCol, nRow, nSizeX, nSizeY ); 1321 long nPosX = aScrPos.X(); 1322 long nPosY = aScrPos.Y(); 1323 // bLayoutRTL is handled in setBoundingBox 1324 1325 String aStr; 1326 pDoc->GetString(nCol, nRow, nTab, aStr); 1327 aCellBtn.setText(aStr); 1328 aCellBtn.setBoundingBox(Point(nPosX, nPosY), Size(nSizeX-1, nSizeY-1), bLayoutRTL); 1329 aCellBtn.setPopupLeft(false); // DataPilot popup is always right-aligned for now 1330 aCellBtn.setDrawBaseButton(true); 1331 aCellBtn.setDrawPopupButton(pInfo->bPopupButton); 1332 aCellBtn.setHasHiddenMember(pInfo->bFilterActive); 1333 aCellBtn.draw(); 1334 } 1335 } 1336 } 1337 1338 if ( bListValButton && pRowInfo[nArrY].nRowNo == aListValPos.Row() && pRowInfo[nArrY].bChanged ) 1339 { 1340 Rectangle aRect = GetListValButtonRect( aListValPos ); 1341 aComboButton.SetPosPixel( aRect.TopLeft() ); 1342 aComboButton.SetSizePixel( aRect.GetSize() ); 1343 pContentDev->SetClipRegion( aRect ); 1344 aComboButton.Draw( sal_False, sal_False ); 1345 pContentDev->SetClipRegion(); // always called from Draw() without clip region 1346 aComboButton.SetPosPixel( aOldPos ); // restore old state 1347 aComboButton.SetSizePixel( aOldSize ); // for MouseUp/Down (AutoFilter) 1348 } 1349 } 1350 1351 delete pQueryParam; 1352 aComboButton.SetOutputDevice( this ); 1353 } 1354 1355 Rectangle ScGridWindow::GetListValButtonRect( const ScAddress& rButtonPos ) 1356 { 1357 ScDocument* pDoc = pViewData->GetDocument(); 1358 SCTAB nTab = pViewData->GetTabNo(); 1359 sal_Bool bLayoutRTL = pDoc->IsLayoutRTL( nTab ); 1360 long nLayoutSign = bLayoutRTL ? -1 : 1; 1361 1362 ScDDComboBoxButton aButton( this ); // for optimal size 1363 Size aBtnSize = aButton.GetSizePixel(); 1364 1365 SCCOL nCol = rButtonPos.Col(); 1366 SCROW nRow = rButtonPos.Row(); 1367 1368 long nCellSizeX; // width of this cell, including merged 1369 long nDummy; 1370 pViewData->GetMergeSizePixel( nCol, nRow, nCellSizeX, nDummy ); 1371 1372 // for height, only the cell's row is used, excluding merged cells 1373 long nCellSizeY = ScViewData::ToPixel( pDoc->GetRowHeight( nRow, nTab ), pViewData->GetPPTY() ); 1374 long nAvailable = nCellSizeX; 1375 1376 // left edge of next cell if there is a non-hidden next column 1377 SCCOL nNextCol = nCol + 1; 1378 const ScMergeAttr* pMerge = static_cast<const ScMergeAttr*>(pDoc->GetAttr( nCol,nRow,nTab, ATTR_MERGE )); 1379 if ( pMerge->GetColMerge() > 1 ) 1380 nNextCol = nCol + pMerge->GetColMerge(); // next cell after the merged area 1381 while ( nNextCol <= MAXCOL && pDoc->ColHidden(nNextCol, nTab) ) 1382 ++nNextCol; 1383 sal_Bool bNextCell = ( nNextCol <= MAXCOL ); 1384 if ( bNextCell ) 1385 nAvailable = ScViewData::ToPixel( pDoc->GetColWidth( nNextCol, nTab ), pViewData->GetPPTX() ); 1386 1387 if ( nAvailable < aBtnSize.Width() ) 1388 aBtnSize.Width() = nAvailable; 1389 if ( nCellSizeY < aBtnSize.Height() ) 1390 aBtnSize.Height() = nCellSizeY; 1391 1392 Point aPos = pViewData->GetScrPos( nCol, nRow, eWhich, sal_True ); 1393 aPos.X() += nCellSizeX * nLayoutSign; // start of next cell 1394 if (!bNextCell) 1395 aPos.X() -= aBtnSize.Width() * nLayoutSign; // right edge of cell if next cell not available 1396 aPos.Y() += nCellSizeY - aBtnSize.Height(); 1397 // X remains at the left edge 1398 1399 if ( bLayoutRTL ) 1400 aPos.X() -= aBtnSize.Width()-1; // align right edge of button with cell border 1401 1402 return Rectangle( aPos, aBtnSize ); 1403 } 1404 1405 sal_Bool ScGridWindow::IsAutoFilterActive( SCCOL nCol, SCROW nRow, SCTAB nTab ) 1406 { 1407 ScDocument* pDoc = pViewData->GetDocument(); 1408 ScDBData* pDBData = pDoc->GetDBAtCursor( nCol, nRow, nTab ); 1409 ScQueryParam aQueryParam; 1410 1411 if ( pDBData ) 1412 pDBData->GetQueryParam( aQueryParam ); 1413 else 1414 { 1415 DBG_ERROR("Auto-Filter-Button ohne DBData"); 1416 } 1417 1418 sal_Bool bSimpleQuery = sal_True; 1419 sal_Bool bColumnFound = sal_False; 1420 SCSIZE nQuery; 1421 1422 if ( !aQueryParam.bInplace ) 1423 bSimpleQuery = sal_False; 1424 1425 // aQueryParam kann nur MAXQUERY Eintraege enthalten 1426 1427 for ( nQuery=0; nQuery<MAXQUERY && bSimpleQuery; nQuery++ ) 1428 if ( aQueryParam.GetEntry(nQuery).bDoQuery ) 1429 { 1430 if (aQueryParam.GetEntry(nQuery).nField == nCol) 1431 bColumnFound = sal_True; 1432 1433 if (nQuery > 0) 1434 if (aQueryParam.GetEntry(nQuery).eConnect != SC_AND) 1435 bSimpleQuery = sal_False; 1436 } 1437 1438 return ( bSimpleQuery && bColumnFound ); 1439 } 1440 1441 void ScGridWindow::DrawComboButton( const Point& rCellPos, 1442 long nCellSizeX, 1443 long nCellSizeY, 1444 sal_Bool bArrowState, 1445 sal_Bool bBtnIn ) 1446 { 1447 Point aScrPos = rCellPos; 1448 Size aBtnSize = aComboButton.GetSizePixel(); 1449 1450 if ( nCellSizeX < aBtnSize.Width() || nCellSizeY < aBtnSize.Height() ) 1451 { 1452 if ( nCellSizeX < aBtnSize.Width() ) 1453 aBtnSize.Width() = nCellSizeX; 1454 1455 if ( nCellSizeY < aBtnSize.Height() ) 1456 aBtnSize.Height() = nCellSizeY; 1457 1458 aComboButton.SetSizePixel( aBtnSize ); 1459 } 1460 1461 sal_Bool bLayoutRTL = pViewData->GetDocument()->IsLayoutRTL( pViewData->GetTabNo() ); 1462 1463 if ( bLayoutRTL ) 1464 aScrPos.X() -= nCellSizeX - 1; 1465 else 1466 aScrPos.X() += nCellSizeX - aBtnSize.Width(); 1467 aScrPos.Y() += nCellSizeY - aBtnSize.Height(); 1468 1469 aComboButton.SetPosPixel( aScrPos ); 1470 1471 HideCursor(); 1472 aComboButton.Draw( bArrowState, bBtnIn ); 1473 ShowCursor(); 1474 } 1475 1476 void ScGridWindow::InvertSimple( SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2, 1477 sal_Bool bTestMerge, sal_Bool bRepeat ) 1478 { 1479 //! if INVERT_HIGHLIGHT swaps foreground and background (like on Mac), 1480 //! use INVERT_HIGHLIGHT only for cells that have no background color set 1481 //! (here and in ScOutputData::DrawMark) 1482 1483 PutInOrder( nX1, nX2 ); 1484 PutInOrder( nY1, nY2 ); 1485 1486 ScMarkData& rMark = pViewData->GetMarkData(); 1487 ScDocument* pDoc = pViewData->GetDocument(); 1488 SCTAB nTab = pViewData->GetTabNo(); 1489 1490 sal_Bool bLayoutRTL = pDoc->IsLayoutRTL( nTab ); 1491 long nLayoutSign = bLayoutRTL ? -1 : 1; 1492 1493 SCCOL nTestX2 = nX2; 1494 SCROW nTestY2 = nY2; 1495 if (bTestMerge) 1496 pDoc->ExtendMerge( nX1,nY1, nTestX2,nTestY2, nTab ); 1497 1498 SCCOL nPosX = pViewData->GetPosX( eHWhich ); 1499 SCROW nPosY = pViewData->GetPosY( eVWhich ); 1500 if (nTestX2 < nPosX || nTestY2 < nPosY) 1501 return; // unsichtbar 1502 SCCOL nRealX1 = nX1; 1503 if (nX1 < nPosX) 1504 nX1 = nPosX; 1505 if (nY1 < nPosY) 1506 nY1 = nPosY; 1507 1508 SCCOL nXRight = nPosX + pViewData->VisibleCellsX(eHWhich); 1509 if (nXRight > MAXCOL) nXRight = MAXCOL; 1510 SCROW nYBottom = nPosY + pViewData->VisibleCellsY(eVWhich); 1511 if (nYBottom > MAXROW) nYBottom = MAXROW; 1512 1513 if (nX1 > nXRight || nY1 > nYBottom) 1514 return; // unsichtbar 1515 if (nX2 > nXRight) nX2 = nXRight; 1516 if (nY2 > nYBottom) nY2 = nYBottom; 1517 1518 MapMode aOld = GetMapMode(); SetMapMode(MAP_PIXEL); // erst nach den return's !!! 1519 1520 double nPPTX = pViewData->GetPPTX(); 1521 double nPPTY = pViewData->GetPPTY(); 1522 1523 ScInvertMerger aInvert( this ); 1524 1525 Point aScrPos = pViewData->GetScrPos( nX1, nY1, eWhich ); 1526 long nScrY = aScrPos.Y(); 1527 sal_Bool bWasHidden = sal_False; 1528 for (SCROW nY=nY1; nY<=nY2; nY++) 1529 { 1530 sal_Bool bFirstRow = ( nY == nPosY ); // first visible row? 1531 sal_Bool bDoHidden = sal_False; // versteckte nachholen ? 1532 sal_uInt16 nHeightTwips = pDoc->GetRowHeight( nY,nTab ); 1533 sal_Bool bDoRow = ( nHeightTwips != 0 ); 1534 if (bDoRow) 1535 { 1536 if (bTestMerge) 1537 if (bWasHidden) // auf versteckte zusammengefasste testen 1538 { 1539 // --nY; // nY geaendert -> vorherige zeichnen 1540 bDoHidden = sal_True; 1541 bDoRow = sal_True; 1542 } 1543 1544 bWasHidden = sal_False; 1545 } 1546 else 1547 { 1548 bWasHidden = sal_True; 1549 if (bTestMerge) 1550 if (nY==nY2) 1551 bDoRow = sal_True; // letzte Zeile aus Block 1552 } 1553 1554 if ( bDoRow ) 1555 { 1556 SCCOL nLoopEndX = nX2; 1557 if (nX2 < nX1) // Rest von zusammengefasst 1558 { 1559 SCCOL nStartX = nX1; 1560 while ( ((const ScMergeFlagAttr*)pDoc-> 1561 GetAttr(nStartX,nY,nTab,ATTR_MERGE_FLAG))->IsHorOverlapped() ) 1562 --nStartX; 1563 if (nStartX <= nX2) 1564 nLoopEndX = nX1; 1565 } 1566 1567 long nEndY = nScrY + ScViewData::ToPixel( nHeightTwips, nPPTY ) - 1; 1568 long nScrX = aScrPos.X(); 1569 for (SCCOL nX=nX1; nX<=nLoopEndX; nX++) 1570 { 1571 long nWidth = ScViewData::ToPixel( pDoc->GetColWidth( nX,nTab ), nPPTX ); 1572 if ( nWidth > 0 ) 1573 { 1574 long nEndX = nScrX + ( nWidth - 1 ) * nLayoutSign; 1575 if (bTestMerge) 1576 { 1577 SCROW nThisY = nY; 1578 const ScPatternAttr* pPattern = pDoc->GetPattern( nX, nY, nTab ); 1579 const ScMergeFlagAttr* pMergeFlag = (const ScMergeFlagAttr*) &pPattern-> 1580 GetItem(ATTR_MERGE_FLAG); 1581 if ( pMergeFlag->IsVerOverlapped() && ( bDoHidden || bFirstRow ) ) 1582 { 1583 while ( pMergeFlag->IsVerOverlapped() && nThisY > 0 && 1584 (pDoc->RowHidden(nThisY-1, nTab) || bFirstRow) ) 1585 { 1586 --nThisY; 1587 pPattern = pDoc->GetPattern( nX, nThisY, nTab ); 1588 pMergeFlag = (const ScMergeFlagAttr*) &pPattern->GetItem(ATTR_MERGE_FLAG); 1589 } 1590 } 1591 1592 // nur Rest von zusammengefasster zu sehen ? 1593 SCCOL nThisX = nX; 1594 if ( pMergeFlag->IsHorOverlapped() && nX == nPosX && nX > nRealX1 ) 1595 { 1596 while ( pMergeFlag->IsHorOverlapped() ) 1597 { 1598 --nThisX; 1599 pPattern = pDoc->GetPattern( nThisX, nThisY, nTab ); 1600 pMergeFlag = (const ScMergeFlagAttr*) &pPattern->GetItem(ATTR_MERGE_FLAG); 1601 } 1602 } 1603 1604 if ( rMark.IsCellMarked( nThisX, nThisY, sal_True ) == bRepeat ) 1605 { 1606 if ( !pMergeFlag->IsOverlapped() ) 1607 { 1608 ScMergeAttr* pMerge = (ScMergeAttr*)&pPattern->GetItem(ATTR_MERGE); 1609 if (pMerge->GetColMerge() > 0 || pMerge->GetRowMerge() > 0) 1610 { 1611 Point aEndPos = pViewData->GetScrPos( 1612 nThisX + pMerge->GetColMerge(), 1613 nThisY + pMerge->GetRowMerge(), eWhich ); 1614 if ( aEndPos.X() * nLayoutSign > nScrX * nLayoutSign && aEndPos.Y() > nScrY ) 1615 { 1616 aInvert.AddRect( Rectangle( nScrX,nScrY, 1617 aEndPos.X()-nLayoutSign,aEndPos.Y()-1 ) ); 1618 } 1619 } 1620 else if ( nEndX * nLayoutSign >= nScrX * nLayoutSign && nEndY >= nScrY ) 1621 { 1622 aInvert.AddRect( Rectangle( nScrX,nScrY,nEndX,nEndY ) ); 1623 } 1624 } 1625 } 1626 } 1627 else // !bTestMerge 1628 { 1629 if ( rMark.IsCellMarked( nX, nY, sal_True ) == bRepeat && 1630 nEndX * nLayoutSign >= nScrX * nLayoutSign && nEndY >= nScrY ) 1631 { 1632 aInvert.AddRect( Rectangle( nScrX,nScrY,nEndX,nEndY ) ); 1633 } 1634 } 1635 1636 nScrX = nEndX + nLayoutSign; 1637 } 1638 } 1639 nScrY = nEndY + 1; 1640 } 1641 } 1642 1643 aInvert.Flush(); // before restoring MapMode 1644 1645 SetMapMode(aOld); 1646 1647 CheckInverted(); 1648 } 1649 1650 void ScGridWindow::GetSelectionRects( ::std::vector< Rectangle >& rPixelRects ) 1651 { 1652 // transformed from ScGridWindow::InvertSimple 1653 1654 // ScMarkData& rMark = pViewData->GetMarkData(); 1655 ScMarkData aMultiMark( pViewData->GetMarkData() ); 1656 aMultiMark.SetMarking( sal_False ); 1657 aMultiMark.MarkToMulti(); 1658 1659 ScDocument* pDoc = pViewData->GetDocument(); 1660 SCTAB nTab = pViewData->GetTabNo(); 1661 1662 sal_Bool bLayoutRTL = pDoc->IsLayoutRTL( nTab ); 1663 long nLayoutSign = bLayoutRTL ? -1 : 1; 1664 1665 if ( !aMultiMark.IsMultiMarked() ) 1666 return; 1667 1668 ScRange aMultiRange; 1669 aMultiMark.GetMultiMarkArea( aMultiRange ); 1670 SCCOL nX1 = aMultiRange.aStart.Col(); 1671 SCROW nY1 = aMultiRange.aStart.Row(); 1672 SCCOL nX2 = aMultiRange.aEnd.Col(); 1673 SCROW nY2 = aMultiRange.aEnd.Row(); 1674 1675 PutInOrder( nX1, nX2 ); 1676 PutInOrder( nY1, nY2 ); 1677 1678 sal_Bool bTestMerge = sal_True; 1679 sal_Bool bRepeat = sal_True; 1680 1681 SCCOL nTestX2 = nX2; 1682 SCROW nTestY2 = nY2; 1683 if (bTestMerge) 1684 pDoc->ExtendMerge( nX1,nY1, nTestX2,nTestY2, nTab ); 1685 1686 SCCOL nPosX = pViewData->GetPosX( eHWhich ); 1687 SCROW nPosY = pViewData->GetPosY( eVWhich ); 1688 if (nTestX2 < nPosX || nTestY2 < nPosY) 1689 return; // unsichtbar 1690 SCCOL nRealX1 = nX1; 1691 if (nX1 < nPosX) 1692 nX1 = nPosX; 1693 if (nY1 < nPosY) 1694 nY1 = nPosY; 1695 1696 SCCOL nXRight = nPosX + pViewData->VisibleCellsX(eHWhich); 1697 if (nXRight > MAXCOL) nXRight = MAXCOL; 1698 SCROW nYBottom = nPosY + pViewData->VisibleCellsY(eVWhich); 1699 if (nYBottom > MAXROW) nYBottom = MAXROW; 1700 1701 if (nX1 > nXRight || nY1 > nYBottom) 1702 return; // unsichtbar 1703 if (nX2 > nXRight) nX2 = nXRight; 1704 if (nY2 > nYBottom) nY2 = nYBottom; 1705 1706 // MapMode aOld = GetMapMode(); SetMapMode(MAP_PIXEL); // erst nach den return's !!! 1707 1708 double nPPTX = pViewData->GetPPTX(); 1709 double nPPTY = pViewData->GetPPTY(); 1710 1711 ScInvertMerger aInvert( &rPixelRects ); 1712 1713 Point aScrPos = pViewData->GetScrPos( nX1, nY1, eWhich ); 1714 long nScrY = aScrPos.Y(); 1715 sal_Bool bWasHidden = sal_False; 1716 for (SCROW nY=nY1; nY<=nY2; nY++) 1717 { 1718 sal_Bool bFirstRow = ( nY == nPosY ); // first visible row? 1719 sal_Bool bDoHidden = sal_False; // versteckte nachholen ? 1720 sal_uInt16 nHeightTwips = pDoc->GetRowHeight( nY,nTab ); 1721 sal_Bool bDoRow = ( nHeightTwips != 0 ); 1722 if (bDoRow) 1723 { 1724 if (bTestMerge) 1725 if (bWasHidden) // auf versteckte zusammengefasste testen 1726 { 1727 bDoHidden = sal_True; 1728 bDoRow = sal_True; 1729 } 1730 1731 bWasHidden = sal_False; 1732 } 1733 else 1734 { 1735 bWasHidden = sal_True; 1736 if (bTestMerge) 1737 if (nY==nY2) 1738 bDoRow = sal_True; // letzte Zeile aus Block 1739 } 1740 1741 if ( bDoRow ) 1742 { 1743 SCCOL nLoopEndX = nX2; 1744 if (nX2 < nX1) // Rest von zusammengefasst 1745 { 1746 SCCOL nStartX = nX1; 1747 while ( ((const ScMergeFlagAttr*)pDoc-> 1748 GetAttr(nStartX,nY,nTab,ATTR_MERGE_FLAG))->IsHorOverlapped() ) 1749 --nStartX; 1750 if (nStartX <= nX2) 1751 nLoopEndX = nX1; 1752 } 1753 1754 long nEndY = nScrY + ScViewData::ToPixel( nHeightTwips, nPPTY ) - 1; 1755 long nScrX = aScrPos.X(); 1756 for (SCCOL nX=nX1; nX<=nLoopEndX; nX++) 1757 { 1758 long nWidth = ScViewData::ToPixel( pDoc->GetColWidth( nX,nTab ), nPPTX ); 1759 if ( nWidth > 0 ) 1760 { 1761 long nEndX = nScrX + ( nWidth - 1 ) * nLayoutSign; 1762 if (bTestMerge) 1763 { 1764 SCROW nThisY = nY; 1765 const ScPatternAttr* pPattern = pDoc->GetPattern( nX, nY, nTab ); 1766 const ScMergeFlagAttr* pMergeFlag = (const ScMergeFlagAttr*) &pPattern-> 1767 GetItem(ATTR_MERGE_FLAG); 1768 if ( pMergeFlag->IsVerOverlapped() && ( bDoHidden || bFirstRow ) ) 1769 { 1770 while ( pMergeFlag->IsVerOverlapped() && nThisY > 0 && 1771 (pDoc->RowHidden(nThisY-1, nTab) || bFirstRow) ) 1772 { 1773 --nThisY; 1774 pPattern = pDoc->GetPattern( nX, nThisY, nTab ); 1775 pMergeFlag = (const ScMergeFlagAttr*) &pPattern->GetItem(ATTR_MERGE_FLAG); 1776 } 1777 } 1778 1779 // nur Rest von zusammengefasster zu sehen ? 1780 SCCOL nThisX = nX; 1781 if ( pMergeFlag->IsHorOverlapped() && nX == nPosX && nX > nRealX1 ) 1782 { 1783 while ( pMergeFlag->IsHorOverlapped() ) 1784 { 1785 --nThisX; 1786 pPattern = pDoc->GetPattern( nThisX, nThisY, nTab ); 1787 pMergeFlag = (const ScMergeFlagAttr*) &pPattern->GetItem(ATTR_MERGE_FLAG); 1788 } 1789 } 1790 1791 if ( aMultiMark.IsCellMarked( nThisX, nThisY, sal_True ) == bRepeat ) 1792 { 1793 if ( !pMergeFlag->IsOverlapped() ) 1794 { 1795 ScMergeAttr* pMerge = (ScMergeAttr*)&pPattern->GetItem(ATTR_MERGE); 1796 if (pMerge->GetColMerge() > 0 || pMerge->GetRowMerge() > 0) 1797 { 1798 Point aEndPos = pViewData->GetScrPos( 1799 nThisX + pMerge->GetColMerge(), 1800 nThisY + pMerge->GetRowMerge(), eWhich ); 1801 if ( aEndPos.X() * nLayoutSign > nScrX * nLayoutSign && aEndPos.Y() > nScrY ) 1802 { 1803 aInvert.AddRect( Rectangle( nScrX,nScrY, 1804 aEndPos.X()-nLayoutSign,aEndPos.Y()-1 ) ); 1805 } 1806 } 1807 else if ( nEndX * nLayoutSign >= nScrX * nLayoutSign && nEndY >= nScrY ) 1808 { 1809 aInvert.AddRect( Rectangle( nScrX,nScrY,nEndX,nEndY ) ); 1810 } 1811 } 1812 } 1813 } 1814 else // !bTestMerge 1815 { 1816 if ( aMultiMark.IsCellMarked( nX, nY, sal_True ) == bRepeat && 1817 nEndX * nLayoutSign >= nScrX * nLayoutSign && nEndY >= nScrY ) 1818 { 1819 aInvert.AddRect( Rectangle( nScrX,nScrY,nEndX,nEndY ) ); 1820 } 1821 } 1822 1823 nScrX = nEndX + nLayoutSign; 1824 } 1825 } 1826 nScrY = nEndY + 1; 1827 } 1828 } 1829 1830 // aInvert.Flush(); // before restoring MapMode 1831 } 1832 1833 // ------------------------------------------------------------------------- 1834 1835 //UNUSED2008-05 void ScGridWindow::DrawDragRect( SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2 ) 1836 //UNUSED2008-05 { 1837 //UNUSED2008-05 if ( nX2 < pViewData->GetPosX(eHWhich) || nY2 < pViewData->GetPosY(eVWhich) ) 1838 //UNUSED2008-05 return; 1839 //UNUSED2008-05 1840 //UNUSED2008-05 Update(); // wegen XOR 1841 //UNUSED2008-05 1842 //UNUSED2008-05 MapMode aOld = GetMapMode(); SetMapMode(MAP_PIXEL); 1843 //UNUSED2008-05 1844 //UNUSED2008-05 SCTAB nTab = pViewData->GetTabNo(); 1845 //UNUSED2008-05 1846 //UNUSED2008-05 SCCOL nPosX = pViewData->GetPosX(WhichH(eWhich)); 1847 //UNUSED2008-05 SCROW nPosY = pViewData->GetPosY(WhichV(eWhich)); 1848 //UNUSED2008-05 if (nX1 < nPosX) nX1 = nPosX; 1849 //UNUSED2008-05 if (nX2 < nPosX) nX2 = nPosX; 1850 //UNUSED2008-05 if (nY1 < nPosY) nY1 = nPosY; 1851 //UNUSED2008-05 if (nY2 < nPosY) nY2 = nPosY; 1852 //UNUSED2008-05 1853 //UNUSED2008-05 Point aScrPos( pViewData->GetScrPos( nX1, nY1, eWhich ) ); 1854 //UNUSED2008-05 1855 //UNUSED2008-05 long nSizeXPix=0; 1856 //UNUSED2008-05 long nSizeYPix=0; 1857 //UNUSED2008-05 ScDocument* pDoc = pViewData->GetDocument(); 1858 //UNUSED2008-05 double nPPTX = pViewData->GetPPTX(); 1859 //UNUSED2008-05 double nPPTY = pViewData->GetPPTY(); 1860 //UNUSED2008-05 SCCOLROW i; 1861 //UNUSED2008-05 1862 //UNUSED2008-05 sal_Bool bLayoutRTL = pDoc->IsLayoutRTL( nTab ); 1863 //UNUSED2008-05 long nLayoutSign = bLayoutRTL ? -1 : 1; 1864 //UNUSED2008-05 1865 //UNUSED2008-05 if (ValidCol(nX2) && nX2>=nX1) 1866 //UNUSED2008-05 for (i=nX1; i<=nX2; i++) 1867 //UNUSED2008-05 nSizeXPix += ScViewData::ToPixel( pDoc->GetColWidth( static_cast<SCCOL>(i), nTab ), nPPTX ); 1868 //UNUSED2008-05 else 1869 //UNUSED2008-05 { 1870 //UNUSED2008-05 aScrPos.X() -= nLayoutSign; 1871 //UNUSED2008-05 nSizeXPix += 2; 1872 //UNUSED2008-05 } 1873 //UNUSED2008-05 1874 //UNUSED2008-05 if (ValidRow(nY2) && nY2>=nY1) 1875 //UNUSED2008-05 for (i=nY1; i<=nY2; i++) 1876 //UNUSED2008-05 nSizeYPix += ScViewData::ToPixel( pDoc->GetRowHeight( i, nTab ), nPPTY ); 1877 //UNUSED2008-05 else 1878 //UNUSED2008-05 { 1879 //UNUSED2008-05 aScrPos.Y() -= 1; 1880 //UNUSED2008-05 nSizeYPix += 2; 1881 //UNUSED2008-05 } 1882 //UNUSED2008-05 1883 //UNUSED2008-05 aScrPos.X() -= 2 * nLayoutSign; 1884 //UNUSED2008-05 aScrPos.Y() -= 2; 1885 //UNUSED2008-05 // Rectangle aRect( aScrPos, Size( nSizeXPix + 3, nSizeYPix + 3 ) ); 1886 //UNUSED2008-05 Rectangle aRect( aScrPos.X(), aScrPos.Y(), 1887 //UNUSED2008-05 aScrPos.X() + ( nSizeXPix + 2 ) * nLayoutSign, aScrPos.Y() + nSizeYPix + 2 ); 1888 //UNUSED2008-05 if ( bLayoutRTL ) 1889 //UNUSED2008-05 { 1890 //UNUSED2008-05 aRect.Left() = aRect.Right(); // end position is left 1891 //UNUSED2008-05 aRect.Right() = aScrPos.X(); 1892 //UNUSED2008-05 } 1893 //UNUSED2008-05 1894 //UNUSED2008-05 Invert(Rectangle( aRect.Left(), aRect.Top(), aRect.Left()+2, aRect.Bottom() )); 1895 //UNUSED2008-05 Invert(Rectangle( aRect.Right()-2, aRect.Top(), aRect.Right(), aRect.Bottom() )); 1896 //UNUSED2008-05 Invert(Rectangle( aRect.Left()+3, aRect.Top(), aRect.Right()-3, aRect.Top()+2 )); 1897 //UNUSED2008-05 Invert(Rectangle( aRect.Left()+3, aRect.Bottom()-2, aRect.Right()-3, aRect.Bottom() )); 1898 //UNUSED2008-05 1899 //UNUSED2008-05 SetMapMode(aOld); 1900 //UNUSED2008-05 } 1901 1902 // ------------------------------------------------------------------------- 1903 1904 void ScGridWindow::DrawCursor() 1905 { 1906 // #114409# 1907 // SCTAB nTab = pViewData->GetTabNo(); 1908 // SCCOL nX = pViewData->GetCurX(); 1909 // SCROW nY = pViewData->GetCurY(); 1910 // 1911 // // in verdeckten Zellen nicht zeichnen 1912 // 1913 // ScDocument* pDoc = pViewData->GetDocument(); 1914 // const ScPatternAttr* pPattern = pDoc->GetPattern(nX,nY,nTab); 1915 // const ScMergeFlagAttr& rMerge = (const ScMergeFlagAttr&) pPattern->GetItem(ATTR_MERGE_FLAG); 1916 // if (rMerge.IsOverlapped()) 1917 // return; 1918 // 1919 // // links/oben ausserhalb des Bildschirms ? 1920 // 1921 // sal_Bool bVis = ( nX>=pViewData->GetPosX(eHWhich) && nY>=pViewData->GetPosY(eVWhich) ); 1922 // if (!bVis) 1923 // { 1924 // SCCOL nEndX = nX; 1925 // SCROW nEndY = nY; 1926 // ScDocument* pDoc = pViewData->GetDocument(); 1927 // const ScMergeAttr& rMerge = (const ScMergeAttr&) pPattern->GetItem(ATTR_MERGE); 1928 // if (rMerge.GetColMerge() > 1) 1929 // nEndX += rMerge.GetColMerge()-1; 1930 // if (rMerge.GetRowMerge() > 1) 1931 // nEndY += rMerge.GetRowMerge()-1; 1932 // bVis = ( nEndX>=pViewData->GetPosX(eHWhich) && nEndY>=pViewData->GetPosY(eVWhich) ); 1933 // } 1934 // 1935 // if ( bVis ) 1936 // { 1937 // // hier kein Update, da aus Paint gerufen und laut Zaehler Cursor schon da 1938 // // wenn Update noetig, dann bei Hide/Showcursor vor dem Hoch-/Runterzaehlen 1939 // 1940 // MapMode aOld = GetMapMode(); SetMapMode(MAP_PIXEL); 1941 // 1942 // Point aScrPos = pViewData->GetScrPos( nX, nY, eWhich, sal_True ); 1943 // sal_Bool bLayoutRTL = pDoc->IsLayoutRTL( nTab ); 1944 // 1945 // // completely right of/below the screen? 1946 // // (test with logical start position in aScrPos) 1947 // sal_Bool bMaybeVisible; 1948 // if ( bLayoutRTL ) 1949 // bMaybeVisible = ( aScrPos.X() >= -2 && aScrPos.Y() >= -2 ); 1950 // else 1951 // { 1952 // Size aOutSize = GetOutputSizePixel(); 1953 // bMaybeVisible = ( aScrPos.X() <= aOutSize.Width() + 2 && aScrPos.Y() <= aOutSize.Height() + 2 ); 1954 // } 1955 // if ( bMaybeVisible ) 1956 // { 1957 // long nSizeXPix; 1958 // long nSizeYPix; 1959 // pViewData->GetMergeSizePixel( nX, nY, nSizeXPix, nSizeYPix ); 1960 // 1961 // if ( bLayoutRTL ) 1962 // aScrPos.X() -= nSizeXPix - 2; // move instead of mirroring 1963 // 1964 // sal_Bool bFix = ( pViewData->GetHSplitMode() == SC_SPLIT_FIX || 1965 // pViewData->GetVSplitMode() == SC_SPLIT_FIX ); 1966 // if ( pViewData->GetActivePart()==eWhich || bFix ) 1967 // { 1968 // // old UNX version with two Invert calls causes flicker. 1969 // // if optimization is needed, a new flag should be added 1970 // // to InvertTracking 1971 // 1972 // aScrPos.X() -= 2; 1973 // aScrPos.Y() -= 2; 1974 // Rectangle aRect( aScrPos, Size( nSizeXPix + 3, nSizeYPix + 3 ) ); 1975 // 1976 // Invert(Rectangle( aRect.Left(), aRect.Top(), aRect.Left()+2, aRect.Bottom() )); 1977 // Invert(Rectangle( aRect.Right()-2, aRect.Top(), aRect.Right(), aRect.Bottom() )); 1978 // Invert(Rectangle( aRect.Left()+3, aRect.Top(), aRect.Right()-3, aRect.Top()+2 )); 1979 // Invert(Rectangle( aRect.Left()+3, aRect.Bottom()-2, aRect.Right()-3, aRect.Bottom() )); 1980 // } 1981 // else 1982 // { 1983 // Rectangle aRect( aScrPos, Size( nSizeXPix - 1, nSizeYPix - 1 ) ); 1984 // Invert( aRect ); 1985 // } 1986 // } 1987 // 1988 // SetMapMode(aOld); 1989 // } 1990 } 1991 1992 // AutoFill-Anfasser: 1993 1994 void ScGridWindow::DrawAutoFillMark() 1995 { 1996 // #114409# 1997 // if ( bAutoMarkVisible && aAutoMarkPos.Tab() == pViewData->GetTabNo() ) 1998 // { 1999 // SCCOL nX = aAutoMarkPos.Col(); 2000 // SCROW nY = aAutoMarkPos.Row(); 2001 // SCTAB nTab = pViewData->GetTabNo(); 2002 // ScDocument* pDoc = pViewData->GetDocument(); 2003 // sal_Bool bLayoutRTL = pDoc->IsLayoutRTL( nTab ); 2004 // 2005 // Point aFillPos = pViewData->GetScrPos( nX, nY, eWhich, sal_True ); 2006 // long nSizeXPix; 2007 // long nSizeYPix; 2008 // pViewData->GetMergeSizePixel( nX, nY, nSizeXPix, nSizeYPix ); 2009 // if ( bLayoutRTL ) 2010 // aFillPos.X() -= nSizeXPix + 3; 2011 // else 2012 // aFillPos.X() += nSizeXPix - 2; 2013 // 2014 // aFillPos.Y() += nSizeYPix; 2015 // aFillPos.Y() -= 2; 2016 // Rectangle aFillRect( aFillPos, Size(6,6) ); 2017 // // Anfasser von Zeichenobjekten sind 7*7 2018 // 2019 // MapMode aOld = GetMapMode(); SetMapMode(MAP_PIXEL); 2020 // Invert( aFillRect ); 2021 // SetMapMode(aOld); 2022 // } 2023 } 2024 2025 // ------------------------------------------------------------------------- 2026 2027 void ScGridWindow::DataChanged( const DataChangedEvent& rDCEvt ) 2028 { 2029 Window::DataChanged(rDCEvt); 2030 2031 if ( (rDCEvt.GetType() == DATACHANGED_PRINTER) || 2032 (rDCEvt.GetType() == DATACHANGED_DISPLAY) || 2033 (rDCEvt.GetType() == DATACHANGED_FONTS) || 2034 (rDCEvt.GetType() == DATACHANGED_FONTSUBSTITUTION) || 2035 ((rDCEvt.GetType() == DATACHANGED_SETTINGS) && 2036 (rDCEvt.GetFlags() & SETTINGS_STYLE)) ) 2037 { 2038 if ( rDCEvt.GetType() == DATACHANGED_FONTS && eWhich == pViewData->GetActivePart() ) 2039 pViewData->GetDocShell()->UpdateFontList(); 2040 2041 if ( (rDCEvt.GetType() == DATACHANGED_SETTINGS) && 2042 (rDCEvt.GetFlags() & SETTINGS_STYLE) ) 2043 { 2044 if ( eWhich == pViewData->GetActivePart() ) // only once for the view 2045 { 2046 ScTabView* pView = pViewData->GetView(); 2047 2048 // update scale in case the UI ScreenZoom has changed 2049 ScGlobal::UpdatePPT(this); 2050 pView->RecalcPPT(); 2051 2052 // RepeatResize in case scroll bar sizes have changed 2053 pView->RepeatResize(); 2054 pView->UpdateAllOverlays(); 2055 2056 // invalidate cell attribs in input handler, in case the 2057 // EditEngine BackgroundColor has to be changed 2058 if ( pViewData->IsActive() ) 2059 { 2060 ScInputHandler* pHdl = SC_MOD()->GetInputHdl(); 2061 if (pHdl) 2062 pHdl->ForgetLastPattern(); 2063 } 2064 } 2065 } 2066 2067 Invalidate(); 2068 } 2069 } 2070 2071 2072 2073 2074