/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include "sddll.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "view/viewoverlaymanager.hxx" #include "res_bmp.hrc" #include "DrawDocShell.hxx" #include "DrawViewShell.hxx" #include "DrawController.hxx" #include "glob.hrc" #include "strings.hrc" #include "sdresid.hxx" #include "EventMultiplexer.hxx" #include "ViewShellManager.hxx" #include "helpids.h" #include "sdpage.hxx" #include "drawdoc.hxx" #include "smarttag.hxx" using namespace ::com::sun::star::uno; using namespace ::com::sun::star::frame; namespace sd { class ImageButtonHdl; static const sal_uInt16 gButtonSlots[] = { SID_INSERT_TABLE, SID_INSERT_DIAGRAM, SID_INSERT_GRAPHIC, SID_INSERT_AVMEDIA }; static const sal_uInt16 gButtonToolTips[] = { STR_INSERT_TABLE, STR_INSERT_CHART, STR_INSERT_PICTURE, STR_INSERT_MOVIE }; static BitmapEx loadImageResource( sal_uInt16 nId ) { SdResId aResId( nId ); aResId.SetRT( RSC_BITMAP ); return BitmapEx( aResId ); } static BitmapEx* getButtonImage( int index, bool large ) { static vcl::DeleteOnDeinit< BitmapEx > gSmallButtonImages[BMP_PLACEHOLDER_SMALL_END - BMP_PLACEHOLDER_SMALL_START] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; static vcl::DeleteOnDeinit< BitmapEx > gLargeButtonImages[BMP_PLACEHOLDER_LARGE_END - BMP_PLACEHOLDER_LARGE_START] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; if( !gSmallButtonImages[0].get() ) { for( sal_uInt16 i = 0; i < (BMP_PLACEHOLDER_SMALL_END-BMP_PLACEHOLDER_SMALL_START); i++ ) { gSmallButtonImages[i].set( new BitmapEx( loadImageResource( BMP_PLACEHOLDER_SMALL_START + i ) ) ); gLargeButtonImages[i].set( new BitmapEx( loadImageResource( BMP_PLACEHOLDER_LARGE_START + i ) ) ); } } if( large ) { return gLargeButtonImages[index].get(); } else { return gSmallButtonImages[index].get(); } } const sal_uInt32 SMART_TAG_HDL_NUM = SAL_MAX_UINT32; class ChangePlaceholderTag : public SmartTag { friend class ImageButtonHdl; public: ChangePlaceholderTag( ::sd::View& rView, SdrObject& rPlaceholderObj ); virtual ~ChangePlaceholderTag(); /** returns true if the SmartTag handled the event. */ virtual bool MouseButtonDown( const MouseEvent&, SmartHdl& ) override; /** returns true if the SmartTag consumes this event. */ virtual bool KeyInput( const KeyEvent& rKEvt ) override; BitmapEx createOverlayImage( int nHighlight = -1 ); protected: virtual void addCustomHandles( SdrHdlList& rHandlerList ) override; virtual void disposing() override; virtual void select() override; virtual void deselect() override; private: SdrObjectWeakRef mxPlaceholderObj; }; class ImageButtonHdl : public SmartHdl { public: ImageButtonHdl( const SmartTagReference& xTag, /* sal_uInt16 nSID, const Image& rImage, const Image& rImageMO, */ const Point& rPnt ); virtual ~ImageButtonHdl(); virtual void CreateB2dIAObject() override; virtual bool IsFocusHdl() const override; virtual Pointer GetPointer() const override; virtual bool isMarkable() const override; virtual void onMouseEnter(const MouseEvent& rMEvt) override; virtual void onMouseLeave() override; int getHighlightId() const { return mnHighlightId; } static void HideTip(); private: rtl::Reference< ChangePlaceholderTag > mxTag; int mnHighlightId; Size maImageSize; }; ImageButtonHdl::ImageButtonHdl( const SmartTagReference& xTag /*, sal_uInt16 nSID, const Image& rImage, const Image& rImageMO*/, const Point& rPnt ) : SmartHdl( xTag, rPnt ) , mxTag( dynamic_cast< ChangePlaceholderTag* >( xTag.get() ) ) , mnHighlightId( -1 ) , maImageSize( 42, 42 ) { } ImageButtonHdl::~ImageButtonHdl() { HideTip(); } void ImageButtonHdl::HideTip() { Help::HideBalloonAndQuickHelp(); } void ImageButtonHdl::onMouseEnter(const MouseEvent& rMEvt) { if( pHdlList && pHdlList->GetView()) { int nHighlightId = 0; OutputDevice* pDev = pHdlList->GetView()->GetFirstOutputDevice(); if( pDev == nullptr ) pDev = Application::GetDefaultDevice(); Point aMDPos( rMEvt.GetPosPixel() ); aMDPos -= pDev->LogicToPixel( GetPos() ); nHighlightId += aMDPos.X() > maImageSize.Width() ? 1 : 0; nHighlightId += aMDPos.Y() > maImageSize.Height() ? 2 : 0; if( mnHighlightId != nHighlightId ) { HideTip(); mnHighlightId = nHighlightId; if( pHdlList ) { SdResId aResId( gButtonToolTips[mnHighlightId] ); aResId.SetRT( RSC_STRING ); OUString aHelpText( aResId ); Rectangle aScreenRect( pDev->LogicToPixel( GetPos() ), maImageSize ); Help::ShowQuickHelp(static_cast< vcl::Window* >( pHdlList->GetView()->GetFirstOutputDevice() ), aScreenRect, aHelpText); } Touch(); } } } void ImageButtonHdl::onMouseLeave() { mnHighlightId = -1; HideTip(); Touch(); } void ImageButtonHdl::CreateB2dIAObject() { // first throw away old one GetRidOfIAObject(); const Point aTagPos( GetPos() ); basegfx::B2DPoint aPosition( aTagPos.X(), aTagPos.Y() ); BitmapEx aBitmapEx( mxTag->createOverlayImage( mnHighlightId ) ); // maImageMO.GetBitmapEx() : maImage.GetBitmapEx() ); maImageSize = aBitmapEx.GetSizePixel(); maImageSize.Width() >>= 1; maImageSize.Height() >>= 1; if(pHdlList) { SdrMarkView* pView = pHdlList->GetView(); if(pView && !pView->areMarkHandlesHidden()) { SdrPageView* pPageView = pView->GetSdrPageView(); if(pPageView) { for(sal_uInt32 b = 0; b < pPageView->PageWindowCount(); b++) { const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b); SdrPaintWindow& rPaintWindow = rPageWindow.GetPaintWindow(); rtl::Reference< sdr::overlay::OverlayManager > xManager = rPageWindow.GetOverlayManager(); if(rPaintWindow.OutputToWindow() && xManager.is() ) { sdr::overlay::OverlayObject* pOverlayObject = nullptr; pOverlayObject = new sdr::overlay::OverlayBitmapEx( aPosition, aBitmapEx, 0, 0 ); xManager->add(*pOverlayObject); maOverlayGroup.append(*pOverlayObject); } } } } } } bool ImageButtonHdl::IsFocusHdl() const { return false; } bool ImageButtonHdl::isMarkable() const { return false; } Pointer ImageButtonHdl::GetPointer() const { return Pointer( PointerStyle::Arrow ); } ChangePlaceholderTag::ChangePlaceholderTag( ::sd::View& rView, SdrObject& rPlaceholderObj ) : SmartTag( rView ) , mxPlaceholderObj( &rPlaceholderObj ) { } ChangePlaceholderTag::~ChangePlaceholderTag() { } /** returns true if the ChangePlaceholderTag handled the event. */ bool ChangePlaceholderTag::MouseButtonDown( const MouseEvent& /*rMEvt*/, SmartHdl& rHdl ) { int nHighlightId = static_cast< ImageButtonHdl& >(rHdl).getHighlightId(); if( nHighlightId >= 0 ) { sal_uInt16 nSID = gButtonSlots[nHighlightId]; if( mxPlaceholderObj.get() ) { // mark placeholder if it is not currently marked (or if also others are marked) if( !mrView.IsObjMarked( mxPlaceholderObj.get() ) || (mrView.GetMarkedObjectList().GetMarkCount() != 1) ) { SdrPageView* pPV = mrView.GetSdrPageView(); mrView.UnmarkAllObj(pPV ); mrView.MarkObj(mxPlaceholderObj.get(), pPV); } } mrView.GetViewShell()->GetViewFrame()->GetDispatcher()->Execute( nSID, SfxCallMode::ASYNCHRON); } return false; } /** returns true if the SmartTag consumes this event. */ bool ChangePlaceholderTag::KeyInput( const KeyEvent& rKEvt ) { sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode(); switch( nCode ) { case KEY_DOWN: case KEY_UP: case KEY_LEFT: case KEY_RIGHT: case KEY_ESCAPE: case KEY_TAB: case KEY_RETURN: case KEY_SPACE: default: return false; } } BitmapEx ChangePlaceholderTag::createOverlayImage( int nHighlight ) { BitmapEx aRet; if( mxPlaceholderObj.is() ) { SdrObject* pPlaceholder = mxPlaceholderObj.get(); SmartTagReference xThis( this ); const Rectangle& rSnapRect = pPlaceholder->GetSnapRect(); OutputDevice* pDev = mrView.GetFirstOutputDevice(); if( pDev == nullptr ) pDev = Application::GetDefaultDevice(); Size aShapeSizePix = pDev->LogicToPixel(rSnapRect.GetSize()); long nShapeSizePix = std::min(aShapeSizePix.Width(),aShapeSizePix.Height()); bool bLarge = nShapeSizePix > 250; Size aSize( getButtonImage( 0, bLarge )->GetSizePixel() ); aRet.SetSizePixel( Size( aSize.Width() << 1, aSize.Height() << 1 ) ); const Rectangle aRectSrc( Point( 0, 0 ), aSize ); aRet = *(getButtonImage((nHighlight == 0) ? 4 : 0, bLarge)); aRet.Expand( aSize.Width(), aSize.Height(), nullptr, true ); aRet.CopyPixel( Rectangle( Point( aSize.Width(), 0 ), aSize ), aRectSrc, getButtonImage((nHighlight == 1) ? 5 : 1, bLarge) ); aRet.CopyPixel( Rectangle( Point( 0, aSize.Height() ), aSize ), aRectSrc, getButtonImage((nHighlight == 2) ? 6 : 2, bLarge) ); aRet.CopyPixel( Rectangle( Point( aSize.Width(), aSize.Height() ), aSize ), aRectSrc, getButtonImage((nHighlight == 3) ? 7 : 3, bLarge) ); } return aRet; } void ChangePlaceholderTag::addCustomHandles( SdrHdlList& rHandlerList ) { if( mxPlaceholderObj.is() ) { SdrObject* pPlaceholder = mxPlaceholderObj.get(); SmartTagReference xThis( this ); const Rectangle& rSnapRect = pPlaceholder->GetSnapRect(); const Point aPoint; OutputDevice* pDev = mrView.GetFirstOutputDevice(); if( pDev == nullptr ) pDev = Application::GetDefaultDevice(); Size aShapeSizePix = pDev->LogicToPixel(rSnapRect.GetSize()); long nShapeSizePix = std::min(aShapeSizePix.Width(),aShapeSizePix.Height()); if( 50 > nShapeSizePix ) return; bool bLarge = nShapeSizePix > 250; Size aButtonSize( pDev->PixelToLogic( getButtonImage(0, bLarge )->GetSizePixel()) ); const int nColumns = 2; const int nRows = 2; long all_width = nColumns * aButtonSize.Width(); long all_height = nRows * aButtonSize.Height(); Point aPos( rSnapRect.Center() ); aPos.X() -= all_width >> 1; aPos.Y() -= all_height >> 1; ImageButtonHdl* pHdl = new ImageButtonHdl( xThis, aPoint ); pHdl->SetObjHdlNum( SMART_TAG_HDL_NUM ); pHdl->SetPageView( mrView.GetSdrPageView() ); pHdl->SetPos( aPos ); rHandlerList.AddHdl( pHdl ); } } void ChangePlaceholderTag::disposing() { SmartTag::disposing(); } void ChangePlaceholderTag::select() { SmartTag::select(); } void ChangePlaceholderTag::deselect() { SmartTag::deselect(); } ViewOverlayManager::ViewOverlayManager( ViewShellBase& rViewShellBase ) : mrBase( rViewShellBase ) , mnUpdateTagsEvent( nullptr ) { Link aLink( LINK(this,ViewOverlayManager,EventMultiplexerListener) ); mrBase.GetEventMultiplexer()->AddEventListener(aLink, tools::EventMultiplexerEvent::EID_CURRENT_PAGE | tools::EventMultiplexerEvent::EID_MAIN_VIEW_ADDED | tools::EventMultiplexerEvent::EID_VIEW_ADDED | tools::EventMultiplexerEvent::EID_BEGIN_TEXT_EDIT | tools::EventMultiplexerEvent::EID_END_TEXT_EDIT ); StartListening( *mrBase.GetDocShell() ); } ViewOverlayManager::~ViewOverlayManager() { Link aLink( LINK(this,ViewOverlayManager,EventMultiplexerListener) ); mrBase.GetEventMultiplexer()->RemoveEventListener( aLink ); if( mnUpdateTagsEvent ) { Application::RemoveUserEvent( mnUpdateTagsEvent ); mnUpdateTagsEvent = nullptr; } DisposeTags(); } void ViewOverlayManager::Notify(SfxBroadcaster&, const SfxHint& rHint) { const SfxSimpleHint* pSimpleHint = dynamic_cast(&rHint); if (pSimpleHint != nullptr) { if (pSimpleHint->GetId() == SFX_HINT_DOCCHANGED) { UpdateTags(); } } } void ViewOverlayManager::onZoomChanged() { if( !maTagVector.empty() ) { UpdateTags(); } } void ViewOverlayManager::UpdateTags() { if( !mnUpdateTagsEvent ) mnUpdateTagsEvent = Application::PostUserEvent( LINK( this, ViewOverlayManager, UpdateTagsHdl ) ); } IMPL_LINK_NOARG_TYPED(ViewOverlayManager, UpdateTagsHdl, void*, void) { OSL_TRACE("ViewOverlayManager::UpdateTagsHdl"); mnUpdateTagsEvent = nullptr; bool bChanges = DisposeTags(); bChanges |= CreateTags(); if( bChanges && mrBase.GetDrawView() ) static_cast< ::sd::View* >( mrBase.GetDrawView() )->updateHandles(); } bool ViewOverlayManager::CreateTags() { bool bChanges = false; std::shared_ptr aMainShell = mrBase.GetMainViewShell(); SdPage* pPage = aMainShell.get() ? aMainShell->getCurrentPage() : nullptr; if( pPage && !pPage->IsMasterPage() && (pPage->GetPageKind() == PK_STANDARD) ) { const std::list< SdrObject* >& rShapes = pPage->GetPresentationShapeList().getList(); for( std::list< SdrObject* >::const_iterator iter( rShapes.begin() ); iter != rShapes.end(); ++iter ) { if( (*iter)->IsEmptyPresObj() && ((*iter)->GetObjIdentifier() == OBJ_OUTLINETEXT) && (mrBase.GetDrawView()->GetTextEditObject() != (*iter)) ) { rtl::Reference< SmartTag > xTag( new ChangePlaceholderTag( *mrBase.GetMainViewShell()->GetView(), *(*iter) ) ); maTagVector.push_back(xTag); bChanges = true; } } } return bChanges; } bool ViewOverlayManager::DisposeTags() { if( !maTagVector.empty() ) { ViewTagVector vec; vec.swap( maTagVector ); ViewTagVector::iterator iter = vec.begin(); do { (*iter++)->Dispose(); } while( iter != vec.end() ); return true; } return false; } IMPL_LINK_TYPED(ViewOverlayManager,EventMultiplexerListener, tools::EventMultiplexerEvent&, rEvent, void) { switch (rEvent.meEventId) { case tools::EventMultiplexerEvent::EID_MAIN_VIEW_ADDED: case tools::EventMultiplexerEvent::EID_VIEW_ADDED: case tools::EventMultiplexerEvent::EID_BEGIN_TEXT_EDIT: case tools::EventMultiplexerEvent::EID_END_TEXT_EDIT: case tools::EventMultiplexerEvent::EID_CURRENT_PAGE: UpdateTags(); break; } } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */