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/geometry/RealPoint2D.hpp>
22 #include <com/sun/star/text/XText.hpp>
23 #include <com/sun/star/document/XEventBroadcaster.hpp>
24 #include <com/sun/star/office/XAnnotationAccess.hpp>
25 #include <comphelper/lok.hxx>
26 #include <svx/svxids.hrc>
28 #include <vcl/commandinfoprovider.hxx>
29 #include <vcl/settings.hxx>
30 #include <vcl/svapp.hxx>
31 #include <vcl/menu.hxx>
32 #include <vcl/weld.hxx>
34 #include <sal/macros.h>
35 #include <svl/itempool.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>
43 #include <sfx2/viewfrm.hxx>
44 #include <sfx2/bindings.hxx>
45 #include <sfx2/request.hxx>
46 #include <sfx2/dispatch.hxx>
48 #include <editeng/editeng.hxx>
49 #include <editeng/eeitem.hxx>
50 #include <editeng/fontitem.hxx>
51 #include <editeng/fhgtitem.hxx>
52 #include <editeng/outlobj.hxx>
53 #include <editeng/postitem.hxx>
54 #include <editeng/wghtitem.hxx>
55 #include <editeng/udlnitem.hxx>
56 #include <editeng/crossedoutitem.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 <DrawDocShell.hxx>
67 #include <DrawViewShell.hxx>
68 #include <sdresid.hxx>
69 #include <EventMultiplexer.hxx>
70 #include <ViewShellBase.hxx>
72 #include <drawdoc.hxx>
73 #include <textapi.hxx>
74 #include <optsitem.hxx>
79 using namespace ::com::sun::star
;
80 using namespace ::com::sun::star::uno
;
81 using namespace ::com::sun::star::drawing
;
82 using namespace ::com::sun::star::document
;
83 using namespace ::com::sun::star::geometry
;
84 using namespace ::com::sun::star::container
;
85 using namespace ::com::sun::star::beans
;
86 using namespace ::com::sun::star::text
;
87 using namespace ::com::sun::star::view
;
88 using namespace ::com::sun::star::style
;
89 using namespace ::com::sun::star::frame
;
90 using namespace ::com::sun::star::lang
;
91 using namespace ::com::sun::star::ui
;
92 using namespace ::com::sun::star::task
;
93 using namespace ::com::sun::star::office
;
97 SfxItemPool
* GetAnnotationPool()
99 static SfxItemPool
* s_pAnnotationPool
= nullptr;
100 if( s_pAnnotationPool
== nullptr )
102 s_pAnnotationPool
= EditEngine::CreatePool();
103 s_pAnnotationPool
->SetPoolDefaultItem(SvxFontHeightItem(423,100,EE_CHAR_FONTHEIGHT
));
105 vcl::Font
aAppFont( Application::GetSettings().GetStyleSettings().GetAppFont() );
106 s_pAnnotationPool
->SetPoolDefaultItem(SvxFontItem(aAppFont
.GetFamilyType(),aAppFont
.GetFamilyName(),"",PITCH_DONTKNOW
,RTL_TEXTENCODING_DONTKNOW
,EE_CHAR_FONTINFO
));
109 return s_pAnnotationPool
;
112 static SfxBindings
* getBindings( ViewShellBase
const & rBase
)
114 if( rBase
.GetMainViewShell().get() && rBase
.GetMainViewShell()->GetViewFrame() )
115 return &rBase
.GetMainViewShell()->GetViewFrame()->GetBindings();
120 static SfxDispatcher
* getDispatcher( ViewShellBase
const & rBase
)
122 if( rBase
.GetMainViewShell().get() && rBase
.GetMainViewShell()->GetViewFrame() )
123 return rBase
.GetMainViewShell()->GetViewFrame()->GetDispatcher();
128 css::util::DateTime
getCurrentDateTime()
130 DateTime
aCurrentDate( DateTime::SYSTEM
);
131 return css::util::DateTime( 0, aCurrentDate
.GetSec(),
132 aCurrentDate
.GetMin(), aCurrentDate
.GetHour(),
133 aCurrentDate
.GetDay(), aCurrentDate
.GetMonth(),
134 aCurrentDate
.GetYear(), false );
137 OUString
getAnnotationDateTimeString( const Reference
< XAnnotation
>& xAnnotation
)
140 if( xAnnotation
.is() )
142 const SvtSysLocale aSysLocale
;
143 const LocaleDataWrapper
& rLocalData
= aSysLocale
.GetLocaleData();
145 css::util::DateTime
aDateTime( xAnnotation
->getDateTime() );
147 Date
aSysDate( Date::SYSTEM
);
148 Date
aDate( aDateTime
.Day
, aDateTime
.Month
, aDateTime
.Year
);
150 sRet
= SdResId(STR_ANNOTATION_TODAY
);
151 else if (aDate
== (aSysDate
-1))
152 sRet
= SdResId(STR_ANNOTATION_YESTERDAY
);
153 else if (aDate
.IsValidAndGregorian() )
154 sRet
= rLocalData
.getDate(aDate
);
156 ::tools::Time
aTime( aDateTime
);
157 if(aTime
.GetTime() != 0)
158 sRet
= sRet
+ " " + rLocalData
.getTime( aTime
,false );
163 AnnotationManagerImpl::AnnotationManagerImpl( ViewShellBase
& rViewShellBase
)
164 : AnnotationManagerImplBase( m_aMutex
)
165 , mrBase( rViewShellBase
)
166 , mpDoc( rViewShellBase
.GetDocument() )
167 , mbShowAnnotations( true )
168 , mbPopupMenuActive( false )
169 , mnUpdateTagsEvent( nullptr )
171 SdOptions
* pOptions
= SD_MOD()->GetSdOptions(mpDoc
->GetDocumentType());
173 mbShowAnnotations
= pOptions
->IsShowComments();
176 void AnnotationManagerImpl::init()
178 // get current controller and initialize listeners
182 mxView
.set(mrBase
.GetController(), UNO_QUERY
);
186 OSL_FAIL( "sd::AnnotationManagerImpl::AnnotationManagerImpl(), Exception caught!" );
191 Reference
<XEventBroadcaster
> xModel (mrBase
.GetDocShell()->GetModel(), UNO_QUERY_THROW
);
192 Reference
<XEventListener
> xListener( this );
193 xModel
->addEventListener( xListener
);
200 // WeakComponentImplHelper
201 void SAL_CALL
AnnotationManagerImpl::disposing ()
205 Reference
<XEventBroadcaster
> xModel (mrBase
.GetDocShell()->GetModel(), UNO_QUERY_THROW
);
206 Reference
<XEventListener
> xListener( this );
207 xModel
->removeEventListener( xListener
);
216 if( mnUpdateTagsEvent
)
218 Application::RemoveUserEvent( mnUpdateTagsEvent
);
219 mnUpdateTagsEvent
= nullptr;
223 mxCurrentPage
.clear();
227 void SAL_CALL
AnnotationManagerImpl::notifyEvent( const css::document::EventObject
& aEvent
)
229 if( !(aEvent
.EventName
== "OnAnnotationInserted" || aEvent
.EventName
== "OnAnnotationRemoved" || aEvent
.EventName
== "OnAnnotationChanged") )
232 // AnnotationInsertion and modification is not handled here because when
233 // a new annotation is inserted, it consists of OnAnnotationInserted
234 // followed by a chain of OnAnnotationChanged (called for setting each
235 // of the annotation attributes - author, text etc.). This is not what a
236 // LOK client wants. So only handle removal here as annotation removal
237 // consists of only one event - 'OnAnnotationRemoved'
238 if ( aEvent
.EventName
== "OnAnnotationRemoved" )
240 Reference
< XAnnotation
> xAnnotation( aEvent
.Source
, uno::UNO_QUERY
);
241 if ( xAnnotation
.is() )
243 LOKCommentNotify(CommentNotificationType::Remove
, &mrBase
, xAnnotation
);
250 void SAL_CALL
AnnotationManagerImpl::disposing( const css::lang::EventObject
& /*Source*/ )
254 Reference
<XAnnotation
> AnnotationManagerImpl::GetAnnotationById(sal_uInt32 nAnnotationId
)
256 SdPage
* pPage
= nullptr;
259 pPage
= GetNextPage(pPage
, true);
260 if( pPage
&& !pPage
->getAnnotations().empty() )
262 AnnotationVector
aAnnotations(pPage
->getAnnotations());
263 auto iter
= std::find_if(aAnnotations
.begin(), aAnnotations
.end(),
264 [nAnnotationId
](const Reference
<XAnnotation
>& xAnnotation
) {
265 return sd::getAnnotationId(xAnnotation
) == nAnnotationId
;
267 if (iter
!= aAnnotations
.end())
272 Reference
<XAnnotation
> xAnnotationEmpty
;
273 return xAnnotationEmpty
;
276 void AnnotationManagerImpl::ShowAnnotations( bool bShow
)
278 // enforce show annotations if a new annotation is inserted
279 if( mbShowAnnotations
!= bShow
)
281 mbShowAnnotations
= bShow
;
283 SdOptions
* pOptions
= SD_MOD()->GetSdOptions(mpDoc
->GetDocumentType());
285 pOptions
->SetShowComments( mbShowAnnotations
);
291 void AnnotationManagerImpl::ExecuteAnnotation(SfxRequest
const & rReq
)
293 switch( rReq
.GetSlot() )
295 case SID_INSERT_POSTIT
:
296 ExecuteInsertAnnotation( rReq
);
298 case SID_DELETE_POSTIT
:
299 case SID_DELETEALL_POSTIT
:
300 case SID_DELETEALLBYAUTHOR_POSTIT
:
301 ExecuteDeleteAnnotation( rReq
);
303 case SID_EDIT_POSTIT
:
304 ExecuteEditAnnotation( rReq
);
306 case SID_PREVIOUS_POSTIT
:
307 case SID_NEXT_POSTIT
:
308 SelectNextAnnotation( rReq
.GetSlot() == SID_NEXT_POSTIT
);
310 case SID_REPLYTO_POSTIT
:
311 ExecuteReplyToAnnotation( rReq
);
313 case SID_TOGGLE_NOTES
:
314 ShowAnnotations( !mbShowAnnotations
);
319 void AnnotationManagerImpl::ExecuteInsertAnnotation(SfxRequest
const & rReq
)
321 if (!comphelper::LibreOfficeKit::isActive() || comphelper::LibreOfficeKit::isTiledAnnotations())
322 ShowAnnotations(true);
324 const SfxItemSet
* pArgs
= rReq
.GetArgs();
328 const SfxPoolItem
* pPoolItem
= nullptr;
329 if (SfxItemState::SET
== pArgs
->GetItemState(SID_ATTR_POSTIT_TEXT
, true, &pPoolItem
))
331 sText
= static_cast<const SfxStringItem
*>(pPoolItem
)->GetValue();
335 InsertAnnotation(sText
);
338 void AnnotationManagerImpl::ExecuteDeleteAnnotation(SfxRequest
const & rReq
)
340 ShowAnnotations( true );
342 const SfxItemSet
* pArgs
= rReq
.GetArgs();
344 switch( rReq
.GetSlot() )
346 case SID_DELETEALL_POSTIT
:
347 DeleteAllAnnotations();
349 case SID_DELETEALLBYAUTHOR_POSTIT
:
352 const SfxPoolItem
* pPoolItem
= nullptr;
353 if( SfxItemState::SET
== pArgs
->GetItemState( SID_DELETEALLBYAUTHOR_POSTIT
, true, &pPoolItem
) )
355 OUString
sAuthor( static_cast<const SfxStringItem
*>( pPoolItem
)->GetValue() );
356 DeleteAnnotationsByAuthor( sAuthor
);
360 case SID_DELETE_POSTIT
:
362 Reference
< XAnnotation
> xAnnotation
;
366 const SfxPoolItem
* pPoolItem
= nullptr;
367 if( SfxItemState::SET
== pArgs
->GetItemState( SID_DELETE_POSTIT
, true, &pPoolItem
) )
368 static_cast<const SfxUnoAnyItem
*>(pPoolItem
)->GetValue() >>= xAnnotation
;
369 if( SfxItemState::SET
== pArgs
->GetItemState( SID_ATTR_POSTIT_ID
, true, &pPoolItem
) )
370 nId
= static_cast<const SvxPostItIdItem
*>(pPoolItem
)->GetValue().toUInt32();
374 xAnnotation
= GetAnnotationById(nId
);
375 else if( !xAnnotation
.is() )
376 GetSelectedAnnotation( xAnnotation
);
378 DeleteAnnotation( xAnnotation
);
386 void AnnotationManagerImpl::ExecuteEditAnnotation(SfxRequest
const & rReq
)
388 const SfxItemSet
* pArgs
= rReq
.GetArgs();
389 Reference
< XAnnotation
> xAnnotation
;
395 if (mpDoc
->IsUndoEnabled())
396 mpDoc
->BegUndo(SdResId(STR_ANNOTATION_UNDO_EDIT
));
398 const SfxPoolItem
* pPoolItem
= nullptr;
399 if (SfxItemState::SET
== pArgs
->GetItemState(SID_ATTR_POSTIT_ID
, true, &pPoolItem
))
401 nId
= static_cast<const SvxPostItIdItem
*>(pPoolItem
)->GetValue().toUInt32();
402 xAnnotation
= GetAnnotationById(nId
);
404 if (SfxItemState::SET
== pArgs
->GetItemState(SID_ATTR_POSTIT_TEXT
, true, &pPoolItem
))
405 sText
= static_cast<const SfxStringItem
*>(pPoolItem
)->GetValue();
407 if (xAnnotation
.is() && !sText
.isEmpty())
409 CreateChangeUndo(xAnnotation
);
411 // TODO: Not allow other authors to change others' comments ?
412 Reference
<XText
> xText(xAnnotation
->getTextRange());
413 xText
->setString(sText
);
415 LOKCommentNotifyAll(CommentNotificationType::Modify
, xAnnotation
);
418 if (mpDoc
->IsUndoEnabled())
424 void AnnotationManagerImpl::InsertAnnotation(const OUString
& rText
)
426 SdPage
* pPage
= GetCurrentPage();
430 if( mpDoc
->IsUndoEnabled() )
431 mpDoc
->BegUndo( SdResId( STR_ANNOTATION_UNDO_INSERT
) );
433 // find free space for new annotation
436 AnnotationVector
aAnnotations( pPage
->getAnnotations() );
437 if( !aAnnotations
.empty() )
439 const int page_width
= pPage
->GetSize().Width();
440 const int width
= 1000;
441 const int height
= 800;
442 ::tools::Rectangle aTagRect
;
446 ::tools::Rectangle
aNewRect( x
, y
, x
+ width
- 1, y
+ height
- 1 );
449 for( const auto& rxAnnotation
: aAnnotations
)
451 RealPoint2D
aPoint( rxAnnotation
->getPosition() );
452 aTagRect
.SetLeft( sal::static_int_cast
< long >( aPoint
.X
* 100.0 ) );
453 aTagRect
.SetTop( sal::static_int_cast
< long >( aPoint
.Y
* 100.0 ) );
454 aTagRect
.SetRight( aTagRect
.Left() + width
- 1 );
455 aTagRect
.SetBottom( aTagRect
.Top() + height
- 1 );
457 if( aNewRect
.IsOver( aTagRect
) )
480 Reference
< XAnnotation
> xAnnotation
;
481 pPage
->createAnnotation( xAnnotation
);
484 if (comphelper::LibreOfficeKit::isActive())
485 sAuthor
= mrBase
.GetMainViewShell()->GetView()->GetAuthor();
488 SvtUserOptions aUserOptions
;
489 sAuthor
= aUserOptions
.GetFullName();
490 xAnnotation
->setInitials( aUserOptions
.GetID() );
493 if (!rText
.isEmpty())
495 Reference
<XText
> xText(xAnnotation
->getTextRange());
496 xText
->setString(rText
);
499 // set current author to new annotation
500 xAnnotation
->setAuthor( sAuthor
);
501 // set current time to new annotation
502 xAnnotation
->setDateTime( getCurrentDateTime() );
505 RealPoint2D
aPos( static_cast<double>(x
) / 100.0, static_cast<double>(y
) / 100.0 );
506 xAnnotation
->setPosition( aPos
);
508 if( mpDoc
->IsUndoEnabled() )
511 // Tell our LOK clients about new comment added
512 LOKCommentNotifyAll(CommentNotificationType::Add
, xAnnotation
);
515 SelectAnnotation( xAnnotation
, true );
518 void AnnotationManagerImpl::ExecuteReplyToAnnotation( SfxRequest
const & rReq
)
520 Reference
< XAnnotation
> xAnnotation
;
521 const SfxItemSet
* pArgs
= rReq
.GetArgs();
522 sal_uInt32 nReplyId
= 0; // Id of the comment to reply to
526 const SfxPoolItem
* pPoolItem
= nullptr;
527 if( SfxItemState::SET
== pArgs
->GetItemState( SID_ATTR_POSTIT_ID
, true, &pPoolItem
) )
529 nReplyId
= static_cast<const SvxPostItIdItem
*>(pPoolItem
)->GetValue().toUInt32();
530 xAnnotation
= GetAnnotationById(nReplyId
);
532 else if( SfxItemState::SET
== pArgs
->GetItemState( rReq
.GetSlot(), true, &pPoolItem
) )
533 static_cast<const SfxUnoAnyItem
*>( pPoolItem
)->GetValue() >>= xAnnotation
;
535 if( SfxItemState::SET
== pArgs
->GetItemState( SID_ATTR_POSTIT_TEXT
, true, &pPoolItem
) )
536 sReplyText
= static_cast<const SvxPostItTextItem
*>( pPoolItem
)->GetValue();
539 TextApiObject
* pTextApi
= getTextApiObject( xAnnotation
);
543 std::unique_ptr
< ::Outliner
> pOutliner( new ::Outliner(GetAnnotationPool(),OutlinerMode::TextObject
) );
545 SdDrawDocument::SetCalcFieldValueHdl( pOutliner
.get() );
546 pOutliner
->SetUpdateMode( true );
548 OUString
aStr(SdResId(STR_ANNOTATION_REPLY
));
549 OUString
sAuthor( xAnnotation
->getAuthor() );
550 if( sAuthor
.isEmpty() )
551 sAuthor
= SdResId( STR_ANNOTATION_NOAUTHOR
);
553 aStr
= aStr
.replaceFirst("%1", sAuthor
);
555 aStr
+= " (" + getAnnotationDateTimeString( xAnnotation
) + "): \"";
557 OUString
sQuote( pTextApi
->GetText() );
559 if( sQuote
.isEmpty() )
561 aStr
+= sQuote
+ "\"\n";
563 for( sal_Int32 nIdx
= 0; nIdx
>= 0; )
564 pOutliner
->Insert( aStr
.getToken( 0, '\n', nIdx
), EE_PARA_APPEND
, -1 );
566 if( pOutliner
->GetParagraphCount() > 1 )
568 SfxItemSet
aAnswerSet( pOutliner
->GetEmptyItemSet() );
569 aAnswerSet
.Put(SvxPostureItem(ITALIC_NORMAL
,EE_CHAR_ITALIC
));
572 aSel
.nEndPara
= pOutliner
->GetParagraphCount()-2;
573 aSel
.nEndPos
= pOutliner
->GetText( pOutliner
->GetParagraph( aSel
.nEndPara
) ).getLength();
575 pOutliner
->QuickSetAttribs( aAnswerSet
, aSel
);
578 if (!sReplyText
.isEmpty())
579 pOutliner
->Insert(sReplyText
);
581 std::unique_ptr
< OutlinerParaObject
> pOPO( pOutliner
->CreateParaObject() );
582 pTextApi
->SetText(*pOPO
);
584 OUString sReplyAuthor
;
585 if (comphelper::LibreOfficeKit::isActive())
586 sReplyAuthor
= mrBase
.GetMainViewShell()->GetView()->GetAuthor();
589 SvtUserOptions aUserOptions
;
590 sReplyAuthor
= aUserOptions
.GetFullName();
591 xAnnotation
->setInitials( aUserOptions
.GetID() );
594 xAnnotation
->setAuthor( sReplyAuthor
);
595 // set current time to reply
596 xAnnotation
->setDateTime( getCurrentDateTime() );
598 // Tell our LOK clients about this (comment modification)
599 LOKCommentNotifyAll(CommentNotificationType::Modify
, xAnnotation
);
602 SelectAnnotation( xAnnotation
, true );
605 void AnnotationManagerImpl::DeleteAnnotation( const Reference
< XAnnotation
>& xAnnotation
)
607 SdPage
* pPage
= GetCurrentPage();
609 if( xAnnotation
.is() && pPage
)
611 if( mpDoc
->IsUndoEnabled() )
612 mpDoc
->BegUndo( SdResId( STR_ANNOTATION_UNDO_DELETE
) );
614 pPage
->removeAnnotation( xAnnotation
);
616 if( mpDoc
->IsUndoEnabled() )
623 void AnnotationManagerImpl::DeleteAnnotationsByAuthor( const OUString
& sAuthor
)
625 if( mpDoc
->IsUndoEnabled() )
626 mpDoc
->BegUndo( SdResId( STR_ANNOTATION_UNDO_DELETE
) );
628 SdPage
* pPage
= nullptr;
631 pPage
= GetNextPage( pPage
, true );
633 if( pPage
&& !pPage
->getAnnotations().empty() )
635 AnnotationVector
aAnnotations( pPage
->getAnnotations() );
636 for( Reference
< XAnnotation
>& xAnnotation
: aAnnotations
)
638 if( xAnnotation
->getAuthor() == sAuthor
)
640 if( mxSelectedAnnotation
== xAnnotation
)
641 mxSelectedAnnotation
.clear();
642 pPage
->removeAnnotation( xAnnotation
);
648 if( mpDoc
->IsUndoEnabled() )
652 void AnnotationManagerImpl::DeleteAllAnnotations()
654 if( mpDoc
->IsUndoEnabled() )
655 mpDoc
->BegUndo( SdResId( STR_ANNOTATION_UNDO_DELETE
) );
657 SdPage
* pPage
= nullptr;
660 pPage
= GetNextPage( pPage
, true );
662 if( pPage
&& !pPage
->getAnnotations().empty() )
665 AnnotationVector
aAnnotations( pPage
->getAnnotations() );
666 for( const auto& rxAnnotation
: aAnnotations
)
668 pPage
->removeAnnotation( rxAnnotation
);
674 mxSelectedAnnotation
.clear();
676 if( mpDoc
->IsUndoEnabled() )
680 void AnnotationManagerImpl::GetAnnotationState(SfxItemSet
& rSet
)
682 SdPage
* pCurrentPage
= GetCurrentPage();
684 const bool bReadOnly
= mrBase
.GetDocShell()->IsReadOnly();
685 const bool bWrongPageKind
= (pCurrentPage
== nullptr) || (pCurrentPage
->GetPageKind() != PageKind::Standard
);
687 const SvtSaveOptions::ODFDefaultVersion
nCurrentODFVersion( SvtSaveOptions().GetODFDefaultVersion() );
689 if( bReadOnly
|| bWrongPageKind
|| (nCurrentODFVersion
<= SvtSaveOptions::ODFVER_012
) )
690 rSet
.DisableItem( SID_INSERT_POSTIT
);
692 rSet
.Put(SfxBoolItem(SID_TOGGLE_NOTES
, mbShowAnnotations
));
694 Reference
< XAnnotation
> xAnnotation
;
695 GetSelectedAnnotation( xAnnotation
);
697 // Don't disable these slot in case of LOK, as postit doesn't need to
698 // selected before doing an operation on it in LOK
699 if( (!xAnnotation
.is() && !comphelper::LibreOfficeKit::isActive()) || bReadOnly
)
701 rSet
.DisableItem( SID_DELETE_POSTIT
);
702 rSet
.DisableItem( SID_EDIT_POSTIT
);
705 SdPage
* pPage
= nullptr;
707 bool bHasAnnotations
= false;
710 pPage
= GetNextPage( pPage
, true );
712 if( pPage
&& !pPage
->getAnnotations().empty() )
713 bHasAnnotations
= true;
715 while( pPage
&& !bHasAnnotations
);
717 if( !bHasAnnotations
|| bReadOnly
)
719 rSet
.DisableItem( SID_DELETEALL_POSTIT
);
722 if( bWrongPageKind
|| !bHasAnnotations
)
724 rSet
.DisableItem( SID_PREVIOUS_POSTIT
);
725 rSet
.DisableItem( SID_NEXT_POSTIT
);
729 void AnnotationManagerImpl::SelectNextAnnotation(bool bForeward
)
731 ShowAnnotations( true );
733 Reference
< XAnnotation
> xCurrent
;
734 GetSelectedAnnotation( xCurrent
);
735 SdPage
* pPage
= GetCurrentPage();
739 AnnotationVector
aAnnotations( pPage
->getAnnotations() );
745 auto iter
= std::find(aAnnotations
.begin(), aAnnotations
.end(), xCurrent
);
746 if (iter
!= aAnnotations
.end())
749 if( iter
!= aAnnotations
.end() )
751 SelectAnnotation( (*iter
) );
756 else if( !aAnnotations
.empty() )
758 SelectAnnotation( *(aAnnotations
.begin()) );
766 auto iter
= std::find(aAnnotations
.begin(), aAnnotations
.end(), xCurrent
);
767 if (iter
!= aAnnotations
.end() && iter
!= aAnnotations
.begin())
770 SelectAnnotation( (*iter
) );
774 else if( !aAnnotations
.empty() )
776 AnnotationVector::iterator
iter( aAnnotations
.end() );
777 SelectAnnotation( *(--iter
) );
782 mxSelectedAnnotation
.clear();
787 pPage
= GetNextPage( pPage
, bForeward
);
789 if( pPage
&& !pPage
->getAnnotations().empty() )
791 // switch to next/previous slide with annotations
792 std::shared_ptr
<DrawViewShell
> pDrawViewShell(std::dynamic_pointer_cast
<DrawViewShell
>(mrBase
.GetMainViewShell()));
793 if (pDrawViewShell
!= nullptr)
795 pDrawViewShell
->ChangeEditMode(pPage
->IsMasterPage() ? EditMode::MasterPage
: EditMode::Page
, false);
796 pDrawViewShell
->SwitchPage((pPage
->GetPageNum() - 1) >> 1);
798 SfxDispatcher
* pDispatcher
= getDispatcher( mrBase
);
800 pDispatcher
->Execute( bForeward
? SID_NEXT_POSTIT
: SID_PREVIOUS_POSTIT
);
808 // The question text depends on the search direction.
809 bool bImpress
= mpDoc
->GetDocumentType() == DocumentType::Impress
;
810 const char* pStringId
;
812 pStringId
= bImpress
? STR_ANNOTATION_WRAP_FORWARD
: STR_ANNOTATION_WRAP_FORWARD_DRAW
;
814 pStringId
= bImpress
? STR_ANNOTATION_WRAP_BACKWARD
: STR_ANNOTATION_WRAP_BACKWARD_DRAW
;
816 // Pop up question box that asks the user whether to wrap around.
817 // The dialog is made modal with respect to the whole application.
818 std::unique_ptr
<weld::MessageDialog
> xQueryBox(Application::CreateMessageDialog(nullptr,
819 VclMessageType::Question
, VclButtonsType::YesNo
,
820 SdResId(pStringId
)));
821 xQueryBox
->set_default_response(RET_YES
);
822 if (xQueryBox
->run() != RET_YES
)
828 void AnnotationManagerImpl::onTagSelected( AnnotationTag
const & rTag
)
830 mxSelectedAnnotation
= rTag
.GetAnnotation();
834 void AnnotationManagerImpl::onTagDeselected( AnnotationTag
const & rTag
)
836 if( rTag
.GetAnnotation() == mxSelectedAnnotation
)
838 mxSelectedAnnotation
.clear();
843 void AnnotationManagerImpl::SelectAnnotation( const css::uno::Reference
< css::office::XAnnotation
>& xAnnotation
, bool bEdit
/* = sal_False */ )
845 mxSelectedAnnotation
= xAnnotation
;
847 auto iter
= std::find_if(maTagVector
.begin(), maTagVector
.end(),
848 [&xAnnotation
](const rtl::Reference
<AnnotationTag
>& rxTag
) { return rxTag
->GetAnnotation() == xAnnotation
; });
849 if (iter
!= maTagVector
.end())
851 SmartTagReference
xTag( (*iter
).get() );
852 mrBase
.GetMainViewShell()->GetView()->getSmartTags().select( xTag
);
853 (*iter
)->OpenPopup( bEdit
);
857 void AnnotationManagerImpl::GetSelectedAnnotation( css::uno::Reference
< css::office::XAnnotation
>& xAnnotation
)
859 xAnnotation
= mxSelectedAnnotation
;
862 void AnnotationManagerImpl::invalidateSlots()
864 SfxBindings
* pBindings
= getBindings( mrBase
);
867 pBindings
->Invalidate( SID_INSERT_POSTIT
);
868 pBindings
->Invalidate( SID_DELETE_POSTIT
);
869 pBindings
->Invalidate( SID_DELETEALL_POSTIT
);
870 pBindings
->Invalidate( SID_PREVIOUS_POSTIT
);
871 pBindings
->Invalidate( SID_NEXT_POSTIT
);
872 pBindings
->Invalidate( SID_UNDO
);
873 pBindings
->Invalidate( SID_REDO
);
877 void AnnotationManagerImpl::onSelectionChanged()
879 if( !(mxView
.is() && mrBase
.GetDrawView()) )
884 Reference
< XAnnotationAccess
> xPage( mxView
->getCurrentPage(), UNO_QUERY
);
886 if( xPage
!= mxCurrentPage
)
888 mxCurrentPage
= xPage
;
895 OSL_FAIL( "sd::AnnotationManagerImpl::onSelectionChanged(), exception caught!" );
899 void AnnotationManagerImpl::UpdateTags( bool bSynchron
)
903 if( mnUpdateTagsEvent
)
904 Application::RemoveUserEvent( mnUpdateTagsEvent
);
906 UpdateTagsHdl(nullptr);
910 if( !mnUpdateTagsEvent
&& mxView
.is() )
911 mnUpdateTagsEvent
= Application::PostUserEvent( LINK( this, AnnotationManagerImpl
, UpdateTagsHdl
) );
915 IMPL_LINK_NOARG(AnnotationManagerImpl
, UpdateTagsHdl
, void*, void)
917 mnUpdateTagsEvent
= nullptr;
920 if( mbShowAnnotations
)
923 if( mrBase
.GetDrawView() )
924 static_cast< ::sd::View
* >( mrBase
.GetDrawView() )->updateHandles();
929 void AnnotationManagerImpl::CreateTags()
932 if( !(mxCurrentPage
.is() && mpDoc
) )
938 maFont
= Application::GetSettings().GetStyleSettings().GetAppFont();
940 rtl::Reference
< AnnotationTag
> xSelectedTag
;
942 Reference
< XAnnotationEnumeration
> xEnum( mxCurrentPage
->createAnnotationEnumeration() );
943 while( xEnum
->hasMoreElements() )
945 Reference
< XAnnotation
> xAnnotation( xEnum
->nextElement() );
946 Color
aColor( GetColorLight( mpDoc
->GetAnnotationAuthorIndex( xAnnotation
->getAuthor() ) ) );
947 rtl::Reference
< AnnotationTag
> xTag( new AnnotationTag( *this, *mrBase
.GetMainViewShell()->GetView(), xAnnotation
, aColor
, nIndex
++, maFont
) );
948 maTagVector
.push_back(xTag
);
950 if( xAnnotation
== mxSelectedAnnotation
)
956 if( xSelectedTag
.is() )
958 SmartTagReference
xTag( xSelectedTag
.get() );
959 mrBase
.GetMainViewShell()->GetView()->getSmartTags().select( xTag
);
963 // no tag, no selection!
964 mxSelectedAnnotation
.clear();
969 OSL_FAIL( "sd::AnnotationManagerImpl::onSelectionChanged(), exception caught!" );
973 void AnnotationManagerImpl::DisposeTags()
975 for (auto& rxTag
: maTagVector
)
983 void AnnotationManagerImpl::addListener()
985 Link
<tools::EventMultiplexerEvent
&,void> aLink( LINK(this,AnnotationManagerImpl
,EventMultiplexerListener
) );
986 mrBase
.GetEventMultiplexer()->AddEventListener(aLink
);
989 void AnnotationManagerImpl::removeListener()
991 Link
<tools::EventMultiplexerEvent
&,void> aLink( LINK(this,AnnotationManagerImpl
,EventMultiplexerListener
) );
992 mrBase
.GetEventMultiplexer()->RemoveEventListener( aLink
);
995 IMPL_LINK(AnnotationManagerImpl
,EventMultiplexerListener
,
996 tools::EventMultiplexerEvent
&, rEvent
, void)
998 switch (rEvent
.meEventId
)
1000 case EventMultiplexerEventId::CurrentPageChanged
:
1001 case EventMultiplexerEventId::EditViewSelection
:
1002 onSelectionChanged();
1005 case EventMultiplexerEventId::MainViewRemoved
:
1007 onSelectionChanged();
1010 case EventMultiplexerEventId::MainViewAdded
:
1011 mxView
.set( mrBase
.GetController(), UNO_QUERY
);
1012 onSelectionChanged();
1021 sal_uInt16
IdentToSID(const OString
& rIdent
)
1023 if (rIdent
== "reply")
1024 return SID_REPLYTO_POSTIT
;
1025 else if (rIdent
== "delete")
1026 return SID_DELETE_POSTIT
;
1027 else if (rIdent
== "deleteby")
1028 return SID_DELETEALLBYAUTHOR_POSTIT
;
1029 else if (rIdent
== "deleteall")
1030 return SID_DELETEALL_POSTIT
;
1031 else if (rIdent
== "copy")
1033 else if (rIdent
== "paste")
1035 else if (rIdent
== "bold")
1036 return SID_ATTR_CHAR_WEIGHT
;
1037 else if (rIdent
== "italic")
1038 return SID_ATTR_CHAR_POSTURE
;
1039 else if (rIdent
== "underline")
1040 return SID_ATTR_CHAR_UNDERLINE
;
1041 else if (rIdent
== "strike")
1042 return SID_ATTR_CHAR_STRIKEOUT
;
1047 void AnnotationManagerImpl::ExecuteAnnotationContextMenu( const Reference
< XAnnotation
>& xAnnotation
, vcl::Window
* pParent
, const ::tools::Rectangle
& rContextRect
, bool bButtonMenu
/* = false */ )
1049 SfxDispatcher
* pDispatcher( getDispatcher( mrBase
) );
1053 const bool bReadOnly
= mrBase
.GetDocShell()->IsReadOnly();
1055 AnnotationWindow
* pAnnotationWindow
= bButtonMenu
? nullptr : dynamic_cast< AnnotationWindow
* >( pParent
);
1057 if( bReadOnly
&& !pAnnotationWindow
)
1061 if (pAnnotationWindow
)
1062 sUIFile
= "modules/simpress/ui/annotationmenu.ui";
1064 sUIFile
= "modules/simpress/ui/annotationtagmenu.ui";
1065 VclBuilder
aBuilder(nullptr, VclBuilderContainer::getUIRootDir(), sUIFile
, "");
1066 VclPtr
<PopupMenu
> pMenu(aBuilder
.get_menu("menu"));
1068 SvtUserOptions aUserOptions
;
1069 OUString
sCurrentAuthor( aUserOptions
.GetFullName() );
1070 OUString
sAuthor( xAnnotation
->getAuthor() );
1072 OUString
aStr(pMenu
->GetItemText(pMenu
->GetItemId("deleteby")));
1073 OUString
aReplace( sAuthor
);
1074 if( aReplace
.isEmpty() )
1075 aReplace
= SdResId( STR_ANNOTATION_NOAUTHOR
);
1076 aStr
= aStr
.replaceFirst("%1", aReplace
);
1077 pMenu
->SetItemText(pMenu
->GetItemId("deleteby"), aStr
);
1078 pMenu
->EnableItem(pMenu
->GetItemId("reply"), (sAuthor
!= sCurrentAuthor
) && !bReadOnly
);
1079 pMenu
->EnableItem(pMenu
->GetItemId("delete"), xAnnotation
.is() && !bReadOnly
);
1080 pMenu
->EnableItem(pMenu
->GetItemId("deleteby"), !bReadOnly
);
1081 pMenu
->EnableItem(pMenu
->GetItemId("deleteall"), !bReadOnly
);
1083 if( pAnnotationWindow
)
1085 if( pAnnotationWindow
->IsProtected() || bReadOnly
)
1087 pMenu
->EnableItem(pMenu
->GetItemId("bold"), false);
1088 pMenu
->EnableItem(pMenu
->GetItemId("italic"), false);
1089 pMenu
->EnableItem(pMenu
->GetItemId("underline"), false);
1090 pMenu
->EnableItem(pMenu
->GetItemId("strike"), false);
1091 pMenu
->EnableItem(pMenu
->GetItemId("paste"), false);
1095 SfxItemSet
aSet(pAnnotationWindow
->getView()->GetAttribs());
1097 if ( aSet
.GetItemState( EE_CHAR_WEIGHT
) == SfxItemState::SET
)
1099 if( aSet
.Get( EE_CHAR_WEIGHT
).GetWeight() == WEIGHT_BOLD
)
1100 pMenu
->CheckItem("bold");
1103 if ( aSet
.GetItemState( EE_CHAR_ITALIC
) == SfxItemState::SET
)
1105 if( aSet
.Get( EE_CHAR_ITALIC
).GetPosture() != ITALIC_NONE
)
1106 pMenu
->CheckItem("italic");
1109 if ( aSet
.GetItemState( EE_CHAR_UNDERLINE
) == SfxItemState::SET
)
1111 if( aSet
.Get( EE_CHAR_UNDERLINE
).GetLineStyle() != LINESTYLE_NONE
)
1112 pMenu
->CheckItem("underline");
1115 if ( aSet
.GetItemState( EE_CHAR_STRIKEOUT
) == SfxItemState::SET
)
1117 if( aSet
.Get( EE_CHAR_STRIKEOUT
).GetStrikeout() != STRIKEOUT_NONE
)
1118 pMenu
->CheckItem("strike");
1120 TransferableDataHelper
aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( pAnnotationWindow
) );
1121 pMenu
->EnableItem(pMenu
->GetItemId("paste"), aDataHelper
.GetFormatCount() != 0);
1124 pMenu
->EnableItem(pMenu
->GetItemId("copy"), pAnnotationWindow
->getView()->HasSelection());
1128 Reference
< css::frame::XFrame
> xFrame( mrBase
.GetMainViewShell()->GetViewFrame()->GetFrame().GetFrameInterface() );
1131 for( sal_uInt16 nPos
= 0; nPos
< pMenu
->GetItemCount(); nPos
++ )
1133 sal_uInt16 nId
= pMenu
->GetItemId( nPos
);
1134 if (!pMenu
->IsItemEnabled(nId
))
1137 Image
aImage( vcl::CommandInfoProvider::GetImageForCommand( pMenu
->GetItemCommand( nId
), xFrame
) );
1139 pMenu
->SetItemImage( nId
, aImage
);
1143 // tdf#99388 and tdf#99712 make known that PopupMenu is active at parent to
1144 // allow suppressing closing of that window if needed
1145 setPopupMenuActive(true);
1147 sal_uInt16 nId
= pMenu
->Execute( pParent
, rContextRect
, PopupMenuFlags::ExecuteDown
|PopupMenuFlags::NoMouseUpClose
);
1148 nId
= IdentToSID(pMenu
->GetItemIdent(nId
));
1150 // tdf#99388 and tdf#99712 reset flag, need to be done before reacting
1151 // since closing it is one possible reaction
1152 setPopupMenuActive(false);
1156 case SID_REPLYTO_POSTIT
:
1158 const SfxUnoAnyItem
aItem( SID_REPLYTO_POSTIT
, Any( xAnnotation
) );
1159 pDispatcher
->ExecuteList(SID_REPLYTO_POSTIT
,
1160 SfxCallMode::ASYNCHRON
, { &aItem
});
1163 case SID_DELETE_POSTIT
:
1165 const SfxUnoAnyItem
aItem( SID_DELETE_POSTIT
, Any( xAnnotation
) );
1166 pDispatcher
->ExecuteList(SID_DELETE_POSTIT
, SfxCallMode::ASYNCHRON
,
1170 case SID_DELETEALLBYAUTHOR_POSTIT
:
1172 const SfxStringItem
aItem( SID_DELETEALLBYAUTHOR_POSTIT
, sAuthor
);
1173 pDispatcher
->ExecuteList( SID_DELETEALLBYAUTHOR_POSTIT
,
1174 SfxCallMode::ASYNCHRON
, { &aItem
});
1177 case SID_DELETEALL_POSTIT
:
1178 pDispatcher
->Execute( SID_DELETEALL_POSTIT
);
1182 case SID_ATTR_CHAR_WEIGHT
:
1183 case SID_ATTR_CHAR_POSTURE
:
1184 case SID_ATTR_CHAR_UNDERLINE
:
1185 case SID_ATTR_CHAR_STRIKEOUT
:
1186 if( pAnnotationWindow
)
1187 pAnnotationWindow
->ExecuteSlot( nId
);
1192 Color
AnnotationManagerImpl::GetColor(sal_uInt16 aAuthorIndex
)
1194 if (!Application::GetSettings().GetStyleSettings().GetHighContrastMode())
1196 static const Color aArrayNormal
[] = {
1197 COL_AUTHOR1_NORMAL
, COL_AUTHOR2_NORMAL
, COL_AUTHOR3_NORMAL
,
1198 COL_AUTHOR4_NORMAL
, COL_AUTHOR5_NORMAL
, COL_AUTHOR6_NORMAL
,
1199 COL_AUTHOR7_NORMAL
, COL_AUTHOR8_NORMAL
, COL_AUTHOR9_NORMAL
};
1201 return aArrayNormal
[ aAuthorIndex
% SAL_N_ELEMENTS( aArrayNormal
) ];
1207 Color
AnnotationManagerImpl::GetColorLight(sal_uInt16 aAuthorIndex
)
1209 if (!Application::GetSettings().GetStyleSettings().GetHighContrastMode())
1211 static const Color aArrayLight
[] = {
1212 COL_AUTHOR1_LIGHT
, COL_AUTHOR2_LIGHT
, COL_AUTHOR3_LIGHT
,
1213 COL_AUTHOR4_LIGHT
, COL_AUTHOR5_LIGHT
, COL_AUTHOR6_LIGHT
,
1214 COL_AUTHOR7_LIGHT
, COL_AUTHOR8_LIGHT
, COL_AUTHOR9_LIGHT
};
1216 return aArrayLight
[ aAuthorIndex
% SAL_N_ELEMENTS( aArrayLight
) ];
1222 Color
AnnotationManagerImpl::GetColorDark(sal_uInt16 aAuthorIndex
)
1224 if (!Application::GetSettings().GetStyleSettings().GetHighContrastMode())
1226 static const Color aArrayAnkor
[] = {
1227 COL_AUTHOR1_DARK
, COL_AUTHOR2_DARK
, COL_AUTHOR3_DARK
,
1228 COL_AUTHOR4_DARK
, COL_AUTHOR5_DARK
, COL_AUTHOR6_DARK
,
1229 COL_AUTHOR7_DARK
, COL_AUTHOR8_DARK
, COL_AUTHOR9_DARK
};
1231 return aArrayAnkor
[ aAuthorIndex
% SAL_N_ELEMENTS( aArrayAnkor
) ];
1237 SdPage
* AnnotationManagerImpl::GetNextPage( SdPage
const * pPage
, bool bForward
)
1239 if( pPage
== nullptr )
1242 return mpDoc
->GetSdPage(0, PageKind::Standard
); // first page
1244 return mpDoc
->GetMasterSdPage( mpDoc
->GetMasterSdPageCount(PageKind::Standard
) - 1, PageKind::Standard
); // last page
1247 sal_uInt16 nPageNum
= (pPage
->GetPageNum() - 1) >> 1;
1249 // first all non master pages
1250 if( !pPage
->IsMasterPage() )
1254 if( nPageNum
>= mpDoc
->GetSdPageCount(PageKind::Standard
)-1 )
1256 // we reached end of draw pages, start with master pages (skip handout master for draw)
1257 return mpDoc
->GetMasterSdPage( (mpDoc
->GetDocumentType() == DocumentType::Impress
) ? 0 : 1, PageKind::Standard
);
1264 return nullptr; // we are already on the first draw page, finished
1268 return mpDoc
->GetSdPage(nPageNum
, PageKind::Standard
);
1274 if( nPageNum
>= mpDoc
->GetMasterSdPageCount(PageKind::Standard
)-1 )
1276 return nullptr; // we reached the end, there is nothing more to see here
1282 if( nPageNum
== (mpDoc
->GetDocumentType() == DocumentType::Impress
? 0 : 1) )
1284 // we reached beginning of master pages, start with end if pages
1285 return mpDoc
->GetSdPage( mpDoc
->GetSdPageCount(PageKind::Standard
)-1, PageKind::Standard
);
1290 return mpDoc
->GetMasterSdPage(nPageNum
,PageKind::Standard
);
1294 SdPage
* AnnotationManagerImpl::GetCurrentPage()
1296 if (mrBase
.GetMainViewShell().get())
1297 return mrBase
.GetMainViewShell()->getCurrentPage();
1301 AnnotationManager::AnnotationManager( ViewShellBase
& rViewShellBase
)
1302 : mxImpl( new AnnotationManagerImpl( rViewShellBase
) )
1307 AnnotationManager::~AnnotationManager()
1312 void AnnotationManager::ExecuteAnnotation(SfxRequest
const & rRequest
)
1314 mxImpl
->ExecuteAnnotation( rRequest
);
1317 void AnnotationManager::GetAnnotationState(SfxItemSet
& rItemSet
)
1319 mxImpl
->GetAnnotationState(rItemSet
);
1324 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */