/* -*- 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 #include #include #include #include #include #include #include #include "imapwnd.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace com::sun::star; using ::com::sun::star::frame::XFrame; using ::com::sun::star::uno::Reference; #define TRANSCOL COL_WHITE static ItemInfoPackage& getItemInfoPackageIMapWindow() { class ItemInfoPackageIMapWindow : public ItemInfoPackage { typedef std::array ItemInfoArrayIMapWindow; ItemInfoArrayIMapWindow maItemInfos {{ // m_nWhich, m_pItem, m_nSlotID, m_nItemInfoFlags { SID_ATTR_MACROITEM, new SvxMacroItem(SID_ATTR_MACROITEM), 0, SFX_ITEMINFOFLAG_NONE } }}; virtual const ItemInfoStatic& getItemInfoStatic(size_t nIndex) const override { return maItemInfos[nIndex]; } public: virtual size_t size() const override { return maItemInfos.size(); } virtual const ItemInfo& getItemInfo(size_t nIndex, SfxItemPool& /*rPool*/) override { return maItemInfos[nIndex]; } }; static std::unique_ptr g_aItemInfoPackageIMapWindow; if (!g_aItemInfoPackageIMapWindow) g_aItemInfoPackageIMapWindow.reset(new ItemInfoPackageIMapWindow); return *g_aItemInfoPackageIMapWindow; } IMapWindow::IMapWindow(const Reference< XFrame >& rxDocumentFrame, weld::Dialog* pDialog) : GraphCtrl(pDialog) , mxDocumentFrame(rxDocumentFrame) { pIMapPool = new SfxItemPool(u"IMapItemPool"_ustr); pIMapPool->registerItemInfoPackage(getItemInfoPackageIMapWindow()); } IMapWindow::~IMapWindow() { } void IMapWindow::SetDrawingArea(weld::DrawingArea* pDrawingArea) { Size aSize(pDrawingArea->get_ref_device().LogicToPixel(Size(270, 170), MapMode(MapUnit::MapAppFont))); pDrawingArea->set_size_request(aSize.Width(), aSize.Height()); SetOutputSizePixel(aSize); weld::CustomWidgetController::SetDrawingArea(pDrawingArea); SetSdrMode(true); mxDropTargetHelper.reset(new IMapDropTargetHelper(*this)); } void IMapWindow::SetImageMap( const ImageMap& rImageMap ) { ReplaceImageMap( rImageMap ); } void IMapWindow::ReplaceImageMap( const ImageMap& rImageMap ) { SdrPage* pPage = nullptr; aIMap = rImageMap; if(GetSdrModel()) { // try to access page pPage = GetSdrModel()->GetPage(0); } if(pPage) { // clear SdrObjects with broadcasting pPage->ClearSdrObjList(); } if(GetSdrView()) { // #i63762# reset selection at view GetSdrView()->UnmarkAllObj(); } // create new drawing objects const sal_uInt16 nCount(rImageMap.GetIMapObjectCount()); for ( sal_uInt16 i(nCount); i > 0; i-- ) { rtl::Reference pNewObj = CreateObj( rImageMap.GetIMapObject( i - 1 ) ); if (pNewObj && pPage) { pPage->InsertObject( pNewObj.get() ); } } } void IMapWindow::ReplaceActualIMapInfo( const NotifyInfo& rNewInfo ) { const SdrObject* pSdrObj = GetSelectedSdrObject(); if ( pSdrObj ) { IMapObject* pIMapObj = GetIMapObj( pSdrObj ); if (pIMapObj) { pIMapObj->SetURL( rNewInfo.aMarkURL ); pIMapObj->SetAltText( rNewInfo.aMarkAltText ); pIMapObj->SetTarget( rNewInfo.aMarkTarget ); pModel->SetChanged(); UpdateInfo( false ); } } } const ImageMap& IMapWindow::GetImageMap() { if ( pModel->IsChanged() ) { SdrPage* pPage = pModel->GetPage( 0 ); if ( pPage ) { const size_t nCount = pPage->GetObjCount(); aIMap.ClearImageMap(); for ( size_t i = nCount; i; ) { --i; aIMap.InsertIMapObject( *( static_cast( pPage->GetObj( i )->GetUserData( 0 ) )->GetObject() ) ); } } pModel->SetChanged( false ); } return aIMap; } void IMapWindow::SetTargetList( const TargetList& rTargetList ) { // Delete old List // Fill with the provided list aTargetList = rTargetList; pModel->SetChanged( false ); } rtl::Reference IMapWindow::CreateObj( const IMapObject* pIMapObj ) { tools::Rectangle aClipRect( Point(), GetGraphicSize() ); rtl::Reference pSdrObj; IMapObjectPtr pCloneIMapObj; switch( pIMapObj->GetType() ) { case IMapObjectType::Rectangle: { const IMapRectangleObject* pIMapRectObj = static_cast(pIMapObj); tools::Rectangle aDrawRect( pIMapRectObj->GetRectangle( false ) ); // clipped on CanvasPane aDrawRect.Intersection( aClipRect ); pSdrObj = new SdrRectObj(*pModel, aDrawRect); pCloneIMapObj.reset(static_cast(new IMapRectangleObject( *pIMapRectObj ))); } break; case IMapObjectType::Circle: { const IMapCircleObject* pIMapCircleObj = static_cast(pIMapObj); const Point aCenter( pIMapCircleObj->GetCenter( false ) ); const tools::Long nRadius = pIMapCircleObj->GetRadius( false ); const Point aOffset( nRadius, nRadius ); tools::Rectangle aCircle( aCenter - aOffset, aCenter + aOffset ); // limited to CanvasPane aCircle.Intersection( aClipRect ); pSdrObj = new SdrCircObj( *pModel, SdrCircKind::Full, aCircle, 0_deg100, 36000_deg100); pCloneIMapObj.reset(static_cast(new IMapCircleObject( *pIMapCircleObj ))); } break; case IMapObjectType::Polygon: { const IMapPolygonObject* pIMapPolyObj = static_cast(pIMapObj); // If it actually is an ellipse, then another ellipse is created again if ( pIMapPolyObj->HasExtraEllipse() ) { tools::Rectangle aDrawRect( pIMapPolyObj->GetExtraEllipse() ); // clipped on CanvasPane aDrawRect.Intersection( aClipRect ); pSdrObj = new SdrCircObj( *pModel, SdrCircKind::Full, aDrawRect, 0_deg100, 36000_deg100); } else { const tools::Polygon aPoly = pIMapPolyObj->GetPolygon( false ); tools::Polygon aDrawPoly( aPoly ); // clipped on CanvasPane aDrawPoly.Clip( aClipRect ); basegfx::B2DPolygon aPolygon; aPolygon.append(aDrawPoly.getB2DPolygon()); pSdrObj = new SdrPathObj( *pModel, SdrObjKind::Polygon, basegfx::B2DPolyPolygon(aPolygon)); } pCloneIMapObj.reset(static_cast(new IMapPolygonObject( *pIMapPolyObj ))); } break; default: break; } if ( pSdrObj ) { SfxItemSet aSet( pModel->GetItemPool() ); aSet.Put( XFillStyleItem( drawing::FillStyle_SOLID ) ); aSet.Put( XFillColorItem( u""_ustr, TRANSCOL ) ); if ( !pIMapObj->IsActive() ) { aSet.Put( XFillTransparenceItem( 100 ) ); aSet.Put( XLineColorItem( u""_ustr, COL_RED ) ); } else { aSet.Put( XFillTransparenceItem( 50 ) ); aSet.Put( XLineColorItem( u""_ustr, COL_BLACK ) ); } pSdrObj->SetMergedItemSetAndBroadcast(aSet); pSdrObj->AppendUserData( std::unique_ptr(new IMapUserData( std::move(pCloneIMapObj) )) ); pSdrObj->SetUserCall( GetSdrUserCall() ); } return pSdrObj; } void IMapWindow::InitSdrModel() { GraphCtrl::InitSdrModel(); SfxItemSet aSet( pModel->GetItemPool() ); aSet.Put( XFillColorItem( u""_ustr, TRANSCOL ) ); aSet.Put( XFillTransparenceItem( 50 ) ); pView->SetAttributes( aSet ); pView->SetFrameDragSingles(); } void IMapWindow::SdrObjCreated( const SdrObject& rObj ) { switch( rObj.GetObjIdentifier() ) { case SdrObjKind::Rectangle: { SdrRectObj* pRectObj = const_cast(static_cast(&rObj)); auto pObj = std::make_shared( pRectObj->GetLogicRect(), "", "", "", "", "", true, false ); pRectObj->AppendUserData( std::unique_ptr(new IMapUserData( pObj )) ); } break; case SdrObjKind::CircleOrEllipse: { SdrCircObj* pCircObj = const_cast( static_cast(&rObj) ); rtl::Reference pPathObj = static_cast( pCircObj->ConvertToPolyObj( false, false ).get() ); tools::Polygon aPoly(pPathObj->GetPathPoly().getB2DPolygon(0)); pPathObj.clear(); auto pObj = std::make_shared( aPoly, "", "", "", "", "", true, false ); pObj->SetExtraEllipse( aPoly.GetBoundRect() ); pCircObj->AppendUserData( std::unique_ptr(new IMapUserData( pObj )) ); } break; case SdrObjKind::Polygon: case SdrObjKind::FreehandFill: case SdrObjKind::PathPoly: case SdrObjKind::PathFill: { SdrPathObj* pPathObj = const_cast( static_cast(&rObj) ); const basegfx::B2DPolyPolygon& rXPolyPoly = pPathObj->GetPathPoly(); if ( rXPolyPoly.count() ) { tools::Polygon aPoly(rXPolyPoly.getB2DPolygon(0)); auto pObj = std::make_shared( aPoly, "", "", "", "", "", true, false ); pPathObj->AppendUserData( std::unique_ptr(new IMapUserData( pObj )) ); } } break; default: break; } } void IMapWindow::SdrObjChanged( const SdrObject& rObj ) { IMapUserData* pUserData = static_cast( rObj.GetUserData( 0 ) ); if ( !pUserData ) return; OUString aURL; OUString aAltText; OUString aDesc; OUString aTarget; IMapObjectPtr pIMapObj = pUserData->GetObject(); bool bActive = true; if ( pIMapObj ) { aURL = pIMapObj->GetURL(); aAltText = pIMapObj->GetAltText(); aDesc = pIMapObj->GetDesc(); aTarget = pIMapObj->GetTarget(); bActive = pIMapObj->IsActive(); } switch( rObj.GetObjIdentifier() ) { case SdrObjKind::Rectangle: { pUserData->ReplaceObject( std::make_shared( static_cast(rObj).GetLogicRect(), aURL, aAltText, aDesc, aTarget, "", bActive, false ) ); } break; case SdrObjKind::CircleOrEllipse: { const SdrCircObj& rCircObj = static_cast(rObj); rtl::Reference pPathObj = static_cast( rCircObj.ConvertToPolyObj( false, false ).get() ); tools::Polygon aPoly(pPathObj->GetPathPoly().getB2DPolygon(0)); auto pObj = std::make_shared( aPoly, aURL, aAltText, aDesc, aTarget, "", bActive, false ); pObj->SetExtraEllipse( aPoly.GetBoundRect() ); pPathObj.clear(); pUserData->ReplaceObject( pObj ); } break; case SdrObjKind::Polygon: case SdrObjKind::FreehandFill: case SdrObjKind::PathPoly: case SdrObjKind::PathFill: { const SdrPathObj& rPathObj = static_cast(rObj); const basegfx::B2DPolyPolygon& rXPolyPoly = rPathObj.GetPathPoly(); if ( rXPolyPoly.count() ) { tools::Polygon aPoly(rPathObj.GetPathPoly().getB2DPolygon(0)); auto pObj = std::make_shared( aPoly, aURL, aAltText, aDesc, aTarget, "", bActive, false ); pUserData->ReplaceObject( pObj ); } } break; default: break; } } bool IMapWindow::MouseButtonUp(const MouseEvent& rMEvt) { bool bRet = GraphCtrl::MouseButtonUp( rMEvt ); UpdateInfo( true ); return bRet; } void IMapWindow::MarkListHasChanged() { GraphCtrl::MarkListHasChanged(); UpdateInfo( false ); } SdrObject* IMapWindow::GetHitSdrObj( const Point& rPosPixel ) const { OutputDevice& rDevice = GetDrawingArea()->get_ref_device(); SdrObject* pObj = nullptr; Point aPt = rDevice.PixelToLogic( rPosPixel ); if ( tools::Rectangle( Point(), GetGraphicSize() ).Contains( aPt ) ) { SdrPage* pPage = pModel->GetPage( 0 ); if ( pPage ) { for ( size_t i = pPage->GetObjCount(); i > 0; ) { --i; SdrObject* pTestObj = pPage->GetObj( i ); IMapObject* pIMapObj = GetIMapObj( pTestObj ); if ( pIMapObj && pIMapObj->IsHit( aPt ) ) { pObj = pTestObj; break; } } } } return pObj; } IMapObject* IMapWindow::GetIMapObj( const SdrObject* pSdrObj ) { IMapObject* pIMapObj = nullptr; if ( pSdrObj ) { IMapUserData* pUserData = static_cast( pSdrObj->GetUserData( 0 ) ); if ( pUserData ) pIMapObj = pUserData->GetObject().get(); } return pIMapObj; } bool IMapWindow::Command(const CommandEvent& rCEvt) { if ( rCEvt.GetCommand() == CommandEventId::ContextMenu ) { std::unique_ptr xBuilder(Application::CreateBuilder(GetDrawingArea(), u"svx/ui/imapmenu.ui"_ustr)); mxPopupMenu = xBuilder->weld_menu(u"menu"_ustr); const SdrMarkList& rMarkList = pView->GetMarkedObjectList(); const size_t nMarked = rMarkList.GetMarkCount(); mxPopupMenu->set_sensitive(u"url"_ustr, false); mxPopupMenu->set_sensitive(u"active"_ustr, false); mxPopupMenu->set_sensitive(u"macro"_ustr, false); mxPopupMenu->set_sensitive(u"selectall"_ustr, pModel->GetPage(0)->GetObjCount() != rMarkList.GetMarkCount()); if ( !nMarked ) { mxPopupMenu->set_sensitive(u"arrange"_ustr, false); mxPopupMenu->set_sensitive(u"delete"_ustr, false); } else { if ( nMarked == 1 ) { SdrObject* pSdrObj = GetSelectedSdrObject(); mxPopupMenu->set_sensitive(u"url"_ustr, true); mxPopupMenu->set_sensitive(u"active"_ustr, true); mxPopupMenu->set_sensitive(u"macro"_ustr, true); mxPopupMenu->set_active(u"active"_ustr, GetIMapObj(pSdrObj)->IsActive()); } mxPopupMenu->set_sensitive(u"arrange"_ustr, true); mxPopupMenu->set_sensitive(u"delete"_ustr, true); } MenuSelectHdl(mxPopupMenu->popup_at_rect(GetDrawingArea(), tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1)))); mxPopupMenu.reset(); return true; } return CustomWidgetController::Command(rCEvt); } IMapDropTargetHelper::IMapDropTargetHelper(IMapWindow& rImapWindow) : DropTargetHelper(rImapWindow.GetDrawingArea()->get_drop_target()) , m_rImapWindow(rImapWindow) { } sal_Int8 IMapDropTargetHelper::AcceptDrop( const AcceptDropEvent& rEvt ) { return m_rImapWindow.AcceptDrop(rEvt); } sal_Int8 IMapDropTargetHelper::ExecuteDrop( const ExecuteDropEvent& rEvt ) { return m_rImapWindow.ExecuteDrop(rEvt); } sal_Int8 IMapWindow::AcceptDrop( const AcceptDropEvent& rEvt ) { return( ( GetHitSdrObj( rEvt.maPosPixel ) != nullptr ) ? rEvt.mnAction : DND_ACTION_NONE ); } sal_Int8 IMapWindow::ExecuteDrop( const ExecuteDropEvent& rEvt ) { sal_Int8 nRet = DND_ACTION_NONE; if (mxDropTargetHelper->IsDropFormatSupported(SotClipboardFormatId::NETSCAPE_BOOKMARK)) { INetBookmark aBookMark( u""_ustr, u""_ustr ); SdrObject* pSdrObj = GetHitSdrObj( rEvt.maPosPixel ); if( pSdrObj && TransferableDataHelper( rEvt.maDropEvent.Transferable ).GetINetBookmark( SotClipboardFormatId::NETSCAPE_BOOKMARK, aBookMark ) ) { IMapObject* pIMapObj = GetIMapObj( pSdrObj ); pIMapObj->SetURL( aBookMark.GetURL() ); pIMapObj->SetAltText( aBookMark.GetDescription() ); pModel->SetChanged(); pView->UnmarkAll(); pView->MarkObj( pSdrObj, pView->GetSdrPageView() ); UpdateInfo( true ); nRet = rEvt.mnAction; } } return nRet; } OUString IMapWindow::RequestHelp(tools::Rectangle& rHelpArea) { OutputDevice& rDevice = GetDrawingArea()->get_ref_device(); Point aPos = rDevice.PixelToLogic(rHelpArea.TopLeft()); SdrPageView* pPageView = nullptr; SdrObject* pSdrObj = pView->PickObj(aPos, pView->getHitTolLog(), pPageView); if (pSdrObj) { const IMapObject* pIMapObj = GetIMapObj( pSdrObj ); if ( pIMapObj ) { OUString aStr = pIMapObj->GetURL(); if ( !aStr.isEmpty() ) { rHelpArea = rDevice.LogicToPixel(tools::Rectangle( Point(), GetGraphicSize())); return aStr; } } } return OUString(); } void IMapWindow::SetCurrentObjState( bool bActive ) { SdrObject* pObj = GetSelectedSdrObject(); if ( !pObj ) return; SfxItemSet aSet( pModel->GetItemPool() ); GetIMapObj( pObj )->SetActive( bActive ); aSet.Put( XFillColorItem( u""_ustr, TRANSCOL ) ); if ( !bActive ) { aSet.Put( XFillTransparenceItem( 100 ) ); aSet.Put( XLineColorItem( u""_ustr, COL_RED ) ); } else { aSet.Put( XFillTransparenceItem( 50 ) ); aSet.Put( XLineColorItem( u""_ustr, COL_BLACK ) ); } pView->SetAttributes( aSet ); } void IMapWindow::UpdateInfo( bool bNewObj ) { if ( !aInfoLink.IsSet() ) return; const SdrObject* pSdrObj = GetSelectedSdrObject(); const IMapObject* pIMapObj = pSdrObj ? GetIMapObj( pSdrObj ) : nullptr; aInfo.bNewObj = bNewObj; if ( pIMapObj ) { aInfo.bOneMarked = true; aInfo.aMarkURL = pIMapObj->GetURL(); aInfo.aMarkAltText = pIMapObj->GetAltText(); aInfo.aMarkTarget = pIMapObj->GetTarget(); aInfo.bActivated = pIMapObj->IsActive(); aInfoLink.Call( *this ); } else { aInfo.aMarkURL.clear(); aInfo.aMarkAltText.clear(); aInfo.aMarkTarget.clear(); aInfo.bOneMarked = false; aInfo.bActivated = false; } aInfoLink.Call( *this ); } void IMapWindow::DoMacroAssign() { SdrObject* pSdrObj = GetSelectedSdrObject(); if ( !pSdrObj ) return; auto xSet = std::make_unique> (*pIMapPool); SfxEventNamesItem aNamesItem(SID_EVENTCONFIG); aNamesItem.AddEvent( u"MouseOver"_ustr, u""_ustr, SvMacroItemId::OnMouseOver ); aNamesItem.AddEvent( u"MouseOut"_ustr, u""_ustr, SvMacroItemId::OnMouseOut ); xSet->Put( aNamesItem ); SvxMacroItem aMacroItem(SID_ATTR_MACROITEM); IMapObject* pIMapObj = GetIMapObj( pSdrObj ); aMacroItem.SetMacroTable( pIMapObj->GetMacroTable() ); xSet->Put( aMacroItem ); SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); VclPtr pMacroDlg(pFact->CreateEventConfigDialog(GetDrawingArea(), std::move(xSet), mxDocumentFrame)); pMacroDlg->StartExecuteAsync( [this, pMacroDlg, pIMapObj] (sal_Int32 nResult)->void { if (nResult == RET_OK) { const SfxItemSet* pOutSet = pMacroDlg->GetOutputItemSet(); pIMapObj->SetMacroTable( pOutSet->Get( SID_ATTR_MACROITEM ).GetMacroTable() ); pModel->SetChanged(); UpdateInfo( false ); } pMacroDlg->disposeOnce(); } ); } void IMapWindow::DoPropertyDialog() { SdrObject* pSdrObj = GetSelectedSdrObject(); if ( !pSdrObj ) return; IMapObject* pIMapObj = GetIMapObj( pSdrObj ); SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); ScopedVclPtr aDlg(pFact->CreateURLDialog(GetDrawingArea(), pIMapObj->GetURL(), pIMapObj->GetAltText(), pIMapObj->GetDesc(), pIMapObj->GetTarget(), pIMapObj->GetName(), aTargetList)); if ( aDlg->Execute() != RET_OK ) return; const OUString aURLText( aDlg->GetURL() ); if ( !aURLText.isEmpty() ) { INetURLObject aObj( aURLText, INetProtocol::File ); DBG_ASSERT( aObj.GetProtocol() != INetProtocol::NotValid, "Invalid URL" ); pIMapObj->SetURL( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); } else pIMapObj->SetURL( aURLText ); pIMapObj->SetAltText( aDlg->GetAltText() ); pIMapObj->SetDesc( aDlg->GetDesc() ); pIMapObj->SetTarget( aDlg->GetTarget() ); pIMapObj->SetName( aDlg->GetName() ); pModel->SetChanged(); UpdateInfo( true ); } void IMapWindow::MenuSelectHdl(const OUString& rId) { if (rId == "url") DoPropertyDialog(); else if (rId == "macro") DoMacroAssign(); else if (rId == "active") { const bool bNewState = !mxPopupMenu->get_active(rId); SetCurrentObjState(bNewState); UpdateInfo( false ); } else if (rId == "front") pView->PutMarkedToTop(); else if (rId == "forward") pView->MovMarkedToTop(); else if (rId == "backward") pView->MovMarkedToBtm(); else if (rId == "back") pView->PutMarkedToBtm(); else if (rId == "selectall") pView->MarkAll(); else if (rId == "delete") pView->DeleteMarked(); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */