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/drawing/XDrawView.hpp>
21 #include <com/sun/star/frame/XController.hpp>
22 #include <com/sun/star/geometry/RealPoint2D.hpp>
23 #include <com/sun/star/text/XText.hpp>
24 #include <com/sun/star/document/XEventBroadcaster.hpp>
25 #include <com/sun/star/office/XAnnotationAccess.hpp>
26 #include <comphelper/lok.hxx>
27 #include <svx/svxids.hrc>
29 #include <vcl/settings.hxx>
30 #include <vcl/svapp.hxx>
31 #include <vcl/weld.hxx>
32 #include <tools/gen.hxx>
34 #include <sal/macros.h>
35 #include <svl/itempool.hxx>
36 #include <svl/intitem.hxx>
37 #include <unotools/localedatawrapper.hxx>
38 #include <unotools/useroptions.hxx>
39 #include <unotools/syslocale.hxx>
40 #include <unotools/saveopt.hxx>
42 #include <tools/datetime.hxx>
43 #include <tools/UnitConversion.hxx>
44 #include <comphelper/diagnose_ex.hxx>
46 #include <sfx2/viewfrm.hxx>
47 #include <sfx2/bindings.hxx>
48 #include <sfx2/request.hxx>
49 #include <sfx2/dispatch.hxx>
51 #include <editeng/editeng.hxx>
52 #include <editeng/eeitem.hxx>
53 #include <editeng/fontitem.hxx>
54 #include <editeng/fhgtitem.hxx>
55 #include <editeng/outlobj.hxx>
56 #include <editeng/postitem.hxx>
58 #include <svx/postattr.hxx>
60 #include <annotationmanager.hxx>
61 #include "annotationmanagerimpl.hxx"
62 #include "annotationwindow.hxx"
63 #include <strings.hrc>
65 #include <Annotation.hxx>
66 #include "AnnotationPopup.hxx"
67 #include <DrawDocShell.hxx>
68 #include <DrawViewShell.hxx>
69 #include <DrawController.hxx>
70 #include <sdresid.hxx>
71 #include <EventMultiplexer.hxx>
72 #include <ViewShellBase.hxx>
74 #include <drawdoc.hxx>
75 #include <svx/annotation/TextAPI.hxx>
76 #include <svx/annotation/AnnotationObject.hxx>
77 #include <svx/annotation/Annotation.hxx>
78 #include <svx/annotation/ObjectAnnotationData.hxx>
79 #include <optsitem.hxx>
82 #include <svx/svdobj.hxx>
83 #include <svx/svdocirc.hxx>
84 #include <svx/svdorect.hxx>
85 #include <svx/svdopath.hxx>
86 #include <svx/svdotext.hxx>
87 #include <svx/svdograf.hxx>
89 #include <svx/xfillit0.hxx>
90 #include <svx/xflclit.hxx>
91 #include <svx/xlineit0.hxx>
92 #include <svx/xlnclit.hxx>
93 #include <svx/xlnstwit.hxx>
94 #include <svx/xlnwtit.hxx>
95 #include <svx/xfltrit.hxx>
96 #include <svx/xlntrit.hxx>
106 SfxItemPool
* GetAnnotationPool()
108 static rtl::Reference
<SfxItemPool
> s_pAnnotationPool
;
109 if( !s_pAnnotationPool
)
111 s_pAnnotationPool
= EditEngine::CreatePool();
112 s_pAnnotationPool
->SetUserDefaultItem(SvxFontHeightItem(423,100,EE_CHAR_FONTHEIGHT
));
114 vcl::Font
aAppFont( Application::GetSettings().GetStyleSettings().GetAppFont() );
115 s_pAnnotationPool
->SetUserDefaultItem(SvxFontItem(aAppFont
.GetFamilyType(),aAppFont
.GetFamilyName(),u
""_ustr
,PITCH_DONTKNOW
,RTL_TEXTENCODING_DONTKNOW
,EE_CHAR_FONTINFO
));
118 return s_pAnnotationPool
.get();
121 static SfxBindings
* getBindings( ViewShellBase
const & rBase
)
123 auto pMainViewShell
= rBase
.GetMainViewShell().get();
124 if( pMainViewShell
&& pMainViewShell
->GetViewFrame() )
125 return &pMainViewShell
->GetViewFrame()->GetBindings();
130 static SfxDispatcher
* getDispatcher( ViewShellBase
const & rBase
)
132 auto pMainViewShell
= rBase
.GetMainViewShell().get();
133 if( pMainViewShell
&& pMainViewShell
->GetViewFrame() )
134 return pMainViewShell
->GetViewFrame()->GetDispatcher();
139 css::util::DateTime
getCurrentDateTime()
141 DateTime
aCurrentDate( DateTime::SYSTEM
);
142 return css::util::DateTime( 0, aCurrentDate
.GetSec(),
143 aCurrentDate
.GetMin(), aCurrentDate
.GetHour(),
144 aCurrentDate
.GetDay(), aCurrentDate
.GetMonth(),
145 aCurrentDate
.GetYear(), false );
148 OUString
getAnnotationDateTimeString(const uno::Reference
<office::XAnnotation
>& xAnnotation
)
151 if( xAnnotation
.is() )
153 const SvtSysLocale aSysLocale
;
154 const LocaleDataWrapper
& rLocalData
= aSysLocale
.GetLocaleData();
156 css::util::DateTime
aDateTime( xAnnotation
->getDateTime() );
158 Date
aSysDate( Date::SYSTEM
);
159 Date
aDate( aDateTime
.Day
, aDateTime
.Month
, aDateTime
.Year
);
161 sRet
= SdResId(STR_ANNOTATION_TODAY
);
162 else if (aDate
== (aSysDate
-1))
163 sRet
= SdResId(STR_ANNOTATION_YESTERDAY
);
164 else if (aDate
.IsValidAndGregorian() )
165 sRet
= rLocalData
.getDate(aDate
);
167 ::tools::Time
aTime( aDateTime
);
168 if(aTime
.GetTime() != 0)
169 sRet
+= " " + rLocalData
.getTime( aTime
,false );
174 AnnotationManagerImpl::AnnotationManagerImpl( ViewShellBase
& rViewShellBase
)
175 : mrBase( rViewShellBase
)
176 , mpDoc( rViewShellBase
.GetDocument() )
177 , mbShowAnnotations( true )
178 , mnUpdateTagsEvent( nullptr )
180 if (SdOptions
* pOptions
= SdModule::get()->GetSdOptions(mpDoc
->GetDocumentType()))
181 mbShowAnnotations
= pOptions
->IsShowComments();
184 void AnnotationManagerImpl::init()
186 // get current controller and initialize listeners
190 mxView
= mrBase
.GetDrawController();
192 catch (uno::Exception
&)
194 TOOLS_WARN_EXCEPTION( "sd", "sd::AnnotationManagerImpl::AnnotationManagerImpl()" );
199 uno::Reference
<document::XEventBroadcaster
> xModel (mrBase
.GetDocShell()->GetModel(), uno::UNO_QUERY_THROW
);
200 uno::Reference
<document::XEventListener
> xListener( this );
201 xModel
->addEventListener( xListener
);
203 catch (uno::Exception
&)
208 // WeakComponentImplHelper
209 void AnnotationManagerImpl::disposing (std::unique_lock
<std::mutex
>&)
213 uno::Reference
<document::XEventBroadcaster
> xModel (mrBase
.GetDocShell()->GetModel(), uno::UNO_QUERY_THROW
);
214 uno::Reference
<document::XEventListener
> xListener( this );
215 xModel
->removeEventListener( xListener
);
217 catch (uno::Exception
&)
223 if( mnUpdateTagsEvent
)
225 Application::RemoveUserEvent( mnUpdateTagsEvent
);
226 mnUpdateTagsEvent
= nullptr;
230 mxCurrentPage
.clear();
234 void SAL_CALL
AnnotationManagerImpl::notifyEvent( const css::document::EventObject
& aEvent
)
236 if( !(aEvent
.EventName
== "OnAnnotationInserted" || aEvent
.EventName
== "OnAnnotationRemoved" || aEvent
.EventName
== "OnAnnotationChanged") )
239 // AnnotationInsertion and modification is not handled here because when
240 // a new annotation is inserted, it consists of OnAnnotationInserted
241 // followed by a chain of OnAnnotationChanged (called for setting each
242 // of the annotation attributes - author, text etc.). This is not what a
243 // LOK client wants. So only handle removal here as annotation removal
244 // consists of only one event - 'OnAnnotationRemoved'
245 if ( aEvent
.EventName
== "OnAnnotationRemoved" )
247 uno::Reference
<office::XAnnotation
> xAnnotation( aEvent
.Source
, uno::UNO_QUERY
);
248 if ( auto pAnnotation
= dynamic_cast<sd::Annotation
*>(xAnnotation
.get()) )
250 LOKCommentNotify(sdr::annotation::CommentNotificationType::Remove
, &mrBase
, *pAnnotation
);
257 void SAL_CALL
AnnotationManagerImpl::disposing( const css::lang::EventObject
& /*Source*/ )
261 rtl::Reference
<sdr::annotation::Annotation
> AnnotationManagerImpl::GetAnnotationById(sal_uInt32 nAnnotationId
)
263 SdPage
* pPage
= nullptr;
266 pPage
= GetNextPage(pPage
, true);
267 if( pPage
&& !pPage
->getAnnotations().empty() )
269 sdr::annotation::AnnotationVector
aAnnotations(pPage
->getAnnotations());
270 auto iterator
= std::find_if(aAnnotations
.begin(), aAnnotations
.end(),
271 [nAnnotationId
](rtl::Reference
<sdr::annotation::Annotation
> const& xAnnotation
)
273 return xAnnotation
->GetId() == nAnnotationId
;
275 if (iterator
!= aAnnotations
.end())
280 rtl::Reference
<sdr::annotation::Annotation
> xAnnotationEmpty
;
281 return xAnnotationEmpty
;
284 void AnnotationManagerImpl::ShowAnnotations( bool bShow
)
286 // enforce show annotations if a new annotation is inserted
287 if( mbShowAnnotations
!= bShow
)
289 mbShowAnnotations
= bShow
;
291 if (SdOptions
* pOptions
= SdModule::get()->GetSdOptions(mpDoc
->GetDocumentType()))
292 pOptions
->SetShowComments( mbShowAnnotations
);
298 void AnnotationManagerImpl::ExecuteAnnotation(SfxRequest
const & rReq
)
300 switch( rReq
.GetSlot() )
302 case SID_INSERT_POSTIT
:
303 ExecuteInsertAnnotation( rReq
);
305 case SID_DELETE_POSTIT
:
306 case SID_DELETEALL_POSTIT
:
307 case SID_DELETEALLBYAUTHOR_POSTIT
:
308 ExecuteDeleteAnnotation( rReq
);
310 case SID_EDIT_POSTIT
:
311 ExecuteEditAnnotation( rReq
);
313 case SID_PREVIOUS_POSTIT
:
314 case SID_NEXT_POSTIT
:
315 SelectNextAnnotation( rReq
.GetSlot() == SID_NEXT_POSTIT
);
317 case SID_REPLYTO_POSTIT
:
318 ExecuteReplyToAnnotation( rReq
);
320 case SID_TOGGLE_NOTES
:
321 ShowAnnotations( !mbShowAnnotations
);
326 void AnnotationManagerImpl::ExecuteInsertAnnotation(SfxRequest
const & rReq
)
328 if (!comphelper::LibreOfficeKit::isActive() || comphelper::LibreOfficeKit::isTiledAnnotations())
329 ShowAnnotations(true);
331 const SfxItemSet
* pArgs
= rReq
.GetArgs();
335 if (const SfxStringItem
* pPoolItem
= pArgs
->GetItemIfSet(SID_ATTR_POSTIT_TEXT
))
337 sText
= pPoolItem
->GetValue();
341 InsertAnnotation(sText
);
344 void AnnotationManagerImpl::ExecuteDeleteAnnotation(SfxRequest
const & rReq
)
346 const SfxItemSet
* pArgs
= rReq
.GetArgs();
348 switch( rReq
.GetSlot() )
350 case SID_DELETEALL_POSTIT
:
351 DeleteAllAnnotations();
353 case SID_DELETEALLBYAUTHOR_POSTIT
:
356 const SfxPoolItem
* pPoolItem
= nullptr;
357 if( SfxItemState::SET
== pArgs
->GetItemState( SID_DELETEALLBYAUTHOR_POSTIT
, true, &pPoolItem
) )
359 OUString
sAuthor( static_cast<const SfxStringItem
*>( pPoolItem
)->GetValue() );
360 DeleteAnnotationsByAuthor( sAuthor
);
364 case SID_DELETE_POSTIT
:
366 rtl::Reference
<sdr::annotation::Annotation
> xAnnotation
;
370 const SfxPoolItem
* pPoolItem
= nullptr;
371 if( SfxItemState::SET
== pArgs
->GetItemState( SID_DELETE_POSTIT
, true, &pPoolItem
) )
373 uno::Reference
<office::XAnnotation
> xTmpAnnotation
;
374 if (static_cast<const SfxUnoAnyItem
*>(pPoolItem
)->GetValue() >>= xTmpAnnotation
)
376 xAnnotation
= dynamic_cast<sdr::annotation::Annotation
*>(xTmpAnnotation
.get());
377 assert(bool(xAnnotation
) == bool(xTmpAnnotation
) && "must be of concrete type sd::Annotation");
380 if( SfxItemState::SET
== pArgs
->GetItemState( SID_ATTR_POSTIT_ID
, true, &pPoolItem
) )
381 nId
= static_cast<const SvxPostItIdItem
*>(pPoolItem
)->GetValue().toUInt32();
385 xAnnotation
= GetAnnotationById(nId
);
386 else if( !xAnnotation
.is() )
387 GetSelectedAnnotation(xAnnotation
);
389 DeleteAnnotation(xAnnotation
);
397 void AnnotationManagerImpl::ExecuteEditAnnotation(SfxRequest
const & rReq
)
399 const SfxItemSet
* pArgs
= rReq
.GetArgs();
400 rtl::Reference
<sdr::annotation::Annotation
> xAnnotation
;
402 sal_Int32 nPositionX
= -1;
403 sal_Int32 nPositionY
= -1;
408 if (mpDoc
->IsUndoEnabled())
409 mpDoc
->BegUndo(SdResId(STR_ANNOTATION_UNDO_EDIT
));
411 if (const SvxPostItIdItem
* pPoolItem
= pArgs
->GetItemIfSet(SID_ATTR_POSTIT_ID
))
413 sal_uInt32 nId
= pPoolItem
->GetValue().toUInt32();
414 xAnnotation
= GetAnnotationById(nId
);
416 if (const SfxStringItem
* pPoolItem
= pArgs
->GetItemIfSet(SID_ATTR_POSTIT_TEXT
))
417 sText
= pPoolItem
->GetValue();
419 if (const SfxInt32Item
* pPoolItem
= pArgs
->GetItemIfSet(SID_ATTR_POSTIT_POSITION_X
))
420 nPositionX
= pPoolItem
->GetValue();
422 if (const SfxInt32Item
* pPoolItem
= pArgs
->GetItemIfSet(SID_ATTR_POSTIT_POSITION_Y
))
423 nPositionY
= pPoolItem
->GetValue();
425 if (xAnnotation
.is())
427 auto pSdAnnotation
= static_cast<sd::Annotation
*>(xAnnotation
.get());
428 pSdAnnotation
->createChangeUndo();
430 SdrObject
* pObject
= xAnnotation
->findAnnotationObject();
431 if (pObject
&& nPositionX
>= 0 && nPositionY
>= 0)
433 double fX
= convertTwipToMm100
<double>(nPositionX
);
434 double fY
= convertTwipToMm100
<double>(nPositionY
);
436 ::tools::Long deltaX
= std::round(fX
- (pSdAnnotation
->getPosition().X
* 100.0));
437 ::tools::Long deltaY
= std::round(fY
- (pSdAnnotation
->getPosition().Y
* 100.0));
439 pObject
->Move({ deltaX
, deltaY
});
442 if (!sText
.isEmpty())
444 // TODO: Not allow other authors to change others' comments ?
445 uno::Reference
<text::XText
> xText(xAnnotation
->getTextRange());
446 xText
->setString(sText
);
449 LOKCommentNotifyAll(sdr::annotation::CommentNotificationType::Modify
, *xAnnotation
);
452 if (mpDoc
->IsUndoEnabled())
458 void AnnotationManagerImpl::InsertAnnotation(const OUString
& rText
)
460 SdPage
* pPage
= GetCurrentPage();
464 if (mpDoc
->IsUndoEnabled())
465 mpDoc
->BegUndo(SdResId(STR_ANNOTATION_UNDO_INSERT
));
467 // find free space for new annotation
471 sdr::annotation::AnnotationVector
aAnnotations(pPage
->getAnnotations());
472 if (!aAnnotations
.empty())
474 const int fPageWidth
= pPage
->GetSize().Width();
475 const int fWidth
= 1000;
476 const int fHeight
= 800;
480 ::tools::Rectangle
aNewRect(Point(x
, y
), Size(fWidth
, fHeight
));
483 for (const auto& rxAnnotation
: aAnnotations
)
485 geometry::RealPoint2D
aRealPoint2D(rxAnnotation
->getPosition());
486 Point
aPoint(::tools::Long(aRealPoint2D
.X
* 100.0), ::tools::Long(aRealPoint2D
.Y
* 100.0));
487 Size
aSize(fWidth
, fHeight
);
489 if (aNewRect
.Overlaps(::tools::Rectangle(aPoint
, aSize
)))
512 rtl::Reference
<sdr::annotation::Annotation
> xAnnotation
= pPage
->createAnnotation();
515 if (comphelper::LibreOfficeKit::isActive())
516 sAuthor
= mrBase
.GetMainViewShell()->GetView()->GetAuthor();
519 SvtUserOptions aUserOptions
;
520 sAuthor
= aUserOptions
.GetFullName();
521 xAnnotation
->setInitials( aUserOptions
.GetID() );
524 if (!rText
.isEmpty())
526 uno::Reference
<text::XText
> xText(xAnnotation
->getTextRange());
527 xText
->setString(rText
);
530 // set current author to new annotation
531 xAnnotation
->setAuthor( sAuthor
);
532 // set current time to new annotation
533 xAnnotation
->setDateTime( getCurrentDateTime() );
536 geometry::RealPoint2D
aPosition(x
/ 100.0, y
/ 100.0);
537 xAnnotation
->setPosition(aPosition
);
538 xAnnotation
->setSize({5.0, 5.0});
540 pPage
->addAnnotation(xAnnotation
, -1);
542 if (mpDoc
->IsUndoEnabled())
545 // Tell our LOK clients about new comment added
546 LOKCommentNotifyAll(sdr::annotation::CommentNotificationType::Add
, *xAnnotation
);
549 SelectAnnotation(xAnnotation
, true);
552 void AnnotationManagerImpl::ExecuteReplyToAnnotation( SfxRequest
const & rReq
)
554 rtl::Reference
< sdr::annotation::Annotation
> xAnnotation
;
555 const SfxItemSet
* pArgs
= rReq
.GetArgs();
559 const SfxPoolItem
* pPoolItem
= nullptr;
560 if( SfxItemState::SET
== pArgs
->GetItemState( SID_ATTR_POSTIT_ID
, true, &pPoolItem
) )
562 sal_uInt32 nReplyId
= 0; // Id of the comment to reply to
563 nReplyId
= static_cast<const SvxPostItIdItem
*>(pPoolItem
)->GetValue().toUInt32();
564 xAnnotation
= GetAnnotationById(nReplyId
);
566 else if( SfxItemState::SET
== pArgs
->GetItemState( rReq
.GetSlot(), true, &pPoolItem
) )
568 uno::Reference
<office::XAnnotation
> xTmpAnnotation
;
569 if (static_cast<const SfxUnoAnyItem
*>(pPoolItem
)->GetValue() >>= xTmpAnnotation
)
571 xAnnotation
= dynamic_cast<Annotation
*>(xTmpAnnotation
.get());
572 assert(bool(xAnnotation
) == bool(xTmpAnnotation
) && "must be of concrete type sd::Annotation");
576 if( SfxItemState::SET
== pArgs
->GetItemState( SID_ATTR_POSTIT_TEXT
, true, &pPoolItem
) )
577 sReplyText
= static_cast<const SvxPostItTextItem
*>( pPoolItem
)->GetValue();
580 auto* pTextApi
= getTextApiObject( xAnnotation
);
584 if (mpDoc
->IsUndoEnabled())
585 mpDoc
->BegUndo(SdResId(STR_ANNOTATION_REPLY
));
589 auto pSdAnnotation
= static_cast<sd::Annotation
*>(xAnnotation
.get());
590 pSdAnnotation
->createChangeUndo();
593 ::Outliner
aOutliner( GetAnnotationPool(),OutlinerMode::TextObject
);
595 SdDrawDocument::SetCalcFieldValueHdl( &aOutliner
);
596 aOutliner
.SetUpdateLayout( true );
598 OUString
aStr(SdResId(STR_ANNOTATION_REPLY
));
599 OUString
sAuthor( xAnnotation
->getAuthor() );
600 if( sAuthor
.isEmpty() )
601 sAuthor
= SdResId( STR_ANNOTATION_NOAUTHOR
);
603 aStr
= aStr
.replaceFirst("%1", sAuthor
) +
604 " (" + getAnnotationDateTimeString( xAnnotation
) + "): \"";
606 OUString
sQuote( pTextApi
->GetText() );
608 if( sQuote
.isEmpty() )
610 aStr
+= sQuote
+ "\"\n";
612 for( sal_Int32 nIdx
= 0; nIdx
>= 0; )
613 aOutliner
.Insert(aStr
.getToken(0, '\n', nIdx
), EE_PARA_MAX
, -1);
615 if( aOutliner
.GetParagraphCount() > 1 )
617 SfxItemSet
aAnswerSet( aOutliner
.GetEmptyItemSet() );
618 aAnswerSet
.Put(SvxPostureItem(ITALIC_NORMAL
,EE_CHAR_ITALIC
));
621 aSel
.end
.nPara
= aOutliner
.GetParagraphCount() - 2;
622 aSel
.end
.nIndex
= aOutliner
.GetText( aOutliner
.GetParagraph( aSel
.end
.nPara
) ).getLength();
624 aOutliner
.QuickSetAttribs( aAnswerSet
, aSel
);
627 if (!sReplyText
.isEmpty())
628 aOutliner
.Insert(sReplyText
);
630 std::optional
< OutlinerParaObject
> pOPO( aOutliner
.CreateParaObject() );
631 pTextApi
->SetText(*pOPO
);
633 OUString sReplyAuthor
;
634 if (comphelper::LibreOfficeKit::isActive())
635 sReplyAuthor
= mrBase
.GetMainViewShell()->GetView()->GetAuthor();
638 SvtUserOptions aUserOptions
;
639 sReplyAuthor
= aUserOptions
.GetFullName();
640 xAnnotation
->setInitials( aUserOptions
.GetID() );
643 xAnnotation
->setAuthor( sReplyAuthor
);
644 // set current time to reply
645 xAnnotation
->setDateTime( getCurrentDateTime() );
647 // Tell our LOK clients about this (comment modification)
648 LOKCommentNotifyAll(sdr::annotation::CommentNotificationType::Modify
, *xAnnotation
);
650 if( mpDoc
->IsUndoEnabled() )
654 SelectAnnotation( xAnnotation
, true );
657 void AnnotationManagerImpl::DeleteAnnotation(rtl::Reference
<sdr::annotation::Annotation
> const& xAnnotation
)
659 SdPage
* pPage
= GetCurrentPage();
661 if( xAnnotation
.is() && pPage
)
663 if( mpDoc
->IsUndoEnabled() )
664 mpDoc
->BegUndo( SdResId( STR_ANNOTATION_UNDO_DELETE
) );
666 pPage
->removeAnnotation( xAnnotation
);
668 if( mpDoc
->IsUndoEnabled() )
673 void AnnotationManagerImpl::DeleteAnnotationsByAuthor( std::u16string_view sAuthor
)
675 if( mpDoc
->IsUndoEnabled() )
676 mpDoc
->BegUndo( SdResId( STR_ANNOTATION_UNDO_DELETE
) );
678 SdPage
* pPage
= nullptr;
681 pPage
= GetNextPage( pPage
, true );
685 std::vector
<rtl::Reference
<sdr::annotation::Annotation
>> aAnnotations(pPage
->getAnnotations()); // intentionally copy
686 for (auto const& xAnnotation
: aAnnotations
)
688 if( xAnnotation
->getAuthor() == sAuthor
)
690 if( mxSelectedAnnotation
== xAnnotation
)
691 mxSelectedAnnotation
.clear();
692 pPage
->removeAnnotation( xAnnotation
);
698 if( mpDoc
->IsUndoEnabled() )
702 void AnnotationManagerImpl::DeleteAllAnnotations()
704 if( mpDoc
->IsUndoEnabled() )
705 mpDoc
->BegUndo( SdResId( STR_ANNOTATION_UNDO_DELETE
) );
707 SdPage
* pPage
= nullptr;
710 pPage
= GetNextPage( pPage
, true );
712 if( pPage
&& !pPage
->getAnnotations().empty() )
714 std::vector
<rtl::Reference
<sdr::annotation::Annotation
>> aAnnotations(pPage
->getAnnotations()); // intentionally copy
715 for( const auto& rxAnnotation
: aAnnotations
)
717 pPage
->removeAnnotation( rxAnnotation
);
723 mxSelectedAnnotation
.clear();
725 if( mpDoc
->IsUndoEnabled() )
729 void AnnotationManagerImpl::GetAnnotationState(SfxItemSet
& rSet
)
731 SdPage
* pCurrentPage
= GetCurrentPage();
733 const bool bReadOnly
= mrBase
.GetDocShell()->IsReadOnly();
734 const bool bWrongPageKind
= (pCurrentPage
== nullptr) || (pCurrentPage
->GetPageKind() != PageKind::Standard
);
736 const SvtSaveOptions::ODFSaneDefaultVersion
nCurrentODFVersion( GetODFSaneDefaultVersion() );
738 if (bReadOnly
|| bWrongPageKind
|| (nCurrentODFVersion
<= SvtSaveOptions::ODFSVER_012
))
739 rSet
.DisableItem( SID_INSERT_POSTIT
);
741 rSet
.Put(SfxBoolItem(SID_TOGGLE_NOTES
, mbShowAnnotations
));
743 rtl::Reference
<sdr::annotation::Annotation
> xAnnotation
;
744 GetSelectedAnnotation(xAnnotation
);
746 // Don't disable these slot in case of LOK, as postit doesn't need to
747 // selected before doing an operation on it in LOK
748 if( (!xAnnotation
.is() && !comphelper::LibreOfficeKit::isActive()) || bReadOnly
)
750 rSet
.DisableItem( SID_DELETE_POSTIT
);
751 rSet
.DisableItem( SID_EDIT_POSTIT
);
754 SdPage
* pPage
= nullptr;
756 bool bHasAnnotations
= false;
759 pPage
= GetNextPage( pPage
, true );
761 if( pPage
&& !pPage
->getAnnotations().empty() )
762 bHasAnnotations
= true;
764 while( pPage
&& !bHasAnnotations
);
766 if( !bHasAnnotations
|| bReadOnly
)
768 rSet
.DisableItem( SID_DELETEALL_POSTIT
);
771 if( bWrongPageKind
|| !bHasAnnotations
)
773 rSet
.DisableItem( SID_PREVIOUS_POSTIT
);
774 rSet
.DisableItem( SID_NEXT_POSTIT
);
778 void AnnotationManagerImpl::SelectNextAnnotation(bool bForward
)
780 ShowAnnotations( true );
782 rtl::Reference
<sdr::annotation::Annotation
> xCurrent
;
783 GetSelectedAnnotation(xCurrent
);
784 SdPage
* pPage
= GetCurrentPage();
788 sdr::annotation::AnnotationVector
const& aAnnotations
= pPage
->getAnnotations();
794 auto iter
= std::find(aAnnotations
.begin(), aAnnotations
.end(), xCurrent
);
795 if (iter
!= aAnnotations
.end())
798 if( iter
!= aAnnotations
.end() )
800 SelectAnnotation( *iter
);
805 else if( !aAnnotations
.empty() )
807 SelectAnnotation( *(aAnnotations
.begin()) );
815 auto iter
= std::find(aAnnotations
.begin(), aAnnotations
.end(), xCurrent
);
816 if (iter
!= aAnnotations
.end() && iter
!= aAnnotations
.begin())
819 SelectAnnotation( *iter
);
823 else if( !aAnnotations
.empty() )
825 auto iterator
= aAnnotations
.end();
827 SelectAnnotation(*iterator
);
832 mxSelectedAnnotation
.clear();
837 pPage
= GetNextPage( pPage
, bForward
);
839 if( pPage
&& !pPage
->getAnnotations().empty() )
841 // switch to next/previous slide with annotations
842 std::shared_ptr
<DrawViewShell
> pDrawViewShell(std::dynamic_pointer_cast
<DrawViewShell
>(mrBase
.GetMainViewShell()));
843 if (pDrawViewShell
!= nullptr)
845 pDrawViewShell
->ChangeEditMode(pPage
->IsMasterPage() ? EditMode::MasterPage
: EditMode::Page
, false);
846 pDrawViewShell
->SwitchPage((pPage
->GetPageNum() - 1) >> 1);
848 SfxDispatcher
* pDispatcher
= getDispatcher( mrBase
);
850 pDispatcher
->Execute( bForward
? SID_NEXT_POSTIT
: SID_PREVIOUS_POSTIT
);
858 // The question text depends on the search direction.
859 bool bImpress
= mpDoc
->GetDocumentType() == DocumentType::Impress
;
860 TranslateId pStringId
;
862 pStringId
= bImpress
? STR_ANNOTATION_WRAP_FORWARD
: STR_ANNOTATION_WRAP_FORWARD_DRAW
;
864 pStringId
= bImpress
? STR_ANNOTATION_WRAP_BACKWARD
: STR_ANNOTATION_WRAP_BACKWARD_DRAW
;
866 // Pop up question box that asks the user whether to wrap around.
867 // The dialog is made modal with respect to the whole application.
868 std::unique_ptr
<weld::MessageDialog
> xQueryBox(Application::CreateMessageDialog(nullptr,
869 VclMessageType::Question
, VclButtonsType::YesNo
,
870 SdResId(pStringId
)));
871 xQueryBox
->set_default_response(RET_YES
);
872 if (xQueryBox
->run() != RET_YES
)
878 void AnnotationManagerImpl::SelectAnnotation(rtl::Reference
<sdr::annotation::Annotation
> const& xAnnotation
, bool /*bEdit*/)
880 mxSelectedAnnotation
= xAnnotation
;
883 void AnnotationManagerImpl::GetSelectedAnnotation( rtl::Reference
<sdr::annotation::Annotation
>& xAnnotation
)
885 xAnnotation
= mxSelectedAnnotation
;
888 void AnnotationManagerImpl::invalidateSlots()
890 SfxBindings
* pBindings
= getBindings( mrBase
);
893 pBindings
->Invalidate( SID_INSERT_POSTIT
);
894 pBindings
->Invalidate( SID_DELETE_POSTIT
);
895 pBindings
->Invalidate( SID_DELETEALL_POSTIT
);
896 pBindings
->Invalidate( SID_PREVIOUS_POSTIT
);
897 pBindings
->Invalidate( SID_NEXT_POSTIT
);
898 pBindings
->Invalidate( SID_UNDO
);
899 pBindings
->Invalidate( SID_REDO
);
903 void AnnotationManagerImpl::onSelectionChanged()
905 if (!mxView
.is() || !mrBase
.GetDrawView())
908 rtl::Reference
<SdPage
> xPage
= mrBase
.GetMainViewShell()->getCurrentPage();
909 if (xPage
!= mxCurrentPage
)
911 mxCurrentPage
= std::move(xPage
);
916 void AnnotationManagerImpl::UpdateTags(bool bSynchron
)
918 SyncAnnotationObjects();
924 if (mnUpdateTagsEvent
)
925 Application::RemoveUserEvent(mnUpdateTagsEvent
);
927 UpdateTagsHdl(nullptr);
931 if (!mnUpdateTagsEvent
&& mxView
.is())
932 mnUpdateTagsEvent
= Application::PostUserEvent(LINK(this, AnnotationManagerImpl
, UpdateTagsHdl
));
936 IMPL_LINK_NOARG(AnnotationManagerImpl
, UpdateTagsHdl
, void*, void)
938 mnUpdateTagsEvent
= nullptr;
939 SyncAnnotationObjects();
941 if (mrBase
.GetDrawView())
942 static_cast<::sd::View
*>(mrBase
.GetDrawView())->updateHandles();
950 void applyAnnotationCommon(SdrObject
& rObject
, rtl::Reference
<sdr::annotation::Annotation
> const& xAnnotation
)
952 rObject
.setAsAnnotationObject(true);
953 auto& xAnnotationData
= rObject
.getAnnotationData();
954 xAnnotationData
->mpAnnotationPopup
.reset(new AnnotationPopup(xAnnotation
));
955 xAnnotationData
->mxAnnotation
= xAnnotation
;
956 rObject
.SetPrintable(false);
959 void applyAnnotationProperties(SdrObject
& rObject
, sdr::annotation::CreationInfo
const& rInfo
)
963 rObject
.SetMergedItem(XLineStyleItem(drawing::LineStyle_SOLID
));
964 rObject
.SetMergedItem(XLineColorItem(OUString(), rInfo
.maColor
));
965 sal_uInt16 nTransparence
= 100.0 - (rInfo
.maColor
.GetAlpha() / 255.0) * 100.0;
966 rObject
.SetMergedItem(XLineTransparenceItem(nTransparence
));
968 rObject
.SetMergedItem(XLineWidthItem(rInfo
.mnWidth
));
970 if (rInfo
.mbFillColor
)
972 rObject
.SetMergedItem(XFillStyleItem(drawing::FillStyle_SOLID
));
973 rObject
.SetMergedItem(XFillColorItem(OUString(), rInfo
.maFillColor
));
974 sal_uInt16 nTransparence
= 100.0 - (rInfo
.maFillColor
.GetAlpha() / 255.0) * 100.0;
975 rObject
.SetMergedItem(XFillTransparenceItem(nTransparence
));
981 void AnnotationManagerImpl::SyncAnnotationObjects()
983 if (!mxCurrentPage
.is() || !mpDoc
)
986 sd::DrawDocShell
* pDocShell
= dynamic_cast<sd::DrawDocShell
*>(SfxObjectShell::Current());
987 sd::ViewShell
* pViewShell
= pDocShell
? pDocShell
->GetViewShell() : nullptr;
991 pViewShell
= mrBase
.GetMainViewShell().get();
996 auto* pView
= pViewShell
->GetView();
1000 if (!pView
->GetSdrPageView())
1003 auto& rModel
= pView
->getSdrModelFromSdrView();
1005 sal_Int32 nIndex
= 1;
1006 bool bAnnotatonInserted
= false;
1007 for (auto const& xAnnotation
: mxCurrentPage
->getAnnotations())
1009 SdrObject
* pObject
= xAnnotation
->findAnnotationObject();
1017 if (!bAnnotatonInserted
&& mpDoc
->IsUndoEnabled())
1018 mpDoc
->BegUndo(SdResId(STR_ANNOTATION_UNDO_INSERT
));
1020 bAnnotatonInserted
= true;
1022 auto const& rInfo
= xAnnotation
->getCreationInfo();
1024 auto aRealPoint2D
= xAnnotation
->getPosition();
1025 Point
aPosition(::tools::Long(aRealPoint2D
.X
* 100.0), ::tools::Long(aRealPoint2D
.Y
* 100.0));
1027 auto aRealSize2D
= xAnnotation
->getSize();
1028 Size
aSize(::tools::Long(aRealSize2D
.Width
* 100.0), ::tools::Long(aRealSize2D
.Height
* 100.0));
1029 // If the size is not set, set it to a default value so it is non-zero
1030 if (aSize
.getWidth() == 0 || aSize
.getHeight() == 0)
1031 aSize
= Size(500, 500);
1033 ::tools::Rectangle
aRectangle(aPosition
, aSize
);
1035 rtl::Reference
<SdrObject
> pNewObject
;
1037 if (rInfo
.meType
== sdr::annotation::AnnotationType::None
)
1039 sal_uInt16 nAuthorIndex
= mpDoc
->GetAnnotationAuthorIndex(xAnnotation
->getAuthor());
1041 sdr::annotation::AnnotationViewData aAnnotationViewData
1044 .nAuthorIndex
= nAuthorIndex
1047 rtl::Reference
<sdr::annotation::AnnotationObject
> pAnnotationObject
= new sdr::annotation::AnnotationObject(rModel
, aRectangle
, aAnnotationViewData
);
1048 pNewObject
= pAnnotationObject
;
1050 applyAnnotationCommon(*pNewObject
, xAnnotation
);
1052 pAnnotationObject
->ApplyAnnotationName();
1054 else if (rInfo
.meType
== sdr::annotation::AnnotationType::FreeText
)
1056 rtl::Reference
<SdrRectObj
> pRectangleObject
= new SdrRectObj(rModel
, SdrObjKind::Text
, aRectangle
);
1057 pNewObject
= pRectangleObject
;
1059 applyAnnotationCommon(*pNewObject
, xAnnotation
);
1060 applyAnnotationProperties(*pNewObject
, rInfo
);
1062 OUString aString
= xAnnotation
->getTextRange()->getString();
1063 pRectangleObject
->SetText(aString
);
1065 else if (rInfo
.meType
== sdr::annotation::AnnotationType::Square
)
1067 pNewObject
= new SdrRectObj(rModel
, aRectangle
);
1069 applyAnnotationCommon(*pNewObject
, xAnnotation
);
1070 applyAnnotationProperties(*pNewObject
, rInfo
);
1072 else if (rInfo
.meType
== sdr::annotation::AnnotationType::Circle
)
1074 pNewObject
= new SdrCircObj(rModel
, SdrCircKind::Full
, aRectangle
);
1076 applyAnnotationCommon(*pNewObject
, xAnnotation
);
1077 applyAnnotationProperties(*pNewObject
, rInfo
);
1079 else if (rInfo
.meType
== sdr::annotation::AnnotationType::Stamp
)
1081 rtl::Reference
<SdrGrafObj
> pGrafObject
= new SdrGrafObj(rModel
, Graphic(rInfo
.maBitmapEx
), aRectangle
);
1082 pNewObject
= pGrafObject
;
1084 applyAnnotationCommon(*pNewObject
, xAnnotation
);
1086 pGrafObject
->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE
));
1087 pGrafObject
->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE
));
1091 SdrObjKind ekind
= SdrObjKind::Polygon
;
1093 switch (rInfo
.meType
)
1095 case sdr::annotation::AnnotationType::Polygon
:
1096 ekind
= SdrObjKind::Polygon
;
1098 case sdr::annotation::AnnotationType::Line
:
1099 ekind
= SdrObjKind::PolyLine
;
1101 case sdr::annotation::AnnotationType::Ink
:
1102 ekind
= SdrObjKind::FreehandLine
;
1108 basegfx::B2DPolyPolygon aPolyPolygon
;
1109 for (auto const& rPolygon
: rInfo
.maPolygons
)
1110 aPolyPolygon
.append(rPolygon
);
1112 pNewObject
= new SdrPathObj(rModel
, ekind
, std::move(aPolyPolygon
));
1114 applyAnnotationCommon(*pNewObject
, xAnnotation
);
1115 applyAnnotationProperties(*pNewObject
, rInfo
);
1118 pNewObject
->SetRelativePos(aRectangle
.TopLeft());
1120 pView
->InsertObjectAtView(pNewObject
.get(), *pView
->GetSdrPageView());
1125 if (bAnnotatonInserted
&& mpDoc
->IsUndoEnabled())
1129 void AnnotationManagerImpl::addListener()
1131 Link
<tools::EventMultiplexerEvent
&,void> aLink( LINK(this,AnnotationManagerImpl
,EventMultiplexerListener
) );
1132 mrBase
.GetEventMultiplexer()->AddEventListener(aLink
);
1135 void AnnotationManagerImpl::removeListener()
1137 Link
<tools::EventMultiplexerEvent
&,void> aLink( LINK(this,AnnotationManagerImpl
,EventMultiplexerListener
) );
1138 mrBase
.GetEventMultiplexer()->RemoveEventListener( aLink
);
1141 IMPL_LINK(AnnotationManagerImpl
,EventMultiplexerListener
,
1142 tools::EventMultiplexerEvent
&, rEvent
, void)
1144 switch (rEvent
.meEventId
)
1146 case EventMultiplexerEventId::CurrentPageChanged
:
1147 case EventMultiplexerEventId::EditViewSelection
:
1148 onSelectionChanged();
1151 case EventMultiplexerEventId::MainViewRemoved
:
1153 onSelectionChanged();
1156 case EventMultiplexerEventId::MainViewAdded
:
1157 mxView
= mrBase
.GetDrawController();
1158 onSelectionChanged();
1165 // TODO: Update colors on notification via DrawViewShell::ConfigurationChanged()
1166 Color
AnnotationManagerImpl::GetColor(sal_uInt16 aAuthorIndex
)
1168 if (!Application::GetSettings().GetStyleSettings().GetHighContrastMode())
1170 svtools::ColorConfig aColorConfig
;
1171 switch (aAuthorIndex
% 9)
1173 case 0: return aColorConfig
.GetColorValue(svtools::AUTHOR1
).nColor
;
1174 case 1: return aColorConfig
.GetColorValue(svtools::AUTHOR2
).nColor
;
1175 case 2: return aColorConfig
.GetColorValue(svtools::AUTHOR3
).nColor
;
1176 case 3: return aColorConfig
.GetColorValue(svtools::AUTHOR4
).nColor
;
1177 case 4: return aColorConfig
.GetColorValue(svtools::AUTHOR5
).nColor
;
1178 case 5: return aColorConfig
.GetColorValue(svtools::AUTHOR6
).nColor
;
1179 case 6: return aColorConfig
.GetColorValue(svtools::AUTHOR7
).nColor
;
1180 case 7: return aColorConfig
.GetColorValue(svtools::AUTHOR8
).nColor
;
1181 case 8: return aColorConfig
.GetColorValue(svtools::AUTHOR9
).nColor
;
1188 Color
AnnotationManagerImpl::GetColorLight(sal_uInt16 aAuthorIndex
)
1190 Color aColor
= GetColor(aAuthorIndex
);
1191 if (MiscSettings::GetUseDarkMode())
1192 aColor
.IncreaseLuminance(30);
1194 aColor
.DecreaseLuminance(30);
1198 Color
AnnotationManagerImpl::GetColorDark(sal_uInt16 aAuthorIndex
)
1200 Color aColor
= GetColor(aAuthorIndex
);;
1201 if (MiscSettings::GetUseDarkMode())
1202 aColor
.DecreaseLuminance(80);
1204 aColor
.IncreaseLuminance(80);
1208 SdPage
* AnnotationManagerImpl::GetNextPage( SdPage
const * pPage
, bool bForward
)
1210 if( pPage
== nullptr )
1213 return mpDoc
->GetSdPage(0, PageKind::Standard
); // first page
1215 return mpDoc
->GetMasterSdPage( mpDoc
->GetMasterSdPageCount(PageKind::Standard
) - 1, PageKind::Standard
); // last page
1218 sal_uInt16 nPageNum
= static_cast<sal_uInt16
>((pPage
->GetPageNum() - 1) >> 1);
1220 // first all non master pages
1221 if( !pPage
->IsMasterPage() )
1225 if( nPageNum
>= mpDoc
->GetSdPageCount(PageKind::Standard
)-1 )
1227 // we reached end of draw pages, start with master pages (skip handout master for draw)
1228 return mpDoc
->GetMasterSdPage( (mpDoc
->GetDocumentType() == DocumentType::Impress
) ? 0 : 1, PageKind::Standard
);
1235 return nullptr; // we are already on the first draw page, finished
1239 return mpDoc
->GetSdPage(nPageNum
, PageKind::Standard
);
1245 if( nPageNum
>= mpDoc
->GetMasterSdPageCount(PageKind::Standard
)-1 )
1247 return nullptr; // we reached the end, there is nothing more to see here
1253 if( nPageNum
== (mpDoc
->GetDocumentType() == DocumentType::Impress
? 0 : 1) )
1255 // we reached beginning of master pages, start with end if pages
1256 return mpDoc
->GetSdPage( mpDoc
->GetSdPageCount(PageKind::Standard
)-1, PageKind::Standard
);
1261 return mpDoc
->GetMasterSdPage(nPageNum
,PageKind::Standard
);
1265 SdPage
* AnnotationManagerImpl::GetCurrentPage()
1267 if (auto pMainViewShell
= mrBase
.GetMainViewShell().get())
1268 return pMainViewShell
->getCurrentPage();
1272 AnnotationManager::AnnotationManager( ViewShellBase
& rViewShellBase
)
1273 : mxImpl( new AnnotationManagerImpl( rViewShellBase
) )
1278 AnnotationManager::~AnnotationManager()
1283 void AnnotationManager::ExecuteAnnotation(SfxRequest
const & rRequest
)
1285 mxImpl
->ExecuteAnnotation( rRequest
);
1288 void AnnotationManager::GetAnnotationState(SfxItemSet
& rItemSet
)
1290 mxImpl
->GetAnnotationState(rItemSet
);
1293 void AnnotationManager::SelectAnnotation(rtl::Reference
<sdr::annotation::Annotation
> const& xAnnotation
)
1295 mxImpl
->SelectAnnotation(xAnnotation
);
1300 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */