/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void SwWrtShell::Insert(SwField const& rField, SwPaM* pAnnotationRange) { ResetCursorStack(); if(!CanInsert()) return; StartAllAction(); SwRewriter aRewriter; aRewriter.AddRule(UndoArg1, rField.GetDescription()); StartUndo(SwUndoId::INSERT, &aRewriter); bool bDeleted = false; std::unique_ptr pAnnotationTextRange; if (pAnnotationRange) { pAnnotationTextRange.reset(new SwPaM(*pAnnotationRange->Start(), *pAnnotationRange->End())); } 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 ) { const SwPaM& rCurrPaM = GetCurrentShellCursor(); if (*rCurrPaM.Start() == *pAnnotationTextRange->Start() && *rCurrPaM.End() == *pAnnotationTextRange->End()) { // Annotation range was passed in externally, and inserting the postit field shifted // its start/end positions right by one. Restore the original position for the range // start. This allows commenting on the placeholder character of the field. SwIndex& rRangeStart = pAnnotationTextRange->Start()->nContent; if (rRangeStart.GetIndex() > 0) --rRangeStart; } 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 std::unique_ptr pTmp; if (!pLst) { pTmp.reset(new SwInputFieldList( this )); pLst = pTmp.get(); } const size_t nCnt = pLst->Count(); if(nCnt) { pLst->PushCursor(); bool bCancel = false; size_t nIndex = 0; FieldDialogPressedButton ePressedButton = FieldDialogPressedButton::NONE; SwField* pField = GetCurField(); if (pField) { for (size_t i = 0; i < nCnt; i++) { if (pField == pLst->GetField(i)) { nIndex = i; break; } } } while (!bCancel) { bool bPrev = nIndex > 0; bool bNext = nIndex < nCnt - 1; pLst->GotoFieldPos(nIndex); pField = pLst->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: pLst->GetField(nIndex)->GetTyp()->UpdateFields(); if (ePressedButton == FieldDialogPressedButton::Previous && nIndex > 0) nIndex--; else if (ePressedButton == FieldDialogPressedButton::Next && nIndex < nCnt - 1) nIndex++; else bCancel = true; } } pLst->PopCursor(); } } namespace { // 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 FieldDeletionListener : public SvtListener { public: FieldDeletionListener(AbstractFieldInputDlg* pInputFieldDlg, SwField* pField) : mpInputFieldDlg(pInputFieldDlg) , mpFormatField(nullptr) { SwInputField *const pInputField(dynamic_cast(pField)); SwSetExpField *const pSetExpField(dynamic_cast(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) StartListening(mpFormatField->GetNotifier()); } virtual ~FieldDeletionListener() override { // Dialog closed, remove modification listener EndListeningAll(); } virtual void Notify(const SfxHint& rHint) override { // Input field has been deleted: better to close the dialog if(rHint.GetId() == SfxHintId::Dying) { mpFormatField = nullptr; mpInputFieldDlg->EndDialog(RET_CANCEL); } } private: VclPtr mpInputFieldDlg; SwFormatField* mpFormatField; }; } // Start input dialog for a specific field bool SwWrtShell::StartInputFieldDlg(SwField* pField, bool bPrevButton, bool bNextButton, weld::Widget* pParentWin, SwWrtShell::FieldDialogPressedButton* pPressedButton) { SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); ScopedVclPtr pDlg(pFact->CreateFieldInputDlg(pParentWin, *this, pField, bPrevButton, bNextButton)); bool bRet; { FieldDeletionListener aModify(pDlg.get(), pField); bRet = RET_CANCEL == pDlg->Execute(); } if (pPressedButton) { if (pDlg->PrevButtonPressed()) *pPressedButton = FieldDialogPressedButton::Previous; else if (pDlg->NextButtonPressed()) *pPressedButton = FieldDialogPressedButton::Next; } pDlg.disposeAndClear(); GetWin()->PaintImmediately(); return bRet; } bool SwWrtShell::StartDropDownFieldDlg(SwField* pField, bool bPrevButton, bool bNextButton, weld::Widget* pParentWin, SwWrtShell::FieldDialogPressedButton* pPressedButton) { SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); ScopedVclPtr pDlg(pFact->CreateDropDownFieldDialog(pParentWin, *this, pField, bPrevButton, bNextButton)); const short nRet = pDlg->Execute(); if (pPressedButton) { if (pDlg->PrevButtonPressed()) *pPressedButton = FieldDialogPressedButton::Previous; else if (pDlg->NextButtonPressed()) *pPressedButton = FieldDialogPressedButton::Next; } pDlg.disposeAndClear(); bool bRet = RET_CANCEL == nRet; GetWin()->PaintImmediately(); 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); } } // 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(&rField); const OUString sText( rField.GetPar2() ); OUString sRet( sText ); ExecMacro( pField->GetSvxMacro(), &sRet ); // return value changed? if( sRet != sText ) { StartAllAction(); const_cast(rField).SetPar2( sRet ); rField.GetTyp()->UpdateFields(); EndAllAction(); } } break; case SwFieldIds::GetRef: StartAllAction(); SwCursorShell::GotoRefMark( static_cast(rField).GetSetRefName(), static_cast(rField).GetSubType(), static_cast(rField).GetSeqNo() ); EndAllAction(); break; case SwFieldIds::Input: { const SwInputField* pInputField = dynamic_cast(&rField); if ( pInputField == nullptr ) { StartInputFieldDlg(const_cast(&rField), false, false, GetView().GetFrameWeld()); } } break; case SwFieldIds::SetExp: if( static_cast(rField).GetInputFlag() ) StartInputFieldDlg(const_cast(&rField), false, false, GetView().GetFrameWeld()); break; case SwFieldIds::Dropdown : StartDropDownFieldDlg(const_cast(&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(pTextAttr)->SetVisited( true ); const_cast(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 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( &rVSh) == nullptr ) return; // 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, rURL.toUtf8().getStr()); return; } //A CursorShell is always a WrtShell SwWrtShell &rSh = static_cast(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 xDPS( pDShell->GetModel(), uno::UNO_QUERY_THROW); uno::Reference 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, rURL ); 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( SectionType::FileLink, GetUniqueSectionName() ); OUString aLinkFile = rBkmk.GetURL().getToken(0, '#') + OUStringChar(sfx2::cTokenSeparator) + OUStringChar(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( SectionType::Content ); 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: */