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>
33 #include <sal/macros.h>
34 #include <svl/itempool.hxx>
35 #include <svl/intitem.hxx>
36 #include <unotools/localedatawrapper.hxx>
37 #include <unotools/useroptions.hxx>
38 #include <unotools/syslocale.hxx>
39 #include <unotools/saveopt.hxx>
41 #include <tools/datetime.hxx>
42 #include <tools/UnitConversion.hxx>
43 #include <comphelper/diagnose_ex.hxx>
45 #include <sfx2/viewfrm.hxx>
46 #include <sfx2/bindings.hxx>
47 #include <sfx2/request.hxx>
48 #include <sfx2/dispatch.hxx>
50 #include <editeng/editeng.hxx>
51 #include <editeng/eeitem.hxx>
52 #include <editeng/fontitem.hxx>
53 #include <editeng/fhgtitem.hxx>
54 #include <editeng/outlobj.hxx>
55 #include <editeng/postitem.hxx>
57 #include <svx/postattr.hxx>
59 #include <annotationmanager.hxx>
60 #include "annotationmanagerimpl.hxx"
61 #include "annotationwindow.hxx"
62 #include <strings.hrc>
64 #include <Annotation.hxx>
65 #include <DrawDocShell.hxx>
66 #include <DrawViewShell.hxx>
67 #include <sdresid.hxx>
68 #include <EventMultiplexer.hxx>
69 #include <ViewShellBase.hxx>
71 #include <drawdoc.hxx>
72 #include <textapi.hxx>
73 #include <optsitem.hxx>
78 using namespace ::com::sun::star
;
79 using namespace ::com::sun::star::uno
;
80 using namespace ::com::sun::star::drawing
;
81 using namespace ::com::sun::star::document
;
82 using namespace ::com::sun::star::geometry
;
83 using namespace ::com::sun::star::container
;
84 using namespace ::com::sun::star::beans
;
85 using namespace ::com::sun::star::text
;
86 using namespace ::com::sun::star::view
;
87 using namespace ::com::sun::star::style
;
88 using namespace ::com::sun::star::frame
;
89 using namespace ::com::sun::star::lang
;
90 using namespace ::com::sun::star::ui
;
91 using namespace ::com::sun::star::task
;
92 using namespace ::com::sun::star::office
;
96 SfxItemPool
* GetAnnotationPool()
98 static rtl::Reference
<SfxItemPool
> s_pAnnotationPool
;
99 if( !s_pAnnotationPool
)
101 s_pAnnotationPool
= EditEngine::CreatePool();
102 s_pAnnotationPool
->SetPoolDefaultItem(SvxFontHeightItem(423,100,EE_CHAR_FONTHEIGHT
));
104 vcl::Font
aAppFont( Application::GetSettings().GetStyleSettings().GetAppFont() );
105 s_pAnnotationPool
->SetPoolDefaultItem(SvxFontItem(aAppFont
.GetFamilyType(),aAppFont
.GetFamilyName(),"",PITCH_DONTKNOW
,RTL_TEXTENCODING_DONTKNOW
,EE_CHAR_FONTINFO
));
108 return s_pAnnotationPool
.get();
111 static SfxBindings
* getBindings( ViewShellBase
const & rBase
)
113 if( rBase
.GetMainViewShell() && rBase
.GetMainViewShell()->GetViewFrame() )
114 return &rBase
.GetMainViewShell()->GetViewFrame()->GetBindings();
119 static SfxDispatcher
* getDispatcher( ViewShellBase
const & rBase
)
121 if( rBase
.GetMainViewShell() && rBase
.GetMainViewShell()->GetViewFrame() )
122 return rBase
.GetMainViewShell()->GetViewFrame()->GetDispatcher();
127 css::util::DateTime
getCurrentDateTime()
129 DateTime
aCurrentDate( DateTime::SYSTEM
);
130 return css::util::DateTime( 0, aCurrentDate
.GetSec(),
131 aCurrentDate
.GetMin(), aCurrentDate
.GetHour(),
132 aCurrentDate
.GetDay(), aCurrentDate
.GetMonth(),
133 aCurrentDate
.GetYear(), false );
136 OUString
getAnnotationDateTimeString( const Reference
< XAnnotation
>& xAnnotation
)
139 if( xAnnotation
.is() )
141 const SvtSysLocale aSysLocale
;
142 const LocaleDataWrapper
& rLocalData
= aSysLocale
.GetLocaleData();
144 css::util::DateTime
aDateTime( xAnnotation
->getDateTime() );
146 Date
aSysDate( Date::SYSTEM
);
147 Date
aDate( aDateTime
.Day
, aDateTime
.Month
, aDateTime
.Year
);
149 sRet
= SdResId(STR_ANNOTATION_TODAY
);
150 else if (aDate
== (aSysDate
-1))
151 sRet
= SdResId(STR_ANNOTATION_YESTERDAY
);
152 else if (aDate
.IsValidAndGregorian() )
153 sRet
= rLocalData
.getDate(aDate
);
155 ::tools::Time
aTime( aDateTime
);
156 if(aTime
.GetTime() != 0)
157 sRet
+= " " + rLocalData
.getTime( aTime
,false );
162 AnnotationManagerImpl::AnnotationManagerImpl( ViewShellBase
& rViewShellBase
)
163 : mrBase( rViewShellBase
)
164 , mpDoc( rViewShellBase
.GetDocument() )
165 , mbShowAnnotations( true )
166 , mnUpdateTagsEvent( nullptr )
168 SdOptions
* pOptions
= SD_MOD()->GetSdOptions(mpDoc
->GetDocumentType());
170 mbShowAnnotations
= pOptions
->IsShowComments();
173 void AnnotationManagerImpl::init()
175 // get current controller and initialize listeners
179 mxView
.set(mrBase
.GetController(), UNO_QUERY
);
183 TOOLS_WARN_EXCEPTION( "sd", "sd::AnnotationManagerImpl::AnnotationManagerImpl()" );
188 Reference
<XEventBroadcaster
> xModel (mrBase
.GetDocShell()->GetModel(), UNO_QUERY_THROW
);
189 Reference
<XEventListener
> xListener( this );
190 xModel
->addEventListener( xListener
);
197 // WeakComponentImplHelper
198 void AnnotationManagerImpl::disposing (std::unique_lock
<std::mutex
>&)
202 Reference
<XEventBroadcaster
> xModel (mrBase
.GetDocShell()->GetModel(), UNO_QUERY_THROW
);
203 Reference
<XEventListener
> xListener( this );
204 xModel
->removeEventListener( xListener
);
213 if( mnUpdateTagsEvent
)
215 Application::RemoveUserEvent( mnUpdateTagsEvent
);
216 mnUpdateTagsEvent
= nullptr;
220 mxCurrentPage
.clear();
224 void SAL_CALL
AnnotationManagerImpl::notifyEvent( const css::document::EventObject
& aEvent
)
226 if( !(aEvent
.EventName
== "OnAnnotationInserted" || aEvent
.EventName
== "OnAnnotationRemoved" || aEvent
.EventName
== "OnAnnotationChanged") )
229 // AnnotationInsertion and modification is not handled here because when
230 // a new annotation is inserted, it consists of OnAnnotationInserted
231 // followed by a chain of OnAnnotationChanged (called for setting each
232 // of the annotation attributes - author, text etc.). This is not what a
233 // LOK client wants. So only handle removal here as annotation removal
234 // consists of only one event - 'OnAnnotationRemoved'
235 if ( aEvent
.EventName
== "OnAnnotationRemoved" )
237 Reference
< XAnnotation
> xAnnotation( aEvent
.Source
, uno::UNO_QUERY
);
238 if ( xAnnotation
.is() )
240 LOKCommentNotify(CommentNotificationType::Remove
, &mrBase
, xAnnotation
);
247 void SAL_CALL
AnnotationManagerImpl::disposing( const css::lang::EventObject
& /*Source*/ )
251 Reference
<XAnnotation
> AnnotationManagerImpl::GetAnnotationById(sal_uInt32 nAnnotationId
)
253 SdPage
* pPage
= nullptr;
256 pPage
= GetNextPage(pPage
, true);
257 if( pPage
&& !pPage
->getAnnotations().empty() )
259 AnnotationVector
aAnnotations(pPage
->getAnnotations());
260 auto iter
= std::find_if(aAnnotations
.begin(), aAnnotations
.end(),
261 [nAnnotationId
](const Reference
<XAnnotation
>& xAnnotation
) {
262 return sd::getAnnotationId(xAnnotation
) == nAnnotationId
;
264 if (iter
!= aAnnotations
.end())
269 Reference
<XAnnotation
> xAnnotationEmpty
;
270 return xAnnotationEmpty
;
273 void AnnotationManagerImpl::ShowAnnotations( bool bShow
)
275 // enforce show annotations if a new annotation is inserted
276 if( mbShowAnnotations
!= bShow
)
278 mbShowAnnotations
= bShow
;
280 SdOptions
* pOptions
= SD_MOD()->GetSdOptions(mpDoc
->GetDocumentType());
282 pOptions
->SetShowComments( mbShowAnnotations
);
288 void AnnotationManagerImpl::ExecuteAnnotation(SfxRequest
const & rReq
)
290 switch( rReq
.GetSlot() )
292 case SID_INSERT_POSTIT
:
293 ExecuteInsertAnnotation( rReq
);
295 case SID_DELETE_POSTIT
:
296 case SID_DELETEALL_POSTIT
:
297 case SID_DELETEALLBYAUTHOR_POSTIT
:
298 ExecuteDeleteAnnotation( rReq
);
300 case SID_EDIT_POSTIT
:
301 ExecuteEditAnnotation( rReq
);
303 case SID_PREVIOUS_POSTIT
:
304 case SID_NEXT_POSTIT
:
305 SelectNextAnnotation( rReq
.GetSlot() == SID_NEXT_POSTIT
);
307 case SID_REPLYTO_POSTIT
:
308 ExecuteReplyToAnnotation( rReq
);
310 case SID_TOGGLE_NOTES
:
311 ShowAnnotations( !mbShowAnnotations
);
316 void AnnotationManagerImpl::ExecuteInsertAnnotation(SfxRequest
const & rReq
)
318 if (!comphelper::LibreOfficeKit::isActive() || comphelper::LibreOfficeKit::isTiledAnnotations())
319 ShowAnnotations(true);
321 const SfxItemSet
* pArgs
= rReq
.GetArgs();
325 if (const SfxStringItem
* pPoolItem
= pArgs
->GetItemIfSet(SID_ATTR_POSTIT_TEXT
))
327 sText
= pPoolItem
->GetValue();
331 InsertAnnotation(sText
);
334 void AnnotationManagerImpl::ExecuteDeleteAnnotation(SfxRequest
const & rReq
)
336 ShowAnnotations( true );
338 const SfxItemSet
* pArgs
= rReq
.GetArgs();
340 switch( rReq
.GetSlot() )
342 case SID_DELETEALL_POSTIT
:
343 DeleteAllAnnotations();
345 case SID_DELETEALLBYAUTHOR_POSTIT
:
348 const SfxPoolItem
* pPoolItem
= nullptr;
349 if( SfxItemState::SET
== pArgs
->GetItemState( SID_DELETEALLBYAUTHOR_POSTIT
, true, &pPoolItem
) )
351 OUString
sAuthor( static_cast<const SfxStringItem
*>( pPoolItem
)->GetValue() );
352 DeleteAnnotationsByAuthor( sAuthor
);
356 case SID_DELETE_POSTIT
:
358 Reference
< XAnnotation
> xAnnotation
;
362 const SfxPoolItem
* pPoolItem
= nullptr;
363 if( SfxItemState::SET
== pArgs
->GetItemState( SID_DELETE_POSTIT
, true, &pPoolItem
) )
364 static_cast<const SfxUnoAnyItem
*>(pPoolItem
)->GetValue() >>= xAnnotation
;
365 if( SfxItemState::SET
== pArgs
->GetItemState( SID_ATTR_POSTIT_ID
, true, &pPoolItem
) )
366 nId
= static_cast<const SvxPostItIdItem
*>(pPoolItem
)->GetValue().toUInt32();
370 xAnnotation
= GetAnnotationById(nId
);
371 else if( !xAnnotation
.is() )
372 GetSelectedAnnotation( xAnnotation
);
374 DeleteAnnotation( xAnnotation
);
382 void AnnotationManagerImpl::ExecuteEditAnnotation(SfxRequest
const & rReq
)
384 const SfxItemSet
* pArgs
= rReq
.GetArgs();
385 Reference
< XAnnotation
> xAnnotation
;
387 sal_Int32 nPositionX
= -1;
388 sal_Int32 nPositionY
= -1;
393 if (mpDoc
->IsUndoEnabled())
394 mpDoc
->BegUndo(SdResId(STR_ANNOTATION_UNDO_EDIT
));
396 if (const SvxPostItIdItem
* pPoolItem
= pArgs
->GetItemIfSet(SID_ATTR_POSTIT_ID
))
398 sal_uInt32 nId
= pPoolItem
->GetValue().toUInt32();
399 xAnnotation
= GetAnnotationById(nId
);
401 if (const SfxStringItem
* pPoolItem
= pArgs
->GetItemIfSet(SID_ATTR_POSTIT_TEXT
))
402 sText
= pPoolItem
->GetValue();
404 if (const SfxInt32Item
* pPoolItem
= pArgs
->GetItemIfSet(SID_ATTR_POSTIT_POSITION_X
))
405 nPositionX
= pPoolItem
->GetValue();
407 if (const SfxInt32Item
* pPoolItem
= pArgs
->GetItemIfSet(SID_ATTR_POSTIT_POSITION_Y
))
408 nPositionY
= pPoolItem
->GetValue();
410 if (xAnnotation
.is())
412 CreateChangeUndo(xAnnotation
);
414 if (nPositionX
>= 0 && nPositionY
>= 0)
416 double fX
= convertTwipToMm100(nPositionX
) / 100.0;
417 double fY
= convertTwipToMm100(nPositionY
) / 100.0;
418 xAnnotation
->setPosition({fX
, fY
});
421 if (!sText
.isEmpty())
423 // TODO: Not allow other authors to change others' comments ?
424 Reference
<XText
> xText(xAnnotation
->getTextRange());
425 xText
->setString(sText
);
428 LOKCommentNotifyAll(CommentNotificationType::Modify
, xAnnotation
);
431 if (mpDoc
->IsUndoEnabled())
437 void AnnotationManagerImpl::InsertAnnotation(const OUString
& rText
)
439 SdPage
* pPage
= GetCurrentPage();
443 if( mpDoc
->IsUndoEnabled() )
444 mpDoc
->BegUndo( SdResId( STR_ANNOTATION_UNDO_INSERT
) );
446 // find free space for new annotation
449 AnnotationVector
aAnnotations( pPage
->getAnnotations() );
450 if( !aAnnotations
.empty() )
452 const int page_width
= pPage
->GetSize().Width();
453 const int width
= 1000;
454 const int height
= 800;
455 ::tools::Rectangle aTagRect
;
459 ::tools::Rectangle
aNewRect( x
, y
, x
+ width
- 1, y
+ height
- 1 );
462 for( const auto& rxAnnotation
: aAnnotations
)
464 RealPoint2D
aPoint( rxAnnotation
->getPosition() );
465 aTagRect
.SetLeft( sal::static_int_cast
< ::tools::Long
>( aPoint
.X
* 100.0 ) );
466 aTagRect
.SetTop( sal::static_int_cast
< ::tools::Long
>( aPoint
.Y
* 100.0 ) );
467 aTagRect
.SetRight( aTagRect
.Left() + width
- 1 );
468 aTagRect
.SetBottom( aTagRect
.Top() + height
- 1 );
470 if( aNewRect
.Overlaps( aTagRect
) )
493 Reference
< XAnnotation
> xAnnotation
;
494 pPage
->createAnnotation( xAnnotation
);
497 if (comphelper::LibreOfficeKit::isActive())
498 sAuthor
= mrBase
.GetMainViewShell()->GetView()->GetAuthor();
501 SvtUserOptions aUserOptions
;
502 sAuthor
= aUserOptions
.GetFullName();
503 xAnnotation
->setInitials( aUserOptions
.GetID() );
506 if (!rText
.isEmpty())
508 Reference
<XText
> xText(xAnnotation
->getTextRange());
509 xText
->setString(rText
);
512 // set current author to new annotation
513 xAnnotation
->setAuthor( sAuthor
);
514 // set current time to new annotation
515 xAnnotation
->setDateTime( getCurrentDateTime() );
518 RealPoint2D
aPos( static_cast<double>(x
) / 100.0, static_cast<double>(y
) / 100.0 );
519 xAnnotation
->setPosition( aPos
);
521 if( mpDoc
->IsUndoEnabled() )
524 // Tell our LOK clients about new comment added
525 LOKCommentNotifyAll(CommentNotificationType::Add
, xAnnotation
);
528 SelectAnnotation( xAnnotation
, true );
531 void AnnotationManagerImpl::ExecuteReplyToAnnotation( SfxRequest
const & rReq
)
533 Reference
< XAnnotation
> xAnnotation
;
534 const SfxItemSet
* pArgs
= rReq
.GetArgs();
538 const SfxPoolItem
* pPoolItem
= nullptr;
539 if( SfxItemState::SET
== pArgs
->GetItemState( SID_ATTR_POSTIT_ID
, true, &pPoolItem
) )
541 sal_uInt32 nReplyId
= 0; // Id of the comment to reply to
542 nReplyId
= static_cast<const SvxPostItIdItem
*>(pPoolItem
)->GetValue().toUInt32();
543 xAnnotation
= GetAnnotationById(nReplyId
);
545 else if( SfxItemState::SET
== pArgs
->GetItemState( rReq
.GetSlot(), true, &pPoolItem
) )
546 static_cast<const SfxUnoAnyItem
*>( pPoolItem
)->GetValue() >>= xAnnotation
;
548 if( SfxItemState::SET
== pArgs
->GetItemState( SID_ATTR_POSTIT_TEXT
, true, &pPoolItem
) )
549 sReplyText
= static_cast<const SvxPostItTextItem
*>( pPoolItem
)->GetValue();
552 TextApiObject
* pTextApi
= getTextApiObject( xAnnotation
);
556 ::Outliner
aOutliner( GetAnnotationPool(),OutlinerMode::TextObject
);
558 SdDrawDocument::SetCalcFieldValueHdl( &aOutliner
);
559 aOutliner
.SetUpdateLayout( true );
561 OUString
aStr(SdResId(STR_ANNOTATION_REPLY
));
562 OUString
sAuthor( xAnnotation
->getAuthor() );
563 if( sAuthor
.isEmpty() )
564 sAuthor
= SdResId( STR_ANNOTATION_NOAUTHOR
);
566 aStr
= aStr
.replaceFirst("%1", sAuthor
) +
567 " (" + getAnnotationDateTimeString( xAnnotation
) + "): \"";
569 OUString
sQuote( pTextApi
->GetText() );
571 if( sQuote
.isEmpty() )
573 aStr
+= sQuote
+ "\"\n";
575 for( sal_Int32 nIdx
= 0; nIdx
>= 0; )
576 aOutliner
.Insert( aStr
.getToken( 0, '\n', nIdx
), EE_PARA_APPEND
, -1 );
578 if( aOutliner
.GetParagraphCount() > 1 )
580 SfxItemSet
aAnswerSet( aOutliner
.GetEmptyItemSet() );
581 aAnswerSet
.Put(SvxPostureItem(ITALIC_NORMAL
,EE_CHAR_ITALIC
));
584 aSel
.nEndPara
= aOutliner
.GetParagraphCount()-2;
585 aSel
.nEndPos
= aOutliner
.GetText( aOutliner
.GetParagraph( aSel
.nEndPara
) ).getLength();
587 aOutliner
.QuickSetAttribs( aAnswerSet
, aSel
);
590 if (!sReplyText
.isEmpty())
591 aOutliner
.Insert(sReplyText
);
593 std::optional
< OutlinerParaObject
> pOPO( aOutliner
.CreateParaObject() );
594 pTextApi
->SetText(*pOPO
);
596 OUString sReplyAuthor
;
597 if (comphelper::LibreOfficeKit::isActive())
598 sReplyAuthor
= mrBase
.GetMainViewShell()->GetView()->GetAuthor();
601 SvtUserOptions aUserOptions
;
602 sReplyAuthor
= aUserOptions
.GetFullName();
603 xAnnotation
->setInitials( aUserOptions
.GetID() );
606 xAnnotation
->setAuthor( sReplyAuthor
);
607 // set current time to reply
608 xAnnotation
->setDateTime( getCurrentDateTime() );
610 // Tell our LOK clients about this (comment modification)
611 LOKCommentNotifyAll(CommentNotificationType::Modify
, xAnnotation
);
614 SelectAnnotation( xAnnotation
, true );
617 void AnnotationManagerImpl::DeleteAnnotation( const Reference
< XAnnotation
>& xAnnotation
)
619 SdPage
* pPage
= GetCurrentPage();
621 if( xAnnotation
.is() && pPage
)
623 if( mpDoc
->IsUndoEnabled() )
624 mpDoc
->BegUndo( SdResId( STR_ANNOTATION_UNDO_DELETE
) );
626 pPage
->removeAnnotation( xAnnotation
);
628 if( mpDoc
->IsUndoEnabled() )
635 void AnnotationManagerImpl::DeleteAnnotationsByAuthor( std::u16string_view sAuthor
)
637 if( mpDoc
->IsUndoEnabled() )
638 mpDoc
->BegUndo( SdResId( STR_ANNOTATION_UNDO_DELETE
) );
640 SdPage
* pPage
= nullptr;
643 pPage
= GetNextPage( pPage
, true );
645 if( pPage
&& !pPage
->getAnnotations().empty() )
647 AnnotationVector
aAnnotations( pPage
->getAnnotations() );
648 for( Reference
< XAnnotation
>& xAnnotation
: aAnnotations
)
650 if( xAnnotation
->getAuthor() == sAuthor
)
652 if( mxSelectedAnnotation
== xAnnotation
)
653 mxSelectedAnnotation
.clear();
654 pPage
->removeAnnotation( xAnnotation
);
660 if( mpDoc
->IsUndoEnabled() )
664 void AnnotationManagerImpl::DeleteAllAnnotations()
666 if( mpDoc
->IsUndoEnabled() )
667 mpDoc
->BegUndo( SdResId( STR_ANNOTATION_UNDO_DELETE
) );
669 SdPage
* pPage
= nullptr;
672 pPage
= GetNextPage( pPage
, true );
674 if( pPage
&& !pPage
->getAnnotations().empty() )
677 AnnotationVector
aAnnotations( pPage
->getAnnotations() );
678 for( const auto& rxAnnotation
: aAnnotations
)
680 pPage
->removeAnnotation( rxAnnotation
);
686 mxSelectedAnnotation
.clear();
688 if( mpDoc
->IsUndoEnabled() )
692 void AnnotationManagerImpl::GetAnnotationState(SfxItemSet
& rSet
)
694 SdPage
* pCurrentPage
= GetCurrentPage();
696 const bool bReadOnly
= mrBase
.GetDocShell()->IsReadOnly();
697 const bool bWrongPageKind
= (pCurrentPage
== nullptr) || (pCurrentPage
->GetPageKind() != PageKind::Standard
);
699 const SvtSaveOptions::ODFSaneDefaultVersion
nCurrentODFVersion( GetODFSaneDefaultVersion() );
701 if (bReadOnly
|| bWrongPageKind
|| (nCurrentODFVersion
<= SvtSaveOptions::ODFSVER_012
))
702 rSet
.DisableItem( SID_INSERT_POSTIT
);
704 rSet
.Put(SfxBoolItem(SID_TOGGLE_NOTES
, mbShowAnnotations
));
706 Reference
< XAnnotation
> xAnnotation
;
707 GetSelectedAnnotation( xAnnotation
);
709 // Don't disable these slot in case of LOK, as postit doesn't need to
710 // selected before doing an operation on it in LOK
711 if( (!xAnnotation
.is() && !comphelper::LibreOfficeKit::isActive()) || bReadOnly
)
713 rSet
.DisableItem( SID_DELETE_POSTIT
);
714 rSet
.DisableItem( SID_EDIT_POSTIT
);
717 SdPage
* pPage
= nullptr;
719 bool bHasAnnotations
= false;
722 pPage
= GetNextPage( pPage
, true );
724 if( pPage
&& !pPage
->getAnnotations().empty() )
725 bHasAnnotations
= true;
727 while( pPage
&& !bHasAnnotations
);
729 if( !bHasAnnotations
|| bReadOnly
)
731 rSet
.DisableItem( SID_DELETEALL_POSTIT
);
734 if( bWrongPageKind
|| !bHasAnnotations
)
736 rSet
.DisableItem( SID_PREVIOUS_POSTIT
);
737 rSet
.DisableItem( SID_NEXT_POSTIT
);
741 void AnnotationManagerImpl::SelectNextAnnotation(bool bForward
)
743 ShowAnnotations( true );
745 Reference
< XAnnotation
> xCurrent
;
746 GetSelectedAnnotation( xCurrent
);
747 SdPage
* pPage
= GetCurrentPage();
751 AnnotationVector
aAnnotations( pPage
->getAnnotations() );
757 auto iter
= std::find(aAnnotations
.begin(), aAnnotations
.end(), xCurrent
);
758 if (iter
!= aAnnotations
.end())
761 if( iter
!= aAnnotations
.end() )
763 SelectAnnotation( *iter
);
768 else if( !aAnnotations
.empty() )
770 SelectAnnotation( *(aAnnotations
.begin()) );
778 auto iter
= std::find(aAnnotations
.begin(), aAnnotations
.end(), xCurrent
);
779 if (iter
!= aAnnotations
.end() && iter
!= aAnnotations
.begin())
782 SelectAnnotation( *iter
);
786 else if( !aAnnotations
.empty() )
788 AnnotationVector::iterator
iter( aAnnotations
.end() );
789 SelectAnnotation( *(--iter
) );
794 mxSelectedAnnotation
.clear();
799 pPage
= GetNextPage( pPage
, bForward
);
801 if( pPage
&& !pPage
->getAnnotations().empty() )
803 // switch to next/previous slide with annotations
804 std::shared_ptr
<DrawViewShell
> pDrawViewShell(std::dynamic_pointer_cast
<DrawViewShell
>(mrBase
.GetMainViewShell()));
805 if (pDrawViewShell
!= nullptr)
807 pDrawViewShell
->ChangeEditMode(pPage
->IsMasterPage() ? EditMode::MasterPage
: EditMode::Page
, false);
808 pDrawViewShell
->SwitchPage((pPage
->GetPageNum() - 1) >> 1);
810 SfxDispatcher
* pDispatcher
= getDispatcher( mrBase
);
812 pDispatcher
->Execute( bForward
? SID_NEXT_POSTIT
: SID_PREVIOUS_POSTIT
);
820 // The question text depends on the search direction.
821 bool bImpress
= mpDoc
->GetDocumentType() == DocumentType::Impress
;
822 TranslateId pStringId
;
824 pStringId
= bImpress
? STR_ANNOTATION_WRAP_FORWARD
: STR_ANNOTATION_WRAP_FORWARD_DRAW
;
826 pStringId
= bImpress
? STR_ANNOTATION_WRAP_BACKWARD
: STR_ANNOTATION_WRAP_BACKWARD_DRAW
;
828 // Pop up question box that asks the user whether to wrap around.
829 // The dialog is made modal with respect to the whole application.
830 std::unique_ptr
<weld::MessageDialog
> xQueryBox(Application::CreateMessageDialog(nullptr,
831 VclMessageType::Question
, VclButtonsType::YesNo
,
832 SdResId(pStringId
)));
833 xQueryBox
->set_default_response(RET_YES
);
834 if (xQueryBox
->run() != RET_YES
)
840 void AnnotationManagerImpl::onTagSelected( AnnotationTag
const & rTag
)
842 mxSelectedAnnotation
= rTag
.GetAnnotation();
846 void AnnotationManagerImpl::onTagDeselected( AnnotationTag
const & rTag
)
848 if( rTag
.GetAnnotation() == mxSelectedAnnotation
)
850 mxSelectedAnnotation
.clear();
855 void AnnotationManagerImpl::SelectAnnotation( const css::uno::Reference
< css::office::XAnnotation
>& xAnnotation
, bool bEdit
/* = sal_False */ )
857 mxSelectedAnnotation
= xAnnotation
;
859 auto iter
= std::find_if(maTagVector
.begin(), maTagVector
.end(),
860 [&xAnnotation
](const rtl::Reference
<AnnotationTag
>& rxTag
) { return rxTag
->GetAnnotation() == xAnnotation
; });
861 if (iter
!= maTagVector
.end())
863 SmartTagReference
xTag( *iter
);
864 mrBase
.GetMainViewShell()->GetView()->getSmartTags().select( xTag
);
865 (*iter
)->OpenPopup( bEdit
);
869 void AnnotationManagerImpl::GetSelectedAnnotation( css::uno::Reference
< css::office::XAnnotation
>& xAnnotation
)
871 xAnnotation
= mxSelectedAnnotation
;
874 void AnnotationManagerImpl::invalidateSlots()
876 SfxBindings
* pBindings
= getBindings( mrBase
);
879 pBindings
->Invalidate( SID_INSERT_POSTIT
);
880 pBindings
->Invalidate( SID_DELETE_POSTIT
);
881 pBindings
->Invalidate( SID_DELETEALL_POSTIT
);
882 pBindings
->Invalidate( SID_PREVIOUS_POSTIT
);
883 pBindings
->Invalidate( SID_NEXT_POSTIT
);
884 pBindings
->Invalidate( SID_UNDO
);
885 pBindings
->Invalidate( SID_REDO
);
889 void AnnotationManagerImpl::onSelectionChanged()
891 if( !(mxView
.is() && mrBase
.GetDrawView()) )
896 Reference
< XAnnotationAccess
> xPage( mxView
->getCurrentPage(), UNO_QUERY
);
898 if( xPage
!= mxCurrentPage
)
900 mxCurrentPage
= xPage
;
907 TOOLS_WARN_EXCEPTION( "sd", "sd::AnnotationManagerImpl::onSelectionChanged()" );
911 void AnnotationManagerImpl::UpdateTags( bool bSynchron
)
915 if( mnUpdateTagsEvent
)
916 Application::RemoveUserEvent( mnUpdateTagsEvent
);
918 UpdateTagsHdl(nullptr);
922 if( !mnUpdateTagsEvent
&& mxView
.is() )
923 mnUpdateTagsEvent
= Application::PostUserEvent( LINK( this, AnnotationManagerImpl
, UpdateTagsHdl
) );
927 IMPL_LINK_NOARG(AnnotationManagerImpl
, UpdateTagsHdl
, void*, void)
929 mnUpdateTagsEvent
= nullptr;
932 if( mbShowAnnotations
)
935 if( mrBase
.GetDrawView() )
936 static_cast< ::sd::View
* >( mrBase
.GetDrawView() )->updateHandles();
941 void AnnotationManagerImpl::CreateTags()
943 if( !(mxCurrentPage
.is() && mpDoc
) )
946 auto xViewShell
= mrBase
.GetMainViewShell();
953 maFont
= Application::GetSettings().GetStyleSettings().GetAppFont();
955 rtl::Reference
< AnnotationTag
> xSelectedTag
;
957 Reference
< XAnnotationEnumeration
> xEnum( mxCurrentPage
->createAnnotationEnumeration() );
958 while( xEnum
->hasMoreElements() )
960 Reference
< XAnnotation
> xAnnotation( xEnum
->nextElement() );
961 Color
aColor( GetColorLight( mpDoc
->GetAnnotationAuthorIndex( xAnnotation
->getAuthor() ) ) );
962 rtl::Reference
< AnnotationTag
> xTag( new AnnotationTag( *this, *xViewShell
->GetView(), xAnnotation
, aColor
, nIndex
++, maFont
) );
963 maTagVector
.push_back(xTag
);
965 if( xAnnotation
== mxSelectedAnnotation
)
971 if( xSelectedTag
.is() )
973 SmartTagReference
xTag( xSelectedTag
);
974 mrBase
.GetMainViewShell()->GetView()->getSmartTags().select( xTag
);
978 // no tag, no selection!
979 mxSelectedAnnotation
.clear();
984 TOOLS_WARN_EXCEPTION( "sd", "sd::AnnotationManagerImpl::onSelectionChanged()" );
988 void AnnotationManagerImpl::DisposeTags()
990 for (auto& rxTag
: maTagVector
)
998 void AnnotationManagerImpl::addListener()
1000 Link
<tools::EventMultiplexerEvent
&,void> aLink( LINK(this,AnnotationManagerImpl
,EventMultiplexerListener
) );
1001 mrBase
.GetEventMultiplexer()->AddEventListener(aLink
);
1004 void AnnotationManagerImpl::removeListener()
1006 Link
<tools::EventMultiplexerEvent
&,void> aLink( LINK(this,AnnotationManagerImpl
,EventMultiplexerListener
) );
1007 mrBase
.GetEventMultiplexer()->RemoveEventListener( aLink
);
1010 IMPL_LINK(AnnotationManagerImpl
,EventMultiplexerListener
,
1011 tools::EventMultiplexerEvent
&, rEvent
, void)
1013 switch (rEvent
.meEventId
)
1015 case EventMultiplexerEventId::CurrentPageChanged
:
1016 case EventMultiplexerEventId::EditViewSelection
:
1017 onSelectionChanged();
1020 case EventMultiplexerEventId::MainViewRemoved
:
1022 onSelectionChanged();
1025 case EventMultiplexerEventId::MainViewAdded
:
1026 mxView
.set( mrBase
.GetController(), UNO_QUERY
);
1027 onSelectionChanged();
1034 void AnnotationManagerImpl::ExecuteAnnotationTagContextMenu(const Reference
<XAnnotation
>& xAnnotation
, weld::Widget
* pParent
, const ::tools::Rectangle
& rContextRect
)
1036 SfxDispatcher
* pDispatcher( getDispatcher( mrBase
) );
1040 const bool bReadOnly
= mrBase
.GetDocShell()->IsReadOnly();
1045 std::unique_ptr
<weld::Builder
> xBuilder(Application::CreateBuilder(pParent
, "modules/simpress/ui/annotationtagmenu.ui"));
1046 std::unique_ptr
<weld::Menu
> xMenu(xBuilder
->weld_menu("menu"));
1048 SvtUserOptions aUserOptions
;
1049 OUString
sCurrentAuthor( aUserOptions
.GetFullName() );
1050 OUString
sAuthor( xAnnotation
->getAuthor() );
1052 OUString
aStr(xMenu
->get_label(".uno:DeleteAllAnnotationByAuthor"));
1053 OUString
aReplace( sAuthor
);
1054 if( aReplace
.isEmpty() )
1055 aReplace
= SdResId( STR_ANNOTATION_NOAUTHOR
);
1056 aStr
= aStr
.replaceFirst("%1", aReplace
);
1057 xMenu
->set_label(".uno:DeleteAllAnnotationByAuthor", aStr
);
1059 bool bShowReply
= sAuthor
!= sCurrentAuthor
;
1060 xMenu
->set_visible(".uno:ReplyToAnnotation", bShowReply
);
1061 xMenu
->set_visible("separator", bShowReply
);
1062 xMenu
->set_visible(".uno:DeleteAnnotation", xAnnotation
.is());
1064 auto sId
= xMenu
->popup_at_rect(pParent
, rContextRect
);
1066 if (sId
== ".uno:ReplyToAnnotation")
1068 const SfxUnoAnyItem
aItem( SID_REPLYTO_POSTIT
, Any( xAnnotation
) );
1069 pDispatcher
->ExecuteList(SID_REPLYTO_POSTIT
,
1070 SfxCallMode::ASYNCHRON
, { &aItem
});
1072 else if (sId
== ".uno:DeleteAnnotation")
1074 const SfxUnoAnyItem
aItem( SID_DELETE_POSTIT
, Any( xAnnotation
) );
1075 pDispatcher
->ExecuteList(SID_DELETE_POSTIT
, SfxCallMode::ASYNCHRON
,
1078 else if (sId
== ".uno:DeleteAllAnnotationByAuthor")
1080 const SfxStringItem
aItem( SID_DELETEALLBYAUTHOR_POSTIT
, sAuthor
);
1081 pDispatcher
->ExecuteList( SID_DELETEALLBYAUTHOR_POSTIT
,
1082 SfxCallMode::ASYNCHRON
, { &aItem
});
1084 else if (sId
== ".uno:DeleteAllAnnotation")
1085 pDispatcher
->Execute( SID_DELETEALL_POSTIT
);
1088 Color
AnnotationManagerImpl::GetColor(sal_uInt16 aAuthorIndex
)
1090 if (!Application::GetSettings().GetStyleSettings().GetHighContrastMode())
1092 static const Color aArrayNormal
[] = {
1093 COL_AUTHOR1_NORMAL
, COL_AUTHOR2_NORMAL
, COL_AUTHOR3_NORMAL
,
1094 COL_AUTHOR4_NORMAL
, COL_AUTHOR5_NORMAL
, COL_AUTHOR6_NORMAL
,
1095 COL_AUTHOR7_NORMAL
, COL_AUTHOR8_NORMAL
, COL_AUTHOR9_NORMAL
};
1097 return aArrayNormal
[ aAuthorIndex
% SAL_N_ELEMENTS( aArrayNormal
) ];
1103 Color
AnnotationManagerImpl::GetColorLight(sal_uInt16 aAuthorIndex
)
1105 if (!Application::GetSettings().GetStyleSettings().GetHighContrastMode())
1107 static const Color aArrayLight
[] = {
1108 COL_AUTHOR1_LIGHT
, COL_AUTHOR2_LIGHT
, COL_AUTHOR3_LIGHT
,
1109 COL_AUTHOR4_LIGHT
, COL_AUTHOR5_LIGHT
, COL_AUTHOR6_LIGHT
,
1110 COL_AUTHOR7_LIGHT
, COL_AUTHOR8_LIGHT
, COL_AUTHOR9_LIGHT
};
1112 return aArrayLight
[ aAuthorIndex
% SAL_N_ELEMENTS( aArrayLight
) ];
1118 Color
AnnotationManagerImpl::GetColorDark(sal_uInt16 aAuthorIndex
)
1120 if (!Application::GetSettings().GetStyleSettings().GetHighContrastMode())
1122 static const Color aArrayAnkor
[] = {
1123 COL_AUTHOR1_DARK
, COL_AUTHOR2_DARK
, COL_AUTHOR3_DARK
,
1124 COL_AUTHOR4_DARK
, COL_AUTHOR5_DARK
, COL_AUTHOR6_DARK
,
1125 COL_AUTHOR7_DARK
, COL_AUTHOR8_DARK
, COL_AUTHOR9_DARK
};
1127 return aArrayAnkor
[ aAuthorIndex
% SAL_N_ELEMENTS( aArrayAnkor
) ];
1133 SdPage
* AnnotationManagerImpl::GetNextPage( SdPage
const * pPage
, bool bForward
)
1135 if( pPage
== nullptr )
1138 return mpDoc
->GetSdPage(0, PageKind::Standard
); // first page
1140 return mpDoc
->GetMasterSdPage( mpDoc
->GetMasterSdPageCount(PageKind::Standard
) - 1, PageKind::Standard
); // last page
1143 sal_uInt16 nPageNum
= (pPage
->GetPageNum() - 1) >> 1;
1145 // first all non master pages
1146 if( !pPage
->IsMasterPage() )
1150 if( nPageNum
>= mpDoc
->GetSdPageCount(PageKind::Standard
)-1 )
1152 // we reached end of draw pages, start with master pages (skip handout master for draw)
1153 return mpDoc
->GetMasterSdPage( (mpDoc
->GetDocumentType() == DocumentType::Impress
) ? 0 : 1, PageKind::Standard
);
1160 return nullptr; // we are already on the first draw page, finished
1164 return mpDoc
->GetSdPage(nPageNum
, PageKind::Standard
);
1170 if( nPageNum
>= mpDoc
->GetMasterSdPageCount(PageKind::Standard
)-1 )
1172 return nullptr; // we reached the end, there is nothing more to see here
1178 if( nPageNum
== (mpDoc
->GetDocumentType() == DocumentType::Impress
? 0 : 1) )
1180 // we reached beginning of master pages, start with end if pages
1181 return mpDoc
->GetSdPage( mpDoc
->GetSdPageCount(PageKind::Standard
)-1, PageKind::Standard
);
1186 return mpDoc
->GetMasterSdPage(nPageNum
,PageKind::Standard
);
1190 SdPage
* AnnotationManagerImpl::GetCurrentPage()
1192 if (mrBase
.GetMainViewShell())
1193 return mrBase
.GetMainViewShell()->getCurrentPage();
1197 AnnotationManager::AnnotationManager( ViewShellBase
& rViewShellBase
)
1198 : mxImpl( new AnnotationManagerImpl( rViewShellBase
) )
1203 AnnotationManager::~AnnotationManager()
1208 void AnnotationManager::ExecuteAnnotation(SfxRequest
const & rRequest
)
1210 mxImpl
->ExecuteAnnotation( rRequest
);
1213 void AnnotationManager::GetAnnotationState(SfxItemSet
& rItemSet
)
1215 mxImpl
->GetAnnotationState(rItemSet
);
1220 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */