bump product version to 6.3.0.0.beta1
[LibreOffice.git] / sd / source / ui / annotations / annotationtag.cxx
blobac07ef84594af4c1e6b7dd49b706fe3f66e584ae
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
35 #include <View.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>
42 #include <Window.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;
51 namespace sd
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();
64 while( nLength )
66 // skip whitespace
67 while( nLength && (*pStr <= ' ') )
69 nLength--; pStr++;
72 // take letter
73 if( nLength )
75 sInitials.append(*pStr);
76 nLength--; pStr++;
79 // skip letters until whitespace
80 while( nLength && (*pStr > ' ') )
82 nLength--; pStr++;
86 return sInitials.makeStringAndClear();
89 class AnnotationDragMove : public SdrDragMove
91 public:
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;
98 private:
99 rtl::Reference <AnnotationTag > mxTag;
100 Point maOrigin;
103 AnnotationDragMove::AnnotationDragMove(SdrDragView& rNewView, const rtl::Reference <AnnotationTag >& xTag)
104 : SdrDragMove(rNewView)
105 , mxTag( xTag )
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));
117 return true;
120 void AnnotationDragMove::MoveSdrDrag(const Point& rNoSnapPnt)
122 Point aPnt(rNoSnapPnt);
124 if (DragStat().CheckMinMoved(rNoSnapPnt))
126 if (aPnt!=DragStat().GetNow())
128 Hide();
129 DragStat().NextMove(aPnt);
130 GetDragHdl()->SetPos( maOrigin + Point( DragStat().GetDX(), DragStat().GetDY() ) );
131 Show();
132 DragStat().SetActionRect(::tools::Rectangle(aPnt,aPnt));
137 bool AnnotationDragMove::EndSdrDrag(bool /*bCopy*/)
139 Hide();
140 if( mxTag.is() )
141 mxTag->Move( DragStat().GetDX(), DragStat().GetDY() );
142 return true;
145 void AnnotationDragMove::CancelSdrDrag()
147 Hide();
150 class AnnotationHdl : public SmartHdl
152 public:
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;
159 private:
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
174 GetRidOfIAObject();
176 if( !mxAnnotation.is() )
177 return;
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()) );
187 BitmapEx aBitmapEx2;
188 if( bFocused )
189 aBitmapEx2 = mxTag->CreateAnnotationBitmap(!mxTag->isSelected() );
191 if(!pHdlList)
192 return;
194 SdrMarkView* pView = pHdlList->GetView();
196 if(!(pView && !pView->areMarkHandlesHidden()))
197 return;
199 SdrPageView* pPageView = pView->GetSdrPageView();
201 if(!pPageView)
202 return;
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
216 if(bFocused)
218 const sal_uInt64 nBlinkTime = rStyleSettings.GetCursorBlinkTime();
220 pOverlayObject.reset(new sdr::overlay::OverlayAnimatedBitmapEx(aPosition, aBitmapEx, aBitmapEx2, nBlinkTime, 0, 0, 0, 0 ));
222 else
224 pOverlayObject.reset(new sdr::overlay::OverlayBitmapEx( aPosition, aBitmapEx, 0, 0 ));
227 // OVERLAYMANAGER
228 insertNewlyCreatedOverlayObjectForSdrHdl(
229 std::move(pOverlayObject),
230 rPageWindow.GetObjectContact(),
231 *xManager);
236 bool AnnotationHdl::IsFocusHdl() const
238 return true;
241 bool AnnotationHdl::isMarkable() const
243 return false;
246 AnnotationTag::AnnotationTag( AnnotationManagerImpl& rManager, ::sd::View& rView, const Reference< XAnnotation >& xAnnotation, Color const & rColor, int nIndex, const vcl::Font& rFont )
247 : SmartTag( rView )
248 , mrManager( rManager )
249 , mxAnnotation( xAnnotation )
250 , maColor( rColor )
251 , mnIndex( nIndex )
252 , mrFont( rFont )
253 , mnClosePopupEvent( nullptr )
254 , mpListenWindow( nullptr )
258 AnnotationTag::~AnnotationTag()
260 DBG_ASSERT( !mxAnnotation.is(), "sd::AnnotationTag::~AnnotationTag(), dispose me first!" );
261 Dispose();
264 /** returns true if the AnnotationTag handled the event. */
265 bool AnnotationTag::MouseButtonDown( const MouseEvent& rMEvt, SmartHdl& /*rHdl*/ )
267 if( !mxAnnotation.is() )
268 return false;
270 bool bRet = false;
271 if( !isSelected() )
273 SmartTagReference xTag( this );
274 mrView.getSmartTags().select( xTag );
275 bRet = true;
278 if( rMEvt.IsLeft() && !rMEvt.IsRight() )
280 vcl::Window* pWindow = mrView.GetViewShell()->GetActiveWindow();
281 if( pWindow )
283 maMouseDownPos = pWindow->PixelToLogic( rMEvt.GetPosPixel() );
285 if( mpListenWindow )
286 mpListenWindow->RemoveEventListener( LINK(this, AnnotationTag, WindowEventHandler));
288 mpListenWindow = pWindow;
289 mpListenWindow->AddEventListener( LINK(this, AnnotationTag, WindowEventHandler));
292 bRet = true;
295 return bRet;
298 /** returns true if the SmartTag consumes this event. */
299 bool AnnotationTag::KeyInput( const KeyEvent& rKEvt )
301 if( !mxAnnotation.is() )
302 return false;
304 sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
305 switch( nCode )
307 case KEY_DELETE:
308 mrManager.DeleteAnnotation( mxAnnotation );
309 return true;
311 case KEY_DOWN:
312 case KEY_UP:
313 case KEY_LEFT:
314 case KEY_RIGHT:
315 return OnMove( rKEvt );
317 case KEY_ESCAPE:
319 SmartTagReference xThis( this );
320 mrView.getSmartTags().deselect();
321 return true;
324 case KEY_TAB:
325 mrManager.SelectNextAnnotation(!rKEvt.GetKeyCode().IsShift());
326 return true;
328 case KEY_RETURN:
329 case KEY_SPACE:
330 OpenPopup( true );
331 return true;
333 default:
334 return false;
338 /** returns true if the SmartTag consumes this event. */
339 bool AnnotationTag::RequestHelp( const HelpEvent& /*rHEvt*/ )
342 return false;
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();
351 if( pWindow )
353 ::tools::Rectangle aContextRect(rCEvt.GetMousePosPixel(),Size(1,1));
354 mrManager.ExecuteAnnotationContextMenu( mxAnnotation, pWindow, aContextRect );
355 return true;
359 return false;
362 void AnnotationTag::Move( int nDX, int nDY )
364 if( !mxAnnotation.is() )
365 return;
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 )
383 long nX = 0;
384 long nY = 0;
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;
392 default: 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();
402 else
404 // old, fixed move distance
405 nX *= 100;
406 nY *= 100;
409 if( nX || nY )
411 // move the annotation
412 Move( nX, nY );
415 return true;
418 void AnnotationTag::CheckPossibilities()
422 sal_Int32 AnnotationTag::GetMarkablePointCount() const
424 return 0;
427 sal_Int32 AnnotationTag::GetMarkedPointCount() const
429 return 0;
432 bool AnnotationTag::MarkPoint(SdrHdl& /*rHdl*/, bool /*bUnmark*/ )
434 return false;
437 bool AnnotationTag::MarkPoints(const ::tools::Rectangle* /*pRect*/, bool /*bUnmark*/ )
439 return false;
442 bool AnnotationTag::getContext( SdrViewContext& /*rContext*/ )
444 return false;
447 void AnnotationTag::addCustomHandles( SdrHdlList& rHandlerList )
449 if( !mxAnnotation.is() )
450 return;
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()
466 if( mpListenWindow )
468 mpListenWindow->RemoveEventListener( LINK(this, AnnotationTag, WindowEventHandler));
471 if( mnClosePopupEvent )
473 Application::RemoveUserEvent( mnClosePopupEvent );
474 mnClosePopupEvent = nullptr;
477 mxAnnotation.clear();
478 ClosePopup();
479 SmartTag::disposing();
482 void AnnotationTag::select()
484 SmartTag::select();
486 mrManager.onTagSelected( *this );
488 vcl::Window* pWindow = mrView.GetViewShell()->GetActiveWindow();
489 if( pWindow )
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();
503 ClosePopup();
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 );
528 if( bSelected )
530 aBorderColor.Invert();
532 else
534 if( maColor.IsDark() )
536 aBorderColor.IncreaseLuminance( 32 );
538 else
540 aBorderColor.DecreaseLuminance( 32 );
544 Point aPos;
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() )
559 return;
561 if( !mpAnnotationWindow.get() )
563 vcl::Window* pWindow = dynamic_cast< vcl::Window* >( getView().GetFirstOutputDevice() );
564 if( pWindow )
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!
570 aPos.AdjustY(1 );
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();
609 if( !pWindow )
610 return;
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
618 // it losing focus
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 )
638 OpenPopup(false);
640 break;
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);
648 if( pHdl )
650 mrView.BrkAction();
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 );
659 break;
660 case VclEventId::ObjectDying:
661 mpListenWindow = nullptr;
662 break;
663 default: break;
668 IMPL_LINK_NOARG(AnnotationTag, ClosePopupHdl, void*, void)
670 mnClosePopupEvent = nullptr;
671 ClosePopup();
674 } // end of namespace sd
676 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */