tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sd / source / ui / annotations / annotationmanager.cxx
blobe3743c97ca42ab7ad9c63701b0c061d86b356259
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <com/sun/star/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>
73 #include <sdpage.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>
80 #include <sdmod.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>
98 #include <cmath>
99 #include <memory>
101 using namespace css;
103 namespace sd
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();
127 return nullptr;
130 static SfxDispatcher* getDispatcher( ViewShellBase const & rBase )
132 auto pMainViewShell = rBase.GetMainViewShell().get();
133 if( pMainViewShell && pMainViewShell->GetViewFrame() )
134 return pMainViewShell->GetViewFrame()->GetDispatcher();
136 return nullptr;
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)
150 OUString sRet;
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 );
160 if (aDate==aSysDate)
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 );
171 return sRet;
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
189 addListener();
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&)
221 removeListener();
223 if( mnUpdateTagsEvent )
225 Application::RemoveUserEvent( mnUpdateTagsEvent );
226 mnUpdateTagsEvent = nullptr;
229 mxView.clear();
230 mxCurrentPage.clear();
233 // XEventListener
234 void SAL_CALL AnnotationManagerImpl::notifyEvent( const css::document::EventObject& aEvent )
236 if( !(aEvent.EventName == "OnAnnotationInserted" || aEvent.EventName == "OnAnnotationRemoved" || aEvent.EventName == "OnAnnotationChanged") )
237 return;
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);
254 UpdateTags();
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())
276 return *iterator;
278 } while(pPage);
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 );
294 UpdateTags();
298 void AnnotationManagerImpl::ExecuteAnnotation(SfxRequest const & rReq )
300 switch( rReq.GetSlot() )
302 case SID_INSERT_POSTIT:
303 ExecuteInsertAnnotation( rReq );
304 break;
305 case SID_DELETE_POSTIT:
306 case SID_DELETEALL_POSTIT:
307 case SID_DELETEALLBYAUTHOR_POSTIT:
308 ExecuteDeleteAnnotation( rReq );
309 break;
310 case SID_EDIT_POSTIT:
311 ExecuteEditAnnotation( rReq );
312 break;
313 case SID_PREVIOUS_POSTIT:
314 case SID_NEXT_POSTIT:
315 SelectNextAnnotation( rReq.GetSlot() == SID_NEXT_POSTIT );
316 break;
317 case SID_REPLYTO_POSTIT:
318 ExecuteReplyToAnnotation( rReq );
319 break;
320 case SID_TOGGLE_NOTES:
321 ShowAnnotations( !mbShowAnnotations );
322 break;
326 void AnnotationManagerImpl::ExecuteInsertAnnotation(SfxRequest const & rReq)
328 if (!comphelper::LibreOfficeKit::isActive() || comphelper::LibreOfficeKit::isTiledAnnotations())
329 ShowAnnotations(true);
331 const SfxItemSet* pArgs = rReq.GetArgs();
332 OUString sText;
333 if (pArgs)
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();
352 break;
353 case SID_DELETEALLBYAUTHOR_POSTIT:
354 if( pArgs )
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 );
363 break;
364 case SID_DELETE_POSTIT:
366 rtl::Reference<sdr::annotation::Annotation> xAnnotation;
367 sal_uInt32 nId = 0;
368 if( pArgs )
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();
384 if (nId != 0)
385 xAnnotation = GetAnnotationById(nId);
386 else if( !xAnnotation.is() )
387 GetSelectedAnnotation(xAnnotation);
389 DeleteAnnotation(xAnnotation);
391 break;
394 UpdateTags();
397 void AnnotationManagerImpl::ExecuteEditAnnotation(SfxRequest const & rReq)
399 const SfxItemSet* pArgs = rReq.GetArgs();
400 rtl::Reference<sdr::annotation::Annotation> xAnnotation;
401 OUString sText;
402 sal_Int32 nPositionX = -1;
403 sal_Int32 nPositionY = -1;
405 if (!pArgs)
406 return;
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())
453 mpDoc->EndUndo();
455 UpdateTags(true);
458 void AnnotationManagerImpl::InsertAnnotation(const OUString& rText)
460 SdPage* pPage = GetCurrentPage();
461 if (!pPage)
462 return;
464 if (mpDoc->IsUndoEnabled())
465 mpDoc->BegUndo(SdResId(STR_ANNOTATION_UNDO_INSERT));
467 // find free space for new annotation
468 int y = 0;
469 int x = 0;
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;
478 while (true)
480 ::tools::Rectangle aNewRect(Point(x, y), Size(fWidth, fHeight));
481 bool bFree = true;
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)))
491 bFree = false;
492 break;
496 if (!bFree)
498 x += fWidth;
499 if (x > fPageWidth)
501 x = 0;
502 y += fHeight;
505 else
507 break;
512 rtl::Reference<sdr::annotation::Annotation> xAnnotation = pPage->createAnnotation();
514 OUString sAuthor;
515 if (comphelper::LibreOfficeKit::isActive())
516 sAuthor = mrBase.GetMainViewShell()->GetView()->GetAuthor();
517 else
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() );
535 // set position
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())
543 mpDoc->EndUndo();
545 // Tell our LOK clients about new comment added
546 LOKCommentNotifyAll(sdr::annotation::CommentNotificationType::Add, *xAnnotation);
548 UpdateTags(true);
549 SelectAnnotation(xAnnotation, true);
552 void AnnotationManagerImpl::ExecuteReplyToAnnotation( SfxRequest const & rReq )
554 rtl::Reference< sdr::annotation::Annotation> xAnnotation;
555 const SfxItemSet* pArgs = rReq.GetArgs();
556 OUString sReplyText;
557 if( pArgs )
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 );
581 if( !pTextApi )
582 return;
584 if (mpDoc->IsUndoEnabled())
585 mpDoc->BegUndo(SdResId(STR_ANNOTATION_REPLY));
587 if (xAnnotation)
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() )
609 sQuote = "...";
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));
620 ESelection aSel;
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();
636 else
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() )
651 mpDoc->EndUndo();
653 UpdateTags(true);
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() )
669 mpDoc->EndUndo();
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 );
683 if( pPage )
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 );
696 } while( pPage );
698 if( mpDoc->IsUndoEnabled() )
699 mpDoc->EndUndo();
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 );
721 while( pPage );
723 mxSelectedAnnotation.clear();
725 if( mpDoc->IsUndoEnabled() )
726 mpDoc->EndUndo();
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();
785 if( !pPage )
786 return;
788 sdr::annotation::AnnotationVector const& aAnnotations = pPage->getAnnotations();
790 if( bForward )
792 if( xCurrent.is() )
794 auto iter = std::find(aAnnotations.begin(), aAnnotations.end(), xCurrent);
795 if (iter != aAnnotations.end())
797 ++iter;
798 if( iter != aAnnotations.end() )
800 SelectAnnotation( *iter );
801 return;
805 else if( !aAnnotations.empty() )
807 SelectAnnotation( *(aAnnotations.begin()) );
808 return;
811 else
813 if( xCurrent.is() )
815 auto iter = std::find(aAnnotations.begin(), aAnnotations.end(), xCurrent);
816 if (iter != aAnnotations.end() && iter != aAnnotations.begin())
818 --iter;
819 SelectAnnotation( *iter );
820 return;
823 else if( !aAnnotations.empty() )
825 auto iterator = aAnnotations.end();
826 iterator--;
827 SelectAnnotation(*iterator);
828 return;
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 );
849 if( pDispatcher )
850 pDispatcher->Execute( bForward ? SID_NEXT_POSTIT : SID_PREVIOUS_POSTIT );
852 return;
856 while( pPage );
858 // The question text depends on the search direction.
859 bool bImpress = mpDoc->GetDocumentType() == DocumentType::Impress;
860 TranslateId pStringId;
861 if(bForward)
862 pStringId = bImpress ? STR_ANNOTATION_WRAP_FORWARD : STR_ANNOTATION_WRAP_FORWARD_DRAW;
863 else
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)
873 break;
875 while( true );
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 );
891 if( pBindings )
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())
906 return;
908 rtl::Reference<SdPage> xPage = mrBase.GetMainViewShell()->getCurrentPage();
909 if (xPage != mxCurrentPage)
911 mxCurrentPage = std::move(xPage);
912 UpdateTags(true);
916 void AnnotationManagerImpl::UpdateTags(bool bSynchron)
918 SyncAnnotationObjects();
920 invalidateSlots();
922 if (bSynchron)
924 if (mnUpdateTagsEvent)
925 Application::RemoveUserEvent(mnUpdateTagsEvent);
927 UpdateTagsHdl(nullptr);
929 else
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();
944 invalidateSlots();
947 namespace
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)
961 if (rInfo.mbColor)
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)
984 return;
986 sd::DrawDocShell* pDocShell = dynamic_cast<sd::DrawDocShell*>(SfxObjectShell::Current());
987 sd::ViewShell* pViewShell = pDocShell ? pDocShell->GetViewShell() : nullptr;
989 if (!pViewShell)
991 pViewShell = mrBase.GetMainViewShell().get();
992 if (!pViewShell)
993 return;
996 auto* pView = pViewShell->GetView();
997 if (!pView)
998 return;
1000 if (!pView->GetSdrPageView())
1001 return;
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();
1011 if (pObject)
1013 nIndex++;
1014 continue;
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
1043 .nIndex = nIndex,
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));
1089 else
1091 SdrObjKind ekind = SdrObjKind::Polygon;
1093 switch (rInfo.meType)
1095 case sdr::annotation::AnnotationType::Polygon:
1096 ekind = SdrObjKind::Polygon;
1097 break;
1098 case sdr::annotation::AnnotationType::Line:
1099 ekind = SdrObjKind::PolyLine;
1100 break;
1101 case sdr::annotation::AnnotationType::Ink:
1102 ekind = SdrObjKind::FreehandLine;
1103 break;
1104 default:
1105 break;
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());
1122 nIndex++;
1125 if (bAnnotatonInserted && mpDoc->IsUndoEnabled())
1126 mpDoc->EndUndo();
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();
1149 break;
1151 case EventMultiplexerEventId::MainViewRemoved:
1152 mxView.clear();
1153 onSelectionChanged();
1154 break;
1156 case EventMultiplexerEventId::MainViewAdded:
1157 mxView = mrBase.GetDrawController();
1158 onSelectionChanged();
1159 break;
1161 default: break;
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;
1185 return COL_WHITE;
1188 Color AnnotationManagerImpl::GetColorLight(sal_uInt16 aAuthorIndex)
1190 Color aColor = GetColor(aAuthorIndex);
1191 if (MiscSettings::GetUseDarkMode())
1192 aColor.IncreaseLuminance(30);
1193 else
1194 aColor.DecreaseLuminance(30);
1195 return aColor;
1198 Color AnnotationManagerImpl::GetColorDark(sal_uInt16 aAuthorIndex)
1200 Color aColor = GetColor(aAuthorIndex);;
1201 if (MiscSettings::GetUseDarkMode())
1202 aColor.DecreaseLuminance(80);
1203 else
1204 aColor.IncreaseLuminance(80);
1205 return aColor;
1208 SdPage* AnnotationManagerImpl::GetNextPage( SdPage const * pPage, bool bForward )
1210 if( pPage == nullptr )
1212 if (bForward)
1213 return mpDoc->GetSdPage(0, PageKind::Standard ); // first page
1214 else
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() )
1223 if( bForward )
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 );
1230 nPageNum++;
1232 else
1234 if( nPageNum == 0 )
1235 return nullptr; // we are already on the first draw page, finished
1237 nPageNum--;
1239 return mpDoc->GetSdPage(nPageNum, PageKind::Standard);
1241 else
1243 if( bForward )
1245 if( nPageNum >= mpDoc->GetMasterSdPageCount(PageKind::Standard)-1 )
1247 return nullptr; // we reached the end, there is nothing more to see here
1249 nPageNum++;
1251 else
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 );
1259 nPageNum--;
1261 return mpDoc->GetMasterSdPage(nPageNum,PageKind::Standard);
1265 SdPage* AnnotationManagerImpl::GetCurrentPage()
1267 if (auto pMainViewShell = mrBase.GetMainViewShell().get())
1268 return pMainViewShell->getCurrentPage();
1269 return nullptr;
1272 AnnotationManager::AnnotationManager( ViewShellBase& rViewShellBase )
1273 : mxImpl( new AnnotationManagerImpl( rViewShellBase ) )
1275 mxImpl->init();
1278 AnnotationManager::~AnnotationManager()
1280 mxImpl->dispose();
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);
1298 } // end sd
1300 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */