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 <vcl/commandevent.hxx>
24 #include <vcl/svapp.hxx>
25 #include <vcl/settings.hxx>
27 #include <svx/sdr/overlay/overlayanimatedbitmapex.hxx>
28 #include <svx/sdr/overlay/overlaybitmapex.hxx>
29 #include <svx/svdpagv.hxx>
30 #include <svx/sdrpagewindow.hxx>
31 #include <svx/sdrpaintwindow.hxx>
32 #include <svx/svddrgmt.hxx>
33 #include <tools/debug.hxx>
36 #include <sdresid.hxx>
37 #include <strings.hrc>
38 #include "annotationmanagerimpl.hxx"
39 #include "annotationwindow.hxx"
40 #include "annotationtag.hxx"
41 #include <ViewShell.hxx>
43 #include <drawdoc.hxx>
45 using namespace ::com::sun::star::uno
;
46 using namespace ::com::sun::star::lang
;
47 using namespace ::com::sun::star::drawing
;
48 using namespace ::com::sun::star::office
;
49 using namespace ::com::sun::star::geometry
;
54 const sal_uInt32 SMART_TAG_HDL_NUM
= SAL_MAX_UINT32
;
55 static const int DRGPIX
= 2; // Drag MinMove in Pixel
57 static OUString
getInitials( const OUString
& rName
)
59 OUStringBuffer sInitials
;
61 const sal_Unicode
* pStr
= rName
.getStr();
62 sal_Int32 nLength
= rName
.getLength();
67 while( nLength
&& (*pStr
<= ' ') )
75 sInitials
.append(*pStr
);
79 // skip letters until whitespace
80 while( nLength
&& (*pStr
> ' ') )
86 return sInitials
.makeStringAndClear();
89 class AnnotationDragMove
: public SdrDragMove
92 AnnotationDragMove(SdrDragView
& rNewView
, const rtl::Reference
<AnnotationTag
>& xTag
);
93 virtual bool BeginSdrDrag() override
;
94 virtual bool EndSdrDrag(bool bCopy
) override
;
95 virtual void MoveSdrDrag(const Point
& rNoSnapPnt
) override
;
96 virtual void CancelSdrDrag() override
;
99 rtl::Reference
<AnnotationTag
> mxTag
;
103 AnnotationDragMove::AnnotationDragMove(SdrDragView
& rNewView
, const rtl::Reference
<AnnotationTag
>& xTag
)
104 : SdrDragMove(rNewView
)
109 bool AnnotationDragMove::BeginSdrDrag()
111 DragStat().SetRef1(GetDragHdl()->GetPos());
112 DragStat().SetShown(!DragStat().IsShown());
114 maOrigin
= GetDragHdl()->GetPos();
115 DragStat().SetActionRect(::tools::Rectangle(maOrigin
,maOrigin
));
120 void AnnotationDragMove::MoveSdrDrag(const Point
& rNoSnapPnt
)
122 Point
aPnt(rNoSnapPnt
);
124 if (DragStat().CheckMinMoved(rNoSnapPnt
))
126 if (aPnt
!=DragStat().GetNow())
129 DragStat().NextMove(aPnt
);
130 GetDragHdl()->SetPos( maOrigin
+ Point( DragStat().GetDX(), DragStat().GetDY() ) );
132 DragStat().SetActionRect(::tools::Rectangle(aPnt
,aPnt
));
137 bool AnnotationDragMove::EndSdrDrag(bool /*bCopy*/)
141 mxTag
->Move( DragStat().GetDX(), DragStat().GetDY() );
145 void AnnotationDragMove::CancelSdrDrag()
150 class AnnotationHdl
: public SmartHdl
153 AnnotationHdl( const SmartTagReference
& xTag
, const Reference
< XAnnotation
>& xAnnotation
, const Point
& rPnt
);
155 virtual void CreateB2dIAObject() override
;
156 virtual bool IsFocusHdl() const override
;
157 virtual bool isMarkable() const override
;
160 Reference
< XAnnotation
> mxAnnotation
;
161 rtl::Reference
< AnnotationTag
> mxTag
;
164 AnnotationHdl::AnnotationHdl( const SmartTagReference
& xTag
, const Reference
< XAnnotation
>& xAnnotation
, const Point
& rPnt
)
165 : SmartHdl( xTag
, rPnt
, SdrHdlKind::SmartTag
)
166 , mxAnnotation( xAnnotation
)
167 , mxTag( dynamic_cast< AnnotationTag
* >( xTag
.get() ) )
171 void AnnotationHdl::CreateB2dIAObject()
173 // first throw away old one
176 if( !mxAnnotation
.is() )
179 const StyleSettings
& rStyleSettings
= Application::GetSettings().GetStyleSettings();
181 const Point
aTagPos( GetPos() );
182 basegfx::B2DPoint
aPosition( aTagPos
.X(), aTagPos
.Y() );
184 const bool bFocused
= IsFocusHdl() && pHdlList
&& (pHdlList
->GetFocusHdl() == this);
186 BitmapEx
aBitmapEx( mxTag
->CreateAnnotationBitmap(mxTag
->isSelected()) );
189 aBitmapEx2
= mxTag
->CreateAnnotationBitmap(!mxTag
->isSelected() );
194 SdrMarkView
* pView
= pHdlList
->GetView();
196 if(!(pView
&& !pView
->areMarkHandlesHidden()))
199 SdrPageView
* pPageView
= pView
->GetSdrPageView();
204 for(sal_uInt32 b
= 0; b
< pPageView
->PageWindowCount(); b
++)
206 // const SdrPageViewWinRec& rPageViewWinRec = rPageViewWinList[b];
207 const SdrPageWindow
& rPageWindow
= *pPageView
->GetPageWindow(b
);
209 SdrPaintWindow
& rPaintWindow
= rPageWindow
.GetPaintWindow();
210 const rtl::Reference
< sdr::overlay::OverlayManager
>& xManager
= rPageWindow
.GetOverlayManager();
211 if(rPaintWindow
.OutputToWindow() && xManager
.is() )
213 std::unique_ptr
<sdr::overlay::OverlayObject
> pOverlayObject
;
215 // animate focused handles
218 const sal_uInt64 nBlinkTime
= rStyleSettings
.GetCursorBlinkTime();
220 pOverlayObject
.reset(new sdr::overlay::OverlayAnimatedBitmapEx(aPosition
, aBitmapEx
, aBitmapEx2
, nBlinkTime
, 0, 0, 0, 0 ));
224 pOverlayObject
.reset(new sdr::overlay::OverlayBitmapEx( aPosition
, aBitmapEx
, 0, 0 ));
228 insertNewlyCreatedOverlayObjectForSdrHdl(
229 std::move(pOverlayObject
),
230 rPageWindow
.GetObjectContact(),
236 bool AnnotationHdl::IsFocusHdl() const
241 bool AnnotationHdl::isMarkable() const
246 AnnotationTag::AnnotationTag( AnnotationManagerImpl
& rManager
, ::sd::View
& rView
, const Reference
< XAnnotation
>& xAnnotation
, Color
const & rColor
, int nIndex
, const vcl::Font
& rFont
)
248 , mrManager( rManager
)
249 , mxAnnotation( xAnnotation
)
253 , mnClosePopupEvent( nullptr )
254 , mpListenWindow( nullptr )
258 AnnotationTag::~AnnotationTag()
260 DBG_ASSERT( !mxAnnotation
.is(), "sd::AnnotationTag::~AnnotationTag(), dispose me first!" );
264 /** returns true if the AnnotationTag handled the event. */
265 bool AnnotationTag::MouseButtonDown( const MouseEvent
& rMEvt
, SmartHdl
& /*rHdl*/ )
267 if( !mxAnnotation
.is() )
273 SmartTagReference
xTag( this );
274 mrView
.getSmartTags().select( xTag
);
278 if( rMEvt
.IsLeft() && !rMEvt
.IsRight() )
280 vcl::Window
* pWindow
= mrView
.GetViewShell()->GetActiveWindow();
283 maMouseDownPos
= pWindow
->PixelToLogic( rMEvt
.GetPosPixel() );
286 mpListenWindow
->RemoveEventListener( LINK(this, AnnotationTag
, WindowEventHandler
));
288 mpListenWindow
= pWindow
;
289 mpListenWindow
->AddEventListener( LINK(this, AnnotationTag
, WindowEventHandler
));
298 /** returns true if the SmartTag consumes this event. */
299 bool AnnotationTag::KeyInput( const KeyEvent
& rKEvt
)
301 if( !mxAnnotation
.is() )
304 sal_uInt16 nCode
= rKEvt
.GetKeyCode().GetCode();
308 mrManager
.DeleteAnnotation( mxAnnotation
);
315 return OnMove( rKEvt
);
319 SmartTagReference
xThis( this );
320 mrView
.getSmartTags().deselect();
325 mrManager
.SelectNextAnnotation(!rKEvt
.GetKeyCode().IsShift());
338 /** returns true if the SmartTag consumes this event. */
339 bool AnnotationTag::RequestHelp( const HelpEvent
& /*rHEvt*/ )
345 /** returns true if the SmartTag consumes this event. */
346 bool AnnotationTag::Command( const CommandEvent
& rCEvt
)
348 if ( rCEvt
.GetCommand() == CommandEventId::ContextMenu
)
350 vcl::Window
* pWindow
= mrView
.GetViewShell()->GetActiveWindow();
353 ::tools::Rectangle
aContextRect(rCEvt
.GetMousePosPixel(),Size(1,1));
354 mrManager
.ExecuteAnnotationContextMenu( mxAnnotation
, pWindow
, aContextRect
);
362 void AnnotationTag::Move( int nDX
, int nDY
)
364 if( !mxAnnotation
.is() )
367 if( mrManager
.GetDoc()->IsUndoEnabled() )
368 mrManager
.GetDoc()->BegUndo( SdResId( STR_ANNOTATION_UNDO_MOVE
) );
370 RealPoint2D
aPosition( mxAnnotation
->getPosition() );
371 aPosition
.X
+= static_cast<double>(nDX
) / 100.0;
372 aPosition
.Y
+= static_cast<double>(nDY
) / 100.0;
373 mxAnnotation
->setPosition( aPosition
);
375 if( mrManager
.GetDoc()->IsUndoEnabled() )
376 mrManager
.GetDoc()->EndUndo();
378 mrView
.updateHandles();
381 bool AnnotationTag::OnMove( const KeyEvent
& rKEvt
)
386 switch( rKEvt
.GetKeyCode().GetCode() )
388 case KEY_UP
: nY
= -1; break;
389 case KEY_DOWN
: nY
= 1; break;
390 case KEY_LEFT
: nX
= -1; break;
391 case KEY_RIGHT
: nX
= 1; break;
395 if(rKEvt
.GetKeyCode().IsMod2())
397 OutputDevice
* pOut
= mrView
.GetViewShell()->GetActiveWindow();
398 Size aLogicSizeOnePixel
= pOut
? pOut
->PixelToLogic(Size(1,1)) : Size(100, 100);
399 nX
*= aLogicSizeOnePixel
.Width();
400 nY
*= aLogicSizeOnePixel
.Height();
404 // old, fixed move distance
411 // move the annotation
418 void AnnotationTag::CheckPossibilities()
422 sal_Int32
AnnotationTag::GetMarkablePointCount() const
427 sal_Int32
AnnotationTag::GetMarkedPointCount() const
432 bool AnnotationTag::MarkPoint(SdrHdl
& /*rHdl*/, bool /*bUnmark*/ )
437 bool AnnotationTag::MarkPoints(const ::tools::Rectangle
* /*pRect*/, bool /*bUnmark*/ )
442 bool AnnotationTag::getContext( SdrViewContext
& /*rContext*/ )
447 void AnnotationTag::addCustomHandles( SdrHdlList
& rHandlerList
)
449 if( !mxAnnotation
.is() )
452 SmartTagReference
xThis( this );
453 std::unique_ptr
<AnnotationHdl
> pHdl(new AnnotationHdl( xThis
, mxAnnotation
, Point() ));
454 pHdl
->SetObjHdlNum( SMART_TAG_HDL_NUM
);
455 pHdl
->SetPageView( mrView
.GetSdrPageView() );
457 RealPoint2D
aPosition( mxAnnotation
->getPosition() );
458 Point
aBasePos( static_cast<long>(aPosition
.X
* 100.0), static_cast<long>(aPosition
.Y
* 100.0) );
459 pHdl
->SetPos( aBasePos
);
461 rHandlerList
.AddHdl( std::move(pHdl
) );
464 void AnnotationTag::disposing()
468 mpListenWindow
->RemoveEventListener( LINK(this, AnnotationTag
, WindowEventHandler
));
471 if( mnClosePopupEvent
)
473 Application::RemoveUserEvent( mnClosePopupEvent
);
474 mnClosePopupEvent
= nullptr;
477 mxAnnotation
.clear();
479 SmartTag::disposing();
482 void AnnotationTag::select()
486 mrManager
.onTagSelected( *this );
488 vcl::Window
* pWindow
= mrView
.GetViewShell()->GetActiveWindow();
491 RealPoint2D
aPosition( mxAnnotation
->getPosition() );
492 Point
aPos( static_cast<long>(aPosition
.X
* 100.0), static_cast<long>(aPosition
.Y
* 100.0) );
494 ::tools::Rectangle
aVisRect( aPos
, pWindow
->PixelToLogic(maSize
) );
495 mrView
.MakeVisible(aVisRect
, *pWindow
);
499 void AnnotationTag::deselect()
501 SmartTag::deselect();
505 mrManager
.onTagDeselected( *this );
508 BitmapEx
AnnotationTag::CreateAnnotationBitmap( bool bSelected
)
510 ScopedVclPtrInstance
< VirtualDevice
> pVDev
;
512 OUString
sInitials(mxAnnotation
->getInitials());
513 if (sInitials
.isEmpty())
514 sInitials
= getInitials(mxAnnotation
->getAuthor());
516 OUString
sAuthor(sInitials
+ " " + OUString::number(mnIndex
));
518 pVDev
->SetFont( mrFont
);
520 const int BORDER_X
= 4; // pixels
521 const int BORDER_Y
= 4; // pixels
523 maSize
= Size( pVDev
->GetTextWidth( sAuthor
) + 2*BORDER_X
, pVDev
->GetTextHeight() + 2*BORDER_Y
);
524 pVDev
->SetOutputSizePixel( maSize
, false );
526 Color
aBorderColor( maColor
);
530 aBorderColor
.Invert();
534 if( maColor
.IsDark() )
536 aBorderColor
.IncreaseLuminance( 32 );
540 aBorderColor
.DecreaseLuminance( 32 );
545 ::tools::Rectangle
aBorderRect( aPos
, maSize
);
546 pVDev
->SetLineColor(aBorderColor
);
547 pVDev
->SetFillColor(maColor
);
548 pVDev
->DrawRect( aBorderRect
);
550 pVDev
->SetTextColor( maColor
.IsDark() ? COL_WHITE
: COL_BLACK
);
551 pVDev
->DrawText( Point( BORDER_X
, BORDER_Y
), sAuthor
);
553 return pVDev
->GetBitmapEx( aPos
, maSize
);
556 void AnnotationTag::OpenPopup( bool bEdit
)
558 if( !mxAnnotation
.is() )
561 if( !mpAnnotationWindow
.get() )
563 vcl::Window
* pWindow
= dynamic_cast< vcl::Window
* >( getView().GetFirstOutputDevice() );
566 RealPoint2D
aPosition( mxAnnotation
->getPosition() );
567 Point
aPos( pWindow
->OutputToScreenPixel( pWindow
->LogicToPixel( Point( static_cast<long>(aPosition
.X
* 100.0), static_cast<long>(aPosition
.Y
* 100.0) ) ) ) );
569 aPos
.AdjustX(4 ); // magic!
572 ::tools::Rectangle
aRect( aPos
, maSize
);
574 mpAnnotationWindow
.reset( VclPtr
<AnnotationWindow
>::Create( mrManager
, mrView
.GetDocSh(), pWindow
->GetWindow(GetWindowType::Frame
) ) );
575 mpAnnotationWindow
->InitControls();
576 mpAnnotationWindow
->setAnnotation(mxAnnotation
);
578 sal_uInt16 nArrangeIndex
= 0;
579 Point
aPopupPos( FloatingWindow::CalcFloatingPosition( mpAnnotationWindow
.get(), aRect
, FloatWinPopupFlags::Right
, nArrangeIndex
) );
580 Size
aPopupSize( 320, 240 );
582 mpAnnotationWindow
->SetPosSizePixel( aPopupPos
, aPopupSize
);
583 mpAnnotationWindow
->DoResize();
585 mpAnnotationWindow
->Show();
586 mpAnnotationWindow
->GrabFocus();
587 mpAnnotationWindow
->AddEventListener( LINK(this, AnnotationTag
, WindowEventHandler
));
591 if( bEdit
&& mpAnnotationWindow
.get() )
592 mpAnnotationWindow
->StartEdit();
595 void AnnotationTag::ClosePopup()
597 if( mpAnnotationWindow
.get())
599 mpAnnotationWindow
->RemoveEventListener( LINK(this, AnnotationTag
, WindowEventHandler
));
600 mpAnnotationWindow
->Deactivate();
601 mpAnnotationWindow
.disposeAndClear();
605 IMPL_LINK(AnnotationTag
, WindowEventHandler
, VclWindowEvent
&, rEvent
, void)
607 vcl::Window
* pWindow
= rEvent
.GetWindow();
612 if( pWindow
== mpAnnotationWindow
.get() )
614 if( rEvent
.GetId() == VclEventId::WindowDeactivate
)
616 // tdf#99388 and tdf#99712 if PopupMenu is active, suppress
617 // deletion of the AnnotationWindow which is triggered by
619 if (!mrManager
.getPopupMenuActive())
621 if( mnClosePopupEvent
)
622 Application::RemoveUserEvent( mnClosePopupEvent
);
624 mnClosePopupEvent
= Application::PostUserEvent( LINK( this, AnnotationTag
, ClosePopupHdl
) );
628 else if( pWindow
== mpListenWindow
)
630 switch( rEvent
.GetId() )
632 case VclEventId::WindowMouseButtonUp
:
634 // if we stop pressing the button without a mouse move we open the popup
635 mpListenWindow
->RemoveEventListener( LINK(this, AnnotationTag
, WindowEventHandler
));
636 mpListenWindow
= nullptr;
637 if( mpAnnotationWindow
.get() == nullptr )
641 case VclEventId::WindowMouseMove
:
643 // if we move the mouse after a button down we want to start dragging
644 mpListenWindow
->RemoveEventListener( LINK(this, AnnotationTag
, WindowEventHandler
));
645 mpListenWindow
= nullptr;
647 SdrHdl
* pHdl
= mrView
.PickHandle(maMouseDownPos
);
651 const sal_uInt16 nDrgLog
= static_cast<sal_uInt16
>(pWindow
->PixelToLogic(Size(DRGPIX
,0)).Width());
653 rtl::Reference
< AnnotationTag
> xTag( this );
655 SdrDragMethod
* pDragMethod
= new AnnotationDragMove( mrView
, xTag
);
656 mrView
.BegDragObj(maMouseDownPos
, nullptr, pHdl
, nDrgLog
, pDragMethod
);
660 case VclEventId::ObjectDying
:
661 mpListenWindow
= nullptr;
668 IMPL_LINK_NOARG(AnnotationTag
, ClosePopupHdl
, void*, void)
670 mnClosePopupEvent
= nullptr;
674 } // end of namespace sd
676 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */