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
InsertField2(SwField const & rField,SwPaM * pAnnotationRange)62 bool SwWrtShell::InsertField2(SwField const& rField, SwPaM* pAnnotationRange)
63 {
64 ResetCursorStack();
65 if(!CanInsert())
66 return false;
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 bool const isSuccess = SwEditShell::InsertField(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 return isSuccess;
141 }
142
143 // Start the field update
144
UpdateInputFields(SwInputFieldList * pLst)145 void SwWrtShell::UpdateInputFields( SwInputFieldList* pLst )
146 {
147 // Go through the list of fields and updating
148 std::unique_ptr<SwInputFieldList> pTmp;
149 if (!pLst)
150 {
151 pTmp.reset(new SwInputFieldList( this ));
152 pLst = pTmp.get();
153 }
154
155 const size_t nCnt = pLst->Count();
156 if(!nCnt)
157 return;
158
159 pLst->PushCursor();
160
161 bool bCancel = false;
162
163 size_t nIndex = 0;
164 FieldDialogPressedButton ePressedButton = FieldDialogPressedButton::NONE;
165
166 SwField* pField = GetCurField();
167 if (pField)
168 {
169 for (size_t i = 0; i < nCnt; i++)
170 {
171 if (pField == pLst->GetField(i))
172 {
173 nIndex = i;
174 break;
175 }
176 }
177 }
178
179 while (!bCancel)
180 {
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)
186 {
187 bCancel = StartDropDownFieldDlg(pField, bPrev, bNext, GetView().GetFrameWeld(), &ePressedButton);
188 }
189 else
190 bCancel = StartInputFieldDlg(pField, bPrev, bNext, GetView().GetFrameWeld(), &ePressedButton);
191
192 if (!bCancel)
193 {
194 // Otherwise update error at multi-selection:
195 pLst->GetField(nIndex)->GetTyp()->UpdateFields();
196
197 if (ePressedButton == FieldDialogPressedButton::Previous && nIndex > 0)
198 nIndex--;
199 else if (ePressedButton == FieldDialogPressedButton::Next && nIndex < nCnt - 1)
200 nIndex++;
201 else
202 bCancel = true;
203 }
204 }
205
206 pLst->PopCursor();
207 }
208
209 namespace {
210
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
217 {
218 public:
FieldDeletionListener(AbstractFieldInputDlg * pInputFieldDlg,SwField * pField)219 FieldDeletionListener(AbstractFieldInputDlg* pInputFieldDlg, SwField* pField)
220 : mpInputFieldDlg(pInputFieldDlg)
221 , mpFormatField(nullptr)
222 {
223 SwInputField *const pInputField(dynamic_cast<SwInputField*>(pField));
224 SwSetExpField *const pSetExpField(dynamic_cast<SwSetExpField*>(pField));
225
226 if (pInputField && pInputField->GetFormatField())
227 {
228 mpFormatField = pInputField->GetFormatField();
229 }
230 else if (pSetExpField && pSetExpField->GetFormatField())
231 {
232 mpFormatField = pSetExpField->GetFormatField();
233 }
234
235 // Register for possible field deletion while dialog is open
236 if (mpFormatField)
237 StartListening(mpFormatField->GetNotifier());
238 }
239
~FieldDeletionListener()240 virtual ~FieldDeletionListener() override
241 {
242 // Dialog closed, remove modification listener
243 EndListeningAll();
244 }
245
Notify(const SfxHint & rHint)246 virtual void Notify(const SfxHint& rHint) override
247 {
248 // Input field has been deleted: better to close the dialog
249 if(rHint.GetId() == SfxHintId::Dying)
250 {
251 mpFormatField = nullptr;
252 mpInputFieldDlg->EndDialog(RET_CANCEL);
253 }
254 }
255 private:
256 VclPtr<AbstractFieldInputDlg> mpInputFieldDlg;
257 SwFormatField* mpFormatField;
258 };
259
260 }
261
262 // Start input dialog for a specific field
StartInputFieldDlg(SwField * pField,bool bPrevButton,bool bNextButton,weld::Widget * pParentWin,SwWrtShell::FieldDialogPressedButton * pPressedButton)263 bool SwWrtShell::StartInputFieldDlg(SwField* pField, bool bPrevButton, bool bNextButton,
264 weld::Widget* pParentWin, SwWrtShell::FieldDialogPressedButton* pPressedButton)
265 {
266
267 SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create();
268 ScopedVclPtr<AbstractFieldInputDlg> pDlg(pFact->CreateFieldInputDlg(pParentWin, *this, pField, bPrevButton, bNextButton));
269
270 bool bRet;
271
272 {
273 FieldDeletionListener aModify(pDlg.get(), pField);
274 bRet = RET_CANCEL == pDlg->Execute();
275 }
276
277 if (pPressedButton)
278 {
279 if (pDlg->PrevButtonPressed())
280 *pPressedButton = FieldDialogPressedButton::Previous;
281 else if (pDlg->NextButtonPressed())
282 *pPressedButton = FieldDialogPressedButton::Next;
283 }
284
285 pDlg.disposeAndClear();
286 GetWin()->PaintImmediately();
287 return bRet;
288 }
289
StartDropDownFieldDlg(SwField * pField,bool bPrevButton,bool bNextButton,weld::Widget * pParentWin,SwWrtShell::FieldDialogPressedButton * pPressedButton)290 bool SwWrtShell::StartDropDownFieldDlg(SwField* pField, bool bPrevButton, bool bNextButton,
291 weld::Widget* pParentWin, SwWrtShell::FieldDialogPressedButton* pPressedButton)
292 {
293 SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create();
294 ScopedVclPtr<AbstractDropDownFieldDialog> pDlg(pFact->CreateDropDownFieldDialog(pParentWin, *this, pField, bPrevButton, bNextButton));
295 const short nRet = pDlg->Execute();
296
297 if (pPressedButton)
298 {
299 if (pDlg->PrevButtonPressed())
300 *pPressedButton = FieldDialogPressedButton::Previous;
301 else if (pDlg->NextButtonPressed())
302 *pPressedButton = FieldDialogPressedButton::Next;
303 }
304
305 pDlg.disposeAndClear();
306 bool bRet = RET_CANCEL == nRet;
307 GetWin()->PaintImmediately();
308 if(RET_YES == nRet)
309 {
310 GetView().GetViewFrame()->GetDispatcher()->Execute(FN_EDIT_FIELD, SfxCallMode::SYNCHRON);
311 }
312 return bRet;
313 }
314
315 // Insert directory - remove selection
316
InsertTableOf(const SwTOXBase & rTOX,const SfxItemSet * pSet)317 void SwWrtShell::InsertTableOf(const SwTOXBase& rTOX, const SfxItemSet* pSet)
318 {
319 if(!CanInsert())
320 return;
321
322 if(HasSelection())
323 DelRight();
324
325 SwEditShell::InsertTableOf(rTOX, pSet);
326 }
327
328 // Update directory - remove selection
329
UpdateTableOf(const SwTOXBase & rTOX,const SfxItemSet * pSet)330 void SwWrtShell::UpdateTableOf(const SwTOXBase& rTOX, const SfxItemSet* pSet)
331 {
332 if(CanInsert())
333 {
334 SwEditShell::UpdateTableOf(rTOX, pSet);
335 }
336 }
337
338 // handler for click on the field given as parameter.
339 // the cursor is positioned on the field.
340
ClickToField(const SwField & rField,bool bExecHyperlinks)341 void SwWrtShell::ClickToField(const SwField& rField, bool bExecHyperlinks)
342 {
343 // cross reference field must not be selected because it moves the cursor
344 if (SwFieldIds::GetRef != rField.GetTyp()->Which())
345 {
346 StartAllAction();
347 Right( CRSR_SKIP_CHARS, true, 1, false ); // Select the field.
348 NormalizePam();
349 EndAllAction();
350 }
351
352 m_bIsInClickToEdit = true;
353 switch( rField.GetTyp()->Which() )
354 {
355 case SwFieldIds::JumpEdit:
356 {
357 sal_uInt16 nSlotId = 0;
358 switch( rField.GetFormat() )
359 {
360 case JE_FMT_TABLE:
361 nSlotId = FN_INSERT_TABLE;
362 break;
363
364 case JE_FMT_FRAME:
365 nSlotId = FN_INSERT_FRAME;
366 break;
367
368 case JE_FMT_GRAPHIC: nSlotId = SID_INSERT_GRAPHIC; break;
369 case JE_FMT_OLE: nSlotId = SID_INSERT_OBJECT; break;
370
371 }
372
373 if( nSlotId )
374 {
375 StartUndo( SwUndoId::START );
376 //#97295# immediately select the right shell
377 GetView().StopShellTimer();
378 GetView().GetViewFrame()->GetDispatcher()->Execute( nSlotId,
379 SfxCallMode::SYNCHRON|SfxCallMode::RECORD );
380 EndUndo( SwUndoId::END );
381 }
382 }
383 break;
384
385 case SwFieldIds::Macro:
386 {
387 const SwMacroField *pField = static_cast<const SwMacroField*>(&rField);
388 const OUString sText( rField.GetPar2() );
389 OUString sRet( sText );
390 ExecMacro( pField->GetSvxMacro(), &sRet );
391
392 // return value changed?
393 if( sRet != sText )
394 {
395 StartAllAction();
396 const_cast<SwField&>(rField).SetPar2( sRet );
397 rField.GetTyp()->UpdateFields();
398 EndAllAction();
399 }
400 }
401 break;
402
403 case SwFieldIds::TableOfAuthorities:
404 {
405 if (!bExecHyperlinks)
406 {
407 break;
408 }
409
410 auto pField = static_cast<const SwAuthorityField*>(&rField);
411 if (!pField->HasURL())
412 {
413 break;
414 }
415
416 const OUString& rURL = pField->GetAbsoluteURL();
417 ::LoadURL(*this, rURL, LoadUrlFlags::NewView, /*rTargetFrameName=*/OUString());
418 }
419 break;
420
421 case SwFieldIds::GetRef:
422 StartAllAction();
423 SwCursorShell::GotoRefMark( static_cast<const SwGetRefField&>(rField).GetSetRefName(),
424 static_cast<const SwGetRefField&>(rField).GetSubType(),
425 static_cast<const SwGetRefField&>(rField).GetSeqNo() );
426 EndAllAction();
427 break;
428
429 case SwFieldIds::Input:
430 {
431 const SwInputField* pInputField = dynamic_cast<const SwInputField*>(&rField);
432 if ( pInputField == nullptr )
433 {
434 StartInputFieldDlg(const_cast<SwField*>(&rField), false, false, GetView().GetFrameWeld());
435 }
436 }
437 break;
438
439 case SwFieldIds::SetExp:
440 if( static_cast<const SwSetExpField&>(rField).GetInputFlag() )
441 StartInputFieldDlg(const_cast<SwField*>(&rField), false, false, GetView().GetFrameWeld());
442 break;
443 case SwFieldIds::Dropdown :
444 StartDropDownFieldDlg(const_cast<SwField*>(&rField), false, false, GetView().GetFrameWeld());
445 break;
446 default:
447 SAL_WARN_IF(rField.IsClickable(), "sw", "unhandled clickable field!");
448 }
449
450 m_bIsInClickToEdit = false;
451 }
452
ClickToINetAttr(const SwFormatINetFormat & rItem,LoadUrlFlags nFilter)453 void SwWrtShell::ClickToINetAttr( const SwFormatINetFormat& rItem, LoadUrlFlags nFilter )
454 {
455 if( rItem.GetValue().isEmpty() )
456 return ;
457
458 m_bIsInClickToEdit = true;
459
460 // At first run the possibly set ObjectSelect Macro
461 const SvxMacro* pMac = rItem.GetMacro( SvMacroItemId::OnClick );
462 if( pMac )
463 {
464 SwCallMouseEvent aCallEvent;
465 aCallEvent.Set( &rItem );
466 GetDoc()->CallEvent( SvMacroItemId::OnClick, aCallEvent );
467 }
468
469 // So that the implementation of templates is displayed immediately
470 ::LoadURL( *this, rItem.GetValue(), nFilter, rItem.GetTargetFrame() );
471 const SwTextINetFormat* pTextAttr = rItem.GetTextINetFormat();
472 if( pTextAttr )
473 {
474 const_cast<SwTextINetFormat*>(pTextAttr)->SetVisited( true );
475 const_cast<SwTextINetFormat*>(pTextAttr)->SetVisitedValid( true );
476 }
477
478 m_bIsInClickToEdit = false;
479 }
480
ClickToINetGrf(const Point & rDocPt,LoadUrlFlags nFilter)481 bool SwWrtShell::ClickToINetGrf( const Point& rDocPt, LoadUrlFlags nFilter )
482 {
483 bool bRet = false;
484 OUString sURL;
485 OUString sTargetFrameName;
486 const SwFrameFormat* pFnd = IsURLGrfAtPos( rDocPt, &sURL, &sTargetFrameName );
487 if( pFnd && !sURL.isEmpty() )
488 {
489 bRet = true;
490 // At first run the possibly set ObjectSelect Macro
491 SwCallMouseEvent aCallEvent;
492 aCallEvent.Set(EVENT_OBJECT_URLITEM, pFnd);
493 GetDoc()->CallEvent(SvMacroItemId::OnClick, aCallEvent);
494
495 ::LoadURL(*this, sURL, nFilter, sTargetFrameName);
496 }
497 return bRet;
498 }
499
LoadURL(SwViewShell & rVSh,const OUString & rURL,LoadUrlFlags nFilter,const OUString & rTargetFrameName)500 void LoadURL( SwViewShell& rVSh, const OUString& rURL, LoadUrlFlags nFilter,
501 const OUString& rTargetFrameName )
502 {
503 OSL_ENSURE( !rURL.isEmpty(), "what should be loaded here?" );
504 if( rURL.isEmpty() )
505 return ;
506
507 // The shell could be 0 also!!!!!
508 if ( dynamic_cast<const SwCursorShell*>( &rVSh) == nullptr )
509 return;
510
511 // We are doing tiledRendering, let the client handles the URL loading,
512 // unless we are jumping to a TOC mark.
513 if (comphelper::LibreOfficeKit::isActive() && !rURL.startsWith("#"))
514 {
515 rVSh.GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_HYPERLINK_CLICKED, rURL.toUtf8().getStr());
516 return;
517 }
518
519 //A CursorShell is always a WrtShell
520 SwWrtShell &rSh = static_cast<SwWrtShell&>(rVSh);
521
522 SwDocShell* pDShell = rSh.GetView().GetDocShell();
523 OSL_ENSURE( pDShell, "No DocShell?!");
524 OUString sTargetFrame(rTargetFrameName);
525 if (sTargetFrame.isEmpty() && pDShell)
526 {
527 using namespace ::com::sun::star;
528 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
529 pDShell->GetModel(), uno::UNO_QUERY_THROW);
530 uno::Reference<document::XDocumentProperties> xDocProps
531 = xDPS->getDocumentProperties();
532 sTargetFrame = xDocProps->getDefaultTarget();
533 }
534
535 OUString sReferer;
536 if( pDShell && pDShell->GetMedium() )
537 sReferer = pDShell->GetMedium()->GetName();
538 SfxViewFrame* pViewFrame = rSh.GetView().GetViewFrame();
539 SfxFrameItem aView( SID_DOCFRAME, pViewFrame );
540 SfxStringItem aName( SID_FILE_NAME, rURL );
541 SfxStringItem aTargetFrameName( SID_TARGETNAME, sTargetFrame );
542 SfxStringItem aReferer( SID_REFERER, sReferer );
543
544 SfxBoolItem aNewView( SID_OPEN_NEW_VIEW, false );
545 //#39076# Silent can be removed accordingly to SFX.
546 SfxBoolItem aBrowse( SID_BROWSE, true );
547
548 if ((nFilter & LoadUrlFlags::NewView) && !comphelper::LibreOfficeKit::isActive())
549 aTargetFrameName.SetValue( "_blank" );
550
551 const SfxPoolItem* aArr[] = {
552 &aName,
553 &aNewView, /*&aSilent,*/
554 &aReferer,
555 &aView, &aTargetFrameName,
556 &aBrowse,
557 nullptr
558 };
559
560 pViewFrame->GetDispatcher()->GetBindings()->Execute( SID_OPENDOC, aArr,
561 SfxCallMode::ASYNCHRON|SfxCallMode::RECORD );
562 }
563
NavigatorPaste(const NaviContentBookmark & rBkmk,const sal_uInt16 nAction)564 void SwWrtShell::NavigatorPaste( const NaviContentBookmark& rBkmk,
565 const sal_uInt16 nAction )
566 {
567 if( EXCHG_IN_ACTION_COPY == nAction )
568 {
569 // Insert
570 OUString sURL = rBkmk.GetURL();
571 // Is this is a jump within the current Doc?
572 const SwDocShell* pDocShell = GetView().GetDocShell();
573 if(pDocShell->HasName())
574 {
575 const OUString rName = pDocShell->GetMedium()->GetURLObject().GetURLNoMark();
576
577 if (sURL.startsWith(rName))
578 {
579 if (sURL.getLength()>rName.getLength())
580 {
581 sURL = sURL.copy(rName.getLength());
582 }
583 else
584 {
585 sURL.clear();
586 }
587 }
588 }
589 SwFormatINetFormat aFormat( sURL, OUString() );
590 InsertURL( aFormat, rBkmk.GetDescription() );
591 }
592 else
593 {
594 SwSectionData aSection( SectionType::FileLink, GetUniqueSectionName() );
595 OUString aLinkFile = rBkmk.GetURL().getToken(0, '#')
596 + OUStringChar(sfx2::cTokenSeparator)
597 + OUStringChar(sfx2::cTokenSeparator)
598 + rBkmk.GetURL().getToken(1, '#');
599 aSection.SetLinkFileName( aLinkFile );
600 aSection.SetProtectFlag( true );
601 const SwSection* pIns = InsertSection( aSection );
602 if( EXCHG_IN_ACTION_MOVE == nAction && pIns )
603 {
604 aSection = SwSectionData(*pIns);
605 aSection.SetLinkFileName( OUString() );
606 aSection.SetType( SectionType::Content );
607 aSection.SetProtectFlag( false );
608
609 // the update of content from linked section at time delete
610 // the undostack. Then the change of the section don't create
611 // any undoobject. - BUG 69145
612 bool bDoesUndo = DoesUndo();
613 SwUndoId nLastUndoId(SwUndoId::EMPTY);
614 if (GetLastUndoInfo(nullptr, & nLastUndoId))
615 {
616 if (SwUndoId::INSSECTION != nLastUndoId)
617 {
618 DoUndo(false);
619 }
620 }
621 UpdateSection( GetSectionFormatPos( *pIns->GetFormat() ), aSection );
622 DoUndo( bDoesUndo );
623 }
624 }
625 }
626
627 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
628