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 <fmtinfmt.hxx>
36 #include <docufld.hxx>
43 #include <swevent.hxx>
44 #include <section.hxx>
45 #include <navicont.hxx>
46 #include <txtinet.hxx>
48 #include <swabstdlg.hxx>
49 #include <SwRewriter.hxx>
50 #include <authfld.hxx>
52 #include <com/sun/star/document/XDocumentProperties.hpp>
53 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
57 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
58 #include <comphelper/lok.hxx>
59 #include <sfx2/event.hxx>
60 #include <sal/log.hxx>
62 bool SwWrtShell::InsertField2(SwField
const& rField
, SwPaM
* pAnnotationRange
)
70 aRewriter
.AddRule(UndoArg1
, rField
.GetDescription());
72 StartUndo(SwUndoId::INSERT
, &aRewriter
);
74 bool bDeleted
= false;
75 std::optional
<SwPaM
> pAnnotationTextRange
;
78 pAnnotationTextRange
.emplace(*pAnnotationRange
->Start(), *pAnnotationRange
->End());
83 if ( rField
.GetTyp()->Which() == SwFieldIds::Postit
)
85 // for annotation fields:
86 // - keep the current selection in order to create a corresponding annotation mark
87 // - collapse cursor to its end
90 GetTableCrs()->Normalize( false );
91 const SwPosition
rStartPos( *(GetTableCrs()->GetMark()->nNode
.GetNode().GetContentNode()), 0 );
97 const SwPosition
rEndPos( *GetCurrentShellCursor().GetPoint() );
98 pAnnotationTextRange
.emplace( rStartPos
, rEndPos
);
102 NormalizePam( false );
103 const SwPaM
& rCurrPaM
= GetCurrentShellCursor();
104 pAnnotationTextRange
.emplace( *rCurrPaM
.GetPoint(), *rCurrPaM
.GetMark() );
110 bDeleted
= DelRight();
114 bool const isSuccess
= SwEditShell::InsertField(rField
, bDeleted
);
116 if ( pAnnotationTextRange
)
118 if ( GetDoc() != nullptr )
120 const SwPaM
& rCurrPaM
= GetCurrentShellCursor();
121 if (*rCurrPaM
.Start() == *pAnnotationTextRange
->Start()
122 && *rCurrPaM
.End() == *pAnnotationTextRange
->End())
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)
131 IDocumentMarkAccess
* pMarksAccess
= GetDoc()->getIDocumentMarkAccess();
132 pMarksAccess
->makeAnnotationMark( *pAnnotationTextRange
, OUString() );
134 pAnnotationTextRange
.reset();
143 // Start the field update
145 void SwWrtShell::UpdateInputFields( SwInputFieldList
* pLst
)
147 // Go through the list of fields and updating
148 std::unique_ptr
<SwInputFieldList
> pTmp
;
151 pTmp
.reset(new SwInputFieldList( this ));
155 const size_t nCnt
= pLst
->Count();
161 bool bCancel
= false;
164 FieldDialogPressedButton ePressedButton
= FieldDialogPressedButton::NONE
;
166 SwField
* pField
= GetCurField();
169 for (size_t i
= 0; i
< nCnt
; i
++)
171 if (pField
== pLst
->GetField(i
))
181 bool bPrev
= nIndex
> 0;
182 bool bNext
= nIndex
< nCnt
- 1;
183 pLst
->GotoFieldPos(nIndex
);
184 pField
= pLst
->GetField(nIndex
);
185 if (pField
->GetTyp()->Which() == SwFieldIds::Dropdown
)
187 bCancel
= StartDropDownFieldDlg(pField
, bPrev
, bNext
, GetView().GetFrameWeld(), &ePressedButton
);
190 bCancel
= StartInputFieldDlg(pField
, bPrev
, bNext
, GetView().GetFrameWeld(), &ePressedButton
);
194 // Otherwise update error at multi-selection:
195 pLst
->GetField(nIndex
)->GetTyp()->UpdateFields();
197 if (ePressedButton
== FieldDialogPressedButton::Previous
&& nIndex
> 0)
199 else if (ePressedButton
== FieldDialogPressedButton::Next
&& nIndex
< nCnt
- 1)
211 // Listener class: will close InputField dialog if input field(s)
212 // is(are) deleted (for instance, by an extension) after the dialog shows up.
213 // Otherwise, the for loop in SwWrtShell::UpdateInputFields will crash when doing:
214 // 'pTmp->GetField( i )->GetTyp()->UpdateFields();'
215 // on a deleted field.
216 class FieldDeletionListener
: public SvtListener
219 FieldDeletionListener(AbstractFieldInputDlg
* pInputFieldDlg
, SwField
* pField
)
220 : mpInputFieldDlg(pInputFieldDlg
)
221 , mpFormatField(nullptr)
223 SwInputField
*const pInputField(dynamic_cast<SwInputField
*>(pField
));
224 SwSetExpField
*const pSetExpField(dynamic_cast<SwSetExpField
*>(pField
));
226 if (pInputField
&& pInputField
->GetFormatField())
228 mpFormatField
= pInputField
->GetFormatField();
230 else if (pSetExpField
&& pSetExpField
->GetFormatField())
232 mpFormatField
= pSetExpField
->GetFormatField();
235 // Register for possible field deletion while dialog is open
237 StartListening(mpFormatField
->GetNotifier());
240 virtual ~FieldDeletionListener() override
242 // Dialog closed, remove modification listener
246 virtual void Notify(const SfxHint
& rHint
) override
248 // Input field has been deleted: better to close the dialog
249 if(rHint
.GetId() == SfxHintId::Dying
)
251 mpFormatField
= nullptr;
252 mpInputFieldDlg
->EndDialog(RET_CANCEL
);
256 VclPtr
<AbstractFieldInputDlg
> mpInputFieldDlg
;
257 SwFormatField
* mpFormatField
;
262 // Start input dialog for a specific field
263 bool SwWrtShell::StartInputFieldDlg(SwField
* pField
, bool bPrevButton
, bool bNextButton
,
264 weld::Widget
* pParentWin
, SwWrtShell::FieldDialogPressedButton
* pPressedButton
)
267 SwAbstractDialogFactory
* pFact
= SwAbstractDialogFactory::Create();
268 ScopedVclPtr
<AbstractFieldInputDlg
> pDlg(pFact
->CreateFieldInputDlg(pParentWin
, *this, pField
, bPrevButton
, bNextButton
));
273 FieldDeletionListener
aModify(pDlg
.get(), pField
);
274 bRet
= RET_CANCEL
== pDlg
->Execute();
279 if (pDlg
->PrevButtonPressed())
280 *pPressedButton
= FieldDialogPressedButton::Previous
;
281 else if (pDlg
->NextButtonPressed())
282 *pPressedButton
= FieldDialogPressedButton::Next
;
285 pDlg
.disposeAndClear();
286 GetWin()->PaintImmediately();
290 bool SwWrtShell::StartDropDownFieldDlg(SwField
* pField
, bool bPrevButton
, bool bNextButton
,
291 weld::Widget
* pParentWin
, SwWrtShell::FieldDialogPressedButton
* pPressedButton
)
293 SwAbstractDialogFactory
* pFact
= SwAbstractDialogFactory::Create();
294 ScopedVclPtr
<AbstractDropDownFieldDialog
> pDlg(pFact
->CreateDropDownFieldDialog(pParentWin
, *this, pField
, bPrevButton
, bNextButton
));
295 const short nRet
= pDlg
->Execute();
299 if (pDlg
->PrevButtonPressed())
300 *pPressedButton
= FieldDialogPressedButton::Previous
;
301 else if (pDlg
->NextButtonPressed())
302 *pPressedButton
= FieldDialogPressedButton::Next
;
305 pDlg
.disposeAndClear();
306 bool bRet
= RET_CANCEL
== nRet
;
307 GetWin()->PaintImmediately();
310 GetView().GetViewFrame()->GetDispatcher()->Execute(FN_EDIT_FIELD
, SfxCallMode::SYNCHRON
);
315 // Insert directory - remove selection
317 void SwWrtShell::InsertTableOf(const SwTOXBase
& rTOX
, const SfxItemSet
* pSet
)
325 SwEditShell::InsertTableOf(rTOX
, pSet
);
328 // Update directory - remove selection
330 void SwWrtShell::UpdateTableOf(const SwTOXBase
& rTOX
, const SfxItemSet
* pSet
)
334 SwEditShell::UpdateTableOf(rTOX
, pSet
);
338 // handler for click on the field given as parameter.
339 // the cursor is positioned on the field.
341 void SwWrtShell::ClickToField(const SwField
& rField
, bool bExecHyperlinks
)
343 addCurrentPosition();
345 // cross reference field must not be selected because it moves the cursor
346 if (SwFieldIds::GetRef
!= rField
.GetTyp()->Which())
349 Right( CRSR_SKIP_CHARS
, true, 1, false ); // Select the field.
354 m_bIsInClickToEdit
= true;
355 switch( rField
.GetTyp()->Which() )
357 case SwFieldIds::JumpEdit
:
359 sal_uInt16 nSlotId
= 0;
360 switch( rField
.GetFormat() )
363 nSlotId
= FN_INSERT_TABLE
;
367 nSlotId
= FN_INSERT_FRAME
;
370 case JE_FMT_GRAPHIC
: nSlotId
= SID_INSERT_GRAPHIC
; break;
371 case JE_FMT_OLE
: nSlotId
= SID_INSERT_OBJECT
; break;
377 StartUndo( SwUndoId::START
);
378 //#97295# immediately select the right shell
379 GetView().StopShellTimer();
380 GetView().GetViewFrame()->GetDispatcher()->Execute( nSlotId
,
381 SfxCallMode::SYNCHRON
|SfxCallMode::RECORD
);
382 EndUndo( SwUndoId::END
);
387 case SwFieldIds::Macro
:
389 const SwMacroField
*pField
= static_cast<const SwMacroField
*>(&rField
);
390 const OUString
sText( rField
.GetPar2() );
391 OUString
sRet( sText
);
392 ExecMacro( pField
->GetSvxMacro(), &sRet
);
394 // return value changed?
398 const_cast<SwField
&>(rField
).SetPar2( sRet
);
399 rField
.GetTyp()->UpdateFields();
405 case SwFieldIds::TableOfAuthorities
:
407 if (!bExecHyperlinks
)
412 auto pField
= static_cast<const SwAuthorityField
*>(&rField
);
413 if (!pField
->HasURL())
418 const OUString
& rURL
= pField
->GetAbsoluteURL();
419 ::LoadURL(*this, rURL
, LoadUrlFlags::NewView
, /*rTargetFrameName=*/OUString());
423 case SwFieldIds::GetRef
:
425 SwCursorShell::GotoRefMark( static_cast<const SwGetRefField
&>(rField
).GetSetRefName(),
426 static_cast<const SwGetRefField
&>(rField
).GetSubType(),
427 static_cast<const SwGetRefField
&>(rField
).GetSeqNo() );
431 case SwFieldIds::Input
:
433 const SwInputField
* pInputField
= dynamic_cast<const SwInputField
*>(&rField
);
434 if ( pInputField
== nullptr )
436 StartInputFieldDlg(const_cast<SwField
*>(&rField
), false, false, GetView().GetFrameWeld());
441 case SwFieldIds::SetExp
:
442 if( static_cast<const SwSetExpField
&>(rField
).GetInputFlag() )
443 StartInputFieldDlg(const_cast<SwField
*>(&rField
), false, false, GetView().GetFrameWeld());
445 case SwFieldIds::Dropdown
:
446 StartDropDownFieldDlg(const_cast<SwField
*>(&rField
), false, false, GetView().GetFrameWeld());
449 SAL_WARN_IF(rField
.IsClickable(), "sw", "unhandled clickable field!");
452 m_bIsInClickToEdit
= false;
455 void SwWrtShell::ClickToINetAttr( const SwFormatINetFormat
& rItem
, LoadUrlFlags nFilter
)
457 addCurrentPosition();
459 if( rItem
.GetValue().isEmpty() )
462 m_bIsInClickToEdit
= true;
464 // At first run the possibly set ObjectSelect Macro
465 const SvxMacro
* pMac
= rItem
.GetMacro( SvMacroItemId::OnClick
);
468 SwCallMouseEvent aCallEvent
;
469 aCallEvent
.Set( &rItem
);
470 GetDoc()->CallEvent( SvMacroItemId::OnClick
, aCallEvent
);
473 // So that the implementation of templates is displayed immediately
474 ::LoadURL( *this, rItem
.GetValue(), nFilter
, rItem
.GetTargetFrame() );
475 const SwTextINetFormat
* pTextAttr
= rItem
.GetTextINetFormat();
478 const_cast<SwTextINetFormat
*>(pTextAttr
)->SetVisited( true );
479 const_cast<SwTextINetFormat
*>(pTextAttr
)->SetVisitedValid( true );
482 m_bIsInClickToEdit
= false;
485 bool SwWrtShell::ClickToINetGrf( const Point
& rDocPt
, LoadUrlFlags nFilter
)
489 OUString sTargetFrameName
;
490 const SwFrameFormat
* pFnd
= IsURLGrfAtPos( rDocPt
, &sURL
, &sTargetFrameName
);
491 if( pFnd
&& !sURL
.isEmpty() )
494 // At first run the possibly set ObjectSelect Macro
495 SwCallMouseEvent aCallEvent
;
496 aCallEvent
.Set(EVENT_OBJECT_URLITEM
, pFnd
);
497 GetDoc()->CallEvent(SvMacroItemId::OnClick
, aCallEvent
);
499 ::LoadURL(*this, sURL
, nFilter
, sTargetFrameName
);
504 static void LoadURL(SwView
& rView
, const OUString
& rURL
, LoadUrlFlags nFilter
,
505 const OUString
& rTargetFrameName
)
507 SwDocShell
* pDShell
= rView
.GetDocShell();
508 OSL_ENSURE( pDShell
, "No DocShell?!");
509 SfxViewFrame
* pViewFrame
= rView
.GetViewFrame();
511 if (!SfxObjectShell::AllowedLinkProtocolFromDocument(rURL
, pDShell
, pViewFrame
->GetFrameWeld()))
514 // We are doing tiledRendering, let the client handles the URL loading,
515 // unless we are jumping to a TOC mark.
516 if (comphelper::LibreOfficeKit::isActive() && !rURL
.startsWith("#"))
518 rView
.libreOfficeKitViewCallback(LOK_CALLBACK_HYPERLINK_CLICKED
, rURL
.toUtf8().getStr());
522 OUString
sTargetFrame(rTargetFrameName
);
523 if (sTargetFrame
.isEmpty() && pDShell
)
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();
534 if( pDShell
&& pDShell
->GetMedium() )
535 sReferer
= pDShell
->GetMedium()->GetName();
536 SfxFrameItem
aView( SID_DOCFRAME
, pViewFrame
);
537 SfxStringItem
aName( SID_FILE_NAME
, rURL
);
538 SfxStringItem
aTargetFrameName( SID_TARGETNAME
, sTargetFrame
);
539 SfxStringItem
aReferer( SID_REFERER
, sReferer
);
541 SfxBoolItem
aNewView( SID_OPEN_NEW_VIEW
, false );
542 //#39076# Silent can be removed accordingly to SFX.
543 SfxBoolItem
aBrowse( SID_BROWSE
, true );
545 if ((nFilter
& LoadUrlFlags::NewView
) && !comphelper::LibreOfficeKit::isActive())
546 aTargetFrameName
.SetValue( "_blank" );
548 const SfxPoolItem
* aArr
[] = {
550 &aNewView
, /*&aSilent,*/
552 &aView
, &aTargetFrameName
,
557 pViewFrame
->GetDispatcher()->GetBindings()->Execute( SID_OPENDOC
, aArr
,
558 SfxCallMode::ASYNCHRON
|SfxCallMode::RECORD
);
561 void LoadURL( SwViewShell
& rVSh
, const OUString
& rURL
, LoadUrlFlags nFilter
,
562 const OUString
& rTargetFrameName
)
564 OSL_ENSURE( !rURL
.isEmpty(), "what should be loaded here?" );
568 // The shell could be 0 also!!!!!
569 if ( dynamic_cast<const SwCursorShell
*>( &rVSh
) == nullptr )
572 //A CursorShell is always a WrtShell
573 SwWrtShell
&rSh
= static_cast<SwWrtShell
&>(rVSh
);
575 ::LoadURL(rSh
.GetView(), rURL
, nFilter
, rTargetFrameName
);
578 void SwWrtShell::NavigatorPaste( const NaviContentBookmark
& rBkmk
,
579 const sal_uInt16 nAction
)
581 if( EXCHG_IN_ACTION_COPY
== nAction
)
584 OUString sURL
= rBkmk
.GetURL();
585 // Is this is a jump within the current Doc?
586 const SwDocShell
* pDocShell
= GetView().GetDocShell();
587 if(pDocShell
->HasName())
589 const OUString rName
= pDocShell
->GetMedium()->GetURLObject().GetURLNoMark();
591 if (sURL
.startsWith(rName
))
593 if (sURL
.getLength()>rName
.getLength())
595 sURL
= sURL
.copy(rName
.getLength());
603 SwFormatINetFormat
aFormat( sURL
, OUString() );
604 InsertURL( aFormat
, rBkmk
.GetDescription() );
608 SwSectionData
aSection( SectionType::FileLink
, GetUniqueSectionName() );
609 OUString aLinkFile
= rBkmk
.GetURL().getToken(0, '#')
610 + OUStringChar(sfx2::cTokenSeparator
)
611 + OUStringChar(sfx2::cTokenSeparator
)
612 + rBkmk
.GetURL().getToken(1, '#');
613 aSection
.SetLinkFileName( aLinkFile
);
614 aSection
.SetProtectFlag( true );
615 const SwSection
* pIns
= InsertSection( aSection
);
616 if( EXCHG_IN_ACTION_MOVE
== nAction
&& pIns
)
618 aSection
= SwSectionData(*pIns
);
619 aSection
.SetLinkFileName( OUString() );
620 aSection
.SetType( SectionType::Content
);
621 aSection
.SetProtectFlag( false );
623 // the update of content from linked section at time delete
624 // the undostack. Then the change of the section don't create
625 // any undoobject. - BUG 69145
626 bool bDoesUndo
= DoesUndo();
627 SwUndoId
nLastUndoId(SwUndoId::EMPTY
);
628 if (GetLastUndoInfo(nullptr, & nLastUndoId
))
630 if (SwUndoId::INSSECTION
!= nLastUndoId
)
635 UpdateSection( GetSectionFormatPos( *pIns
->GetFormat() ), aSection
);
641 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */