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 <config_feature_desktop.h>
22 #include <vcl/weld.hxx>
23 #include <vcl/svapp.hxx>
24 #include <vcl/settings.hxx>
25 #include <viewopt.hxx>
26 #include <frmtool.hxx>
30 #include <swtable.hxx>
31 #include <viewimp.hxx>
33 #include <rootfrm.hxx>
37 #include <scriptinfo.hxx>
39 #include <IDocumentLayoutAccess.hxx>
41 #include <svx/sdr/overlay/overlaymanager.hxx>
42 #include <svx/sdrpaintwindow.hxx>
43 #include <svx/srchdlg.hxx>
44 #include <svx/sdr/overlay/overlayselection.hxx>
45 #include "overlayrangesoutline.hxx"
49 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
50 #include <comphelper/lok.hxx>
51 #include <sfx2/lokhelper.hxx>
52 #include <boost/property_tree/json_parser.hpp>
53 #include <comphelper/string.hxx>
54 #include <osl/diagnose.h>
55 #include <paintfrm.hxx>
56 #include <PostItMgr.hxx>
57 #include <SwGrammarMarkUp.hxx>
59 #include <svtools/optionsdrawinglayer.hxx>
60 #include <o3tl/string_view.hxx>
61 #include <tools/json_writer.hxx>
62 #include <cellfrm.hxx>
64 #include <textcontentcontrol.hxx>
65 #include <dropdowncontentcontrolbutton.hxx>
66 #include <datecontentcontrolbutton.hxx>
67 #include <FrameControlsManager.hxx>
69 // Here static members are defined. They will get changed on alteration of the
70 // MapMode. This is done so that on ShowCursor the same size does not have to be
71 // expensively determined again and again.
73 tools::Long
SwSelPaintRects::s_nPixPtX
= 0;
74 tools::Long
SwSelPaintRects::s_nPixPtY
= 0;
75 MapMode
* SwSelPaintRects::s_pMapMode
= nullptr;
77 // Starting from here: classes / methods for the non-text-cursor
78 SwVisibleCursor::SwVisibleCursor( const SwCursorShell
* pCShell
)
79 : m_pCursorShell( pCShell
)
82 pCShell
->GetWin()->SetCursor( &m_aTextCursor
);
83 m_bIsVisible
= m_aTextCursor
.IsVisible();
84 m_bIsDragCursor
= false;
85 m_aTextCursor
.SetWidth( 0 );
88 SwVisibleCursor::~SwVisibleCursor()
90 if( m_bIsVisible
&& m_aTextCursor
.IsVisible() )
93 m_pCursorShell
->GetWin()->SetCursor( nullptr );
96 void SwVisibleCursor::Show()
103 if( m_pCursorShell
->VisArea().Overlaps( m_pCursorShell
->m_aCharRect
) || comphelper::LibreOfficeKit::isActive() )
104 SetPosAndShow(nullptr);
108 void SwVisibleCursor::Hide()
112 m_bIsVisible
= false;
114 if( m_aTextCursor
.IsVisible() ) // Shouldn't the flags be in effect?
115 m_aTextCursor
.Hide();
122 // Build JSON message to be sent to Online
123 OString
buildHyperlinkJSON(const OUString
& sText
, const OUString
& sLink
)
125 boost::property_tree::ptree aTree
;
126 aTree
.put("text", sText
);
127 aTree
.put("link", sLink
);
128 std::stringstream aStream
;
129 boost::property_tree::write_json(aStream
, aTree
, false);
131 return OString(o3tl::trim(aStream
.str()));
136 void SwVisibleCursor::SetPosAndShow(SfxViewShell
const * pViewShell
)
139 tools::Long nTmpY
= m_pCursorShell
->m_aCursorHeight
.getY();
143 m_aTextCursor
.SetOrientation( 900_deg10
);
144 aRect
= SwRect( m_pCursorShell
->m_aCharRect
.Pos(),
145 Size( m_pCursorShell
->m_aCharRect
.Height(), nTmpY
) );
146 aRect
.Pos().setX(aRect
.Pos().getX() + m_pCursorShell
->m_aCursorHeight
.getX());
147 if( m_pCursorShell
->IsOverwriteCursor() )
148 aRect
.Pos().setY(aRect
.Pos().getY() + aRect
.Width());
152 m_aTextCursor
.SetOrientation();
153 aRect
= SwRect( m_pCursorShell
->m_aCharRect
.Pos(),
154 Size( m_pCursorShell
->m_aCharRect
.Width(), nTmpY
) );
155 aRect
.Pos().setY(aRect
.Pos().getY() + m_pCursorShell
->m_aCursorHeight
.getX());
158 // check if cursor should show the current cursor bidi level
159 m_aTextCursor
.SetDirection();
160 const SwCursor
* pTmpCursor
= m_pCursorShell
->GetCursor_();
162 if ( pTmpCursor
&& !m_pCursorShell
->IsOverwriteCursor() )
164 SwNode
& rNode
= pTmpCursor
->GetPoint()->GetNode();
165 if( rNode
.IsTextNode() )
167 const SwTextNode
& rTNd
= *rNode
.GetTextNode();
168 const SwFrame
* pFrame
= rTNd
.getLayoutFrame(m_pCursorShell
->GetLayout(), nullptr, nullptr);
171 const SwScriptInfo
* pSI
= static_cast<const SwTextFrame
*>(pFrame
)->GetScriptInfo();
172 // cursor level has to be shown
173 if ( pSI
&& pSI
->CountDirChg() > 1 )
175 m_aTextCursor
.SetDirection(
176 ( pTmpCursor
->GetCursorBidiLevel() % 2 ) ?
177 CursorDirection::RTL
:
178 CursorDirection::LTR
);
180 if ( pFrame
->IsRightToLeft() )
182 const OutputDevice
*pOut
= m_pCursorShell
->GetOut();
185 tools::Long nSize
= pOut
->GetSettings().GetStyleSettings().GetCursorSize();
186 Size
aSize( nSize
, nSize
);
187 aSize
= pOut
->PixelToLogic( aSize
);
188 aRect
.Left( aRect
.Left() - aSize
.Width() );
197 ::SwCalcPixStatics( m_pCursorShell
->GetOut() );
199 // Disable pixel alignment when tiled rendering, so that twip values of
200 // the cursor don't depend on statics.
201 if (!comphelper::LibreOfficeKit::isActive())
202 ::SwAlignRect( aRect
, static_cast<SwViewShell
const *>(m_pCursorShell
), m_pCursorShell
->GetOut() );
204 if( !m_pCursorShell
->IsOverwriteCursor() || m_bIsDragCursor
||
205 m_pCursorShell
->IsSelection() )
208 m_aTextCursor
.SetSize( aRect
.SSize() );
210 m_aTextCursor
.SetPos( aRect
.Pos() );
212 bool bPostItActive
= false;
213 SwView
* pView
= dynamic_cast<SwView
*>(m_pCursorShell
->GetSfxViewShell());
216 if (SwPostItMgr
* pPostItMgr
= pView
->GetPostItMgr())
217 bPostItActive
= pPostItMgr
->GetActiveSidebarWin() != nullptr;
220 if (comphelper::LibreOfficeKit::isActive() && !bPostItActive
)
222 // notify about page number change (if that happened)
223 sal_uInt16 nPage
, nVirtPage
;
224 // bCalcFrame=false is important to avoid calculating the layout when
225 // we're in the middle of doing that already.
226 const_cast<SwCursorShell
*>(m_pCursorShell
)->GetPageNum(nPage
, nVirtPage
, /*bAtCursorPos=*/true, /*bCalcFrame=*/false);
227 if (nPage
!= m_nPageLastTime
)
229 m_nPageLastTime
= nPage
;
230 OString aPayload
= OString::number(nPage
- 1);
231 m_pCursorShell
->GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_SET_PART
, aPayload
);
234 // This may get called often, so instead of sending data on each update, just notify
235 // that there's been an update, and the other side will pull the data using
236 // getLOKPayload() when it decides to.
237 m_aLastLOKRect
= aRect
;
240 if (pViewShell
== m_pCursorShell
->GetSfxViewShell())
242 SfxLokHelper::notifyUpdatePerViewId(pViewShell
, LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR
);
246 SfxLokHelper::notifyUpdatePerViewId(pViewShell
, m_pCursorShell
->GetSfxViewShell(), pViewShell
,
247 LOK_CALLBACK_INVALIDATE_VIEW_CURSOR
);
252 SfxLokHelper::notifyUpdatePerViewId(m_pCursorShell
->GetSfxViewShell(), SfxViewShell::Current(),
253 m_pCursorShell
->GetSfxViewShell(), LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR
);
254 SfxLokHelper::notifyOtherViewsUpdatePerViewId(m_pCursorShell
->GetSfxViewShell(), LOK_CALLBACK_INVALIDATE_VIEW_CURSOR
);
258 if ( m_pCursorShell
->IsCursorReadonly() && !m_pCursorShell
->GetViewOptions()->IsSelectionInReadonly() )
261 if ( m_pCursorShell
->GetDrawView() )
262 const_cast<SwDrawView
*>(static_cast<const SwDrawView
*>(m_pCursorShell
->GetDrawView()))->SetAnimationEnabled(
263 !m_pCursorShell
->IsSelection() );
265 sal_uInt16 nStyle
= m_bIsDragCursor
? CURSOR_SHADOW
: 0;
266 if( nStyle
!= m_aTextCursor
.GetStyle() )
268 m_aTextCursor
.SetStyle( nStyle
);
269 m_aTextCursor
.SetWindow( m_bIsDragCursor
? m_pCursorShell
->GetWin() : nullptr );
272 m_aTextCursor
.Show();
275 std::optional
<OString
> SwVisibleCursor::getLOKPayload(int nType
, int nViewId
) const
277 assert(nType
== LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR
|| nType
== LOK_CALLBACK_INVALIDATE_VIEW_CURSOR
);
278 if (comphelper::LibreOfficeKit::isActive())
280 SwRect aRect
= m_aLastLOKRect
;
282 // notify about the cursor position & size
283 tools::Rectangle
aSVRect(aRect
.Pos().getX(), aRect
.Pos().getY(), aRect
.Pos().getX() + aRect
.SSize().Width(), aRect
.Pos().getY() + aRect
.SSize().Height());
284 OString sRect
= aSVRect
.toString();
286 if(nType
== LOK_CALLBACK_INVALIDATE_VIEW_CURSOR
)
287 return SfxLokHelper::makePayloadJSON(m_pCursorShell
->GetSfxViewShell(), nViewId
, "rectangle", sRect
);
289 // is cursor at a misspelled word ?
290 bool bIsWrong
= false;
291 SwView
* pView
= dynamic_cast<SwView
*>(m_pCursorShell
->GetSfxViewShell());
292 if (pView
&& pView
->GetWrtShellPtr())
294 const SwViewOption
* pVOpt
= pView
->GetWrtShell().GetViewOptions();
295 if(pVOpt
&& pVOpt
->IsOnlineSpell())
297 SwPaM
* pCursor
= m_pCursorShell
->GetCursor();
298 SwPosition
aPos(*pCursor
->GetPoint());
299 Point aPt
= aRect
.Pos();
300 SwCursorMoveState
eTmpState(CursorMoveState::SetOnlyText
);
301 SwTextNode
*pNode
= nullptr;
302 if (m_pCursorShell
->GetLayout()->GetModelPositionForViewPoint(&aPos
, aPt
, &eTmpState
))
303 pNode
= aPos
.GetNode().GetTextNode();
304 if (pNode
&& !pNode
->IsInProtectSect())
306 sal_Int32 nBegin
= aPos
.GetContentIndex();
309 SwWrongList
*pWrong
= pNode
->GetWrong();
311 pWrong
= pNode
->GetGrammarCheck();
313 bIsWrong
= pWrong
->InWrongWord(nBegin
,nLen
) && !pNode
->IsSymbolAt(nBegin
);
319 SwContentAtPos
aContentAtPos(IsAttrAtPos::InetAttr
);
320 bool bIsSelection
= m_pCursorShell
->IsSelection();
322 if (const_cast<SwCursorShell
*>(m_pCursorShell
)->GetContentAtPos(aRect
.Pos(), aContentAtPos
))
324 const SwFormatINetFormat
* pItem
= static_cast<const SwFormatINetFormat
*>(aContentAtPos
.aFnd
.pAttr
);
325 sHyperlink
= buildHyperlinkJSON(aContentAtPos
.sStr
, pItem
->GetValue());
327 else if (bIsSelection
)
329 SwWrtShell
* pShell
= m_pCursorShell
->GetDoc()->GetDocShell()->GetWrtShell();
333 SfxItemSetFixed
<RES_TXTATR_INETFMT
, RES_TXTATR_INETFMT
>
334 aSet(m_pCursorShell
->GetSfxViewShell()->GetPool());
335 pShell
->GetCurAttr(aSet
);
336 if(SfxItemState::SET
<= aSet
.GetItemState( RES_TXTATR_INETFMT
))
338 sHyperlink
= buildHyperlinkJSON(m_pCursorShell
->GetSelText(),
339 aSet
.GetItem(RES_TXTATR_INETFMT
)->GetValue());
344 return SfxLokHelper::makeVisCursorInvalidation(nViewId
, sRect
, bIsWrong
, sHyperlink
);
350 const vcl::Cursor
& SwVisibleCursor::GetTextCursor() const
352 return m_aTextCursor
;
355 SwSelPaintRects::SwSelPaintRects( const SwCursorShell
& rCSh
)
356 : m_pCursorShell( &rCSh
)
357 #if HAVE_FEATURE_DESKTOP
358 , m_bShowTextInputFieldOverlay(true)
359 , m_bShowContentControlOverlay(true)
364 SwSelPaintRects::~SwSelPaintRects()
367 m_pContentControlButton
.disposeAndClear();
370 void SwSelPaintRects::swapContent(SwSelPaintRects
& rSwap
)
372 SwRects::swap(rSwap
);
374 #if HAVE_FEATURE_DESKTOP
375 // #i75172# also swap m_pCursorOverlay
376 std::swap(m_pCursorOverlay
, rSwap
.m_pCursorOverlay
);
377 std::swap(m_bShowTextInputFieldOverlay
, rSwap
.m_bShowTextInputFieldOverlay
);
378 std::swap(m_pTextInputFieldOverlay
, rSwap
.m_pTextInputFieldOverlay
);
379 std::swap(m_bShowContentControlOverlay
, rSwap
.m_bShowContentControlOverlay
);
380 std::swap(m_pContentControlOverlay
, rSwap
.m_pContentControlOverlay
);
384 void SwSelPaintRects::Hide()
386 #if HAVE_FEATURE_DESKTOP
387 m_pCursorOverlay
.reset();
388 m_pTextInputFieldOverlay
.reset();
389 m_pContentControlOverlay
.reset();
396 * Return a layout rectangle (typically with minimal width) that represents a
397 * cursor at rPosition.
399 * @param rPoint layout position as a hint about what layout frame contains
400 * rPosition (there might be multiple frames for a single node)
401 * @param rPosition the doc model position (paragraph / character index)
403 static SwRect
lcl_getLayoutRect(const Point
& rPoint
, const SwPosition
& rPosition
)
405 const SwContentNode
* pNode
= rPosition
.GetNode().GetContentNode();
406 std::pair
<Point
, bool> const tmp(rPoint
, true);
407 const SwContentFrame
* pFrame
= pNode
->getLayoutFrame(
408 pNode
->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
411 pFrame
->GetCharRect(aRect
, rPosition
);
415 void SwShellCursor::FillStartEnd(SwRect
& rStart
, SwRect
& rEnd
) const
417 const SwShellCursor
* pCursor
= GetShell()->getShellCursor(false);
418 rStart
= lcl_getLayoutRect(pCursor
->GetSttPos(), *pCursor
->Start());
419 rEnd
= lcl_getLayoutRect(pCursor
->GetEndPos(), *pCursor
->End());
422 void SwSelPaintRects::Show(std::vector
<OString
>* pSelectionRectangles
)
424 SdrView
*const pView
= const_cast<SdrView
*>(m_pCursorShell
->GetDrawView());
426 if(!(pView
&& pView
->PaintWindowCount()))
433 #if HAVE_FEATURE_DESKTOP
435 std::vector
< basegfx::B2DRange
> aNewRanges
;
436 aNewRanges
.reserve(size());
437 for(size_type a
= 0; a
< size(); ++a
)
439 const SwRect
aNextRect((*this)[a
]);
440 const tools::Rectangle
aPntRect(aNextRect
.SVRect());
442 aNewRanges
.emplace_back(
443 aPntRect
.Left(), aPntRect
.Top(),
444 aPntRect
.Right() + 1, aPntRect
.Bottom() + 1);
447 if (m_pCursorOverlay
)
449 if(!aNewRanges
.empty())
451 static_cast<sdr::overlay::OverlaySelection
*>(m_pCursorOverlay
.get())->setRanges(std::move(aNewRanges
));
455 m_pCursorOverlay
.reset();
460 SdrPaintWindow
* pCandidate
= pView
->GetPaintWindow(0);
461 const rtl::Reference
< sdr::overlay::OverlayManager
>& xTargetOverlay
= pCandidate
->GetOverlayManager();
463 if (xTargetOverlay
.is())
465 // get the system's highlight color
466 const Color
aHighlight(SvtOptionsDrawinglayer::getHilightColor());
468 // create correct selection
469 m_pCursorOverlay
.reset( new sdr::overlay::OverlaySelection(
470 sdr::overlay::OverlayType::Transparent
,
472 std::move(aNewRanges
),
475 xTargetOverlay
->add(*m_pCursorOverlay
);
479 HighlightInputField();
480 HighlightContentControl();
483 // Tiled editing does not expose the draw and writer cursor, it just
484 // talks about "the" cursor at the moment. As long as that's true,
485 // don't say anything about the Writer cursor till a draw object is
487 if (!comphelper::LibreOfficeKit::isActive() || pView
->GetTextEditObject())
490 // If pSelectionRectangles is set, we're just collecting the text selections -> don't emit start/end.
491 if (!empty() && !pSelectionRectangles
)
495 FillStartEnd(aStartRect
, aEndRect
);
497 if (aStartRect
.HasArea())
498 SfxLokHelper::notifyUpdate(GetShell()->GetSfxViewShell(), LOK_CALLBACK_TEXT_SELECTION_START
);
499 if (aEndRect
.HasArea())
500 SfxLokHelper::notifyUpdate(GetShell()->GetSfxViewShell(), LOK_CALLBACK_TEXT_SELECTION_END
);
503 std::vector
<OString
> aRect
;
504 aRect
.reserve(size());
505 for (size_type i
= 0; i
< size(); ++i
)
507 const SwRect
& rRect
= (*this)[i
];
508 aRect
.push_back(rRect
.SVRect().toString());
510 OString sRect
= comphelper::string::join("; ", aRect
);
511 if (!pSelectionRectangles
)
513 SfxLokHelper::notifyUpdate(GetShell()->GetSfxViewShell(),LOK_CALLBACK_TEXT_SELECTION
);
514 SfxLokHelper::notifyOtherViewsUpdatePerViewId(GetShell()->GetSfxViewShell(), LOK_CALLBACK_TEXT_VIEW_SELECTION
);
517 pSelectionRectangles
->push_back(sRect
);
520 std::optional
<OString
> SwSelPaintRects::getLOKPayload(int nType
, int nViewId
) const
524 case LOK_CALLBACK_TEXT_SELECTION_START
:
525 case LOK_CALLBACK_TEXT_SELECTION_END
:
527 // The selection may be a complex polygon, emit the logical
528 // start/end cursor rectangle of the selection as separate
529 // events, if there is a real selection.
530 // This can be used to easily show selection handles on the
534 FillStartEnd(aStartRect
, aEndRect
);
540 if( nType
== LOK_CALLBACK_TEXT_SELECTION_START
)
542 if (aStartRect
.HasArea())
543 return aStartRect
.SVRect().toString();
546 else // LOK_CALLBACK_TEXT_SELECTION_END
548 if (aEndRect
.HasArea())
549 return aEndRect
.SVRect().toString();
554 case LOK_CALLBACK_TEXT_SELECTION
:
555 case LOK_CALLBACK_TEXT_VIEW_SELECTION
:
557 std::vector
<OString
> aRect
;
558 aRect
.reserve(size());
559 for (size_type i
= 0; i
< size(); ++i
)
561 const SwRect
& rRect
= (*this)[i
];
562 aRect
.push_back(rRect
.SVRect().toString());
564 OString sRect
= comphelper::string::join("; ", aRect
);
565 if( nType
== LOK_CALLBACK_TEXT_SELECTION
)
567 else // LOK_CALLBACK_TEXT_VIEW_SELECTION
568 return SfxLokHelper::makePayloadJSON(GetShell()->GetSfxViewShell(), nViewId
, "selection", sRect
);
575 void SwSelPaintRects::HighlightInputField()
577 std::vector
< basegfx::B2DRange
> aInputFieldRanges
;
579 if (m_bShowTextInputFieldOverlay
)
581 SwTextInputField
* pCurTextInputFieldAtCursor
=
582 dynamic_cast<SwTextInputField
*>(SwCursorShell::GetTextFieldAtPos( GetShell()->GetCursor()->Start(), ::sw::GetTextAttrMode::Expand
));
583 if ( pCurTextInputFieldAtCursor
!= nullptr )
585 SwTextNode
* pTextNode
= pCurTextInputFieldAtCursor
->GetpTextNode();
586 std::unique_ptr
<SwShellCursor
> pCursorForInputTextField(
587 new SwShellCursor( *GetShell(), SwPosition( *pTextNode
, pCurTextInputFieldAtCursor
->GetStart() ) ) );
588 pCursorForInputTextField
->SetMark();
589 pCursorForInputTextField
->GetMark()->Assign(*pTextNode
, *(pCurTextInputFieldAtCursor
->End()) );
591 pCursorForInputTextField
->FillRects();
592 SwRects
* pRects
= pCursorForInputTextField
.get();
593 for (const SwRect
& rNextRect
: *pRects
)
595 const tools::Rectangle
aPntRect(rNextRect
.SVRect());
597 aInputFieldRanges
.emplace_back(
598 aPntRect
.Left(), aPntRect
.Top(),
599 aPntRect
.Right() + 1, aPntRect
.Bottom() + 1);
604 if ( !aInputFieldRanges
.empty() )
606 if (m_pTextInputFieldOverlay
!= nullptr)
608 m_pTextInputFieldOverlay
->setRanges( std::move(aInputFieldRanges
) );
612 SdrView
* pView
= const_cast<SdrView
*>(GetShell()->GetDrawView());
613 SdrPaintWindow
* pCandidate
= pView
->GetPaintWindow(0);
614 const rtl::Reference
<sdr::overlay::OverlayManager
>& xTargetOverlay
= pCandidate
->GetOverlayManager();
616 if (xTargetOverlay
.is())
618 // use system's highlight color with decreased luminance as highlight color
619 Color
aHighlight(SvtOptionsDrawinglayer::getHilightColor());
620 aHighlight
.DecreaseLuminance( 128 );
622 m_pTextInputFieldOverlay
.reset( new sw::overlay::OverlayRangesOutline(
623 aHighlight
, std::move(aInputFieldRanges
) ) );
624 xTargetOverlay
->add( *m_pTextInputFieldOverlay
);
630 m_pTextInputFieldOverlay
.reset();
634 void SwSelPaintRects::HighlightContentControl()
636 std::vector
<basegfx::B2DRange
> aContentControlRanges
;
637 std::vector
<OString
> aLOKRectangles
;
638 SwRect aFirstPortionPaintArea
;
639 SwRect aLastPortionPaintArea
;
641 std::shared_ptr
<SwContentControl
> pContentControl
;
643 if (m_bShowContentControlOverlay
)
645 const SwPosition
* pStart
= GetShell()->GetCursor()->Start();
646 SwTextNode
* pTextNode
= pStart
->GetNode().GetTextNode();
647 SwTextContentControl
* pCurContentControlAtCursor
= nullptr;
650 // GetTextAttrMode::Parent because this way we highlight when the user will type inside the
651 // content control, not outside of it.
652 SwTextAttr
* pAttr
= pTextNode
->GetTextAttrAt(
653 pStart
->GetContentIndex(), RES_TXTATR_CONTENTCONTROL
, ::sw::GetTextAttrMode::Parent
);
656 pCurContentControlAtCursor
= static_txtattr_cast
<SwTextContentControl
*>(pAttr
);
659 if (pCurContentControlAtCursor
)
661 auto pCursorForContentControl
= std::make_unique
<SwShellCursor
>(
662 *GetShell(), SwPosition(*pTextNode
, pCurContentControlAtCursor
->GetStart()));
663 pCursorForContentControl
->SetMark();
664 pCursorForContentControl
->GetMark()->Assign(
665 *pTextNode
, *(pCurContentControlAtCursor
->End()));
667 pCursorForContentControl
->FillRects();
668 SwRects
* pRects
= pCursorForContentControl
.get();
669 for (const auto& rRect
: *pRects
)
671 tools::Rectangle
aRect(rRect
.SVRect());
673 aContentControlRanges
.emplace_back(aRect
.Left(), aRect
.Top(), aRect
.Right() + 1,
675 if (comphelper::LibreOfficeKit::isActive())
677 aLOKRectangles
.push_back(aRect
.toString());
681 if (!pRects
->empty())
683 aFirstPortionPaintArea
= (*pRects
)[0];
684 aLastPortionPaintArea
= (*pRects
)[pRects
->size() - 1];
686 pContentControl
= pCurContentControlAtCursor
->GetContentControl().GetContentControl();
688 // The layout knows if the text node is RTL (either set directly, or inherited from the
690 SwIterator
<SwTextFrame
, SwTextNode
, sw::IteratorMode::UnwrapMulti
> aFrames(*pTextNode
);
691 SwTextFrame
* pFrame
= aFrames
.First();
694 bRTL
= pFrame
->IsRightToLeft();
699 auto pWrtShell
= dynamic_cast<const SwWrtShell
*>(GetShell());
700 if (!aContentControlRanges
.empty())
702 if (comphelper::LibreOfficeKit::isActive())
704 OString aPayload
= comphelper::string::join("; ", aLOKRectangles
);
705 tools::JsonWriter aJson
;
706 aJson
.put("action", "show");
707 aJson
.put("rectangles", aPayload
);
709 if (pContentControl
&& (pContentControl
->GetComboBox() || pContentControl
->GetDropDown()))
711 tools::ScopedJsonWriterArray aItems
= aJson
.startArray("items");
712 for (const auto& rItem
: pContentControl
->GetListItems())
714 aJson
.putSimpleValue(rItem
.ToString());
718 if (pContentControl
&& pContentControl
->GetDate())
720 aJson
.put("date", "true");
723 if (pContentControl
&& !pContentControl
->GetAlias().isEmpty())
725 aJson
.put("alias", pContentControl
->GetAlias());
728 OString
pJson(aJson
.finishAndGetAsOString());
729 GetShell()->GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_CONTENT_CONTROL
, pJson
);
731 if (m_pContentControlOverlay
)
733 m_pContentControlOverlay
->setRanges(std::move(aContentControlRanges
));
737 SdrView
* pView
= const_cast<SdrView
*>(GetShell()->GetDrawView());
738 SdrPaintWindow
* pCandidate
= pView
->GetPaintWindow(0);
739 const rtl::Reference
<sdr::overlay::OverlayManager
>& xTargetOverlay
740 = pCandidate
->GetOverlayManager();
742 if (xTargetOverlay
.is())
744 // Use the system's highlight color with decreased luminance as highlight color.
745 Color
aHighlight(SvtOptionsDrawinglayer::getHilightColor());
746 aHighlight
.DecreaseLuminance(128);
748 m_pContentControlOverlay
.reset(new sw::overlay::OverlayRangesOutline(
749 aHighlight
, std::move(aContentControlRanges
)));
750 xTargetOverlay
->add(*m_pContentControlOverlay
);
754 if (pContentControl
&& (pContentControl
->GetComboBox() || pContentControl
->GetDropDown()))
758 auto& rEditWin
= const_cast<SwEditWin
&>(pWrtShell
->GetView().GetEditWin());
759 if (m_pContentControlButton
760 && m_pContentControlButton
->GetContentControl() != pContentControl
)
762 m_pContentControlButton
.disposeAndClear();
764 if (!m_pContentControlButton
)
766 m_pContentControlButton
= VclPtr
<SwDropDownContentControlButton
>::Create(
767 &rEditWin
, pContentControl
);
769 m_pContentControlButton
->SetRTL(bRTL
);
772 m_pContentControlButton
->CalcPosAndSize(aFirstPortionPaintArea
);
776 m_pContentControlButton
->CalcPosAndSize(aLastPortionPaintArea
);
778 m_pContentControlButton
->Show();
781 if (pContentControl
&& pContentControl
->GetDate())
785 auto& rEditWin
= const_cast<SwEditWin
&>(pWrtShell
->GetView().GetEditWin());
786 if (m_pContentControlButton
787 && m_pContentControlButton
->GetContentControl() != pContentControl
)
789 m_pContentControlButton
.disposeAndClear();
791 if (!m_pContentControlButton
)
793 m_pContentControlButton
= VclPtr
<SwDateContentControlButton
>::Create(
794 &rEditWin
, pContentControl
, pWrtShell
->GetDoc()->GetNumberFormatter());
796 m_pContentControlButton
->CalcPosAndSize(aLastPortionPaintArea
);
797 m_pContentControlButton
->Show();
803 auto& rEditWin
= const_cast<SwEditWin
&>(pWrtShell
->GetView().GetEditWin());
804 SwFrameControlsManager
& rMngr
= rEditWin
.GetFrameControlsManager();
805 if (pContentControl
&& !pContentControl
->GetAlias().isEmpty())
807 Point aTopLeftPixel
= rEditWin
.LogicToPixel(aFirstPortionPaintArea
.TopLeft());
808 rMngr
.SetContentControlAliasButton(pContentControl
.get(), aTopLeftPixel
);
812 rMngr
.HideControls(FrameControlType::ContentControl
);
818 if (comphelper::LibreOfficeKit::isActive() && m_pContentControlOverlay
)
820 tools::JsonWriter aJson
;
821 aJson
.put("action", "hide");
822 OString
pJson(aJson
.finishAndGetAsOString());
823 GetShell()->GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_CONTENT_CONTROL
, pJson
);
825 m_pContentControlOverlay
.reset();
827 if (m_pContentControlButton
)
829 m_pContentControlButton
.disposeAndClear();
834 auto& rEditWin
= const_cast<SwEditWin
&>(pWrtShell
->GetView().GetEditWin());
835 SwFrameControlsManager
& rMngr
= rEditWin
.GetFrameControlsManager();
836 rMngr
.HideControls(FrameControlType::ContentControl
);
841 const VclPtr
<SwContentControlButton
>& SwSelPaintRects::GetContentControlButton() const
843 return m_pContentControlButton
;
846 void SwSelPaintRects::Invalidate( const SwRect
& rRect
)
848 size_type nSz
= size();
852 SwRegionRects
aReg( GetShell()->VisArea() );
853 aReg
.assign( begin(), end() );
855 SwRects::erase( begin(), begin() + nSz
);
856 SwRects::insert( begin(), aReg
.begin(), aReg
.end() );
858 // If the selection is to the right or at the bottom, outside the
859 // visible area, it is never aligned on one pixel at the right/bottom.
860 // This has to be determined here and if that is the case the
861 // rectangle has to be expanded.
862 if( !(GetShell()->m_bVisPortChgd
&& 0 != ( nSz
= size())) )
865 SwSelPaintRects::Get1PixelInLogic( *GetShell() );
866 iterator it
= begin();
869 SwRect
& rRectIt
= *it
;
870 if( rRectIt
.Right() == GetShell()->m_aOldRBPos
.X() )
871 rRectIt
.AddRight( s_nPixPtX
);
872 if( rRectIt
.Bottom() == GetShell()->m_aOldRBPos
.Y() )
873 rRectIt
.AddBottom( s_nPixPtY
);
877 // check current MapMode of the shell and set possibly the static members.
878 // Optional set the parameters pX, pY
879 void SwSelPaintRects::Get1PixelInLogic( const SwViewShell
& rSh
,
880 tools::Long
* pX
, tools::Long
* pY
)
882 const OutputDevice
* pOut
= rSh
.GetWin()->GetOutDev();
886 const MapMode
& rMM
= pOut
->GetMapMode();
887 if (s_pMapMode
->GetMapUnit() != rMM
.GetMapUnit() ||
888 s_pMapMode
->GetScaleX() != rMM
.GetScaleX() ||
889 s_pMapMode
->GetScaleY() != rMM
.GetScaleY())
893 aTmp
= pOut
->PixelToLogic( aTmp
);
894 s_nPixPtX
= aTmp
.Width();
895 s_nPixPtY
= aTmp
.Height();
903 SwShellCursor::SwShellCursor(
904 const SwCursorShell
& rCShell
,
905 const SwPosition
&rPos
)
906 : SwCursor(rPos
,nullptr)
907 , SwSelPaintRects(rCShell
)
908 , m_pInitialPoint(SwPaM::GetPoint())
911 SwShellCursor::SwShellCursor(
912 const SwCursorShell
& rCShell
,
913 const SwPosition
&rPos
,
916 : SwCursor(rPos
, pRing
)
917 , SwSelPaintRects(rCShell
)
920 , m_pInitialPoint(SwPaM::GetPoint())
923 SwShellCursor::SwShellCursor( SwShellCursor
& rICursor
)
924 : SwCursor(rICursor
, &rICursor
)
925 , SwSelPaintRects(*rICursor
.GetShell())
926 , m_MarkPt(rICursor
.GetMkPos())
927 , m_PointPt(rICursor
.GetPtPos())
928 , m_pInitialPoint(SwPaM::GetPoint())
931 SwShellCursor::~SwShellCursor()
934 bool SwShellCursor::IsReadOnlyAvailable() const
936 return GetShell()->IsReadOnlyAvailable();
939 void SwShellCursor::SetMark()
941 if (SwPaM::GetPoint() == m_pInitialPoint
)
942 m_MarkPt
= m_PointPt
;
944 m_PointPt
= m_MarkPt
;
948 void SwShellCursor::FillRects()
950 // calculate the new rectangles
952 GetPoint()->GetNode().IsContentNode() &&
953 GetPoint()->GetNode().GetContentNode()->getLayoutFrame( GetShell()->GetLayout() ) &&
954 (GetMark()->GetNode() == GetPoint()->GetNode() ||
955 (GetMark()->GetNode().IsContentNode() &&
956 GetMark()->GetNode().GetContentNode()->getLayoutFrame( GetShell()->GetLayout() ) ) ))
957 GetShell()->GetLayout()->CalcFrameRects( *this );
960 void SwShellCursor::Show(SfxViewShell
const * pViewShell
)
962 std::vector
<OString
> aSelectionRectangles
;
963 for(SwPaM
& rPaM
: GetRingContainer())
965 SwShellCursor
* pShCursor
= dynamic_cast<SwShellCursor
*>(&rPaM
);
967 pShCursor
->SwSelPaintRects::Show(&aSelectionRectangles
);
970 if (!comphelper::LibreOfficeKit::isActive())
973 std::vector
<OString
> aRect
;
974 for (const OString
& rSelectionRectangle
: aSelectionRectangles
)
976 if (rSelectionRectangle
.isEmpty())
978 aRect
.push_back(rSelectionRectangle
);
980 OString sRect
= comphelper::string::join("; ", aRect
);
983 // Just notify pViewShell about our existing selection.
984 if (pViewShell
!= GetShell()->GetSfxViewShell())
985 SfxLokHelper::notifyOtherView(GetShell()->GetSfxViewShell(), pViewShell
, LOK_CALLBACK_TEXT_VIEW_SELECTION
, "selection", sRect
);
989 GetShell()->GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION
, sRect
);
990 SfxLokHelper::notifyOtherViews(GetShell()->GetSfxViewShell(), LOK_CALLBACK_TEXT_VIEW_SELECTION
, "selection", sRect
);
994 // This rectangle gets painted anew, therefore the SSelection in this
996 void SwShellCursor::Invalidate( const SwRect
& rRect
)
998 for(SwPaM
& rPaM
: GetRingContainer())
1000 SwShellCursor
* pShCursor
= dynamic_cast<SwShellCursor
*>(&rPaM
);
1001 // skip any non SwShellCursor objects in the ring
1002 // see also: SwAutoFormat::DeleteSel()
1004 pShCursor
->SwSelPaintRects::Invalidate(rRect
);
1008 void SwShellCursor::Hide()
1010 for(SwPaM
& rPaM
: GetRingContainer())
1012 SwShellCursor
* pShCursor
= dynamic_cast<SwShellCursor
*>(&rPaM
);
1014 pShCursor
->SwSelPaintRects::Hide();
1018 SwCursor
* SwShellCursor::Create( SwPaM
* pRing
) const
1020 return new SwShellCursor( *GetShell(), *GetPoint(), GetPtPos(), pRing
);
1023 short SwShellCursor::MaxReplaceArived()
1025 short nRet
= RET_YES
;
1026 SvxSearchDialog
* pDlg
= SwView::GetSearchDialog();
1029 // Terminate old actions. The table-frames get constructed and
1030 // a SSelection can be created.
1031 std::vector
<sal_uInt16
> vActionCounts
;
1032 for(SwViewShell
& rShell
: const_cast< SwCursorShell
* >( GetShell() )->GetRingContainer())
1034 sal_uInt16 nActCnt
= 0;
1035 while(rShell
.ActionPend())
1040 vActionCounts
.push_back(nActCnt
);
1042 std::unique_ptr
<weld::Builder
> xBuilder(Application::CreateBuilder(pDlg
->getDialog(), "modules/swriter/ui/asksearchdialog.ui"));
1043 std::unique_ptr
<weld::MessageDialog
> xDialog(xBuilder
->weld_message_dialog("AskSearchDialog"));
1044 nRet
= xDialog
->run();
1045 auto pActionCount
= vActionCounts
.begin();
1046 for(SwViewShell
& rShell
: const_cast< SwCursorShell
* >( GetShell() )->GetRingContainer())
1048 while(*pActionCount
)
1050 rShell
.StartAction();
1057 // otherwise from the Basic, and then switch to RET_YES
1063 void SwShellCursor::SaveTableBoxContent( const SwPosition
* pPos
)
1065 const_cast<SwCursorShell
*>(GetShell())->SaveTableBoxContent( pPos
);
1068 bool SwShellCursor::UpDown( bool bUp
, sal_uInt16 nCnt
)
1070 // tdf#124603 trigger pending spell checking of the node
1073 SwTextNode
* pNode
= GetPoint()->GetNode().GetTextNode();
1074 if( pNode
&& sw::WrongState::PENDING
== pNode
->GetWrongDirty() )
1076 SwWrtShell
* pShell
= pNode
->GetDoc().GetDocShell()->GetWrtShell();
1077 if ( pShell
&& !pShell
->IsSelection() && !pShell
->IsSelFrameMode() )
1079 const SwViewOption
* pVOpt
= pShell
->GetViewOptions();
1080 if ( pVOpt
&& pVOpt
->IsOnlineSpell() )
1082 const bool bOldViewLock
= pShell
->IsViewLocked();
1083 pShell
->LockView( true );
1085 SwTextFrame
* pFrame(
1086 static_cast<SwTextFrame
*>(pNode
->getLayoutFrame(GetShell()->GetLayout())));
1087 SwRect
aRepaint(pFrame
->AutoSpell_(*pNode
, 0));
1088 if (aRepaint
.HasArea())
1089 pShell
->InvalidateWindows(aRepaint
);
1091 pShell
->LockView( bOldViewLock
);
1097 return SwCursor::UpDown( bUp
, nCnt
,
1098 &GetPtPos(), GetShell()->GetUpDownX(),
1099 *GetShell()->GetLayout());
1102 // if <true> than the cursor can be set to the position.
1103 bool SwShellCursor::IsAtValidPos( bool bPoint
) const
1105 if( GetShell() && ( GetShell()->IsAllProtect() ||
1106 GetShell()->GetViewOptions()->IsReadonly() ||
1107 ( GetShell()->Imp()->GetDrawView() &&
1108 GetShell()->Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount() )))
1111 return SwCursor::IsAtValidPos( bPoint
);
1114 SwShellTableCursor::SwShellTableCursor( const SwCursorShell
& rCursorSh
,
1115 const SwPosition
& rPos
)
1116 : SwCursor(rPos
,nullptr), SwShellCursor(rCursorSh
, rPos
), SwTableCursor(rPos
)
1120 SwShellTableCursor::SwShellTableCursor( const SwCursorShell
& rCursorSh
,
1121 const SwPosition
& rMkPos
, const Point
& rMkPt
,
1122 const SwPosition
& rPtPos
, const Point
& rPtPt
)
1123 : SwCursor(rPtPos
,nullptr), SwShellCursor(rCursorSh
, rPtPos
), SwTableCursor(rPtPos
)
1126 *GetMark() = rMkPos
;
1131 SwShellTableCursor::~SwShellTableCursor() {}
1133 void SwShellTableCursor::SetMark() { SwShellCursor::SetMark(); }
1135 SwCursor
* SwShellTableCursor::Create( SwPaM
* pRing
) const
1137 return SwShellCursor::Create( pRing
);
1140 short SwShellTableCursor::MaxReplaceArived()
1142 return SwShellCursor::MaxReplaceArived();
1145 void SwShellTableCursor::SaveTableBoxContent( const SwPosition
* pPos
)
1147 SwShellCursor::SaveTableBoxContent( pPos
);
1150 void SwShellTableCursor::FillRects()
1152 // Calculate the new rectangles. If the cursor is still "parked" do nothing
1153 if (m_SelectedBoxes
.empty() || m_bParked
|| !GetPoint()->GetNodeIndex())
1157 SwRegionRects
aReg( comphelper::LibreOfficeKit::isActive()
1158 ? GetShell()->getIDocumentLayoutAccess().GetCurrentLayout()->getFrameArea()
1159 : GetShell()->VisArea() );
1160 SwNodes
& rNds
= GetDoc().GetNodes();
1161 SwFrame
* pEndFrame
= nullptr;
1162 for (size_t n
= 0; n
< m_SelectedBoxes
.size(); ++n
)
1164 const SwStartNode
* pSttNd
= m_SelectedBoxes
[n
]->GetSttNd();
1165 const SwTableNode
* pSelTableNd
= pSttNd
->FindTableNode();
1167 SwNodeIndex
aIdx( *pSttNd
);
1168 SwContentNode
* pCNd
= rNds
.GoNextSection( &aIdx
, true, false );
1171 // (see also lcl_FindTopLevelTable in unoobj2.cxx for a different
1172 // version to do this)
1173 const SwTableNode
* pCurTableNd
= pCNd
? pCNd
->FindTableNode() : nullptr;
1174 while ( pSelTableNd
!= pCurTableNd
&& pCurTableNd
)
1176 aIdx
= pCurTableNd
->EndOfSectionIndex();
1177 pCNd
= rNds
.GoNextSection( &aIdx
, true, false );
1178 pCurTableNd
= pCNd
->FindTableNode();
1184 std::pair
<Point
, bool> const tmp(GetSttPos(), false);
1185 SwFrame
* pFrame
= pCNd
->getLayoutFrame(GetShell()->GetLayout(), nullptr, &tmp
);
1186 while( pFrame
&& !pFrame
->IsCellFrame() )
1187 pFrame
= pFrame
->GetUpper();
1189 OSL_ENSURE( pFrame
, "Node not in a table" );
1193 if( aReg
.GetOrigin().Overlaps( pFrame
->getFrameArea() ) )
1195 aReg
-= pFrame
->getFrameArea();
1199 m_aStart
= SwRect(pFrame
->getFrameArea().Left(), pFrame
->getFrameArea().Top(), 1, pFrame
->getFrameArea().Height());
1204 pFrame
= pFrame
->GetNextCellLeaf();
1208 m_aEnd
= SwRect(pEndFrame
->getFrameArea().Right(), pEndFrame
->getFrameArea().Top(), 1, pEndFrame
->getFrameArea().Height());
1210 insert( begin(), aReg
.begin(), aReg
.end() );
1213 void SwShellTableCursor::FillStartEnd(SwRect
& rStart
, SwRect
& rEnd
) const
1219 // Check if the SPoint is within the Table-SSelection.
1220 bool SwShellTableCursor::Contains( const Point
& rPt
) const
1222 // Calculate the new rectangles. If the cursor is still "parked" do nothing
1223 if (m_SelectedBoxes
.empty() || m_bParked
|| !GetPoint()->GetNodeIndex())
1226 SwNodes
& rNds
= GetDoc().GetNodes();
1227 for (size_t n
= 0; n
< m_SelectedBoxes
.size(); ++n
)
1229 SwNodeIndex
aIdx( *m_SelectedBoxes
[n
]->GetSttNd() );
1230 SwContentNode
* pCNd
= rNds
.GoNextSection( &aIdx
, true, false );
1234 std::pair
<Point
, bool> const tmp(GetPtPos(), true);
1235 SwFrame
* pFrame
= pCNd
->getLayoutFrame(GetShell()->GetLayout(), nullptr, &tmp
);
1236 while( pFrame
&& !pFrame
->IsCellFrame() )
1237 pFrame
= pFrame
->GetUpper();
1238 OSL_ENSURE( pFrame
, "Node not in a table" );
1239 if( pFrame
&& pFrame
->getFrameArea().Contains( rPt
) )
1242 for ( SwCellFrame
* pCellFrame
= static_cast<SwCellFrame
*>(pFrame
); pCellFrame
; pCellFrame
= pCellFrame
->GetFollowCell() )
1244 if( pCellFrame
->getFrameArea().Contains( rPt
) )
1251 bool SwShellTableCursor::IsAtValidPos( bool bPoint
) const
1253 return SwShellCursor::IsAtValidPos( bPoint
);
1256 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */