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 <tools/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 OUString 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 OUString 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 ::tools::DeleteOnDeinit
< BitmapEx
> gSmallButtonImages
[SAL_N_ELEMENTS(aSmallPlaceHolders
)] = {
92 ::tools::DeleteOnDeinitFlag::Empty
, ::tools::DeleteOnDeinitFlag::Empty
,
93 ::tools::DeleteOnDeinitFlag::Empty
, ::tools::DeleteOnDeinitFlag::Empty
,
94 ::tools::DeleteOnDeinitFlag::Empty
, ::tools::DeleteOnDeinitFlag::Empty
,
95 ::tools::DeleteOnDeinitFlag::Empty
, ::tools::DeleteOnDeinitFlag::Empty
};
96 static ::tools::DeleteOnDeinit
< BitmapEx
> gLargeButtonImages
[SAL_N_ELEMENTS(aBigPlaceHolders
)] = {
97 ::tools::DeleteOnDeinitFlag::Empty
, ::tools::DeleteOnDeinitFlag::Empty
,
98 ::tools::DeleteOnDeinitFlag::Empty
, ::tools::DeleteOnDeinitFlag::Empty
,
99 ::tools::DeleteOnDeinitFlag::Empty
, ::tools::DeleteOnDeinitFlag::Empty
,
100 ::tools::DeleteOnDeinitFlag::Empty
, ::tools::DeleteOnDeinitFlag::Empty
};
102 assert(SAL_N_ELEMENTS(aSmallPlaceHolders
) == SAL_N_ELEMENTS(aBigPlaceHolders
));
104 if( !gSmallButtonImages
[0].get() )
106 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aSmallPlaceHolders
); i
++ )
108 gSmallButtonImages
[i
].set(aSmallPlaceHolders
[i
]);
109 gLargeButtonImages
[i
].set(aBigPlaceHolders
[i
]);
115 return *gLargeButtonImages
[index
].get();
119 return *gSmallButtonImages
[index
].get();
123 const sal_uInt32 SMART_TAG_HDL_NUM
= SAL_MAX_UINT32
;
127 class ChangePlaceholderTag
: public SmartTag
129 friend class ImageButtonHdl
;
131 ChangePlaceholderTag( ::sd::View
& rView
, SdrObject
& rPlaceholderObj
);
133 /** returns true if the SmartTag handled the event. */
134 virtual bool MouseButtonDown( const MouseEvent
&, SmartHdl
& ) override
;
136 /** returns true if the SmartTag consumes this event. */
137 virtual bool KeyInput( const KeyEvent
& rKEvt
) override
;
139 BitmapEx
createOverlayImage( int nHighlight
);
142 virtual void addCustomHandles( SdrHdlList
& rHandlerList
) override
;
145 ::unotools::WeakReference
<SdrObject
> mxPlaceholderObj
;
148 class ImageButtonHdl
: public SmartHdl
151 ImageButtonHdl( const SmartTagReference
& xTag
, /* sal_uInt16 nSID, const Image& rImage, const Image& rImageMO, */ const Point
& rPnt
);
152 virtual ~ImageButtonHdl() override
;
153 virtual void CreateB2dIAObject() override
;
154 virtual bool IsFocusHdl() const override
;
155 virtual PointerStyle
GetPointer() const override
;
157 virtual void onMouseEnter(const MouseEvent
& rMEvt
) override
;
158 virtual void onHelpRequest() override
;
159 virtual void onMouseLeave() override
;
161 int getHighlightId() const { return mnHighlightId
; }
164 static void HideTip();
167 rtl::Reference
< ChangePlaceholderTag
> mxChangePlaceholderTag
;
175 ImageButtonHdl::ImageButtonHdl( const SmartTagReference
& xTag
/*, sal_uInt16 nSID, const Image& rImage, const Image& rImageMO*/, const Point
& rPnt
)
176 : SmartHdl( xTag
, rPnt
, SdrHdlKind::SmartTag
)
177 , mxChangePlaceholderTag( dynamic_cast< ChangePlaceholderTag
* >( xTag
.get() ) )
178 , mnHighlightId( -1 )
179 , maImageSize( 42, 42 )
183 ImageButtonHdl::~ImageButtonHdl()
188 void ImageButtonHdl::HideTip()
190 Help::HideBalloonAndQuickHelp();
193 void ImageButtonHdl::ShowTip()
195 if (!m_pHdlList
|| !m_pHdlList
->GetView() || mnHighlightId
== -1)
198 OutputDevice
* pDev
= m_pHdlList
->GetView()->GetFirstOutputDevice();
199 if( pDev
== nullptr )
200 pDev
= Application::GetDefaultDevice();
202 OUString
aHelpText(SdResId(gButtonToolTips
[mnHighlightId
]));
203 Point
aHelpPos(pDev
->LogicToPixel(GetPos()));
204 if (mnHighlightId
== 1)
205 aHelpPos
.Move(maImageSize
.Width(), 0);
206 else if (mnHighlightId
== 2)
207 aHelpPos
.Move(0, maImageSize
.Height());
208 else if (mnHighlightId
== 3)
209 aHelpPos
.Move(maImageSize
.Width(), maImageSize
.Height());
210 ::tools::Rectangle
aLogicPix(aHelpPos
, maImageSize
);
211 vcl::Window
* pWindow
= m_pHdlList
->GetView()->GetFirstOutputDevice()->GetOwnerWindow();
212 ::tools::Rectangle
aScreenRect(pWindow
->OutputToScreenPixel(aLogicPix
.TopLeft()),
213 pWindow
->OutputToScreenPixel(aLogicPix
.BottomRight()));
214 Help::ShowQuickHelp(pWindow
, aScreenRect
, aHelpText
);
217 void ImageButtonHdl::onHelpRequest()
222 void ImageButtonHdl::onMouseEnter(const MouseEvent
& rMEvt
)
224 if( !(m_pHdlList
&& m_pHdlList
->GetView()))
227 int nHighlightId
= 0;
228 OutputDevice
* pDev
= m_pHdlList
->GetView()->GetFirstOutputDevice();
229 if( pDev
== nullptr )
230 pDev
= Application::GetDefaultDevice();
232 Point
aMDPos( rMEvt
.GetPosPixel() );
233 aMDPos
-= pDev
->LogicToPixel( GetPos() );
235 nHighlightId
+= aMDPos
.X() > maImageSize
.Width() ? 1 : 0;
236 nHighlightId
+= aMDPos
.Y() > maImageSize
.Height() ? 2 : 0;
238 if( mnHighlightId
!= nHighlightId
)
242 mnHighlightId
= nHighlightId
;
250 void ImageButtonHdl::onMouseLeave()
257 void ImageButtonHdl::CreateB2dIAObject()
259 // first throw away old one
262 const Point
aTagPos( GetPos() );
263 basegfx::B2DPoint
aPosition( aTagPos
.X(), aTagPos
.Y() );
265 BitmapEx
aBitmapEx( mxChangePlaceholderTag
->createOverlayImage( mnHighlightId
) ); // maImageMO.GetBitmapEx() : maImage.GetBitmapEx() );
266 maImageSize
= aBitmapEx
.GetSizePixel();
267 maImageSize
.setWidth( maImageSize
.Width() >> 1 );
268 maImageSize
.setHeight( maImageSize
.Height() >> 1 );
273 SdrMarkView
* pView
= m_pHdlList
->GetView();
275 if(!pView
|| pView
->areMarkHandlesHidden())
278 SdrPageView
* pPageView
= pView
->GetSdrPageView();
283 for(sal_uInt32 b
= 0; b
< pPageView
->PageWindowCount(); b
++)
285 const SdrPageWindow
& rPageWindow
= *pPageView
->GetPageWindow(b
);
287 SdrPaintWindow
& rPaintWindow
= rPageWindow
.GetPaintWindow();
288 const rtl::Reference
< sdr::overlay::OverlayManager
>& xManager
= rPageWindow
.GetOverlayManager();
289 if(rPaintWindow
.OutputToWindow() && xManager
.is() )
291 std::unique_ptr
<sdr::overlay::OverlayObject
> pOverlayObject(
292 new sdr::overlay::OverlayBitmapEx( aPosition
, aBitmapEx
, 0, 0 ));
295 insertNewlyCreatedOverlayObjectForSdrHdl(
296 std::move(pOverlayObject
),
297 rPageWindow
.GetObjectContact(),
303 bool ImageButtonHdl::IsFocusHdl() const
308 PointerStyle
ImageButtonHdl::GetPointer() const
310 return PointerStyle::Arrow
;
313 ChangePlaceholderTag::ChangePlaceholderTag( ::sd::View
& rView
, SdrObject
& rPlaceholderObj
)
315 , mxPlaceholderObj( &rPlaceholderObj
)
319 /** returns true if the ChangePlaceholderTag handled the event. */
320 bool ChangePlaceholderTag::MouseButtonDown( const MouseEvent
& /*rMEvt*/, SmartHdl
& rHdl
)
322 int nHighlightId
= static_cast< ImageButtonHdl
& >(rHdl
).getHighlightId();
323 if( nHighlightId
>= 0 )
325 sal_uInt16 nSID
= gButtonSlots
[nHighlightId
];
327 if( auto pPlaceholder
= mxPlaceholderObj
.get() )
329 const SdrMarkList
& rMarkList
= mrView
.GetMarkedObjectList();
330 // mark placeholder if it is not currently marked (or if also others are marked)
331 if( !mrView
.IsObjMarked( pPlaceholder
.get() ) || (rMarkList
.GetMarkCount() != 1) )
333 SdrPageView
* pPV
= mrView
.GetSdrPageView();
334 mrView
.UnmarkAllObj(pPV
);
335 mrView
.MarkObj(pPlaceholder
.get(), pPV
);
339 mrView
.GetViewShell()->GetViewFrame()->GetDispatcher()->Execute( nSID
, SfxCallMode::ASYNCHRON
);
344 /** returns true if the SmartTag consumes this event. */
345 bool ChangePlaceholderTag::KeyInput( const KeyEvent
& rKEvt
)
347 sal_uInt16 nCode
= rKEvt
.GetKeyCode().GetCode();
363 BitmapEx
ChangePlaceholderTag::createOverlayImage( int nHighlight
)
366 if( auto pPlaceholder
= mxPlaceholderObj
.get() )
368 SmartTagReference
xThis( this );
369 const ::tools::Rectangle
& rSnapRect
= pPlaceholder
->GetSnapRect();
371 OutputDevice
* pDev
= mrView
.GetFirstOutputDevice();
372 if( pDev
== nullptr )
373 pDev
= Application::GetDefaultDevice();
375 Size aShapeSizePix
= pDev
->LogicToPixel(rSnapRect
.GetSize());
376 ::tools::Long nShapeSizePix
= std::min(aShapeSizePix
.Width(),aShapeSizePix
.Height());
378 bool bLarge
= nShapeSizePix
> 250;
380 Size
aSize( getButtonImage( 0, bLarge
).GetSizePixel() );
382 aRet
.Scale(Size(aSize
.Width() << 1, aSize
.Height() << 1));
384 const ::tools::Rectangle
aRectSrc( Point( 0, 0 ), aSize
);
386 aRet
= getButtonImage((nHighlight
== 0) ? 4 : 0, bLarge
);
387 aRet
.Expand( aSize
.Width(), aSize
.Height(), true );
389 aRet
.CopyPixel( ::tools::Rectangle( Point( aSize
.Width(), 0 ), aSize
), aRectSrc
, getButtonImage((nHighlight
== 1) ? 5 : 1, bLarge
) );
390 aRet
.CopyPixel( ::tools::Rectangle( Point( 0, aSize
.Height() ), aSize
), aRectSrc
, getButtonImage((nHighlight
== 2) ? 6 : 2, bLarge
) );
391 aRet
.CopyPixel( ::tools::Rectangle( Point( aSize
.Width(), aSize
.Height() ), aSize
), aRectSrc
, getButtonImage((nHighlight
== 3) ? 7 : 3, bLarge
) );
397 void ChangePlaceholderTag::addCustomHandles( SdrHdlList
& rHandlerList
)
399 rtl::Reference
<SdrObject
> pPlaceholder
= mxPlaceholderObj
.get();
403 SmartTagReference
xThis( this );
404 const ::tools::Rectangle
& rSnapRect
= pPlaceholder
->GetSnapRect();
407 OutputDevice
* pDev
= mrView
.GetFirstOutputDevice();
408 if( pDev
== nullptr )
409 pDev
= Application::GetDefaultDevice();
411 Size aShapeSizePix
= pDev
->LogicToPixel(rSnapRect
.GetSize());
412 ::tools::Long nShapeSizePix
= std::min(aShapeSizePix
.Width(),aShapeSizePix
.Height());
413 if( 50 > nShapeSizePix
)
416 bool bLarge
= nShapeSizePix
> 250;
418 Size
aButtonSize( pDev
->PixelToLogic( getButtonImage(0, bLarge
).GetSizePixel()) );
420 const int nColumns
= 2;
423 ::tools::Long all_width
= nColumns
* aButtonSize
.Width();
424 ::tools::Long all_height
= nRows
* aButtonSize
.Height();
426 Point
aPos( rSnapRect
.Center() );
427 aPos
.AdjustX( -(all_width
>> 1) );
428 aPos
.AdjustY( -(all_height
>> 1) );
430 std::unique_ptr
<ImageButtonHdl
> pHdl(new ImageButtonHdl( xThis
, aPoint
));
431 pHdl
->SetObjHdlNum( SMART_TAG_HDL_NUM
);
432 pHdl
->SetPageView( mrView
.GetSdrPageView() );
434 pHdl
->SetPos( aPos
);
436 rHandlerList
.AddHdl( std::move(pHdl
) );
439 ViewOverlayManager::ViewOverlayManager( ViewShellBase
& rViewShellBase
)
440 : mrBase( rViewShellBase
)
441 , mnUpdateTagsEvent( nullptr )
443 Link
<tools::EventMultiplexerEvent
&,void> aLink( LINK(this,ViewOverlayManager
,EventMultiplexerListener
) );
444 mrBase
.GetEventMultiplexer()->AddEventListener(aLink
);
446 StartListening( *mrBase
.GetDocShell() );
449 ViewOverlayManager::~ViewOverlayManager()
451 Link
<tools::EventMultiplexerEvent
&,void> aLink( LINK(this,ViewOverlayManager
,EventMultiplexerListener
) );
452 mrBase
.GetEventMultiplexer()->RemoveEventListener( aLink
);
454 if( mnUpdateTagsEvent
)
456 Application::RemoveUserEvent( mnUpdateTagsEvent
);
457 mnUpdateTagsEvent
= nullptr;
463 void ViewOverlayManager::Notify(SfxBroadcaster
&, const SfxHint
& rHint
)
465 if (rHint
.GetId() == SfxHintId::DocChanged
)
471 void ViewOverlayManager::onZoomChanged()
473 if( !maTagVector
.empty() )
479 void ViewOverlayManager::UpdateTags()
481 if( !mnUpdateTagsEvent
)
482 mnUpdateTagsEvent
= Application::PostUserEvent( LINK( this, ViewOverlayManager
, UpdateTagsHdl
) );
485 IMPL_LINK_NOARG(ViewOverlayManager
, UpdateTagsHdl
, void*, void)
487 mnUpdateTagsEvent
= nullptr;
488 bool bChanges
= DisposeTags();
489 bChanges
|= CreateTags();
491 SdrView
* pDrawView
= mrBase
.GetDrawView();
492 if( bChanges
&& pDrawView
)
493 static_cast< ::sd::View
* >( pDrawView
)->updateHandles();
496 bool ViewOverlayManager::CreateTags()
498 bool bChanges
= false;
500 std::shared_ptr
<ViewShell
> aMainShell
= mrBase
.GetMainViewShell();
502 SdPage
* pPage
= aMainShell
? aMainShell
->getCurrentPage() : nullptr;
503 SdrView
* pDrawView
= mrBase
.GetDrawView();
505 if( pDrawView
&& pPage
&& !pPage
->IsMasterPage() && (pPage
->GetPageKind() == PageKind::Standard
) )
507 const std::list
< SdrObject
* >& rShapes
= pPage
->GetPresentationShapeList().getList();
509 for( SdrObject
* pShape
: rShapes
)
511 if( pShape
->IsEmptyPresObj() && (pShape
->GetObjIdentifier() == SdrObjKind::OutlineText
) && (pDrawView
->GetTextEditObject() != pShape
) )
513 rtl::Reference
< SmartTag
> xTag( new ChangePlaceholderTag( *mrBase
.GetMainViewShell()->GetView(), *pShape
) );
514 maTagVector
.push_back(xTag
);
523 bool ViewOverlayManager::DisposeTags()
525 if( !maTagVector
.empty() )
528 vec
.swap( maTagVector
);
530 for (auto& rxViewTag
: vec
)
531 rxViewTag
->Dispose();
538 IMPL_LINK(ViewOverlayManager
,EventMultiplexerListener
,
539 tools::EventMultiplexerEvent
&, rEvent
, void)
541 switch (rEvent
.meEventId
)
543 case EventMultiplexerEventId::MainViewAdded
:
544 case EventMultiplexerEventId::ViewAdded
:
545 case EventMultiplexerEventId::BeginTextEdit
:
546 case EventMultiplexerEventId::EndTextEdit
:
547 case EventMultiplexerEventId::CurrentPageChanged
:
556 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */