diff options
Diffstat (limited to '')
-rw-r--r-- | sc/source/ui/drawfunc/fusel.cxx | 546 |
1 files changed, 546 insertions, 0 deletions
diff --git a/sc/source/ui/drawfunc/fusel.cxx b/sc/source/ui/drawfunc/fusel.cxx new file mode 100644 index 000000000..46b7fc47d --- /dev/null +++ b/sc/source/ui/drawfunc/fusel.cxx @@ -0,0 +1,546 @@ +/* -*- 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 <com/sun/star/embed/EmbedVerbs.hpp> +#include <editeng/flditem.hxx> +#include <svx/svddrgmt.hxx> +#include <svx/svdoole2.hxx> +#include <svx/svdotext.hxx> +#include <sfx2/dispatch.hxx> +#include <vcl/imapobj.hxx> +#include <svx/svdouno.hxx> +#include <svx/svdomedia.hxx> +#include <svx/svdpagv.hxx> +#include <svx/ImageMapInfo.hxx> +#include <editeng/outlobj.hxx> +#include <sfx2/app.hxx> +#include <sfx2/ipclient.hxx> +#include <sfx2/viewfrm.hxx> +#include <comphelper/lok.hxx> + +#include <fusel.hxx> +#include <sc.hrc> +#include <fudraw.hxx> +#include <futext.hxx> +#include <drawview.hxx> +#include <tabvwsh.hxx> +#include <drwlayer.hxx> +#include <userdat.hxx> +#include <scmod.hxx> +#include <charthelper.hxx> +#include <docuno.hxx> +#include <docsh.hxx> + +// maximal permitted mouse movement to start Drag&Drop +//! fusel,fuconstr,futext - combine them! +#define SC_MAXDRAGMOVE 3 +// Min necessary mouse motion for normal dragging +#define SC_MINDRAGMOVE 2 + +using namespace com::sun::star; + +FuSelection::FuSelection(ScTabViewShell& rViewSh, vcl::Window* pWin, ScDrawView* pViewP, + SdrModel* pDoc, const SfxRequest& rReq) + : FuDraw(rViewSh, pWin, pViewP, pDoc, rReq) +{ +} + +FuSelection::~FuSelection() +{ +} + +bool FuSelection::MouseButtonDown(const MouseEvent& rMEvt) +{ + // remember button state for creation of own MouseEvents + SetMouseButtonCode(rMEvt.GetButtons()); + const bool bSelectionOnly = rMEvt.IsRight(); + if ( pView->IsAction() ) + { + if ( bSelectionOnly ) + pView->BckAction(); + return true; + } + + bIsInDragMode = false; // somewhere it has to be reset (#50033#) + + bool bReturn = FuDraw::MouseButtonDown(rMEvt); + auto aLogicPosition = rMEvt.getLogicPosition(); + if (aLogicPosition) + aMDPos = *aLogicPosition; + else + aMDPos = pWindow->PixelToLogic(rMEvt.GetPosPixel()); + + if (comphelper::LibreOfficeKit::isActive()) + { + ScViewData& rViewData = rViewShell.GetViewData(); + ScDocument& rDocument = rViewData.GetDocument(); + if (rDocument.IsNegativePage(rViewData.GetTabNo())) + aMDPos.setX(-aMDPos.X()); + } + + if ( rMEvt.IsLeft() ) + { + SdrHdl* pHdl = pView->PickHandle(aMDPos); + + if ( pHdl!=nullptr || pView->IsMarkedHit(aMDPos) ) + { + // Determine if this is the tail of a SdrCaptionObj i.e. + // we need to disable the drag option on the tail of a note + // object. Also, disable the ability to use the circular + // drag of a note object. + bool bDrag = false; + const SdrMarkList& rMarkList = pView->GetMarkedObjectList(); + if( rMarkList.GetMarkCount() == 1 ) + { + SdrObject* pMarkedObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj(); + if( ScDrawLayer::IsNoteCaption( pMarkedObj ) ) + { + // move using the valid caption handles for note text box. + if(pHdl && (pHdl->GetKind() != SdrHdlKind::Poly && pHdl->GetKind() != SdrHdlKind::Circle)) + bDrag = true; + // move the complete note box. + else if(!pHdl) + bDrag = true; + } + else + bDrag = true; // different object + } + else + bDrag = true; // several objects + + if ( bDrag ) + { + aDragTimer.Start(); + if (pView->BegDragObj(aMDPos, nullptr, pHdl)) + pView->GetDragMethod()->SetShiftPressed( rMEvt.IsShift() ); + bReturn = true; + } + } + else + { + SdrPageView* pPV = nullptr; + bool bAlt = rMEvt.IsMod2(); + SdrObject* pObj = !bAlt ? pView->PickObj(aMDPos, pView->getHitTolLog(), pPV, SdrSearchOptions::PICKMACRO) : nullptr; + if (pObj) + { + pView->BegMacroObj(aMDPos, pObj, pPV, pWindow); + bReturn = true; + } + else + { + OUString sURL, sTarget; + pObj = !bAlt ? pView->PickObj(aMDPos, pView->getHitTolLog(), pPV, SdrSearchOptions::ALSOONMASTER) : nullptr; + if (pObj) + { + // Support for imported Excel docs + // Excel is of course not consistent and allows + // a hyperlink to be assigned for an object group + // and even though the hyperlink is exported in the Escher layer + // its never used, when dealing with a group object the link + // associated with the clicked object is used only + + // additionally you can also select a macro in Excel for a grouped + // objects and this *usually* results in the macro being set + // for the elements in the group and no macro is exported + // for the group itself ( this however is not always true ) + // if a macro and hlink are defined favour the hlink + // If a group object has no hyperlink use the hyperlink of the + // object clicked + + if ( pObj->IsGroupObject() ) + { + ScMacroInfo* pTmpInfo = ScDrawLayer::GetMacroInfo( pObj ); + if ( !pTmpInfo || pTmpInfo->GetMacro().isEmpty() ) + { + SdrObject* pHit = pView->PickObj(aMDPos, pView->getHitTolLog(), pPV, SdrSearchOptions::DEEP); + if (pHit) + pObj = pHit; + } + } + + ScMacroInfo* pInfo = ScDrawLayer::GetMacroInfo( pObj, true ); + // For interoperability favour links over macros if both are defined + if ( !pObj->getHyperlink().isEmpty() ) + { + sURL = pObj->getHyperlink(); + } + else if ( !pInfo->GetMacro().isEmpty() ) + { + SfxObjectShell* pObjSh = SfxObjectShell::Current(); + if ( pObjSh && SfxApplication::IsXScriptURL( pInfo->GetMacro() ) ) + { + uno::Reference< beans::XPropertySet > xProps( pObj->getUnoShape(), uno::UNO_QUERY ); + uno::Any aCaller; + if ( xProps.is() ) + { + try + { + aCaller = xProps->getPropertyValue("Name"); + } + catch( uno::Exception& ) {} + } + uno::Any aRet; + uno::Sequence< sal_Int16 > aOutArgsIndex; + uno::Sequence< uno::Any > aOutArgs; + uno::Sequence< uno::Any > aInArgs; + pObjSh->CallXScript( pInfo->GetMacro(), + aInArgs, aRet, aOutArgsIndex, aOutArgs, true, &aCaller ); + rViewShell.FakeButtonUp( rViewShell.GetViewData().GetActivePart() ); + return true; // no CaptureMouse etc. + } + } + } + + // URL / ImageMap + + SdrViewEvent aVEvt; + if ( !bAlt && + pView->PickAnything( rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt ) != SdrHitKind::NONE && + aVEvt.mpObj != nullptr ) + { + if ( SvxIMapInfo::GetIMapInfo( aVEvt.mpObj ) ) // ImageMap + { + const IMapObject* pIMapObj = + SvxIMapInfo::GetHitIMapObject( aVEvt.mpObj, aMDPos, pWindow->GetOutDev() ); + if ( pIMapObj && !pIMapObj->GetURL().isEmpty() ) + { + sURL = pIMapObj->GetURL(); + sTarget = pIMapObj->GetTarget(); + } + } + if ( aVEvt.meEvent == SdrEventKind::ExecuteUrl && aVEvt.mpURLField ) // URL + { + sURL = aVEvt.mpURLField->GetURL(); + sTarget = aVEvt.mpURLField->GetTargetFrame(); + } + } + + // open hyperlink, if found at object or in object's text + // Fragments pointing into the current document should be always opened. + if ( !sURL.isEmpty() && (ScGlobal::ShouldOpenURL() || sURL.startsWith("#")) ) + { + ScGlobal::OpenURL( sURL, sTarget ); + rViewShell.FakeButtonUp( rViewShell.GetViewData().GetActivePart() ); + return true; // no CaptureMouse etc. + } + + // Is another object being edited in this view? + // (Editing is ended in MarkListHasChanged - test before UnmarkAll) + SfxInPlaceClient* pClient = rViewShell.GetIPClient(); + bool bWasOleActive = ( pClient && pClient->IsObjectInPlaceActive() ); + + // Selection + + // do not allow multiselection with note caption + bool bCaptionClicked = IsNoteCaptionClicked( aMDPos ); + if ( !rMEvt.IsShift() || bCaptionClicked || IsNoteCaptionMarked() ) + pView->UnmarkAll(); + + /* Unlock internal layer, if a note caption is clicked. The + layer will be relocked in ScDrawView::MarkListHasChanged(). */ + if( bCaptionClicked ) + pView->UnlockInternalLayer(); + + // try to select the clicked object + if ( pView->MarkObj( aMDPos, -2, false, rMEvt.IsMod1() ) ) + { + + // move object + + if (pView->IsMarkedHit(aMDPos)) + { + // Don't start drag timer if inplace editing of an OLE object + // was just ended with this mouse click - the view will be moved + // (different tool bars) and the object that was clicked on would + // be moved unintentionally. + if ( !bWasOleActive ) + aDragTimer.Start(); + + pHdl=pView->PickHandle(aMDPos); + pView->BegDragObj(aMDPos, nullptr, pHdl); + bReturn = true; + } + else // object at the edge + if (rViewShell.IsDrawSelMode()) + bReturn = true; + } + else + { + if (rViewShell.IsDrawSelMode()) + { + + // select object + + pView->BegMarkObj(aMDPos); + bReturn = true; + } + } + } + } + + } + + if (!bIsInDragMode) + { + // VC calls CaptureMouse itself + pWindow->CaptureMouse(); + ForcePointer(&rMEvt); + } + + return bReturn; +} + +bool FuSelection::MouseMove(const MouseEvent& rMEvt) +{ + bool bReturn = FuDraw::MouseMove(rMEvt); + + if (aDragTimer.IsActive() ) + { + Point aOldPixel = pWindow->LogicToPixel( aMDPos ); + Point aNewPixel = rMEvt.GetPosPixel(); + if ( std::abs( aOldPixel.X() - aNewPixel.X() ) > SC_MAXDRAGMOVE || + std::abs( aOldPixel.Y() - aNewPixel.Y() ) > SC_MAXDRAGMOVE ) + aDragTimer.Stop(); + } + + if ( pView->IsAction() ) + { + Point aPix(rMEvt.GetPosPixel()); + Point aPnt(pWindow->PixelToLogic(aPix)); + + ForceScroll(aPix); + pView->MovAction(aPnt); + bReturn = true; + } + + ForcePointer(&rMEvt); + + return bReturn; +} + +bool FuSelection::MouseButtonUp(const MouseEvent& rMEvt) +{ + // remember button state for creation of own MouseEvents + SetMouseButtonCode(rMEvt.GetButtons()); + + bool bReturn = FuDraw::MouseButtonUp(rMEvt); + bool bOle = rViewShell.GetViewFrame()->GetFrame().IsInPlace(); + + SdrObject* pObj = nullptr; + if (aDragTimer.IsActive() ) + { + aDragTimer.Stop(); + } + + sal_uInt16 nDrgLog = sal_uInt16 ( pWindow->PixelToLogic(Size(SC_MINDRAGMOVE,0)).Width() ); + auto aLogicPosition = rMEvt.getLogicPosition(); + Point aPnt(aLogicPosition ? *aLogicPosition : pWindow->PixelToLogic(rMEvt.GetPosPixel())); + + bool bCopy = false; + ScViewData& rViewData = rViewShell.GetViewData(); + ScDocument& rDocument = rViewData.GetDocument(); + SdrPageView* pPageView = ( pView ? pView->GetSdrPageView() : nullptr ); + SdrPage* pPage = ( pPageView ? pPageView->GetPage() : nullptr ); + ::std::vector< OUString > aExcludedChartNames; + ScRangeListVector aProtectedChartRangesVector; + + if (comphelper::LibreOfficeKit::isActive() && rDocument.IsNegativePage(rViewData.GetTabNo())) + aPnt.setX(-aPnt.X()); + + if (pView && rMEvt.IsLeft()) + { + if ( pView->IsDragObj() ) + { + // object was moved + if ( rMEvt.IsMod1() ) + { + if ( pPage ) + { + ScChartHelper::GetChartNames( aExcludedChartNames, pPage ); + } + if ( pView ) + { + const SdrMarkList& rSdrMarkList = pView->GetMarkedObjectList(); + const size_t nMarkCount = rSdrMarkList.GetMarkCount(); + for ( size_t i = 0; i < nMarkCount; ++i ) + { + SdrMark* pMark = rSdrMarkList.GetMark( i ); + pObj = ( pMark ? pMark->GetMarkedSdrObj() : nullptr ); + if ( pObj ) + { + ScChartHelper::AddRangesIfProtectedChart( aProtectedChartRangesVector, rDocument, pObj ); + } + } + } + bCopy = true; + } + + if (!rMEvt.IsShift() && !rMEvt.IsMod1() && !rMEvt.IsMod2() && + std::abs(aPnt.X() - aMDPos.X()) < nDrgLog && + std::abs(aPnt.Y() - aMDPos.Y()) < nDrgLog) + { + /* If a user wants to click on an object in front of a marked + one, he releases the mouse button immediately */ + SdrPageView* pPV = nullptr; + pObj = pView->PickObj(aMDPos, pView->getHitTolLog(), pPV, SdrSearchOptions::ALSOONMASTER | SdrSearchOptions::BEFOREMARK); + if (pObj) + { + pView->UnmarkAllObj(); + pView->MarkObj(pObj,pPV); + return true; + } + } + pView->EndDragObj( rMEvt.IsMod1() ); + pView->ForceMarkedToAnotherPage(); + + bReturn = true; + } + else if (pView->IsAction() ) + { + // unlock internal layer to include note captions + pView->UnlockInternalLayer(); + pView->EndAction(); + if ( pView->AreObjectsMarked() ) + { + bReturn = true; + + /* if multi-selection contains a note caption object, remove + all other objects from selection. */ + const SdrMarkList& rMarkList = pView->GetMarkedObjectList(); + const size_t nCount = rMarkList.GetMarkCount(); + if( nCount > 1 ) + { + bool bFound = false; + for( size_t nIdx = 0; !bFound && (nIdx < nCount); ++nIdx ) + { + pObj = rMarkList.GetMark( nIdx )->GetMarkedSdrObj(); + bFound = ScDrawLayer::IsNoteCaption( pObj ); + if( bFound ) + { + pView->UnMarkAll(); + pView->MarkObj( pObj, pView->GetSdrPageView() ); + } + } + } + } + } + } + + // maybe consider OLE object + SfxInPlaceClient* pIPClient = rViewShell.GetIPClient(); + + if (pIPClient) + { + ScModule* pScMod = SC_MOD(); + bool bUnoRefDialog = pScMod->IsRefDialogOpen() && pScMod->GetCurRefDlgId() == WID_SIMPLE_REF; + + if ( pIPClient->IsObjectInPlaceActive() && !bUnoRefDialog ) + pIPClient->DeactivateObject(); + } + + sal_uInt16 nClicks = rMEvt.GetClicks(); + if (pView && nClicks == 2 && rMEvt.IsLeft()) + { + if ( pView->AreObjectsMarked() ) + { + const SdrMarkList& rMarkList = pView->GetMarkedObjectList(); + if (rMarkList.GetMarkCount() == 1) + { + SdrMark* pMark = rMarkList.GetMark(0); + pObj = pMark->GetMarkedSdrObj(); + + // only activate, when the mouse also is over the selected object + + SdrViewEvent aVEvt; + SdrHitKind eHit = pView->PickAnything( rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt ); + if (eHit != SdrHitKind::NONE && aVEvt.mpObj == pObj) + { + SdrObjKind nSdrObjKind = pObj->GetObjIdentifier(); + + // OLE: activate + + if (nSdrObjKind == SdrObjKind::OLE2) + { + if (!bOle) + { + if (static_cast<SdrOle2Obj*>(pObj)->GetObjRef().is()) + { + rViewShell.ActivateObject(static_cast<SdrOle2Obj*>(pObj), css::embed::EmbedVerbs::MS_OLEVERB_PRIMARY); + } + } + } + + // Edit text + // not in UNO controls + // #i32352# not in media objects + + else if ( dynamic_cast<const SdrTextObj*>( pObj) != nullptr && dynamic_cast<const SdrUnoObj*>( pObj) == nullptr && dynamic_cast<const SdrMediaObj*>( pObj) == nullptr ) + { + OutlinerParaObject* pOPO = pObj->GetOutlinerParaObject(); + bool bVertical = ( pOPO && pOPO->IsEffectivelyVertical() ); + sal_uInt16 nTextSlotId = bVertical ? SID_DRAW_TEXT_VERTICAL : SID_DRAW_TEXT; + + rViewShell.GetViewData().GetDispatcher(). + Execute(nTextSlotId, SfxCallMode::SYNCHRON | SfxCallMode::RECORD); + + // Get the created FuText now and change into EditMode + FuPoor* pPoor = rViewShell.GetViewData().GetView()->GetDrawFuncPtr(); + if ( pPoor && pPoor->GetSlotID() == nTextSlotId ) // has no RTTI + { + FuText* pText = static_cast<FuText*>(pPoor); + Point aMousePixel = rMEvt.GetPosPixel(); + pText->SetInEditMode( pObj, &aMousePixel ); + } + bReturn = true; + } + } + } + } + else if ( TestDetective( pView->GetSdrPageView(), aPnt ) ) + bReturn = true; + } + + ForcePointer(&rMEvt); + + if (pWindow->IsMouseCaptured()) + pWindow->ReleaseMouse(); + + // command handler for context menu follows after MouseButtonUp, + // therefore here the hard IsLeft call + if ( !bReturn && rMEvt.IsLeft() ) + if (rViewShell.IsDrawSelMode()) + rViewShell.GetViewData().GetDispatcher(). + Execute(SID_OBJECT_SELECT, SfxCallMode::SLOT | SfxCallMode::RECORD); + + if ( bCopy && pPage ) + { + ScDocShell* pDocShell = rViewData.GetDocShell(); + ScModelObj* pModelObj = ( pDocShell ? comphelper::getFromUnoTunnel<ScModelObj>( pDocShell->GetModel() ) : nullptr ); + if ( pModelObj ) + { + SCTAB nTab = rViewData.GetTabNo(); + ScChartHelper::CreateProtectedChartListenersAndNotify( rDocument, pPage, pModelObj, nTab, + aProtectedChartRangesVector, aExcludedChartNames ); + } + } + + return bReturn; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |