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 <com/sun/star/geometry/RealPoint2D.hpp>
21 #include <com/sun/star/office/XAnnotation.hpp>
23 #include <rtl/ustrbuf.hxx>
26 #include <vcl/commandevent.hxx>
27 #include <vcl/svapp.hxx>
28 #include <vcl/settings.hxx>
29 #include <vcl/weldutils.hxx>
31 #include <svx/sdr/overlay/overlayanimatedbitmapex.hxx>
32 #include <svx/sdr/overlay/overlaybitmapex.hxx>
33 #include <svx/sdr/overlay/overlaypolypolygon.hxx>
34 #include <svx/svdpagv.hxx>
35 #include <svx/sdrpagewindow.hxx>
36 #include <svx/sdrpaintwindow.hxx>
37 #include <svx/svddrgmt.hxx>
38 #include <tools/debug.hxx>
41 #include <sdresid.hxx>
42 #include <strings.hrc>
43 #include "annotationmanagerimpl.hxx"
44 #include "annotationwindow.hxx"
45 #include "annotationtag.hxx"
46 #include <Annotation.hxx>
47 #include <ViewShell.hxx>
49 #include <drawdoc.hxx>
51 using namespace ::com::sun::star::uno
;
52 using namespace ::com::sun::star::lang
;
53 using namespace ::com::sun::star::drawing
;
54 using namespace ::com::sun::star::office
;
55 using namespace ::com::sun::star::geometry
;
60 const sal_uInt32 SMART_TAG_HDL_NUM
= SAL_MAX_UINT32
;
61 const int DRGPIX
= 2; // Drag MinMove in Pixel
63 static OUString
getInitials( const OUString
& rName
)
65 OUStringBuffer sInitials
;
67 const sal_Unicode
* pStr
= rName
.getStr();
68 sal_Int32 nLength
= rName
.getLength();
73 while( nLength
&& (*pStr
<= ' ') )
81 sInitials
.append(*pStr
);
85 // skip letters until whitespace
86 while( nLength
&& (*pStr
> ' ') )
92 return sInitials
.makeStringAndClear();
97 class AnnotationDragMove
: public SdrDragMove
100 AnnotationDragMove(SdrDragView
& rNewView
, rtl::Reference
<AnnotationTag
> xTag
);
101 virtual bool BeginSdrDrag() override
;
102 virtual bool EndSdrDrag(bool bCopy
) override
;
103 virtual void MoveSdrDrag(const Point
& rNoSnapPnt
) override
;
104 virtual void CancelSdrDrag() override
;
107 rtl::Reference
<AnnotationTag
> mxTag
;
113 AnnotationDragMove::AnnotationDragMove(SdrDragView
& rNewView
, rtl::Reference
<AnnotationTag
> xTag
)
114 : SdrDragMove(rNewView
)
115 , mxTag(std::move( xTag
))
119 bool AnnotationDragMove::BeginSdrDrag()
121 DragStat().SetRef1(GetDragHdl()->GetPos());
122 DragStat().SetShown(!DragStat().IsShown());
124 maOrigin
= GetDragHdl()->GetPos();
125 DragStat().SetActionRect(::tools::Rectangle(maOrigin
,maOrigin
));
130 void AnnotationDragMove::MoveSdrDrag(const Point
& rNoSnapPnt
)
132 Point
aPnt(rNoSnapPnt
);
134 if (DragStat().CheckMinMoved(rNoSnapPnt
))
136 if (aPnt
!=DragStat().GetNow())
139 DragStat().NextMove(aPnt
);
140 GetDragHdl()->SetPos( maOrigin
+ Point( DragStat().GetDX(), DragStat().GetDY() ) );
142 DragStat().SetActionRect(::tools::Rectangle(aPnt
,aPnt
));
147 bool AnnotationDragMove::EndSdrDrag(bool /*bCopy*/)
151 mxTag
->Move( DragStat().GetDX(), DragStat().GetDY() );
155 void AnnotationDragMove::CancelSdrDrag()
162 class AnnotationHdl
: public SmartHdl
165 AnnotationHdl( const SmartTagReference
& xTag
, const Reference
< XAnnotation
>& xAnnotation
, const Point
& rPnt
);
167 virtual void CreateB2dIAObject() override
;
168 virtual bool IsFocusHdl() const override
;
171 Reference
< XAnnotation
> mxAnnotation
;
172 rtl::Reference
< AnnotationTag
> mxTag
;
177 AnnotationHdl::AnnotationHdl( const SmartTagReference
& xTag
, const Reference
< XAnnotation
>& xAnnotation
, const Point
& rPnt
)
178 : SmartHdl( xTag
, rPnt
, SdrHdlKind::SmartTag
)
179 , mxAnnotation( xAnnotation
)
180 , mxTag( dynamic_cast< AnnotationTag
* >( xTag
.get() ) )
184 void AnnotationHdl::CreateB2dIAObject()
186 // first throw away old one
189 if (!mxAnnotation
.is())
192 const StyleSettings
& rStyleSettings
= Application::GetSettings().GetStyleSettings();
194 const Point
aTagPos( GetPos() );
195 basegfx::B2DPoint
aPosition( aTagPos
.X(), aTagPos
.Y() );
197 const bool bFocused
= IsFocusHdl() && pHdlList
&& (pHdlList
->GetFocusHdl() == this);
199 BitmapEx
aBitmapEx( mxTag
->CreateAnnotationBitmap(mxTag
->isSelected()) );
202 aBitmapEx2
= mxTag
->CreateAnnotationBitmap(!mxTag
->isSelected() );
207 SdrMarkView
* pView
= pHdlList
->GetView();
209 if(!pView
|| pView
->areMarkHandlesHidden())
212 SdrPageView
* pPageView
= pView
->GetSdrPageView();
217 for(sal_uInt32 b
= 0; b
< pPageView
->PageWindowCount(); b
++)
219 // const SdrPageViewWinRec& rPageViewWinRec = rPageViewWinList[b];
220 const SdrPageWindow
& rPageWindow
= *pPageView
->GetPageWindow(b
);
222 SdrPaintWindow
& rPaintWindow
= rPageWindow
.GetPaintWindow();
223 const rtl::Reference
< sdr::overlay::OverlayManager
>& xManager
= rPageWindow
.GetOverlayManager();
224 if(rPaintWindow
.OutputToWindow() && xManager
.is() )
226 std::unique_ptr
<sdr::overlay::OverlayObject
> pOverlayObject
;
228 auto* pAnnotation
= dynamic_cast<sd::Annotation
*>(mxAnnotation
.get());
230 if (pAnnotation
&& pAnnotation
->hasCustomAnnotationMarker())
232 CustomAnnotationMarker
& rCustomAnnotationMarker
= pAnnotation
->getCustomAnnotationMarker();
234 auto& rPolygons
= rCustomAnnotationMarker
.maPolygons
;
235 if (!rPolygons
.empty())
237 basegfx::B2DPolyPolygon aPolyPolygon
;
238 for (auto const & rPolygon
: rPolygons
)
239 aPolyPolygon
.append(rPolygon
);
241 pOverlayObject
.reset(new sdr::overlay::OverlayPolyPolygon(
242 std::move(aPolyPolygon
),
243 rCustomAnnotationMarker
.maLineColor
,
244 rCustomAnnotationMarker
.mnLineWidth
,
245 rCustomAnnotationMarker
.maFillColor
));
250 // animate focused handles
253 const sal_uInt64 nBlinkTime
= rStyleSettings
.GetCursorBlinkTime();
255 pOverlayObject
.reset(new sdr::overlay::OverlayAnimatedBitmapEx(aPosition
, aBitmapEx
, aBitmapEx2
, nBlinkTime
, 0, 0, 0, 0 ));
259 pOverlayObject
.reset(new sdr::overlay::OverlayBitmapEx( aPosition
, aBitmapEx
, 0, 0 ));
264 insertNewlyCreatedOverlayObjectForSdrHdl(
265 std::move(pOverlayObject
),
266 rPageWindow
.GetObjectContact(),
272 bool AnnotationHdl::IsFocusHdl() const
277 AnnotationTag::AnnotationTag( AnnotationManagerImpl
& rManager
, ::sd::View
& rView
, const Reference
< XAnnotation
>& xAnnotation
, Color
const & rColor
, int nIndex
, const vcl::Font
& rFont
)
279 , mrManager( rManager
)
280 , mxAnnotation( xAnnotation
)
284 , mpListenWindow( nullptr )
288 AnnotationTag::~AnnotationTag()
290 DBG_ASSERT( !mxAnnotation
.is(), "sd::AnnotationTag::~AnnotationTag(), dispose me first!" );
294 /** returns true if the AnnotationTag handled the event. */
295 bool AnnotationTag::MouseButtonDown( const MouseEvent
& rMEvt
, SmartHdl
& /*rHdl*/ )
297 if( !mxAnnotation
.is() )
303 SmartTagReference
xTag( this );
304 mrView
.getSmartTags().select( xTag
);
308 if( rMEvt
.IsLeft() && !rMEvt
.IsRight() )
310 vcl::Window
* pWindow
= mrView
.GetViewShell()->GetActiveWindow();
313 maMouseDownPos
= pWindow
->PixelToLogic( rMEvt
.GetPosPixel() );
316 mpListenWindow
->RemoveEventListener( LINK(this, AnnotationTag
, WindowEventHandler
));
318 mpListenWindow
= pWindow
;
319 mpListenWindow
->AddEventListener( LINK(this, AnnotationTag
, WindowEventHandler
));
328 /** returns true if the SmartTag consumes this event. */
329 bool AnnotationTag::KeyInput( const KeyEvent
& rKEvt
)
331 if( !mxAnnotation
.is() )
334 sal_uInt16 nCode
= rKEvt
.GetKeyCode().GetCode();
338 mrManager
.DeleteAnnotation( mxAnnotation
);
345 return OnMove( rKEvt
);
349 SmartTagReference
xThis( this );
350 mrView
.getSmartTags().deselect();
355 mrManager
.SelectNextAnnotation(!rKEvt
.GetKeyCode().IsShift());
368 /** returns true if the SmartTag consumes this event. */
369 bool AnnotationTag::Command( const CommandEvent
& rCEvt
)
371 if (rCEvt
.GetCommand() != CommandEventId::ContextMenu
)
373 if (vcl::Window
* pWindow
= mrView
.GetViewShell()->GetActiveWindow())
375 ::tools::Rectangle
aContextRect(rCEvt
.GetMousePosPixel(),Size(1,1));
376 weld::Window
* pParent
= weld::GetPopupParent(*pWindow
, aContextRect
);
377 mrManager
.ExecuteAnnotationTagContextMenu(mxAnnotation
, pParent
, aContextRect
);
383 void AnnotationTag::Move( int nDX
, int nDY
)
385 if( !mxAnnotation
.is() )
388 if( mrManager
.GetDoc()->IsUndoEnabled() )
389 mrManager
.GetDoc()->BegUndo( SdResId( STR_ANNOTATION_UNDO_MOVE
) );
391 RealPoint2D
aPosition( mxAnnotation
->getPosition() );
392 aPosition
.X
+= static_cast<double>(nDX
) / 100.0;
393 aPosition
.Y
+= static_cast<double>(nDY
) / 100.0;
394 mxAnnotation
->setPosition( aPosition
);
396 if( mrManager
.GetDoc()->IsUndoEnabled() )
397 mrManager
.GetDoc()->EndUndo();
399 mrView
.updateHandles();
402 bool AnnotationTag::OnMove( const KeyEvent
& rKEvt
)
404 ::tools::Long nX
= 0;
405 ::tools::Long nY
= 0;
407 switch( rKEvt
.GetKeyCode().GetCode() )
409 case KEY_UP
: nY
= -1; break;
410 case KEY_DOWN
: nY
= 1; break;
411 case KEY_LEFT
: nX
= -1; break;
412 case KEY_RIGHT
: nX
= 1; break;
416 if(rKEvt
.GetKeyCode().IsMod2())
418 OutputDevice
* pOut
= mrView
.GetViewShell()->GetActiveWindow()->GetOutDev();
419 Size aLogicSizeOnePixel
= pOut
? pOut
->PixelToLogic(Size(1,1)) : Size(100, 100);
420 nX
*= aLogicSizeOnePixel
.Width();
421 nY
*= aLogicSizeOnePixel
.Height();
425 // old, fixed move distance
432 // move the annotation
439 void AnnotationTag::CheckPossibilities()
443 sal_Int32
AnnotationTag::GetMarkablePointCount() const
448 sal_Int32
AnnotationTag::GetMarkedPointCount() const
453 bool AnnotationTag::MarkPoint(SdrHdl
& /*rHdl*/, bool /*bUnmark*/ )
458 bool AnnotationTag::MarkPoints(const ::tools::Rectangle
* /*pRect*/, bool /*bUnmark*/ )
463 bool AnnotationTag::getContext( SdrViewContext
& /*rContext*/ )
468 void AnnotationTag::addCustomHandles( SdrHdlList
& rHandlerList
)
470 if( !mxAnnotation
.is() )
473 SmartTagReference
xThis( this );
474 std::unique_ptr
<AnnotationHdl
> pHdl(new AnnotationHdl( xThis
, mxAnnotation
, Point() ));
475 pHdl
->SetObjHdlNum( SMART_TAG_HDL_NUM
);
476 pHdl
->SetPageView( mrView
.GetSdrPageView() );
478 RealPoint2D
aPosition( mxAnnotation
->getPosition() );
479 Point
aBasePos( static_cast<::tools::Long
>(aPosition
.X
* 100.0), static_cast<::tools::Long
>(aPosition
.Y
* 100.0) );
480 pHdl
->SetPos( aBasePos
);
482 rHandlerList
.AddHdl( std::move(pHdl
) );
485 void AnnotationTag::disposing()
489 mpListenWindow
->RemoveEventListener( LINK(this, AnnotationTag
, WindowEventHandler
));
492 mxAnnotation
.clear();
494 SmartTag::disposing();
497 void AnnotationTag::select()
501 mrManager
.onTagSelected( *this );
503 vcl::Window
* pWindow
= mrView
.GetViewShell()->GetActiveWindow();
506 RealPoint2D
aPosition( mxAnnotation
->getPosition() );
507 Point
aPos( static_cast<::tools::Long
>(aPosition
.X
* 100.0), static_cast<::tools::Long
>(aPosition
.Y
* 100.0) );
509 ::tools::Rectangle
aVisRect( aPos
, pWindow
->PixelToLogic(maSize
) );
510 mrView
.MakeVisible(aVisRect
, *pWindow
);
514 void AnnotationTag::deselect()
516 SmartTag::deselect();
520 mrManager
.onTagDeselected( *this );
523 BitmapEx
AnnotationTag::CreateAnnotationBitmap( bool bSelected
)
525 ScopedVclPtrInstance
< VirtualDevice
> pVDev
;
528 auto* pAnnotation
= dynamic_cast<sd::Annotation
*>(mxAnnotation
.get());
529 if (pAnnotation
&& pAnnotation
->isFreeText())
531 sText
= mxAnnotation
->getTextRange()->getString();
535 OUString
sInitials(mxAnnotation
->getInitials());
536 if (sInitials
.isEmpty())
538 sInitials
= getInitials(mxAnnotation
->getAuthor());
541 sText
= sInitials
+ " " + OUString::number(mnIndex
);
544 pVDev
->SetFont( mrFont
);
546 const int BORDER_X
= 4; // pixels
547 const int BORDER_Y
= 4; // pixels
549 maSize
= Size(pVDev
->GetTextWidth(sText
) + 2 * BORDER_X
, pVDev
->GetTextHeight() + 2 * BORDER_Y
);
550 pVDev
->SetOutputSizePixel( maSize
, false );
552 Color
aBorderColor( maColor
);
556 aBorderColor
.Invert();
560 if( maColor
.IsDark() )
562 aBorderColor
.IncreaseLuminance( 32 );
566 aBorderColor
.DecreaseLuminance( 32 );
571 ::tools::Rectangle
aBorderRect( aPos
, maSize
);
572 pVDev
->SetLineColor(aBorderColor
);
573 pVDev
->SetFillColor(maColor
);
574 pVDev
->DrawRect( aBorderRect
);
576 pVDev
->SetTextColor( maColor
.IsDark() ? COL_WHITE
: COL_BLACK
);
577 pVDev
->DrawText(Point(BORDER_X
, BORDER_Y
), sText
);
579 return pVDev
->GetBitmapEx( aPos
, maSize
);
582 void AnnotationTag::OpenPopup( bool bEdit
)
584 if( !mxAnnotation
.is() )
587 if( !mpAnnotationWindow
)
589 OutputDevice
* pOut
= getView().GetFirstOutputDevice();
590 vcl::Window
* pWindow
= pOut
? pOut
->GetOwnerWindow() : nullptr;
593 RealPoint2D
aPosition( mxAnnotation
->getPosition() );
594 Point
aPos(pWindow
->LogicToPixel( Point( static_cast<::tools::Long
>(aPosition
.X
* 100.0), static_cast<::tools::Long
>(aPosition
.Y
* 100.0) ) ) );
596 aPos
.AdjustX(4 ); // magic!
599 ::tools::Rectangle
aRect( aPos
, maSize
);
601 weld::Window
* pParent
= weld::GetPopupParent(*pWindow
, aRect
);
602 mpAnnotationWindow
.reset(new AnnotationWindow(pParent
, aRect
, mrView
.GetDocSh(), mxAnnotation
));
603 mpAnnotationWindow
->connect_closed(LINK(this, AnnotationTag
, PopupModeEndHdl
));
607 if (bEdit
&& mpAnnotationWindow
)
608 mpAnnotationWindow
->StartEdit();
611 IMPL_LINK_NOARG(AnnotationTag
, PopupModeEndHdl
, weld::Popover
&, void)
616 void AnnotationTag::ClosePopup()
618 if (mpAnnotationWindow
)
620 mpAnnotationWindow
->SaveToDocument();
621 mpAnnotationWindow
.reset();
625 IMPL_LINK(AnnotationTag
, WindowEventHandler
, VclWindowEvent
&, rEvent
, void)
627 vcl::Window
* pWindow
= rEvent
.GetWindow();
632 if( pWindow
!= mpListenWindow
)
635 switch( rEvent
.GetId() )
637 case VclEventId::WindowMouseButtonUp
:
639 // if we stop pressing the button without a mouse move we open the popup
640 mpListenWindow
->RemoveEventListener( LINK(this, AnnotationTag
, WindowEventHandler
));
641 mpListenWindow
= nullptr;
642 if( !mpAnnotationWindow
)
646 case VclEventId::WindowMouseMove
:
648 // if we move the mouse after a button down we want to start dragging
649 mpListenWindow
->RemoveEventListener( LINK(this, AnnotationTag
, WindowEventHandler
));
650 mpListenWindow
= nullptr;
652 SdrHdl
* pHdl
= mrView
.PickHandle(maMouseDownPos
);
656 const sal_uInt16 nDrgLog
= static_cast<sal_uInt16
>(pWindow
->PixelToLogic(Size(DRGPIX
,0)).Width());
658 rtl::Reference
< AnnotationTag
> xTag( this );
660 SdrDragMethod
* pDragMethod
= new AnnotationDragMove( mrView
, xTag
);
661 mrView
.BegDragObj(maMouseDownPos
, nullptr, pHdl
, nDrgLog
, pDragMethod
);
665 case VclEventId::ObjectDying
:
666 mpListenWindow
= nullptr;
672 } // end of namespace sd
674 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */