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 <sal/config.h>
22 #include <sfx2/viewfrm.hxx>
23 #include <sfx2/bindings.hxx>
24 #include <sfx2/dispatch.hxx>
26 #include <vcl/help.hxx>
27 #include <vcl/lazydelete.hxx>
28 #include <vcl/ptrstyle.hxx>
29 #include <vcl/svapp.hxx>
31 #include <svx/sdrpagewindow.hxx>
32 #include <svx/sdrpaintwindow.hxx>
33 #include <svx/sdr/overlay/overlaybitmapex.hxx>
34 #include <svx/sdr/overlay/overlaymanager.hxx>
35 #include <svx/svxids.hrc>
36 #include <svx/svdpagv.hxx>
38 #include <view/viewoverlaymanager.hxx>
41 #include <DrawDocShell.hxx>
42 #include <strings.hrc>
43 #include <bitmaps.hlst>
44 #include <sdresid.hxx>
45 #include <EventMultiplexer.hxx>
47 #include <ViewShellBase.hxx>
48 #include <ViewShell.hxx>
50 #include <smarttag.hxx>
52 using namespace ::com::sun::star::uno
;
62 const sal_uInt16 gButtonSlots
[] = { SID_INSERT_TABLE
, SID_INSERT_DIAGRAM
, SID_INSERT_GRAPHIC
, SID_INSERT_AVMEDIA
};
63 const TranslateId gButtonToolTips
[] = { STR_INSERT_TABLE
, STR_INSERT_CHART
, STR_INSERT_PICTURE
, STR_INSERT_MOVIE
};
65 constexpr rtl::OUStringConstExpr aSmallPlaceHolders
[] =
67 BMP_PLACEHOLDER_TABLE_SMALL
,
68 BMP_PLACEHOLDER_CHART_SMALL
,
69 BMP_PLACEHOLDER_IMAGE_SMALL
,
70 BMP_PLACEHOLDER_MOVIE_SMALL
,
71 BMP_PLACEHOLDER_TABLE_SMALL_HOVER
,
72 BMP_PLACEHOLDER_CHART_SMALL_HOVER
,
73 BMP_PLACEHOLDER_IMAGE_SMALL_HOVER
,
74 BMP_PLACEHOLDER_MOVIE_SMALL_HOVER
77 constexpr rtl::OUStringConstExpr aBigPlaceHolders
[] =
79 BMP_PLACEHOLDER_TABLE_LARGE
,
80 BMP_PLACEHOLDER_CHART_LARGE
,
81 BMP_PLACEHOLDER_IMAGE_LARGE
,
82 BMP_PLACEHOLDER_MOVIE_LARGE
,
83 BMP_PLACEHOLDER_TABLE_LARGE_HOVER
,
84 BMP_PLACEHOLDER_CHART_LARGE_HOVER
,
85 BMP_PLACEHOLDER_IMAGE_LARGE_HOVER
,
86 BMP_PLACEHOLDER_MOVIE_LARGE_HOVER
89 static BitmapEx
* getButtonImage( int index
, bool large
)
91 static vcl::DeleteOnDeinit
< BitmapEx
> gSmallButtonImages
[SAL_N_ELEMENTS(aSmallPlaceHolders
)] = { vcl::DeleteOnDeinitFlag::Empty
, vcl::DeleteOnDeinitFlag::Empty
, vcl::DeleteOnDeinitFlag::Empty
, vcl::DeleteOnDeinitFlag::Empty
, vcl::DeleteOnDeinitFlag::Empty
, vcl::DeleteOnDeinitFlag::Empty
, vcl::DeleteOnDeinitFlag::Empty
, vcl::DeleteOnDeinitFlag::Empty
};
92 static vcl::DeleteOnDeinit
< BitmapEx
> gLargeButtonImages
[SAL_N_ELEMENTS(aBigPlaceHolders
)] = { vcl::DeleteOnDeinitFlag::Empty
, vcl::DeleteOnDeinitFlag::Empty
, vcl::DeleteOnDeinitFlag::Empty
, vcl::DeleteOnDeinitFlag::Empty
, vcl::DeleteOnDeinitFlag::Empty
, vcl::DeleteOnDeinitFlag::Empty
, vcl::DeleteOnDeinitFlag::Empty
, vcl::DeleteOnDeinitFlag::Empty
};
94 assert(SAL_N_ELEMENTS(aSmallPlaceHolders
) == SAL_N_ELEMENTS(aBigPlaceHolders
));
96 if( !gSmallButtonImages
[0].get() )
98 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aSmallPlaceHolders
); i
++ )
100 gSmallButtonImages
[i
].set(OUString(aSmallPlaceHolders
[i
]));
101 gLargeButtonImages
[i
].set(OUString(aBigPlaceHolders
[i
]));
107 return gLargeButtonImages
[index
].get();
111 return gSmallButtonImages
[index
].get();
115 const sal_uInt32 SMART_TAG_HDL_NUM
= SAL_MAX_UINT32
;
119 class ChangePlaceholderTag
: public SmartTag
121 friend class ImageButtonHdl
;
123 ChangePlaceholderTag( ::sd::View
& rView
, SdrObject
& rPlaceholderObj
);
125 /** returns true if the SmartTag handled the event. */
126 virtual bool MouseButtonDown( const MouseEvent
&, SmartHdl
& ) override
;
128 /** returns true if the SmartTag consumes this event. */
129 virtual bool KeyInput( const KeyEvent
& rKEvt
) override
;
131 BitmapEx
createOverlayImage( int nHighlight
);
134 virtual void addCustomHandles( SdrHdlList
& rHandlerList
) override
;
137 ::unotools::WeakReference
<SdrObject
> mxPlaceholderObj
;
140 class ImageButtonHdl
: public SmartHdl
143 ImageButtonHdl( const SmartTagReference
& xTag
, /* sal_uInt16 nSID, const Image& rImage, const Image& rImageMO, */ const Point
& rPnt
);
144 virtual ~ImageButtonHdl() override
;
145 virtual void CreateB2dIAObject() override
;
146 virtual bool IsFocusHdl() const override
;
147 virtual PointerStyle
GetPointer() const override
;
149 virtual void onMouseEnter(const MouseEvent
& rMEvt
) override
;
150 virtual void onHelpRequest() override
;
151 virtual void onMouseLeave() override
;
153 int getHighlightId() const { return mnHighlightId
; }
156 static void HideTip();
159 rtl::Reference
< ChangePlaceholderTag
> mxChangePlaceholderTag
;
167 ImageButtonHdl::ImageButtonHdl( const SmartTagReference
& xTag
/*, sal_uInt16 nSID, const Image& rImage, const Image& rImageMO*/, const Point
& rPnt
)
168 : SmartHdl( xTag
, rPnt
, SdrHdlKind::SmartTag
)
169 , mxChangePlaceholderTag( dynamic_cast< ChangePlaceholderTag
* >( xTag
.get() ) )
170 , mnHighlightId( -1 )
171 , maImageSize( 42, 42 )
175 ImageButtonHdl::~ImageButtonHdl()
180 void ImageButtonHdl::HideTip()
182 Help::HideBalloonAndQuickHelp();
185 void ImageButtonHdl::ShowTip()
187 if (!pHdlList
|| !pHdlList
->GetView() || mnHighlightId
== -1)
190 OutputDevice
* pDev
= pHdlList
->GetView()->GetFirstOutputDevice();
191 if( pDev
== nullptr )
192 pDev
= Application::GetDefaultDevice();
194 OUString
aHelpText(SdResId(gButtonToolTips
[mnHighlightId
]));
195 Point
aHelpPos(pDev
->LogicToPixel(GetPos()));
196 if (mnHighlightId
== 1)
197 aHelpPos
.Move(maImageSize
.Width(), 0);
198 else if (mnHighlightId
== 2)
199 aHelpPos
.Move(0, maImageSize
.Height());
200 else if (mnHighlightId
== 3)
201 aHelpPos
.Move(maImageSize
.Width(), maImageSize
.Height());
202 ::tools::Rectangle
aLogicPix(aHelpPos
, maImageSize
);
203 vcl::Window
* pWindow
= pHdlList
->GetView()->GetFirstOutputDevice()->GetOwnerWindow();
204 ::tools::Rectangle
aScreenRect(pWindow
->OutputToScreenPixel(aLogicPix
.TopLeft()),
205 pWindow
->OutputToScreenPixel(aLogicPix
.BottomRight()));
206 Help::ShowQuickHelp(pWindow
, aScreenRect
, aHelpText
);
209 void ImageButtonHdl::onHelpRequest()
214 void ImageButtonHdl::onMouseEnter(const MouseEvent
& rMEvt
)
216 if( !(pHdlList
&& pHdlList
->GetView()))
219 int nHighlightId
= 0;
220 OutputDevice
* pDev
= pHdlList
->GetView()->GetFirstOutputDevice();
221 if( pDev
== nullptr )
222 pDev
= Application::GetDefaultDevice();
224 Point
aMDPos( rMEvt
.GetPosPixel() );
225 aMDPos
-= pDev
->LogicToPixel( GetPos() );
227 nHighlightId
+= aMDPos
.X() > maImageSize
.Width() ? 1 : 0;
228 nHighlightId
+= aMDPos
.Y() > maImageSize
.Height() ? 2 : 0;
230 if( mnHighlightId
!= nHighlightId
)
234 mnHighlightId
= nHighlightId
;
242 void ImageButtonHdl::onMouseLeave()
249 void ImageButtonHdl::CreateB2dIAObject()
251 // first throw away old one
254 const Point
aTagPos( GetPos() );
255 basegfx::B2DPoint
aPosition( aTagPos
.X(), aTagPos
.Y() );
257 BitmapEx
aBitmapEx( mxChangePlaceholderTag
->createOverlayImage( mnHighlightId
) ); // maImageMO.GetBitmapEx() : maImage.GetBitmapEx() );
258 maImageSize
= aBitmapEx
.GetSizePixel();
259 maImageSize
.setWidth( maImageSize
.Width() >> 1 );
260 maImageSize
.setHeight( maImageSize
.Height() >> 1 );
265 SdrMarkView
* pView
= pHdlList
->GetView();
267 if(!pView
|| pView
->areMarkHandlesHidden())
270 SdrPageView
* pPageView
= pView
->GetSdrPageView();
275 for(sal_uInt32 b
= 0; b
< pPageView
->PageWindowCount(); b
++)
277 const SdrPageWindow
& rPageWindow
= *pPageView
->GetPageWindow(b
);
279 SdrPaintWindow
& rPaintWindow
= rPageWindow
.GetPaintWindow();
280 const rtl::Reference
< sdr::overlay::OverlayManager
>& xManager
= rPageWindow
.GetOverlayManager();
281 if(rPaintWindow
.OutputToWindow() && xManager
.is() )
283 std::unique_ptr
<sdr::overlay::OverlayObject
> pOverlayObject(
284 new sdr::overlay::OverlayBitmapEx( aPosition
, aBitmapEx
, 0, 0 ));
287 insertNewlyCreatedOverlayObjectForSdrHdl(
288 std::move(pOverlayObject
),
289 rPageWindow
.GetObjectContact(),
295 bool ImageButtonHdl::IsFocusHdl() const
300 PointerStyle
ImageButtonHdl::GetPointer() const
302 return PointerStyle::Arrow
;
305 ChangePlaceholderTag::ChangePlaceholderTag( ::sd::View
& rView
, SdrObject
& rPlaceholderObj
)
307 , mxPlaceholderObj( &rPlaceholderObj
)
311 /** returns true if the ChangePlaceholderTag handled the event. */
312 bool ChangePlaceholderTag::MouseButtonDown( const MouseEvent
& /*rMEvt*/, SmartHdl
& rHdl
)
314 int nHighlightId
= static_cast< ImageButtonHdl
& >(rHdl
).getHighlightId();
315 if( nHighlightId
>= 0 )
317 sal_uInt16 nSID
= gButtonSlots
[nHighlightId
];
319 if( auto pPlaceholder
= mxPlaceholderObj
.get() )
321 // mark placeholder if it is not currently marked (or if also others are marked)
322 if( !mrView
.IsObjMarked( pPlaceholder
.get() ) || (mrView
.GetMarkedObjectList().GetMarkCount() != 1) )
324 SdrPageView
* pPV
= mrView
.GetSdrPageView();
325 mrView
.UnmarkAllObj(pPV
);
326 mrView
.MarkObj(pPlaceholder
.get(), pPV
);
330 mrView
.GetViewShell()->GetViewFrame()->GetDispatcher()->Execute( nSID
, SfxCallMode::ASYNCHRON
);
335 /** returns true if the SmartTag consumes this event. */
336 bool ChangePlaceholderTag::KeyInput( const KeyEvent
& rKEvt
)
338 sal_uInt16 nCode
= rKEvt
.GetKeyCode().GetCode();
354 BitmapEx
ChangePlaceholderTag::createOverlayImage( int nHighlight
)
357 if( auto pPlaceholder
= mxPlaceholderObj
.get() )
359 SmartTagReference
xThis( this );
360 const ::tools::Rectangle
& rSnapRect
= pPlaceholder
->GetSnapRect();
362 OutputDevice
* pDev
= mrView
.GetFirstOutputDevice();
363 if( pDev
== nullptr )
364 pDev
= Application::GetDefaultDevice();
366 Size aShapeSizePix
= pDev
->LogicToPixel(rSnapRect
.GetSize());
367 ::tools::Long nShapeSizePix
= std::min(aShapeSizePix
.Width(),aShapeSizePix
.Height());
369 bool bLarge
= nShapeSizePix
> 250;
371 Size
aSize( getButtonImage( 0, bLarge
)->GetSizePixel() );
373 aRet
.Scale(Size(aSize
.Width() << 1, aSize
.Height() << 1));
375 const ::tools::Rectangle
aRectSrc( Point( 0, 0 ), aSize
);
377 aRet
= *(getButtonImage((nHighlight
== 0) ? 4 : 0, bLarge
));
378 aRet
.Expand( aSize
.Width(), aSize
.Height(), true );
380 aRet
.CopyPixel( ::tools::Rectangle( Point( aSize
.Width(), 0 ), aSize
), aRectSrc
, getButtonImage((nHighlight
== 1) ? 5 : 1, bLarge
) );
381 aRet
.CopyPixel( ::tools::Rectangle( Point( 0, aSize
.Height() ), aSize
), aRectSrc
, getButtonImage((nHighlight
== 2) ? 6 : 2, bLarge
) );
382 aRet
.CopyPixel( ::tools::Rectangle( Point( aSize
.Width(), aSize
.Height() ), aSize
), aRectSrc
, getButtonImage((nHighlight
== 3) ? 7 : 3, bLarge
) );
388 void ChangePlaceholderTag::addCustomHandles( SdrHdlList
& rHandlerList
)
390 rtl::Reference
<SdrObject
> pPlaceholder
= mxPlaceholderObj
.get();
394 SmartTagReference
xThis( this );
395 const ::tools::Rectangle
& rSnapRect
= pPlaceholder
->GetSnapRect();
398 OutputDevice
* pDev
= mrView
.GetFirstOutputDevice();
399 if( pDev
== nullptr )
400 pDev
= Application::GetDefaultDevice();
402 Size aShapeSizePix
= pDev
->LogicToPixel(rSnapRect
.GetSize());
403 ::tools::Long nShapeSizePix
= std::min(aShapeSizePix
.Width(),aShapeSizePix
.Height());
404 if( 50 > nShapeSizePix
)
407 bool bLarge
= nShapeSizePix
> 250;
409 Size
aButtonSize( pDev
->PixelToLogic( getButtonImage(0, bLarge
)->GetSizePixel()) );
411 const int nColumns
= 2;
414 ::tools::Long all_width
= nColumns
* aButtonSize
.Width();
415 ::tools::Long all_height
= nRows
* aButtonSize
.Height();
417 Point
aPos( rSnapRect
.Center() );
418 aPos
.AdjustX( -(all_width
>> 1) );
419 aPos
.AdjustY( -(all_height
>> 1) );
421 std::unique_ptr
<ImageButtonHdl
> pHdl(new ImageButtonHdl( xThis
, aPoint
));
422 pHdl
->SetObjHdlNum( SMART_TAG_HDL_NUM
);
423 pHdl
->SetPageView( mrView
.GetSdrPageView() );
425 pHdl
->SetPos( aPos
);
427 rHandlerList
.AddHdl( std::move(pHdl
) );
430 ViewOverlayManager::ViewOverlayManager( ViewShellBase
& rViewShellBase
)
431 : mrBase( rViewShellBase
)
432 , mnUpdateTagsEvent( nullptr )
434 Link
<tools::EventMultiplexerEvent
&,void> aLink( LINK(this,ViewOverlayManager
,EventMultiplexerListener
) );
435 mrBase
.GetEventMultiplexer()->AddEventListener(aLink
);
437 StartListening( *mrBase
.GetDocShell() );
440 ViewOverlayManager::~ViewOverlayManager()
442 Link
<tools::EventMultiplexerEvent
&,void> aLink( LINK(this,ViewOverlayManager
,EventMultiplexerListener
) );
443 mrBase
.GetEventMultiplexer()->RemoveEventListener( aLink
);
445 if( mnUpdateTagsEvent
)
447 Application::RemoveUserEvent( mnUpdateTagsEvent
);
448 mnUpdateTagsEvent
= nullptr;
454 void ViewOverlayManager::Notify(SfxBroadcaster
&, const SfxHint
& rHint
)
456 if (rHint
.GetId() == SfxHintId::DocChanged
)
462 void ViewOverlayManager::onZoomChanged()
464 if( !maTagVector
.empty() )
470 void ViewOverlayManager::UpdateTags()
472 if( !mnUpdateTagsEvent
)
473 mnUpdateTagsEvent
= Application::PostUserEvent( LINK( this, ViewOverlayManager
, UpdateTagsHdl
) );
476 IMPL_LINK_NOARG(ViewOverlayManager
, UpdateTagsHdl
, void*, void)
478 mnUpdateTagsEvent
= nullptr;
479 bool bChanges
= DisposeTags();
480 bChanges
|= CreateTags();
482 if( bChanges
&& mrBase
.GetDrawView() )
483 static_cast< ::sd::View
* >( mrBase
.GetDrawView() )->updateHandles();
486 bool ViewOverlayManager::CreateTags()
488 bool bChanges
= false;
490 std::shared_ptr
<ViewShell
> aMainShell
= mrBase
.GetMainViewShell();
492 SdPage
* pPage
= aMainShell
? aMainShell
->getCurrentPage() : nullptr;
494 if( pPage
&& !pPage
->IsMasterPage() && (pPage
->GetPageKind() == PageKind::Standard
) )
496 const std::list
< SdrObject
* >& rShapes
= pPage
->GetPresentationShapeList().getList();
498 for( SdrObject
* pShape
: rShapes
)
500 if( pShape
->IsEmptyPresObj() && (pShape
->GetObjIdentifier() == SdrObjKind::OutlineText
) && (mrBase
.GetDrawView()->GetTextEditObject() != pShape
) )
502 rtl::Reference
< SmartTag
> xTag( new ChangePlaceholderTag( *mrBase
.GetMainViewShell()->GetView(), *pShape
) );
503 maTagVector
.push_back(xTag
);
512 bool ViewOverlayManager::DisposeTags()
514 if( !maTagVector
.empty() )
517 vec
.swap( maTagVector
);
519 for (auto& rxViewTag
: vec
)
520 rxViewTag
->Dispose();
527 IMPL_LINK(ViewOverlayManager
,EventMultiplexerListener
,
528 tools::EventMultiplexerEvent
&, rEvent
, void)
530 switch (rEvent
.meEventId
)
532 case EventMultiplexerEventId::MainViewAdded
:
533 case EventMultiplexerEventId::ViewAdded
:
534 case EventMultiplexerEventId::BeginTextEdit
:
535 case EventMultiplexerEventId::EndTextEdit
:
536 case EventMultiplexerEventId::CurrentPageChanged
:
545 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */