1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 5 * This Source Code Form is subject to the terms of the Mozilla Public 6 * License, v. 2.0. If a copy of the MPL was not distributed with this 7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 * 9 * This file incorporates work covered by the following license notice: 10 * 11 * Licensed to the Apache Software Foundation (ASF) under one or more 12 * contributor license agreements. See the NOTICE file distributed 13 * with this work for additional information regarding copyright 14 * ownership. The ASF licenses this file to you under the Apache 15 * License, Version 2.0 (the "License"); you may not use this file 16 * except in compliance with the License. You may obtain a copy of 17 * the License at http://www.apache.org/licenses/LICENSE-2.0 . 18 */ 19 20 #include <com/sun/star/i18n/WordType.hpp> 21 #include <editeng/editdata.hxx> 22 #include <editeng/editeng.hxx> 23 #include <editeng/editobj.hxx> 24 #include <editeng/editstat.hxx> 25 #include <editeng/outlobj.hxx> 26 #include <editeng/unotext.hxx> 27 #include <o3tl/deleter.hxx> 28 #include <svl/itemiter.hxx> 29 #include <svl/style.hxx> 30 #include <svl/whiter.hxx> 31 #include <svtools/accessibilityoptions.hxx> 32 #include <svx/sdtfchim.hxx> 33 #include <svx/selectioncontroller.hxx> 34 #include <svx/svdedxv.hxx> 35 #include <svx/svdetc.hxx> 36 #include <svx/svdotable.hxx> 37 #include <svx/svdotext.hxx> 38 #include <svx/svdoutl.hxx> 39 #include <svx/svdpage.hxx> 40 #include <svx/svdpagv.hxx> 41 #include <svx/svdundo.hxx> 42 #include <vcl/canvastools.hxx> 43 #include <vcl/commandevent.hxx> 44 #include <vcl/cursor.hxx> 45 #include <vcl/weld.hxx> 46 #include <vcl/window.hxx> 47 #include <comphelper/lok.hxx> 48 #include <drawinglayer/processor2d/baseprocessor2d.hxx> 49 #include <drawinglayer/processor2d/processor2dtools.hxx> 50 #include <editeng/outliner.hxx> 51 #include <sal/log.hxx> 52 #include <sdr/overlay/overlaytools.hxx> 53 #include <sfx2/viewsh.hxx> 54 #include <svx/dialmgr.hxx> 55 #include <svx/sdr/overlay/overlaymanager.hxx> 56 #include <svx/sdr/overlay/overlayselection.hxx> 57 #include <svx/sdr/table/tablecontroller.hxx> 58 #include <svx/sdrpagewindow.hxx> 59 #include <svx/sdrpaintwindow.hxx> 60 #include <svx/sdrundomanager.hxx> 61 #include <svx/strings.hrc> 62 #include <svx/svdviter.hxx> 63 #include <svtools/optionsdrawinglayer.hxx> 64 #include <textchain.hxx> 65 #include <textchaincursor.hxx> 66 #include <tools/debug.hxx> 67 #include <vcl/svapp.hxx> 68 69 #include <memory> 70 71 SdrObjEditView::SdrObjEditView(SdrModel& rSdrModel, OutputDevice* pOut) 72 : SdrGlueEditView(rSdrModel, pOut) 73 , mpTextEditPV(nullptr) 74 , mpTextEditOutlinerView(nullptr) 75 , mpTextEditWin(nullptr) 76 , pTextEditCursorBuffer(nullptr) 77 , pMacroObj(nullptr) 78 , pMacroPV(nullptr) 79 , pMacroWin(nullptr) 80 , nMacroTol(0) 81 , mbTextEditDontDelete(false) 82 , mbTextEditOnlyOneView(false) 83 , mbTextEditNewObj(false) 84 , mbQuickTextEditMode(true) 85 , mbMacroDown(false) 86 , mpOldTextEditUndoManager(nullptr) 87 { 88 } 89 90 SdrObjEditView::~SdrObjEditView() 91 { 92 mpTextEditWin = nullptr; // so there's no ShowCursor in SdrEndTextEdit 93 assert(!IsTextEdit()); 94 if (IsTextEdit()) 95 suppress_fun_call_w_exception(SdrEndTextEdit()); 96 mpTextEditOutliner.reset(); 97 assert(nullptr == mpOldTextEditUndoManager); // should have been reset 98 } 99 100 bool SdrObjEditView::IsAction() const { return IsMacroObj() || SdrGlueEditView::IsAction(); } 101 102 void SdrObjEditView::MovAction(const Point& rPnt) 103 { 104 if (IsMacroObj()) 105 MovMacroObj(rPnt); 106 SdrGlueEditView::MovAction(rPnt); 107 } 108 109 void SdrObjEditView::EndAction() 110 { 111 if (IsMacroObj()) 112 EndMacroObj(); 113 SdrGlueEditView::EndAction(); 114 } 115 116 void SdrObjEditView::BckAction() 117 { 118 BrkMacroObj(); 119 SdrGlueEditView::BckAction(); 120 } 121 122 void SdrObjEditView::BrkAction() 123 { 124 BrkMacroObj(); 125 SdrGlueEditView::BrkAction(); 126 } 127 128 SdrPageView* SdrObjEditView::ShowSdrPage(SdrPage* pPage) 129 { 130 SdrPageView* pPageView = SdrGlueEditView::ShowSdrPage(pPage); 131 132 if (comphelper::LibreOfficeKit::isActive() && pPageView) 133 { 134 // Check if other views have an active text edit on the same page as 135 // this one. 136 SdrViewIter aIter(pPageView->GetPage()); 137 for (SdrView* pView = aIter.FirstView(); pView; pView = aIter.NextView()) 138 { 139 if (pView == this || !pView->IsTextEdit()) 140 continue; 141 142 OutputDevice* pOutDev = GetFirstOutputDevice(); 143 if (!pOutDev || pOutDev->GetOutDevType() != OUTDEV_WINDOW) 144 continue; 145 146 // Found one, so create an outliner view, to get invalidations when 147 // the text edit changes. 148 // Call GetSfxViewShell() to make sure ImpMakeOutlinerView() 149 // registers the view shell of this draw view, and not the view 150 // shell of pView. 151 OutlinerView* pOutlinerView 152 = pView->ImpMakeOutlinerView(pOutDev->GetOwnerWindow(), nullptr, GetSfxViewShell()); 153 pOutlinerView->HideCursor(); 154 pView->GetTextEditOutliner()->InsertView(pOutlinerView); 155 } 156 } 157 158 return pPageView; 159 } 160 161 namespace 162 { 163 /// Removes outliner views registered in other draw views that use pOutputDevice. 164 void lcl_RemoveTextEditOutlinerViews(SdrObjEditView const* pThis, SdrPageView const* pPageView, 165 OutputDevice const* pOutputDevice) 166 { 167 if (!comphelper::LibreOfficeKit::isActive()) 168 return; 169 170 if (!pPageView) 171 return; 172 173 if (!pOutputDevice || pOutputDevice->GetOutDevType() != OUTDEV_WINDOW) 174 return; 175 176 SdrViewIter aIter(pPageView->GetPage()); 177 for (SdrView* pView = aIter.FirstView(); pView; pView = aIter.NextView()) 178 { 179 if (pView == pThis || !pView->IsTextEdit()) 180 continue; 181 182 SdrOutliner* pOutliner = pView->GetTextEditOutliner(); 183 for (size_t nView = 0; nView < pOutliner->GetViewCount(); ++nView) 184 { 185 OutlinerView* pOutlinerView = pOutliner->GetView(nView); 186 if (pOutlinerView->GetWindow()->GetOutDev() != pOutputDevice) 187 continue; 188 189 pOutliner->RemoveView(pOutlinerView); 190 delete pOutlinerView; 191 } 192 } 193 } 194 } 195 196 void SdrObjEditView::HideSdrPage() 197 { 198 lcl_RemoveTextEditOutlinerViews(this, GetSdrPageView(), GetFirstOutputDevice()); 199 200 if (mpTextEditPV == GetSdrPageView()) 201 { 202 // HideSdrPage() will clear mpPageView, avoid a dangling pointer. 203 mpTextEditPV = nullptr; 204 } 205 206 SdrGlueEditView::HideSdrPage(); 207 } 208 209 void SdrObjEditView::TakeActionRect(tools::Rectangle& rRect) const 210 { 211 if (IsMacroObj()) 212 { 213 rRect = pMacroObj->GetCurrentBoundRect(); 214 } 215 else 216 { 217 SdrGlueEditView::TakeActionRect(rRect); 218 } 219 } 220 221 void SdrObjEditView::Notify(SfxBroadcaster& rBC, const SfxHint& rHint) 222 { 223 SdrGlueEditView::Notify(rBC, rHint); 224 if (mpTextEditOutliner == nullptr) 225 return; 226 227 // change of printer while editing 228 if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint) 229 return; 230 231 const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint); 232 SdrHintKind eKind = pSdrHint->GetKind(); 233 if (eKind == SdrHintKind::RefDeviceChange) 234 { 235 mpTextEditOutliner->SetRefDevice(mpModel->GetRefDevice()); 236 } 237 if (eKind == SdrHintKind::DefaultTabChange) 238 { 239 mpTextEditOutliner->SetDefTab(mpModel->GetDefaultTabulator()); 240 } 241 } 242 243 void SdrObjEditView::ModelHasChanged() 244 { 245 SdrGlueEditView::ModelHasChanged(); 246 if (mxWeakTextEditObj.is() && !mxWeakTextEditObj->IsInserted()) 247 SdrEndTextEdit(); // object deleted 248 // TextEditObj changed? 249 if (!IsTextEdit()) 250 return; 251 252 SdrTextObj* pTextObj = mxWeakTextEditObj.get(); 253 if (pTextObj != nullptr) 254 { 255 size_t nOutlViewCnt = mpTextEditOutliner->GetViewCount(); 256 bool bAreaChg = false; 257 bool bAnchorChg = false; 258 bool bColorChg = false; 259 bool bContourFrame = pTextObj->IsContourTextFrame(); 260 EEAnchorMode eNewAnchor(EEAnchorMode::VCenterHCenter); 261 tools::Rectangle aOldArea(aMinTextEditArea); 262 aOldArea.Union(aTextEditArea); 263 Color aNewColor; 264 { // check area 265 Size aPaperMin1; 266 Size aPaperMax1; 267 tools::Rectangle aEditArea1; 268 tools::Rectangle aMinArea1; 269 pTextObj->TakeTextEditArea(&aPaperMin1, &aPaperMax1, &aEditArea1, &aMinArea1); 270 Point aPvOfs(pTextObj->GetTextEditOffset()); 271 272 // add possible GridOffset to up-to-now view-independent EditAreas 273 basegfx::B2DVector aGridOffset(0.0, 0.0); 274 if (getPossibleGridOffsetForSdrObject(aGridOffset, pTextObj, GetSdrPageView())) 275 { 276 const Point aOffset(basegfx::fround(aGridOffset.getX()), 277 basegfx::fround(aGridOffset.getY())); 278 279 aEditArea1 += aOffset; 280 aMinArea1 += aOffset; 281 } 282 283 aEditArea1.Move(aPvOfs.X(), aPvOfs.Y()); 284 aMinArea1.Move(aPvOfs.X(), aPvOfs.Y()); 285 tools::Rectangle aNewArea(aMinArea1); 286 aNewArea.Union(aEditArea1); 287 288 if (aNewArea != aOldArea || aEditArea1 != aTextEditArea || aMinArea1 != aMinTextEditArea 289 || mpTextEditOutliner->GetMinAutoPaperSize() != aPaperMin1 290 || mpTextEditOutliner->GetMaxAutoPaperSize() != aPaperMax1) 291 { 292 aTextEditArea = aEditArea1; 293 aMinTextEditArea = aMinArea1; 294 295 const bool bPrevUpdateLayout = mpTextEditOutliner->SetUpdateLayout(false); 296 mpTextEditOutliner->SetMinAutoPaperSize(aPaperMin1); 297 mpTextEditOutliner->SetMaxAutoPaperSize(aPaperMax1); 298 mpTextEditOutliner->SetPaperSize(Size(0, 0)); // re-format Outliner 299 300 if (!bContourFrame) 301 { 302 mpTextEditOutliner->ClearPolygon(); 303 EEControlBits nStat = mpTextEditOutliner->GetControlWord(); 304 nStat |= EEControlBits::AUTOPAGESIZE; 305 mpTextEditOutliner->SetControlWord(nStat); 306 } 307 else 308 { 309 EEControlBits nStat = mpTextEditOutliner->GetControlWord(); 310 nStat &= ~EEControlBits::AUTOPAGESIZE; 311 mpTextEditOutliner->SetControlWord(nStat); 312 tools::Rectangle aAnchorRect; 313 pTextObj->TakeTextAnchorRect(aAnchorRect); 314 pTextObj->ImpSetContourPolygon(*mpTextEditOutliner, aAnchorRect, true); 315 } 316 for (size_t nOV = 0; nOV < nOutlViewCnt; nOV++) 317 { 318 OutlinerView* pOLV = mpTextEditOutliner->GetView(nOV); 319 EVControlBits nStat0 = pOLV->GetControlWord(); 320 EVControlBits nStat = nStat0; 321 // AutoViewSize only if not ContourFrame. 322 if (!bContourFrame) 323 nStat |= EVControlBits::AUTOSIZE; 324 else 325 nStat &= ~EVControlBits::AUTOSIZE; 326 if (nStat != nStat0) 327 pOLV->SetControlWord(nStat); 328 } 329 330 mpTextEditOutliner->SetUpdateLayout(bPrevUpdateLayout); 331 bAreaChg = true; 332 } 333 } 334 if (mpTextEditOutlinerView != nullptr) 335 { // check fill and anchor 336 EEAnchorMode eOldAnchor = mpTextEditOutlinerView->GetAnchorMode(); 337 eNewAnchor = pTextObj->GetOutlinerViewAnchorMode(); 338 bAnchorChg = eOldAnchor != eNewAnchor; 339 Color aOldColor(mpTextEditOutlinerView->GetBackgroundColor()); 340 aNewColor = GetTextEditBackgroundColor(*this); 341 bColorChg = aOldColor != aNewColor; 342 } 343 // refresh always when it's a contour frame. That 344 // refresh is necessary since it triggers the repaint 345 // which makes the Handles visible. Changes at TakeTextRect() 346 // seem to have resulted in a case where no refresh is executed. 347 // Before that, a refresh must have been always executed 348 // (else this error would have happened earlier), thus I 349 // even think here a refresh should be done always. 350 // Since follow-up problems cannot even be guessed I only 351 // add this one more case to the if below. 352 // BTW: It's VERY bad style that here, inside ModelHasChanged() 353 // the outliner is again massively changed for the text object 354 // in text edit mode. Normally, all necessary data should be 355 // set at SdrBeginTextEdit(). Some changes and value assigns in 356 // SdrBeginTextEdit() are completely useless since they are set here 357 // again on ModelHasChanged(). 358 if (bContourFrame || bAreaChg || bAnchorChg || bColorChg) 359 { 360 for (size_t nOV = 0; nOV < nOutlViewCnt; nOV++) 361 { 362 OutlinerView* pOLV = mpTextEditOutliner->GetView(nOV); 363 { // invalidate old OutlinerView area 364 vcl::Window* pWin = pOLV->GetWindow(); 365 tools::Rectangle aTmpRect(aOldArea); 366 sal_uInt16 nPixSiz = pOLV->GetInvalidateMore() + 1; 367 Size aMore(pWin->PixelToLogic(Size(nPixSiz, nPixSiz))); 368 aTmpRect.AdjustLeft(-(aMore.Width())); 369 aTmpRect.AdjustRight(aMore.Width()); 370 aTmpRect.AdjustTop(-(aMore.Height())); 371 aTmpRect.AdjustBottom(aMore.Height()); 372 InvalidateOneWin(*pWin->GetOutDev(), aTmpRect); 373 } 374 if (bAnchorChg) 375 pOLV->SetAnchorMode(eNewAnchor); 376 if (bColorChg) 377 pOLV->SetBackgroundColor(aNewColor); 378 379 pOLV->SetOutputArea( 380 aTextEditArea); // because otherwise, we're not re-anchoring correctly 381 ImpInvalidateOutlinerView(*pOLV); 382 } 383 mpTextEditOutlinerView->ShowCursor(); 384 } 385 } 386 ImpMakeTextCursorAreaVisible(); 387 } 388 389 namespace 390 { 391 /** 392 Helper class to visualize the content of an active EditView as an 393 OverlayObject. These objects work with Primitives and are handled 394 from the OverlayManager(s) in place as needed. 395 396 It allows complete visualization of the content of the active 397 EditView without the need of Invalidates triggered by the EditView 398 and thus avoiding potentially expensive repaints by using the 399 automatically buffered Overlay mechanism. 400 401 It buffers as much as possible locally and *only* triggers a real 402 change (see call to objectChange()) when really needed. 403 */ 404 class TextEditOverlayObject : public sdr::overlay::OverlayObject 405 { 406 protected: 407 /// local access to associated sdr::overlay::OverlaySelection 408 std::unique_ptr<sdr::overlay::OverlaySelection> mxOverlaySelection; 409 410 /// local definition depends on active OutlinerView 411 OutlinerView& mrOutlinerView; 412 413 /// geometry definitions with buffering 414 basegfx::B2DRange maLastRange; 415 basegfx::B2DRange maRange; 416 417 /// text content definitions with buffering 418 drawinglayer::primitive2d::Primitive2DContainer maTextPrimitives; 419 drawinglayer::primitive2d::Primitive2DContainer maLastTextPrimitives; 420 421 /// bitfield 422 bool mbVisualizeSurroundingFrame : 1; 423 424 // geometry creation for OverlayObject, can use local *Last* values 425 virtual drawinglayer::primitive2d::Primitive2DContainer 426 createOverlayObjectPrimitive2DSequence() override; 427 428 public: 429 TextEditOverlayObject(const Color& rColor, OutlinerView& rOutlinerView, 430 bool bVisualizeSurroundingFrame); 431 virtual ~TextEditOverlayObject() override; 432 433 // data read access 434 const sdr::overlay::OverlaySelection* getOverlaySelection() const 435 { 436 return mxOverlaySelection.get(); 437 } 438 const OutlinerView& getOutlinerView() const { return mrOutlinerView; } 439 440 /// override to check conditions for last createOverlayObjectPrimitive2DSequence 441 virtual drawinglayer::primitive2d::Primitive2DContainer 442 getOverlayObjectPrimitive2DSequence() const override; 443 444 // data write access. In this OverlayObject we only have the 445 // callback that triggers detecting if something *has* changed 446 void checkDataChange(const basegfx::B2DRange& rMinTextEditArea); 447 void checkSelectionChange(); 448 }; 449 450 drawinglayer::primitive2d::Primitive2DContainer 451 TextEditOverlayObject::createOverlayObjectPrimitive2DSequence() 452 { 453 drawinglayer::primitive2d::Primitive2DContainer aRetval; 454 455 /// outer frame visualization 456 if (mbVisualizeSurroundingFrame) 457 { 458 const double fTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01); 459 const sal_uInt16 nPixSiz(getOutlinerView().GetInvalidateMore() - 1); 460 461 aRetval.push_back(new drawinglayer::primitive2d::OverlayRectanglePrimitive( 462 maRange, getBaseColor().getBColor(), fTransparence, std::max(6, nPixSiz - 2), // grow 463 0.0, // shrink 464 0.0)); 465 } 466 467 // add buffered TextPrimitives 468 aRetval.append(maTextPrimitives); 469 470 return aRetval; 471 } 472 473 TextEditOverlayObject::TextEditOverlayObject(const Color& rColor, OutlinerView& rOutlinerView, 474 bool bVisualizeSurroundingFrame) 475 : OverlayObject(rColor) 476 , mrOutlinerView(rOutlinerView) 477 , mbVisualizeSurroundingFrame(bVisualizeSurroundingFrame) 478 { 479 // no AA for TextEdit overlay 480 allowAntiAliase(false); 481 482 // create local OverlaySelection - this is an integral part of EditText 483 // visualization 484 std::vector<basegfx::B2DRange> aEmptySelection{}; 485 mxOverlaySelection.reset(new sdr::overlay::OverlaySelection( 486 sdr::overlay::OverlayType::Transparent, rColor, std::move(aEmptySelection), true)); 487 } 488 489 TextEditOverlayObject::~TextEditOverlayObject() 490 { 491 mxOverlaySelection.reset(); 492 493 if (getOverlayManager()) 494 { 495 getOverlayManager()->remove(*this); 496 } 497 } 498 499 drawinglayer::primitive2d::Primitive2DContainer 500 TextEditOverlayObject::getOverlayObjectPrimitive2DSequence() const 501 { 502 if (!getPrimitive2DSequence().empty()) 503 { 504 if (!maRange.equal(maLastRange) || maLastTextPrimitives != maTextPrimitives) 505 { 506 // conditions of last local decomposition have changed, delete to force new evaluation 507 const_cast<TextEditOverlayObject*>(this)->resetPrimitive2DSequence(); 508 } 509 } 510 511 if (getPrimitive2DSequence().empty()) 512 { 513 // remember new buffered values 514 const_cast<TextEditOverlayObject*>(this)->maLastRange = maRange; 515 const_cast<TextEditOverlayObject*>(this)->maLastTextPrimitives = maTextPrimitives; 516 } 517 518 // call base implementation 519 return OverlayObject::getOverlayObjectPrimitive2DSequence(); 520 } 521 522 void TextEditOverlayObject::checkDataChange(const basegfx::B2DRange& rMinTextEditArea) 523 { 524 bool bObjectChange(false); 525 526 // check current range 527 const tools::Rectangle aOutArea(mrOutlinerView.GetOutputArea()); 528 basegfx::B2DRange aNewRange = vcl::unotools::b2DRectangleFromRectangle(aOutArea); 529 aNewRange.expand(rMinTextEditArea); 530 531 if (aNewRange != maRange) 532 { 533 maRange = aNewRange; 534 bObjectChange = true; 535 } 536 537 // check if text primitives did change 538 SdrOutliner* pSdrOutliner = dynamic_cast<SdrOutliner*>(getOutlinerView().GetOutliner()); 539 540 if (pSdrOutliner) 541 { 542 // get TextPrimitives directly from active Outliner 543 basegfx::B2DHomMatrix aNewTransformA; 544 basegfx::B2DHomMatrix aNewTransformB; 545 basegfx::B2DRange aClipRange; 546 drawinglayer::primitive2d::Primitive2DContainer aNewTextPrimitives; 547 548 // active Outliner is always in unified oriented coordinate system (currently) 549 // so just translate to TopLeft of visible Range. Keep in mind that top-left 550 // depends on vertical text and top-to-bottom text attributes 551 const tools::Rectangle aVisArea(mrOutlinerView.GetVisArea()); 552 const bool bVerticalWriting(pSdrOutliner->IsVertical()); 553 const bool bTopToBottom(pSdrOutliner->IsTopToBottom()); 554 const double fStartInX(bVerticalWriting && bTopToBottom 555 ? aOutArea.Right() - aVisArea.Left() 556 : aOutArea.Left() - aVisArea.Left()); 557 const double fStartInY(bVerticalWriting && !bTopToBottom 558 ? aOutArea.Bottom() - aVisArea.Top() 559 : aOutArea.Top() - aVisArea.Top()); 560 561 aNewTransformB.translate(fStartInX, fStartInY); 562 563 // get the current TextPrimitives. This is the most expensive part 564 // of this mechanism, it *may* be possible to buffer layouted 565 // primitives per ParaPortion with/in/dependent on the EditEngine 566 // content if needed. For now, get and compare 567 SdrTextObj::impDecomposeBlockTextPrimitiveDirect( 568 aNewTextPrimitives, *pSdrOutliner, aNewTransformA, aNewTransformB, aClipRange); 569 570 if (aNewTextPrimitives != maTextPrimitives) 571 { 572 maTextPrimitives = std::move(aNewTextPrimitives); 573 bObjectChange = true; 574 } 575 } 576 577 if (bObjectChange) 578 { 579 // if there really *was* a change signal the OverlayManager to 580 // refresh this object's visualization 581 objectChange(); 582 583 // on data change, always do a SelectionChange, too 584 // since the selection is an integral part of text visualization 585 checkSelectionChange(); 586 } 587 } 588 589 void TextEditOverlayObject::checkSelectionChange() 590 { 591 if (!(getOverlaySelection() && getOverlayManager())) 592 return; 593 594 std::vector<tools::Rectangle> aLogicRects; 595 std::vector<basegfx::B2DRange> aLogicRanges; 596 const Size aLogicPixel(getOverlayManager()->getOutputDevice().PixelToLogic(Size(1, 1))); 597 598 // get logic selection 599 getOutlinerView().GetSelectionRectangles(aLogicRects); 600 601 aLogicRanges.reserve(aLogicRects.size()); 602 for (const auto& aRect : aLogicRects) 603 { 604 // convert from logic Rectangles to logic Ranges, do not forget to add 605 // one Unit (in this case logical units for one pixel, pre-calculated) 606 aLogicRanges.emplace_back( 607 aRect.Left() - aLogicPixel.Width(), aRect.Top() - aLogicPixel.Height(), 608 aRect.Right() + aLogicPixel.Width(), aRect.Bottom() + aLogicPixel.Height()); 609 } 610 611 mxOverlaySelection->setRanges(std::move(aLogicRanges)); 612 } 613 } // end of anonymous namespace 614 615 // TextEdit 616 617 // callback from the active EditView, forward to evtl. existing instances of the 618 // TextEditOverlayObject(s). This will additionally update the selection which 619 // is an integral part of the text visualization 620 void SdrObjEditView::EditViewInvalidate(const tools::Rectangle&) 621 { 622 if (!IsTextEdit()) 623 return; 624 625 // MinTextRange may have changed. Forward it, too 626 const basegfx::B2DRange aMinTextRange 627 = vcl::unotools::b2DRectangleFromRectangle(aMinTextEditArea); 628 629 for (sal_uInt32 a(0); a < maTEOverlayGroup.count(); a++) 630 { 631 TextEditOverlayObject* pCandidate 632 = dynamic_cast<TextEditOverlayObject*>(&maTEOverlayGroup.getOverlayObject(a)); 633 634 if (pCandidate) 635 { 636 pCandidate->checkDataChange(aMinTextRange); 637 } 638 } 639 } 640 641 // callback from the active EditView, forward to evtl. existing instances of the 642 // TextEditOverlayObject(s). This cvall *only* updates the selection visualization 643 // which is e.g. used when only the selection is changed, but not the text 644 void SdrObjEditView::EditViewSelectionChange() 645 { 646 if (!IsTextEdit()) 647 return; 648 649 for (sal_uInt32 a(0); a < maTEOverlayGroup.count(); a++) 650 { 651 TextEditOverlayObject* pCandidate 652 = dynamic_cast<TextEditOverlayObject*>(&maTEOverlayGroup.getOverlayObject(a)); 653 654 if (pCandidate) 655 { 656 pCandidate->checkSelectionChange(); 657 } 658 } 659 } 660 661 OutputDevice& SdrObjEditView::EditViewOutputDevice() const { return *mpTextEditWin->GetOutDev(); } 662 663 Point SdrObjEditView::EditViewPointerPosPixel() const 664 { 665 return mpTextEditWin->GetPointerPosPixel(); 666 } 667 668 css::uno::Reference<css::datatransfer::clipboard::XClipboard> SdrObjEditView::GetClipboard() const 669 { 670 if (!mpTextEditWin) 671 return nullptr; 672 return mpTextEditWin->GetClipboard(); 673 } 674 675 css::uno::Reference<css::datatransfer::dnd::XDropTarget> SdrObjEditView::GetDropTarget() 676 { 677 if (!mpTextEditWin) 678 return nullptr; 679 return mpTextEditWin->GetDropTarget(); 680 } 681 682 void SdrObjEditView::EditViewInputContext(const InputContext& rInputContext) 683 { 684 if (!mpTextEditWin) 685 return; 686 mpTextEditWin->SetInputContext(rInputContext); 687 } 688 689 void SdrObjEditView::EditViewCursorRect(const tools::Rectangle& rRect, int nExtTextInputWidth) 690 { 691 if (!mpTextEditWin) 692 return; 693 mpTextEditWin->SetCursorRect(&rRect, nExtTextInputWidth); 694 } 695 696 void SdrObjEditView::TextEditDrawing(SdrPaintWindow& rPaintWindow) 697 { 698 if (!comphelper::LibreOfficeKit::isActive()) 699 { 700 // adapt all TextEditOverlayObject(s), so call EditViewInvalidate() 701 // to update accordingly (will update selection, too). Suppress new 702 // stuff when LibreOfficeKit is active 703 EditViewInvalidate(tools::Rectangle()); 704 } 705 else 706 { 707 // draw old text edit stuff 708 if (IsTextEdit()) 709 { 710 const SdrOutliner* pActiveOutliner = GetTextEditOutliner(); 711 712 if (pActiveOutliner) 713 { 714 const sal_uInt32 nViewCount(pActiveOutliner->GetViewCount()); 715 716 if (nViewCount) 717 { 718 const vcl::Region& rRedrawRegion = rPaintWindow.GetRedrawRegion(); 719 const tools::Rectangle aCheckRect(rRedrawRegion.GetBoundRect()); 720 721 for (sal_uInt32 i(0); i < nViewCount; i++) 722 { 723 OutlinerView* pOLV = pActiveOutliner->GetView(i); 724 725 // If rPaintWindow knows that the output device is a render 726 // context and is aware of the underlying vcl::Window, 727 // compare against that; that's how double-buffering can 728 // still find the matching OutlinerView. 729 OutputDevice* pOutputDevice = rPaintWindow.GetWindow() 730 ? rPaintWindow.GetWindow()->GetOutDev() 731 : &rPaintWindow.GetOutputDevice(); 732 if (pOLV->GetWindow()->GetOutDev() == pOutputDevice 733 || comphelper::LibreOfficeKit::isActive()) 734 { 735 ImpPaintOutlinerView(*pOLV, aCheckRect, 736 rPaintWindow.GetTargetOutputDevice()); 737 return; 738 } 739 } 740 } 741 } 742 } 743 } 744 } 745 746 void SdrObjEditView::ImpPaintOutlinerView(OutlinerView& rOutlView, const tools::Rectangle& rRect, 747 OutputDevice& rTargetDevice) const 748 { 749 const SdrTextObj* pText = GetTextEditObject(); 750 bool bTextFrame(pText && pText->IsTextFrame()); 751 bool bFitToSize(mpTextEditOutliner->GetControlWord() & EEControlBits::STRETCHING); 752 bool bModified(mpTextEditOutliner->IsModified()); 753 tools::Rectangle aBlankRect(rOutlView.GetOutputArea()); 754 aBlankRect.Union(aMinTextEditArea); 755 tools::Rectangle aPixRect(rTargetDevice.LogicToPixel(aBlankRect)); 756 757 // in the tiled rendering case, the setup is incomplete, and we very 758 // easily get an empty rRect on input - that will cause that everything is 759 // clipped; happens in case of editing text inside a shape in Calc. 760 // FIXME would be better to complete the setup so that we don't get an 761 // empty rRect here 762 if (!comphelper::LibreOfficeKit::isActive() || !rRect.IsEmpty()) 763 aBlankRect.Intersection(rRect); 764 765 rOutlView.GetOutliner()->SetUpdateLayout(true); // Bugfix #22596# 766 rOutlView.Paint(aBlankRect, &rTargetDevice); 767 768 if (!bModified) 769 { 770 mpTextEditOutliner->ClearModifyFlag(); 771 } 772 773 if (bTextFrame && !bFitToSize) 774 { 775 // completely reworked to use primitives; this ensures same look and functionality 776 const drawinglayer::geometry::ViewInformation2D aViewInformation2D; 777 std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> xProcessor( 778 drawinglayer::processor2d::createProcessor2DFromOutputDevice(rTargetDevice, 779 aViewInformation2D)); 780 781 const bool bMapModeEnabled(rTargetDevice.IsMapModeEnabled()); 782 const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(aPixRect); 783 const Color aHilightColor(SvtOptionsDrawinglayer::getHilightColor()); 784 const double fTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01); 785 const sal_uInt16 nPixSiz(rOutlView.GetInvalidateMore() - 1); 786 const drawinglayer::primitive2d::Primitive2DReference xReference( 787 new drawinglayer::primitive2d::OverlayRectanglePrimitive( 788 aRange, aHilightColor.getBColor(), fTransparence, std::max(6, nPixSiz - 2), // grow 789 0.0, // shrink 790 0.0)); 791 const drawinglayer::primitive2d::Primitive2DContainer aSequence{ xReference }; 792 793 rTargetDevice.EnableMapMode(false); 794 xProcessor->process(aSequence); 795 rTargetDevice.EnableMapMode(bMapModeEnabled); 796 } 797 798 rOutlView.ShowCursor(/*bGotoCursor=*/true, /*bActivate=*/true); 799 } 800 801 void SdrObjEditView::ImpInvalidateOutlinerView(OutlinerView const& rOutlView) const 802 { 803 vcl::Window* pWin = rOutlView.GetWindow(); 804 805 if (!pWin) 806 return; 807 808 const SdrTextObj* pText = GetTextEditObject(); 809 bool bTextFrame(pText && pText->IsTextFrame()); 810 bool bFitToSize(pText && pText->IsFitToSize()); 811 812 if (!bTextFrame || bFitToSize) 813 return; 814 815 tools::Rectangle aBlankRect(rOutlView.GetOutputArea()); 816 aBlankRect.Union(aMinTextEditArea); 817 tools::Rectangle aPixRect(pWin->LogicToPixel(aBlankRect)); 818 sal_uInt16 nPixSiz(rOutlView.GetInvalidateMore() - 1); 819 820 aPixRect.AdjustLeft(-1); 821 aPixRect.AdjustTop(-1); 822 aPixRect.AdjustRight(1); 823 aPixRect.AdjustBottom(1); 824 825 { 826 // limit xPixRect because of driver problems when pixel coordinates are too far out 827 Size aMaxXY(pWin->GetOutputSizePixel()); 828 tools::Long a(2 * nPixSiz); 829 tools::Long nMaxX(aMaxXY.Width() + a); 830 tools::Long nMaxY(aMaxXY.Height() + a); 831 832 if (aPixRect.Left() < -a) 833 aPixRect.SetLeft(-a); 834 if (aPixRect.Top() < -a) 835 aPixRect.SetTop(-a); 836 if (aPixRect.Right() > nMaxX) 837 aPixRect.SetRight(nMaxX); 838 if (aPixRect.Bottom() > nMaxY) 839 aPixRect.SetBottom(nMaxY); 840 } 841 842 tools::Rectangle aOuterPix(aPixRect); 843 aOuterPix.AdjustLeft(-nPixSiz); 844 aOuterPix.AdjustTop(-nPixSiz); 845 aOuterPix.AdjustRight(nPixSiz); 846 aOuterPix.AdjustBottom(nPixSiz); 847 848 bool bMapModeEnabled(pWin->IsMapModeEnabled()); 849 pWin->EnableMapMode(false); 850 pWin->Invalidate(aOuterPix); 851 pWin->EnableMapMode(bMapModeEnabled); 852 } 853 854 OutlinerView* SdrObjEditView::ImpMakeOutlinerView(vcl::Window* pWin, OutlinerView* pGivenView, 855 SfxViewShell* pViewShell) const 856 { 857 // background 858 Color aBackground(GetTextEditBackgroundColor(*this)); 859 SdrTextObj* pText = mxWeakTextEditObj.get(); 860 bool bTextFrame = pText != nullptr && pText->IsTextFrame(); 861 bool bContourFrame = pText != nullptr && pText->IsContourTextFrame(); 862 // create OutlinerView 863 OutlinerView* pOutlView = pGivenView; 864 mpTextEditOutliner->SetUpdateLayout(false); 865 866 if (pOutlView == nullptr) 867 { 868 pOutlView = new OutlinerView(mpTextEditOutliner.get(), pWin); 869 } 870 else 871 { 872 pOutlView->SetWindow(pWin); 873 } 874 875 if (mbNegativeX) 876 pOutlView->GetEditView().SetNegativeX(mbNegativeX); 877 878 // disallow scrolling 879 EVControlBits nStat = pOutlView->GetControlWord(); 880 nStat &= ~EVControlBits::AUTOSCROLL; 881 // AutoViewSize only if not ContourFrame. 882 if (!bContourFrame) 883 nStat |= EVControlBits::AUTOSIZE; 884 if (bTextFrame) 885 { 886 sal_uInt16 nPixSiz = maHdlList.GetHdlSize() * 2 + 1; 887 nStat |= EVControlBits::INVONEMORE; 888 pOutlView->SetInvalidateMore(nPixSiz); 889 } 890 pOutlView->SetControlWord(nStat); 891 pOutlView->SetBackgroundColor(aBackground); 892 893 // In case we're in the process of constructing a new view shell, 894 // SfxViewShell::Current() may still point to the old one. So if possible, 895 // depend on the application owning this draw view to provide the view 896 // shell. 897 SfxViewShell* pSfxViewShell = pViewShell ? pViewShell : GetSfxViewShell(); 898 pOutlView->RegisterViewShell(pSfxViewShell ? pSfxViewShell : SfxViewShell::Current()); 899 900 if (pText != nullptr) 901 { 902 pOutlView->SetAnchorMode(pText->GetOutlinerViewAnchorMode()); 903 mpTextEditOutliner->SetFixedCellHeight( 904 pText->GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue()); 905 } 906 // do update before setting output area so that aTextEditArea can be recalculated 907 mpTextEditOutliner->SetUpdateLayout(true); 908 pOutlView->SetOutputArea(aTextEditArea); 909 ImpInvalidateOutlinerView(*pOutlView); 910 return pOutlView; 911 } 912 913 IMPL_LINK(SdrObjEditView, ImpOutlinerStatusEventHdl, EditStatus&, rEditStat, void) 914 { 915 if (mpTextEditOutliner) 916 { 917 SdrTextObj* pTextObj = mxWeakTextEditObj.get(); 918 if (pTextObj) 919 { 920 pTextObj->onEditOutlinerStatusEvent(&rEditStat); 921 } 922 } 923 } 924 925 void SdrObjEditView::ImpChainingEventHdl() 926 { 927 if (!mpTextEditOutliner) 928 return; 929 930 SdrTextObj* pTextObj = mxWeakTextEditObj.get(); 931 OutlinerView* pOLV = GetTextEditOutlinerView(); 932 if (pTextObj && pOLV) 933 { 934 TextChain* pTextChain = pTextObj->GetTextChain(); 935 936 // XXX: IsChainable and GetNilChainingEvent are a bit mixed up atm 937 if (!pTextObj->IsChainable()) 938 { 939 return; 940 } 941 // This is true during an underflow-caused overflow (with pEdtOutl->SetText()) 942 if (pTextChain->GetNilChainingEvent(pTextObj)) 943 { 944 return; 945 } 946 947 // We prevent to trigger further handling of overflow/underflow for pTextObj 948 pTextChain->SetNilChainingEvent(pTextObj, true); // XXX 949 950 // Save previous selection pos // NOTE: It must be done to have the right CursorEvent in KeyInput 951 pTextChain->SetPreChainingSel(pTextObj, pOLV->GetSelection()); 952 //maPreChainingSel = new ESelection(pOLV->GetSelection()); 953 954 // Handling Undo 955 const int nText = 0; // XXX: hardcoded index (SdrTextObj::getText handles only 0) 956 957 const bool bUndoEnabled = GetModel() && IsUndoEnabled(); 958 std::unique_ptr<SdrUndoObjSetText> pTxtUndo; 959 if (bUndoEnabled) 960 pTxtUndo.reset( 961 dynamic_cast<SdrUndoObjSetText*>(GetModel() 962 ->GetSdrUndoFactory() 963 .CreateUndoObjectSetText(*pTextObj, nText) 964 .release())); 965 966 // trigger actual chaining 967 pTextObj->onChainingEvent(); 968 969 if (pTxtUndo) 970 { 971 pTxtUndo->AfterSetText(); 972 if (!pTxtUndo->IsDifferent()) 973 { 974 pTxtUndo.reset(); 975 } 976 } 977 978 if (pTxtUndo) 979 AddUndo(std::move(pTxtUndo)); 980 981 //maCursorEvent = new CursorChainingEvent(pTextChain->GetCursorEvent(pTextObj)); 982 //SdrTextObj *pNextLink = pTextObj->GetNextLinkInChain(); 983 984 // NOTE: Must be called. Don't let the function return if you set it to true and not reset it 985 pTextChain->SetNilChainingEvent(pTextObj, false); 986 } 987 else 988 { 989 // XXX 990 SAL_INFO("svx.chaining", "[OnChaining] No Edit Outliner View"); 991 } 992 } 993 994 IMPL_LINK_NOARG(SdrObjEditView, ImpAfterCutOrPasteChainingEventHdl, LinkParamNone*, void) 995 { 996 SdrTextObj* pTextObj = GetTextEditObject(); 997 if (!pTextObj) 998 return; 999 ImpChainingEventHdl(); 1000 TextChainCursorManager aCursorManager(this, pTextObj); 1001 ImpMoveCursorAfterChainingEvent(&aCursorManager); 1002 } 1003 1004 void SdrObjEditView::ImpMoveCursorAfterChainingEvent(TextChainCursorManager* pCursorManager) 1005 { 1006 if (!mxWeakTextEditObj.is() || !pCursorManager) 1007 return; 1008 1009 SdrTextObj* pTextObj = mxWeakTextEditObj.get(); 1010 1011 // Check if it has links to move it to 1012 if (!pTextObj || !pTextObj->IsChainable()) 1013 return; 1014 1015 TextChain* pTextChain = pTextObj->GetTextChain(); 1016 ESelection aNewSel = pTextChain->GetPostChainingSel(pTextObj); 1017 1018 pCursorManager->HandleCursorEventAfterChaining(pTextChain->GetCursorEvent(pTextObj), aNewSel); 1019 1020 // Reset event 1021 pTextChain->SetCursorEvent(pTextObj, CursorChainingEvent::NULL_EVENT); 1022 } 1023 1024 IMPL_LINK(SdrObjEditView, ImpOutlinerCalcFieldValueHdl, EditFieldInfo*, pFI, void) 1025 { 1026 bool bOk = false; 1027 OUString& rStr = pFI->GetRepresentation(); 1028 rStr.clear(); 1029 SdrTextObj* pTextObj = mxWeakTextEditObj.get(); 1030 if (pTextObj != nullptr) 1031 { 1032 std::optional<Color> pTxtCol; 1033 std::optional<Color> pFldCol; 1034 bOk = pTextObj->CalcFieldValue(pFI->GetField(), pFI->GetPara(), pFI->GetPos(), true, 1035 pTxtCol, pFldCol, rStr); 1036 if (bOk) 1037 { 1038 if (pTxtCol) 1039 { 1040 pFI->SetTextColor(*pTxtCol); 1041 } 1042 if (pFldCol) 1043 { 1044 pFI->SetFieldColor(*pFldCol); 1045 } 1046 else 1047 { 1048 pFI->SetFieldColor(COL_LIGHTGRAY); // TODO: remove this later on (357) 1049 } 1050 } 1051 } 1052 Outliner& rDrawOutl = mpModel->GetDrawOutliner(pTextObj); 1053 Link<EditFieldInfo*, void> aDrawOutlLink = rDrawOutl.GetCalcFieldValueHdl(); 1054 if (!bOk && aDrawOutlLink.IsSet()) 1055 { 1056 aDrawOutlLink.Call(pFI); 1057 bOk = !rStr.isEmpty(); 1058 } 1059 if (!bOk) 1060 { 1061 aOldCalcFieldValueLink.Call(pFI); 1062 } 1063 } 1064 1065 IMPL_LINK_NOARG(SdrObjEditView, EndTextEditHdl, SdrUndoManager*, void) { SdrEndTextEdit(); } 1066 1067 // Default implementation - null UndoManager 1068 std::unique_ptr<SdrUndoManager> SdrObjEditView::createLocalTextUndoManager() 1069 { 1070 SAL_WARN("svx", "SdrObjEditView::createLocalTextUndoManager needs to be overridden"); 1071 return std::unique_ptr<SdrUndoManager>(); 1072 } 1073 1074 bool SdrObjEditView::SdrBeginTextEdit(SdrObject* pObj_, SdrPageView* pPV, vcl::Window* pWin, 1075 bool bIsNewObj, SdrOutliner* pGivenOutliner, 1076 OutlinerView* pGivenOutlinerView, bool bDontDeleteOutliner, 1077 bool bOnlyOneView, bool bGrabFocus) 1078 { 1079 // FIXME cannot be an assert() yet, the code is not ready for that; 1080 // eg. press F7 in Impress when you are inside a text object with spelling 1081 // mistakes => boom; and it is unclear how to avoid that 1082 SAL_WARN_IF(IsTextEdit(), "svx", "SdrBeginTextEdit called when IsTextEdit() is already true."); 1083 // FIXME this encourages all sorts of bad habits and should be removed 1084 SdrEndTextEdit(); 1085 1086 SdrTextObj* pObj = dynamic_cast<SdrTextObj*>(pObj_); 1087 if (!pObj) 1088 return false; // currently only possible with text objects 1089 1090 if (bGrabFocus && pWin) 1091 { 1092 // attention, this call may cause an EndTextEdit() call to this view 1093 pWin->GrabFocus(); // to force the cursor into the edit view 1094 } 1095 1096 mbTextEditDontDelete = bDontDeleteOutliner && pGivenOutliner != nullptr; 1097 mbTextEditOnlyOneView = bOnlyOneView; 1098 mbTextEditNewObj = bIsNewObj; 1099 const sal_uInt32 nWinCount(PaintWindowCount()); 1100 1101 bool bBrk(false); 1102 1103 if (!pWin) 1104 { 1105 for (sal_uInt32 i = 0; i < nWinCount && !pWin; i++) 1106 { 1107 SdrPaintWindow* pPaintWindow = GetPaintWindow(i); 1108 1109 if (OUTDEV_WINDOW == pPaintWindow->GetOutputDevice().GetOutDevType()) 1110 { 1111 pWin = pPaintWindow->GetOutputDevice().GetOwnerWindow(); 1112 } 1113 } 1114 1115 // break, when no window exists 1116 if (!pWin) 1117 { 1118 bBrk = true; 1119 } 1120 } 1121 1122 if (!bBrk && !pPV) 1123 { 1124 pPV = GetSdrPageView(); 1125 1126 // break, when no PageView for the object exists 1127 if (!pPV) 1128 { 1129 bBrk = true; 1130 } 1131 } 1132 1133 // no TextEdit on objects in locked Layer 1134 if (pPV && pPV->GetLockedLayers().IsSet(pObj->GetLayer())) 1135 { 1136 bBrk = true; 1137 } 1138 1139 if (mpTextEditOutliner) 1140 { 1141 OSL_FAIL("SdrObjEditView::SdrBeginTextEdit(): Old Outliner still exists."); 1142 mpTextEditOutliner.reset(); 1143 } 1144 1145 if (!bBrk) 1146 { 1147 mpTextEditWin = pWin; 1148 mpTextEditPV = pPV; 1149 mxWeakTextEditObj.reset(pObj); 1150 if (pGivenOutliner) 1151 { 1152 mpTextEditOutliner.reset(pGivenOutliner); 1153 pGivenOutliner = nullptr; // so we don't delete it on the error path 1154 } 1155 else 1156 mpTextEditOutliner = SdrMakeOutliner(OutlinerMode::TextObject, 1157 mxWeakTextEditObj->getSdrModelFromSdrObject()); 1158 1159 { 1160 SvtAccessibilityOptions aOptions; 1161 mpTextEditOutliner->ForceAutoColor(aOptions.GetIsAutomaticFontColor()); 1162 } 1163 1164 aOldCalcFieldValueLink = mpTextEditOutliner->GetCalcFieldValueHdl(); 1165 // FieldHdl has to be set by SdrBeginTextEdit, because this call an UpdateFields 1166 mpTextEditOutliner->SetCalcFieldValueHdl( 1167 LINK(this, SdrObjEditView, ImpOutlinerCalcFieldValueHdl)); 1168 mpTextEditOutliner->SetBeginPasteOrDropHdl(LINK(this, SdrObjEditView, BeginPasteOrDropHdl)); 1169 mpTextEditOutliner->SetEndPasteOrDropHdl(LINK(this, SdrObjEditView, EndPasteOrDropHdl)); 1170 1171 // It is just necessary to make the visualized page known. Set it. 1172 mpTextEditOutliner->setVisualizedPage(pPV->GetPage()); 1173 1174 mpTextEditOutliner->SetTextObjNoInit(mxWeakTextEditObj.get()); 1175 1176 if (mxWeakTextEditObj->BegTextEdit(*mpTextEditOutliner)) 1177 { 1178 SdrTextObj* pTextObj = mxWeakTextEditObj.get(); 1179 DBG_ASSERT(pTextObj, "svx::SdrObjEditView::BegTextEdit(), no text object?"); 1180 if (!pTextObj) 1181 return false; 1182 1183 // switch off any running TextAnimations 1184 pTextObj->SetTextAnimationAllowed(false); 1185 1186 // remember old cursor 1187 if (mpTextEditOutliner->GetViewCount() != 0) 1188 { 1189 mpTextEditOutliner->RemoveView(static_cast<size_t>(0)); 1190 } 1191 1192 // Determine EditArea via TakeTextEditArea. 1193 // TODO: This could theoretically be left out, because TakeTextRect() calculates the aTextEditArea, 1194 // but aMinTextEditArea has to happen, too (therefore leaving this in right now) 1195 pTextObj->TakeTextEditArea(nullptr, nullptr, &aTextEditArea, &aMinTextEditArea); 1196 1197 tools::Rectangle aTextRect; 1198 tools::Rectangle aAnchorRect; 1199 pTextObj->TakeTextRect(*mpTextEditOutliner, aTextRect, true, 1200 &aAnchorRect /* Give true here, not false */); 1201 1202 if (!pTextObj->IsContourTextFrame()) 1203 { 1204 // FitToSize not together with ContourFrame, for now 1205 if (pTextObj->IsFitToSize()) 1206 aTextRect = aAnchorRect; 1207 } 1208 1209 aTextEditArea = aTextRect; 1210 1211 // add possible GridOffset to up-to-now view-independent EditAreas 1212 basegfx::B2DVector aGridOffset(0.0, 0.0); 1213 if (getPossibleGridOffsetForSdrObject(aGridOffset, pTextObj, pPV)) 1214 { 1215 const Point aOffset(basegfx::fround(aGridOffset.getX()), 1216 basegfx::fround(aGridOffset.getY())); 1217 1218 aTextEditArea += aOffset; 1219 aMinTextEditArea += aOffset; 1220 } 1221 1222 Point aPvOfs(pTextObj->GetTextEditOffset()); 1223 aTextEditArea.Move(aPvOfs.X(), aPvOfs.Y()); 1224 aMinTextEditArea.Move(aPvOfs.X(), aPvOfs.Y()); 1225 pTextEditCursorBuffer = pWin->GetCursor(); 1226 1227 maHdlList.SetMoveOutside(true); 1228 1229 // Since IsMarkHdlWhenTextEdit() is ignored, it is necessary 1230 // to call AdjustMarkHdl() always. 1231 AdjustMarkHdl(); 1232 1233 mpTextEditOutlinerView = ImpMakeOutlinerView(pWin, pGivenOutlinerView); 1234 1235 if (!comphelper::LibreOfficeKit::isActive() && mpTextEditOutlinerView) 1236 { 1237 // activate visualization of EditView on Overlay, suppress when 1238 // LibreOfficeKit is active 1239 mpTextEditOutlinerView->GetEditView().setEditViewCallbacks(this); 1240 1241 const Color aHilightColor(SvtOptionsDrawinglayer::getHilightColor()); 1242 const SdrTextObj* pText = GetTextEditObject(); 1243 const bool bTextFrame(pText && pText->IsTextFrame()); 1244 const bool bFitToSize(mpTextEditOutliner->GetControlWord() 1245 & EEControlBits::STRETCHING); 1246 const bool bVisualizeSurroundingFrame(bTextFrame && !bFitToSize); 1247 SdrPageView* pPageView = GetSdrPageView(); 1248 1249 if (pPageView) 1250 { 1251 for (sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++) 1252 { 1253 const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b); 1254 1255 if (rPageWindow.GetPaintWindow().OutputToWindow()) 1256 { 1257 const rtl::Reference<sdr::overlay::OverlayManager>& xManager 1258 = rPageWindow.GetOverlayManager(); 1259 if (xManager.is()) 1260 { 1261 std::unique_ptr<TextEditOverlayObject> pNewTextEditOverlayObject( 1262 new TextEditOverlayObject(aHilightColor, 1263 *mpTextEditOutlinerView, 1264 bVisualizeSurroundingFrame)); 1265 1266 xManager->add(*pNewTextEditOverlayObject); 1267 xManager->add(const_cast<sdr::overlay::OverlaySelection&>( 1268 *pNewTextEditOverlayObject->getOverlaySelection())); 1269 1270 maTEOverlayGroup.append(std::move(pNewTextEditOverlayObject)); 1271 } 1272 } 1273 } 1274 } 1275 } 1276 1277 // check if this view is already inserted 1278 size_t i2, nCount = mpTextEditOutliner->GetViewCount(); 1279 for (i2 = 0; i2 < nCount; i2++) 1280 { 1281 if (mpTextEditOutliner->GetView(i2) == mpTextEditOutlinerView) 1282 break; 1283 } 1284 1285 if (i2 == nCount) 1286 mpTextEditOutliner->InsertView(mpTextEditOutlinerView, 0); 1287 1288 maHdlList.SetMoveOutside(false); 1289 maHdlList.SetMoveOutside(true); 1290 1291 // register all windows as OutlinerViews with the Outliner 1292 if (!bOnlyOneView) 1293 { 1294 for (sal_uInt32 i = 0; i < nWinCount; i++) 1295 { 1296 SdrPaintWindow* pPaintWindow = GetPaintWindow(i); 1297 OutputDevice& rOutDev = pPaintWindow->GetOutputDevice(); 1298 1299 if (&rOutDev != pWin->GetOutDev() && OUTDEV_WINDOW == rOutDev.GetOutDevType()) 1300 { 1301 OutlinerView* pOutlView 1302 = ImpMakeOutlinerView(rOutDev.GetOwnerWindow(), nullptr); 1303 mpTextEditOutliner->InsertView(pOutlView, static_cast<sal_uInt16>(i)); 1304 } 1305 } 1306 1307 if (comphelper::LibreOfficeKit::isActive()) 1308 { 1309 // Register an outliner view for all other sdr views that 1310 // show the same page, so that when the text edit changes, 1311 // all interested windows get an invalidation. 1312 SdrViewIter aIter(pObj->getSdrPageFromSdrObject()); 1313 for (SdrView* pView = aIter.FirstView(); pView; pView = aIter.NextView()) 1314 { 1315 if (pView == this) 1316 continue; 1317 1318 for (sal_uInt32 nViewPaintWindow = 0; 1319 nViewPaintWindow < pView->PaintWindowCount(); ++nViewPaintWindow) 1320 { 1321 SdrPaintWindow* pPaintWindow = pView->GetPaintWindow(nViewPaintWindow); 1322 OutputDevice& rOutDev = pPaintWindow->GetOutputDevice(); 1323 1324 if (&rOutDev != pWin->GetOutDev() 1325 && OUTDEV_WINDOW == rOutDev.GetOutDevType()) 1326 { 1327 OutlinerView* pOutlView 1328 = ImpMakeOutlinerView(rOutDev.GetOwnerWindow(), nullptr); 1329 pOutlView->HideCursor(); 1330 rOutDev.GetOwnerWindow()->SetCursor(nullptr); 1331 mpTextEditOutliner->InsertView(pOutlView); 1332 } 1333 } 1334 } 1335 } 1336 } 1337 1338 mpTextEditOutlinerView->ShowCursor(); 1339 mpTextEditOutliner->SetStatusEventHdl( 1340 LINK(this, SdrObjEditView, ImpOutlinerStatusEventHdl)); 1341 if (pTextObj->IsChainable()) 1342 { 1343 mpTextEditOutlinerView->SetEndCutPasteLinkHdl( 1344 LINK(this, SdrObjEditView, ImpAfterCutOrPasteChainingEventHdl)); 1345 } 1346 1347 mpTextEditOutliner->ClearModifyFlag(); 1348 1349 if (pTextObj->IsFitToSize()) 1350 { 1351 pWin->Invalidate(aTextEditArea); 1352 } 1353 1354 if (GetModel()) 1355 { 1356 SdrHint aHint(SdrHintKind::BeginEdit, *pTextObj); 1357 GetModel()->Broadcast(aHint); 1358 } 1359 1360 mpTextEditOutliner->setVisualizedPage(nullptr); 1361 1362 if (mxSelectionController.is()) 1363 mxSelectionController->onSelectionHasChanged(); 1364 1365 if (GetModel() && IsUndoEnabled() 1366 && !GetModel()->GetDisableTextEditUsesCommonUndoManager()) 1367 { 1368 SdrUndoManager* pSdrUndoManager = nullptr; 1369 mpLocalTextEditUndoManager = createLocalTextUndoManager(); 1370 1371 if (mpLocalTextEditUndoManager) 1372 pSdrUndoManager = mpLocalTextEditUndoManager.get(); 1373 1374 if (pSdrUndoManager) 1375 { 1376 // we have an outliner, undo manager and it's an EditUndoManager, exchange 1377 // the document undo manager and the default one from the outliner and tell 1378 // it that text edit starts by setting a callback if it needs to end text edit mode. 1379 assert(nullptr == mpOldTextEditUndoManager); 1380 1381 mpOldTextEditUndoManager = mpTextEditOutliner->SetUndoManager(pSdrUndoManager); 1382 pSdrUndoManager->SetEndTextEditHdl(LINK(this, SdrObjEditView, EndTextEditHdl)); 1383 } 1384 else 1385 { 1386 OSL_ENSURE(false, 1387 "The document undo manager is not derived from SdrUndoManager (!)"); 1388 } 1389 } 1390 1391 return true; // ran fine, let TextEdit run now 1392 } 1393 else 1394 { 1395 mpTextEditOutliner->SetCalcFieldValueHdl(aOldCalcFieldValueLink); 1396 mpTextEditOutliner->SetBeginPasteOrDropHdl(Link<PasteOrDropInfos*, void>()); 1397 mpTextEditOutliner->SetEndPasteOrDropHdl(Link<PasteOrDropInfos*, void>()); 1398 } 1399 } 1400 if (mpTextEditOutliner != nullptr) 1401 { 1402 mpTextEditOutliner->setVisualizedPage(nullptr); 1403 } 1404 1405 // something went wrong... 1406 if (!bDontDeleteOutliner) 1407 { 1408 delete pGivenOutliner; 1409 if (pGivenOutlinerView != nullptr) 1410 { 1411 delete pGivenOutlinerView; 1412 pGivenOutlinerView = nullptr; 1413 } 1414 } 1415 mpTextEditOutliner.reset(); 1416 1417 mpTextEditOutlinerView = nullptr; 1418 mxWeakTextEditObj.reset(nullptr); 1419 mpTextEditPV = nullptr; 1420 mpTextEditWin = nullptr; 1421 maHdlList.SetMoveOutside(false); 1422 1423 return false; 1424 } 1425 1426 SdrEndTextEditKind SdrObjEditView::SdrEndTextEdit(bool bDontDeleteReally) 1427 { 1428 SdrEndTextEditKind eRet = SdrEndTextEditKind::Unchanged; 1429 SdrTextObj* pTEObj = mxWeakTextEditObj.get(); 1430 vcl::Window* pTEWin = mpTextEditWin; 1431 OutlinerView* pTEOutlinerView = mpTextEditOutlinerView; 1432 vcl::Cursor* pTECursorBuffer = pTextEditCursorBuffer; 1433 SdrUndoManager* pUndoEditUndoManager = nullptr; 1434 bool bNeedToUndoSavedRedoTextEdit(false); 1435 1436 if (GetModel() && IsUndoEnabled() && pTEObj && mpTextEditOutliner 1437 && !GetModel()->GetDisableTextEditUsesCommonUndoManager()) 1438 { 1439 // change back the UndoManager to the remembered original one 1440 SfxUndoManager* pOriginal = mpTextEditOutliner->SetUndoManager(mpOldTextEditUndoManager); 1441 mpOldTextEditUndoManager = nullptr; 1442 1443 if (pOriginal) 1444 { 1445 // check if we got back our document undo manager 1446 SdrUndoManager* pSdrUndoManager = mpLocalTextEditUndoManager.get(); 1447 1448 if (pSdrUndoManager && dynamic_cast<SdrUndoManager*>(pOriginal) == pSdrUndoManager) 1449 { 1450 if (pSdrUndoManager->isEndTextEditTriggeredFromUndo()) 1451 { 1452 // remember the UndoManager where missing Undos have to be triggered after end 1453 // text edit. When the undo had triggered the end text edit, the original action 1454 // which had to be undone originally is not yet undone. 1455 pUndoEditUndoManager = pSdrUndoManager; 1456 1457 // We are ending text edit; if text edit was triggered from undo, execute all redos 1458 // to create a complete text change undo action for the redo buffer. Also mark this 1459 // state when at least one redo was executed; the created extra TextChange needs to 1460 // be undone in addition to the first real undo outside the text edit changes 1461 while (pSdrUndoManager->GetRedoActionCount() 1462 > pSdrUndoManager->GetRedoActionCountBeforeTextEdit()) 1463 { 1464 bNeedToUndoSavedRedoTextEdit = true; 1465 pSdrUndoManager->Redo(); 1466 } 1467 } 1468 1469 // reset the callback link and let the undo manager cleanup all text edit 1470 // undo actions to get the stack back to the form before the text edit 1471 pSdrUndoManager->SetEndTextEditHdl(Link<SdrUndoManager*, void>()); 1472 } 1473 else 1474 { 1475 OSL_ENSURE(false, "Got UndoManager back in SdrEndTextEdit which is NOT the " 1476 "expected document UndoManager (!)"); 1477 delete pOriginal; 1478 } 1479 1480 // cid#1493241 - Wrapper object use after free 1481 if (pUndoEditUndoManager == mpLocalTextEditUndoManager.get()) 1482 pUndoEditUndoManager = nullptr; 1483 mpLocalTextEditUndoManager.reset(); 1484 } 1485 } 1486 else 1487 { 1488 assert(nullptr == mpOldTextEditUndoManager); // cannot be restored! 1489 } 1490 1491 if (GetModel() && mxWeakTextEditObj.is()) 1492 { 1493 SdrHint aHint(SdrHintKind::EndEdit, *mxWeakTextEditObj); 1494 GetModel()->Broadcast(aHint); 1495 } 1496 1497 // if new mechanism was used, clean it up. At cleanup no need to check 1498 // for LibreOfficeKit 1499 if (mpTextEditOutlinerView) 1500 { 1501 mpTextEditOutlinerView->GetEditView().setEditViewCallbacks(nullptr); 1502 maTEOverlayGroup.clear(); 1503 } 1504 1505 mxWeakTextEditObj.reset(nullptr); 1506 mpTextEditPV = nullptr; 1507 mpTextEditWin = nullptr; 1508 SdrOutliner* pTEOutliner = mpTextEditOutliner.release(); 1509 mpTextEditOutlinerView = nullptr; 1510 pTextEditCursorBuffer = nullptr; 1511 aTextEditArea = tools::Rectangle(); 1512 1513 if (pTEOutliner != nullptr) 1514 { 1515 bool bModified = pTEOutliner->IsModified(); 1516 if (pTEOutlinerView != nullptr) 1517 { 1518 pTEOutlinerView->HideCursor(); 1519 } 1520 if (pTEObj != nullptr) 1521 { 1522 pTEOutliner->CompleteOnlineSpelling(); 1523 1524 std::unique_ptr<SdrUndoObjSetText> pTxtUndo; 1525 1526 if (bModified) 1527 { 1528 sal_Int32 nText; 1529 for (nText = 0; nText < pTEObj->getTextCount(); ++nText) 1530 if (pTEObj->getText(nText) == pTEObj->getActiveText()) 1531 break; 1532 1533 pTxtUndo.reset( 1534 dynamic_cast<SdrUndoObjSetText*>(GetModel() 1535 ->GetSdrUndoFactory() 1536 .CreateUndoObjectSetText(*pTEObj, nText) 1537 .release())); 1538 } 1539 DBG_ASSERT(!bModified || pTxtUndo, 1540 "svx::SdrObjEditView::EndTextEdit(), could not create undo action!"); 1541 // Set old CalcFieldValue-Handler again, this 1542 // has to happen before Obj::EndTextEdit(), as this does UpdateFields(). 1543 pTEOutliner->SetCalcFieldValueHdl(aOldCalcFieldValueLink); 1544 pTEOutliner->SetBeginPasteOrDropHdl(Link<PasteOrDropInfos*, void>()); 1545 pTEOutliner->SetEndPasteOrDropHdl(Link<PasteOrDropInfos*, void>()); 1546 1547 const bool bUndo = IsUndoEnabled(); 1548 if (bUndo) 1549 { 1550 OUString aObjName(pTEObj->TakeObjNameSingul()); 1551 BegUndo(SvxResId(STR_UndoObjSetText), aObjName); 1552 } 1553 1554 pTEObj->EndTextEdit(*pTEOutliner); 1555 1556 if ((pTEObj->GetRotateAngle() != 0_deg100) || (pTEObj && pTEObj->IsFontwork())) 1557 { 1558 pTEObj->ActionChanged(); 1559 } 1560 1561 if (pTxtUndo != nullptr) 1562 { 1563 pTxtUndo->AfterSetText(); 1564 if (!pTxtUndo->IsDifferent()) 1565 { 1566 pTxtUndo.reset(); 1567 } 1568 } 1569 // check deletion of entire TextObj 1570 std::unique_ptr<SdrUndoAction> pDelUndo; 1571 bool bDelObj = false; 1572 if (mbTextEditNewObj) 1573 { 1574 bDelObj = pTEObj->IsTextFrame() && !pTEObj->HasText() && !pTEObj->IsEmptyPresObj() 1575 && !pTEObj->HasFill() && !pTEObj->HasLine(); 1576 1577 if (pTEObj->IsInserted() && bDelObj 1578 && pTEObj->GetObjInventor() == SdrInventor::Default && !bDontDeleteReally) 1579 { 1580 SdrObjKind eIdent = pTEObj->GetObjIdentifier(); 1581 if (eIdent == SdrObjKind::Text) 1582 { 1583 pDelUndo = GetModel()->GetSdrUndoFactory().CreateUndoDeleteObject(*pTEObj); 1584 } 1585 } 1586 } 1587 if (pTxtUndo) 1588 { 1589 if (bUndo) 1590 AddUndo(std::move(pTxtUndo)); 1591 eRet = SdrEndTextEditKind::Changed; 1592 } 1593 if (pDelUndo != nullptr) 1594 { 1595 if (bUndo) 1596 { 1597 AddUndo(std::move(pDelUndo)); 1598 } 1599 eRet = SdrEndTextEditKind::Deleted; 1600 DBG_ASSERT(pTEObj->getParentSdrObjListFromSdrObject() != nullptr, 1601 "SdrObjEditView::SdrEndTextEdit(): Fatal: Object edited doesn't have an " 1602 "ObjList!"); 1603 if (pTEObj->getParentSdrObjListFromSdrObject() != nullptr) 1604 { 1605 pTEObj->getParentSdrObjListFromSdrObject()->RemoveObject(pTEObj->GetOrdNum()); 1606 CheckMarked(); // remove selection immediately... 1607 } 1608 } 1609 else if (bDelObj) 1610 { // for Writer: the app has to do the deletion itself. 1611 eRet = SdrEndTextEditKind::ShouldBeDeleted; 1612 } 1613 1614 if (bUndo) 1615 EndUndo(); // EndUndo after Remove, in case UndoStack is deleted immediately 1616 1617 // Switch on any TextAnimation again after TextEdit 1618 if (pTEObj) 1619 { 1620 pTEObj->SetTextAnimationAllowed(true); 1621 } 1622 1623 // Since IsMarkHdlWhenTextEdit() is ignored, it is necessary 1624 // to call AdjustMarkHdl() always. 1625 AdjustMarkHdl(); 1626 } 1627 // delete all OutlinerViews 1628 for (size_t i = pTEOutliner->GetViewCount(); i > 0;) 1629 { 1630 i--; 1631 OutlinerView* pOLV = pTEOutliner->GetView(i); 1632 sal_uInt16 nMorePix = pOLV->GetInvalidateMore() + 10; 1633 vcl::Window* pWin = pOLV->GetWindow(); 1634 tools::Rectangle aRect(pOLV->GetOutputArea()); 1635 pTEOutliner->RemoveView(i); 1636 if (!mbTextEditDontDelete || i != 0) 1637 { 1638 // may not own the zeroth one 1639 delete pOLV; 1640 } 1641 aRect.Union(aTextEditArea); 1642 aRect.Union(aMinTextEditArea); 1643 aRect = pWin->LogicToPixel(aRect); 1644 aRect.AdjustLeft(-nMorePix); 1645 aRect.AdjustTop(-nMorePix); 1646 aRect.AdjustRight(nMorePix); 1647 aRect.AdjustBottom(nMorePix); 1648 aRect = pWin->PixelToLogic(aRect); 1649 InvalidateOneWin(*pWin->GetOutDev(), aRect); 1650 pWin->GetOutDev()->SetFillColor(); 1651 pWin->GetOutDev()->SetLineColor(COL_BLACK); 1652 } 1653 // and now the Outliner itself 1654 if (!mbTextEditDontDelete) 1655 delete pTEOutliner; 1656 else 1657 pTEOutliner->Clear(); 1658 if (pTEWin != nullptr) 1659 { 1660 pTEWin->SetCursor(pTECursorBuffer); 1661 } 1662 maHdlList.SetMoveOutside(false); 1663 if (eRet != SdrEndTextEditKind::Unchanged) 1664 { 1665 GetMarkedObjectListWriteAccess().SetNameDirty(); 1666 } 1667 } 1668 1669 if (pTEObj && !pTEObj->getSdrModelFromSdrObject().isLocked() && pTEObj->GetBroadcaster()) 1670 { 1671 SdrHint aHint(SdrHintKind::EndEdit, *pTEObj); 1672 const_cast<SfxBroadcaster*>(pTEObj->GetBroadcaster())->Broadcast(aHint); 1673 } 1674 1675 if (pUndoEditUndoManager) 1676 { 1677 if (bNeedToUndoSavedRedoTextEdit) 1678 { 1679 // undo the text edit action since it was created as part of an EndTextEdit 1680 // callback from undo itself. This needs to be done after the call to 1681 // FmFormView::SdrEndTextEdit since it gets created there 1682 pUndoEditUndoManager->Undo(); 1683 } 1684 1685 // trigger the Undo which was not executed, but lead to this 1686 // end text edit 1687 pUndoEditUndoManager->Undo(); 1688 } 1689 1690 return eRet; 1691 } 1692 1693 // info about TextEdit. Default is false. 1694 bool SdrObjEditView::IsTextEdit() const { return mxWeakTextEditObj.is(); } 1695 1696 // info about TextEditPageView. Default is 0L. 1697 SdrPageView* SdrObjEditView::GetTextEditPageView() const { return mpTextEditPV; } 1698 1699 OutlinerView* SdrObjEditView::ImpFindOutlinerView(vcl::Window const* pWin) const 1700 { 1701 if (pWin == nullptr) 1702 return nullptr; 1703 if (mpTextEditOutliner == nullptr) 1704 return nullptr; 1705 OutlinerView* pNewView = nullptr; 1706 size_t nWinCount = mpTextEditOutliner->GetViewCount(); 1707 for (size_t i = 0; i < nWinCount && pNewView == nullptr; i++) 1708 { 1709 OutlinerView* pView = mpTextEditOutliner->GetView(i); 1710 if (pView->GetWindow() == pWin) 1711 pNewView = pView; 1712 } 1713 return pNewView; 1714 } 1715 1716 void SdrObjEditView::SetTextEditWin(vcl::Window* pWin) 1717 { 1718 if (!(mxWeakTextEditObj.is() && pWin != nullptr && pWin != mpTextEditWin)) 1719 return; 1720 1721 OutlinerView* pNewView = ImpFindOutlinerView(pWin); 1722 if (pNewView != nullptr && pNewView != mpTextEditOutlinerView) 1723 { 1724 if (mpTextEditOutlinerView != nullptr) 1725 { 1726 mpTextEditOutlinerView->HideCursor(); 1727 } 1728 mpTextEditOutlinerView = pNewView; 1729 mpTextEditWin = pWin; 1730 pWin->GrabFocus(); // Make the cursor blink here as well 1731 pNewView->ShowCursor(); 1732 ImpMakeTextCursorAreaVisible(); 1733 } 1734 } 1735 1736 bool SdrObjEditView::IsTextEditHit(const Point& rHit) const 1737 { 1738 bool bOk = false; 1739 if (mxWeakTextEditObj.is()) 1740 { 1741 tools::Rectangle aEditArea; 1742 if (OutlinerView* pOLV = mpTextEditOutliner->GetView(0)) 1743 aEditArea.Union(pOLV->GetOutputArea()); 1744 1745 if (aEditArea.Contains(rHit)) 1746 { // check if any characters were actually hit 1747 const Point aPnt(rHit - aEditArea.TopLeft()); 1748 tools::Long nHitTol = 2000; 1749 if (OutputDevice* pRef = mpTextEditOutliner->GetRefDevice()) 1750 nHitTol = OutputDevice::LogicToLogic(nHitTol, MapUnit::Map100thMM, 1751 pRef->GetMapMode().GetMapUnit()); 1752 1753 bOk = mpTextEditOutliner->IsTextPos(aPnt, static_cast<sal_uInt16>(nHitTol)); 1754 } 1755 } 1756 return bOk; 1757 } 1758 1759 bool SdrObjEditView::IsTextEditFrameHit(const Point& rHit) const 1760 { 1761 bool bOk = false; 1762 if (mxWeakTextEditObj.is()) 1763 { 1764 SdrTextObj* pText = mxWeakTextEditObj.get(); 1765 OutlinerView* pOLV = mpTextEditOutliner->GetView(0); 1766 if (pOLV) 1767 { 1768 vcl::Window* pWin = pOLV->GetWindow(); 1769 if (pText != nullptr && pText->IsTextFrame() && pWin != nullptr) 1770 { 1771 sal_uInt16 nPixSiz = pOLV->GetInvalidateMore(); 1772 tools::Rectangle aEditArea(aMinTextEditArea); 1773 aEditArea.Union(pOLV->GetOutputArea()); 1774 if (!aEditArea.Contains(rHit)) 1775 { 1776 Size aSiz(pWin->PixelToLogic(Size(nPixSiz, nPixSiz))); 1777 aEditArea.AdjustLeft(-(aSiz.Width())); 1778 aEditArea.AdjustTop(-(aSiz.Height())); 1779 aEditArea.AdjustRight(aSiz.Width()); 1780 aEditArea.AdjustBottom(aSiz.Height()); 1781 bOk = aEditArea.Contains(rHit); 1782 } 1783 } 1784 } 1785 } 1786 return bOk; 1787 } 1788 1789 std::unique_ptr<TextChainCursorManager> 1790 SdrObjEditView::ImpHandleMotionThroughBoxesKeyInput(const KeyEvent& rKEvt, bool* bOutHandled) 1791 { 1792 *bOutHandled = false; 1793 1794 SdrTextObj* pTextObj = mxWeakTextEditObj.get(); 1795 if (!pTextObj) 1796 return nullptr; 1797 1798 if (!pTextObj->GetNextLinkInChain() && !pTextObj->GetPrevLinkInChain()) 1799 return nullptr; 1800 1801 std::unique_ptr<TextChainCursorManager> pCursorManager( 1802 new TextChainCursorManager(this, pTextObj)); 1803 if (pCursorManager->HandleKeyEvent(rKEvt)) 1804 { 1805 // Possibly do other stuff here if necessary... 1806 // XXX: Careful with the checks below (in KeyInput) for pWin and co. You should do them here I guess. 1807 *bOutHandled = true; 1808 } 1809 1810 return pCursorManager; 1811 } 1812 1813 bool SdrObjEditView::KeyInput(const KeyEvent& rKEvt, vcl::Window* pWin) 1814 { 1815 if (mpTextEditOutlinerView) 1816 { 1817 /* Start special handling of keys within a chain */ 1818 // We possibly move to another box before any handling 1819 bool bHandled = false; 1820 std::unique_ptr<TextChainCursorManager> xCursorManager( 1821 ImpHandleMotionThroughBoxesKeyInput(rKEvt, &bHandled)); 1822 if (bHandled) 1823 return true; 1824 /* End special handling of keys within a chain */ 1825 1826 if (mpTextEditOutlinerView->PostKeyEvent(rKEvt, pWin)) 1827 { 1828 if (mpModel) 1829 { 1830 if (mpTextEditOutliner && mpTextEditOutliner->IsModified()) 1831 mpModel->SetChanged(); 1832 } 1833 1834 /* Start chaining processing */ 1835 ImpChainingEventHdl(); 1836 ImpMoveCursorAfterChainingEvent(xCursorManager.get()); 1837 /* End chaining processing */ 1838 1839 if (pWin != nullptr && pWin != mpTextEditWin) 1840 SetTextEditWin(pWin); 1841 ImpMakeTextCursorAreaVisible(); 1842 return true; 1843 } 1844 } 1845 return SdrGlueEditView::KeyInput(rKEvt, pWin); 1846 } 1847 1848 bool SdrObjEditView::MouseButtonDown(const MouseEvent& rMEvt, OutputDevice* pWin) 1849 { 1850 if (mpTextEditOutlinerView != nullptr) 1851 { 1852 bool bPostIt = mpTextEditOutliner->IsInSelectionMode(); 1853 if (!bPostIt) 1854 { 1855 Point aPt(rMEvt.GetPosPixel()); 1856 if (pWin != nullptr) 1857 aPt = pWin->PixelToLogic(aPt); 1858 else if (mpTextEditWin != nullptr) 1859 aPt = mpTextEditWin->PixelToLogic(aPt); 1860 bPostIt = IsTextEditHit(aPt); 1861 } 1862 if (bPostIt) 1863 { 1864 Point aPixPos(rMEvt.GetPosPixel()); 1865 if (pWin) 1866 { 1867 tools::Rectangle aR(pWin->LogicToPixel(mpTextEditOutlinerView->GetOutputArea())); 1868 if (aPixPos.X() < aR.Left()) 1869 aPixPos.setX(aR.Left()); 1870 if (aPixPos.X() > aR.Right()) 1871 aPixPos.setX(aR.Right()); 1872 if (aPixPos.Y() < aR.Top()) 1873 aPixPos.setY(aR.Top()); 1874 if (aPixPos.Y() > aR.Bottom()) 1875 aPixPos.setY(aR.Bottom()); 1876 } 1877 MouseEvent aMEvt(aPixPos, rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(), 1878 rMEvt.GetModifier()); 1879 if (mpTextEditOutlinerView->MouseButtonDown(aMEvt)) 1880 { 1881 if (pWin != nullptr && pWin != mpTextEditWin->GetOutDev() 1882 && pWin->GetOutDevType() == OUTDEV_WINDOW) 1883 SetTextEditWin(pWin->GetOwnerWindow()); 1884 ImpMakeTextCursorAreaVisible(); 1885 return true; 1886 } 1887 } 1888 } 1889 return SdrGlueEditView::MouseButtonDown(rMEvt, pWin); 1890 } 1891 1892 bool SdrObjEditView::MouseButtonUp(const MouseEvent& rMEvt, OutputDevice* pWin) 1893 { 1894 if (mpTextEditOutlinerView != nullptr) 1895 { 1896 bool bPostIt = mpTextEditOutliner->IsInSelectionMode(); 1897 if (!bPostIt) 1898 { 1899 Point aPt(rMEvt.GetPosPixel()); 1900 if (pWin != nullptr) 1901 aPt = pWin->PixelToLogic(aPt); 1902 else if (mpTextEditWin != nullptr) 1903 aPt = mpTextEditWin->PixelToLogic(aPt); 1904 bPostIt = IsTextEditHit(aPt); 1905 } 1906 if (bPostIt && pWin) 1907 { 1908 Point aPixPos(rMEvt.GetPosPixel()); 1909 tools::Rectangle aR(pWin->LogicToPixel(mpTextEditOutlinerView->GetOutputArea())); 1910 if (aPixPos.X() < aR.Left()) 1911 aPixPos.setX(aR.Left()); 1912 if (aPixPos.X() > aR.Right()) 1913 aPixPos.setX(aR.Right()); 1914 if (aPixPos.Y() < aR.Top()) 1915 aPixPos.setY(aR.Top()); 1916 if (aPixPos.Y() > aR.Bottom()) 1917 aPixPos.setY(aR.Bottom()); 1918 MouseEvent aMEvt(aPixPos, rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(), 1919 rMEvt.GetModifier()); 1920 if (mpTextEditOutlinerView->MouseButtonUp(aMEvt)) 1921 { 1922 ImpMakeTextCursorAreaVisible(); 1923 return true; 1924 } 1925 } 1926 } 1927 return SdrGlueEditView::MouseButtonUp(rMEvt, pWin); 1928 } 1929 1930 bool SdrObjEditView::MouseMove(const MouseEvent& rMEvt, OutputDevice* pWin) 1931 { 1932 if (mpTextEditOutlinerView != nullptr) 1933 { 1934 bool bSelMode = mpTextEditOutliner->IsInSelectionMode(); 1935 bool bPostIt = bSelMode; 1936 if (!bPostIt) 1937 { 1938 Point aPt(rMEvt.GetPosPixel()); 1939 if (pWin) 1940 aPt = pWin->PixelToLogic(aPt); 1941 else if (mpTextEditWin) 1942 aPt = mpTextEditWin->PixelToLogic(aPt); 1943 bPostIt = IsTextEditHit(aPt); 1944 } 1945 if (bPostIt) 1946 { 1947 Point aPixPos(rMEvt.GetPosPixel()); 1948 tools::Rectangle aR(mpTextEditOutlinerView->GetOutputArea()); 1949 if (pWin) 1950 aR = pWin->LogicToPixel(aR); 1951 else if (mpTextEditWin) 1952 aR = mpTextEditWin->LogicToPixel(aR); 1953 if (aPixPos.X() < aR.Left()) 1954 aPixPos.setX(aR.Left()); 1955 if (aPixPos.X() > aR.Right()) 1956 aPixPos.setX(aR.Right()); 1957 if (aPixPos.Y() < aR.Top()) 1958 aPixPos.setY(aR.Top()); 1959 if (aPixPos.Y() > aR.Bottom()) 1960 aPixPos.setY(aR.Bottom()); 1961 MouseEvent aMEvt(aPixPos, rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(), 1962 rMEvt.GetModifier()); 1963 if (mpTextEditOutlinerView->MouseMove(aMEvt) && bSelMode) 1964 { 1965 ImpMakeTextCursorAreaVisible(); 1966 return true; 1967 } 1968 } 1969 } 1970 return SdrGlueEditView::MouseMove(rMEvt, pWin); 1971 } 1972 1973 bool SdrObjEditView::Command(const CommandEvent& rCEvt, vcl::Window* pWin) 1974 { 1975 // as long as OutlinerView returns a sal_Bool, it only gets CommandEventId::StartDrag 1976 if (mpTextEditOutlinerView != nullptr) 1977 { 1978 if (rCEvt.GetCommand() == CommandEventId::StartDrag) 1979 { 1980 bool bPostIt = mpTextEditOutliner->IsInSelectionMode() || !rCEvt.IsMouseEvent(); 1981 if (!bPostIt && rCEvt.IsMouseEvent()) 1982 { 1983 Point aPt(rCEvt.GetMousePosPixel()); 1984 if (pWin != nullptr) 1985 aPt = pWin->PixelToLogic(aPt); 1986 else if (mpTextEditWin != nullptr) 1987 aPt = mpTextEditWin->PixelToLogic(aPt); 1988 bPostIt = IsTextEditHit(aPt); 1989 } 1990 if (bPostIt) 1991 { 1992 Point aPixPos(rCEvt.GetMousePosPixel()); 1993 if (rCEvt.IsMouseEvent() && pWin) 1994 { 1995 tools::Rectangle aR( 1996 pWin->LogicToPixel(mpTextEditOutlinerView->GetOutputArea())); 1997 if (aPixPos.X() < aR.Left()) 1998 aPixPos.setX(aR.Left()); 1999 if (aPixPos.X() > aR.Right()) 2000 aPixPos.setX(aR.Right()); 2001 if (aPixPos.Y() < aR.Top()) 2002 aPixPos.setY(aR.Top()); 2003 if (aPixPos.Y() > aR.Bottom()) 2004 aPixPos.setY(aR.Bottom()); 2005 } 2006 CommandEvent aCEvt(aPixPos, rCEvt.GetCommand(), rCEvt.IsMouseEvent()); 2007 // Command is void at the OutlinerView, sadly 2008 mpTextEditOutlinerView->Command(aCEvt); 2009 if (pWin != nullptr && pWin != mpTextEditWin) 2010 SetTextEditWin(pWin); 2011 ImpMakeTextCursorAreaVisible(); 2012 return true; 2013 } 2014 } 2015 else 2016 { 2017 mpTextEditOutlinerView->Command(rCEvt); 2018 if (mpModel && comphelper::LibreOfficeKit::isActive()) 2019 { 2020 // It could execute CommandEventId::ExtTextInput, while SdrObjEditView::KeyInput 2021 // isn't called 2022 if (mpTextEditOutliner && mpTextEditOutliner->IsModified()) 2023 mpModel->SetChanged(); 2024 } 2025 return true; 2026 } 2027 } 2028 return SdrGlueEditView::Command(rCEvt, pWin); 2029 } 2030 2031 bool SdrObjEditView::ImpIsTextEditAllSelected() const 2032 { 2033 bool bRet = false; 2034 if (mpTextEditOutliner != nullptr && mpTextEditOutlinerView != nullptr) 2035 { 2036 if (SdrTextObj::HasTextImpl(mpTextEditOutliner.get())) 2037 { 2038 const sal_Int32 nParaCnt = mpTextEditOutliner->GetParagraphCount(); 2039 Paragraph* pLastPara 2040 = mpTextEditOutliner->GetParagraph(nParaCnt > 1 ? nParaCnt - 1 : 0); 2041 2042 ESelection aESel(mpTextEditOutlinerView->GetSelection()); 2043 if (aESel.nStartPara == 0 && aESel.nStartPos == 0 && aESel.nEndPara == (nParaCnt - 1)) 2044 { 2045 if (mpTextEditOutliner->GetText(pLastPara).getLength() == aESel.nEndPos) 2046 bRet = true; 2047 } 2048 // in case the selection was done backwards 2049 if (!bRet && aESel.nEndPara == 0 && aESel.nEndPos == 0 2050 && aESel.nStartPara == (nParaCnt - 1)) 2051 { 2052 if (mpTextEditOutliner->GetText(pLastPara).getLength() == aESel.nStartPos) 2053 bRet = true; 2054 } 2055 } 2056 else 2057 { 2058 bRet = true; 2059 } 2060 } 2061 return bRet; 2062 } 2063 2064 void SdrObjEditView::ImpMakeTextCursorAreaVisible() 2065 { 2066 if (mpTextEditOutlinerView != nullptr && mpTextEditWin != nullptr) 2067 { 2068 vcl::Cursor* pCsr = mpTextEditWin->GetCursor(); 2069 if (pCsr != nullptr) 2070 { 2071 Size aSiz(pCsr->GetSize()); 2072 if (!aSiz.IsEmpty()) 2073 { 2074 MakeVisible(tools::Rectangle(pCsr->GetPos(), aSiz), *mpTextEditWin); 2075 } 2076 } 2077 } 2078 } 2079 2080 SvtScriptType SdrObjEditView::GetScriptType() const 2081 { 2082 SvtScriptType nScriptType = SvtScriptType::NONE; 2083 2084 if (IsTextEdit()) 2085 { 2086 if (mxWeakTextEditObj->GetOutlinerParaObject()) 2087 nScriptType 2088 = mxWeakTextEditObj->GetOutlinerParaObject()->GetTextObject().GetScriptType(); 2089 2090 if (mpTextEditOutlinerView) 2091 nScriptType = mpTextEditOutlinerView->GetSelectedScriptType(); 2092 } 2093 else 2094 { 2095 const size_t nMarkCount(GetMarkedObjectCount()); 2096 2097 for (size_t i = 0; i < nMarkCount; ++i) 2098 { 2099 OutlinerParaObject* pParaObj = GetMarkedObjectByIndex(i)->GetOutlinerParaObject(); 2100 2101 if (pParaObj) 2102 { 2103 nScriptType |= pParaObj->GetTextObject().GetScriptType(); 2104 } 2105 } 2106 } 2107 2108 if (nScriptType == SvtScriptType::NONE) 2109 nScriptType = SvtScriptType::LATIN; 2110 2111 return nScriptType; 2112 } 2113 2114 void SdrObjEditView::GetAttributes(SfxItemSet& rTargetSet, bool bOnlyHardAttr) const 2115 { 2116 if (mxSelectionController.is()) 2117 if (mxSelectionController->GetAttributes(rTargetSet, bOnlyHardAttr)) 2118 return; 2119 2120 if (IsTextEdit()) 2121 { 2122 DBG_ASSERT(mpTextEditOutlinerView != nullptr, 2123 "SdrObjEditView::GetAttributes(): mpTextEditOutlinerView=NULL"); 2124 DBG_ASSERT(mpTextEditOutliner != nullptr, 2125 "SdrObjEditView::GetAttributes(): mpTextEditOutliner=NULL"); 2126 2127 // take care of bOnlyHardAttr(!) 2128 if (!bOnlyHardAttr && mxWeakTextEditObj->GetStyleSheet()) 2129 rTargetSet.Put(mxWeakTextEditObj->GetStyleSheet()->GetItemSet()); 2130 2131 // add object attributes 2132 rTargetSet.Put(mxWeakTextEditObj->GetMergedItemSet()); 2133 2134 if (mpTextEditOutlinerView) 2135 { 2136 // FALSE= regard InvalidItems as "holes," not as Default 2137 rTargetSet.Put(mpTextEditOutlinerView->GetAttribs(), false); 2138 } 2139 2140 if (GetMarkedObjectCount() == 1 && GetMarkedObjectByIndex(0) == mxWeakTextEditObj.get()) 2141 { 2142 MergeNotPersistAttrFromMarked(rTargetSet); 2143 } 2144 } 2145 else 2146 { 2147 SdrGlueEditView::GetAttributes(rTargetSet, bOnlyHardAttr); 2148 } 2149 } 2150 2151 bool SdrObjEditView::SetAttributes(const SfxItemSet& rSet, bool bReplaceAll) 2152 { 2153 bool bRet = false; 2154 bool bTextEdit = mpTextEditOutlinerView != nullptr && mxWeakTextEditObj.is(); 2155 bool bAllTextSelected = ImpIsTextEditAllSelected(); 2156 const SfxItemSet* pSet = &rSet; 2157 2158 if (!bTextEdit) 2159 { 2160 // no TextEdit active -> all Items to drawing object 2161 if (mxSelectionController.is()) 2162 bRet = mxSelectionController->SetAttributes(*pSet, bReplaceAll); 2163 2164 if (!bRet) 2165 { 2166 SdrGlueEditView::SetAttributes(*pSet, bReplaceAll); 2167 bRet = true; 2168 } 2169 } 2170 else 2171 { 2172 #ifdef DBG_UTIL 2173 { 2174 bool bHasEEFeatureItems = false; 2175 SfxItemIter aIter(rSet); 2176 for (const SfxPoolItem* pItem = aIter.GetCurItem(); !bHasEEFeatureItems && pItem; 2177 pItem = aIter.NextItem()) 2178 { 2179 if (!IsInvalidItem(pItem)) 2180 { 2181 sal_uInt16 nW = pItem->Which(); 2182 if (nW >= EE_FEATURE_START && nW <= EE_FEATURE_END) 2183 bHasEEFeatureItems = true; 2184 } 2185 } 2186 2187 if (bHasEEFeatureItems) 2188 { 2189 std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog( 2190 nullptr, VclMessageType::Info, VclButtonsType::Ok, 2191 "SdrObjEditView::SetAttributes(): Setting EE_FEATURE items " 2192 "at the SdrView does not make sense! It only leads to " 2193 "overhead and unreadable documents.")); 2194 xInfoBox->run(); 2195 } 2196 } 2197 #endif 2198 2199 bool bOnlyEEItems; 2200 bool bNoEEItems = !SearchOutlinerItems(*pSet, bReplaceAll, &bOnlyEEItems); 2201 // everything selected? -> attributes to the border, too 2202 // if no EEItems, attributes to the border only 2203 if (bAllTextSelected || bNoEEItems) 2204 { 2205 if (mxSelectionController.is()) 2206 bRet = mxSelectionController->SetAttributes(*pSet, bReplaceAll); 2207 2208 if (!bRet) 2209 { 2210 const bool bUndo = IsUndoEnabled(); 2211 2212 if (bUndo) 2213 { 2214 BegUndo(ImpGetDescriptionString(STR_EditSetAttributes)); 2215 AddUndo( 2216 GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*mxWeakTextEditObj)); 2217 2218 // If this is a text object also rescue the OutlinerParaObject since 2219 // applying attributes to the object may change text layout when 2220 // multiple portions exist with multiple formats. If an OutlinerParaObject 2221 // really exists and needs to be rescued is evaluated in the undo 2222 // implementation itself. 2223 bool bRescueText = mxWeakTextEditObj; 2224 2225 AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoAttrObject( 2226 *mxWeakTextEditObj, false, !bNoEEItems || bRescueText)); 2227 EndUndo(); 2228 } 2229 2230 mxWeakTextEditObj->SetMergedItemSetAndBroadcast(*pSet, bReplaceAll); 2231 2232 FlushComeBackTimer(); // to set ModeHasChanged immediately 2233 } 2234 } 2235 else if (!bOnlyEEItems) 2236 { 2237 // Otherwise split Set, if necessary. 2238 // Now we build an ItemSet aSet that doesn't contain EE_Items from 2239 // *pSet (otherwise it would be a copy). 2240 WhichRangesContainer pNewWhichTable 2241 = RemoveWhichRange(pSet->GetRanges(), EE_ITEMS_START, EE_ITEMS_END); 2242 SfxItemSet aSet(mpModel->GetItemPool(), std::move(pNewWhichTable)); 2243 SfxWhichIter aIter(aSet); 2244 sal_uInt16 nWhich = aIter.FirstWhich(); 2245 while (nWhich != 0) 2246 { 2247 const SfxPoolItem* pItem; 2248 SfxItemState eState = pSet->GetItemState(nWhich, false, &pItem); 2249 if (eState == SfxItemState::SET) 2250 aSet.Put(*pItem); 2251 nWhich = aIter.NextWhich(); 2252 } 2253 2254 if (mxSelectionController.is()) 2255 bRet = mxSelectionController->SetAttributes(aSet, bReplaceAll); 2256 2257 if (!bRet) 2258 { 2259 if (IsUndoEnabled()) 2260 { 2261 BegUndo(ImpGetDescriptionString(STR_EditSetAttributes)); 2262 AddUndo( 2263 GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*mxWeakTextEditObj)); 2264 AddUndo( 2265 GetModel()->GetSdrUndoFactory().CreateUndoAttrObject(*mxWeakTextEditObj)); 2266 EndUndo(); 2267 } 2268 2269 mxWeakTextEditObj->SetMergedItemSetAndBroadcast(aSet, bReplaceAll); 2270 2271 if (GetMarkedObjectCount() == 1 2272 && GetMarkedObjectByIndex(0) == mxWeakTextEditObj.get()) 2273 { 2274 SetNotPersistAttrToMarked(aSet); 2275 } 2276 } 2277 FlushComeBackTimer(); 2278 } 2279 if (!bNoEEItems) 2280 { 2281 // and now the attributes to the EditEngine 2282 if (bReplaceAll) 2283 { 2284 mpTextEditOutlinerView->RemoveAttribs(true); 2285 } 2286 mpTextEditOutlinerView->SetAttribs(rSet); 2287 2288 Outliner* pTEOutliner = mpTextEditOutlinerView->GetOutliner(); 2289 if (mpModel && pTEOutliner && pTEOutliner->IsModified()) 2290 mpModel->SetChanged(); 2291 2292 ImpMakeTextCursorAreaVisible(); 2293 } 2294 bRet = true; 2295 } 2296 return bRet; 2297 } 2298 2299 SfxStyleSheet* SdrObjEditView::GetStyleSheet() const 2300 { 2301 SfxStyleSheet* pSheet = nullptr; 2302 2303 if (mxSelectionController.is()) 2304 { 2305 if (mxSelectionController->GetStyleSheet(pSheet)) 2306 return pSheet; 2307 } 2308 2309 if (mpTextEditOutlinerView) 2310 { 2311 pSheet = mpTextEditOutlinerView->GetStyleSheet(); 2312 } 2313 else 2314 { 2315 pSheet = SdrGlueEditView::GetStyleSheet(); 2316 } 2317 return pSheet; 2318 } 2319 2320 void SdrObjEditView::SetStyleSheet(SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr) 2321 { 2322 if (mxSelectionController.is()) 2323 { 2324 if (mxSelectionController->SetStyleSheet(pStyleSheet, bDontRemoveHardAttr)) 2325 return; 2326 } 2327 2328 // if we are currently in edit mode we must also set the stylesheet 2329 // on all paragraphs in the Outliner for the edit view 2330 if (nullptr != mpTextEditOutlinerView) 2331 { 2332 Outliner* pOutliner = mpTextEditOutlinerView->GetOutliner(); 2333 2334 const sal_Int32 nParaCount = pOutliner->GetParagraphCount(); 2335 for (sal_Int32 nPara = 0; nPara < nParaCount; nPara++) 2336 { 2337 pOutliner->SetStyleSheet(nPara, pStyleSheet); 2338 } 2339 } 2340 2341 SdrGlueEditView::SetStyleSheet(pStyleSheet, bDontRemoveHardAttr); 2342 } 2343 2344 void SdrObjEditView::AddWindowToPaintView(OutputDevice* pNewWin, vcl::Window* pWindow) 2345 { 2346 SdrGlueEditView::AddWindowToPaintView(pNewWin, pWindow); 2347 2348 if (mxWeakTextEditObj.is() && !mbTextEditOnlyOneView 2349 && pNewWin->GetOutDevType() == OUTDEV_WINDOW) 2350 { 2351 OutlinerView* pOutlView = ImpMakeOutlinerView(pNewWin->GetOwnerWindow(), nullptr); 2352 mpTextEditOutliner->InsertView(pOutlView); 2353 } 2354 } 2355 2356 void SdrObjEditView::DeleteWindowFromPaintView(OutputDevice* pOldWin) 2357 { 2358 SdrGlueEditView::DeleteWindowFromPaintView(pOldWin); 2359 2360 if (mxWeakTextEditObj.is() && !mbTextEditOnlyOneView 2361 && pOldWin->GetOutDevType() == OUTDEV_WINDOW) 2362 { 2363 for (size_t i = mpTextEditOutliner->GetViewCount(); i > 0;) 2364 { 2365 i--; 2366 OutlinerView* pOLV = mpTextEditOutliner->GetView(i); 2367 if (pOLV && pOLV->GetWindow() == pOldWin->GetOwnerWindow()) 2368 { 2369 mpTextEditOutliner->RemoveView(i); 2370 } 2371 } 2372 } 2373 2374 lcl_RemoveTextEditOutlinerViews(this, GetSdrPageView(), pOldWin); 2375 } 2376 2377 bool SdrObjEditView::IsTextEditInSelectionMode() const 2378 { 2379 return mpTextEditOutliner != nullptr && mpTextEditOutliner->IsInSelectionMode(); 2380 } 2381 2382 // MacroMode 2383 2384 void SdrObjEditView::BegMacroObj(const Point& rPnt, short nTol, SdrObject* pObj, SdrPageView* pPV, 2385 vcl::Window* pWin) 2386 { 2387 BrkMacroObj(); 2388 if (pObj != nullptr && pPV != nullptr && pWin != nullptr && pObj->HasMacro()) 2389 { 2390 nTol = ImpGetHitTolLogic(nTol, nullptr); 2391 pMacroObj = pObj; 2392 pMacroPV = pPV; 2393 pMacroWin = pWin; 2394 mbMacroDown = false; 2395 nMacroTol = sal_uInt16(nTol); 2396 aMacroDownPos = rPnt; 2397 MovMacroObj(rPnt); 2398 } 2399 } 2400 2401 void SdrObjEditView::ImpMacroUp(const Point& rUpPos) 2402 { 2403 if (pMacroObj != nullptr && mbMacroDown) 2404 { 2405 SdrObjMacroHitRec aHitRec; 2406 aHitRec.aPos = rUpPos; 2407 aHitRec.nTol = nMacroTol; 2408 aHitRec.pVisiLayer = &pMacroPV->GetVisibleLayers(); 2409 aHitRec.pPageView = pMacroPV; 2410 pMacroObj->PaintMacro(*pMacroWin->GetOutDev(), tools::Rectangle(), aHitRec); 2411 mbMacroDown = false; 2412 } 2413 } 2414 2415 void SdrObjEditView::ImpMacroDown(const Point& rDownPos) 2416 { 2417 if (pMacroObj != nullptr && !mbMacroDown) 2418 { 2419 SdrObjMacroHitRec aHitRec; 2420 aHitRec.aPos = rDownPos; 2421 aHitRec.nTol = nMacroTol; 2422 aHitRec.pVisiLayer = &pMacroPV->GetVisibleLayers(); 2423 aHitRec.pPageView = pMacroPV; 2424 pMacroObj->PaintMacro(*pMacroWin->GetOutDev(), tools::Rectangle(), aHitRec); 2425 mbMacroDown = true; 2426 } 2427 } 2428 2429 void SdrObjEditView::MovMacroObj(const Point& rPnt) 2430 { 2431 if (pMacroObj == nullptr) 2432 return; 2433 2434 SdrObjMacroHitRec aHitRec; 2435 aHitRec.aPos = rPnt; 2436 aHitRec.nTol = nMacroTol; 2437 aHitRec.pVisiLayer = &pMacroPV->GetVisibleLayers(); 2438 aHitRec.pPageView = pMacroPV; 2439 bool bDown = pMacroObj->IsMacroHit(aHitRec); 2440 if (bDown) 2441 ImpMacroDown(rPnt); 2442 else 2443 ImpMacroUp(rPnt); 2444 } 2445 2446 void SdrObjEditView::BrkMacroObj() 2447 { 2448 if (pMacroObj != nullptr) 2449 { 2450 ImpMacroUp(aMacroDownPos); 2451 pMacroObj = nullptr; 2452 pMacroPV = nullptr; 2453 pMacroWin = nullptr; 2454 } 2455 } 2456 2457 bool SdrObjEditView::EndMacroObj() 2458 { 2459 if (pMacroObj != nullptr && mbMacroDown) 2460 { 2461 ImpMacroUp(aMacroDownPos); 2462 SdrObjMacroHitRec aHitRec; 2463 aHitRec.aPos = aMacroDownPos; 2464 aHitRec.nTol = nMacroTol; 2465 aHitRec.pVisiLayer = &pMacroPV->GetVisibleLayers(); 2466 aHitRec.pPageView = pMacroPV; 2467 bool bRet = pMacroObj->DoMacro(aHitRec); 2468 pMacroObj = nullptr; 2469 pMacroPV = nullptr; 2470 pMacroWin = nullptr; 2471 return bRet; 2472 } 2473 else 2474 { 2475 BrkMacroObj(); 2476 return false; 2477 } 2478 } 2479 2480 /** fills the given any with a XTextCursor for the current text selection. 2481 Leaves the any untouched if there currently is no text selected */ 2482 void SdrObjEditView::getTextSelection(css::uno::Any& rSelection) 2483 { 2484 if (!IsTextEdit()) 2485 return; 2486 2487 OutlinerView* pOutlinerView = GetTextEditOutlinerView(); 2488 if (!(pOutlinerView && pOutlinerView->HasSelection())) 2489 return; 2490 2491 SdrObject* pObj = GetTextEditObject(); 2492 2493 if (!pObj) 2494 return; 2495 2496 css::uno::Reference<css::text::XText> xText(pObj->getUnoShape(), css::uno::UNO_QUERY); 2497 if (xText.is()) 2498 { 2499 SvxUnoTextBase* pRange = comphelper::getFromUnoTunnel<SvxUnoTextBase>(xText); 2500 if (pRange) 2501 { 2502 rSelection <<= pRange->createTextCursorBySelection(pOutlinerView->GetSelection()); 2503 } 2504 } 2505 } 2506 2507 /* check if we have a single selection and that single object likes 2508 to handle the mouse and keyboard events itself 2509 2510 TODO: the selection controller should be queried from the 2511 object specific view contact. Currently this method only 2512 works for tables. 2513 */ 2514 void SdrObjEditView::MarkListHasChanged() 2515 { 2516 SdrGlueEditView::MarkListHasChanged(); 2517 2518 if (mxSelectionController.is()) 2519 { 2520 mxLastSelectionController = mxSelectionController; 2521 mxSelectionController->onSelectionHasChanged(); 2522 } 2523 2524 mxSelectionController.clear(); 2525 2526 const SdrMarkList& rMarkList = GetMarkedObjectList(); 2527 if (rMarkList.GetMarkCount() != 1) 2528 return; 2529 2530 const SdrObject* pObj(rMarkList.GetMark(0)->GetMarkedSdrObj()); 2531 SdrView* pView(dynamic_cast<SdrView*>(this)); 2532 2533 // check for table 2534 if (pObj && pView && (pObj->GetObjInventor() == SdrInventor::Default) 2535 && (pObj->GetObjIdentifier() == SdrObjKind::Table)) 2536 { 2537 mxSelectionController = sdr::table::CreateTableController( 2538 *pView, static_cast<const sdr::table::SdrTableObj&>(*pObj), mxLastSelectionController); 2539 2540 if (mxSelectionController.is()) 2541 { 2542 mxLastSelectionController.clear(); 2543 mxSelectionController->onSelectionHasChanged(); 2544 } 2545 } 2546 } 2547 2548 IMPL_LINK(SdrObjEditView, EndPasteOrDropHdl, PasteOrDropInfos*, pInfo, void) 2549 { 2550 OnEndPasteOrDrop(pInfo); 2551 } 2552 2553 IMPL_LINK(SdrObjEditView, BeginPasteOrDropHdl, PasteOrDropInfos*, pInfo, void) 2554 { 2555 OnBeginPasteOrDrop(pInfo); 2556 } 2557 2558 void SdrObjEditView::OnBeginPasteOrDrop(PasteOrDropInfos*) 2559 { 2560 // applications can derive from these virtual methods to do something before a drop or paste operation 2561 } 2562 2563 void SdrObjEditView::OnEndPasteOrDrop(PasteOrDropInfos*) 2564 { 2565 // applications can derive from these virtual methods to do something before a drop or paste operation 2566 } 2567 2568 sal_uInt16 SdrObjEditView::GetSelectionLevel() const 2569 { 2570 sal_uInt16 nLevel = 0xFFFF; 2571 if (IsTextEdit()) 2572 { 2573 DBG_ASSERT(mpTextEditOutlinerView != nullptr, 2574 "SdrObjEditView::GetAttributes(): mpTextEditOutlinerView=NULL"); 2575 DBG_ASSERT(mpTextEditOutliner != nullptr, 2576 "SdrObjEditView::GetAttributes(): mpTextEditOutliner=NULL"); 2577 if (mpTextEditOutlinerView) 2578 { 2579 //start and end position 2580 ESelection aSelect = mpTextEditOutlinerView->GetSelection(); 2581 sal_uInt16 nStartPara = ::std::min(aSelect.nStartPara, aSelect.nEndPara); 2582 sal_uInt16 nEndPara = ::std::max(aSelect.nStartPara, aSelect.nEndPara); 2583 //get level from each paragraph 2584 nLevel = 0; 2585 for (sal_uInt16 nPara = nStartPara; nPara <= nEndPara; nPara++) 2586 { 2587 sal_uInt16 nParaDepth 2588 = 1 << static_cast<sal_uInt16>(mpTextEditOutliner->GetDepth(nPara)); 2589 if (!(nLevel & nParaDepth)) 2590 nLevel += nParaDepth; 2591 } 2592 //reduce one level for Outliner Object 2593 //if( nLevel > 0 && GetTextEditObject()->GetObjIdentifier() == OBJ_OUTLINETEXT ) 2594 // nLevel = nLevel >> 1; 2595 //no bullet paragraph selected 2596 if (nLevel == 0) 2597 nLevel = 0xFFFF; 2598 } 2599 } 2600 return nLevel; 2601 } 2602 2603 bool SdrObjEditView::SupportsFormatPaintbrush(SdrInventor nObjectInventor, 2604 SdrObjKind nObjectIdentifier) 2605 { 2606 if (nObjectInventor != SdrInventor::Default && nObjectInventor != SdrInventor::E3d) 2607 return false; 2608 switch (nObjectIdentifier) 2609 { 2610 case SdrObjKind::NONE: 2611 case SdrObjKind::Group: 2612 return false; 2613 case SdrObjKind::Line: 2614 case SdrObjKind::Rectangle: 2615 case SdrObjKind::CircleOrEllipse: 2616 case SdrObjKind::CircleSection: 2617 case SdrObjKind::CircleArc: 2618 case SdrObjKind::CircleCut: 2619 case SdrObjKind::Polygon: 2620 case SdrObjKind::PolyLine: 2621 case SdrObjKind::PathLine: 2622 case SdrObjKind::PathFill: 2623 case SdrObjKind::FreehandLine: 2624 case SdrObjKind::FreehandFill: 2625 case SdrObjKind::SplineLine: 2626 case SdrObjKind::SplineFill: 2627 case SdrObjKind::Text: 2628 case SdrObjKind::TitleText: 2629 case SdrObjKind::OutlineText: 2630 case SdrObjKind::Graphic: 2631 case SdrObjKind::OLE2: 2632 case SdrObjKind::Table: 2633 return true; 2634 case SdrObjKind::Edge: 2635 case SdrObjKind::Caption: 2636 return false; 2637 case SdrObjKind::PathPoly: 2638 case SdrObjKind::PathPolyLine: 2639 return true; 2640 case SdrObjKind::Page: 2641 case SdrObjKind::Measure: 2642 case SdrObjKind::OLEPluginFrame: 2643 case SdrObjKind::UNO: 2644 return false; 2645 case SdrObjKind::CustomShape: 2646 return true; 2647 default: 2648 return false; 2649 } 2650 } 2651 2652 static const WhichRangesContainer& GetFormatRangeImpl(bool bTextOnly) 2653 { 2654 static const WhichRangesContainer gFull( 2655 svl::Items<XATTR_LINE_FIRST, XATTR_LINE_LAST, XATTR_FILL_FIRST, XATTRSET_FILL, 2656 SDRATTR_SHADOW_FIRST, SDRATTR_SHADOW_LAST, SDRATTR_MISC_FIRST, 2657 SDRATTR_MISC_LAST, // table cell formats 2658 SDRATTR_GRAF_FIRST, SDRATTR_GRAF_LAST, SDRATTR_TABLE_FIRST, SDRATTR_TABLE_LAST, 2659 EE_PARA_START, EE_PARA_END, EE_CHAR_START, EE_CHAR_END>); 2660 2661 static const WhichRangesContainer gTextOnly( 2662 svl::Items<SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST, EE_PARA_START, EE_PARA_END, EE_CHAR_START, 2663 EE_CHAR_END>); 2664 2665 return bTextOnly ? gTextOnly : gFull; 2666 } 2667 2668 void SdrObjEditView::TakeFormatPaintBrush(std::shared_ptr<SfxItemSet>& rFormatSet) 2669 { 2670 const SdrMarkList& rMarkList = GetMarkedObjectList(); 2671 if (rMarkList.GetMarkCount() <= 0) 2672 return; 2673 2674 OutlinerView* pOLV = GetTextEditOutlinerView(); 2675 2676 rFormatSet = std::make_shared<SfxItemSet>(GetModel()->GetItemPool(), 2677 GetFormatRangeImpl(pOLV != nullptr)); 2678 if (pOLV) 2679 { 2680 rFormatSet->Put(pOLV->GetAttribs()); 2681 } 2682 else 2683 { 2684 const bool bOnlyHardAttr = false; 2685 rFormatSet->Put(GetAttrFromMarked(bOnlyHardAttr)); 2686 } 2687 2688 // check for cloning from table cell, in which case we need to copy cell-specific formatting attributes 2689 const SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); 2690 if (pObj && (pObj->GetObjInventor() == SdrInventor::Default) 2691 && (pObj->GetObjIdentifier() == SdrObjKind::Table)) 2692 { 2693 auto pTable = static_cast<const sdr::table::SdrTableObj*>(pObj); 2694 if (mxSelectionController.is() && pTable->getActiveCell().is()) 2695 { 2696 mxSelectionController->GetAttributes(*rFormatSet, false); 2697 } 2698 } 2699 } 2700 2701 static SfxItemSet CreatePaintSet(const WhichRangesContainer& pRanges, SfxItemPool& rPool, 2702 const SfxItemSet& rSourceSet, const SfxItemSet& rTargetSet, 2703 bool bNoCharacterFormats, bool bNoParagraphFormats) 2704 { 2705 SfxItemSet aPaintSet(rPool, pRanges); 2706 2707 for (const auto& pRange : pRanges) 2708 { 2709 sal_uInt16 nWhich = pRange.first; 2710 const sal_uInt16 nLastWhich = pRange.second; 2711 2712 if (bNoCharacterFormats && (nWhich == EE_CHAR_START)) 2713 continue; 2714 2715 if (bNoParagraphFormats && (nWhich == EE_PARA_START)) 2716 continue; 2717 2718 for (; nWhich < nLastWhich; nWhich++) 2719 { 2720 const SfxPoolItem* pSourceItem = rSourceSet.GetItem(nWhich); 2721 const SfxPoolItem* pTargetItem = rTargetSet.GetItem(nWhich); 2722 2723 if ((pSourceItem && !pTargetItem) 2724 || (pSourceItem && pTargetItem && *pSourceItem != *pTargetItem)) 2725 { 2726 aPaintSet.Put(*pSourceItem); 2727 } 2728 } 2729 } 2730 return aPaintSet; 2731 } 2732 2733 void SdrObjEditView::ApplyFormatPaintBrushToText(SfxItemSet const& rFormatSet, SdrTextObj& rTextObj, 2734 SdrText* pText, bool bNoCharacterFormats, 2735 bool bNoParagraphFormats) 2736 { 2737 OutlinerParaObject* pParaObj = pText ? pText->GetOutlinerParaObject() : nullptr; 2738 if (!pParaObj) 2739 return; 2740 2741 SdrOutliner& rOutliner = rTextObj.ImpGetDrawOutliner(); 2742 rOutliner.SetText(*pParaObj); 2743 2744 sal_Int32 nParaCount(rOutliner.GetParagraphCount()); 2745 2746 if (!nParaCount) 2747 return; 2748 2749 for (sal_Int32 nPara = 0; nPara < nParaCount; nPara++) 2750 { 2751 if (!bNoCharacterFormats) 2752 rOutliner.RemoveCharAttribs(nPara); 2753 2754 SfxItemSet aSet(rOutliner.GetParaAttribs(nPara)); 2755 aSet.Put(CreatePaintSet(GetFormatRangeImpl(true), *aSet.GetPool(), rFormatSet, aSet, 2756 bNoCharacterFormats, bNoParagraphFormats)); 2757 rOutliner.SetParaAttribs(nPara, aSet); 2758 } 2759 2760 std::optional<OutlinerParaObject> pTemp = rOutliner.CreateParaObject(0, nParaCount); 2761 rOutliner.Clear(); 2762 2763 rTextObj.NbcSetOutlinerParaObjectForText(std::move(pTemp), pText); 2764 } 2765 2766 void SdrObjEditView::DisposeUndoManager() 2767 { 2768 if (mpTextEditOutliner) 2769 { 2770 if (typeid(mpTextEditOutliner->GetUndoManager()) != typeid(EditUndoManager)) 2771 { 2772 // Non-owning pointer, clear it. 2773 mpTextEditOutliner->SetUndoManager(nullptr); 2774 } 2775 } 2776 2777 mpOldTextEditUndoManager = nullptr; 2778 } 2779 2780 void SdrObjEditView::ApplyFormatPaintBrush(SfxItemSet& rFormatSet, bool bNoCharacterFormats, 2781 bool bNoParagraphFormats) 2782 { 2783 if (mxSelectionController.is() 2784 && mxSelectionController->ApplyFormatPaintBrush(rFormatSet, bNoCharacterFormats, 2785 bNoParagraphFormats)) 2786 { 2787 return; 2788 } 2789 2790 OutlinerView* pOLV = GetTextEditOutlinerView(); 2791 const SdrMarkList& rMarkList = GetMarkedObjectList(); 2792 if (!pOLV) 2793 { 2794 SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); 2795 const SfxItemSet& rShapeSet = pObj->GetMergedItemSet(); 2796 2797 // if not in text edit mode (aka the user selected text or clicked on a word) 2798 // apply formatting attributes to selected shape 2799 // All formatting items (see ranges above) that are unequal in selected shape and 2800 // the format paintbrush are hard set on the selected shape. 2801 2802 const WhichRangesContainer& pRanges = rFormatSet.GetRanges(); 2803 bool bTextOnly = true; 2804 2805 for (const auto& pRange : pRanges) 2806 { 2807 if ((pRange.first != EE_PARA_START) && (pRange.first != EE_CHAR_START)) 2808 { 2809 bTextOnly = false; 2810 break; 2811 } 2812 } 2813 2814 if (!bTextOnly) 2815 { 2816 SfxItemSet aPaintSet(CreatePaintSet(GetFormatRangeImpl(false), *rShapeSet.GetPool(), 2817 rFormatSet, rShapeSet, bNoCharacterFormats, 2818 bNoParagraphFormats)); 2819 SetAttrToMarked(aPaintSet, false /*bReplaceAll*/); 2820 } 2821 2822 // now apply character and paragraph formatting to text, if the shape has any 2823 SdrTextObj* pTextObj = dynamic_cast<SdrTextObj*>(pObj); 2824 if (pTextObj) 2825 { 2826 sal_Int32 nText = pTextObj->getTextCount(); 2827 2828 while (--nText >= 0) 2829 { 2830 SdrText* pText = pTextObj->getText(nText); 2831 ApplyFormatPaintBrushToText(rFormatSet, *pTextObj, pText, bNoCharacterFormats, 2832 bNoParagraphFormats); 2833 } 2834 } 2835 } 2836 else 2837 { 2838 ::Outliner* pOutliner = pOLV->GetOutliner(); 2839 if (pOutliner) 2840 { 2841 const EditEngine& rEditEngine = pOutliner->GetEditEngine(); 2842 2843 ESelection aSel(pOLV->GetSelection()); 2844 if (!aSel.HasRange()) 2845 pOLV->SetSelection(rEditEngine.GetWord(aSel, css::i18n::WordType::DICTIONARY_WORD)); 2846 2847 const bool bRemoveParaAttribs = !bNoParagraphFormats; 2848 pOLV->RemoveAttribsKeepLanguages(bRemoveParaAttribs); 2849 SfxItemSet aSet(pOLV->GetAttribs()); 2850 SfxItemSet aPaintSet(CreatePaintSet(GetFormatRangeImpl(true), *aSet.GetPool(), 2851 rFormatSet, aSet, bNoCharacterFormats, 2852 bNoParagraphFormats)); 2853 pOLV->SetAttribs(aPaintSet); 2854 } 2855 } 2856 2857 // check for cloning to table cell, in which case we need to copy cell-specific formatting attributes 2858 SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); 2859 if (pObj && (pObj->GetObjInventor() == SdrInventor::Default) 2860 && (pObj->GetObjIdentifier() == SdrObjKind::Table)) 2861 { 2862 auto pTable = static_cast<sdr::table::SdrTableObj*>(pObj); 2863 if (pTable->getActiveCell().is() && mxSelectionController.is()) 2864 { 2865 mxSelectionController->SetAttributes(rFormatSet, false); 2866 } 2867 } 2868 } 2869 2870 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 2871
