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 <svl/macitem.hxx> 21 #include <sfx2/frame.hxx> 22 #include <svl/eitem.hxx> 23 #include <svl/listener.hxx> 24 #include <svl/stritem.hxx> 25 #include <sfx2/docfile.hxx> 26 #include <sfx2/dispatch.hxx> 27 #include <sfx2/linkmgr.hxx> 28 #include <sfx2/viewfrm.hxx> 29 #include <sot/exchange.hxx> 30 #include <osl/diagnose.h> 31 #include <fmtinfmt.hxx> 32 #include <wrtsh.hxx> 33 #include <docsh.hxx> 34 #include <fldbas.hxx> 35 #include <expfld.hxx> 36 #include <docufld.hxx> 37 #include <reffld.hxx> 38 #include <swundo.hxx> 39 #include <doc.hxx> 40 #include <frmfmt.hxx> 41 #include <fmtfld.hxx> 42 #include <view.hxx> 43 #include <swevent.hxx> 44 #include <section.hxx> 45 #include <navicont.hxx> 46 #include <txtinet.hxx> 47 #include <cmdid.h> 48 #include <swabstdlg.hxx> 49 #include <SwRewriter.hxx> 50 #include <authfld.hxx> 51 52 #include <com/sun/star/document/XDocumentProperties.hpp> 53 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> 54 55 #include <memory> 56 57 #include <LibreOfficeKit/LibreOfficeKitEnums.h> 58 #include <comphelper/lok.hxx> 59 #include <sfx2/event.hxx> 60 #include <sal/log.hxx> 61 62 void SwWrtShell::Insert(SwField const& rField, SwPaM* pAnnotationRange) 63 { 64 ResetCursorStack(); 65 if(!CanInsert()) 66 return; 67 StartAllAction(); 68 69 SwRewriter aRewriter; 70 aRewriter.AddRule(UndoArg1, rField.GetDescription()); 71 72 StartUndo(SwUndoId::INSERT, &aRewriter); 73 74 bool bDeleted = false; 75 std::unique_ptr<SwPaM> pAnnotationTextRange; 76 if (pAnnotationRange) 77 { 78 pAnnotationTextRange.reset(new SwPaM(*pAnnotationRange->Start(), *pAnnotationRange->End())); 79 } 80 81 if ( HasSelection() ) 82 { 83 if ( rField.GetTyp()->Which() == SwFieldIds::Postit ) 84 { 85 // for annotation fields: 86 // - keep the current selection in order to create a corresponding annotation mark 87 // - collapse cursor to its end 88 if ( IsTableMode() ) 89 { 90 GetTableCrs()->Normalize( false ); 91 const SwPosition rStartPos( *(GetTableCrs()->GetMark()->nNode.GetNode().GetContentNode()), 0 ); 92 KillPams(); 93 if ( !IsEndOfPara() ) 94 { 95 EndPara(); 96 } 97 const SwPosition rEndPos( *GetCurrentShellCursor().GetPoint() ); 98 pAnnotationTextRange.reset(new SwPaM( rStartPos, rEndPos )); 99 } 100 else 101 { 102 NormalizePam( false ); 103 const SwPaM& rCurrPaM = GetCurrentShellCursor(); 104 pAnnotationTextRange.reset(new SwPaM( *rCurrPaM.GetPoint(), *rCurrPaM.GetMark() )); 105 ClearMark(); 106 } 107 } 108 else 109 { 110 bDeleted = DelRight(); 111 } 112 } 113 114 SwEditShell::Insert2(rField, bDeleted); 115 116 if ( pAnnotationTextRange ) 117 { 118 if ( GetDoc() != nullptr ) 119 { 120 const SwPaM& rCurrPaM = GetCurrentShellCursor(); 121 if (*rCurrPaM.Start() == *pAnnotationTextRange->Start() 122 && *rCurrPaM.End() == *pAnnotationTextRange->End()) 123 { 124 // Annotation range was passed in externally, and inserting the postit field shifted 125 // its start/end positions right by one. Restore the original position for the range 126 // start. This allows commenting on the placeholder character of the field. 127 SwIndex& rRangeStart = pAnnotationTextRange->Start()->nContent; 128 if (rRangeStart.GetIndex() > 0) 129 --rRangeStart; 130 } 131 IDocumentMarkAccess* pMarksAccess = GetDoc()->getIDocumentMarkAccess(); 132 pMarksAccess->makeAnnotationMark( *pAnnotationTextRange, OUString() ); 133 } 134 pAnnotationTextRange.reset(); 135 } 136 137 EndUndo(); 138 EndAllAction(); 139 } 140 141 // Start the field update 142 143 void SwWrtShell::UpdateInputFields( SwInputFieldList* pLst ) 144 { 145 // Go through the list of fields and updating 146 std::unique_ptr<SwInputFieldList> pTmp; 147 if (!pLst) 148 { 149 pTmp.reset(new SwInputFieldList( this )); 150 pLst = pTmp.get(); 151 } 152 153 const size_t nCnt = pLst->Count(); 154 if(!nCnt) 155 return; 156 157 pLst->PushCursor(); 158 159 bool bCancel = false; 160 161 size_t nIndex = 0; 162 FieldDialogPressedButton ePressedButton = FieldDialogPressedButton::NONE; 163 164 SwField* pField = GetCurField(); 165 if (pField) 166 { 167 for (size_t i = 0; i < nCnt; i++) 168 { 169 if (pField == pLst->GetField(i)) 170 { 171 nIndex = i; 172 break; 173 } 174 } 175 } 176 177 while (!bCancel) 178 { 179 bool bPrev = nIndex > 0; 180 bool bNext = nIndex < nCnt - 1; 181 pLst->GotoFieldPos(nIndex); 182 pField = pLst->GetField(nIndex); 183 if (pField->GetTyp()->Which() == SwFieldIds::Dropdown) 184 { 185 bCancel = StartDropDownFieldDlg(pField, bPrev, bNext, GetView().GetFrameWeld(), &ePressedButton); 186 } 187 else 188 bCancel = StartInputFieldDlg(pField, bPrev, bNext, GetView().GetFrameWeld(), &ePressedButton); 189 190 if (!bCancel) 191 { 192 // Otherwise update error at multi-selection: 193 pLst->GetField(nIndex)->GetTyp()->UpdateFields(); 194 195 if (ePressedButton == FieldDialogPressedButton::Previous && nIndex > 0) 196 nIndex--; 197 else if (ePressedButton == FieldDialogPressedButton::Next && nIndex < nCnt - 1) 198 nIndex++; 199 else 200 bCancel = true; 201 } 202 } 203 204 pLst->PopCursor(); 205 } 206 207 namespace { 208 209 // Listener class: will close InputField dialog if input field(s) 210 // is(are) deleted (for instance, by an extension) after the dialog shows up. 211 // Otherwise, the for loop in SwWrtShell::UpdateInputFields will crash when doing: 212 // 'pTmp->GetField( i )->GetTyp()->UpdateFields();' 213 // on a deleted field. 214 class FieldDeletionListener : public SvtListener 215 { 216 public: 217 FieldDeletionListener(AbstractFieldInputDlg* pInputFieldDlg, SwField* pField) 218 : mpInputFieldDlg(pInputFieldDlg) 219 , mpFormatField(nullptr) 220 { 221 SwInputField *const pInputField(dynamic_cast<SwInputField*>(pField)); 222 SwSetExpField *const pSetExpField(dynamic_cast<SwSetExpField*>(pField)); 223 224 if (pInputField && pInputField->GetFormatField()) 225 { 226 mpFormatField = pInputField->GetFormatField(); 227 } 228 else if (pSetExpField && pSetExpField->GetFormatField()) 229 { 230 mpFormatField = pSetExpField->GetFormatField(); 231 } 232 233 // Register for possible field deletion while dialog is open 234 if (mpFormatField) 235 StartListening(mpFormatField->GetNotifier()); 236 } 237 238 virtual ~FieldDeletionListener() override 239 { 240 // Dialog closed, remove modification listener 241 EndListeningAll(); 242 } 243 244 virtual void Notify(const SfxHint& rHint) override 245 { 246 // Input field has been deleted: better to close the dialog 247 if(rHint.GetId() == SfxHintId::Dying) 248 { 249 mpFormatField = nullptr; 250 mpInputFieldDlg->EndDialog(RET_CANCEL); 251 } 252 } 253 private: 254 VclPtr<AbstractFieldInputDlg> mpInputFieldDlg; 255 SwFormatField* mpFormatField; 256 }; 257 258 } 259 260 // Start input dialog for a specific field 261 bool SwWrtShell::StartInputFieldDlg(SwField* pField, bool bPrevButton, bool bNextButton, 262 weld::Widget* pParentWin, SwWrtShell::FieldDialogPressedButton* pPressedButton) 263 { 264 265 SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); 266 ScopedVclPtr<AbstractFieldInputDlg> pDlg(pFact->CreateFieldInputDlg(pParentWin, *this, pField, bPrevButton, bNextButton)); 267 268 bool bRet; 269 270 { 271 FieldDeletionListener aModify(pDlg.get(), pField); 272 bRet = RET_CANCEL == pDlg->Execute(); 273 } 274 275 if (pPressedButton) 276 { 277 if (pDlg->PrevButtonPressed()) 278 *pPressedButton = FieldDialogPressedButton::Previous; 279 else if (pDlg->NextButtonPressed()) 280 *pPressedButton = FieldDialogPressedButton::Next; 281 } 282 283 pDlg.disposeAndClear(); 284 GetWin()->PaintImmediately(); 285 return bRet; 286 } 287 288 bool SwWrtShell::StartDropDownFieldDlg(SwField* pField, bool bPrevButton, bool bNextButton, 289 weld::Widget* pParentWin, SwWrtShell::FieldDialogPressedButton* pPressedButton) 290 { 291 SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); 292 ScopedVclPtr<AbstractDropDownFieldDialog> pDlg(pFact->CreateDropDownFieldDialog(pParentWin, *this, pField, bPrevButton, bNextButton)); 293 const short nRet = pDlg->Execute(); 294 295 if (pPressedButton) 296 { 297 if (pDlg->PrevButtonPressed()) 298 *pPressedButton = FieldDialogPressedButton::Previous; 299 else if (pDlg->NextButtonPressed()) 300 *pPressedButton = FieldDialogPressedButton::Next; 301 } 302 303 pDlg.disposeAndClear(); 304 bool bRet = RET_CANCEL == nRet; 305 GetWin()->PaintImmediately(); 306 if(RET_YES == nRet) 307 { 308 GetView().GetViewFrame()->GetDispatcher()->Execute(FN_EDIT_FIELD, SfxCallMode::SYNCHRON); 309 } 310 return bRet; 311 } 312 313 // Insert directory - remove selection 314 315 void SwWrtShell::InsertTableOf(const SwTOXBase& rTOX, const SfxItemSet* pSet) 316 { 317 if(!CanInsert()) 318 return; 319 320 if(HasSelection()) 321 DelRight(); 322 323 SwEditShell::InsertTableOf(rTOX, pSet); 324 } 325 326 // Update directory - remove selection 327 328 void SwWrtShell::UpdateTableOf(const SwTOXBase& rTOX, const SfxItemSet* pSet) 329 { 330 if(CanInsert()) 331 { 332 SwEditShell::UpdateTableOf(rTOX, pSet); 333 } 334 } 335 336 // handler for click on the field given as parameter. 337 // the cursor is positioned on the field. 338 339 void SwWrtShell::ClickToField(const SwField& rField, bool bExecHyperlinks) 340 { 341 // cross reference field must not be selected because it moves the cursor 342 if (SwFieldIds::GetRef != rField.GetTyp()->Which()) 343 { 344 StartAllAction(); 345 Right( CRSR_SKIP_CHARS, true, 1, false ); // Select the field. 346 NormalizePam(); 347 EndAllAction(); 348 } 349 350 m_bIsInClickToEdit = true; 351 switch( rField.GetTyp()->Which() ) 352 { 353 case SwFieldIds::JumpEdit: 354 { 355 sal_uInt16 nSlotId = 0; 356 switch( rField.GetFormat() ) 357 { 358 case JE_FMT_TABLE: 359 nSlotId = FN_INSERT_TABLE; 360 break; 361 362 case JE_FMT_FRAME: 363 nSlotId = FN_INSERT_FRAME; 364 break; 365 366 case JE_FMT_GRAPHIC: nSlotId = SID_INSERT_GRAPHIC; break; 367 case JE_FMT_OLE: nSlotId = SID_INSERT_OBJECT; break; 368 369 } 370 371 if( nSlotId ) 372 { 373 StartUndo( SwUndoId::START ); 374 //#97295# immediately select the right shell 375 GetView().StopShellTimer(); 376 GetView().GetViewFrame()->GetDispatcher()->Execute( nSlotId, 377 SfxCallMode::SYNCHRON|SfxCallMode::RECORD ); 378 EndUndo( SwUndoId::END ); 379 } 380 } 381 break; 382 383 case SwFieldIds::Macro: 384 { 385 const SwMacroField *pField = static_cast<const SwMacroField*>(&rField); 386 const OUString sText( rField.GetPar2() ); 387 OUString sRet( sText ); 388 ExecMacro( pField->GetSvxMacro(), &sRet ); 389 390 // return value changed? 391 if( sRet != sText ) 392 { 393 StartAllAction(); 394 const_cast<SwField&>(rField).SetPar2( sRet ); 395 rField.GetTyp()->UpdateFields(); 396 EndAllAction(); 397 } 398 } 399 break; 400 401 case SwFieldIds::TableOfAuthorities: 402 { 403 if (!bExecHyperlinks) 404 { 405 break; 406 } 407 408 auto pField = static_cast<const SwAuthorityField*>(&rField); 409 if (!pField->HasURL()) 410 { 411 break; 412 } 413 414 const OUString& rURL = pField->GetAbsoluteURL(); 415 ::LoadURL(*this, rURL, LoadUrlFlags::NewView, /*rTargetFrameName=*/OUString()); 416 } 417 break; 418 419 case SwFieldIds::GetRef: 420 StartAllAction(); 421 SwCursorShell::GotoRefMark( static_cast<const SwGetRefField&>(rField).GetSetRefName(), 422 static_cast<const SwGetRefField&>(rField).GetSubType(), 423 static_cast<const SwGetRefField&>(rField).GetSeqNo() ); 424 EndAllAction(); 425 break; 426 427 case SwFieldIds::Input: 428 { 429 const SwInputField* pInputField = dynamic_cast<const SwInputField*>(&rField); 430 if ( pInputField == nullptr ) 431 { 432 StartInputFieldDlg(const_cast<SwField*>(&rField), false, false, GetView().GetFrameWeld()); 433 } 434 } 435 break; 436 437 case SwFieldIds::SetExp: 438 if( static_cast<const SwSetExpField&>(rField).GetInputFlag() ) 439 StartInputFieldDlg(const_cast<SwField*>(&rField), false, false, GetView().GetFrameWeld()); 440 break; 441 case SwFieldIds::Dropdown : 442 StartDropDownFieldDlg(const_cast<SwField*>(&rField), false, false, GetView().GetFrameWeld()); 443 break; 444 default: 445 SAL_WARN_IF(rField.IsClickable(), "sw", "unhandled clickable field!"); 446 } 447 448 m_bIsInClickToEdit = false; 449 } 450 451 void SwWrtShell::ClickToINetAttr( const SwFormatINetFormat& rItem, LoadUrlFlags nFilter ) 452 { 453 if( rItem.GetValue().isEmpty() ) 454 return ; 455 456 m_bIsInClickToEdit = true; 457 458 // At first run the possibly set ObjectSelect Macro 459 const SvxMacro* pMac = rItem.GetMacro( SvMacroItemId::OnClick ); 460 if( pMac ) 461 { 462 SwCallMouseEvent aCallEvent; 463 aCallEvent.Set( &rItem ); 464 GetDoc()->CallEvent( SvMacroItemId::OnClick, aCallEvent ); 465 } 466 467 // So that the implementation of templates is displayed immediately 468 ::LoadURL( *this, rItem.GetValue(), nFilter, rItem.GetTargetFrame() ); 469 const SwTextINetFormat* pTextAttr = rItem.GetTextINetFormat(); 470 if( pTextAttr ) 471 { 472 const_cast<SwTextINetFormat*>(pTextAttr)->SetVisited( true ); 473 const_cast<SwTextINetFormat*>(pTextAttr)->SetVisitedValid( true ); 474 } 475 476 m_bIsInClickToEdit = false; 477 } 478 479 bool SwWrtShell::ClickToINetGrf( const Point& rDocPt, LoadUrlFlags nFilter ) 480 { 481 bool bRet = false; 482 OUString sURL; 483 OUString sTargetFrameName; 484 const SwFrameFormat* pFnd = IsURLGrfAtPos( rDocPt, &sURL, &sTargetFrameName ); 485 if( pFnd && !sURL.isEmpty() ) 486 { 487 bRet = true; 488 // At first run the possibly set ObjectSelect Macro 489 SwCallMouseEvent aCallEvent; 490 aCallEvent.Set(EVENT_OBJECT_URLITEM, pFnd); 491 GetDoc()->CallEvent(SvMacroItemId::OnClick, aCallEvent); 492 493 ::LoadURL(*this, sURL, nFilter, sTargetFrameName); 494 } 495 return bRet; 496 } 497 498 void LoadURL( SwViewShell& rVSh, const OUString& rURL, LoadUrlFlags nFilter, 499 const OUString& rTargetFrameName ) 500 { 501 OSL_ENSURE( !rURL.isEmpty(), "what should be loaded here?" ); 502 if( rURL.isEmpty() ) 503 return ; 504 505 // The shell could be 0 also!!!!! 506 if ( dynamic_cast<const SwCursorShell*>( &rVSh) == nullptr ) 507 return; 508 509 // We are doing tiledRendering, let the client handles the URL loading, 510 // unless we are jumping to a TOC mark. 511 if (comphelper::LibreOfficeKit::isActive() && !rURL.startsWith("#")) 512 { 513 rVSh.GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_HYPERLINK_CLICKED, rURL.toUtf8().getStr()); 514 return; 515 } 516 517 //A CursorShell is always a WrtShell 518 SwWrtShell &rSh = static_cast<SwWrtShell&>(rVSh); 519 520 SwDocShell* pDShell = rSh.GetView().GetDocShell(); 521 OSL_ENSURE( pDShell, "No DocShell?!"); 522 OUString sTargetFrame(rTargetFrameName); 523 if (sTargetFrame.isEmpty() && pDShell) 524 { 525 using namespace ::com::sun::star; 526 uno::Reference<document::XDocumentPropertiesSupplier> xDPS( 527 pDShell->GetModel(), uno::UNO_QUERY_THROW); 528 uno::Reference<document::XDocumentProperties> xDocProps 529 = xDPS->getDocumentProperties(); 530 sTargetFrame = xDocProps->getDefaultTarget(); 531 } 532 533 OUString sReferer; 534 if( pDShell && pDShell->GetMedium() ) 535 sReferer = pDShell->GetMedium()->GetName(); 536 SfxViewFrame* pViewFrame = rSh.GetView().GetViewFrame(); 537 SfxFrameItem aView( SID_DOCFRAME, pViewFrame ); 538 SfxStringItem aName( SID_FILE_NAME, rURL ); 539 SfxStringItem aTargetFrameName( SID_TARGETNAME, sTargetFrame ); 540 SfxStringItem aReferer( SID_REFERER, sReferer ); 541 542 SfxBoolItem aNewView( SID_OPEN_NEW_VIEW, false ); 543 //#39076# Silent can be removed accordingly to SFX. 544 SfxBoolItem aBrowse( SID_BROWSE, true ); 545 546 if ((nFilter & LoadUrlFlags::NewView) && !comphelper::LibreOfficeKit::isActive()) 547 aTargetFrameName.SetValue( "_blank" ); 548 549 const SfxPoolItem* aArr[] = { 550 &aName, 551 &aNewView, /*&aSilent,*/ 552 &aReferer, 553 &aView, &aTargetFrameName, 554 &aBrowse, 555 nullptr 556 }; 557 558 pViewFrame->GetDispatcher()->GetBindings()->Execute( SID_OPENDOC, aArr, 559 SfxCallMode::ASYNCHRON|SfxCallMode::RECORD ); 560 } 561 562 void SwWrtShell::NavigatorPaste( const NaviContentBookmark& rBkmk, 563 const sal_uInt16 nAction ) 564 { 565 if( EXCHG_IN_ACTION_COPY == nAction ) 566 { 567 // Insert 568 OUString sURL = rBkmk.GetURL(); 569 // Is this is a jump within the current Doc? 570 const SwDocShell* pDocShell = GetView().GetDocShell(); 571 if(pDocShell->HasName()) 572 { 573 const OUString rName = pDocShell->GetMedium()->GetURLObject().GetURLNoMark(); 574 575 if (sURL.startsWith(rName)) 576 { 577 if (sURL.getLength()>rName.getLength()) 578 { 579 sURL = sURL.copy(rName.getLength()); 580 } 581 else 582 { 583 sURL.clear(); 584 } 585 } 586 } 587 SwFormatINetFormat aFormat( sURL, OUString() ); 588 InsertURL( aFormat, rBkmk.GetDescription() ); 589 } 590 else 591 { 592 SwSectionData aSection( SectionType::FileLink, GetUniqueSectionName() ); 593 OUString aLinkFile = rBkmk.GetURL().getToken(0, '#') 594 + OUStringChar(sfx2::cTokenSeparator) 595 + OUStringChar(sfx2::cTokenSeparator) 596 + rBkmk.GetURL().getToken(1, '#'); 597 aSection.SetLinkFileName( aLinkFile ); 598 aSection.SetProtectFlag( true ); 599 const SwSection* pIns = InsertSection( aSection ); 600 if( EXCHG_IN_ACTION_MOVE == nAction && pIns ) 601 { 602 aSection = SwSectionData(*pIns); 603 aSection.SetLinkFileName( OUString() ); 604 aSection.SetType( SectionType::Content ); 605 aSection.SetProtectFlag( false ); 606 607 // the update of content from linked section at time delete 608 // the undostack. Then the change of the section don't create 609 // any undoobject. - BUG 69145 610 bool bDoesUndo = DoesUndo(); 611 SwUndoId nLastUndoId(SwUndoId::EMPTY); 612 if (GetLastUndoInfo(nullptr, & nLastUndoId)) 613 { 614 if (SwUndoId::INSSECTION != nLastUndoId) 615 { 616 DoUndo(false); 617 } 618 } 619 UpdateSection( GetSectionFormatPos( *pIns->GetFormat() ), aSection ); 620 DoUndo( bDoesUndo ); 621 } 622 } 623 } 624 625 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 626
