1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
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 <o3tl/string_view.hxx>
32 #include <fmtinfmt.hxx>
37 #include <docufld.hxx>
44 #include <swevent.hxx>
45 #include <section.hxx>
46 #include <navicont.hxx>
47 #include <txtinet.hxx>
49 #include <swabstdlg.hxx>
50 #include <SwRewriter.hxx>
51 #include <authfld.hxx>
54 #include <com/sun/star/document/XDocumentProperties.hpp>
55 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
59 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
60 #include <comphelper/lok.hxx>
61 #include <sfx2/event.hxx>
62 #include <sal/log.hxx>
64 bool SwWrtShell::InsertField2(SwField
const& rField
, SwPaM
* pAnnotationRange
)
72 aRewriter
.AddRule(UndoArg1
, rField
.GetDescription());
74 StartUndo(SwUndoId::INSERT
, &aRewriter
);
76 bool bDeleted
= false;
77 std::optional
<SwPaM
> pAnnotationTextRange
;
80 pAnnotationTextRange
.emplace(*pAnnotationRange
->Start(), *pAnnotationRange
->End());
85 if ( rField
.GetTyp()->Which() == SwFieldIds::Postit
)
87 // for annotation fields:
88 // - keep the current selection in order to create a corresponding annotation mark
89 // - collapse cursor to its end
92 GetTableCrs()->Normalize( false );
93 const SwPosition
rStartPos( *(GetTableCrs()->GetMark()->GetNode().GetContentNode()), 0 );
99 const SwPosition
rEndPos( *GetCurrentShellCursor().GetPoint() );
100 pAnnotationTextRange
.emplace( rStartPos
, rEndPos
);
104 NormalizePam( false );
105 const SwPaM
& rCurrPaM
= GetCurrentShellCursor();
106 pAnnotationTextRange
.emplace( *rCurrPaM
.GetPoint(), *rCurrPaM
.GetMark() );
112 bDeleted
= DelRight();
116 bool const isSuccess
= SwEditShell::InsertField(rField
, bDeleted
);
118 if ( pAnnotationTextRange
)
120 if ( GetDoc() != nullptr )
122 const SwPaM
& rCurrPaM
= GetCurrentShellCursor();
123 if (*rCurrPaM
.Start() == *pAnnotationTextRange
->Start()
124 && *rCurrPaM
.End() == *pAnnotationTextRange
->End())
126 // Annotation range was passed in externally, and inserting the postit field shifted
127 // its start/end positions right by one. Restore the original position for the range
128 // start. This allows commenting on the placeholder character of the field.
129 if (pAnnotationTextRange
->Start()->GetContentIndex() > 0)
130 pAnnotationTextRange
->Start()->AdjustContent(-1);
132 IDocumentMarkAccess
* pMarksAccess
= GetDoc()->getIDocumentMarkAccess();
133 pMarksAccess
->makeAnnotationMark( *pAnnotationTextRange
, OUString() );
135 pAnnotationTextRange
.reset();
144 // Start the field update
146 void SwWrtShell::UpdateInputFields( SwInputFieldList
* pLst
)
148 // Go through the list of fields and updating
149 std::unique_ptr
<SwInputFieldList
> pTmp
;
152 pTmp
.reset(new SwInputFieldList( this ));
156 const size_t nCnt
= pLst
->Count();
162 bool bCancel
= false;
165 FieldDialogPressedButton ePressedButton
= FieldDialogPressedButton::NONE
;
167 SwField
* pField
= GetCurField();
170 for (size_t i
= 0; i
< nCnt
; i
++)
172 if (pField
== pLst
->GetField(i
))
182 bool bPrev
= nIndex
> 0;
183 bool bNext
= nIndex
< nCnt
- 1;
184 pLst
->GotoFieldPos(nIndex
);
185 pField
= pLst
->GetField(nIndex
);
186 if (pField
->GetTyp()->Which() == SwFieldIds::Dropdown
)
188 bCancel
= StartDropDownFieldDlg(pField
, bPrev
, bNext
, GetView().GetFrameWeld(), &ePressedButton
);
191 bCancel
= StartInputFieldDlg(pField
, bPrev
, bNext
, GetView().GetFrameWeld(), &ePressedButton
);
195 // Otherwise update error at multi-selection:
196 pLst
->GetField(nIndex
)->GetTyp()->UpdateFields();
198 if (ePressedButton
== FieldDialogPressedButton::Previous
&& nIndex
> 0)
200 else if (ePressedButton
== FieldDialogPressedButton::Next
&& nIndex
< nCnt
- 1)
212 // Listener class: will close InputField dialog if input field(s)
213 // is(are) deleted (for instance, by an extension) after the dialog shows up.
214 // Otherwise, the for loop in SwWrtShell::UpdateInputFields will crash when doing:
215 // 'pTmp->GetField( i )->GetTyp()->UpdateFields();'
216 // on a deleted field.
217 class FieldDeletionListener
: public SvtListener
220 FieldDeletionListener(AbstractFieldInputDlg
* pInputFieldDlg
, SwField
* pField
)
221 : mpInputFieldDlg(pInputFieldDlg
)
222 , mpFormatField(nullptr)
224 SwInputField
*const pInputField(dynamic_cast<SwInputField
*>(pField
));
225 SwSetExpField
*const pSetExpField(dynamic_cast<SwSetExpField
*>(pField
));
227 if (pInputField
&& pInputField
->GetFormatField())
229 mpFormatField
= pInputField
->GetFormatField();
231 else if (pSetExpField
&& pSetExpField
->GetFormatField())
233 mpFormatField
= pSetExpField
->GetFormatField();
236 // Register for possible field deletion while dialog is open
238 StartListening(mpFormatField
->GetNotifier());
241 virtual ~FieldDeletionListener() override
243 // Dialog closed, remove modification listener
247 virtual void Notify(const SfxHint
& rHint
) override
249 // Input field has been deleted: better to close the dialog
250 if(rHint
.GetId() == SfxHintId::Dying
)
252 mpFormatField
= nullptr;
253 mpInputFieldDlg
->EndDialog(RET_CANCEL
);
257 VclPtr
<AbstractFieldInputDlg
> mpInputFieldDlg
;
258 SwFormatField
* mpFormatField
;
263 // Start input dialog for a specific field
264 bool SwWrtShell::StartInputFieldDlg(SwField
* pField
, bool bPrevButton
, bool bNextButton
,
265 weld::Widget
* pParentWin
, SwWrtShell::FieldDialogPressedButton
* pPressedButton
)
268 SwAbstractDialogFactory
* pFact
= SwAbstractDialogFactory::Create();
269 ScopedVclPtr
<AbstractFieldInputDlg
> pDlg(pFact
->CreateFieldInputDlg(pParentWin
, *this, pField
, bPrevButton
, bNextButton
));
274 FieldDeletionListener
aModify(pDlg
.get(), pField
);
275 bRet
= RET_CANCEL
== pDlg
->Execute();
280 if (pDlg
->PrevButtonPressed())
281 *pPressedButton
= FieldDialogPressedButton::Previous
;
282 else if (pDlg
->NextButtonPressed())
283 *pPressedButton
= FieldDialogPressedButton::Next
;
286 pDlg
.disposeAndClear();
287 GetWin()->PaintImmediately();
291 bool SwWrtShell::StartDropDownFieldDlg(SwField
* pField
, bool bPrevButton
, bool bNextButton
,
292 weld::Widget
* pParentWin
, SwWrtShell::FieldDialogPressedButton
* pPressedButton
)
294 SwAbstractDialogFactory
* pFact
= SwAbstractDialogFactory::Create();
295 ScopedVclPtr
<AbstractDropDownFieldDialog
> pDlg(pFact
->CreateDropDownFieldDialog(pParentWin
, *this, pField
, bPrevButton
, bNextButton
));
296 const short nRet
= pDlg
->Execute();
300 if (pDlg
->PrevButtonPressed())
301 *pPressedButton
= FieldDialogPressedButton::Previous
;
302 else if (pDlg
->NextButtonPressed())
303 *pPressedButton
= FieldDialogPressedButton::Next
;
306 pDlg
.disposeAndClear();
307 bool bRet
= RET_CANCEL
== nRet
;
308 GetWin()->PaintImmediately();
311 GetView().GetViewFrame().GetDispatcher()->Execute(FN_EDIT_FIELD
, SfxCallMode::SYNCHRON
);
316 // Insert directory - remove selection
318 void SwWrtShell::InsertTableOf(const SwTOXBase
& rTOX
, const SfxItemSet
* pSet
)
326 SwEditShell::InsertTableOf(rTOX
, pSet
);
329 // Update directory - remove selection
331 void SwWrtShell::UpdateTableOf(const SwTOXBase
& rTOX
, const SfxItemSet
* pSet
)
335 SwEditShell::UpdateTableOf(rTOX
, pSet
);
339 // handler for click on the field given as parameter.
340 // the cursor is positioned on the field.
342 void SwWrtShell::ClickToField(const SwField
& rField
, bool bExecHyperlinks
)
344 addCurrentPosition();
346 // Since the cross reference and bibliography mark move the cursor,
347 // only select the field if it's not a Ctrl+Click
349 || (SwFieldIds::GetRef
!= rField
.GetTyp()->Which()
350 && SwFieldIds::TableOfAuthorities
!= rField
.GetTyp()->Which()))
353 Right( SwCursorSkipMode::Chars
, true, 1, false ); // Select the field.
358 m_bIsInClickToEdit
= true;
359 switch( rField
.GetTyp()->Which() )
361 case SwFieldIds::JumpEdit
:
363 sal_uInt16 nSlotId
= 0;
364 switch( rField
.GetFormat() )
367 nSlotId
= FN_INSERT_TABLE
;
371 nSlotId
= FN_INSERT_FRAME
;
374 case JE_FMT_GRAPHIC
: nSlotId
= SID_INSERT_GRAPHIC
; break;
375 case JE_FMT_OLE
: nSlotId
= SID_INSERT_OBJECT
; break;
381 StartUndo( SwUndoId::START
);
382 //#97295# immediately select the right shell
383 GetView().StopShellTimer();
384 GetView().GetViewFrame().GetDispatcher()->Execute( nSlotId
,
385 SfxCallMode::SYNCHRON
|SfxCallMode::RECORD
);
386 EndUndo( SwUndoId::END
);
391 case SwFieldIds::Macro
:
393 const SwMacroField
*pField
= static_cast<const SwMacroField
*>(&rField
);
394 const OUString
sText( rField
.GetPar2() );
395 OUString
sRet( sText
);
396 ExecMacro( pField
->GetSvxMacro(), &sRet
);
398 // return value changed?
402 const_cast<SwField
&>(rField
).SetPar2( sRet
);
403 rField
.GetTyp()->UpdateFields();
409 case SwFieldIds::TableOfAuthorities
:
411 if (!bExecHyperlinks
)
412 break; // Since it's not a Ctrl+Click, do not jump anywhere
414 Point vStartPoint
= GetCursor_()->GetPtPos();
415 const SwAuthorityField
* pField
= static_cast<const SwAuthorityField
*>(&rField
);
417 if (auto targetType
= pField
->GetTargetType();
418 targetType
== SwAuthorityField::TargetType::UseDisplayURL
419 || targetType
== SwAuthorityField::TargetType::UseTargetURL
)
421 // Since the user selected target type with URL, try to use it if not empty
422 if (const OUString
& rURL
= pField
->GetAbsoluteURL();
423 rURL
.getLength() > 0)
424 ::LoadURL(*this, rURL
, LoadUrlFlags::NewView
, /*rTargetFrameName=*/OUString());
426 else if (targetType
== SwAuthorityField::TargetType::BibliographyTableRow
)
428 // Since the user selected to target Bibliography Table Row,
429 // try finding matching bibliography table line
431 const bool bWasViewLocked
= IsViewLocked();
434 // Note: This way of iterating doesn't seem to take into account TOXes
435 // that are in a frame, probably in some other cases too
437 while (GotoNextTOXBase())
439 const SwTOXBase
* pIteratedTOX
= nullptr;
440 const SwTOXBase
* pPreviousTOX
= nullptr;
442 while ((pIteratedTOX
= GetCurTOX()) != nullptr
443 && pIteratedTOX
->GetType() == TOX_AUTHORITIES
)
445 if (pIteratedTOX
!= pPreviousTOX
)
446 vFieldText
= pField
->GetAuthority(GetLayout(), &pIteratedTOX
->GetTOXForm());
448 if (const SwNode
& rCurrentNode
= GetCursor()->GetPoint()->GetNode();
449 rCurrentNode
.GetNodeType() == SwNodeType::Text
450 && (GetCursor()->GetPoint()->GetNode().FindSectionNode()->GetSection().GetType()
451 == SectionType::ToxContent
) // this checks it's not a heading
452 && static_cast<const SwTextNode
*>(&rCurrentNode
)->GetText() == vFieldText
)
454 // Since a node has been found that is a text node, isn't a heading,
455 // and has text matching to text generated by the field, jump to it
456 LockView(bWasViewLocked
);
460 pPreviousTOX
= pIteratedTOX
;
464 // Since a matching node has not been found, return to original position
465 SetCursor(&vStartPoint
);
466 LockView(bWasViewLocked
);
471 case SwFieldIds::GetRef
:
472 if (!bExecHyperlinks
)
476 SwCursorShell::GotoRefMark( static_cast<const SwGetRefField
&>(rField
).GetSetRefName(),
477 static_cast<const SwGetRefField
&>(rField
).GetSubType(),
478 static_cast<const SwGetRefField
&>(rField
).GetSeqNo() );
482 case SwFieldIds::Input
:
484 const SwInputField
* pInputField
= dynamic_cast<const SwInputField
*>(&rField
);
485 if ( pInputField
== nullptr )
487 StartInputFieldDlg(const_cast<SwField
*>(&rField
), false, false, GetView().GetFrameWeld());
492 case SwFieldIds::SetExp
:
493 if( static_cast<const SwSetExpField
&>(rField
).GetInputFlag() )
494 StartInputFieldDlg(const_cast<SwField
*>(&rField
), false, false, GetView().GetFrameWeld());
496 case SwFieldIds::Dropdown
:
497 StartDropDownFieldDlg(const_cast<SwField
*>(&rField
), false, false, GetView().GetFrameWeld());
500 SAL_WARN_IF(rField
.IsClickable(), "sw", "unhandled clickable field!");
503 m_bIsInClickToEdit
= false;
506 void SwWrtShell::ClickToINetAttr( const SwFormatINetFormat
& rItem
, LoadUrlFlags nFilter
)
508 addCurrentPosition();
510 if( rItem
.GetValue().isEmpty() )
513 m_bIsInClickToEdit
= true;
515 // At first run the possibly set ObjectSelect Macro
516 const SvxMacro
* pMac
= rItem
.GetMacro( SvMacroItemId::OnClick
);
519 SwCallMouseEvent aCallEvent
;
520 aCallEvent
.Set( &rItem
);
521 GetDoc()->CallEvent( SvMacroItemId::OnClick
, aCallEvent
);
524 // So that the implementation of templates is displayed immediately
525 ::LoadURL( *this, rItem
.GetValue(), nFilter
, rItem
.GetTargetFrame() );
526 const SwTextINetFormat
* pTextAttr
= rItem
.GetTextINetFormat();
529 const_cast<SwTextINetFormat
*>(pTextAttr
)->SetVisited( true );
530 const_cast<SwTextINetFormat
*>(pTextAttr
)->SetVisitedValid( true );
533 m_bIsInClickToEdit
= false;
536 bool SwWrtShell::ClickToINetGrf( const Point
& rDocPt
, LoadUrlFlags nFilter
)
540 OUString sTargetFrameName
;
541 const SwFrameFormat
* pFnd
= IsURLGrfAtPos( rDocPt
, &sURL
, &sTargetFrameName
);
542 if( pFnd
&& !sURL
.isEmpty() )
545 // At first run the possibly set ObjectSelect Macro
546 SwCallMouseEvent aCallEvent
;
547 aCallEvent
.Set(EVENT_OBJECT_URLITEM
, pFnd
);
548 GetDoc()->CallEvent(SvMacroItemId::OnClick
, aCallEvent
);
550 ::LoadURL(*this, sURL
, nFilter
, sTargetFrameName
);
555 void LoadURL( SwViewShell
& rVSh
, const OUString
& rURL
, LoadUrlFlags nFilter
,
556 const OUString
& rTargetFrameName
)
558 OSL_ENSURE( !rURL
.isEmpty(), "what should be loaded here?" );
562 // The shell could be 0 also!!!!!
563 if ( dynamic_cast<const SwCursorShell
*>( &rVSh
) == nullptr )
566 // We are doing tiledRendering, let the client handles the URL loading,
567 // unless we are jumping to a TOC mark.
568 if (comphelper::LibreOfficeKit::isActive() && !rURL
.startsWith("#"))
570 rVSh
.GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_HYPERLINK_CLICKED
, rURL
.toUtf8());
574 //A CursorShell is always a WrtShell
575 SwWrtShell
&rSh
= static_cast<SwWrtShell
&>(rVSh
);
577 SwDocShell
* pDShell
= rSh
.GetView().GetDocShell();
578 OSL_ENSURE( pDShell
, "No DocShell?!");
579 OUString
sTargetFrame(rTargetFrameName
);
580 if (sTargetFrame
.isEmpty() && pDShell
)
582 using namespace ::com::sun::star
;
583 uno::Reference
<document::XDocumentPropertiesSupplier
> xDPS(
584 pDShell
->GetModel(), uno::UNO_QUERY_THROW
);
585 uno::Reference
<document::XDocumentProperties
> xDocProps
586 = xDPS
->getDocumentProperties();
587 sTargetFrame
= xDocProps
->getDefaultTarget();
591 if( pDShell
&& pDShell
->GetMedium() )
592 sReferer
= pDShell
->GetMedium()->GetName();
593 SfxViewFrame
& rViewFrame
= rSh
.GetView().GetViewFrame();
594 SfxFrameItem
aView( SID_DOCFRAME
, &rViewFrame
);
595 SfxStringItem
aName( SID_FILE_NAME
, rURL
);
596 SfxStringItem
aTargetFrameName( SID_TARGETNAME
, sTargetFrame
);
597 SfxStringItem
aReferer( SID_REFERER
, sReferer
);
599 SfxBoolItem
aNewView( SID_OPEN_NEW_VIEW
, false );
600 //#39076# Silent can be removed accordingly to SFX.
601 SfxBoolItem
aBrowse( SID_BROWSE
, true );
603 if ((nFilter
& LoadUrlFlags::NewView
) && !comphelper::LibreOfficeKit::isActive())
604 aTargetFrameName
.SetValue( "_blank" );
606 const SfxPoolItem
* aArr
[] = {
608 &aNewView
, /*&aSilent,*/
610 &aView
, &aTargetFrameName
,
615 rViewFrame
.GetDispatcher()->GetBindings()->Execute( SID_OPENDOC
, aArr
,
616 SfxCallMode::ASYNCHRON
|SfxCallMode::RECORD
);
619 void SwWrtShell::NavigatorPaste( const NaviContentBookmark
& rBkmk
,
620 const sal_uInt16 nAction
)
622 if( EXCHG_IN_ACTION_COPY
== nAction
)
625 OUString sURL
= rBkmk
.GetURL();
626 // Is this is a jump within the current Doc?
627 const SwDocShell
* pDocShell
= GetView().GetDocShell();
628 if(pDocShell
->HasName())
630 const OUString rName
= pDocShell
->GetMedium()->GetURLObject().GetURLNoMark();
632 if (sURL
.startsWith(rName
))
634 if (sURL
.getLength()>rName
.getLength())
636 sURL
= sURL
.copy(rName
.getLength());
644 SwFormatINetFormat
aFormat( sURL
, OUString() );
645 InsertURL( aFormat
, rBkmk
.GetDescription() );
649 SwSectionData
aSection( SectionType::FileLink
, GetUniqueSectionName() );
650 OUString aLinkFile
= o3tl::getToken(rBkmk
.GetURL(), 0, '#')
651 + OUStringChar(sfx2::cTokenSeparator
)
652 + OUStringChar(sfx2::cTokenSeparator
)
653 + o3tl::getToken(rBkmk
.GetURL(), 1, '#');
654 aSection
.SetLinkFileName( aLinkFile
);
655 aSection
.SetProtectFlag( true );
656 const SwSection
* pIns
= InsertSection( aSection
);
657 if( EXCHG_IN_ACTION_MOVE
== nAction
&& pIns
)
659 aSection
= SwSectionData(*pIns
);
660 aSection
.SetLinkFileName( OUString() );
661 aSection
.SetType( SectionType::Content
);
662 aSection
.SetProtectFlag( false );
664 // the update of content from linked section at time delete
665 // the undostack. Then the change of the section don't create
666 // any undoobject. - BUG 69145
667 bool bDoesUndo
= DoesUndo();
668 SwUndoId
nLastUndoId(SwUndoId::EMPTY
);
669 if (GetLastUndoInfo(nullptr, & nLastUndoId
))
671 if (SwUndoId::INSSECTION
!= nLastUndoId
)
676 UpdateSection( GetSectionFormatPos( *pIns
->GetFormat() ), aSection
);
682 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */