/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <hintids.hxx>
#include <svl/macitem.hxx>
#include <sfx2/frame.hxx>
#include <svl/urihelper.hxx>
#include <svl/eitem.hxx>
#include <svl/stritem.hxx>
#include <sfx2/docfile.hxx>
#include <sfx2/fcontnr.hxx>
#include <sfx2/dispatch.hxx>
#include <sfx2/linkmgr.hxx>
#include <fmtinfmt.hxx>
#include <frmatr.hxx>
#include <swtypes.hxx>
#include <wrtsh.hxx>
#include <docsh.hxx>
#include <fldbas.hxx>
#include <expfld.hxx>
#include <ddefld.hxx>
#include <docufld.hxx>
#include <reffld.hxx>
#include <swundo.hxx>
#include <doc.hxx>
#include <IDocumentUndoRedo.hxx>
#include <viewopt.hxx>
#include <frmfmt.hxx>
#include <fmtfld.hxx>
#include <swtable.hxx>
#include <mdiexp.hxx>
#include <view.hxx>
#include <swevent.hxx>
#include <poolfmt.hxx>
#include <section.hxx>
#include <navicont.hxx>
#include <navipi.hxx>
#include <txtinet.hxx>
#include <cmdid.h>
#include <swabstdlg.hxx>
#include <SwRewriter.hxx>
#include <com/sun/star/document/XDocumentProperties.hpp>
#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
#include <memory>
#include <LibreOfficeKit/LibreOfficeKitEnums.h>
#include <comphelper/lok.hxx>
#include <sfx2/event.hxx>
#include <sal/log.hxx>
void SwWrtShell::Insert(SwField const &rField)
{
ResetCursorStack();
if(!CanInsert())
return;
StartAllAction();
SwRewriter aRewriter;
aRewriter.AddRule(UndoArg1, rField.GetDescription());
StartUndo(SwUndoId::INSERT, &aRewriter);
bool bDeleted = false;
std::unique_ptr<SwPaM> pAnnotationTextRange;
if ( HasSelection() )
{
if ( rField.GetTyp()->Which() == SwFieldIds::Postit )
{
// for annotation fields:
// - keep the current selection in order to create a corresponding annotation mark
// - collapse cursor to its end
if ( IsTableMode() )
{
GetTableCrs()->Normalize( false );
const SwPosition rStartPos( *(GetTableCrs()->GetMark()->nNode.GetNode().GetContentNode()), 0 );
KillPams();
if ( !IsEndOfPara() )
{
EndPara();
}
const SwPosition rEndPos( *GetCurrentShellCursor().GetPoint() );
pAnnotationTextRange.reset(new SwPaM( rStartPos, rEndPos ));
}
else
{
NormalizePam( false );
const SwPaM& rCurrPaM = GetCurrentShellCursor();
pAnnotationTextRange.reset(new SwPaM( *rCurrPaM.GetPoint(), *rCurrPaM.GetMark() ));
ClearMark();
}
}
else
{
bDeleted = DelRight();
}
}
SwEditShell::Insert2(rField, bDeleted);
if ( pAnnotationTextRange )
{
if ( GetDoc() != nullptr )
{
IDocumentMarkAccess* pMarksAccess = GetDoc()->getIDocumentMarkAccess();
pMarksAccess->makeAnnotationMark( *pAnnotationTextRange, OUString() );
}
pAnnotationTextRange.reset();
}
EndUndo();
EndAllAction();
}
// Start the field update
void SwWrtShell::UpdateInputFields( SwInputFieldList* pLst )
{
// Go through the list of fields and updating
SwInputFieldList* pTmp = pLst;
if( !pTmp )
pTmp = new SwInputFieldList( this );
const size_t nCnt = pTmp->Count();
if(nCnt)
{
pTmp->PushCursor();
bool bCancel = false;
size_t nIndex = 0;
FieldDialogPressedButton ePressedButton = BTN_NONE;
SwField* pField = GetCurField();
if (pField)
{
for (size_t i = 0; i < nCnt; i++)
{
if (pField == pTmp->GetField(i))
{
nIndex = i;
break;
}
}
}
while (!bCancel)
{
bool bPrev = nIndex > 0;
bool bNext = nIndex < nCnt - 1;
pTmp->GotoFieldPos(nIndex);
pField = pTmp->GetField(nIndex);
if (pField->GetTyp()->Which() == SwFieldIds::Dropdown)
{
bCancel = StartDropDownFieldDlg(pField, bPrev, bNext, GetView().GetFrameWeld(), &ePressedButton);
}
else
bCancel = StartInputFieldDlg(pField, bPrev, bNext, GetView().GetFrameWeld(), &ePressedButton);
if (!bCancel)
{
// Otherwise update error at multi-selection:
pTmp->GetField(nIndex)->GetTyp()->UpdateFields();
if (ePressedButton == BTN_PREV && nIndex > 0)
nIndex--;
else if (ePressedButton == BTN_NEXT && nIndex < nCnt - 1)
nIndex++;
else
bCancel = true;
}
}
pTmp->PopCursor();
}
if( !pLst )
delete pTmp;
}
// Listener class: will close InputField dialog if input field(s)
// is(are) deleted (for instance, by an extension) after the dialog shows up.
// Otherwise, the for loop in SwWrtShell::UpdateInputFields will crash when doing:
// 'pTmp->GetField( i )->GetTyp()->UpdateFields();'
// on a deleted field.
class FieldDeletionModify : public SwModify
{
public:
FieldDeletionModify(AbstractFieldInputDlg* pInputFieldDlg, SwField* pField)
: mpInputFieldDlg(pInputFieldDlg)
, mpFormatField(nullptr)
{
SwInputField *const pInputField(dynamic_cast<SwInputField*>(pField));
SwSetExpField *const pSetExpField(dynamic_cast<SwSetExpField*>(pField));
if (pInputField && pInputField->GetFormatField())
{
mpFormatField = pInputField->GetFormatField();
}
else if (pSetExpField && pSetExpField->GetFormatField())
{
mpFormatField = pSetExpField->GetFormatField();
}
// Register for possible field deletion while dialog is open
if (mpFormatField)
mpFormatField->Add(this);
}
virtual ~FieldDeletionModify() override
{
// Dialog closed, remove modification listener
EndListeningAll();
}
void Modify( const SfxPoolItem* pOld, const SfxPoolItem *) override
{
// Input field has been deleted: better to close the dialog
if (pOld)
{
switch (pOld->Which())
{
case RES_REMOVE_UNO_OBJECT:
case RES_OBJECTDYING:
mpFormatField = nullptr;
mpInputFieldDlg->EndDialog(RET_CANCEL);
break;
}
}
}
private:
VclPtr<AbstractFieldInputDlg> mpInputFieldDlg;
SwFormatField* mpFormatField;
};
// Start input dialog for a specific field
bool SwWrtShell::StartInputFieldDlg(SwField* pField, bool bPrevButton, bool bNextButton,
weld::Window* pParentWin, SwWrtShell::FieldDialogPressedButton* pPressedButton)
{
SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create();
ScopedVclPtr<AbstractFieldInputDlg> pDlg(pFact->CreateFieldInputDlg(pParentWin, *this, pField, bPrevButton, bNextButton));
bool bRet;
{
FieldDeletionModify aModify(pDlg.get(), pField);
bRet = RET_CANCEL == pDlg->Execute();
}
if (pPressedButton)
{
if (pDlg->PrevButtonPressed())
*pPressedButton = BTN_PREV;
else if (pDlg->NextButtonPressed())
*pPressedButton = BTN_NEXT;
}
pDlg.disposeAndClear();
GetWin()->Update();
return bRet;
}
bool SwWrtShell::StartDropDownFieldDlg(SwField* pField, bool bPrevButton, bool bNextButton,
weld::Window* pParentWin, SwWrtShell::FieldDialogPressedButton* pPressedButton)
{
SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create();
ScopedVclPtr<AbstractDropDownFieldDialog> pDlg(pFact->CreateDropDownFieldDialog(pParentWin, *this, pField, bPrevButton, bNextButton));
const short nRet = pDlg->Execute();
if (pPressedButton)
{
if (pDlg->PrevButtonPressed())
*pPressedButton = BTN_PREV;
else if (pDlg->NextButtonPressed())
*pPressedButton = BTN_NEXT;
}
pDlg.disposeAndClear();
bool bRet = RET_CANCEL == nRet;
GetWin()->Update();
if(RET_YES == nRet)
{
GetView().GetViewFrame()->GetDispatcher()->Execute(FN_EDIT_FIELD, SfxCallMode::SYNCHRON);
}
return bRet;
}
// Insert directory - remove selection
void SwWrtShell::InsertTableOf(const SwTOXBase& rTOX, const SfxItemSet* pSet)
{
if(!CanInsert())
return;
if(HasSelection())
DelRight();
SwEditShell::InsertTableOf(rTOX, pSet);
}
// Update directory - remove selection
void SwWrtShell::UpdateTableOf(const SwTOXBase& rTOX, const SfxItemSet* pSet)
{
if(CanInsert())
{
SwEditShell::UpdateTableOf(rTOX, pSet);
if (pSet == nullptr)
{
SwDoc *const pDoc_ = GetDoc();
if (pDoc_)
{
pDoc_->GetIDocumentUndoRedo().DelAllUndoObj();
}
}
}
}
// handler for click on the field given as parameter.
// the cursor is positioned on the field.
void SwWrtShell::ClickToField( const SwField& rField )
{
// cross reference field must not be selected because it moves the cursor
if (SwFieldIds::GetRef != rField.GetTyp()->Which())
{
StartAllAction();
Right( CRSR_SKIP_CHARS, true, 1, false ); // Select the field.
NormalizePam();
EndAllAction();
}
m_bIsInClickToEdit = true;
switch( rField.GetTyp()->Which() )
{
case SwFieldIds::JumpEdit:
{
sal_uInt16 nSlotId = 0;
switch( rField.GetFormat() )
{
case JE_FMT_TABLE:
nSlotId = FN_INSERT_TABLE;
break;
case JE_FMT_FRAME:
nSlotId = FN_INSERT_FRAME;
break;
case JE_FMT_GRAPHIC: nSlotId = SID_INSERT_GRAPHIC; break;
case JE_FMT_OLE: nSlotId = SID_INSERT_OBJECT; break;
}
if( nSlotId )
{
StartUndo( SwUndoId::START );
//#97295# immediately select the right shell
GetView().StopShellTimer();
GetView().GetViewFrame()->GetDispatcher()->Execute( nSlotId,
SfxCallMode::SYNCHRON|SfxCallMode::RECORD );
EndUndo( SwUndoId::END );
}
}
break;
case SwFieldIds::Macro:
{
const SwMacroField *pField = static_cast<const SwMacroField*>(&rField);
const OUString sText( rField.GetPar2() );
OUString sRet( sText );
ExecMacro( pField->GetSvxMacro(), &sRet );
// return value changed?
if( sRet != sText )
{
StartAllAction();
const_cast<SwField&>(rField).SetPar2( sRet );
rField.GetTyp()->UpdateFields();
EndAllAction();
}
}
break;
case SwFieldIds::GetRef:
StartAllAction();
SwCursorShell::GotoRefMark( static_cast<const SwGetRefField&>(rField).GetSetRefName(),
static_cast<const SwGetRefField&>(rField).GetSubType(),
static_cast<const SwGetRefField&>(rField).GetSeqNo() );
EndAllAction();
break;
case SwFieldIds::Input:
{
const SwInputField* pInputField = dynamic_cast<const SwInputField*>(&rField);
if ( pInputField == nullptr )
{
StartInputFieldDlg(const_cast<SwField*>(&rField), false, false, GetView().GetFrameWeld());
}
}
break;
case SwFieldIds::SetExp:
if( static_cast<const SwSetExpField&>(rField).GetInputFlag() )
StartInputFieldDlg(const_cast<SwField*>(&rField), false, false, GetView().GetFrameWeld());
break;
case SwFieldIds::Dropdown :
StartDropDownFieldDlg(const_cast<SwField*>(&rField), false, false, GetView().GetFrameWeld());
break;
default:
SAL_WARN_IF(rField.IsClickable(), "sw", "unhandled clickable field!");
}
m_bIsInClickToEdit = false;
}
void SwWrtShell::ClickToINetAttr( const SwFormatINetFormat& rItem, LoadUrlFlags nFilter )
{
if( rItem.GetValue().isEmpty() )
return ;
m_bIsInClickToEdit = true;
// At first run the possibly set ObjectSelect Macro
const SvxMacro* pMac = rItem.GetMacro( SvMacroItemId::OnClick );
if( pMac )
{
SwCallMouseEvent aCallEvent;
aCallEvent.Set( &rItem );
GetDoc()->CallEvent( SvMacroItemId::OnClick, aCallEvent );
}
// So that the implementation of templates is displayed immediately
::LoadURL( *this, rItem.GetValue(), nFilter, rItem.GetTargetFrame() );
const SwTextINetFormat* pTextAttr = rItem.GetTextINetFormat();
if( pTextAttr )
{
const_cast<SwTextINetFormat*>(pTextAttr)->SetVisited( true );
const_cast<SwTextINetFormat*>(pTextAttr)->SetVisitedValid( true );
}
m_bIsInClickToEdit = false;
}
bool SwWrtShell::ClickToINetGrf( const Point& rDocPt, LoadUrlFlags nFilter )
{
bool bRet = false;
OUString sURL;
OUString sTargetFrameName;
const SwFrameFormat* pFnd = IsURLGrfAtPos( rDocPt, &sURL, &sTargetFrameName );
if( pFnd && !sURL.isEmpty() )
{
bRet = true;
// At first run the possibly set ObjectSelect Macro
const SvxMacro* pMac = &pFnd->GetMacro().GetMacro( SvMacroItemId::OnClick );
if( pMac )
{
SwCallMouseEvent aCallEvent;
aCallEvent.Set( EVENT_OBJECT_URLITEM, pFnd );
GetDoc()->CallEvent( SvMacroItemId::OnClick, aCallEvent );
}
::LoadURL(*this, sURL, nFilter, sTargetFrameName);
}
return bRet;
}
void LoadURL( SwViewShell& rVSh, const OUString& rURL, LoadUrlFlags nFilter,
const OUString& rTargetFrameName )
{
OSL_ENSURE( !rURL.isEmpty(), "what should be loaded here?" );
if( rURL.isEmpty() )
return ;
// The shell could be 0 also!!!!!
if ( dynamic_cast<const SwCursorShell*>( &rVSh) == nullptr )
return;
OUString sFileURL = rURL;
INetURLObject aURL( sFileURL );
if( aURL.GetProtocol() == INetProtocol::NotValid && !sFileURL.startsWith("#") )
{
// May be the relative link -> try to convert to absolute path
OUString sParentPath =
rVSh.GetDoc()->GetDocShell()->GetMedium()->GetURLObject().GetPath();
bool bCorrectURL = true;
aURL = INetURLObject();
bCorrectURL &= aURL.setFSysPath( sParentPath, FSysStyle::Detect );
bCorrectURL &= aURL.insertName( sFileURL );
if( bCorrectURL )
sFileURL = aURL.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous );
}
// We are doing tiledRendering, let the client handles the URL loading,
// unless we are jumping to a TOC mark.
if (comphelper::LibreOfficeKit::isActive() && !rURL.startsWith("#"))
{
rVSh.GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_HYPERLINK_CLICKED, sFileURL.toUtf8().getStr());
return;
}
//A CursorShell is always a WrtShell
SwWrtShell &rSh = static_cast<SwWrtShell&>(rVSh);
SwDocShell* pDShell = rSh.GetView().GetDocShell();
OSL_ENSURE( pDShell, "No DocShell?!");
OUString sTargetFrame(rTargetFrameName);
if (sTargetFrame.isEmpty() && pDShell)
{
using namespace ::com::sun::star;
uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
pDShell->GetModel(), uno::UNO_QUERY_THROW);
uno::Reference<document::XDocumentProperties> xDocProps
= xDPS->getDocumentProperties();
sTargetFrame = xDocProps->getDefaultTarget();
}
OUString sReferer;
if( pDShell && pDShell->GetMedium() )
sReferer = pDShell->GetMedium()->GetName();
SfxViewFrame* pViewFrame = rSh.GetView().GetViewFrame();
SfxFrameItem aView( SID_DOCFRAME, pViewFrame );
SfxStringItem aName( SID_FILE_NAME, sFileURL );
SfxStringItem aTargetFrameName( SID_TARGETNAME, sTargetFrame );
SfxStringItem aReferer( SID_REFERER, sReferer );
SfxBoolItem aNewView( SID_OPEN_NEW_VIEW, false );
//#39076# Silent can be removed accordingly to SFX.
SfxBoolItem aBrowse( SID_BROWSE, true );
if ((nFilter & LoadUrlFlags::NewView) && !comphelper::LibreOfficeKit::isActive())
aTargetFrameName.SetValue( "_blank" );
const SfxPoolItem* aArr[] = {
&aName,
&aNewView, /*&aSilent,*/
&aReferer,
&aView, &aTargetFrameName,
&aBrowse,
nullptr
};
pViewFrame->GetDispatcher()->GetBindings()->Execute( SID_OPENDOC, aArr,
SfxCallMode::ASYNCHRON|SfxCallMode::RECORD );
}
void SwWrtShell::NavigatorPaste( const NaviContentBookmark& rBkmk,
const sal_uInt16 nAction )
{
if( EXCHG_IN_ACTION_COPY == nAction )
{
// Insert
OUString sURL = rBkmk.GetURL();
// Is this is a jump within the current Doc?
const SwDocShell* pDocShell = GetView().GetDocShell();
if(pDocShell->HasName())
{
const OUString rName = pDocShell->GetMedium()->GetURLObject().GetURLNoMark();
if (sURL.startsWith(rName))
{
if (sURL.getLength()>rName.getLength())
{
sURL = sURL.copy(rName.getLength());
}
else
{
sURL.clear();
}
}
}
SwFormatINetFormat aFormat( sURL, OUString() );
InsertURL( aFormat, rBkmk.GetDescription() );
}
else
{
SwSectionData aSection( FILE_LINK_SECTION, GetUniqueSectionName() );
OUString aLinkFile = rBkmk.GetURL().getToken(0, '#')
+ OUStringLiteral1(sfx2::cTokenSeparator)
+ OUStringLiteral1(sfx2::cTokenSeparator)
+ rBkmk.GetURL().getToken(1, '#');
aSection.SetLinkFileName( aLinkFile );
aSection.SetProtectFlag( true );
const SwSection* pIns = InsertSection( aSection );
if( EXCHG_IN_ACTION_MOVE == nAction && pIns )
{
aSection = SwSectionData(*pIns);
aSection.SetLinkFileName( OUString() );
aSection.SetType( CONTENT_SECTION );
aSection.SetProtectFlag( false );
// the update of content from linked section at time delete
// the undostack. Then the change of the section don't create
// any undoobject. - BUG 69145
bool bDoesUndo = DoesUndo();
SwUndoId nLastUndoId(SwUndoId::EMPTY);
if (GetLastUndoInfo(nullptr, & nLastUndoId))
{
if (SwUndoId::INSSECTION != nLastUndoId)
{
DoUndo(false);
}
}
UpdateSection( GetSectionFormatPos( *pIns->GetFormat() ), aSection );
DoUndo( bDoesUndo );
}
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V773 Visibility scope of the 'pTmp' pointer was exited without releasing the memory. A memory leak is possible.
↑ V547 Expression 'pMac' is always true.