diff options
Diffstat (limited to 'sc/source/ui/app')
-rw-r--r-- | sc/source/ui/app/client.cxx | 232 | ||||
-rw-r--r-- | sc/source/ui/app/drwtrans.cxx | 738 | ||||
-rw-r--r-- | sc/source/ui/app/inputhdl.cxx | 4635 | ||||
-rw-r--r-- | sc/source/ui/app/inputwin.cxx | 2703 | ||||
-rw-r--r-- | sc/source/ui/app/lnktrans.cxx | 76 | ||||
-rw-r--r-- | sc/source/ui/app/msgpool.cxx | 93 | ||||
-rw-r--r-- | sc/source/ui/app/rfindlst.cxx | 90 | ||||
-rw-r--r-- | sc/source/ui/app/scdll.cxx | 257 | ||||
-rw-r--r-- | sc/source/ui/app/scmod.cxx | 2325 | ||||
-rw-r--r-- | sc/source/ui/app/seltrans.cxx | 429 | ||||
-rw-r--r-- | sc/source/ui/app/transobj.cxx | 921 | ||||
-rw-r--r-- | sc/source/ui/app/typemap.cxx | 140 | ||||
-rw-r--r-- | sc/source/ui/app/uiitems.cxx | 438 |
13 files changed, 13077 insertions, 0 deletions
diff --git a/sc/source/ui/app/client.cxx b/sc/source/ui/app/client.cxx new file mode 100644 index 000000000..0886fae89 --- /dev/null +++ b/sc/source/ui/app/client.cxx @@ -0,0 +1,232 @@ +/* -*- 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/XEmbeddedObject.hpp> + +#include <toolkit/helper/vclunohelper.hxx> +#include <tools/diagnose_ex.h> +#include <sfx2/objsh.hxx> +#include <svx/svditer.hxx> +#include <svx/svdobj.hxx> +#include <svx/svdmodel.hxx> +#include <svx/svdpage.hxx> +#include <svx/svdoole2.hxx> + +#include <client.hxx> +#include <tabvwsh.hxx> +#include <docsh.hxx> +#include <gridwin.hxx> + +using namespace com::sun::star; + +ScClient::ScClient( ScTabViewShell* pViewShell, vcl::Window* pDraw, SdrModel* pSdrModel, const SdrOle2Obj* pObj ) : + SfxInPlaceClient( pViewShell, pDraw, pObj->GetAspect() ), + pModel( pSdrModel ) +{ + SetObject( pObj->GetObjRef() ); +} + +ScClient::~ScClient() +{ +} + +SdrOle2Obj* ScClient::GetDrawObj() +{ + uno::Reference < embed::XEmbeddedObject > xObj = GetObject(); + SdrOle2Obj* pOle2Obj = nullptr; + OUString aName = GetViewShell()->GetObjectShell()->GetEmbeddedObjectContainer().GetEmbeddedObjectName( xObj ); + + sal_uInt16 nPages = pModel->GetPageCount(); + for (sal_uInt16 nPNr=0; nPNr<nPages && !pOle2Obj; nPNr++) + { + SdrPage* pPage = pModel->GetPage(nPNr); + SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups ); + SdrObject* pObject = aIter.Next(); + while (pObject && !pOle2Obj) + { + if ( pObject->GetObjIdentifier() == SdrObjKind::OLE2 ) + { + // name from InfoObject is PersistName + if ( static_cast<SdrOle2Obj*>(pObject)->GetPersistName() == aName ) + pOle2Obj = static_cast<SdrOle2Obj*>(pObject); + } + pObject = aIter.Next(); + } + } + return pOle2Obj; +} + +void ScClient::RequestNewObjectArea( tools::Rectangle& aLogicRect ) +{ + SfxViewShell* pSfxViewSh = GetViewShell(); + ScTabViewShell* pViewSh = dynamic_cast<ScTabViewShell*>( pSfxViewSh ); + if (!pViewSh) + { + OSL_FAIL("Wrong ViewShell"); + return; + } + + tools::Rectangle aOldRect = GetObjArea(); + SdrOle2Obj* pDrawObj = GetDrawObj(); + if ( pDrawObj ) + { + if ( pDrawObj->IsResizeProtect() ) + aLogicRect.SetSize( aOldRect.GetSize() ); + + if ( pDrawObj->IsMoveProtect() ) + aLogicRect.SetPos( aOldRect.TopLeft() ); + } + + sal_uInt16 nTab = pViewSh->GetViewData().GetTabNo(); + SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(static_cast<sal_Int16>(nTab))); + if ( !(pPage && aLogicRect != aOldRect) ) + return; + + Point aPos; + Size aSize = pPage->GetSize(); + if ( aSize.Width() < 0 ) + { + aPos.setX( aSize.Width() + 1 ); // negative + aSize.setWidth( -aSize.Width() ); // positive + } + tools::Rectangle aPageRect( aPos, aSize ); + + if (aLogicRect.Right() > aPageRect.Right()) + { + tools::Long nDiff = aLogicRect.Right() - aPageRect.Right(); + aLogicRect.AdjustLeft( -nDiff ); + aLogicRect.AdjustRight( -nDiff ); + } + if (aLogicRect.Bottom() > aPageRect.Bottom()) + { + tools::Long nDiff = aLogicRect.Bottom() - aPageRect.Bottom(); + aLogicRect.AdjustTop( -nDiff ); + aLogicRect.AdjustBottom( -nDiff ); + } + + if (aLogicRect.Left() < aPageRect.Left()) + { + tools::Long nDiff = aLogicRect.Left() - aPageRect.Left(); + aLogicRect.AdjustRight( -nDiff ); + aLogicRect.AdjustLeft( -nDiff ); + } + if (aLogicRect.Top() < aPageRect.Top()) + { + tools::Long nDiff = aLogicRect.Top() - aPageRect.Top(); + aLogicRect.AdjustBottom( -nDiff ); + aLogicRect.AdjustTop( -nDiff ); + } +} + +void ScClient::ObjectAreaChanged() +{ + SfxViewShell* pSfxViewSh = GetViewShell(); + ScTabViewShell* pViewSh = dynamic_cast<ScTabViewShell*>( pSfxViewSh ); + if (!pViewSh) + { + OSL_FAIL("Wrong ViewShell"); + return; + } + + // Take over position and size into document + SdrOle2Obj* pDrawObj = GetDrawObj(); + if (!pDrawObj) + return; + + tools::Rectangle aNewRectangle(GetScaledObjArea()); + + // #i118524# if sheared/rotated, center to non-rotated LogicRect + pDrawObj->setSuppressSetVisAreaSize(true); + + if(pDrawObj->GetGeoStat().nRotationAngle || pDrawObj->GetGeoStat().nShearAngle) + { + pDrawObj->SetLogicRect( aNewRectangle ); + + const tools::Rectangle& rBoundRect = pDrawObj->GetCurrentBoundRect(); + const Point aDelta(aNewRectangle.Center() - rBoundRect.Center()); + + aNewRectangle.Move(aDelta.X(), aDelta.Y()); + } + + pDrawObj->SetLogicRect( aNewRectangle ); + pDrawObj->setSuppressSetVisAreaSize(false); + + // set document modified (SdrModel::SetChanged is not used) + pViewSh->GetViewData().GetDocShell()->SetDrawModified(); + pViewSh->ScrollToObject(pDrawObj); +} + +void ScClient::ViewChanged() +{ + if ( GetAspect() == embed::Aspects::MSOLE_ICON ) + { + // the iconified object seems not to need such a scaling handling + // since the replacement image and the size a completely controlled by the container + // TODO/LATER: when the icon exchange is implemented the scaling handling might be required again here + + return; + } + + uno::Reference < embed::XEmbeddedObject > xObj = GetObject(); + + // TODO/LEAN: working with Visual Area can switch object to running state + awt::Size aSz; + try { + aSz = xObj->getVisualAreaSize( GetAspect() ); + } catch (const uno::Exception&) { + TOOLS_WARN_EXCEPTION("sc", "The visual area size must be available!"); + return; // leave it unchanged on failure + } + + MapUnit aMapUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( GetAspect() ) ); + Size aVisSize = OutputDevice::LogicToLogic(Size(aSz.Width, aSz.Height), MapMode(aMapUnit), MapMode(MapUnit::Map100thMM)); + + // Take over position and size into document + SdrOle2Obj* pDrawObj = GetDrawObj(); + if (!pDrawObj) + return; + + tools::Rectangle aLogicRect = pDrawObj->GetLogicRect(); + Fraction aFractX = GetScaleWidth() * aVisSize.Width(); + Fraction aFractY = GetScaleHeight() * aVisSize.Height(); + aVisSize = Size( static_cast<tools::Long>(aFractX), static_cast<tools::Long>(aFractY) ); // Scaled for Draw model + + // pClientData->SetObjArea before pDrawObj->SetLogicRect, so that we don't + // calculate wrong scalings: + //Rectangle aObjArea = aLogicRect; + //aObjArea.SetSize( aVisSize ); // Document size from the server + //SetObjArea( aObjArea ); + + SfxViewShell* pSfxViewSh = GetViewShell(); + ScTabViewShell* pViewSh = dynamic_cast<ScTabViewShell*>( pSfxViewSh ); + if ( pViewSh ) + { + vcl::Window* pWin = pViewSh->GetActiveWin(); + if ( pWin->LogicToPixel( aVisSize ) != pWin->LogicToPixel( aLogicRect.GetSize() ) ) + { + aLogicRect.SetSize( aVisSize ); + pDrawObj->SetLogicRect( aLogicRect ); + + // set document modified (SdrModel::SetChanged is not used) + pViewSh->GetViewData().GetDocShell()->SetDrawModified(); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/app/drwtrans.cxx b/sc/source/ui/app/drwtrans.cxx new file mode 100644 index 000000000..8cd335b2e --- /dev/null +++ b/sc/source/ui/app/drwtrans.cxx @@ -0,0 +1,738 @@ +/* -*- 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/XTransactedObject.hpp> +#include <com/sun/star/embed/XEmbedPersist.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/uno/Exception.hpp> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XPropertySetInfo.hpp> +#include <com/sun/star/form/FormButtonType.hpp> +#include <unotools/streamwrap.hxx> + +#include <svx/unomodel.hxx> +#include <unotools/tempfile.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <comphelper/fileformat.h> +#include <comphelper/storagehelper.hxx> +#include <comphelper/servicehelper.hxx> + +#include <svtools/embedtransfer.hxx> +#include <sot/storage.hxx> +#include <svx/svditer.hxx> +#include <svx/svdograf.hxx> +#include <svx/svdoole2.hxx> +#include <svx/svdouno.hxx> +#include <sfx2/docfile.hxx> +#include <svl/itempool.hxx> +#include <svl/urlbmk.hxx> +#include <tools/urlobj.hxx> +#include <osl/diagnose.h> + +#include <drwtrans.hxx> +#include <docsh.hxx> +#include <drwlayer.hxx> +#include <drawview.hxx> +#include <viewdata.hxx> +#include <scmod.hxx> +#include <dragdata.hxx> + +#include <editeng/eeitem.hxx> + +#include <editeng/fhgtitem.hxx> +#include <vcl/svapp.hxx> + +using namespace com::sun::star; + +constexpr sal_uInt32 SCDRAWTRANS_TYPE_EMBOBJ = 1; +constexpr sal_uInt32 SCDRAWTRANS_TYPE_DRAWMODEL = 2; +constexpr sal_uInt32 SCDRAWTRANS_TYPE_DOCUMENT = 3; + +ScDrawTransferObj::ScDrawTransferObj( std::unique_ptr<SdrModel> pClipModel, ScDocShell* pContainerShell, + const TransferableObjectDescriptor& rDesc ) : + m_pModel( std::move(pClipModel) ), + m_aObjDesc( rDesc ), + m_bGraphic( false ), + m_bGrIsBit( false ), + m_bOleObj( false ), + m_nDragSourceFlags( ScDragSrc::Undefined ), + m_bDragWasInternal( false ), + maShellID(SfxObjectShell::CreateShellID(pContainerShell)) +{ + + // check what kind of objects are contained + + SdrPage* pPage = m_pModel->GetPage(0); + if (pPage) + { + SdrObjListIter aIter( pPage, SdrIterMode::Flat ); + SdrObject* pObject = aIter.Next(); + if (pObject && !aIter.Next()) // exactly one object? + { + + // OLE object + + SdrObjKind nSdrObjKind = pObject->GetObjIdentifier(); + if (nSdrObjKind == SdrObjKind::OLE2) + { + // if object has no persistence it must be copied as a part of document + try + { + uno::Reference< embed::XEmbedPersist > xPersObj( static_cast<SdrOle2Obj*>(pObject)->GetObjRef(), uno::UNO_QUERY ); + if ( xPersObj.is() && xPersObj->hasEntry() ) + m_bOleObj = true; + } + catch( uno::Exception& ) + {} + // aOleData is initialized later + } + + // Graphic object + + if (nSdrObjKind == SdrObjKind::Graphic) + { + m_bGraphic = true; + if ( static_cast<SdrGrafObj*>(pObject)->GetGraphic().GetType() == GraphicType::Bitmap ) + m_bGrIsBit = true; + } + + // URL button + + SdrUnoObj* pUnoCtrl = dynamic_cast<SdrUnoObj*>( pObject ); + if (pUnoCtrl && SdrInventor::FmForm == pUnoCtrl->GetObjInventor()) + { + const uno::Reference<awt::XControlModel>& xControlModel = pUnoCtrl->GetUnoControlModel(); + OSL_ENSURE( xControlModel.is(), "uno control without model" ); + if ( xControlModel.is() ) + { + uno::Reference< beans::XPropertySet > xPropSet( xControlModel, uno::UNO_QUERY ); + uno::Reference< beans::XPropertySetInfo > xInfo = xPropSet->getPropertySetInfo(); + + OUString sPropButtonType( "ButtonType" ); + + if(xInfo->hasPropertyByName( sPropButtonType )) + { + uno::Any aAny = xPropSet->getPropertyValue( sPropButtonType ); + form::FormButtonType eTmp; + if ( (aAny >>= eTmp) && eTmp == form::FormButtonType_URL ) + { + // URL + OUString sPropTargetURL( "TargetURL" ); + if(xInfo->hasPropertyByName( sPropTargetURL )) + { + aAny = xPropSet->getPropertyValue( sPropTargetURL ); + OUString sTmp; + if ( (aAny >>= sTmp) && !sTmp.isEmpty() ) + { + OUString aUrl = sTmp; + OUString aAbs = aUrl; + if (pContainerShell) + { + const SfxMedium* pMedium = pContainerShell->GetMedium(); + if (pMedium) + { + bool bWasAbs = true; + aAbs = pMedium->GetURLObject().smartRel2Abs( aUrl, bWasAbs ). + GetMainURL(INetURLObject::DecodeMechanism::NONE); + // full path as stored INetBookmark must be encoded + } + } + + // Label + OUString aLabel; + OUString sPropLabel( "Label" ); + if(xInfo->hasPropertyByName( sPropLabel )) + { + aAny = xPropSet->getPropertyValue( sPropLabel ); + if ( (aAny >>= sTmp) && !sTmp.isEmpty() ) + { + aLabel = sTmp; + } + } + m_pBookmark.reset( new INetBookmark( aAbs, aLabel ) ); + } + } + } + } + } + } + } + } + + // get size for object descriptor + + // #i71538# use complete SdrViews + // SdrExchangeView aView(pModel); + SdrView aView(*m_pModel); + SdrPageView* pPv = aView.ShowSdrPage(aView.GetModel()->GetPage(0)); + aView.MarkAllObj(pPv); + m_aSrcSize = aView.GetAllMarkedRect().GetSize(); + + if ( m_bOleObj ) // single OLE object + { + SdrOle2Obj* pObj = GetSingleObject(); + if ( pObj && pObj->GetObjRef().is() ) + SvEmbedTransferHelper::FillTransferableObjectDescriptor( m_aObjDesc, pObj->GetObjRef(), pObj->GetGraphic(), pObj->GetAspect() ); + } + + m_aObjDesc.maSize = m_aSrcSize; + PrepareOLE( m_aObjDesc ); + + // remember a unique ID of the source document + + if ( pContainerShell ) + { + ScDocument& rDoc = pContainerShell->GetDocument(); + if ( pPage ) + { + ScChartHelper::FillProtectedChartRangesVector( m_aProtectedChartRangesVector, rDoc, pPage ); + } + } +} + +ScDrawTransferObj::~ScDrawTransferObj() +{ + SolarMutexGuard aSolarGuard; + + ScModule* pScMod = SC_MOD(); + if (pScMod && pScMod->GetDragData().pDrawTransfer == this) + { + OSL_FAIL("ScDrawTransferObj wasn't released"); + pScMod->ResetDragObject(); + } + + m_aOleData = TransferableDataHelper(); // clear before releasing the mutex + m_aDocShellRef.clear(); + + m_pModel.reset(); + m_aDrawPersistRef.clear(); // after the model + + m_pBookmark.reset(); + m_pDragSourceView.reset(); +} + +ScDrawTransferObj* ScDrawTransferObj::GetOwnClipboard(const uno::Reference<datatransfer::XTransferable2>& xTransferable) +{ + return comphelper::getFromUnoTunnel<ScDrawTransferObj>(xTransferable); +} + +static bool lcl_HasOnlyControls( SdrModel* pModel ) +{ + bool bOnlyControls = false; // default if there are no objects + + if ( pModel ) + { + SdrPage* pPage = pModel->GetPage(0); + if (pPage) + { + SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups ); + SdrObject* pObj = aIter.Next(); + if ( pObj ) + { + bOnlyControls = true; // only set if there are any objects at all + while ( pObj ) + { + if (dynamic_cast<const SdrUnoObj*>( pObj) == nullptr) + { + bOnlyControls = false; + break; + } + pObj = aIter.Next(); + } + } + } + } + + return bOnlyControls; +} + +void ScDrawTransferObj::AddSupportedFormats() +{ + if ( m_bGrIsBit ) // single bitmap graphic + { + AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ); + AddFormat( SotClipboardFormatId::SVXB ); + AddFormat( SotClipboardFormatId::PNG ); + AddFormat( SotClipboardFormatId::BITMAP ); + AddFormat( SotClipboardFormatId::GDIMETAFILE ); + } + else if ( m_bGraphic ) // other graphic + { + // #i25616# + AddFormat( SotClipboardFormatId::DRAWING ); + + AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ); + AddFormat( SotClipboardFormatId::SVXB ); + AddFormat( SotClipboardFormatId::GDIMETAFILE ); + AddFormat( SotClipboardFormatId::PNG ); + AddFormat( SotClipboardFormatId::BITMAP ); + } + else if ( m_pBookmark ) // url button + { +// AddFormat( SotClipboardFormatId::EMBED_SOURCE ); + AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ); + AddFormat( SotClipboardFormatId::SOLK ); + AddFormat( SotClipboardFormatId::STRING ); + AddFormat( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ); + AddFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK ); + AddFormat( SotClipboardFormatId::DRAWING ); + } + else if ( m_bOleObj ) // single OLE object + { + AddFormat( SotClipboardFormatId::EMBED_SOURCE ); + AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ); + AddFormat( SotClipboardFormatId::GDIMETAFILE ); + + CreateOLEData(); + + if ( m_aOleData.GetTransferable().is() ) + { + // get format list from object snapshot + // (this must be after inserting the default formats!) + + DataFlavorExVector aVector( m_aOleData.GetDataFlavorExVector() ); + + for( const auto& rItem : aVector ) + AddFormat( rItem ); + } + } + else // any drawing objects + { + AddFormat( SotClipboardFormatId::EMBED_SOURCE ); + AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ); + AddFormat( SotClipboardFormatId::DRAWING ); + + // leave out bitmap and metafile if there are only controls + if ( !lcl_HasOnlyControls( m_pModel.get() ) ) + { + AddFormat( SotClipboardFormatId::PNG ); + AddFormat( SotClipboardFormatId::BITMAP ); + AddFormat( SotClipboardFormatId::GDIMETAFILE ); + } + } + +// if( pImageMap ) +// AddFormat( SotClipboardFormatId::SVIM ); +} + +bool ScDrawTransferObj::GetData( const css::datatransfer::DataFlavor& rFlavor, const OUString& rDestDoc ) +{ + bool bOK = false; + SotClipboardFormatId nFormat = SotExchange::GetFormat( rFlavor ); + + if ( m_bOleObj && nFormat != SotClipboardFormatId::GDIMETAFILE ) + { + CreateOLEData(); + + if( m_aOleData.GetTransferable().is() && m_aOleData.HasFormat( rFlavor ) ) + { + bOK = SetAny( m_aOleData.GetAny(rFlavor, rDestDoc) ); + + return bOK; + } + } + + if( HasFormat( nFormat ) ) + { + if ( nFormat == SotClipboardFormatId::LINKSRCDESCRIPTOR || nFormat == SotClipboardFormatId::OBJECTDESCRIPTOR ) + { + bOK = SetTransferableObjectDescriptor( m_aObjDesc ); + } + else if ( nFormat == SotClipboardFormatId::DRAWING ) + { + bOK = SetObject( m_pModel.get(), SCDRAWTRANS_TYPE_DRAWMODEL, rFlavor ); + } + else if ( nFormat == SotClipboardFormatId::BITMAP + || nFormat == SotClipboardFormatId::PNG + || nFormat == SotClipboardFormatId::GDIMETAFILE ) + { + // #i71538# use complete SdrViews + // SdrExchangeView aView( pModel ); + SdrView aView(*m_pModel); + SdrPageView* pPv = aView.ShowSdrPage(aView.GetModel()->GetPage(0)); + OSL_ENSURE( pPv, "pPv not there..." ); + aView.MarkAllObj( pPv ); + if ( nFormat == SotClipboardFormatId::GDIMETAFILE ) + bOK = SetGDIMetaFile( aView.GetMarkedObjMetaFile(true) ); + else + bOK = SetBitmapEx( aView.GetMarkedObjBitmapEx(true), rFlavor ); + } + else if ( nFormat == SotClipboardFormatId::SVXB ) + { + // only enabled for single graphics object + + SdrPage* pPage = m_pModel->GetPage(0); + if (pPage) + { + SdrObjListIter aIter( pPage, SdrIterMode::Flat ); + SdrObject* pObject = aIter.Next(); + if (pObject && pObject->GetObjIdentifier() == SdrObjKind::Graphic) + { + SdrGrafObj* pGraphObj = static_cast<SdrGrafObj*>(pObject); + bOK = SetGraphic( pGraphObj->GetGraphic() ); + } + } + } + else if ( nFormat == SotClipboardFormatId::EMBED_SOURCE ) + { + if ( m_bOleObj ) // single OLE object + { + SdrOle2Obj* pObj = GetSingleObject(); + if ( pObj && pObj->GetObjRef().is() ) + { + bOK = SetObject( pObj->GetObjRef().get(), SCDRAWTRANS_TYPE_EMBOBJ, rFlavor ); + } + } + else // create object from contents + { + //TODO/LATER: needs new Format, because now single OLE and "this" are different + InitDocShell(); // set aDocShellRef + + SfxObjectShell* pEmbObj = m_aDocShellRef.get(); + bOK = SetObject( pEmbObj, SCDRAWTRANS_TYPE_DOCUMENT, rFlavor ); + } + } + else if( m_pBookmark ) + { + bOK = SetINetBookmark( *m_pBookmark, rFlavor ); + } + } + return bOK; +} + +bool ScDrawTransferObj::WriteObject( tools::SvRef<SotTempStream>& rxOStm, void* pUserObject, sal_uInt32 nUserObjectId, + const css::datatransfer::DataFlavor& /* rFlavor */ ) +{ + // called from SetObject, put data into stream + + bool bRet = false; + switch (nUserObjectId) + { + case SCDRAWTRANS_TYPE_DRAWMODEL: + { + SdrModel* pDrawModel = static_cast<SdrModel*>(pUserObject); + rxOStm->SetBufferSize( 0xff00 ); + + // for the changed pool defaults from drawing layer pool set those + // attributes as hard attributes to preserve them for saving + const SfxItemPool& rItemPool = m_pModel->GetItemPool(); + const SvxFontHeightItem& rDefaultFontHeight = rItemPool.GetDefaultItem(EE_CHAR_FONTHEIGHT); + + // SW should have no MasterPages + OSL_ENSURE(0 == m_pModel->GetMasterPageCount(), "SW with MasterPages (!)"); + + for(sal_uInt16 a(0); a < m_pModel->GetPageCount(); a++) + { + const SdrPage* pPage(m_pModel->GetPage(a)); + SdrObjListIter aIter(pPage, SdrIterMode::DeepNoGroups); + + while(aIter.IsMore()) + { + SdrObject* pObj = aIter.Next(); + const SvxFontHeightItem& rItem = pObj->GetMergedItem(EE_CHAR_FONTHEIGHT); + + if(rItem.GetHeight() == rDefaultFontHeight.GetHeight()) + { + pObj->SetMergedItem(rDefaultFontHeight); + } + } + } + + { + css::uno::Reference<css::io::XOutputStream> xDocOut( new utl::OOutputStreamWrapper( *rxOStm ) ); + SvxDrawingLayerExport( pDrawModel, xDocOut ); + } + + bRet = ( rxOStm->GetError() == ERRCODE_NONE ); + } + break; + + case SCDRAWTRANS_TYPE_EMBOBJ: + { + // impl. for "single OLE" + embed::XEmbeddedObject* pEmbObj = static_cast<embed::XEmbeddedObject*>(pUserObject); + + ::utl::TempFile aTempFile; + aTempFile.EnableKillingFile(); + uno::Reference< embed::XStorage > xWorkStore = + ::comphelper::OStorageHelper::GetStorageFromURL( aTempFile.GetURL(), embed::ElementModes::READWRITE ); + + uno::Reference < embed::XEmbedPersist > xPers( static_cast<embed::XVisualObject*>(pEmbObj), uno::UNO_QUERY ); + if ( xPers.is() ) + { + try + { + uno::Sequence < beans::PropertyValue > aSeq; + OUString aDummyName("Dummy"); + xPers->storeToEntry( xWorkStore, aDummyName, aSeq, aSeq ); + if ( xWorkStore->isStreamElement( aDummyName ) ) + { + uno::Reference < io::XOutputStream > xDocOut( new utl::OOutputStreamWrapper( *rxOStm ) ); + uno::Reference < io::XStream > xNewStream = xWorkStore->openStreamElement( aDummyName, embed::ElementModes::READ ); + ::comphelper::OStorageHelper::CopyInputToOutput( xNewStream->getInputStream(), xDocOut ); + } + else + { + uno::Reference < io::XStream > xDocStr( new utl::OStreamWrapper( *rxOStm ) ); + uno::Reference< embed::XStorage > xDocStg = ::comphelper::OStorageHelper::GetStorageFromStream( xDocStr ); + uno::Reference < embed::XStorage > xNewStg = xWorkStore->openStorageElement( aDummyName, embed::ElementModes::READ ); + xNewStg->copyToStorage( xDocStg ); + uno::Reference < embed::XTransactedObject > xTrans( xDocStg, uno::UNO_QUERY ); + if ( xTrans.is() ) + xTrans->commit(); + } + } + catch ( uno::Exception& ) + { + } + } + + break; + } + case SCDRAWTRANS_TYPE_DOCUMENT: + { + // impl. for "DocShell" + SfxObjectShell* pEmbObj = static_cast<SfxObjectShell*>(pUserObject); + + try + { + ::utl::TempFile aTempFile; + aTempFile.EnableKillingFile(); + uno::Reference< embed::XStorage > xWorkStore = + ::comphelper::OStorageHelper::GetStorageFromURL( aTempFile.GetURL(), embed::ElementModes::READWRITE ); + + // write document storage + pEmbObj->SetupStorage( xWorkStore, SOFFICE_FILEFORMAT_CURRENT, false ); + + // mba: no relative URLs for clipboard! + SfxMedium aMedium( xWorkStore, OUString() ); + pEmbObj->DoSaveObjectAs( aMedium, false ); + pEmbObj->DoSaveCompleted(); + + uno::Reference< embed::XTransactedObject > xTransact( xWorkStore, uno::UNO_QUERY ); + if ( xTransact.is() ) + xTransact->commit(); + + std::unique_ptr<SvStream> pSrcStm = ::utl::UcbStreamHelper::CreateStream( aTempFile.GetURL(), StreamMode::READ ); + if( pSrcStm ) + { + rxOStm->SetBufferSize( 0xff00 ); + rxOStm->WriteStream( *pSrcStm ); + pSrcStm.reset(); + } + + xWorkStore->dispose(); + xWorkStore.clear(); + } + catch ( uno::Exception& ) + {} + + bRet = ( rxOStm->GetError() == ERRCODE_NONE ); + } + break; + + default: + OSL_FAIL("unknown object id"); + } + return bRet; +} + +void ScDrawTransferObj::DragFinished( sal_Int8 nDropAction ) +{ + if ( nDropAction == DND_ACTION_MOVE && !m_bDragWasInternal && !(m_nDragSourceFlags & ScDragSrc::Navigator) ) + { + // move: delete source objects + + if ( m_pDragSourceView ) + m_pDragSourceView->DeleteMarked(); + } + + ScModule* pScMod = SC_MOD(); + if ( pScMod->GetDragData().pDrawTransfer == this ) + pScMod->ResetDragObject(); + + m_pDragSourceView.reset(); + + TransferDataContainer::DragFinished( nDropAction ); +} + +void ScDrawTransferObj::SetDrawPersist( const SfxObjectShellRef& rRef ) +{ + m_aDrawPersistRef = rRef; +} + +static void lcl_InitMarks( SdrMarkView& rDest, const SdrMarkView& rSource, SCTAB nTab ) +{ + rDest.ShowSdrPage(rDest.GetModel()->GetPage(nTab)); + SdrPageView* pDestPV = rDest.GetSdrPageView(); + OSL_ENSURE(pDestPV,"PageView ?"); + + const SdrMarkList& rMarkList = rSource.GetMarkedObjectList(); + const size_t nCount = rMarkList.GetMarkCount(); + for (size_t i=0; i<nCount; ++i) + { + SdrMark* pMark = rMarkList.GetMark(i); + SdrObject* pObj = pMark->GetMarkedSdrObj(); + + rDest.MarkObj(pObj, pDestPV); + } +} + +void ScDrawTransferObj::SetDragSource( const ScDrawView* pView ) +{ + m_pDragSourceView.reset(new SdrView(pView->getSdrModelFromSdrView())); // TTTT pView should be reference + lcl_InitMarks( *m_pDragSourceView, *pView, pView->GetTab() ); + + //! add as listener with document, delete pDragSourceView if document gone +} + +void ScDrawTransferObj::SetDragSourceObj( SdrObject& rObj, SCTAB nTab ) +{ + m_pDragSourceView.reset(new SdrView(rObj.getSdrModelFromSdrObject())); + m_pDragSourceView->ShowSdrPage(m_pDragSourceView->GetModel()->GetPage(nTab)); + SdrPageView* pPV = m_pDragSourceView->GetSdrPageView(); + m_pDragSourceView->MarkObj(&rObj, pPV); // TTTT MarkObj should take SdrObject& + + //! add as listener with document, delete pDragSourceView if document gone +} + +void ScDrawTransferObj::SetDragSourceFlags(ScDragSrc nFlags) +{ + m_nDragSourceFlags = nFlags; +} + +void ScDrawTransferObj::SetDragWasInternal() +{ + m_bDragWasInternal = true; +} + +const OUString& ScDrawTransferObj::GetShellID() const +{ + return maShellID; +} + +SdrOle2Obj* ScDrawTransferObj::GetSingleObject() +{ + // if single OLE object was copied, get its object + + SdrPage* pPage = m_pModel->GetPage(0); + if (pPage) + { + SdrObjListIter aIter( pPage, SdrIterMode::Flat ); + SdrObject* pObject = aIter.Next(); + if (pObject && pObject->GetObjIdentifier() == SdrObjKind::OLE2) + { + return static_cast<SdrOle2Obj*>(pObject); + } + } + + return nullptr; +} + +void ScDrawTransferObj::CreateOLEData() +{ + if (m_aOleData.GetTransferable().is()) + // Already created. + return; + + SdrOle2Obj* pObj = GetSingleObject(); + if (!pObj || !pObj->GetObjRef().is()) + // No OLE object present. + return; + + rtl::Reference<SvEmbedTransferHelper> pEmbedTransfer = + new SvEmbedTransferHelper( + pObj->GetObjRef(), pObj->GetGraphic(), pObj->GetAspect()); + + pEmbedTransfer->SetParentShellID(maShellID); + + m_aOleData = TransferableDataHelper(pEmbedTransfer); +} + +// initialize aDocShellRef with a live document from the ClipDoc + +void ScDrawTransferObj::InitDocShell() +{ + if ( m_aDocShellRef.is() ) + return; + + ScDocShell* pDocSh = new ScDocShell; + m_aDocShellRef = pDocSh; // ref must be there before InitNew + + pDocSh->DoInitNew(); + + ScDocument& rDestDoc = pDocSh->GetDocument(); + rDestDoc.InitDrawLayer( pDocSh ); + + SdrModel* pDestModel = rDestDoc.GetDrawLayer(); + // #i71538# use complete SdrViews + // SdrExchangeView aDestView( pDestModel ); + SdrView aDestView(*pDestModel); + aDestView.ShowSdrPage(aDestView.GetModel()->GetPage(0)); + aDestView.Paste( + *m_pModel, + Point(m_aSrcSize.Width()/2, m_aSrcSize.Height()/2), + nullptr, SdrInsertFlags::NONE); + + // put objects to right layer (see ScViewFunc::PasteDataFormat for SotClipboardFormatId::DRAWING) + + SdrPage* pPage = pDestModel->GetPage(0); + if (pPage) + { + SdrObjListIter aIter( pPage, SdrIterMode::DeepWithGroups ); + SdrObject* pObject = aIter.Next(); + while (pObject) + { + if ( dynamic_cast<const SdrUnoObj*>( pObject) != nullptr ) + pObject->NbcSetLayer(SC_LAYER_CONTROLS); + else + pObject->NbcSetLayer(SC_LAYER_FRONT); + pObject = aIter.Next(); + } + } + + tools::Rectangle aDestArea( Point(), m_aSrcSize ); + pDocSh->SetVisArea( aDestArea ); + + ScViewOptions aViewOpt( rDestDoc.GetViewOptions() ); + aViewOpt.SetOption( VOPT_GRID, false ); + rDestDoc.SetViewOptions( aViewOpt ); + + ScViewData aViewData( *pDocSh, nullptr ); + aViewData.SetTabNo( 0 ); + aViewData.SetScreen( aDestArea ); + aViewData.SetCurX( 0 ); + aViewData.SetCurY( 0 ); + pDocSh->UpdateOle(aViewData, true); +} + +const css::uno::Sequence< sal_Int8 >& ScDrawTransferObj::getUnoTunnelId() +{ + static const comphelper::UnoIdInit theScDrawTransferObjUnoTunnelId; + return theScDrawTransferObjUnoTunnelId.getSeq(); +} + +sal_Int64 SAL_CALL ScDrawTransferObj::getSomething( const css::uno::Sequence< sal_Int8 >& rId ) +{ + return comphelper::getSomethingImpl( + rId, this, comphelper::FallbackToGetSomethingOf<TransferDataContainer>{}); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/app/inputhdl.cxx b/sc/source/ui/app/inputhdl.cxx new file mode 100644 index 000000000..884e2e696 --- /dev/null +++ b/sc/source/ui/app/inputhdl.cxx @@ -0,0 +1,4635 @@ +/* -*- 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 <iterator> +#include <memory> +#include <string_view> + +#include <inputhdl.hxx> +#include <scitems.hxx> +#include <editeng/eeitem.hxx> + +#include <sfx2/app.hxx> +#include <editeng/acorrcfg.hxx> +#include <formula/errorcodes.hxx> +#include <editeng/adjustitem.hxx> +#include <editeng/brushitem.hxx> +#include <svtools/colorcfg.hxx> +#include <editeng/colritem.hxx> +#include <editeng/editobj.hxx> +#include <editeng/editstat.hxx> +#include <editeng/editview.hxx> +#include <editeng/langitem.hxx> +#include <editeng/svxacorr.hxx> +#include <editeng/unolingu.hxx> +#include <editeng/wghtitem.hxx> +#include <editeng/justifyitem.hxx> +#include <editeng/misspellrange.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/printer.hxx> +#include <svl/numformat.hxx> +#include <svl/zforlist.hxx> +#include <unotools/localedatawrapper.hxx> +#include <unotools/charclass.hxx> +#include <vcl/help.hxx> +#include <vcl/jsdialog/executor.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/cursor.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <tools/urlobj.hxx> +#include <formula/formulahelper.hxx> +#include <formula/funcvarargs.h> +#include <LibreOfficeKit/LibreOfficeKitEnums.h> +#include <comphelper/lok.hxx> +#include <osl/diagnose.h> + +#include <attrib.hxx> +#include <inputwin.hxx> +#include <tabvwsh.hxx> +#include <docsh.hxx> +#include <scmod.hxx> +#include <formulaopt.hxx> +#include <uiitems.hxx> +#include <global.hxx> +#include <sc.hrc> +#include <globstr.hrc> +#include <scresid.hxx> +#include <patattr.hxx> +#include <viewdata.hxx> +#include <document.hxx> +#include <docpool.hxx> +#include <editutil.hxx> +#include <appoptio.hxx> +#include <docoptio.hxx> +#include <validat.hxx> +#include <rfindlst.hxx> +#include <inputopt.hxx> +#include <simpleformulacalc.hxx> +#include <compiler.hxx> +#include <editable.hxx> +#include <funcdesc.hxx> +#include <markdata.hxx> +#include <tokenarray.hxx> +#include <gridwin.hxx> +#include <output.hxx> +#include <fillinfo.hxx> + +// Maximum Ranges in RangeFinder +#define RANGEFIND_MAX 128 + +using namespace formula; + +namespace { + +ScTypedCaseStrSet::const_iterator findText( + const ScTypedCaseStrSet& rDataSet, ScTypedCaseStrSet::const_iterator const & itPos, + const OUString& rStart, OUString& rResult, bool bBack) +{ + auto lIsMatch = [&rStart](const ScTypedStrData& rData) { + return (rData.GetStringType() != ScTypedStrData::Value) && ScGlobal::GetTransliteration().isMatch(rStart, rData.GetString()); }; + + if (bBack) // Backwards + { + ScTypedCaseStrSet::const_reverse_iterator it = rDataSet.rbegin(), itEnd = rDataSet.rend(); + if (itPos != rDataSet.end()) + { + size_t nPos = std::distance(rDataSet.begin(), itPos); + size_t nRPos = rDataSet.size() - 1 - nPos; + std::advance(it, nRPos); + ++it; + } + + it = std::find_if(it, itEnd, lIsMatch); + if (it != itEnd) + { + rResult = it->GetString(); + return (++it).base(); // convert the reverse iterator back to iterator. + } + } + else // Forwards + { + ScTypedCaseStrSet::const_iterator it = rDataSet.begin(), itEnd = rDataSet.end(); + if (itPos != itEnd) + { + it = std::next(itPos); + } + + it = std::find_if(it, itEnd, lIsMatch); + if (it != itEnd) + { + rResult = it->GetString(); + return it; + } + } + + return rDataSet.end(); // no matching text found +} + +OUString getExactMatch(const ScTypedCaseStrSet& rDataSet, const OUString& rString) +{ + auto it = std::find_if(rDataSet.begin(), rDataSet.end(), + [&rString](const ScTypedStrData& rData) { + return (rData.GetStringType() != ScTypedStrData::Value) + && ScGlobal::GetTransliteration().isEqual(rData.GetString(), rString); + }); + if (it != rDataSet.end()) + return it->GetString(); + return rString; +} + +// This assumes that rResults is a sorted ring w.r.t ScTypedStrData::LessCaseInsensitive() or +// in the reverse direction, whose origin is specified by nRingOrigin. +sal_Int32 getLongestCommonPrefixLength(const std::vector<OUString>& rResults, const OUString& rUserEntry, sal_Int32 nRingOrigin) +{ + sal_Int32 nResults = rResults.size(); + if (!nResults) + return 0; + + if (nResults == 1) + return rResults[0].getLength(); + + sal_Int32 nMinLen = rUserEntry.getLength(); + sal_Int32 nLastIdx = nRingOrigin ? nRingOrigin - 1 : nResults - 1; + const OUString& rFirst = rResults[nRingOrigin]; + const OUString& rLast = rResults[nLastIdx]; + const sal_Int32 nMaxLen = std::min(rFirst.getLength(), rLast.getLength()); + + for (sal_Int32 nLen = nMaxLen; nLen > nMinLen; --nLen) + { + if (ScGlobal::GetTransliteration().isMatch(rFirst.copy(0, nLen), rLast)) + return nLen; + } + + return nMinLen; +} + +ScTypedCaseStrSet::const_iterator findTextAll( + const ScTypedCaseStrSet& rDataSet, ScTypedCaseStrSet::const_iterator const & itPos, + const OUString& rStart, ::std::vector< OUString > &rResultVec, bool bBack, sal_Int32* pLongestPrefixLen = nullptr) +{ + rResultVec.clear(); // clear contents + + if (!rDataSet.size()) + return rDataSet.end(); + + sal_Int32 nRingOrigin = 0; + size_t nCount = 0; + ScTypedCaseStrSet::const_iterator retit; + if ( bBack ) // Backwards + { + ScTypedCaseStrSet::const_reverse_iterator it, itEnd; + if ( itPos == rDataSet.end() ) + { + it = rDataSet.rend(); + --it; + itEnd = it; + } + else + { + it = rDataSet.rbegin(); + size_t nPos = std::distance(rDataSet.begin(), itPos); + size_t nRPos = rDataSet.size() - 1 - nPos; // if itPos == rDataSet.end(), then nRPos = -1 + std::advance(it, nRPos); + if ( it == rDataSet.rend() ) + it = rDataSet.rbegin(); + itEnd = it; + } + bool bFirstTime = true; + + while ( it != itEnd || bFirstTime ) + { + ++it; + if ( it == rDataSet.rend() ) // go to the first if reach the end + { + it = rDataSet.rbegin(); + nRingOrigin = nCount; + } + + if ( bFirstTime ) + bFirstTime = false; + const ScTypedStrData& rData = *it; + if ( rData.GetStringType() == ScTypedStrData::Value ) + // skip values + continue; + + if ( !ScGlobal::GetTransliteration().isMatch(rStart, rData.GetString()) ) + // not a match + continue; + + rResultVec.push_back(rData.GetString()); // set the match data + if ( nCount == 0 ) // convert the reverse iterator back to iterator. + { + // actually we want to do "retit = it;". + retit = rDataSet.begin(); + size_t nRPos = std::distance(rDataSet.rbegin(), it); + size_t nPos = rDataSet.size() - 1 - nRPos; + std::advance(retit, nPos); + } + ++nCount; + } + } + else // Forwards + { + ScTypedCaseStrSet::const_iterator it, itEnd; + it = itPos; + if ( it == rDataSet.end() ) + it = --rDataSet.end(); + itEnd = it; + bool bFirstTime = true; + + while ( it != itEnd || bFirstTime ) + { + ++it; + if ( it == rDataSet.end() ) // go to the first if reach the end + { + it = rDataSet.begin(); + nRingOrigin = nCount; + } + + if ( bFirstTime ) + bFirstTime = false; + const ScTypedStrData& rData = *it; + if ( rData.GetStringType() == ScTypedStrData::Value ) + // skip values + continue; + + if ( !ScGlobal::GetTransliteration().isMatch(rStart, rData.GetString()) ) + // not a match + continue; + + rResultVec.push_back(rData.GetString()); // set the match data + if ( nCount == 0 ) + retit = it; // remember first match iterator + ++nCount; + } + } + + if (pLongestPrefixLen) + { + if (nRingOrigin >= static_cast<sal_Int32>(nCount)) + { + // All matches were picked when rDataSet was read in one direction. + nRingOrigin = 0; + } + // rResultsVec is a sorted ring with nRingOrigin "origin". + // The direction of sorting is not important for getLongestCommonPrefixLength. + *pLongestPrefixLen = getLongestCommonPrefixLength(rResultVec, rStart, nRingOrigin); + } + + if ( nCount > 0 ) // at least one function has matched + return retit; + return rDataSet.end(); // no matching text found +} + +} + +void ScInputHandler::SendReferenceMarks( const SfxViewShell* pViewShell, + const std::vector<ReferenceMark>& rReferenceMarks ) +{ + if ( !pViewShell ) + return; + + bool bSend = false; + + std::stringstream ss; + + ss << "{ \"marks\": [ "; + + for ( size_t i = 0; i < rReferenceMarks.size(); i++ ) + { + if ( rReferenceMarks[i].Is() ) + { + if ( bSend ) + ss << ", "; + + ss << "{ \"rectangle\": \"" + << rReferenceMarks[i].nX << ", " + << rReferenceMarks[i].nY << ", " + << rReferenceMarks[i].nWidth << ", " + << rReferenceMarks[i].nHeight << "\", " + "\"color\": \"" << rReferenceMarks[i].aColor.AsRGBHexString() << "\", " + "\"part\": \"" << rReferenceMarks[i].nTab << "\" } "; + + bSend = true; + } + } + + ss << " ] }"; + + OString aPayload = ss.str().c_str(); + pViewShell->libreOfficeKitViewCallback( + LOK_CALLBACK_REFERENCE_MARKS, aPayload.getStr() ); +} + +void ScInputHandler::InitRangeFinder( const OUString& rFormula ) +{ + DeleteRangeFinder(); + if ( !pActiveViewSh || !SC_MOD()->GetInputOptions().GetRangeFinder() ) + return; + ScDocShell* pDocSh = pActiveViewSh->GetViewData().GetDocShell(); + ScDocument& rDoc = pDocSh->GetDocument(); + const sal_Unicode cSheetSep = rDoc.GetSheetSeparator(); + + OUString aDelimiters = ScEditUtil::ModifyDelimiters(" !~\""); + // delimiters (in addition to ScEditUtil): only characters that are + // allowed in formulas next to references and the quotation mark (so + // string constants can be skipped) + + sal_Int32 nColon = aDelimiters.indexOf( ':' ); + if ( nColon != -1 ) + aDelimiters = aDelimiters.replaceAt( nColon, 1, u""); // Delimiter without colon + sal_Int32 nDot = aDelimiters.indexOf(cSheetSep); + if ( nDot != -1 ) + aDelimiters = aDelimiters.replaceAt( nDot, 1 , u""); // Delimiter without dot + + const sal_Unicode* pChar = rFormula.getStr(); + sal_Int32 nLen = rFormula.getLength(); + sal_Int32 nPos = 0; + sal_Int32 nStart = 0; + sal_uInt16 nCount = 0; + ScRange aRange; + while ( nPos < nLen && nCount < RANGEFIND_MAX ) + { + // Skip separator + while ( nPos<nLen && ScGlobal::UnicodeStrChr( aDelimiters.getStr(), pChar[nPos] ) ) + { + if ( pChar[nPos] == '"' ) // String + { + ++nPos; + while (nPos<nLen && pChar[nPos] != '"') // Skip until end + ++nPos; + } + ++nPos; // Separator or closing quote + } + + // text between separators + nStart = nPos; +handle_r1c1: + { + bool bSingleQuoted = false; + while (nPos < nLen) + { + // tdf#114113: handle addresses with quoted sheet names like "'Sheet 1'.A1" + // Literal single quotes in sheet names are masked by another single quote + if (pChar[nPos] == '\'') + { + bSingleQuoted = !bSingleQuoted; + } + else if (!bSingleQuoted) // Get everything in single quotes, including separators + { + if (ScGlobal::UnicodeStrChr(aDelimiters.getStr(), pChar[nPos])) + break; + } + ++nPos; + } + } + + // for R1C1 '-' in R[-]... or C[-]... are not delimiters + // Nothing heroic here to ensure that there are '[]' around a negative + // integer. we need to clean up this code. + if( nPos < nLen && nPos > 0 && + '-' == pChar[nPos] && '[' == pChar[nPos-1] && + formula::FormulaGrammar::CONV_XL_R1C1 == rDoc.GetAddressConvention() ) + { + nPos++; + goto handle_r1c1; + } + + if ( nPos > nStart ) + { + OUString aTest = rFormula.copy( nStart, nPos-nStart ); + const ScAddress::Details aAddrDetails( rDoc, aCursorPos ); + ScRefFlags nFlags = aRange.ParseAny( aTest, rDoc, aAddrDetails ); + if ( nFlags & ScRefFlags::VALID ) + { + // Set tables if not specified + if ( (nFlags & ScRefFlags::TAB_3D) == ScRefFlags::ZERO) + aRange.aStart.SetTab( pActiveViewSh->GetViewData().GetTabNo() ); + if ( (nFlags & ScRefFlags::TAB2_3D) == ScRefFlags::ZERO) + aRange.aEnd.SetTab( aRange.aStart.Tab() ); + + if ( ( nFlags & (ScRefFlags::COL2_VALID|ScRefFlags::ROW2_VALID|ScRefFlags::TAB2_VALID) ) == + ScRefFlags::ZERO ) + { + // #i73766# if a single ref was parsed, set the same "abs" flags for ref2, + // so Format doesn't output a double ref because of different flags. + ScRefFlags nAbsFlags = nFlags & (ScRefFlags::COL_ABS|ScRefFlags::ROW_ABS|ScRefFlags::TAB_ABS); + applyStartToEndFlags(nFlags, nAbsFlags); + } + + if (!nCount) + { + mpEditEngine->SetUpdateLayout( false ); + pRangeFindList.reset(new ScRangeFindList( pDocSh->GetTitle() )); + } + + Color nColor = pRangeFindList->Insert( ScRangeFindData( aRange, nFlags, nStart, nPos ) ); + + ESelection aSel( 0, nStart, 0, nPos ); + SfxItemSet aSet( mpEditEngine->GetEmptyItemSet() ); + aSet.Put( SvxColorItem( nColor, EE_CHAR_COLOR ) ); + mpEditEngine->QuickSetAttribs( aSet, aSel ); + ++nCount; + } + } + + // Do not skip last separator; could be a quote (?) + } + + UpdateLokReferenceMarks(); + + if (nCount) + { + mpEditEngine->SetUpdateLayout( true ); + + pDocSh->Broadcast( SfxHint( SfxHintId::ScShowRangeFinder ) ); + } +} + +ReferenceMark ScInputHandler::GetReferenceMark( const ScViewData& rViewData, ScDocShell* pDocSh, + tools::Long nX1, tools::Long nX2, tools::Long nY1, tools::Long nY2, + tools::Long nTab, const Color& rColor ) +{ + ScSplitPos eWhich = rViewData.GetActivePart(); + + // This method is LOK specific. + if (comphelper::LibreOfficeKit::isCompatFlagSet( + comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs)) + { + SCCOL nCol1 = nX1, nCol2 = nX2; + SCROW nRow1 = nY1, nRow2 = nY2; + PutInOrder(nCol1, nCol2); + PutInOrder(nRow1, nRow2); + if (nCol1 == nCol2 && nRow1 == nRow2) + pDocSh->GetDocument().ExtendMerge(nCol1, nRow1, nCol2, nRow2, nTab); + + Point aTopLeft = rViewData.GetPrintTwipsPos(nCol1, nRow1); + Point aBottomRight = rViewData.GetPrintTwipsPos(nCol2 + 1, nRow2 + 1); + tools::Long nSizeX = aBottomRight.X() - aTopLeft.X() - 1; + tools::Long nSizeY = aBottomRight.Y() - aTopLeft.Y() - 1; + + return ReferenceMark(aTopLeft.X(), aTopLeft.Y(), nSizeX, nSizeY, nTab, rColor); + } + + Point aScrPos = rViewData.GetScrPos( nX1, nY1, eWhich ); + tools::Long nScrX = aScrPos.X(); + tools::Long nScrY = aScrPos.Y(); + + double nPPTX = rViewData.GetPPTX(); + double nPPTY = rViewData.GetPPTY(); + + Fraction aZoomX = rViewData.GetZoomX(); + Fraction aZoomY = rViewData.GetZoomY(); + + ScTableInfo aTabInfo; + pDocSh->GetDocument().FillInfo( aTabInfo, nX1, nY1, nX2, nY2, + nTab, nPPTX, nPPTY, false, false ); + + ScOutputData aOutputData( nullptr, OUTTYPE_WINDOW, aTabInfo, + &( pDocSh->GetDocument() ), nTab, + nScrX, nScrY, + nX1, nY1, nX2, nY2, + nPPTX, nPPTY, + &aZoomX, &aZoomY ); + + return aOutputData.FillReferenceMark( nX1, nY1, nX2, nY2, + rColor ); +} + +void ScInputHandler::UpdateLokReferenceMarks() +{ + if ( !comphelper::LibreOfficeKit::isActive()) + return; + + ScTabViewShell* pShell = pActiveViewSh ? pActiveViewSh + : dynamic_cast<ScTabViewShell*>(SfxViewShell::Current()); + + if (!pShell) + return; + + ScViewData& rViewData = pShell->GetViewData(); + ScDocShell* pDocSh = rViewData.GetDocShell(); + ScRangeFindList* pRangeFinder = GetRangeFindList(); + + if ( !pRangeFinder && !rViewData.IsRefMode() ) + return; + + sal_uInt16 nAdditionalMarks = 0; + std::vector<ReferenceMark> aReferenceMarks( 1 ); + + if ( rViewData.IsRefMode() ) + { + nAdditionalMarks = 1; + + const svtools::ColorConfig& rColorCfg = SC_MOD()->GetColorConfig(); + Color aRefColor( rColorCfg.GetColorValue( svtools::CALCREFERENCE ).nColor ); + tools::Long nX1 = rViewData.GetRefStartX(); + tools::Long nX2 = rViewData.GetRefEndX(); + tools::Long nY1 = rViewData.GetRefStartY(); + tools::Long nY2 = rViewData.GetRefEndY(); + tools::Long nTab = rViewData.GetRefStartZ(); + + if (rViewData.GetRefEndZ() == rViewData.GetTabNo()) + nTab = rViewData.GetRefEndZ(); + + PutInOrder(nX1, nX2); + PutInOrder(nY1, nY2); + + aReferenceMarks[0] = ScInputHandler::GetReferenceMark( rViewData, pDocSh, + nX1, nX2, nY1, nY2, + nTab, aRefColor ); + } + + sal_uInt16 nCount = pRangeFinder ? + ( static_cast<sal_uInt16>( pRangeFinder->Count() ) + nAdditionalMarks ) : nAdditionalMarks; + aReferenceMarks.resize( nCount ); + + if ( nCount && pRangeFinder && !pRangeFinder->IsHidden() && + pRangeFinder->GetDocName() == pDocSh->GetTitle() ) + { + for (sal_uInt16 i = 0; i < nCount - nAdditionalMarks; i++) + { + ScRangeFindData& rData = pRangeFinder->GetObject( i ); + ScRange aRef = rData.aRef; + aRef.PutInOrder(); + + tools::Long nX1 = aRef.aStart.Col(); + tools::Long nX2 = aRef.aEnd.Col(); + tools::Long nY1 = aRef.aStart.Row(); + tools::Long nY2 = aRef.aEnd.Row(); + tools::Long nTab = aRef.aStart.Tab(); + + aReferenceMarks[i + nAdditionalMarks] = ScInputHandler::GetReferenceMark( rViewData, pDocSh, + nX1, nX2, nY1, nY2, + nTab, rData.nColor ); + + ScInputHandler::SendReferenceMarks( pShell, aReferenceMarks ); + } + } + else if ( nCount ) + { + ScInputHandler::SendReferenceMarks( pShell, aReferenceMarks ); + } + else + { + // Clear + aReferenceMarks.clear(); + ScInputHandler::SendReferenceMarks( pShell, aReferenceMarks ); + } +} + +void ScInputHandler::SetDocumentDisposing( bool b ) +{ + mbDocumentDisposing = b; +} + +static void lcl_Replace( EditView* pView, const OUString& rNewStr, const ESelection& rOldSel ) +{ + if ( !pView ) + return; + + ESelection aOldSel = pView->GetSelection(); + if (aOldSel.HasRange()) + pView->SetSelection( ESelection( aOldSel.nEndPara, aOldSel.nEndPos, + aOldSel.nEndPara, aOldSel.nEndPos ) ); + + EditEngine* pEngine = pView->GetEditEngine(); + pEngine->QuickInsertText( rNewStr, rOldSel ); + + // Dummy InsertText for Update and Paint + // To do that we need to cancel the selection from above (before QuickInsertText) + pView->InsertText( OUString() ); + + sal_Int32 nLen = pEngine->GetTextLen(0); + ESelection aSel( 0, nLen, 0, nLen ); + pView->SetSelection( aSel ); // Set cursor to the end +} + +void ScInputHandler::UpdateRange( sal_uInt16 nIndex, const ScRange& rNew ) +{ + ScTabViewShell* pDocView = pRefViewSh ? pRefViewSh : pActiveViewSh; + if ( pDocView && pRangeFindList && nIndex < pRangeFindList->Count() ) + { + ScRangeFindData& rData = pRangeFindList->GetObject( nIndex ); + sal_Int32 nOldStart = rData.nSelStart; + sal_Int32 nOldEnd = rData.nSelEnd; + Color nNewColor = pRangeFindList->FindColor( rNew, nIndex ); + + ScRange aJustified = rNew; + aJustified.PutInOrder(); // Always display Ref in the Formula the right way + ScDocument& rDoc = pDocView->GetViewData().GetDocument(); + const ScAddress::Details aAddrDetails( rDoc, aCursorPos ); + OUString aNewStr(aJustified.Format(rDoc, rData.nFlags, aAddrDetails)); + ESelection aOldSel( 0, nOldStart, 0, nOldEnd ); + SfxItemSet aSet( mpEditEngine->GetEmptyItemSet() ); + + DataChanging(); + + lcl_Replace( pTopView, aNewStr, aOldSel ); + lcl_Replace( pTableView, aNewStr, aOldSel ); + aSet.Put( SvxColorItem( nNewColor, EE_CHAR_COLOR ) ); + mpEditEngine->QuickSetAttribs( aSet, aOldSel ); + + bInRangeUpdate = true; + DataChanged(); + bInRangeUpdate = false; + + tools::Long nDiff = aNewStr.getLength() - static_cast<tools::Long>(nOldEnd-nOldStart); + + rData.aRef = rNew; + rData.nSelEnd = rData.nSelEnd + nDiff; + rData.nColor = nNewColor; + + sal_uInt16 nCount = static_cast<sal_uInt16>(pRangeFindList->Count()); + for (sal_uInt16 i=nIndex+1; i<nCount; i++) + { + ScRangeFindData& rNext = pRangeFindList->GetObject( i ); + rNext.nSelStart = rNext.nSelStart + nDiff; + rNext.nSelEnd = rNext.nSelEnd + nDiff; + } + + EditView* pActiveView = pTopView ? pTopView : pTableView; + pActiveView->ShowCursor( false ); + } + else + { + OSL_FAIL("UpdateRange: we're missing something"); + } +} + +void ScInputHandler::DeleteRangeFinder() +{ + ScTabViewShell* pPaintView = pRefViewSh ? pRefViewSh : pActiveViewSh; + if ( pRangeFindList && pPaintView ) + { + ScDocShell* pDocSh = pActiveViewSh->GetViewData().GetDocShell(); + pRangeFindList->SetHidden(true); + pDocSh->Broadcast( SfxHint( SfxHintId::ScShowRangeFinder ) ); // Steal + pRangeFindList.reset(); + } +} + +static OUString GetEditText(const EditEngine* pEng) +{ + return ScEditUtil::GetMultilineString(*pEng); +} + +static void lcl_RemoveTabs(OUString& rStr) +{ + rStr = rStr.replace('\t', ' '); +} + +static void lcl_RemoveLineEnd(OUString& rStr) +{ + rStr = convertLineEnd(rStr, LINEEND_LF); + rStr = rStr.replace('\n', ' '); +} + +static sal_Int32 lcl_MatchParenthesis( const OUString& rStr, sal_Int32 nPos ) +{ + int nDir; + sal_Unicode c1, c2 = 0; + c1 = rStr[nPos]; + switch ( c1 ) + { + case '(' : + c2 = ')'; + nDir = 1; + break; + case ')' : + c2 = '('; + nDir = -1; + break; + case '<' : + c2 = '>'; + nDir = 1; + break; + case '>' : + c2 = '<'; + nDir = -1; + break; + case '{' : + c2 = '}'; + nDir = 1; + break; + case '}' : + c2 = '{'; + nDir = -1; + break; + case '[' : + c2 = ']'; + nDir = 1; + break; + case ']' : + c2 = '['; + nDir = -1; + break; + default: + nDir = 0; + } + if ( !nDir ) + return -1; + sal_Int32 nLen = rStr.getLength(); + const sal_Unicode* p0 = rStr.getStr(); + const sal_Unicode* p; + const sal_Unicode* p1; + sal_uInt16 nQuotes = 0; + if ( nPos < nLen / 2 ) + { + p = p0; + p1 = p0 + nPos; + } + else + { + p = p0 + nPos; + p1 = p0 + nLen; + } + while ( p < p1 ) + { + if ( *p++ == '\"' ) + nQuotes++; + } + // Odd number of quotes that we find ourselves in a string + bool bLookInString = ((nQuotes % 2) != 0); + bool bInString = bLookInString; + p = p0 + nPos; + p1 = (nDir < 0 ? p0 : p0 + nLen) ; + sal_uInt16 nLevel = 1; + while ( p != p1 && nLevel ) + { + p += nDir; + if ( *p == '\"' ) + { + bInString = !bInString; + if ( bLookInString && !bInString ) + p = p1; // That's it then + } + else if ( bInString == bLookInString ) + { + if ( *p == c1 ) + nLevel++; + else if ( *p == c2 ) + nLevel--; + } + } + if ( nLevel ) + return -1; + return static_cast<sal_Int32>(p - p0); +} + +ScInputHandler::ScInputHandler() + : pInputWin( nullptr ), + pTableView( nullptr ), + pTopView( nullptr ), + pTipVisibleParent( nullptr ), + nTipVisible( nullptr ), + pTipVisibleSecParent( nullptr ), + nTipVisibleSec( nullptr ), + nFormSelStart( 0 ), + nFormSelEnd( 0 ), + nCellPercentFormatDecSep( 0 ), + nAutoPar( 0 ), + eMode( SC_INPUT_NONE ), + bUseTab( false ), + bTextValid( true ), + bModified( false ), + bSelIsRef( false ), + bFormulaMode( false ), + bInRangeUpdate( false ), + bParenthesisShown( false ), + bCreatingFuncView( false ), + bInEnterHandler( false ), + bCommandErrorShown( false ), + bInOwnChange( false ), + bProtected( false ), + bLastIsSymbol( false ), + mbDocumentDisposing(false), + mbPartialPrefix(false), + mbEditingExistingContent(false), + nValidation( 0 ), + eAttrAdjust( SvxCellHorJustify::Standard ), + aScaleX( 1,1 ), + aScaleY( 1,1 ), + pRefViewSh( nullptr ), + pLastPattern( nullptr ) +{ + // The InputHandler is constructed with the view, so SfxViewShell::Current + // doesn't have the right view yet. pActiveViewSh is updated in NotifyChange. + pActiveViewSh = nullptr; + + // Bindings (only still used for Invalidate) are retrieved if needed on demand + + pDelayTimer.reset( new Timer( "ScInputHandlerDelay timer" ) ); + pDelayTimer->SetTimeout( 500 ); // 500 ms delay + pDelayTimer->SetInvokeHandler( LINK( this, ScInputHandler, DelayTimer ) ); +} + +ScInputHandler::~ScInputHandler() +{ + // If this is the application InputHandler, the dtor is called after SfxApplication::Main, + // thus we can't rely on any Sfx functions + if (!mbDocumentDisposing) // inplace + EnterHandler(); // Finish input + + if (SC_MOD()->GetRefInputHdl() == this) + SC_MOD()->SetRefInputHdl(nullptr); + + if ( pInputWin && pInputWin->GetInputHandler() == this ) + pInputWin->SetInputHandler( nullptr ); +} + +void ScInputHandler::SetRefScale( const Fraction& rX, const Fraction& rY ) +{ + if ( rX != aScaleX || rY != aScaleY ) + { + aScaleX = rX; + aScaleY = rY; + if (mpEditEngine) + { + MapMode aMode( MapUnit::Map100thMM, Point(), aScaleX, aScaleY ); + mpEditEngine->SetRefMapMode( aMode ); + } + } +} + +void ScInputHandler::UpdateRefDevice() +{ + if (!mpEditEngine) + return; + + bool bTextWysiwyg = SC_MOD()->GetInputOptions().GetTextWysiwyg(); + bool bInPlace = pActiveViewSh && pActiveViewSh->GetViewFrame()->GetFrame().IsInPlace(); + EEControlBits nCtrl = mpEditEngine->GetControlWord(); + if ( bTextWysiwyg || bInPlace ) + nCtrl |= EEControlBits::FORMAT100; // EditEngine default: always format for 100% + else + nCtrl &= ~EEControlBits::FORMAT100; // when formatting for screen, use the actual MapMode + mpEditEngine->SetControlWord( nCtrl ); + if ( bTextWysiwyg && pActiveViewSh ) + mpEditEngine->SetRefDevice( pActiveViewSh->GetViewData().GetDocument().GetPrinter() ); + else + mpEditEngine->SetRefDevice( nullptr ); + + MapMode aMode( MapUnit::Map100thMM, Point(), aScaleX, aScaleY ); + mpEditEngine->SetRefMapMode( aMode ); + + // SetRefDevice(NULL) uses VirtualDevice, SetRefMapMode forces creation of a local VDev, + // so the DigitLanguage can be safely modified (might use an own VDev instead of NULL). + if ( !( bTextWysiwyg && pActiveViewSh ) ) + { + mpEditEngine->GetRefDevice()->SetDigitLanguage( SC_MOD()->GetOptDigitLanguage() ); + } +} + +void ScInputHandler::ImplCreateEditEngine() +{ + if ( mpEditEngine ) + return; + + if ( pActiveViewSh ) + { + ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument(); + mpEditEngine = std::make_unique<ScFieldEditEngine>(&rDoc, rDoc.GetEnginePool(), rDoc.GetEditPool()); + } + else + mpEditEngine = std::make_unique<ScFieldEditEngine>(nullptr, EditEngine::CreatePool().get(), nullptr, true); + + mpEditEngine->SetWordDelimiters( ScEditUtil::ModifyDelimiters( mpEditEngine->GetWordDelimiters() ) ); + UpdateRefDevice(); // also sets MapMode + mpEditEngine->SetPaperSize( Size( 1000000, 1000000 ) ); + pEditDefaults.reset( new SfxItemSet( mpEditEngine->GetEmptyItemSet() ) ); + + mpEditEngine->SetControlWord( mpEditEngine->GetControlWord() | EEControlBits::AUTOCORRECT ); + mpEditEngine->SetReplaceLeadingSingleQuotationMark( false ); + mpEditEngine->SetModifyHdl( LINK( this, ScInputHandler, ModifyHdl ) ); +} + +void ScInputHandler::UpdateAutoCorrFlag() +{ + EEControlBits nCntrl = mpEditEngine->GetControlWord(); + EEControlBits nOld = nCntrl; + + // Don't use pLastPattern here (may be invalid because of AutoStyle) + bool bDisable = bLastIsSymbol || bFormulaMode; + if ( bDisable ) + nCntrl &= ~EEControlBits::AUTOCORRECT; + else + nCntrl |= EEControlBits::AUTOCORRECT; + + if ( nCntrl != nOld ) + mpEditEngine->SetControlWord(nCntrl); +} + +void ScInputHandler::UpdateSpellSettings( bool bFromStartTab ) +{ + if ( !pActiveViewSh ) + return; + + ScViewData& rViewData = pActiveViewSh->GetViewData(); + bool bOnlineSpell = rViewData.GetDocument().GetDocOptions().IsAutoSpell(); + + // SetDefaultLanguage is independent of the language attributes, + // ScGlobal::GetEditDefaultLanguage is always used. + // It must be set every time in case the office language was changed. + + mpEditEngine->SetDefaultLanguage( ScGlobal::GetEditDefaultLanguage() ); + + // if called for changed options, update flags only if already editing + // if called from StartTable, always update flags + + if ( bFromStartTab || eMode != SC_INPUT_NONE ) + { + EEControlBits nCntrl = mpEditEngine->GetControlWord(); + EEControlBits nOld = nCntrl; + if( bOnlineSpell ) + nCntrl |= EEControlBits::ONLINESPELLING; + else + nCntrl &= ~EEControlBits::ONLINESPELLING; + // No AutoCorrect for Symbol Font (EditEngine does no evaluate Default) + if ( pLastPattern && pLastPattern->IsSymbolFont() ) + nCntrl &= ~EEControlBits::AUTOCORRECT; + else + nCntrl |= EEControlBits::AUTOCORRECT; + if ( nCntrl != nOld ) + mpEditEngine->SetControlWord(nCntrl); + + ScDocument& rDoc = rViewData.GetDocument(); + rDoc.ApplyAsianEditSettings( *mpEditEngine ); + mpEditEngine->SetDefaultHorizontalTextDirection( + rDoc.GetEditTextDirection( rViewData.GetTabNo() ) ); + mpEditEngine->SetFirstWordCapitalization( false ); + } + + // Language is set separately, so the speller is needed only if online spelling is active + if ( bOnlineSpell ) { + css::uno::Reference<css::linguistic2::XSpellChecker1> xXSpellChecker1( LinguMgr::GetSpellChecker() ); + mpEditEngine->SetSpeller( xXSpellChecker1 ); + } + + bool bHyphen = pLastPattern && pLastPattern->GetItem(ATTR_HYPHENATE).GetValue(); + if ( bHyphen ) { + css::uno::Reference<css::linguistic2::XHyphenator> xXHyphenator( LinguMgr::GetHyphenator() ); + mpEditEngine->SetHyphenator( xXHyphenator ); + } +} + +// Function/Range names etc. as Tip help + +// The other types are defined in ScDocument::GetFormulaEntries +void ScInputHandler::GetFormulaData() +{ + if ( !pActiveViewSh ) + return; + + ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument(); + + if ( pFormulaData ) + pFormulaData->clear(); + else + { + pFormulaData.reset( new ScTypedCaseStrSet ); + } + + if( pFormulaDataPara ) + pFormulaDataPara->clear(); + else + pFormulaDataPara.reset( new ScTypedCaseStrSet ); + + const OUString aParenthesesReplacement( cParenthesesReplacement); + const ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList(); + const sal_uInt32 nListCount = pFuncList->GetCount(); + const InputHandlerFunctionNames& rFunctionNames = ScGlobal::GetInputHandlerFunctionNames(); + *pFormulaData = rFunctionNames.maFunctionData; + *pFormulaDataPara = rFunctionNames.maFunctionDataPara; + maFormulaChar = rFunctionNames.maFunctionChar; + + // Increase suggestion priority of MRU formulas + const ScAppOptions& rOpt = SC_MOD()->GetAppOptions(); + const sal_uInt16 nMRUCount = rOpt.GetLRUFuncListCount(); + const sal_uInt16* pMRUList = rOpt.GetLRUFuncList(); + for (sal_uInt16 i = 0; i < nMRUCount; i++) + { + const sal_uInt16 nId = pMRUList[i]; + for (sal_uInt32 j = 0; j < nListCount; j++) + { + const ScFuncDesc* pDesc = pFuncList->GetFunction(j); + if (pDesc->nFIndex == nId && pDesc->mxFuncName) + { + const OUString aEntry = *pDesc->mxFuncName + aParenthesesReplacement;; + const ScTypedStrData aData(aEntry, 0.0, 0.0, ScTypedStrData::Standard); + auto it = pFormulaData->find(aData); + if (it != pFormulaData->end()) + pFormulaData->erase(it); + pFormulaData->insert(ScTypedStrData(aEntry, 0.0, 0.0, ScTypedStrData::MRU)); + break; // Stop searching + } + } + } + miAutoPosFormula = pFormulaData->end(); + + // tdf#142031 - collect all the characters for the formula suggestion auto input + ScTypedCaseStrSet aStrSet; + rDoc.GetFormulaEntries( aStrSet ); + for (auto iter = aStrSet.begin(); iter != aStrSet.end(); ++iter) + { + const OUString aFuncName = ScGlobal::getCharClass().uppercase((*iter).GetString()); + // fdo#75264 fill maFormulaChar with all characters used in formula names + for (sal_Int32 j = 0; j < aFuncName.getLength(); j++) + maFormulaChar.insert(aFuncName[j]); + } + pFormulaData->insert(aStrSet.begin(), aStrSet.end()); + pFormulaDataPara->insert(aStrSet.begin(), aStrSet.end()); +} + +IMPL_LINK( ScInputHandler, ShowHideTipVisibleParentListener, VclWindowEvent&, rEvent, void ) +{ + if (rEvent.GetId() == VclEventId::ObjectDying || rEvent.GetId() == VclEventId::WindowHide + || rEvent.GetId() == VclEventId::WindowLoseFocus || rEvent.GetId() == VclEventId::ControlLoseFocus) + HideTip(); +} + +IMPL_LINK( ScInputHandler, ShowHideTipVisibleSecParentListener, VclWindowEvent&, rEvent, void ) +{ + if (rEvent.GetId() == VclEventId::ObjectDying || rEvent.GetId() == VclEventId::WindowHide + || rEvent.GetId() == VclEventId::WindowLoseFocus || rEvent.GetId() == VclEventId::ControlLoseFocus) + HideTipBelow(); +} + +void ScInputHandler::HideTip() +{ + if ( nTipVisible ) + { + pTipVisibleParent->RemoveEventListener( LINK( this, ScInputHandler, ShowHideTipVisibleParentListener ) ); + Help::HidePopover(pTipVisibleParent, nTipVisible ); + nTipVisible = nullptr; + pTipVisibleParent = nullptr; + } + aManualTip.clear(); +} +void ScInputHandler::HideTipBelow() +{ + if ( nTipVisibleSec ) + { + pTipVisibleSecParent->RemoveEventListener( LINK( this, ScInputHandler, ShowHideTipVisibleSecParentListener ) ); + Help::HidePopover(pTipVisibleSecParent, nTipVisibleSec); + nTipVisibleSec = nullptr; + pTipVisibleSecParent = nullptr; + } + aManualTip.clear(); +} + +namespace +{ + +bool lcl_hasSingleToken(std::u16string_view s, sal_Unicode c) +{ + return !s.empty() && s.find(c) == std::u16string_view::npos; +} + +} + +void ScInputHandler::ShowArgumentsTip( OUString& rSelText ) +{ + if (comphelper::LibreOfficeKit::isActive()) + { + return; + } + + ScDocShell* pDocSh = pActiveViewSh->GetViewData().GetDocShell(); + const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep); + const sal_Unicode cSheetSep = pDocSh->GetDocument().GetSheetSeparator(); + FormulaHelper aHelper(ScGlobal::GetStarCalcFunctionMgr()); + bool bFound = false; + while( !bFound ) + { + rSelText += ")"; + sal_Int32 nLeftParentPos = lcl_MatchParenthesis( rSelText, rSelText.getLength()-1 ); + if( nLeftParentPos != -1 ) + { + sal_Int32 nNextFStart = aHelper.GetFunctionStart( rSelText, nLeftParentPos, true); + const IFunctionDescription* ppFDesc; + ::std::vector< OUString> aArgs; + if( aHelper.GetNextFunc( rSelText, false, nNextFStart, nullptr, &ppFDesc, &aArgs ) ) + { + if( !ppFDesc->getFunctionName().isEmpty() ) + { + sal_Int32 nArgPos = aHelper.GetArgStart( rSelText, nNextFStart, 0 ); + sal_uInt16 nArgs = static_cast<sal_uInt16>(ppFDesc->getParameterCount()); + OUString aFuncName( ppFDesc->getFunctionName() + "("); + OUString aNew; + ScTypedCaseStrSet::const_iterator it = + findText(*pFormulaDataPara, pFormulaDataPara->end(), aFuncName, aNew, false); + if (it != pFormulaDataPara->end()) + { + bool bFlag = false; + sal_uInt16 nActive = 0; + for( sal_uInt16 i=0; i < nArgs; i++ ) + { + sal_Int32 nLength = aArgs[i].getLength(); + if( nArgPos <= rSelText.getLength()-1 ) + { + nActive = i+1; + bFlag = true; + } + nArgPos+=nLength+1; + } + if( bFlag ) + { + sal_Int32 nStartPosition = 0; + sal_Int32 nEndPosition = 0; + + if( lcl_hasSingleToken(aNew, cSep) ) + { + for (sal_Int32 i = 0; i < aNew.getLength(); ++i) + { + sal_Unicode cNext = aNew[i]; + if( cNext == '(' ) + { + nStartPosition = i+1; + } + } + } + else if( lcl_hasSingleToken(aNew, cSheetSep) ) + { + sal_uInt16 nCount = 0; + for (sal_Int32 i = 0; i < aNew.getLength(); ++i) + { + sal_Unicode cNext = aNew[i]; + if( cNext == '(' ) + { + nStartPosition = i+1; + } + else if( cNext == cSep ) + { + nCount ++; + nEndPosition = i; + if( nCount == nActive ) + { + break; + } + nStartPosition = nEndPosition+1; + } + } + } + else + { + sal_uInt16 nCount = 0; + for (sal_Int32 i = 0; i < aNew.getLength(); ++i) + { + sal_Unicode cNext = aNew[i]; + if( cNext == '(' ) + { + nStartPosition = i+1; + } + else if( cNext == cSep ) + { + nCount ++; + nEndPosition = i; + if( nCount == nActive ) + { + break; + } + nStartPosition = nEndPosition+1; + } + else if( cNext == cSheetSep ) + { + continue; + } + } + } + + if (nStartPosition > 0) + { + nArgs = ppFDesc->getParameterCount(); + sal_Int16 nVarArgsSet = 0; + if ( nArgs >= PAIRED_VAR_ARGS ) + { + nVarArgsSet = 2; + nArgs -= PAIRED_VAR_ARGS - nVarArgsSet; + } + else if ( nArgs >= VAR_ARGS ) + { + nVarArgsSet = 1; + nArgs -= VAR_ARGS - nVarArgsSet; + } + if ( nVarArgsSet > 0 && nActive > nArgs ) + nActive = nArgs - (nActive - nArgs) % nVarArgsSet; + aNew = OUString::Concat(aNew.subView(0, nStartPosition)) + + u"\x25BA" + + aNew.subView(nStartPosition) + + " : " + + ppFDesc->getParameterDescription(nActive-1); + if (eMode != SC_INPUT_TOP) + { + ShowTipBelow( aNew ); + } + else + { + ShowTip(aNew); + } + bFound = true; + } + } + else + { + ShowTipBelow( aNew ); + bFound = true; + } + } + } + } + } + else + { + break; + } + } +} + +void ScInputHandler::ShowTipCursor() +{ + HideTip(); + HideTipBelow(); + EditView* pActiveView = pTopView ? pTopView : pTableView; + + /* TODO: MLFORMULA: this should work also with multi-line formulas. */ + if ( !(bFormulaMode && pActiveView && pFormulaDataPara && mpEditEngine->GetParagraphCount() == 1) ) + return; + + OUString aParagraph = mpEditEngine->GetText( 0 ); + ESelection aSel = pActiveView->GetSelection(); + aSel.Adjust(); + + if ( aParagraph.getLength() < aSel.nEndPos ) + return; + + if ( aSel.nEndPos > 0 ) + { + OUString aSelText( aParagraph.copy( 0, aSel.nEndPos )); + + ShowArgumentsTip( aSelText ); + } +} + +void ScInputHandler::ShowTip( const OUString& rText ) +{ + // aManualTip needs to be set afterwards from outside + + HideTip(); + HideTipBelow(); + + EditView* pActiveView = pTopView ? pTopView : pTableView; + if (!pActiveView) + return; + + Point aPos; + if (pInputWin && pInputWin->GetEditView() == pActiveView) + { + pTipVisibleParent = pInputWin->GetEditWindow(); + aPos = pInputWin->GetCursorScreenPixelPos(); + } + else + { + pTipVisibleParent = pActiveView->GetWindow(); + if (vcl::Cursor* pCur = pActiveView->GetCursor()) + aPos = pTipVisibleParent->LogicToPixel( pCur->GetPos() ); + aPos = pTipVisibleParent->OutputToScreenPixel( aPos ); + } + + tools::Rectangle aRect( aPos, aPos ); + QuickHelpFlags const nAlign = QuickHelpFlags::Left|QuickHelpFlags::Bottom; + nTipVisible = Help::ShowPopover(pTipVisibleParent, aRect, rText, nAlign); + pTipVisibleParent->AddEventListener( LINK( this, ScInputHandler, ShowHideTipVisibleParentListener ) ); +} + +void ScInputHandler::ShowTipBelow( const OUString& rText ) +{ + HideTipBelow(); + + EditView* pActiveView = pTopView ? pTopView : pTableView; + if ( !pActiveView ) + return; + + Point aPos; + if (pInputWin && pInputWin->GetEditView() == pActiveView) + { + pTipVisibleSecParent = pInputWin->GetEditWindow(); + aPos = pInputWin->GetCursorScreenPixelPos(true); + } + else + { + pTipVisibleSecParent = pActiveView->GetWindow(); + if (vcl::Cursor* pCur = pActiveView->GetCursor()) + { + Point aLogicPos = pCur->GetPos(); + aLogicPos.AdjustY(pCur->GetHeight() ); + aPos = pTipVisibleSecParent->LogicToPixel( aLogicPos ); + } + aPos = pTipVisibleSecParent->OutputToScreenPixel( aPos ); + } + + tools::Rectangle aRect( aPos, aPos ); + QuickHelpFlags const nAlign = QuickHelpFlags::Left | QuickHelpFlags::Top | QuickHelpFlags::NoEvadePointer; + nTipVisibleSec = Help::ShowPopover(pTipVisibleSecParent, aRect, rText, nAlign); + pTipVisibleSecParent->AddEventListener( LINK( this, ScInputHandler, ShowHideTipVisibleSecParentListener ) ); +} + +bool ScInputHandler::GetFuncName( OUString& aStart, OUString& aResult ) +{ + if ( aStart.isEmpty() ) + return false; + + aStart = ScGlobal::getCharClass().uppercase( aStart ); + sal_Int32 nPos = aStart.getLength() - 1; + sal_Unicode c = aStart[ nPos ]; + // fdo#75264 use maFormulaChar to check if characters are used in function names + ::std::set< sal_Unicode >::const_iterator p = maFormulaChar.find( c ); + if ( p == maFormulaChar.end() ) + return false; // last character is not part of any function name, quit + + ::std::vector<sal_Unicode> aTemp { c }; + for(sal_Int32 i = nPos - 1; i >= 0; --i) + { + c = aStart[ i ]; + p = maFormulaChar.find( c ); + + if (p == maFormulaChar.end()) + break; + + aTemp.push_back( c ); + } + + ::std::vector<sal_Unicode>::reverse_iterator rIt = aTemp.rbegin(); + aResult = OUString( *rIt++ ); + while ( rIt != aTemp.rend() ) + aResult += OUStringChar( *rIt++ ); + + return true; +} + +namespace { + /// Rid ourselves of unwanted " quoted json characters. + OString escapeJSON(const OUString &aStr) + { + OUString aEscaped = aStr; + aEscaped = aEscaped.replaceAll("\n", " "); + aEscaped = aEscaped.replaceAll("\"", "'"); + return OUStringToOString(aEscaped, RTL_TEXTENCODING_UTF8); + } +} + +void ScInputHandler::ShowFuncList( const ::std::vector< OUString > & rFuncStrVec ) +{ + const SfxViewShell* pViewShell = SfxViewShell::Current(); + if (comphelper::LibreOfficeKit::isActive()) + { + if (rFuncStrVec.size() && pViewShell && pViewShell->isLOKMobilePhone()) + { + auto aPos = pFormulaData->begin(); + sal_uInt32 nCurIndex = std::distance(aPos, miAutoPosFormula); + const sal_uInt32 nSize = pFormulaData->size(); + + OUString aFuncNameStr; + OUString aDescFuncNameStr; + OStringBuffer aPayload; + aPayload.append("[ "); + for (const OUString& rFunc : rFuncStrVec) + { + if ( rFunc[rFunc.getLength()-1] == cParenthesesReplacement ) + { + aFuncNameStr = rFunc.copy(0, rFunc.getLength()-1); + } + else + { + aFuncNameStr = rFunc; + } + + FormulaHelper aHelper(ScGlobal::GetStarCalcFunctionMgr()); + aDescFuncNameStr = aFuncNameStr + "()"; + sal_Int32 nNextFStart = 0; + const IFunctionDescription* ppFDesc; + ::std::vector< OUString > aArgs; + OUString eqPlusFuncName = "=" + aDescFuncNameStr; + if ( aHelper.GetNextFunc( eqPlusFuncName, false, nNextFStart, nullptr, &ppFDesc, &aArgs ) ) + { + if ( !ppFDesc->getFunctionName().isEmpty() ) + { + aPayload.append("{"); + aPayload.append("\"index\": "); + aPayload.append(static_cast<sal_Int64>(nCurIndex)); + aPayload.append(", "); + aPayload.append("\"signature\": \""); + aPayload.append(escapeJSON(ppFDesc->getSignature())); + aPayload.append("\", "); + aPayload.append("\"description\": \""); + aPayload.append(escapeJSON(ppFDesc->getDescription())); + aPayload.append("\"}, "); + } + } + ++nCurIndex; + if (nCurIndex == nSize) + nCurIndex = 0; + } + sal_Int32 nLen = aPayload.getLength(); + aPayload[nLen - 2] = ' '; + aPayload[nLen - 1] = ']'; + + OString s = aPayload.makeStringAndClear(); + pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CALC_FUNCTION_LIST, s.getStr()); + } + // not tunnel tooltips in the lok case + return; + } + + OUStringBuffer aTipStr; + OUString aFuncNameStr; + OUString aDescFuncNameStr; + ::std::vector<OUString>::const_iterator itStr = rFuncStrVec.begin(); + sal_Int32 nMaxFindNumber = 3; + sal_Int32 nRemainFindNumber = nMaxFindNumber; + for ( ; itStr != rFuncStrVec.end(); ++itStr ) + { + const OUString& rFunc = *itStr; + if ( rFunc[rFunc.getLength()-1] == cParenthesesReplacement ) + { + aFuncNameStr = rFunc.copy(0, rFunc.getLength()-1); + } + else + { + aFuncNameStr = rFunc; + } + if ( itStr == rFuncStrVec.begin() ) + { + aTipStr = "["; + aDescFuncNameStr = aFuncNameStr + "()"; + } + else + { + aTipStr.append(", "); + } + aTipStr.append(aFuncNameStr); + if ( itStr == rFuncStrVec.begin() ) + aTipStr.append("]"); + if ( --nRemainFindNumber <= 0 ) + break; + } + sal_Int32 nRemainNumber = rFuncStrVec.size() - nMaxFindNumber; + if ( nRemainFindNumber == 0 && nRemainNumber > 0 ) + { + OUString aMessage( ScResId( STR_FUNCTIONS_FOUND ) ); + aMessage = aMessage.replaceFirst("%2", OUString::number(nRemainNumber)); + aMessage = aMessage.replaceFirst("%1", aTipStr); + aTipStr = aMessage; + } + FormulaHelper aHelper(ScGlobal::GetStarCalcFunctionMgr()); + sal_Int32 nNextFStart = 0; + const IFunctionDescription* ppFDesc; + ::std::vector< OUString > aArgs; + OUString eqPlusFuncName = "=" + aDescFuncNameStr; + if ( aHelper.GetNextFunc( eqPlusFuncName, false, nNextFStart, nullptr, &ppFDesc, &aArgs ) ) + { + if ( !ppFDesc->getFunctionName().isEmpty() ) + { + aTipStr.append(" : " + ppFDesc->getDescription()); + } + } + ShowTip( aTipStr.makeStringAndClear() ); +} + +void ScInputHandler::UseFormulaData() +{ + EditView* pActiveView = pTopView ? pTopView : pTableView; + + /* TODO: MLFORMULA: this should work also with multi-line formulas. */ + if ( !(pActiveView && pFormulaData && mpEditEngine->GetParagraphCount() == 1) ) + return; + + OUString aParagraph = mpEditEngine->GetText( 0 ); + ESelection aSel = pActiveView->GetSelection(); + aSel.Adjust(); + + // Due to differences between table and input cell (e.g clipboard with line breaks), + // the selection may not be in line with the EditEngine anymore. + // Just return without any indication as to why. + if ( aSel.nEndPos > aParagraph.getLength() ) + return; + + if ( aParagraph.getLength() > aSel.nEndPos && + ( ScGlobal::getCharClass().isLetterNumeric( aParagraph, aSel.nEndPos ) || + aParagraph[ aSel.nEndPos ] == '_' || + aParagraph[ aSel.nEndPos ] == '.' || + aParagraph[ aSel.nEndPos ] == '$' ) ) + return; + + // Is the cursor at the end of a word? + if ( aSel.nEndPos <= 0 ) + return; + + OUString aSelText( aParagraph.copy( 0, aSel.nEndPos )); + + OUString aText; + if ( GetFuncName( aSelText, aText ) ) + { + // function name is incomplete: + // show matching functions name as tip above cell + ::std::vector<OUString> aNewVec; + miAutoPosFormula = pFormulaData->end(); + miAutoPosFormula = findTextAll(*pFormulaData, miAutoPosFormula, aText, aNewVec, false); + if (miAutoPosFormula != pFormulaData->end()) + { + // check if partial function name is not between quotes + sal_Unicode cBetweenQuotes = 0; + for ( int n = 0; n < aSelText.getLength(); n++ ) + { + if (cBetweenQuotes) + { + if (aSelText[n] == cBetweenQuotes) + cBetweenQuotes = 0; + } + else if ( aSelText[ n ] == '"' ) + cBetweenQuotes = '"'; + else if ( aSelText[ n ] == '\'' ) + cBetweenQuotes = '\''; + } + if ( cBetweenQuotes ) + return; // we're between quotes + + ShowFuncList(aNewVec); + aAutoSearch = aText; + } + return; + } + + // function name is complete: + // show tip below the cell with function name and arguments of function + ShowArgumentsTip( aSelText ); +} + +void ScInputHandler::NextFormulaEntry( bool bBack ) +{ + EditView* pActiveView = pTopView ? pTopView : pTableView; + if ( pActiveView && pFormulaData ) + { + ::std::vector<OUString> aNewVec; + ScTypedCaseStrSet::const_iterator itNew = findTextAll(*pFormulaData, miAutoPosFormula, aAutoSearch, aNewVec, bBack); + if (itNew != pFormulaData->end()) + { + miAutoPosFormula = itNew; + ShowFuncList( aNewVec ); + } + } + + // For Tab we always call HideCursor first + if (pActiveView) + pActiveView->ShowCursor(); +} + +namespace { + +void completeFunction( EditView* pView, const OUString& rInsert, bool& rParInserted ) +{ + if (!pView) + return; + + ESelection aSel = pView->GetSelection(); + + bool bNoInitialLetter = false; + OUString aOld = pView->GetEditEngine()->GetText(0); + // in case we want just insert a function and not completing + if ( comphelper::LibreOfficeKit::isActive() ) + { + ESelection aSelRange = aSel; + --aSelRange.nStartPos; + --aSelRange.nEndPos; + pView->SetSelection(aSelRange); + pView->SelectCurrentWord(); + + if ( aOld == "=" ) + { + bNoInitialLetter = true; + aSelRange.nStartPos = 1; + aSelRange.nEndPos = 1; + pView->SetSelection(aSelRange); + } + else if ( pView->GetSelected().startsWith("()") ) + { + bNoInitialLetter = true; + ++aSelRange.nStartPos; + ++aSelRange.nEndPos; + pView->SetSelection(aSelRange); + } + } + + if(!bNoInitialLetter) + { + const sal_Int32 nMinLen = std::max(aSel.nEndPos - aSel.nStartPos, sal_Int32(1)); + // Since transliteration service is used to test for match, the replaced string could be + // longer than rInsert, so in order to find longest match before the cursor, test whole + // string from start to current cursor position (don't limit to length of rInsert) + // Disclaimer: I really don't know if a match longer than rInsert is actually possible, + // so the above is based on assumptions how "transliteration" might possibly work. If + // it's in fact impossible, an optimization would be useful to limit aSel.nStartPos to + // std::max(sal_Int32(0), aSel.nEndPos - rInsert.getLength()). + aSel.nStartPos = 0; + pView->SetSelection(aSel); + const OUString aAll = pView->GetSelected(); + OUString aMatch; + for (sal_Int32 n = aAll.getLength(); n >= nMinLen && aMatch.isEmpty(); --n) + { + const OUString aTest = aAll.copy(aAll.getLength() - n); // n trailing chars + if (ScGlobal::GetTransliteration().isMatch(aTest, rInsert)) + aMatch = aTest; // Found => break the loop + } + + aSel.nStartPos = aSel.nEndPos - aMatch.getLength(); + pView->SetSelection(aSel); + } + + OUString aInsStr = rInsert; + sal_Int32 nInsLen = aInsStr.getLength(); + bool bDoParen = ( nInsLen > 1 && aInsStr[nInsLen-2] == '(' + && aInsStr[nInsLen-1] == ')' ); + if ( bDoParen ) + { + // Do not insert parentheses after function names if there already are some + // (e.g. if the function name was edited). + ESelection aWordSel = pView->GetSelection(); + + // aWordSel.EndPos points one behind string if word at end + if (aWordSel.nEndPos < aOld.getLength()) + { + sal_Unicode cNext = aOld[aWordSel.nEndPos]; + if ( cNext == '(' ) + { + bDoParen = false; + aInsStr = aInsStr.copy( 0, nInsLen - 2 ); // Skip parentheses + } + } + } + + pView->InsertText( aInsStr ); + + if ( bDoParen ) // Put cursor between parentheses + { + aSel = pView->GetSelection(); + --aSel.nStartPos; + --aSel.nEndPos; + pView->SetSelection(aSel); + + rParInserted = true; + } +} + +} + +void ScInputHandler::PasteFunctionData() +{ + if (pFormulaData && miAutoPosFormula != pFormulaData->end()) + { + const ScTypedStrData& rData = *miAutoPosFormula; + OUString aInsert = rData.GetString(); + if (aInsert[aInsert.getLength()-1] == cParenthesesReplacement) + aInsert = OUString::Concat(aInsert.subView( 0, aInsert.getLength()-1)) + "()"; + bool bParInserted = false; + + DataChanging(); // Cannot be new + completeFunction( pTopView, aInsert, bParInserted ); + completeFunction( pTableView, aInsert, bParInserted ); + DataChanged(); + ShowTipCursor(); + + if (bParInserted) + AutoParAdded(); + } + + HideTip(); + + EditView* pActiveView = pTopView ? pTopView : pTableView; + if (comphelper::LibreOfficeKit::isActive() && pTopView && pInputWin) + pInputWin->TextGrabFocus(); + if (pActiveView) + pActiveView->ShowCursor(); +} + +void ScInputHandler::LOKPasteFunctionData(const OUString& rFunctionName) +{ + // in case we have no top view try to create it + if (!pTopView && pInputWin) + { + ScInputMode eCurMode = eMode; + SetMode(SC_INPUT_TOP); + if (!pTopView) + SetMode(eCurMode); + } + + EditView* pEditView = pTopView ? pTopView : pTableView; + + if (!pActiveViewSh || !pEditView) + return; + + bool bEdit = false; + OUString aFormula; + const EditEngine* pEditEngine = pEditView->GetEditEngine(); + if (pEditEngine) + { + aFormula = pEditEngine->GetText(0); + /* TODO: LOK: are you sure you want '+' and '-' let start formulas with + * function names? That was meant for "data typist" numeric keyboard + * input. */ + bEdit = aFormula.getLength() > 1 && (aFormula[0] == '=' || aFormula[0] == '+' || aFormula[0] == '-'); + } + + if ( !bEdit ) + { + OUString aNewFormula('='); + if ( aFormula.startsWith("=") ) + aNewFormula = aFormula; + + InputReplaceSelection( aNewFormula ); + } + + if (pFormulaData) + { + OUString aNew; + ScTypedCaseStrSet::const_iterator aPos = findText(*pFormulaData, pFormulaData->begin(), rFunctionName, aNew, /* backward = */false); + + if (aPos != pFormulaData->end()) + { + miAutoPosFormula = aPos; + PasteFunctionData(); + } + } +} + +void ScInputHandler::LOKSendFormulabarUpdate(const SfxViewShell* pActiveViewSh, + const OUString& rText, + const ESelection& rSelection) +{ + OUString aSelection = + OUString::number(rSelection.nStartPos) + ";" + OUString::number(rSelection.nEndPos); + + std::unique_ptr<jsdialog::ActionDataMap> pData = std::make_unique<jsdialog::ActionDataMap>(); + (*pData)["action_type"] = "setText"; + (*pData)["text"] = rText; + (*pData)["selection"] = aSelection; + + sal_uInt64 nCurrentShellId = reinterpret_cast<sal_uInt64>(pActiveViewSh); + std::string sWindowId = std::to_string(nCurrentShellId) + "formulabar"; + jsdialog::SendAction(sWindowId, "sc_input_window", std::move(pData)); +} + +// Calculate selection and display as tip help +static OUString lcl_Calculate( const OUString& rFormula, ScDocument& rDoc, const ScAddress &rPos ) +{ + //TODO: Merge with ScFormulaDlg::CalcValue and move into Document! + // Quotation marks for Strings are only inserted here. + + if(rFormula.isEmpty()) + return OUString(); + + std::optional<ScSimpleFormulaCalculator> pCalc( std::in_place, rDoc, rPos, rFormula, false ); + + // FIXME: HACK! In order to not get a #REF! for ColRowNames, if a name is actually inserted as a Range + // into the whole Formula, but is interpreted as a single cell reference when displaying it on its own + bool bColRowName = pCalc->HasColRowName(); + if ( bColRowName ) + { + // ColRowName in RPN code? + if ( pCalc->GetCode()->GetCodeLen() <= 1 ) + { // ==1: Single one is as a Parameter always a Range + // ==0: It might be one, if ... + OUString aBraced = "(" + rFormula + ")"; + pCalc.emplace( rDoc, rPos, aBraced, false ); + } + else + bColRowName = false; + } + + FormulaError nErrCode = pCalc->GetErrCode(); + if ( nErrCode != FormulaError::NONE ) + return ScGlobal::GetErrorString(nErrCode); + + SvNumberFormatter& aFormatter = *rDoc.GetFormatTable(); + OUString aValue; + if ( pCalc->IsValue() ) + { + double n = pCalc->GetValue(); + sal_uInt32 nFormat = aFormatter.GetStandardFormat( n, 0, + pCalc->GetFormatType(), ScGlobal::eLnge ); + aFormatter.GetInputLineString( n, nFormat, aValue ); + //! display OutputString but insert InputLineString + } + else + { + OUString aStr = pCalc->GetString().getString(); + sal_uInt32 nFormat = aFormatter.GetStandardFormat( + pCalc->GetFormatType(), ScGlobal::eLnge); + { + const Color* pColor; + aFormatter.GetOutputString( aStr, nFormat, + aValue, &pColor ); + } + + aValue = "\"" + aValue + "\""; + //! Escape quotation marks in String?? + } + + ScRange aTestRange; + if ( bColRowName || (aTestRange.Parse(rFormula, rDoc) & ScRefFlags::VALID) ) + aValue += " ..."; + + return aValue; +} + +void ScInputHandler::FormulaPreview() +{ + OUString aValue; + EditView* pActiveView = pTopView ? pTopView : pTableView; + if ( pActiveView && pActiveViewSh ) + { + OUString aPart = pActiveView->GetSelected(); + if (aPart.isEmpty()) + aPart = mpEditEngine->GetText(0); + ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument(); + aValue = lcl_Calculate( aPart, rDoc, aCursorPos ); + } + + if (!aValue.isEmpty()) + { + ShowTip( aValue ); // Display as QuickHelp + aManualTip = aValue; // Set after ShowTip + if (pFormulaData) + miAutoPosFormula = pFormulaData->end(); + if (pColumnData) + miAutoPosColumn = pColumnData->end(); + } +} + +void ScInputHandler::PasteManualTip() +{ + // Three dots at the end -> Range reference -> do not insert + // FIXME: Once we have matrix constants, we can change this + sal_Int32 nTipLen = aManualTip.getLength(); + sal_uInt32 const nTipLen2(sal::static_int_cast<sal_uInt32>(nTipLen)); + if ( nTipLen && ( nTipLen < 3 || aManualTip.subView( nTipLen2-3 ) != u"..." ) ) + { + DataChanging(); // Cannot be new + + OUString aInsert = aManualTip; + EditView* pActiveView = pTopView ? pTopView : pTableView; + if (!pActiveView->HasSelection()) + { + // Nothing selected -> select everything + sal_Int32 nOldLen = mpEditEngine->GetTextLen(0); + ESelection aAllSel( 0, 0, 0, nOldLen ); + if ( pTopView ) + pTopView->SetSelection( aAllSel ); + if ( pTableView ) + pTableView->SetSelection( aAllSel ); + } + + ESelection aSel = pActiveView->GetSelection(); + aSel.Adjust(); + OSL_ENSURE( !aSel.nStartPara && !aSel.nEndPara, "Too many paragraphs in Formula" ); + if ( !aSel.nStartPos ) // Selection from the start? + { + if ( aSel.nEndPos == mpEditEngine->GetTextLen(0) ) + { + // Everything selected -> skip quotation marks + if ( aInsert[0] == '"' ) + aInsert = aInsert.copy(1); + sal_Int32 nInsLen = aInsert.getLength(); + if ( aInsert.endsWith("\"") ) + aInsert = aInsert.copy( 0, nInsLen-1 ); + } + else if ( aSel.nEndPos ) + { + // Not everything selected -> do not overwrite equality sign + //FIXME: Even double equality signs?? + aSel.nStartPos = 1; + if ( pTopView ) + pTopView->SetSelection( aSel ); + if ( pTableView ) + pTableView->SetSelection( aSel ); + } + } + if ( pTopView ) + pTopView->InsertText( aInsert, true ); + if ( pTableView ) + pTableView->InsertText( aInsert, true ); + + DataChanged(); + } + + HideTip(); +} + +void ScInputHandler::ResetAutoPar() +{ + nAutoPar = 0; +} + +void ScInputHandler::AutoParAdded() +{ + ++nAutoPar; // Closing parenthesis can be overwritten +} + +bool ScInputHandler::CursorAtClosingPar() +{ + // Test if the cursor is before a closing parenthesis + // Selection from SetReference has been removed before + EditView* pActiveView = pTopView ? pTopView : pTableView; + if ( pActiveView && !pActiveView->HasSelection() && bFormulaMode ) + { + ESelection aSel = pActiveView->GetSelection(); + sal_Int32 nPos = aSel.nStartPos; + OUString aFormula = mpEditEngine->GetText(0); + if ( nPos < aFormula.getLength() && aFormula[nPos] == ')' ) + return true; + } + return false; +} + +void ScInputHandler::SkipClosingPar() +{ + // this is called when a ')' is typed and the cursor is before a ')' + // that can be overwritten -> just set the cursor behind the ')' + + EditView* pActiveView = pTopView ? pTopView : pTableView; + if (pActiveView) + { + ESelection aSel = pActiveView->GetSelection(); + ++aSel.nStartPos; + ++aSel.nEndPos; + + // this is in a formula (only one paragraph), so the selection + // can be used directly for the TopView + + if ( pTopView ) + pTopView->SetSelection( aSel ); + if ( pTableView ) + pTableView->SetSelection( aSel ); + } + + OSL_ENSURE(nAutoPar, "SkipClosingPar: count is wrong"); + --nAutoPar; +} + +// Auto input + +void ScInputHandler::GetColData() +{ + if ( !pActiveViewSh ) + return; + + ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument(); + + if ( pColumnData ) + pColumnData->clear(); + else + pColumnData.reset( new ScTypedCaseStrSet ); + + std::vector<ScTypedStrData> aEntries; + rDoc.GetDataEntries( + aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab(), aEntries); + if (!aEntries.empty()) + pColumnData->insert(aEntries.begin(), aEntries.end()); + + miAutoPosColumn = pColumnData->end(); +} + +void ScInputHandler::UseColData() // When typing +{ + EditView* pActiveView = pTopView ? pTopView : pTableView; + if ( !(pActiveView && pColumnData) ) + return; + + // Only change when cursor is at the end + ESelection aSel = pActiveView->GetSelection(); + aSel.Adjust(); + + sal_Int32 nParCnt = mpEditEngine->GetParagraphCount(); + if ( aSel.nEndPara+1 != nParCnt ) + return; + + sal_Int32 nParLen = mpEditEngine->GetTextLen( aSel.nEndPara ); + if ( aSel.nEndPos != nParLen ) + return; + + OUString aText = GetEditText(mpEditEngine.get()); + if (aText.isEmpty()) + return; + + std::vector< OUString > aResultVec; + OUString aNew; + sal_Int32 nLongestPrefixLen = 0; + miAutoPosColumn = pColumnData->end(); + mbPartialPrefix = false; + miAutoPosColumn = findTextAll(*pColumnData, miAutoPosColumn, aText, aResultVec, false, &nLongestPrefixLen); + + if (nLongestPrefixLen <= 0 || aResultVec.empty()) + return; + + if (aResultVec.size() > 1) + { + mbPartialPrefix = true; + bUseTab = true; // Allow Ctrl (+ Shift + ) + TAB cycling. + miAutoPosColumn = pColumnData->end(); + + // Display the rest of longest common prefix as suggestion. + aNew = aResultVec[0].copy(0, nLongestPrefixLen); + } + else + { + aNew = aResultVec[0]; + } + + // Strings can contain line endings (e.g. due to dBase import), + // which would result in multiple paragraphs here, which is not desirable. + //! Then GetExactMatch doesn't work either + lcl_RemoveLineEnd( aNew ); + + // Keep paragraph, just append the rest + //! Exact replacement in EnterHandler !!! + // One Space between paragraphs: + sal_Int32 nEdLen = mpEditEngine->GetTextLen() + nParCnt - 1; + OUString aIns = aNew.copy(nEdLen); + + // Selection must be "backwards", so the cursor stays behind the last + // typed character + ESelection aSelection( aSel.nEndPara, aSel.nEndPos + aIns.getLength(), + aSel.nEndPara, aSel.nEndPos ); + + // When editing in input line, apply to both edit views + if ( pTableView ) + { + pTableView->InsertText( aIns ); + pTableView->SetSelection( aSelection ); + } + if ( pTopView ) + { + pTopView->InsertText( aIns ); + pTopView->SetSelection( aSelection ); + } + + aAutoSearch = aText; // To keep searching - nAutoPos is set +} + +void ScInputHandler::NextAutoEntry( bool bBack ) +{ + EditView* pActiveView = pTopView ? pTopView : pTableView; + if ( pActiveView && pColumnData ) + { + if (!aAutoSearch.isEmpty()) + { + // Is the selection still valid (could be changed via the mouse)? + ESelection aSel = pActiveView->GetSelection(); + aSel.Adjust(); + sal_Int32 nParCnt = mpEditEngine->GetParagraphCount(); + if ( aSel.nEndPara+1 == nParCnt && aSel.nStartPara == aSel.nEndPara ) + { + OUString aText = GetEditText(mpEditEngine.get()); + sal_Int32 nSelLen = aSel.nEndPos - aSel.nStartPos; + sal_Int32 nParLen = mpEditEngine->GetTextLen( aSel.nEndPara ); + if ( aSel.nEndPos == nParLen && aText.getLength() == aAutoSearch.getLength() + nSelLen ) + { + OUString aNew; + ScTypedCaseStrSet::const_iterator itNew = + findText(*pColumnData, miAutoPosColumn, aAutoSearch, aNew, bBack); + + if (itNew != pColumnData->end()) + { + // match found! + miAutoPosColumn = itNew; + bInOwnChange = true; // disable ModifyHdl (reset below) + mbPartialPrefix = false; + + lcl_RemoveLineEnd( aNew ); + OUString aIns = aNew.copy(aAutoSearch.getLength()); + + // when editing in input line, apply to both edit views + if ( pTableView ) + { + pTableView->DeleteSelected(); + pTableView->InsertText( aIns ); + pTableView->SetSelection( ESelection( + aSel.nEndPara, aSel.nStartPos + aIns.getLength(), + aSel.nEndPara, aSel.nStartPos ) ); + } + if ( pTopView ) + { + pTopView->DeleteSelected(); + pTopView->InsertText( aIns ); + pTopView->SetSelection( ESelection( + aSel.nEndPara, aSel.nStartPos + aIns.getLength(), + aSel.nEndPara, aSel.nStartPos ) ); + } + + bInOwnChange = false; + } + } + } + } + } + + // For Tab, HideCursor was always called first + if (pActiveView) + pActiveView->ShowCursor(); +} + +// Highlight parentheses +void ScInputHandler::UpdateParenthesis() +{ + // Find parentheses + //TODO: Can we disable parentheses highlighting per parentheses? + bool bFound = false; + if ( bFormulaMode && eMode != SC_INPUT_TOP ) + { + if ( pTableView && !pTableView->HasSelection() ) // Selection is always at the bottom + { + ESelection aSel = pTableView->GetSelection(); + if (aSel.nStartPos) + { + // Examine character left to the cursor + sal_Int32 nPos = aSel.nStartPos - 1; + OUString aFormula = mpEditEngine->GetText(aSel.nStartPara); + sal_Unicode c = aFormula[nPos]; + if ( c == '(' || c == ')' ) + { + // Note this matches only within one paragraph. + sal_Int32 nOther = lcl_MatchParenthesis( aFormula, nPos ); + if ( nOther != -1 ) + { + SfxItemSet aSet( mpEditEngine->GetEmptyItemSet() ); + aSet.Put( SvxWeightItem( WEIGHT_BOLD, EE_CHAR_WEIGHT ) ); + + //! Distinguish if cell is already highlighted!!!! + if (bParenthesisShown) + { + // Remove old highlighting + sal_Int32 nCount = mpEditEngine->GetParagraphCount(); + for (sal_Int32 i=0; i<nCount; i++) + mpEditEngine->RemoveCharAttribs( i, EE_CHAR_WEIGHT ); + } + + ESelection aSelThis( aSel.nStartPara, nPos, aSel.nStartPara, nPos+1); + mpEditEngine->QuickSetAttribs( aSet, aSelThis ); + ESelection aSelOther( aSel.nStartPara, nOther, aSel.nStartPara, nOther+1); + mpEditEngine->QuickSetAttribs( aSet, aSelOther ); + + // Dummy InsertText for Update and Paint (selection is empty) + pTableView->InsertText( OUString() ); + + bFound = true; + } + } + } + + // mark parenthesis right of cursor if it will be overwritten (nAutoPar) + // with different color (COL_LIGHTBLUE) ?? + } + } + + // Remove old highlighting, if no new one is set + if ( bParenthesisShown && !bFound && pTableView ) + { + sal_Int32 nCount = mpEditEngine->GetParagraphCount(); + for (sal_Int32 i=0; i<nCount; i++) + pTableView->RemoveCharAttribs( i, EE_CHAR_WEIGHT ); + } + + bParenthesisShown = bFound; +} + +void ScInputHandler::ViewShellGone(const ScTabViewShell* pViewSh) // Executed synchronously! +{ + if ( pViewSh == pActiveViewSh ) + { + pLastState.reset(); + pLastPattern = nullptr; + } + + if ( pViewSh == pRefViewSh ) + { + //! The input from the EnterHandler does not arrive anymore + // We end the EditMode anyways + EnterHandler(); + bFormulaMode = false; + pRefViewSh = nullptr; + SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) ); + SC_MOD()->SetRefInputHdl(nullptr); + if (pInputWin) + pInputWin->SetFormulaMode(false); + UpdateAutoCorrFlag(); + } + + pActiveViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() ); + + if ( pActiveViewSh && pActiveViewSh == pViewSh ) + { + OSL_FAIL("pActiveViewSh is gone"); + pActiveViewSh = nullptr; + } + + if ( SC_MOD()->GetInputOptions().GetTextWysiwyg() ) + UpdateRefDevice(); // Don't keep old document's printer as RefDevice +} + +void ScInputHandler::UpdateActiveView() +{ + ImplCreateEditEngine(); + + // #i20588# Don't rely on focus to find the active edit view. Instead, the + // active pane at the start of editing is now stored (GetEditActivePart). + // GetActiveWin (the currently active pane) fails for ref input across the + // panes of a split view. + + vcl::Window* pShellWin = pActiveViewSh ? + pActiveViewSh->GetWindowByPos( pActiveViewSh->GetViewData().GetEditActivePart() ) : + nullptr; + + sal_uInt16 nCount = mpEditEngine->GetViewCount(); + if (nCount > 0) + { + pTableView = mpEditEngine->GetView(); + for (sal_uInt16 i=1; i<nCount; i++) + { + EditView* pThis = mpEditEngine->GetView(i); + vcl::Window* pWin = pThis->GetWindow(); + if ( pWin==pShellWin ) + pTableView = pThis; + } + } + else + pTableView = nullptr; + + // setup the pTableView editeng for tiled rendering to get cursor and selections + if (pTableView && pActiveViewSh) + { + if (comphelper::LibreOfficeKit::isActive()) + { + pTableView->RegisterViewShell(pActiveViewSh); + } + } + + if (pInputWin && (eMode == SC_INPUT_TOP || eMode == SC_INPUT_TABLE)) + { + // tdf#71409: Always create the edit engine instance for the input + // window, in order to properly manage accessibility events. + pTopView = pInputWin->GetEditView(); + if (eMode != SC_INPUT_TOP) + pTopView = nullptr; + } + else + pTopView = nullptr; +} + +void ScInputHandler::SetInputWindow( ScInputWindow* pNew ) +{ + pInputWin = pNew; +} + +void ScInputHandler::StopInputWinEngine( bool bAll ) +{ + if (pInputWin && !pInputWin->isDisposed()) + pInputWin->StopEditEngine( bAll ); + + pTopView = nullptr; // invalid now +} + +EditView* ScInputHandler::GetActiveView() +{ + UpdateActiveView(); + return pTopView ? pTopView : pTableView; +} + +void ScInputHandler::ForgetLastPattern() +{ + pLastPattern = nullptr; + if ( !pLastState && pActiveViewSh ) + pActiveViewSh->UpdateInputHandler( true ); // Get status again + else + NotifyChange( pLastState.get(), true ); +} + +void ScInputHandler::UpdateAdjust( sal_Unicode cTyped ) +{ + SvxAdjust eSvxAdjust; + switch (eAttrAdjust) + { + case SvxCellHorJustify::Standard: + { + bool bNumber = false; + if (cTyped) // Restarted + bNumber = (cTyped>='0' && cTyped<='9'); // Only ciphers are numbers + else if ( pActiveViewSh ) + { + ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument(); + bNumber = ( rDoc.GetCellType( aCursorPos ) == CELLTYPE_VALUE ); + } + eSvxAdjust = bNumber ? SvxAdjust::Right : SvxAdjust::Left; + } + break; + case SvxCellHorJustify::Block: + eSvxAdjust = SvxAdjust::Block; + break; + case SvxCellHorJustify::Center: + eSvxAdjust = SvxAdjust::Center; + break; + case SvxCellHorJustify::Right: + eSvxAdjust = SvxAdjust::Right; + break; + default: // SvxCellHorJustify::Left + eSvxAdjust = SvxAdjust::Left; + break; + } + + bool bAsianVertical = pLastPattern && + pLastPattern->GetItem( ATTR_STACKED ).GetValue() && + pLastPattern->GetItem( ATTR_VERTICAL_ASIAN ).GetValue(); + if ( bAsianVertical ) + { + // Always edit at top of cell -> LEFT when editing vertically + eSvxAdjust = SvxAdjust::Left; + } + + pEditDefaults->Put( SvxAdjustItem( eSvxAdjust, EE_PARA_JUST ) ); + mpEditEngine->SetDefaults( *pEditDefaults ); + + if ( pActiveViewSh ) + { + pActiveViewSh->GetViewData().SetEditAdjust( eSvxAdjust ); + } + mpEditEngine->SetVertical( bAsianVertical ); +} + +void ScInputHandler::RemoveAdjust() +{ + // Delete hard alignment attributes + bool bUndo = mpEditEngine->IsUndoEnabled(); + if ( bUndo ) + mpEditEngine->EnableUndo( false ); + + // Non-default paragraph attributes (e.g. from clipboard) + // must be turned into character attributes + mpEditEngine->RemoveParaAttribs(); + + if ( bUndo ) + mpEditEngine->EnableUndo( true ); + +} + +void ScInputHandler::RemoveRangeFinder() +{ + // Delete pRangeFindList and colors + mpEditEngine->SetUpdateLayout(false); + sal_Int32 nCount = mpEditEngine->GetParagraphCount(); // Could just have been inserted + for (sal_Int32 i=0; i<nCount; i++) + mpEditEngine->RemoveCharAttribs( i, EE_CHAR_COLOR ); + mpEditEngine->SetUpdateLayout(true); + + EditView* pActiveView = pTopView ? pTopView : pTableView; + pActiveView->ShowCursor( false ); + + DeleteRangeFinder(); // Deletes the list and the labels on the table +} + +bool ScInputHandler::StartTable( sal_Unicode cTyped, bool bFromCommand, bool bInputActivated, + ScEditEngineDefaulter* pTopEngine ) +{ + bool bNewTable = false; + + if (bModified) + return false; + + if (pActiveViewSh) + { + ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument(); + + if (!rDoc.ValidCol(aCursorPos.Col())) + return false; + + ImplCreateEditEngine(); + UpdateActiveView(); + SyncViews(); + + + const ScMarkData& rMark = pActiveViewSh->GetViewData().GetMarkData(); + ScEditableTester aTester; + if ( rMark.IsMarked() || rMark.IsMultiMarked() ) + aTester.TestSelection( rDoc, rMark ); + else + aTester.TestSelectedBlock( + rDoc, aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Col(), aCursorPos.Row(), rMark ); + + bool bStartInputMode = true; + + if (!aTester.IsEditable()) + { + bProtected = true; + // We allow read-only input mode activation regardless + // whether it's part of an array or not or whether explicit cell + // activation is requested (double-click or F2) or a click in input + // line. + bool bShowError = (!bInputActivated || !aTester.GetMessageId() || aTester.GetMessageId() != STR_PROTECTIONERR) && + !pActiveViewSh->GetViewData().GetDocShell()->IsReadOnly(); + if (bShowError) + { + eMode = SC_INPUT_NONE; + StopInputWinEngine( true ); + UpdateFormulaMode(); + if ( pActiveViewSh && ( !bFromCommand || !bCommandErrorShown ) ) + { + // Prevent repeated error messages for the same cell from command events + // (for keyboard events, multiple messages are wanted). + // Set the flag before showing the error message because the command handler + // for the next IME command may be called when showing the dialog. + if ( bFromCommand ) + bCommandErrorShown = true; + + pActiveViewSh->GetActiveWin()->GrabFocus(); + pActiveViewSh->ErrorMessage(aTester.GetMessageId()); + } + bStartInputMode = false; + } + } + + if (bStartInputMode) + { + // UpdateMode is enabled again in ScViewData::SetEditEngine (and not needed otherwise) + mpEditEngine->SetUpdateLayout( false ); + + // Take over attributes in EditEngine + const ScPatternAttr* pPattern = rDoc.GetPattern( aCursorPos.Col(), + aCursorPos.Row(), + aCursorPos.Tab() ); + if (pPattern != pLastPattern) + { + // Percent format? + const SfxItemSet& rAttrSet = pPattern->GetItemSet(); + + if ( const SfxUInt32Item* pItem = rAttrSet.GetItemIfSet( ATTR_VALUE_FORMAT ) ) + { + sal_uInt32 nFormat = pItem->GetValue(); + if (SvNumFormatType::PERCENT == rDoc.GetFormatTable()->GetType( nFormat )) + nCellPercentFormatDecSep = rDoc.GetFormatTable()->GetFormatDecimalSep( nFormat).toChar(); + else + nCellPercentFormatDecSep = 0; + } + else + nCellPercentFormatDecSep = 0; // Default: no percent + + // Validity specified? + if ( const SfxUInt32Item* pItem = rAttrSet.GetItemIfSet( ATTR_VALIDDATA ) ) + nValidation = pItem->GetValue(); + else + nValidation = 0; + + // EditEngine Defaults + // In no case SetParaAttribs, because the EditEngine might already + // be filled (for Edit cells). + // SetParaAttribs would change the content. + + //! The SetDefaults is now (since MUST/src602 + //! EditEngine changes) implemented as a SetParaAttribs. + //! Any problems? + + pPattern->FillEditItemSet( pEditDefaults.get() ); + mpEditEngine->SetDefaults( *pEditDefaults ); + pLastPattern = pPattern; + bLastIsSymbol = pPattern->IsSymbolFont(); + + // Background color must be known for automatic font color. + // For transparent cell background, the document background color must be used. + + Color aBackCol = pPattern->GetItem( ATTR_BACKGROUND ).GetColor(); + ScModule* pScMod = SC_MOD(); + if ( aBackCol.IsTransparent() || + Application::GetSettings().GetStyleSettings().GetHighContrastMode() ) + aBackCol = pScMod->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor; + mpEditEngine->SetBackgroundColor( aBackCol ); + + // Adjustment + eAttrAdjust = pPattern->GetItem(ATTR_HOR_JUSTIFY).GetValue(); + if ( eAttrAdjust == SvxCellHorJustify::Repeat && + pPattern->GetItem(ATTR_LINEBREAK).GetValue() ) + { + // #i31843# "repeat" with "line breaks" is treated as default alignment + eAttrAdjust = SvxCellHorJustify::Standard; + } + } + + if (pTopEngine) + { + // Necessary to sync SvxAutoCorrect behavior. This has to be + // done before InitRangeFinder() below. + MergeLanguageAttributes( *pTopEngine); + } + + // UpdateSpellSettings enables online spelling if needed + // -> also call if attributes are unchanged + UpdateSpellSettings( true ); // uses pLastPattern + + // Fill EditEngine + OUString aStr; + if (bTextValid) + { + mpEditEngine->SetTextCurrentDefaults(aCurrentText); + aStr = aCurrentText; + bTextValid = false; + aCurrentText.clear(); + } + else + aStr = GetEditText(mpEditEngine.get()); + + // cTyped!=0 is overtyping, not editing. + mbEditingExistingContent = !cTyped && !aStr.isEmpty(); + + if (aStr.startsWith("{=") && aStr.endsWith("}") ) // Matrix formula? + { + aStr = aStr.copy(1, aStr.getLength() -2); + mpEditEngine->SetTextCurrentDefaults(aStr); + if ( pInputWin ) + pInputWin->SetTextString(aStr); + } + + UpdateAdjust( cTyped ); + + if ( SC_MOD()->GetAppOptions().GetAutoComplete() ) + GetColData(); + + if (!cTyped && !bCreatingFuncView && StartsLikeFormula(aStr)) + InitRangeFinder(aStr); // Formula is being edited -> RangeFinder + + bNewTable = true; // -> PostEditView Call + } + } + + if (!bProtected && pInputWin) + pInputWin->SetOkCancelMode(); + + return bNewTable; +} + +void ScInputHandler::MergeLanguageAttributes( ScEditEngineDefaulter& rDestEngine ) const +{ + const SfxItemSet& rSrcSet = mpEditEngine->GetDefaults(); + rDestEngine.SetDefaultItem( rSrcSet.Get( EE_CHAR_LANGUAGE )); + rDestEngine.SetDefaultItem( rSrcSet.Get( EE_CHAR_LANGUAGE_CJK )); + rDestEngine.SetDefaultItem( rSrcSet.Get( EE_CHAR_LANGUAGE_CTL )); +} + +static void lcl_SetTopSelection( EditView* pEditView, ESelection& rSel ) +{ + OSL_ENSURE( rSel.nStartPara==0 && rSel.nEndPara==0, "SetTopSelection: Para != 0" ); + + EditEngine* pEngine = pEditView->GetEditEngine(); + sal_Int32 nCount = pEngine->GetParagraphCount(); + if (nCount > 1) + { + sal_Int32 nParLen = pEngine->GetTextLen(rSel.nStartPara); + while (rSel.nStartPos > nParLen && rSel.nStartPara+1 < nCount) + { + rSel.nStartPos -= nParLen + 1; // Including space from line break + nParLen = pEngine->GetTextLen(++rSel.nStartPara); + } + + nParLen = pEngine->GetTextLen(rSel.nEndPara); + while (rSel.nEndPos > nParLen && rSel.nEndPara+1 < nCount) + { + rSel.nEndPos -= nParLen + 1; // Including space from line break + nParLen = pEngine->GetTextLen(++rSel.nEndPara); + } + } + + ESelection aSel = pEditView->GetSelection(); + + if ( rSel.nStartPara != aSel.nStartPara || rSel.nEndPara != aSel.nEndPara + || rSel.nStartPos != aSel.nStartPos || rSel.nEndPos != aSel.nEndPos ) + pEditView->SetSelection( rSel ); +} + +void ScInputHandler::SyncViews( const EditView* pSourceView ) +{ + if (pSourceView) + { + bool bSelectionForTopView = false; + if (pTopView && pTopView != pSourceView) + bSelectionForTopView = true; + bool bSelectionForTableView = false; + if (pTableView && pTableView != pSourceView) + bSelectionForTableView = true; + if (bSelectionForTopView || bSelectionForTableView) + { + ESelection aSel(pSourceView->GetSelection()); + if (bSelectionForTopView) + pTopView->SetSelection(aSel); + if (bSelectionForTableView) + lcl_SetTopSelection(pTableView, aSel); + } + } + // Only sync selection from topView if we are actually editing there + else if (pTopView && pTableView) + { + ESelection aSel(pTopView->GetSelection()); + lcl_SetTopSelection( pTableView, aSel ); + } +} + +IMPL_LINK_NOARG(ScInputHandler, ModifyHdl, LinkParamNone*, void) +{ + if ( !bInOwnChange && ( eMode==SC_INPUT_TYPE || eMode==SC_INPUT_TABLE ) && + mpEditEngine && mpEditEngine->IsUpdateLayout() && pInputWin ) + { + // Update input line from ModifyHdl for changes that are not + // wrapped by DataChanging/DataChanged calls (like Drag&Drop) + OUString aText(ScEditUtil::GetMultilineString(*mpEditEngine)); + lcl_RemoveTabs(aText); + pInputWin->SetTextString(aText); + } +} + +/** + * @return true means new view created + */ +bool ScInputHandler::DataChanging( sal_Unicode cTyped, bool bFromCommand ) +{ + if (pActiveViewSh) + pActiveViewSh->GetViewData().SetPasteMode( ScPasteFlags::NONE ); + bInOwnChange = true; // disable ModifyHdl (reset in DataChanged) + + if ( eMode == SC_INPUT_NONE ) + return StartTable( cTyped, bFromCommand, false, nullptr ); + else + return false; +} + +void ScInputHandler::DataChanged( bool bFromTopNotify, bool bSetModified ) +{ + ImplCreateEditEngine(); + + if (eMode==SC_INPUT_NONE) + eMode = SC_INPUT_TYPE; + + if ( eMode == SC_INPUT_TOP && pTopView && !bFromTopNotify ) + { + // table EditEngine is formatted below, input line needs formatting after paste + // #i20282# not when called from the input line's modify handler + pTopView->GetEditEngine()->QuickFormatDoc( true ); + + // #i23720# QuickFormatDoc hides the cursor, but can't show it again because it + // can't safely access the EditEngine's current view, so the cursor has to be + // shown again here. + pTopView->ShowCursor(); + } + + if (bSetModified) + bModified = true; + bSelIsRef = false; + + if ( pRangeFindList && !bInRangeUpdate ) + RemoveRangeFinder(); // Delete attributes and labels + + UpdateParenthesis(); // Highlight parentheses anew + + if (eMode==SC_INPUT_TYPE || eMode==SC_INPUT_TABLE) + { + OUString aText; + if (pInputWin) + aText = ScEditUtil::GetMultilineString(*mpEditEngine); + else + aText = GetEditText(mpEditEngine.get()); + lcl_RemoveTabs(aText); + + if ( pInputWin ) + pInputWin->SetTextString( aText ); + + if (comphelper::LibreOfficeKit::isActive()) + { + if (pActiveViewSh) + { + // TODO: deprecated? + pActiveViewSh->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_FORMULA, aText.toUtf8().getStr()); + } + } + } + + // If the cursor is before the end of a paragraph, parts are being pushed to + // the right (independently from the eMode) -> Adapt View! + // If the cursor is at the end, the StatusHandler of the ViewData is sufficient. + // + // First make sure the status handler is called now if the cursor + // is outside the visible area + mpEditEngine->QuickFormatDoc(); + + EditView* pActiveView = pTopView ? pTopView : pTableView; + ESelection aSel; + if (pActiveView && pActiveViewSh) + { + ScViewData& rViewData = pActiveViewSh->GetViewData(); + + bool bNeedGrow = ( rViewData.GetEditAdjust() != SvxAdjust::Left ); // Always right-aligned + if (!bNeedGrow) + { + // Cursor before the end? + aSel = pActiveView->GetSelection(); + aSel.Adjust(); + bNeedGrow = ( aSel.nEndPos != mpEditEngine->GetTextLen(aSel.nEndPara) ); + } + if (!bNeedGrow) + { + bNeedGrow = rViewData.GetDocument().IsLayoutRTL( rViewData.GetTabNo() ); + } + if (bNeedGrow) + { + // Adjust inplace view + rViewData.EditGrowY(); + rViewData.EditGrowX(); + } + } + + if (comphelper::LibreOfficeKit::isActive() && pActiveViewSh && pInputWin) + { + ScInputHandler::LOKSendFormulabarUpdate(pActiveViewSh, + ScEditUtil::GetMultilineString(*mpEditEngine), + aSel); + } + + UpdateFormulaMode(); + bTextValid = false; // Changes only in the EditEngine + bInOwnChange = false; +} + +bool ScInputHandler::StartsLikeFormula( std::u16string_view rStr ) const +{ + // For new input '+' and '-' may start the dreaded "lazy data typist" + // formula input, editing existing formula content can only start with '='. + return !rStr.empty() && (rStr[0] == '=' || (!mbEditingExistingContent && (rStr[0] == '+' || rStr[0] == '-'))); +} + +void ScInputHandler::UpdateFormulaMode() +{ + SfxApplication* pSfxApp = SfxGetpApp(); + + bool bIsFormula = !bProtected; + if (bIsFormula) + { + const OUString& rText = mpEditEngine->GetText(0); + bIsFormula = StartsLikeFormula(rText); + } + + if ( bIsFormula ) + { + if (!bFormulaMode) + { + bFormulaMode = true; + pRefViewSh = pActiveViewSh; + pSfxApp->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) ); + ScModule* pMod = SC_MOD(); + pMod->SetRefInputHdl(this); + if (pInputWin) + pInputWin->SetFormulaMode(true); + + // in LOK, we always need to perform the GetFormulaData() call so + // that the formula insertion works + if (comphelper::LibreOfficeKit::isActive() || pMod->GetAppOptions().GetAutoComplete()) + GetFormulaData(); + + UpdateParenthesis(); + UpdateAutoCorrFlag(); + } + } + else // Deactivate + { + if (bFormulaMode) + { + ShowRefFrame(); + bFormulaMode = false; + pRefViewSh = nullptr; + pSfxApp->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) ); + SC_MOD()->SetRefInputHdl(nullptr); + if (pInputWin) + pInputWin->SetFormulaMode(false); + UpdateAutoCorrFlag(); + } + } +} + +void ScInputHandler::ShowRefFrame() +{ + // Modifying pActiveViewSh here would interfere with the bInEnterHandler / bRepeat + // checks in NotifyChange, and lead to keeping the wrong value in pActiveViewSh. + // A local variable is used instead. + ScTabViewShell* pVisibleSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() ); + if ( !(pRefViewSh && pRefViewSh != pVisibleSh) ) + return; + + bool bFound = false; + SfxViewFrame* pRefFrame = pRefViewSh->GetViewFrame(); + SfxViewFrame* pOneFrame = SfxViewFrame::GetFirst(); + while ( pOneFrame && !bFound ) + { + if ( pOneFrame == pRefFrame ) + bFound = true; + pOneFrame = SfxViewFrame::GetNext( *pOneFrame ); + } + + if (bFound) + { + // We count on Activate working synchronously here + // (pActiveViewSh is set while doing so) + pRefViewSh->SetActive(); // Appear and SetViewFrame + + // pLastState is set correctly in the NotifyChange from the Activate + } + else + { + OSL_FAIL("ViewFrame for reference input is not here anymore"); + } +} + +void ScInputHandler::RemoveSelection() +{ + EditView* pActiveView = pTopView ? pTopView : pTableView; + if (!pActiveView) + return; + + ESelection aSel = pActiveView->GetSelection(); + aSel.nStartPara = aSel.nEndPara; + aSel.nStartPos = aSel.nEndPos; + if (pTableView) + pTableView->SetSelection( aSel ); + if (pTopView) + pTopView->SetSelection( aSel ); +} + +void ScInputHandler::InvalidateAttribs() +{ + SfxViewFrame* pViewFrm = SfxViewFrame::Current(); + if (!pViewFrm) + return; + + SfxBindings& rBindings = pViewFrm->GetBindings(); + + rBindings.Invalidate( SID_ATTR_CHAR_FONT ); + rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT ); + rBindings.Invalidate( SID_ATTR_CHAR_COLOR ); + + rBindings.Invalidate( SID_ATTR_CHAR_WEIGHT ); + rBindings.Invalidate( SID_ATTR_CHAR_POSTURE ); + rBindings.Invalidate( SID_ATTR_CHAR_UNDERLINE ); + rBindings.Invalidate( SID_ATTR_CHAR_OVERLINE ); + rBindings.Invalidate( SID_ULINE_VAL_NONE ); + rBindings.Invalidate( SID_ULINE_VAL_SINGLE ); + rBindings.Invalidate( SID_ULINE_VAL_DOUBLE ); + rBindings.Invalidate( SID_ULINE_VAL_DOTTED ); + + rBindings.Invalidate( SID_HYPERLINK_GETLINK ); + + rBindings.Invalidate( SID_ATTR_CHAR_KERNING ); + rBindings.Invalidate( SID_SET_SUPER_SCRIPT ); + rBindings.Invalidate( SID_SET_SUB_SCRIPT ); + rBindings.Invalidate( SID_ATTR_CHAR_STRIKEOUT ); + rBindings.Invalidate( SID_ATTR_CHAR_SHADOWED ); + + rBindings.Invalidate( SID_SAVEDOC ); + rBindings.Invalidate( SID_DOC_MODIFIED ); +} + +// --------------- public methods -------------------------------------------- + +void ScInputHandler::SetMode( ScInputMode eNewMode, const OUString* pInitText, ScEditEngineDefaulter* pTopEngine ) +{ + if ( eMode == eNewMode ) + return; + + ImplCreateEditEngine(); + + if (bProtected) + { + eMode = SC_INPUT_NONE; + StopInputWinEngine( true ); + if (pActiveViewSh) + pActiveViewSh->GetActiveWin()->GrabFocus(); + return; + } + + if (eNewMode != SC_INPUT_NONE && pActiveViewSh) + // Disable paste mode when edit mode starts. + pActiveViewSh->GetViewData().SetPasteMode( ScPasteFlags::NONE ); + + bInOwnChange = true; // disable ModifyHdl (reset below) + + ScInputMode eOldMode = eMode; + eMode = eNewMode; + if (eOldMode == SC_INPUT_TOP && eNewMode != eOldMode) + StopInputWinEngine( false ); + + if (eMode==SC_INPUT_TOP || eMode==SC_INPUT_TABLE) + { + if (eOldMode == SC_INPUT_NONE) // not if switching between modes + { + if (StartTable(0, false, eMode == SC_INPUT_TABLE, pTopEngine)) + { + if (pActiveViewSh) + pActiveViewSh->GetViewData().GetDocShell()->PostEditView( mpEditEngine.get(), aCursorPos ); + } + } + + if (pInitText) + { + mpEditEngine->SetTextCurrentDefaults(*pInitText); + bModified = true; + } + + sal_Int32 nPara = mpEditEngine->GetParagraphCount()-1; + sal_Int32 nLen = mpEditEngine->GetText(nPara).getLength(); + sal_uInt16 nCount = mpEditEngine->GetViewCount(); + + for (sal_uInt16 i=0; i<nCount; i++) + { + if ( eMode == SC_INPUT_TABLE && eOldMode == SC_INPUT_TOP ) + { + // Keep Selection + } + else + { + mpEditEngine->GetView(i)-> + SetSelection( ESelection( nPara, nLen, nPara, nLen ) ); + } + mpEditEngine->GetView(i)->ShowCursor(false); + } + } + + UpdateActiveView(); + if (eMode==SC_INPUT_TABLE || eMode==SC_INPUT_TYPE) + { + if (pTableView) + pTableView->SetEditEngineUpdateLayout(true); + } + else + { + if (pTopView) + pTopView->SetEditEngineUpdateLayout(true); + } + + if (eNewMode != eOldMode) + UpdateFormulaMode(); + + bInOwnChange = false; +} + +/** + * @return true if rString only contains digits (no autocorrect then) + */ +static bool lcl_IsNumber(const OUString& rString) +{ + sal_Int32 nLen = rString.getLength(); + for (sal_Int32 i=0; i<nLen; i++) + { + sal_Unicode c = rString[i]; + if ( c < '0' || c > '9' ) + return false; + } + return true; +} + +static void lcl_SelectionToEnd( EditView* pView ) +{ + if ( pView ) + { + EditEngine* pEngine = pView->GetEditEngine(); + sal_Int32 nParCnt = pEngine->GetParagraphCount(); + if ( nParCnt == 0 ) + nParCnt = 1; + ESelection aSel( nParCnt-1, pEngine->GetTextLen(nParCnt-1) ); // empty selection, cursor at the end + pView->SetSelection( aSel ); + } +} + +void ScInputHandler::EnterHandler( ScEnterMode nBlockMode, bool bBeforeSavingInLOK ) +{ + if (!mbDocumentDisposing && comphelper::LibreOfficeKit::isActive() + && pActiveViewSh != SfxViewShell::Current()) + return; + + // Macro calls for validity can cause a lot of problems, so inhibit + // nested calls of EnterHandler(). + if (bInEnterHandler) return; + bInEnterHandler = true; + bInOwnChange = true; // disable ModifyHdl (reset below) + mbPartialPrefix = false; + + ImplCreateEditEngine(); + + bool bMatrix = ( nBlockMode == ScEnterMode::MATRIX ); + + SfxApplication* pSfxApp = SfxGetpApp(); + std::unique_ptr<EditTextObject> pObject; + std::unique_ptr<ScPatternAttr> pCellAttrs; + bool bForget = false; // Remove due to validity? + + OUString aString = GetEditText(mpEditEngine.get()); + OUString aPreAutoCorrectString(aString); + EditView* pActiveView = pTopView ? pTopView : pTableView; + if (bModified && pActiveView && !aString.isEmpty() && !lcl_IsNumber(aString)) + { + if (pColumnData && miAutoPosColumn != pColumnData->end()) + { + // #i47125# If AutoInput appended something, do the final AutoCorrect + // with the cursor at the end of the input. + lcl_SelectionToEnd(pTopView); + lcl_SelectionToEnd(pTableView); + } + + vcl::Window* pFrameWin = pActiveViewSh ? pActiveViewSh->GetFrameWin() : nullptr; + + if (pTopView) + pTopView->CompleteAutoCorrect(); // CompleteAutoCorrect for both Views + if (pTableView) + pTableView->CompleteAutoCorrect(pFrameWin); + aString = GetEditText(mpEditEngine.get()); + } + lcl_RemoveTabs(aString); + lcl_RemoveTabs(aPreAutoCorrectString); + + // Test if valid (always with simple string) + if (bModified && nValidation && pActiveViewSh) + { + ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocument(); + const ScValidationData* pData = rDoc.GetValidationEntry( nValidation ); + if (pData && pData->HasErrMsg()) + { + // #i67990# don't use pLastPattern in EnterHandler + const ScPatternAttr* pPattern = rDoc.GetPattern( aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab() ); + + bool bOk; + + if (pData->GetDataMode() == SC_VALID_CUSTOM) + { + bOk = pData->IsDataValidCustom( aString, *pPattern, aCursorPos, ScValidationData::CustomValidationPrivateAccess() ); + } + else + { + bOk = pData->IsDataValid( aString, *pPattern, aCursorPos ); + } + + if (!bOk) + { + pActiveViewSh->StopMarking(); // (the InfoBox consumes the MouseButtonUp) + + // tdf#125917 Release the grab that a current mouse-down event being handled + // by ScTabView has put on the mouse via its SelectionEngine. + // Otherwise the warning box cannot interact with the mouse + if (ScTabView* pView = pActiveViewSh->GetViewData().GetView()) + { + if (ScViewSelectionEngine* pSelEngine = pView->GetSelEngine()) + pSelEngine->ReleaseMouse(); + } + + if (bBeforeSavingInLOK) + { + // Invalid entry but not applied to the document model. + // Exit to complete the "save", leaving the edit view as it is + // for the user to continue after save. + bInOwnChange = false; + bInEnterHandler = false; + return; + } + + if (pData->DoError(pActiveViewSh->GetFrameWeld(), aString, aCursorPos)) + bForget = true; // Do not take over input + } + } + } + + // Check for input into DataPilot table + if ( bModified && pActiveViewSh && !bForget ) + { + ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocument(); + ScDPObject* pDPObj = rDoc.GetDPAtCursor( aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab() ); + if ( pDPObj ) + { + // Any input within the DataPilot table is either a valid renaming + // or an invalid action - normal cell input is always aborted + pActiveViewSh->DataPilotInput( aCursorPos, aString ); + bForget = true; + } + } + + std::vector<editeng::MisspellRanges> aMisspellRanges; + // UpdateLayout must be true during CompleteOnlineSpelling + const bool bUpdateLayout = mpEditEngine->SetUpdateLayout( true ); + mpEditEngine->CompleteOnlineSpelling(); + bool bSpellErrors = !bFormulaMode && mpEditEngine->HasOnlineSpellErrors(); + if ( bSpellErrors ) + { + // #i3820# If the spell checker flags numerical input as error, + // it still has to be treated as number, not EditEngine object. + if ( pActiveViewSh ) + { + ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocument(); + // #i67990# don't use pLastPattern in EnterHandler + const ScPatternAttr* pPattern = rDoc.GetPattern( aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab() ); + if (pPattern) + { + SvNumberFormatter* pFormatter = rDoc.GetFormatTable(); + // without conditional format, as in ScColumn::SetString + sal_uInt32 nFormat = pPattern->GetNumberFormat( pFormatter ); + double nVal; + if ( pFormatter->IsNumberFormat( aString, nFormat, nVal ) ) + { + bSpellErrors = false; // ignore the spelling errors + } + } + } + } + + // After RemoveAdjust, the EditView must not be repainted (has wrong font size etc). + // SetUpdateLayout must come after CompleteOnlineSpelling. + // The view is hidden in any case below (Broadcast). + mpEditEngine->SetUpdateLayout( false ); + + if ( bModified && !bForget ) // What is being entered (text/object)? + { + sal_Int32 nParCnt = mpEditEngine->GetParagraphCount(); + if ( nParCnt == 0 ) + nParCnt = 1; + + bool bUniformAttribs = true; + SfxItemSet aPara1Attribs = mpEditEngine->GetAttribs(0, 0, mpEditEngine->GetTextLen(0)); + for (sal_Int32 nPara = 1; nPara < nParCnt; ++nPara) + { + SfxItemSet aPara2Attribs = mpEditEngine->GetAttribs(nPara, 0, mpEditEngine->GetTextLen(nPara)); + if (!(aPara1Attribs == aPara2Attribs)) + { + // Paragraph format different from that of the 1st paragraph. + bUniformAttribs = false; + break; + } + } + + ESelection aSel( 0, 0, nParCnt-1, mpEditEngine->GetTextLen(nParCnt-1) ); + SfxItemSet aOldAttribs = mpEditEngine->GetAttribs( aSel ); + const SfxPoolItem* pItem = nullptr; + + // Find common (cell) attributes before RemoveAdjust + if ( pActiveViewSh && bUniformAttribs ) + { + std::optional<SfxItemSet> pCommonAttrs; + for (sal_uInt16 nId = EE_CHAR_START; nId <= EE_CHAR_END; nId++) + { + SfxItemState eState = aOldAttribs.GetItemState( nId, false, &pItem ); + if ( eState == SfxItemState::SET && + nId != EE_CHAR_ESCAPEMENT && nId != EE_CHAR_PAIRKERNING && + nId != EE_CHAR_KERNING && nId != EE_CHAR_XMLATTRIBS && + *pItem != pEditDefaults->Get(nId) ) + { + if ( !pCommonAttrs ) + pCommonAttrs.emplace( mpEditEngine->GetEmptyItemSet() ); + pCommonAttrs->Put( *pItem ); + } + } + + if ( pCommonAttrs ) + { + ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocument(); + pCellAttrs = std::make_unique<ScPatternAttr>(rDoc.GetPool()); + pCellAttrs->GetFromEditItemSet( &*pCommonAttrs ); + } + } + + // Clear ParaAttribs (including adjustment) + RemoveAdjust(); + + bool bAttrib = false; // Formatting present? + + // check if EditObject is needed + if (nParCnt > 1) + bAttrib = true; + else + { + for (sal_uInt16 nId = EE_CHAR_START; nId <= EE_CHAR_END && !bAttrib; nId++) + { + SfxItemState eState = aOldAttribs.GetItemState( nId, false, &pItem ); + if (eState == SfxItemState::DONTCARE) + bAttrib = true; + else if (eState == SfxItemState::SET) + { + // Keep same items in EditEngine as in ScEditAttrTester + if ( nId == EE_CHAR_ESCAPEMENT || nId == EE_CHAR_PAIRKERNING || + nId == EE_CHAR_KERNING || nId == EE_CHAR_XMLATTRIBS ) + { + if ( *pItem != pEditDefaults->Get(nId) ) + bAttrib = true; + } + } + } + + // Contains fields? + SfxItemState eFieldState = aOldAttribs.GetItemState( EE_FEATURE_FIELD, false ); + if ( eFieldState == SfxItemState::DONTCARE || eFieldState == SfxItemState::SET ) + bAttrib = true; + + // Not converted characters? + SfxItemState eConvState = aOldAttribs.GetItemState( EE_FEATURE_NOTCONV, false ); + if ( eConvState == SfxItemState::DONTCARE || eConvState == SfxItemState::SET ) + bAttrib = true; + + // Always recognize formulas as formulas + // We still need the preceding test due to cell attributes + } + + if (bSpellErrors) + mpEditEngine->GetAllMisspellRanges(aMisspellRanges); + + if (bMatrix) + bAttrib = false; + + if (bAttrib) + { + mpEditEngine->ClearSpellErrors(); + pObject = mpEditEngine->CreateTextObject(); + } + else if (SC_MOD()->GetAppOptions().GetAutoComplete()) // Adjust Upper/Lower case + { + // Perform case-matching only when the typed text is partial. + if (pColumnData && aAutoSearch.getLength() < aString.getLength()) + aString = getExactMatch(*pColumnData, aString); + } + } + + // Don't rely on ShowRefFrame switching the active view synchronously + // execute the function directly on the correct view's bindings instead + // pRefViewSh is reset in ShowRefFrame - get pointer before ShowRefFrame call + ScTabViewShell* pExecuteSh = pRefViewSh ? pRefViewSh : pActiveViewSh; + + if (bFormulaMode) + { + ShowRefFrame(); + + if (pExecuteSh) + { + pExecuteSh->SetTabNo(aCursorPos.Tab()); + pExecuteSh->ActiveGrabFocus(); + } + + bFormulaMode = false; + pSfxApp->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) ); + SC_MOD()->SetRefInputHdl(nullptr); + if (pInputWin) + pInputWin->SetFormulaMode(false); + UpdateAutoCorrFlag(); + } + pRefViewSh = nullptr; // Also without FormulaMode due to FunctionsAutoPilot + DeleteRangeFinder(); + ResetAutoPar(); + + bool bOldMod = bModified; + + bModified = false; + bSelIsRef = false; + eMode = SC_INPUT_NONE; + StopInputWinEngine(true); + + // Text input (through number formats) or ApplySelectionPattern modify + // the cell's attributes, so pLastPattern is no longer valid + pLastPattern = nullptr; + + if (bOldMod && !bProtected && !bForget) + { + bool bInsertPreCorrectedString = true; + // No typographic quotes in formulas + if (aString.startsWith("=")) + { + SvxAutoCorrect* pAuto = SvxAutoCorrCfg::Get().GetAutoCorrect(); + if ( pAuto ) + { + bInsertPreCorrectedString = false; + OUString aReplace(pAuto->GetStartDoubleQuote()); + if( aReplace.isEmpty() ) + aReplace = ScGlobal::getLocaleData().getDoubleQuotationMarkStart(); + if( aReplace != "\"" ) + aString = aString.replaceAll( aReplace, "\"" ); + + aReplace = OUString(pAuto->GetEndDoubleQuote()); + if( aReplace.isEmpty() ) + aReplace = ScGlobal::getLocaleData().getDoubleQuotationMarkEnd(); + if( aReplace != "\"" ) + aString = aString.replaceAll( aReplace, "\"" ); + + aReplace = OUString(pAuto->GetStartSingleQuote()); + if( aReplace.isEmpty() ) + aReplace = ScGlobal::getLocaleData().getQuotationMarkStart(); + if( aReplace != "'" ) + aString = aString.replaceAll( aReplace, "'" ); + + aReplace = OUString(pAuto->GetEndSingleQuote()); + if( aReplace.isEmpty() ) + aReplace = ScGlobal::getLocaleData().getQuotationMarkEnd(); + if( aReplace != "'" ) + aString = aString.replaceAll( aReplace, "'"); + } + } + + pSfxApp->Broadcast( SfxHint( SfxHintId::ScKillEditViewNoPaint ) ); + + if ( pExecuteSh ) + { + SfxBindings& rBindings = pExecuteSh->GetViewFrame()->GetBindings(); + + sal_uInt16 nId = FID_INPUTLINE_ENTER; + if ( nBlockMode == ScEnterMode::BLOCK ) + nId = FID_INPUTLINE_BLOCK; + else if ( nBlockMode == ScEnterMode::MATRIX ) + nId = FID_INPUTLINE_MATRIX; + + const SfxPoolItem* aArgs[2]; + aArgs[1] = nullptr; + + if ( bInsertPreCorrectedString && aString != aPreAutoCorrectString ) + { + ScInputStatusItem aItem(FID_INPUTLINE_STATUS, + aCursorPos, aCursorPos, aCursorPos, + aPreAutoCorrectString, pObject.get()); + aArgs[0] = &aItem; + rBindings.Execute(nId, aArgs); + } + + ScInputStatusItem aItemCorrected(FID_INPUTLINE_STATUS, + aCursorPos, aCursorPos, aCursorPos, + aString, pObject.get()); + if ( !aMisspellRanges.empty() ) + aItemCorrected.SetMisspellRanges(&aMisspellRanges); + + aArgs[0] = &aItemCorrected; + rBindings.Execute(nId, aArgs); + } + + pLastState.reset(); // pLastState still contains the old text + } + else + pSfxApp->Broadcast( SfxHint( SfxHintId::ScKillEditView ) ); + + if ( bOldMod && pExecuteSh && pCellAttrs && !bForget ) + { + // Combine with input? + pExecuteSh->ApplySelectionPattern( *pCellAttrs, true ); + pExecuteSh->AdjustBlockHeight(); + } + + HideTip(); + HideTipBelow(); + + nFormSelStart = nFormSelEnd = 0; + aFormText.clear(); + + mbEditingExistingContent = false; + bInOwnChange = false; + bInEnterHandler = false; + if (bUpdateLayout) + mpEditEngine->SetUpdateLayout( true ); +} + +void ScInputHandler::CancelHandler() +{ + bInOwnChange = true; // Also without FormulaMode due to FunctionsAutoPilot + + ImplCreateEditEngine(); + + bModified = false; + mbPartialPrefix = false; + mbEditingExistingContent = false; + + // Don't rely on ShowRefFrame switching the active view synchronously + // execute the function directly on the correct view's bindings instead + // pRefViewSh is reset in ShowRefFrame - get pointer before ShowRefFrame call + ScTabViewShell* pExecuteSh = pRefViewSh ? pRefViewSh : pActiveViewSh; + + if (bFormulaMode) + { + ShowRefFrame(); + if (pExecuteSh) + { + pExecuteSh->SetTabNo(aCursorPos.Tab()); + pExecuteSh->ActiveGrabFocus(); + } + bFormulaMode = false; + SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) ); + SC_MOD()->SetRefInputHdl(nullptr); + if (pInputWin) + pInputWin->SetFormulaMode(false); + UpdateAutoCorrFlag(); + } + pRefViewSh = nullptr; // Also without FormulaMode due to FunctionsAutoPilot + DeleteRangeFinder(); + ResetAutoPar(); + + eMode = SC_INPUT_NONE; + StopInputWinEngine( true ); + if (pExecuteSh) + pExecuteSh->StopEditShell(); + + aCursorPos.Set(pExecuteSh->GetViewData().GetDocument().MaxCol()+1,0,0); // Invalid flag + mpEditEngine->SetTextCurrentDefaults(OUString()); + + if ( !pLastState && pExecuteSh ) + pExecuteSh->UpdateInputHandler( true ); // Update status again + else + NotifyChange( pLastState.get(), true ); + + nFormSelStart = nFormSelEnd = 0; + aFormText.clear(); + + bInOwnChange = false; + + if ( comphelper::LibreOfficeKit::isActive() && pExecuteSh ) + { + // Clear + std::vector<ReferenceMark> aReferenceMarks; + ScInputHandler::SendReferenceMarks( pActiveViewSh, aReferenceMarks ); + } +} + +bool ScInputHandler::IsModalMode( const SfxObjectShell* pDocSh ) +{ + // References to unnamed document; that doesn't work + return bFormulaMode && pRefViewSh + && pRefViewSh->GetViewData().GetDocument().GetDocumentShell() != pDocSh + && !pDocSh->HasName(); +} + +void ScInputHandler::AddRefEntry() +{ + const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep); + UpdateActiveView(); + if (!pTableView && !pTopView) + return; // E.g. FillMode + + DataChanging(); // Cannot be new + + RemoveSelection(); + OUString aText = GetEditText(mpEditEngine.get()); + sal_Unicode cLastChar = 0; + sal_Int32 nPos = aText.getLength() - 1; + while (nPos >= 0) //checking space + { + cLastChar = aText[nPos]; + if (cLastChar != ' ') + break; + --nPos; + } + + bool bAppendSeparator = (cLastChar != '(' && cLastChar != cSep && cLastChar != '='); + if (bAppendSeparator) + { + if (pTableView) + pTableView->InsertText( OUString(cSep) ); + if (pTopView) + pTopView->InsertText( OUString(cSep) ); + } + + DataChanged(); +} + +void ScInputHandler::SetReference( const ScRange& rRef, const ScDocument& rDoc ) +{ + HideTip(); + + const ScDocument* pThisDoc = nullptr; + if (pRefViewSh) + pThisDoc = &pRefViewSh->GetViewData().GetDocument(); + bool bOtherDoc = (pThisDoc != &rDoc); + if (bOtherDoc && !rDoc.GetDocumentShell()->HasName()) + { + // References to unnamed document; that doesn't work + // SetReference should not be called, then + return; + } + if (!pThisDoc) + pThisDoc = &rDoc; + + UpdateActiveView(); + if (!pTableView && !pTopView) + return; // E.g. FillMode + + // Never overwrite the "="! + EditView* pActiveView = pTopView ? pTopView : pTableView; + ESelection aSel = pActiveView->GetSelection(); + aSel.Adjust(); + if ( aSel.nStartPara == 0 && aSel.nStartPos == 0 ) + return; + + DataChanging(); // Cannot be new + + // Turn around selection if backwards. + if (pTableView) + { + ESelection aTabSel = pTableView->GetSelection(); + if (aTabSel.nStartPos > aTabSel.nEndPos && aTabSel.nStartPara == aTabSel.nEndPara) + { + aTabSel.Adjust(); + pTableView->SetSelection(aTabSel); + } + } + if (pTopView) + { + ESelection aTopSel = pTopView->GetSelection(); + if (aTopSel.nStartPos > aTopSel.nEndPos && aTopSel.nStartPara == aTopSel.nEndPara) + { + aTopSel.Adjust(); + pTopView->SetSelection(aTopSel); + } + } + + // Create string from reference, in the syntax of the document being edited. + OUString aRefStr; + const ScAddress::Details aAddrDetails( *pThisDoc, aCursorPos ); + if (bOtherDoc) + { + // Reference to other document + OSL_ENSURE(rRef.aStart.Tab()==rRef.aEnd.Tab(), "nStartTab!=nEndTab"); + + // Always 3D and absolute. + OUString aTmp(rRef.Format(rDoc, ScRefFlags::VALID | ScRefFlags::TAB_ABS_3D, aAddrDetails)); + + SfxObjectShell* pObjSh = rDoc.GetDocumentShell(); + // #i75893# convert escaped URL of the document to something user friendly + OUString aFileName = pObjSh->GetMedium()->GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::Unambiguous ); + + switch(aAddrDetails.eConv) + { + case formula::FormulaGrammar::CONV_XL_A1 : + case formula::FormulaGrammar::CONV_XL_OOX : + case formula::FormulaGrammar::CONV_XL_R1C1 : + aRefStr = "[\'" + aFileName + "']"; + break; + case formula::FormulaGrammar::CONV_OOO : + default: + aRefStr = "\'" + aFileName + "'#"; + break; + } + aRefStr += aTmp; + } + else + { + if ( rRef.aStart.Tab() != aCursorPos.Tab() || + rRef.aStart.Tab() != rRef.aEnd.Tab() ) + // pointer-selected => absolute sheet reference + aRefStr = rRef.Format(rDoc, ScRefFlags::VALID | ScRefFlags::TAB_ABS_3D, aAddrDetails); + else + aRefStr = rRef.Format(rDoc, ScRefFlags::VALID, aAddrDetails); + } + bool bLOKShowSelect = true; + if(comphelper::LibreOfficeKit::isActive() && pRefViewSh->GetViewData().GetRefTabNo() != pRefViewSh->GetViewData().GetTabNo()) + bLOKShowSelect = false; + + if (pTableView || pTopView) + { + if (pTableView) + pTableView->InsertText( aRefStr, true, bLOKShowSelect ); + if (pTopView) + pTopView->InsertText( aRefStr, true, bLOKShowSelect ); + + DataChanged(); + } + + bSelIsRef = true; +} + +void ScInputHandler::InsertFunction( const OUString& rFuncName, bool bAddPar ) +{ + if ( eMode == SC_INPUT_NONE ) + { + OSL_FAIL("InsertFunction, not during input mode"); + return; + } + + UpdateActiveView(); + if (!pTableView && !pTopView) + return; // E.g. FillMode + + DataChanging(); // Cannot be new + + OUString aText = rFuncName; + if (bAddPar) + aText += "()"; + + if (pTableView) + { + pTableView->InsertText( aText ); + if (bAddPar) + { + ESelection aSel = pTableView->GetSelection(); + --aSel.nStartPos; + --aSel.nEndPos; + pTableView->SetSelection(aSel); + } + } + if (pTopView) + { + pTopView->InsertText( aText ); + if (bAddPar) + { + ESelection aSel = pTopView->GetSelection(); + --aSel.nStartPos; + --aSel.nEndPos; + pTopView->SetSelection(aSel); + } + } + + DataChanged(); + + if (bAddPar) + AutoParAdded(); +} + +void ScInputHandler::ClearText() +{ + if ( eMode == SC_INPUT_NONE ) + { + OSL_FAIL("ClearText, not during input mode"); + return; + } + + UpdateActiveView(); + if (!pTableView && !pTopView) + return; // E.g. FillMode + + DataChanging(); // Cannot be new + + if (pTableView) + { + pTableView->GetEditEngine()->SetText( "" ); + pTableView->SetSelection( ESelection(0,0, 0,0) ); + } + if (pTopView) + { + pTopView->GetEditEngine()->SetText( "" ); + pTopView->SetSelection( ESelection(0,0, 0,0) ); + } + + DataChanged(); +} + +bool ScInputHandler::KeyInput( const KeyEvent& rKEvt, bool bStartEdit /* = false */ ) +{ + vcl::KeyCode aCode = rKEvt.GetKeyCode(); + sal_uInt16 nModi = aCode.GetModifier(); + bool bShift = aCode.IsShift(); + bool bControl = aCode.IsMod1(); + bool bAlt = aCode.IsMod2(); + sal_uInt16 nCode = aCode.GetCode(); + sal_Unicode nChar = rKEvt.GetCharCode(); + + if (bAlt && !bControl && nCode != KEY_RETURN) + // Alt-Return and Alt-Ctrl-* are accepted. Everything else with ALT are not. + return false; + + // There is a partial autocomplete suggestion. + // Allow its completion with right arrow key (without modifiers). + if (mbPartialPrefix && nCode == KEY_RIGHT && !bControl && !bShift && !bAlt && + (pTopView || pTableView)) + { + if (pTopView) + pTopView->PostKeyEvent(KeyEvent(0, css::awt::Key::MOVE_TO_END_OF_PARAGRAPH)); + if (pTableView) + pTableView->PostKeyEvent(KeyEvent(0, css::awt::Key::MOVE_TO_END_OF_PARAGRAPH)); + + mbPartialPrefix = false; + + // Indicate that this event has been consumed and ScTabViewShell should not act on this. + return true; + } + + if (!bControl && nCode == KEY_TAB) + { + // Normal TAB moves the cursor right. + EnterHandler(); + + if (pActiveViewSh) + pActiveViewSh->FindNextUnprot( bShift, true ); + return true; + } + + bool bInputLine = ( eMode==SC_INPUT_TOP ); + + bool bUsed = false; + bool bSkip = false; + bool bDoEnter = false; + + switch ( nCode ) + { + case KEY_RETURN: + // New line when in the input line and Shift/Ctrl-Enter is pressed, + // or when in a cell and Ctrl-Enter is pressed. + if ((pInputWin && bInputLine && bControl != bShift) || (!bInputLine && bControl && !bShift)) + { + bDoEnter = true; + } + else if (nModi == 0 && nTipVisible && pFormulaData && miAutoPosFormula != pFormulaData->end()) + { + PasteFunctionData(); + bUsed = true; + } + else if ( nModi == 0 && nTipVisible && !aManualTip.isEmpty() ) + { + PasteManualTip(); + bUsed = true; + } + else + { + ScEnterMode nMode = ScEnterMode::NORMAL; + if ( bShift && bControl ) + nMode = ScEnterMode::MATRIX; + else if ( bAlt ) + nMode = ScEnterMode::BLOCK; + EnterHandler( nMode ); + + if (pActiveViewSh) + pActiveViewSh->MoveCursorEnter( bShift && !bControl ); + + bUsed = true; + } + break; + case KEY_TAB: + if (bControl && !bAlt) + { + if (pFormulaData && nTipVisible && miAutoPosFormula != pFormulaData->end()) + { + // Iterate + NextFormulaEntry( bShift ); + bUsed = true; + } + else if (pColumnData && bUseTab) + { + // Iterate through AutoInput entries + NextAutoEntry( bShift ); + bUsed = true; + } + } + break; + case KEY_ESCAPE: + if ( nTipVisible ) + { + HideTip(); + bUsed = true; + } + else if( nTipVisibleSec ) + { + HideTipBelow(); + bUsed = true; + } + else if (eMode != SC_INPUT_NONE) + { + CancelHandler(); + bUsed = true; + } + else + bSkip = true; + break; + case KEY_F2: + if ( !bShift && !bControl && !bAlt && eMode == SC_INPUT_TABLE ) + { + eMode = SC_INPUT_TYPE; + bUsed = true; + } + break; + } + + // Only execute cursor keys if already in EditMode + // E.g. due to Shift-Ctrl-PageDn (not defined as an accelerator) + bool bCursorKey = EditEngine::DoesKeyMoveCursor(rKEvt); + bool bInsKey = ( nCode == KEY_INSERT && !nModi ); // Treat Insert like Cursorkeys + if ( !bUsed && !bSkip && ( bDoEnter || EditEngine::DoesKeyChangeText(rKEvt) || + ( eMode != SC_INPUT_NONE && ( bCursorKey || bInsKey ) ) ) ) + { + HideTip(); + HideTipBelow(); + + if (bSelIsRef) + { + RemoveSelection(); + bSelIsRef = false; + } + + UpdateActiveView(); + bool bNewView = DataChanging( nChar ); + + if (bProtected) // Protected cell? + bUsed = true; // Don't forward KeyEvent + else // Changes allowed + { + if (bNewView ) // Create anew + { + if (pActiveViewSh) + pActiveViewSh->GetViewData().GetDocShell()->PostEditView( mpEditEngine.get(), aCursorPos ); + UpdateActiveView(); + if (eMode==SC_INPUT_NONE) + if (pTableView || pTopView) + { + OUString aStrLoP; + + if (bStartEdit && nCellPercentFormatDecSep != 0 && + ((nChar >= '0' && nChar <= '9') || nChar == '-' || nChar == nCellPercentFormatDecSep)) + { + aStrLoP = "%"; + } + + if (pTableView) + { + pTableView->GetEditEngine()->SetText( aStrLoP ); + if ( !aStrLoP.isEmpty() ) + pTableView->SetSelection( ESelection(0,0, 0,0) ); // before the '%' + + // Don't call SetSelection if the string is empty anyway, + // to avoid breaking the bInitial handling in ScViewData::EditGrowY + } + if (pTopView) + { + pTopView->GetEditEngine()->SetText( aStrLoP ); + if ( !aStrLoP.isEmpty() ) + pTopView->SetSelection( ESelection(0,0, 0,0) ); // before the '%' + } + } + SyncViews(); + } + + if (pTableView || pTopView) + { + if (bDoEnter) + { + if (pTableView) + if( pTableView->PostKeyEvent( KeyEvent( '\r', vcl::KeyCode(KEY_RETURN) ) ) ) + bUsed = true; + if (pTopView) + if( pTopView->PostKeyEvent( KeyEvent( '\r', vcl::KeyCode(KEY_RETURN) ) ) ) + bUsed = true; + } + else if ( nAutoPar && nChar == ')' && CursorAtClosingPar() ) + { + SkipClosingPar(); + bUsed = true; + } + else + { + if (pTableView) + { + if (pTopView) + pTableView->SetControlWord(pTableView->GetControlWord() | EVControlBits::SINGLELINEPASTE); + + vcl::Window* pFrameWin = pActiveViewSh ? pActiveViewSh->GetFrameWin() : nullptr; + if ( pTableView->PostKeyEvent( rKEvt, pFrameWin ) ) + bUsed = true; + + pTableView->SetControlWord(pTableView->GetControlWord() & ~EVControlBits::SINGLELINEPASTE); + } + if (pTopView) + { + if ( bUsed && rKEvt.GetKeyCode().GetFunction() == KeyFuncType::CUT ) + pTopView->DeleteSelected(); + else if ( pTopView->PostKeyEvent( rKEvt ) ) + bUsed = true; + } + } + + // AutoInput: + if ( bUsed && SC_MOD()->GetAppOptions().GetAutoComplete() ) + { + bUseTab = false; + if (pFormulaData) + miAutoPosFormula = pFormulaData->end(); // do not search further + if (pColumnData) + miAutoPosColumn = pColumnData->end(); + + KeyFuncType eFunc = rKEvt.GetKeyCode().GetFunction(); + if ( nChar && nChar != 8 && nChar != 127 && // no 'backspace', no 'delete' + KeyFuncType::CUT != eFunc) // and no 'CTRL-X' + { + if (bFormulaMode) + UseFormulaData(); + else + UseColData(); + } + } + + // When the selection is changed manually or an opening parenthesis + // is typed, stop overwriting parentheses + if ( bUsed && nChar == '(' ) + ResetAutoPar(); + + if ( KEY_INSERT == nCode ) + { + SfxViewFrame* pViewFrm = SfxViewFrame::Current(); + if (pViewFrm) + pViewFrm->GetBindings().Invalidate( SID_ATTR_INSERT ); + } + if( bUsed && bFormulaMode && ( bCursorKey || bInsKey || nCode == KEY_DELETE || nCode == KEY_BACKSPACE ) ) + { + ShowTipCursor(); + } + if( bUsed && bFormulaMode && nCode == KEY_BACKSPACE ) + { + UseFormulaData(); + } + + } + + // #i114511# don't count cursor keys as modification + bool bSetModified = !bCursorKey; + DataChanged(false, bSetModified); // also calls UpdateParenthesis() + + // In the LOK case, we want to set the document modified state + // right away at the start of the edit, so that the content is + // saved even when the user leaves the document before hitting + // Enter + if (comphelper::LibreOfficeKit::isActive() && bSetModified && pActiveViewSh && !pActiveViewSh->GetViewData().GetDocShell()->IsModified()) + pActiveViewSh->GetViewData().GetDocShell()->SetModified(); + + InvalidateAttribs(); //! in DataChanged? + } + } + + if (pTopView && eMode != SC_INPUT_NONE) + SyncViews(); + + return bUsed; +} + +OUString ScInputHandler::GetSurroundingText() +{ + if (eMode != SC_INPUT_NONE) + { + UpdateActiveView(); + if (pTableView || pTopView) + { + if (pTableView) + return pTableView->GetSurroundingText(); + else if (pTopView) // call only once + return pTopView->GetSurroundingText(); + } + } + return OUString(); +} + +Selection ScInputHandler::GetSurroundingTextSelection() +{ + if (eMode != SC_INPUT_NONE) + { + UpdateActiveView(); + if (pTableView || pTopView) + { + if (pTableView) + return pTableView->GetSurroundingTextSelection(); + else if (pTopView) // call only once + return pTopView->GetSurroundingTextSelection(); + } + } + return Selection(0, 0); +} + +bool ScInputHandler::DeleteSurroundingText(const Selection& rSelection) +{ + if (eMode != SC_INPUT_NONE) + { + UpdateActiveView(); + if (pTableView || pTopView) + { + if (pTableView) + return pTableView->DeleteSurroundingText(rSelection); + else if (pTopView) // call only once + return pTopView->DeleteSurroundingText(rSelection); + } + } + return false; +} + +void ScInputHandler::InputCommand( const CommandEvent& rCEvt ) +{ + if ( rCEvt.GetCommand() == CommandEventId::CursorPos ) + { + // For CommandEventId::CursorPos, do as little as possible, because + // with remote VCL, even a ShowCursor will generate another event. + if ( eMode != SC_INPUT_NONE ) + { + UpdateActiveView(); + if (pTableView || pTopView) + { + if (pTableView) + pTableView->Command( rCEvt ); + else if (pTopView) // call only once + pTopView->Command( rCEvt ); + } + } + } + else if ( rCEvt.GetCommand() == CommandEventId::QueryCharPosition ) + { + if ( eMode != SC_INPUT_NONE ) + { + UpdateActiveView(); + if (pTableView || pTopView) + { + if (pTableView) + pTableView->Command( rCEvt ); + else if (pTopView) // call only once + pTopView->Command( rCEvt ); + } + } + } + else + { + HideTip(); + HideTipBelow(); + + if ( bSelIsRef ) + { + RemoveSelection(); + bSelIsRef = false; + } + + UpdateActiveView(); + bool bNewView = DataChanging( 0, true ); + + if (!bProtected) // changes allowed + { + if (bNewView) // create new edit view + { + if (pActiveViewSh) + pActiveViewSh->GetViewData().GetDocShell()->PostEditView( mpEditEngine.get(), aCursorPos ); + UpdateActiveView(); + if (eMode==SC_INPUT_NONE) + if (pTableView || pTopView) + { + if (pTableView) + { + pTableView->GetEditEngine()->SetText( "" ); + pTableView->SetSelection( ESelection(0,0, 0,0) ); + } + if (pTopView) + { + pTopView->GetEditEngine()->SetText( "" ); + pTopView->SetSelection( ESelection(0,0, 0,0) ); + } + } + SyncViews(); + } + + if (pTableView || pTopView) + { + if (pTableView) + pTableView->Command( rCEvt ); + if (pTopView && !comphelper::LibreOfficeKit::isActive()) + pTopView->Command( rCEvt ); + + if ( rCEvt.GetCommand() == CommandEventId::EndExtTextInput ) + { + // AutoInput after ext text input + + if (pFormulaData) + miAutoPosFormula = pFormulaData->end(); + if (pColumnData) + miAutoPosColumn = pColumnData->end(); + + if (bFormulaMode) + UseFormulaData(); + else + UseColData(); + } + } + + DataChanged(); // calls UpdateParenthesis() + InvalidateAttribs(); //! in DataChanged ? + } + + if (pTopView && eMode != SC_INPUT_NONE) + SyncViews(); + } +} + +void ScInputHandler::NotifyChange( const ScInputHdlState* pState, + bool bForce, ScTabViewShell* pSourceSh, + bool bStopEditing) +{ + // If the call originates from a macro call in the EnterHandler, + // return immediately and don't mess up the status + if (bInEnterHandler) + return; + + bool bRepeat = (pState == pLastState.get()); + if (!bRepeat && pState && pLastState) + bRepeat = (*pState == *pLastState); + if (bRepeat && !bForce) + return; + + bInOwnChange = true; // disable ModifyHdl (reset below) + + if ( pState && !pLastState ) // Enable again + bForce = true; + + bool bHadObject = pLastState && pLastState->GetEditData(); + + //! Before EditEngine gets eventually created (so it gets the right pools) + if ( pSourceSh ) + pActiveViewSh = pSourceSh; + else + pActiveViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() ); + + ImplCreateEditEngine(); + + if ( pState != pLastState.get() ) + { + pLastState.reset( pState ? new ScInputHdlState( *pState ) : nullptr); + } + + if ( pState && pActiveViewSh ) + { + ScModule* pScMod = SC_MOD(); + + ScTabViewShell* pScTabViewShell = dynamic_cast<ScTabViewShell*>(pScMod->GetViewShell()); + + // Also take foreign reference input into account here (e.g. FunctionsAutoPilot), + // FormEditData, if we're switching from Help to Calc: + if ( !bFormulaMode && !pScMod->IsFormulaMode() && + ( !pScTabViewShell || !pScTabViewShell->GetFormEditData() ) ) + { + bool bIgnore = false; + if ( bModified ) + { + if (pState->GetPos() != aCursorPos) + { + if (!bProtected) + EnterHandler(); + } + else + bIgnore = true; + } + + if ( !bIgnore ) + { + const ScAddress& rSPos = pState->GetStartPos(); + const ScAddress& rEPos = pState->GetEndPos(); + const EditTextObject* pData = pState->GetEditData(); + OUString aString = pState->GetString(); + bool bTxtMod = false; + ScDocShell* pDocSh = pActiveViewSh->GetViewData().GetDocShell(); + ScDocument& rDoc = pDocSh->GetDocument(); + + aCursorPos = pState->GetPos(); + + if ( pData ) + bTxtMod = true; + else if ( bHadObject ) + bTxtMod = true; + else if ( bTextValid ) + bTxtMod = ( aString != aCurrentText ); + else + bTxtMod = ( aString != GetEditText(mpEditEngine.get()) ); + + if ( bTxtMod || bForce ) + { + if (pData) + { + mpEditEngine->SetTextCurrentDefaults( *pData ); + if (pInputWin) + aString = ScEditUtil::GetMultilineString(*mpEditEngine); + else + aString = GetEditText(mpEditEngine.get()); + lcl_RemoveTabs(aString); + bTextValid = false; + aCurrentText.clear(); + } + else + { + aCurrentText = aString; + bTextValid = true; //! To begin with remember as a string + } + + if ( pInputWin ) + pInputWin->SetTextString(aString); + + if (comphelper::LibreOfficeKit::isActive() && pActiveViewSh) + { + EditView* pActiveView = pTopView ? pTopView : pTableView; + ESelection aSel = pActiveView ? pActiveView->GetSelection() : ESelection(); + ScInputHandler::LOKSendFormulabarUpdate(pActiveViewSh, aString, aSel); + // TODO: deprecated? + pActiveViewSh->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_FORMULA, aString.toUtf8().getStr()); + } + } + + if ( pInputWin || comphelper::LibreOfficeKit::isActive()) // Named range input + { + OUString aPosStr; + bool bSheetLocal = false; + const ScAddress::Details aAddrDetails( rDoc, aCursorPos ); + + // Is the range a name? + //! Find by Timer? + if ( pActiveViewSh ) + pActiveViewSh->GetViewData().GetDocument(). + GetRangeAtBlock( ScRange( rSPos, rEPos ), aPosStr, &bSheetLocal); + + if ( aPosStr.isEmpty() ) // Not a name -> format + { + ScRefFlags nFlags = ScRefFlags::ZERO; + if( aAddrDetails.eConv == formula::FormulaGrammar::CONV_XL_R1C1 ) + nFlags |= ScRefFlags::COL_ABS | ScRefFlags::ROW_ABS; + if ( rSPos != rEPos ) + { + ScRange r(rSPos, rEPos); + applyStartToEndFlags(nFlags); + aPosStr = r.Format(rDoc, ScRefFlags::VALID | nFlags, aAddrDetails); + } + else + aPosStr = aCursorPos.Format(ScRefFlags::VALID | nFlags, &rDoc, aAddrDetails); + } + else if (bSheetLocal) + { + OUString aName; + if (rDoc.GetName( rSPos.Tab(), aName)) + aPosStr = ScPosWnd::createLocalRangeName( aPosStr, aName); + } + + if (pInputWin) + { + // Disable the accessible VALUE_CHANGE event + bool bIsSuppressed = pInputWin->IsAccessibilityEventsSuppressed(false); + pInputWin->SetAccessibilityEventsSuppressed(true); + pInputWin->SetPosString(aPosStr); + pInputWin->SetAccessibilityEventsSuppressed(bIsSuppressed); + pInputWin->SetSumAssignMode(); + } + + if (comphelper::LibreOfficeKit::isActive() && pActiveViewSh) + pActiveViewSh->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_ADDRESS, aPosStr.toUtf8().getStr()); + } + + if (bStopEditing) { + SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScKillEditView ) ); + + // As long as the content is not edited, turn off online spelling. + // Online spelling is turned back on in StartTable, after setting + // the right language from cell attributes. + + EEControlBits nCntrl = mpEditEngine->GetControlWord(); + if ( nCntrl & EEControlBits::ONLINESPELLING ) + mpEditEngine->SetControlWord( nCntrl & ~EEControlBits::ONLINESPELLING ); + } + + bModified = false; + bSelIsRef = false; + bProtected = false; + bCommandErrorShown = false; + } + } + + if ( pInputWin) + { + // Do not enable if RefDialog is open + if(!pScMod->IsFormulaMode()&& !pScMod->IsRefDialogOpen()) + { + if ( !pInputWin->IsEnabled()) + { + pDelayTimer->Stop(); + pInputWin->Enable(); + } + } + else if(pScMod->IsRefDialogOpen()) + { // Because every document has its own InputWin, + // we should start Timer again, because the input line may + // still be active + if ( !pDelayTimer->IsActive() ) + pDelayTimer->Start(); + } + } + } + else // !pState || !pActiveViewSh + { + if ( !pDelayTimer->IsActive() ) + pDelayTimer->Start(); + } + + HideTip(); + HideTipBelow(); + bInOwnChange = false; +} + +void ScInputHandler::UpdateCellAdjust( SvxCellHorJustify eJust ) +{ + eAttrAdjust = eJust; + UpdateAdjust( 0 ); +} + +void ScInputHandler::ResetDelayTimer() +{ + if( pDelayTimer->IsActive() ) + { + pDelayTimer->Stop(); + if ( pInputWin ) + pInputWin->Enable(); + } +} + +IMPL_LINK_NOARG( ScInputHandler, DelayTimer, Timer*, void ) +{ + if ( !(nullptr == pLastState || SC_MOD()->IsFormulaMode() || SC_MOD()->IsRefDialogOpen())) + return; + + //! New method at ScModule to query if function autopilot is open + SfxViewFrame* pViewFrm = SfxViewFrame::Current(); + if ( pViewFrm && pViewFrm->GetChildWindow( SID_OPENDLG_FUNCTION ) ) + { + if ( pInputWin) + { + pInputWin->EnableButtons( false ); + pInputWin->Disable(); + } + } + else if ( !bFormulaMode ) // Keep formula e.g. for help + { + bInOwnChange = true; // disable ModifyHdl (reset below) + + pActiveViewSh = nullptr; + mpEditEngine->SetTextCurrentDefaults( OUString() ); + if ( pInputWin ) + { + pInputWin->SetPosString( OUString() ); + pInputWin->SetTextString( OUString() ); + pInputWin->Disable(); + } + + bInOwnChange = false; + } +} + +void ScInputHandler::InputSelection( const EditView* pView ) +{ + SyncViews( pView ); + ShowTipCursor(); + UpdateParenthesis(); // Selection changed -> update parentheses highlighting + + // When the selection is changed manually, stop overwriting parentheses + ResetAutoPar(); + + if (comphelper::LibreOfficeKit::isActive() && pActiveViewSh) + { + EditView* pActiveView = pTopView ? pTopView : pTableView; + ESelection aSel = pActiveView ? pActiveView->GetSelection() : ESelection(); + ScInputHandler::LOKSendFormulabarUpdate(pActiveViewSh, GetEditString(), aSel); + } +} + +void ScInputHandler::InputChanged( const EditView* pView, bool bFromNotify ) +{ + if ( !pView ) + return; + + UpdateActiveView(); + + // #i20282# DataChanged needs to know if this is from the input line's modify handler + bool bFromTopNotify = ( bFromNotify && pView == pTopView ); + + bool bNewView = DataChanging(); //FIXME: Is this at all possible? + aCurrentText = pView->GetEditEngine()->GetText(); // Also remember the string + mpEditEngine->SetTextCurrentDefaults( aCurrentText ); + DataChanged( bFromTopNotify ); + bTextValid = true; // Is set to false in DataChanged + + if ( pActiveViewSh ) + { + ScViewData& rViewData = pActiveViewSh->GetViewData(); + if ( bNewView ) + rViewData.GetDocShell()->PostEditView( mpEditEngine.get(), aCursorPos ); + + rViewData.EditGrowY(); + rViewData.EditGrowX(); + } + + SyncViews( pView ); +} + +const OUString& ScInputHandler::GetEditString() +{ + if (mpEditEngine) + { + aCurrentText = mpEditEngine->GetText(); // Always new from Engine + bTextValid = true; + } + + return aCurrentText; +} + +Size ScInputHandler::GetTextSize() +{ + Size aSize; + if ( mpEditEngine ) + aSize = Size( mpEditEngine->CalcTextWidth(), mpEditEngine->GetTextHeight() ); + + return aSize; +} + +bool ScInputHandler::GetTextAndFields( ScEditEngineDefaulter& rDestEngine ) +{ + bool bRet = false; + if (mpEditEngine) + { + // Contains field? + sal_Int32 nParCnt = mpEditEngine->GetParagraphCount(); + SfxItemSet aSet = mpEditEngine->GetAttribs( ESelection(0,0,nParCnt,0) ); + SfxItemState eFieldState = aSet.GetItemState( EE_FEATURE_FIELD, false ); + if ( eFieldState == SfxItemState::DONTCARE || eFieldState == SfxItemState::SET ) + { + // Copy content + std::unique_ptr<EditTextObject> pObj = mpEditEngine->CreateTextObject(); + rDestEngine.SetTextCurrentDefaults(*pObj); + pObj.reset(); + + // Delete attributes + for (sal_Int32 i=0; i<nParCnt; i++) + rDestEngine.RemoveCharAttribs( i ); + + // Combine paragraphs + while ( nParCnt > 1 ) + { + sal_Int32 nLen = rDestEngine.GetTextLen( 0 ); + ESelection aSel( 0,nLen, 1,0 ); + rDestEngine.QuickInsertText( OUString(' '), aSel ); // Replace line break with space + --nParCnt; + } + + bRet = true; + } + } + return bRet; +} + +/** + * Methods for FunctionAutoPilot: + * InputGetSelection, InputSetSelection, InputReplaceSelection, InputGetFormulaStr + */ +void ScInputHandler::InputGetSelection( sal_Int32& rStart, sal_Int32& rEnd ) +{ + rStart = nFormSelStart; + rEnd = nFormSelEnd; +} + +EditView* ScInputHandler::GetFuncEditView() +{ + UpdateActiveView(); // Due to pTableView + + EditView* pView = nullptr; + if ( pInputWin ) + { + pInputWin->MakeDialogEditView(); + pView = pInputWin->GetEditView(); + } + else + { + if ( eMode != SC_INPUT_TABLE ) + { + bCreatingFuncView = true; // Don't display RangeFinder + SetMode( SC_INPUT_TABLE ); + bCreatingFuncView = false; + if ( pTableView ) + pTableView->GetEditEngine()->SetText( OUString() ); + } + pView = pTableView; + } + + return pView; +} + +void ScInputHandler::InputSetSelection( sal_Int32 nStart, sal_Int32 nEnd ) +{ + if ( nStart <= nEnd ) + { + nFormSelStart = nStart; + nFormSelEnd = nEnd; + } + else + { + nFormSelEnd = nStart; + nFormSelStart = nEnd; + } + + EditView* pView = GetFuncEditView(); + if (pView) + pView->SetSelection( ESelection(0,nStart, 0,nEnd) ); + + bModified = true; +} + +void ScInputHandler::InputReplaceSelection( const OUString& rStr ) +{ + if (!pRefViewSh) + pRefViewSh = pActiveViewSh; + + OSL_ENSURE(nFormSelEnd>=nFormSelStart,"Selection broken..."); + + sal_Int32 nOldLen = nFormSelEnd - nFormSelStart; + sal_Int32 nNewLen = rStr.getLength(); + + OUStringBuffer aBuf(aFormText); + if (nOldLen) + aBuf.remove(nFormSelStart, nOldLen); + if (nNewLen) + aBuf.insert(nFormSelStart, rStr); + + aFormText = aBuf.makeStringAndClear(); + + nFormSelEnd = nFormSelStart + nNewLen; + + EditView* pView = GetFuncEditView(); + if (pView) + { + pView->SetEditEngineUpdateLayout( false ); + pView->GetEditEngine()->SetText( aFormText ); + pView->SetSelection( ESelection(0,nFormSelStart, 0,nFormSelEnd) ); + pView->SetEditEngineUpdateLayout( true ); + } + bModified = true; +} + +void ScInputHandler::InputTurnOffWinEngine() +{ + bInOwnChange = true; // disable ModifyHdl (reset below) + + eMode = SC_INPUT_NONE; + /* TODO: it would be better if there was some way to reset the input bar + * engine instead of deleting and having it recreate through + * GetFuncEditView(), but first least invasively let this fix fdo#71667 and + * fdo#72278 without reintroducing fdo#69971. */ + StopInputWinEngine(true); + + bInOwnChange = false; +} + +/** + * ScInputHdlState + */ +ScInputHdlState::ScInputHdlState( const ScAddress& rCurPos, + const ScAddress& rStartPos, + const ScAddress& rEndPos, + const OUString& rString, + const EditTextObject* pData ) + : aCursorPos ( rCurPos ), + aStartPos ( rStartPos ), + aEndPos ( rEndPos ), + aString ( rString ), + pEditData ( pData ? pData->Clone() : nullptr ) +{ +} + +ScInputHdlState::ScInputHdlState( const ScInputHdlState& rCpy ) +{ + *this = rCpy; +} + +ScInputHdlState::~ScInputHdlState() +{ +} + +bool ScInputHdlState::operator==( const ScInputHdlState& r ) const +{ + return ( (aStartPos == r.aStartPos) + && (aEndPos == r.aEndPos) + && (aCursorPos == r.aCursorPos) + && (aString == r.aString) + && ScGlobal::EETextObjEqual( pEditData.get(), r.pEditData.get() ) ); +} + +ScInputHdlState& ScInputHdlState::operator=( const ScInputHdlState& r ) +{ + if (this != &r) + { + aCursorPos = r.aCursorPos; + aStartPos = r.aStartPos; + aEndPos = r.aEndPos; + aString = r.aString; + pEditData.reset(); + if (r.pEditData) + pEditData = r.pEditData->Clone(); + } + return *this; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/app/inputwin.cxx b/sc/source/ui/app/inputwin.cxx new file mode 100644 index 000000000..0b31b8160 --- /dev/null +++ b/sc/source/ui/app/inputwin.cxx @@ -0,0 +1,2703 @@ +/* -*- 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 <memory> +#include <algorithm> +#include <string_view> + +#include <editeng/eeitem.hxx> + +#include <sfx2/app.hxx> +#include <editeng/adjustitem.hxx> +#include <editeng/editview.hxx> +#include <editeng/editstat.hxx> +#include <editeng/lspcitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/wghtitem.hxx> +#include <editeng/postitem.hxx> +#include <editeng/langitem.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/event.hxx> +#include <editeng/scriptspaceitem.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/cursor.hxx> +#include <vcl/help.hxx> +#include <vcl/settings.hxx> +#include <svl/stritem.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weldutils.hxx> +#include <unotools/charclass.hxx> + +#include <inputwin.hxx> +#include <scmod.hxx> +#include <global.hxx> +#include <scresid.hxx> +#include <strings.hrc> +#include <globstr.hrc> +#include <bitmaps.hlst> +#include <reffact.hxx> +#include <editutil.hxx> +#include <inputhdl.hxx> +#include <tabvwsh.hxx> +#include <document.hxx> +#include <docsh.hxx> +#include <appoptio.hxx> +#include <rangenam.hxx> +#include <rangeutl.hxx> +#include <docfunc.hxx> +#include <funcdesc.hxx> +#include <editeng/fontitem.hxx> +#include <AccessibleEditObject.hxx> +#include <AccessibleText.hxx> +#include <comphelper/lok.hxx> +#include <comphelper/string.hxx> +#include <com/sun/star/frame/XLayoutManager.hpp> +#include <helpids.h> +#include <output.hxx> + +namespace com::sun::star::accessibility { class XAccessible; } + +const tools::Long THESIZE = 1000000; // Should be more than enough! +const tools::Long INPUTLINE_INSET_MARGIN = 2; // Space between border and interior widgets of input line +const tools::Long LEFT_OFFSET = 5; // Left offset of input line +//TODO const long BUTTON_OFFSET = 2; // Space between input line and button to expand/collapse +const tools::Long INPUTWIN_MULTILINES = 6; // Initial number of lines within multiline dropdown +const tools::Long TOOLBOX_WINDOW_HEIGHT = 22; // Height of toolbox window in pixels - TODO: The same on all systems? +const tools::Long POSITION_COMBOBOX_WIDTH = 18; // Width of position combobox in characters +const int RESIZE_HOTSPOT_HEIGHT = 4; + +using com::sun::star::uno::Reference; +using com::sun::star::uno::UNO_QUERY; + +using com::sun::star::frame::XLayoutManager; +using com::sun::star::beans::XPropertySet; + +namespace { + +constexpr ToolBoxItemId SID_INPUT_FUNCTION (SC_VIEW_START + 47); +constexpr ToolBoxItemId SID_INPUT_SUM (SC_VIEW_START + 48); +constexpr ToolBoxItemId SID_INPUT_EQUAL (SC_VIEW_START + 49); +constexpr ToolBoxItemId SID_INPUT_CANCEL (SC_VIEW_START + 50); +constexpr ToolBoxItemId SID_INPUT_OK (SC_VIEW_START + 51); + +enum ScNameInputType +{ + SC_NAME_INPUT_CELL, + SC_NAME_INPUT_RANGE, + SC_NAME_INPUT_NAMEDRANGE_LOCAL, + SC_NAME_INPUT_NAMEDRANGE_GLOBAL, + SC_NAME_INPUT_DATABASE, + SC_NAME_INPUT_ROW, + SC_NAME_INPUT_SHEET, + SC_NAME_INPUT_DEFINE, + SC_NAME_INPUT_BAD_NAME, + SC_NAME_INPUT_BAD_SELECTION, + SC_MANAGE_NAMES +}; + +} + +SFX_IMPL_CHILDWINDOW_WITHID(ScInputWindowWrapper,FID_INPUTLINE_STATUS) + +ScInputWindowWrapper::ScInputWindowWrapper( vcl::Window* pParentP, + sal_uInt16 nId, + SfxBindings* pBindings, + SfxChildWinInfo* /* pInfo */ ) + : SfxChildWindow( pParentP, nId ) +{ + VclPtr<ScInputWindow> pWin = VclPtr<ScInputWindow>::Create( pParentP, pBindings ); + SetWindow( pWin ); + + pWin->Show(); + + pWin->SetSizePixel( pWin->CalcWindowSizePixel() ); + + SetAlignment(SfxChildAlignment::LOWESTTOP); + pBindings->Invalidate( FID_TOGGLEINPUTLINE ); +} + +/** + * GetInfo is disposed of if there's a SFX_IMPL_TOOLBOX! + */ +SfxChildWinInfo ScInputWindowWrapper::GetInfo() const +{ + SfxChildWinInfo aInfo = SfxChildWindow::GetInfo(); + return aInfo; +} + + +static VclPtr<ScInputBarGroup> lcl_chooseRuntimeImpl( vcl::Window* pParent, const SfxBindings* pBind ) +{ + ScTabViewShell* pViewSh = nullptr; + SfxDispatcher* pDisp = pBind->GetDispatcher(); + if ( pDisp ) + { + SfxViewFrame* pViewFrm = pDisp->GetFrame(); + if ( pViewFrm ) + pViewSh = dynamic_cast<ScTabViewShell*>( pViewFrm->GetViewShell() ); + } + + return VclPtr<ScInputBarGroup>::Create( pParent, pViewSh ); +} + +ScInputWindow::ScInputWindow( vcl::Window* pParent, const SfxBindings* pBind ) : + // With WB_CLIPCHILDREN otherwise we get flickering + ToolBox ( pParent, WinBits(WB_CLIPCHILDREN | WB_BORDER | WB_NOSHADOW) ), + aWndPos ( !comphelper::LibreOfficeKit::isActive() ? VclPtr<ScPosWnd>::Create(this) : nullptr ), + mxTextWindow ( lcl_chooseRuntimeImpl( this, pBind ) ), + pInputHdl ( nullptr ), + mpViewShell ( nullptr ), + mnMaxY (0), + mnStandardItemHeight(0), + bIsOkCancelMode ( false ), + bInResize ( false ) +{ + // #i73615# don't rely on SfxViewShell::Current while constructing the input line + // (also for GetInputHdl below) + ScTabViewShell* pViewSh = nullptr; + SfxDispatcher* pDisp = pBind->GetDispatcher(); + if ( pDisp ) + { + SfxViewFrame* pViewFrm = pDisp->GetFrame(); + if ( pViewFrm ) + pViewSh = dynamic_cast<ScTabViewShell*>( pViewFrm->GetViewShell() ); + } + OSL_ENSURE( pViewSh, "no view shell for input window" ); + + mpViewShell = pViewSh; + + // Position window, 3 buttons, input window + if (!comphelper::LibreOfficeKit::isActive()) + { + InsertWindow (ToolBoxItemId(1), aWndPos.get(), ToolBoxItemBits::NONE, 0); + InsertSeparator (1); + InsertItem (SID_INPUT_FUNCTION, Image(StockImage::Yes, RID_BMP_INPUT_FUNCTION), ToolBoxItemBits::NONE, 2); + } + + const bool bIsLOKMobilePhone = mpViewShell && mpViewShell->isLOKMobilePhone(); + + // sigma and equal buttons + if (!bIsLOKMobilePhone) + { + InsertItem (SID_INPUT_SUM, Image(StockImage::Yes, RID_BMP_INPUT_SUM), ToolBoxItemBits::DROPDOWNONLY, 3); + InsertItem (SID_INPUT_EQUAL, Image(StockImage::Yes, RID_BMP_INPUT_EQUAL), ToolBoxItemBits::NONE, 4); + InsertItem (SID_INPUT_CANCEL, Image(StockImage::Yes, RID_BMP_INPUT_CANCEL), ToolBoxItemBits::NONE, 5); + InsertItem (SID_INPUT_OK, Image(StockImage::Yes, RID_BMP_INPUT_OK), ToolBoxItemBits::NONE, 6); + } + + InsertWindow (ToolBoxItemId(7), mxTextWindow.get(), ToolBoxItemBits::NONE, 7); + SetDropdownClickHdl( LINK( this, ScInputWindow, DropdownClickHdl )); + + if (!comphelper::LibreOfficeKit::isActive()) + { + aWndPos ->SetQuickHelpText(ScResId(SCSTR_QHELP_POSWND)); + aWndPos ->SetHelpId (HID_INSWIN_POS); + + mxTextWindow->SetQuickHelpText(ScResId(SCSTR_QHELP_INPUTWND)); + mxTextWindow->SetHelpId (HID_INSWIN_INPUT); + + // No SetHelpText: the helptexts come from the Help + SetItemText (SID_INPUT_FUNCTION, ScResId(SCSTR_QHELP_BTNCALC)); + SetHelpId (SID_INPUT_FUNCTION, HID_INSWIN_CALC); + } + + // sigma and equal buttons + if (!bIsLOKMobilePhone) + { + SetHelpId (SID_INPUT_SUM, HID_INSWIN_SUMME); + SetHelpId (SID_INPUT_EQUAL, HID_INSWIN_FUNC); + SetHelpId (SID_INPUT_CANCEL, HID_INSWIN_CANCEL); + SetHelpId (SID_INPUT_OK, HID_INSWIN_OK); + + if (!comphelper::LibreOfficeKit::isActive()) + { + SetItemText ( SID_INPUT_SUM, ScResId( SCSTR_QHELP_BTNSUM ) ); + SetItemText ( SID_INPUT_EQUAL, ScResId( SCSTR_QHELP_BTNEQUAL ) ); + SetItemText ( SID_INPUT_CANCEL, ScResId( SCSTR_QHELP_BTNCANCEL ) ); + SetItemText ( SID_INPUT_OK, ScResId( SCSTR_QHELP_BTNOK ) ); + } + + EnableItem( SID_INPUT_CANCEL, false ); + EnableItem( SID_INPUT_OK, false ); + + HideItem( SID_INPUT_CANCEL ); + HideItem( SID_INPUT_OK ); + + mnStandardItemHeight = GetItemRect(SID_INPUT_SUM).GetHeight(); + } + + SetHelpId( HID_SC_INPUTWIN ); // For the whole input row + + if (!comphelper::LibreOfficeKit::isActive()) + aWndPos ->Show(); + mxTextWindow->Show(); + + pInputHdl = SC_MOD()->GetInputHdl( pViewSh, false ); // use own handler even if ref-handler is set + if (pInputHdl) + pInputHdl->SetInputWindow( this ); + + if (pInputHdl && !pInputHdl->GetFormString().isEmpty()) + { + // Switch over while the Function AutoPilot is active + // -> show content of the Function AutoPilot again + // Also show selection (remember at the InputHdl) + mxTextWindow->SetTextString( pInputHdl->GetFormString() ); + } + else if (pInputHdl && pInputHdl->IsInputMode()) + { + // If the input row was hidden while editing (e.g. when editing a formula + // and then switching to another document or the help), display the text + // we just edited from the InputHandler + mxTextWindow->SetTextString( pInputHdl->GetEditString() ); // Display text + if ( pInputHdl->IsTopMode() ) + pInputHdl->SetMode( SC_INPUT_TABLE ); // Focus ends up at the bottom anyways + } + else if (pViewSh) + { + // Don't stop editing in LOK a remote user might be editing. + const bool bStopEditing = !comphelper::LibreOfficeKit::isActive(); + pViewSh->UpdateInputHandler(true, bStopEditing); // Absolutely necessary update + } + + SetToolbarLayoutMode( ToolBoxLayoutMode::Locked ); + + SetAccessibleName(ScResId(STR_ACC_TOOLBAR_FORMULA)); +} + +ScInputWindow::~ScInputWindow() +{ + disposeOnce(); +} + +void ScInputWindow::dispose() +{ + bool bDown = !ScGlobal::oSysLocale; // after Clear? + + // if any view's input handler has a pointer to this input window, reset it + // (may be several ones, #74522#) + // member pInputHdl is not used here + + if ( !bDown ) + { + SfxViewShell* pSh = SfxViewShell::GetFirst( true, checkSfxViewShell<ScTabViewShell> ); + while ( pSh ) + { + ScInputHandler* pHdl = static_cast<ScTabViewShell*>(pSh)->GetInputHandler(); + if ( pHdl && pHdl->GetInputWindow() == this ) + { + pHdl->SetInputWindow( nullptr ); + pHdl->StopInputWinEngine( false ); // reset pTopView pointer + } + pSh = SfxViewShell::GetNext( *pSh, true, checkSfxViewShell<ScTabViewShell> ); + } + } + + if (comphelper::LibreOfficeKit::isActive()) + { + if (GetLOKNotifier()) + ReleaseLOKNotifier(); + } + + mxTextWindow.disposeAndClear(); + aWndPos.disposeAndClear(); + + ToolBox::dispose(); +} + +void ScInputWindow::SetInputHandler( ScInputHandler* pNew ) +{ + // Is called in the Activate of the View ... + if ( pNew != pInputHdl ) + { + // On Reload (last version) the pInputHdl is the InputHandler of the old, deleted + // ViewShell: so don't touch it here! + pInputHdl = pNew; + if (pInputHdl) + pInputHdl->SetInputWindow( this ); + } +} + +void ScInputWindow::Select() +{ + ScModule* pScMod = SC_MOD(); + ToolBox::Select(); + + ToolBoxItemId curItemId = GetCurItemId(); + if (curItemId == SID_INPUT_FUNCTION) + { + //! new method at ScModule to query if function autopilot is open + SfxViewFrame* pViewFrm = SfxViewFrame::Current(); + if ( pViewFrm && ( comphelper::LibreOfficeKit::isActive() || !pViewFrm->GetChildWindow( SID_OPENDLG_FUNCTION ) ) ) + { + pViewFrm->GetDispatcher()->Execute( SID_OPENDLG_FUNCTION, + SfxCallMode::SYNCHRON | SfxCallMode::RECORD ); + + // The Toolbox will be disabled anyways, so we don't need to switch here, + // regardless whether it succeeded or not! +// SetOkCancelMode(); + } + } + else if (curItemId == SID_INPUT_CANCEL) + { + pScMod->InputCancelHandler(); + SetSumAssignMode(); + } + else if (curItemId == SID_INPUT_OK) + { + pScMod->InputEnterHandler(); + SetSumAssignMode(); + mxTextWindow->Invalidate(); // Or else the Selection remains + } + else if (curItemId == SID_INPUT_EQUAL) + { + StartFormula(); + } +} + +void ScInputWindow::StartFormula() +{ + ScModule* pScMod = SC_MOD(); + mxTextWindow->StartEditEngine(); + if ( pScMod->IsEditMode() ) // Isn't if e.g. protected + { + mxTextWindow->StartEditEngine(); + + sal_Int32 nStartPos = 1; + sal_Int32 nEndPos = 1; + + ScTabViewShell* pViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() ); + if ( pViewSh ) + { + const OUString& rString = mxTextWindow->GetTextString(); + const sal_Int32 nLen = rString.getLength(); + + ScDocument& rDoc = pViewSh->GetViewData().GetDocument(); + CellType eCellType = rDoc.GetCellType( pViewSh->GetViewData().GetCurPos() ); + switch ( eCellType ) + { + case CELLTYPE_VALUE: + { + nEndPos = nLen + 1; + mxTextWindow->SetTextString("=" + rString); + break; + } + case CELLTYPE_STRING: + case CELLTYPE_EDIT: + nStartPos = 0; + nEndPos = nLen; + break; + case CELLTYPE_FORMULA: + nEndPos = nLen; + break; + default: + mxTextWindow->SetTextString("="); + break; + } + } + + EditView* pView = mxTextWindow->GetEditView(); + if (pView) + { + if (comphelper::LibreOfficeKit::isActive()) + TextGrabFocus(); + pView->SetSelection( ESelection(0, nStartPos, 0, nEndPos) ); + pScMod->InputChanged(pView); + SetOkCancelMode(); + pView->SetEditEngineUpdateLayout(true); + } + } +} + +void ScInputWindow::PixelInvalidate(const tools::Rectangle* pRectangle) +{ + if (comphelper::LibreOfficeKit::isDialogPainting() || !comphelper::LibreOfficeKit::isActive()) + return; + + if (pRectangle) + { + tools::Rectangle aRect(*pRectangle); + aRect.Move(-GetOutOffXPixel(), -GetOutOffYPixel()); + Window::PixelInvalidate(&aRect); + } + else + { + Window::PixelInvalidate(nullptr); + } +} + +void ScInputWindow::SetSizePixel( const Size& rNewSize ) +{ + const vcl::ILibreOfficeKitNotifier* pNotifier = GetLOKNotifier(); + if (pNotifier) + { + if (vcl::Window* pFrameWindowImpl = GetParent()) + { + if (vcl::Window* pWorkWindow = pFrameWindowImpl->GetParent()) + { + if (vcl::Window* pImplBorderWindow = pWorkWindow->GetParent()) + { + Size aSize = pImplBorderWindow->GetSizePixel(); + aSize.setWidth(rNewSize.getWidth()); + pImplBorderWindow->SetSizePixel(aSize); + } + } + } + } + + ToolBox::SetSizePixel(rNewSize); +} + +void ScInputWindow::Resize() +{ + ToolBox::Resize(); + + Size aStartSize = GetSizePixel(); + Size aSize = aStartSize; + + auto nLines = mxTextWindow->GetNumLines(); + //(-10) to allow margin between sidebar and formulabar + tools::Long margin = (comphelper::LibreOfficeKit::isActive()) ? 10 : 0; + Size aTextWindowSize(aSize.Width() - mxTextWindow->GetPosPixel().X() - LEFT_OFFSET - margin, + mxTextWindow->GetPixelHeightForLines(nLines)); + mxTextWindow->SetSizePixel(aTextWindowSize); + + int nTopOffset = 0; + if (nLines > 1) + { + // Initially there is 1 line and the edit is vertically centered in the toolbar + // Later, if expanded then the vertical position of the edit will remain at + // that initial position, so when calculating the overall size of the expanded + // toolbar we have to include that initial offset in order to not make + // the edit overlap the RESIZE_HOTSPOT_HEIGHT area so that dragging to resize + // is still possible. + int nNormalHeight = mxTextWindow->GetPixelHeightForLines(1); + int nInitialTopMargin = (mnStandardItemHeight - nNormalHeight) / 2; + if (nInitialTopMargin > 0) + nTopOffset = nInitialTopMargin; + } + + // add empty space of RESIZE_HOTSPOT_HEIGHT so resize is possible when hovering there + aSize.setHeight(CalcWindowSizePixel().Height() + RESIZE_HOTSPOT_HEIGHT + nTopOffset); + + if (aStartSize != aSize) + SetSizePixel(aSize); + + Invalidate(); +} + +void ScInputWindow::NotifyLOKClient() +{ + if (comphelper::LibreOfficeKit::isActive() && !GetLOKNotifier() && mpViewShell) + SetLOKNotifier(mpViewShell); +} + +void ScInputWindow::SetFuncString( const OUString& rString, bool bDoEdit ) +{ + //! new method at ScModule to query if function autopilot is open + SfxViewFrame* pViewFrm = SfxViewFrame::Current(); + EnableButtons( pViewFrm && !pViewFrm->GetChildWindow( SID_OPENDLG_FUNCTION ) ); + mxTextWindow->StartEditEngine(); + + ScModule* pScMod = SC_MOD(); + if ( !pScMod->IsEditMode() ) + return; + + if ( bDoEdit ) + mxTextWindow->TextGrabFocus(); + mxTextWindow->SetTextString( rString ); + EditView* pView = mxTextWindow->GetEditView(); + if (!pView) + return; + + sal_Int32 nLen = rString.getLength(); + + if ( nLen > 0 ) + { + nLen--; + pView->SetSelection( ESelection( 0, nLen, 0, nLen ) ); + } + + pScMod->InputChanged(pView); + if ( bDoEdit ) + SetOkCancelMode(); // Not the case if immediately followed by Enter/Cancel + + pView->SetEditEngineUpdateLayout(true); +} + +void ScInputWindow::SetPosString( const OUString& rStr ) +{ + if (!comphelper::LibreOfficeKit::isActive()) + aWndPos->SetPos( rStr ); +} + +void ScInputWindow::SetTextString( const OUString& rString ) +{ + if (rString.getLength() <= 32767) + mxTextWindow->SetTextString(rString); + else + mxTextWindow->SetTextString(rString.copy(0, 32767)); +} + +void ScInputWindow::SetOkCancelMode() +{ + //! new method at ScModule to query if function autopilot is open + SfxViewFrame* pViewFrm = SfxViewFrame::Current(); + EnableButtons( pViewFrm && !pViewFrm->GetChildWindow( SID_OPENDLG_FUNCTION ) ); + + if (bIsOkCancelMode) + return; + + EnableItem ( SID_INPUT_SUM, false ); + EnableItem ( SID_INPUT_EQUAL, false ); + HideItem ( SID_INPUT_SUM ); + HideItem ( SID_INPUT_EQUAL ); + + ShowItem ( SID_INPUT_CANCEL, true ); + ShowItem ( SID_INPUT_OK, true ); + EnableItem ( SID_INPUT_CANCEL, true ); + EnableItem ( SID_INPUT_OK, true ); + + bIsOkCancelMode = true; +} + +void ScInputWindow::SetSumAssignMode() +{ + //! new method at ScModule to query if function autopilot is open + SfxViewFrame* pViewFrm = SfxViewFrame::Current(); + EnableButtons( pViewFrm && !pViewFrm->GetChildWindow( SID_OPENDLG_FUNCTION ) ); + + if (!bIsOkCancelMode) + return; + + EnableItem ( SID_INPUT_CANCEL, false ); + EnableItem ( SID_INPUT_OK, false ); + HideItem ( SID_INPUT_CANCEL ); + HideItem ( SID_INPUT_OK ); + + ShowItem ( SID_INPUT_SUM, true ); + ShowItem ( SID_INPUT_EQUAL, true ); + EnableItem ( SID_INPUT_SUM, true ); + EnableItem ( SID_INPUT_EQUAL, true ); + + bIsOkCancelMode = false; + + SetFormulaMode(false); // No editing -> no formula +} + +void ScInputWindow::SetFormulaMode( bool bSet ) +{ + if (!comphelper::LibreOfficeKit::isActive()) + aWndPos->SetFormulaMode(bSet); + mxTextWindow->SetFormulaMode(bSet); +} + +bool ScInputWindow::IsInputActive() +{ + return mxTextWindow->IsInputActive(); +} + +EditView* ScInputWindow::GetEditView() +{ + return mxTextWindow->GetEditView(); +} + +vcl::Window* ScInputWindow::GetEditWindow() +{ + return mxTextWindow; +} + +Point ScInputWindow::GetCursorScreenPixelPos(bool bBelow) +{ + return mxTextWindow->GetCursorScreenPixelPos(bBelow); +} + +void ScInputWindow::MakeDialogEditView() +{ + mxTextWindow->MakeDialogEditView(); +} + +void ScInputWindow::StopEditEngine( bool bAll ) +{ + mxTextWindow->StopEditEngine( bAll ); +} + +void ScInputWindow::TextGrabFocus() +{ + mxTextWindow->TextGrabFocus(); +} + +void ScInputWindow::TextInvalidate() +{ + mxTextWindow->Invalidate(); +} + +void ScInputWindow::SwitchToTextWin() +{ + // used for shift-ctrl-F2 + + mxTextWindow->StartEditEngine(); + if ( SC_MOD()->IsEditMode() ) + { + mxTextWindow->TextGrabFocus(); + EditView* pView = mxTextWindow->GetEditView(); + if (pView) + { + sal_Int32 nPara = pView->GetEditEngine()->GetParagraphCount() ? ( pView->GetEditEngine()->GetParagraphCount() - 1 ) : 0; + sal_Int32 nLen = pView->GetEditEngine()->GetTextLen( nPara ); + ESelection aSel( nPara, nLen, nPara, nLen ); + pView->SetSelection( aSel ); // set cursor to end of text + } + } +} + +void ScInputWindow::PosGrabFocus() +{ + if (!comphelper::LibreOfficeKit::isActive()) + aWndPos->GrabFocus(); +} + +void ScInputWindow::EnableButtons( bool bEnable ) +{ + // when enabling buttons, always also enable the input window itself + if ( bEnable && !IsEnabled() ) + Enable(); + + EnableItem( SID_INPUT_FUNCTION, bEnable ); + EnableItem( bIsOkCancelMode ? SID_INPUT_CANCEL : SID_INPUT_SUM, bEnable ); + EnableItem( bIsOkCancelMode ? SID_INPUT_OK : SID_INPUT_EQUAL, bEnable ); +// Invalidate(); +} + +void ScInputWindow::NumLinesChanged() +{ + mxTextWindow->NumLinesChanged(); +} + +void ScInputWindow::StateChanged( StateChangedType nType ) +{ + ToolBox::StateChanged( nType ); + + if ( nType == StateChangedType::InitShow ) Resize(); +} + +void ScInputWindow::DataChanged( const DataChangedEvent& rDCEvt ) +{ + if ( rDCEvt.GetType() == DataChangedEventType::SETTINGS && (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) ) + { + // update item images + SetItemImage(SID_INPUT_FUNCTION, Image(StockImage::Yes, RID_BMP_INPUT_FUNCTION)); + if ( bIsOkCancelMode ) + { + SetItemImage(SID_INPUT_CANCEL, Image(StockImage::Yes, RID_BMP_INPUT_CANCEL)); + SetItemImage(SID_INPUT_OK, Image(StockImage::Yes, RID_BMP_INPUT_OK)); + } + else + { + SetItemImage(SID_INPUT_SUM, Image(StockImage::Yes, RID_BMP_INPUT_SUM)); + SetItemImage(SID_INPUT_EQUAL, Image(StockImage::Yes, RID_BMP_INPUT_EQUAL)); + } + } + + ToolBox::DataChanged( rDCEvt ); +} + +bool ScInputWindow::IsPointerAtResizePos() +{ + return GetOutputSizePixel().Height() - GetPointerPosPixel().Y() <= RESIZE_HOTSPOT_HEIGHT; +} + +void ScInputWindow::MouseMove( const MouseEvent& rMEvt ) +{ + Point aPosPixel = GetPointerPosPixel(); + + ScInputBarGroup* pGroupBar = mxTextWindow.get(); + + if (bInResize || IsPointerAtResizePos()) + SetPointer(PointerStyle::WindowSSize); + else + SetPointer(PointerStyle::Arrow); + + if (bInResize) + { + // detect direction + tools::Long nResizeThreshold = tools::Long(TOOLBOX_WINDOW_HEIGHT * 0.7); + bool bResetPointerPos = false; + + // Detect attempt to expand toolbar too much + if (aPosPixel.Y() >= mnMaxY) + { + bResetPointerPos = true; + aPosPixel.setY( mnMaxY ); + } // or expanding down + else if (GetOutputSizePixel().Height() - aPosPixel.Y() < -nResizeThreshold) + { + pGroupBar->IncrementVerticalSize(); + bResetPointerPos = true; + } // or shrinking up + else if ((GetOutputSizePixel().Height() - aPosPixel.Y()) > nResizeThreshold) + { + bResetPointerPos = true; + pGroupBar->DecrementVerticalSize(); + } + + if (bResetPointerPos) + { + aPosPixel.setY( GetOutputSizePixel().Height() ); + SetPointerPosPixel(aPosPixel); + } + } + + ToolBox::MouseMove(rMEvt); +} + +void ScInputWindow::MouseButtonDown( const MouseEvent& rMEvt ) +{ + if (rMEvt.IsLeft()) + { + if (IsPointerAtResizePos()) + { + // Don't leave the mouse pointer leave *this* window + CaptureMouse(); + bInResize = true; + + // find the height of the gridwin, we don't want to be + // able to expand the toolbar too far so we need to + // calculate an upper limit + // I'd prefer to leave at least a single column header and a + // row but I don't know how to get that value in pixels. + // Use TOOLBOX_WINDOW_HEIGHT for the moment + ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell(); + mnMaxY = GetOutputSizePixel().Height() + (pViewSh->GetGridHeight(SC_SPLIT_TOP) + + pViewSh->GetGridHeight(SC_SPLIT_BOTTOM)) - TOOLBOX_WINDOW_HEIGHT; + } + } + + ToolBox::MouseButtonDown( rMEvt ); +} +void ScInputWindow::MouseButtonUp( const MouseEvent& rMEvt ) +{ + ReleaseMouse(); + if ( rMEvt.IsLeft() ) + { + bInResize = false; + mnMaxY = 0; + } + + ToolBox::MouseButtonUp( rMEvt ); +} + +void ScInputWindow::AutoSum( bool& bRangeFinder, bool& bSubTotal, OpCode eCode ) +{ + ScModule* pScMod = SC_MOD(); + ScTabViewShell* pViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() ); + if ( !pViewSh ) + return; + + const OUString aFormula = pViewSh->DoAutoSum(bRangeFinder, bSubTotal, eCode); + if ( aFormula.isEmpty() ) + return; + + SetFuncString( aFormula ); + const sal_Int32 aOpen = aFormula.indexOf('('); + const sal_Int32 aLen = aFormula.getLength(); + if (!(bRangeFinder && pScMod->IsEditMode())) + return; + + ScInputHandler* pHdl = pScMod->GetInputHdl( pViewSh ); + if ( !pHdl ) + return; + + pHdl->InitRangeFinder( aFormula ); + + //! SetSelection at the InputHandler? + //! Set bSelIsRef? + if ( aOpen != -1 && aLen > aOpen ) + { + ESelection aSel( 0, aOpen + (bSubTotal ? 3 : 1), 0, aLen-1 ); + EditView* pTableView = pHdl->GetTableView(); + if ( pTableView ) + pTableView->SetSelection( aSel ); + EditView* pTopView = pHdl->GetTopView(); + if ( pTopView ) + pTopView->SetSelection( aSel ); + } +} + +ScInputBarGroup::ScInputBarGroup(vcl::Window* pParent, ScTabViewShell* pViewSh) + : InterimItemWindow(pParent, "modules/scalc/ui/inputbar.ui", "InputBar", true, reinterpret_cast<sal_uInt64>(pViewSh)) + , mxBackground(m_xBuilder->weld_container("background")) + , mxTextWndGroup(new ScTextWndGroup(*this, pViewSh)) + , mxButtonUp(m_xBuilder->weld_button("up")) + , mxButtonDown(m_xBuilder->weld_button("down")) +{ + InitControlBase(m_xContainer.get()); + + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + + SetPaintTransparent(false); + SetBackground(rStyleSettings.GetFaceColor()); + + // match to bg used in ScTextWnd::SetDrawingArea to the margin area is drawn with the + // same desired bg + mxBackground->set_background(rStyleSettings.GetWindowColor()); + + mxButtonUp->connect_clicked(LINK(this, ScInputBarGroup, ClickHdl)); + mxButtonDown->connect_clicked(LINK(this, ScInputBarGroup, ClickHdl)); + + if (!comphelper::LibreOfficeKit::isActive()) + { + mxButtonUp->set_tooltip_text(ScResId( SCSTR_QHELP_COLLAPSE_FORMULA)); + mxButtonDown->set_tooltip_text(ScResId(SCSTR_QHELP_EXPAND_FORMULA)); + } + + int nHeight = mxTextWndGroup->GetPixelHeightForLines(1); + mxButtonUp->set_size_request(-1, nHeight); + mxButtonDown->set_size_request(-1, nHeight); + + // disable the multiline toggle on the mobile phones + const SfxViewShell* pViewShell = SfxViewShell::Current(); + if (!comphelper::LibreOfficeKit::isActive() || !(pViewShell && pViewShell->isLOKMobilePhone())) + mxButtonDown->show(); + + // tdf#154042 Use an initial height of one row so the Toolbar positions + // this in the same place regardless of how many rows it eventually shows + Size aSize(GetSizePixel().Width(), nHeight); + SetSizePixel(aSize); +} + +Point ScInputBarGroup::GetCursorScreenPixelPos(bool bBelow) +{ + return mxTextWndGroup->GetCursorScreenPixelPos(bBelow); +} + +ScInputBarGroup::~ScInputBarGroup() +{ + disposeOnce(); +} + +void ScInputBarGroup::dispose() +{ + mxTextWndGroup.reset(); + mxButtonUp.reset(); + mxButtonDown.reset(); + mxBackground.reset(); + InterimItemWindow::dispose(); +} + +void ScInputBarGroup::InsertAccessibleTextData( ScAccessibleEditLineTextData& rTextData ) +{ + mxTextWndGroup->InsertAccessibleTextData(rTextData); +} + +void ScInputBarGroup::RemoveAccessibleTextData( ScAccessibleEditLineTextData& rTextData ) +{ + mxTextWndGroup->RemoveAccessibleTextData(rTextData); +} + +const OUString& ScInputBarGroup::GetTextString() const +{ + return mxTextWndGroup->GetTextString(); +} + +void ScInputBarGroup::SetTextString( const OUString& rString ) +{ + mxTextWndGroup->SetTextString(rString); +} + +void ScInputBarGroup::Resize() +{ + mxTextWndGroup->SetScrollPolicy(); + InterimItemWindow::Resize(); + TriggerToolboxLayout(); +} + +void ScInputBarGroup::StopEditEngine(bool bAll) +{ + mxTextWndGroup->StopEditEngine(bAll); +} + +void ScInputBarGroup::StartEditEngine() +{ + mxTextWndGroup->StartEditEngine(); +} + +void ScInputBarGroup::MakeDialogEditView() +{ + mxTextWndGroup->MakeDialogEditView(); +} + +EditView* ScInputBarGroup::GetEditView() const +{ + return mxTextWndGroup->GetEditView(); +} + +bool ScInputBarGroup::HasEditView() const +{ + return mxTextWndGroup->HasEditView(); +} + +bool ScInputBarGroup::IsInputActive() +{ + return mxTextWndGroup->IsInputActive(); +} + +void ScInputBarGroup::SetFormulaMode(bool bSet) +{ + mxTextWndGroup->SetFormulaMode(bSet); +} + +void ScInputBarGroup::IncrementVerticalSize() +{ + mxTextWndGroup->SetNumLines(mxTextWndGroup->GetNumLines() + 1); + TriggerToolboxLayout(); +} + +void ScInputBarGroup::DecrementVerticalSize() +{ + if (mxTextWndGroup->GetNumLines() > 1) + { + mxTextWndGroup->SetNumLines(mxTextWndGroup->GetNumLines() - 1); + TriggerToolboxLayout(); + } +} + +void ScInputWindow::MenuHdl(std::string_view command) +{ + if (command.empty()) + return; + + bool bSubTotal = false; + bool bRangeFinder = false; + OpCode eCode = ocSum; + if ( command == "sum" ) + { + eCode = ocSum; + } + else if ( command == "average" ) + { + eCode = ocAverage; + } + else if ( command == "max" ) + { + eCode = ocMax; + } + else if ( command == "min" ) + { + eCode = ocMin; + } + else if ( command == "count" ) + { + eCode = ocCount; + } + else if ( command == "counta" ) + { + eCode = ocCount2; + } + else if ( command == "product" ) + { + eCode = ocProduct; + } + else if (command == "stdev") + { + eCode = ocStDev; + } + else if (command == "stdevp") + { + eCode = ocStDevP; + } + else if (command == "var") + { + eCode = ocVar; + } + else if (command == "varp") + { + eCode = ocVarP; + } + + AutoSum( bRangeFinder, bSubTotal, eCode ); +} + +IMPL_LINK_NOARG(ScInputWindow, DropdownClickHdl, ToolBox *, void) +{ + ToolBoxItemId nCurID = GetCurItemId(); + EndSelection(); + + if (nCurID == SID_INPUT_SUM) + { + tools::Rectangle aRect(GetItemRect(SID_INPUT_SUM)); + weld::Window* pPopupParent = weld::GetPopupParent(*this, aRect); + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(pPopupParent, "modules/scalc/ui/autosum.ui")); + std::unique_ptr<weld::Menu> xPopMenu(xBuilder->weld_menu("menu")); + MenuHdl(xPopMenu->popup_at_rect(pPopupParent, aRect)); + } +} + +IMPL_LINK_NOARG(ScInputBarGroup, ClickHdl, weld::Button&, void) +{ + if (mxTextWndGroup->GetNumLines() > 1) + mxTextWndGroup->SetNumLines(1); + else + mxTextWndGroup->SetNumLines(mxTextWndGroup->GetLastNumExpandedLines()); + + NumLinesChanged(); +} + +void ScInputBarGroup::NumLinesChanged() +{ + if (mxTextWndGroup->GetNumLines() > 1) + { + mxButtonDown->hide(); + mxButtonUp->show(); + mxTextWndGroup->SetLastNumExpandedLines(mxTextWndGroup->GetNumLines()); + } + else + { + mxButtonUp->hide(); + mxButtonDown->show(); + } + TriggerToolboxLayout(); + + // Restore focus to input line(s) if necessary + ScInputHandler* pHdl = SC_MOD()->GetInputHdl(); + if ( pHdl && pHdl->IsTopMode() ) + mxTextWndGroup->TextGrabFocus(); +} + +void ScInputBarGroup::TriggerToolboxLayout() +{ + vcl::Window *w=GetParent(); + ScInputWindow &rParent = dynamic_cast<ScInputWindow&>(*w); + SfxViewFrame* pViewFrm = SfxViewFrame::Current(); + + if ( !pViewFrm ) + return; + + Reference< css::beans::XPropertySet > xPropSet( pViewFrm->GetFrame().GetFrameInterface(), UNO_QUERY ); + Reference< css::frame::XLayoutManager > xLayoutManager; + + if ( xPropSet.is() ) + { + css::uno::Any aValue = xPropSet->getPropertyValue("LayoutManager"); + aValue >>= xLayoutManager; + } + + if ( !xLayoutManager.is() ) + return; + + xLayoutManager->lock(); + DataChangedEvent aFakeUpdate( DataChangedEventType::SETTINGS, nullptr, AllSettingsFlags::STYLE ); + + // this basically will trigger the repositioning of the + // items in the toolbar from ImplFormat ( which is controlled by + // mnWinHeight ) which in turn is updated in ImplCalcItem which is + // controlled by mbCalc. Additionally the ImplFormat above is + // controlled via mbFormat. It seems the easiest way to get these + // booleans set is to send in the fake event below. + rParent.DataChanged( aFakeUpdate); + + // highest item in toolbar will have been calculated via the + // event above. Call resize on InputBar to pick up the height + // change + rParent.Resize(); + + // unlock relayouts the toolbars in the 4 quadrants + xLayoutManager->unlock(); +} + +void ScInputBarGroup::TextGrabFocus() +{ + mxTextWndGroup->TextGrabFocus(); +} + +constexpr tools::Long gnBorderWidth = (INPUTLINE_INSET_MARGIN + 1) * 2; +constexpr tools::Long gnBorderHeight = INPUTLINE_INSET_MARGIN + 1; + +ScTextWndGroup::ScTextWndGroup(ScInputBarGroup& rParent, ScTabViewShell* pViewSh) + : mxTextWnd(new ScTextWnd(*this, pViewSh)) + , mxScrollWin(rParent.GetBuilder().weld_scrolled_window("scrolledwindow", true)) + , mxTextWndWin(new weld::CustomWeld(rParent.GetBuilder(), "sc_input_window", *mxTextWnd)) + , mrParent(rParent) +{ + mxScrollWin->connect_vadjustment_changed(LINK(this, ScTextWndGroup, Impl_ScrollHdl)); + if (comphelper::LibreOfficeKit::isActive()) + ScInputHandler::LOKSendFormulabarUpdate(SfxViewShell::Current(), "", ESelection()); +} + +Point ScTextWndGroup::GetCursorScreenPixelPos(bool bBelow) +{ + Point aPos; + if (!HasEditView()) + return aPos; + EditView* pEditView = GetEditView(); + vcl::Cursor* pCur = pEditView->GetCursor(); + if (!pCur) + return aPos; + Point aLogicPos = pCur->GetPos(); + if (bBelow) + aLogicPos.AdjustY(pCur->GetHeight()); + aPos = GetEditViewDevice().LogicToPixel(aLogicPos); + bool bRTL = mrParent.IsRTLEnabled(); + if (bRTL) + aPos.setX(mxTextWnd->GetOutputSizePixel().Width() - aPos.X() + gnBorderWidth); + else + aPos.AdjustX(gnBorderWidth + 1); + + return mrParent.OutputToScreenPixel(aPos); +} + +ScTextWndGroup::~ScTextWndGroup() +{ +} + +void ScTextWndGroup::InsertAccessibleTextData(ScAccessibleEditLineTextData& rTextData) +{ + mxTextWnd->InsertAccessibleTextData(rTextData); +} + +EditView* ScTextWndGroup::GetEditView() const +{ + return mxTextWnd->GetEditView(); +} + +const OutputDevice& ScTextWndGroup::GetEditViewDevice() const +{ + return mxTextWnd->GetEditViewDevice(); +} + +tools::Long ScTextWndGroup::GetLastNumExpandedLines() const +{ + return mxTextWnd->GetLastNumExpandedLines(); +} + +void ScTextWndGroup::SetLastNumExpandedLines(tools::Long nLastExpandedLines) +{ + mxTextWnd->SetLastNumExpandedLines(nLastExpandedLines); +} + +tools::Long ScTextWndGroup::GetNumLines() const +{ + return mxTextWnd->GetNumLines(); +} + +int ScTextWndGroup::GetPixelHeightForLines(tools::Long nLines) +{ + return mxTextWnd->GetPixelHeightForLines(nLines) + 2 * gnBorderHeight; +} + +weld::ScrolledWindow& ScTextWndGroup::GetScrollWin() +{ + return *mxScrollWin; +} + +const OUString& ScTextWndGroup::GetTextString() const +{ + return mxTextWnd->GetTextString(); +} + +bool ScTextWndGroup::HasEditView() const +{ + return mxTextWnd->HasEditView(); +} + +bool ScTextWndGroup::IsInputActive() +{ + return mxTextWnd->IsInputActive(); +} + +void ScTextWndGroup::MakeDialogEditView() +{ + mxTextWnd->MakeDialogEditView(); +} + +void ScTextWndGroup::RemoveAccessibleTextData(ScAccessibleEditLineTextData& rTextData) +{ + mxTextWnd->RemoveAccessibleTextData(rTextData); +} + +void ScTextWndGroup::SetScrollPolicy() +{ + if (mxTextWnd->GetNumLines() > 2) + mxScrollWin->set_vpolicy(VclPolicyType::ALWAYS); + else + mxScrollWin->set_vpolicy(VclPolicyType::NEVER); +} + +void ScTextWndGroup::SetNumLines(tools::Long nLines) +{ + mxTextWnd->SetNumLines(nLines); +} + +void ScTextWndGroup::SetFormulaMode(bool bSet) +{ + mxTextWnd->SetFormulaMode(bSet); +} + +void ScTextWndGroup::SetTextString(const OUString& rString) +{ + mxTextWnd->SetTextString(rString); +} + +void ScTextWndGroup::StartEditEngine() +{ + mxTextWnd->StartEditEngine(); +} + +void ScTextWndGroup::StopEditEngine(bool bAll) +{ + mxTextWnd->StopEditEngine( bAll ); +} + +void ScTextWndGroup::TextGrabFocus() +{ + mxTextWnd->TextGrabFocus(); +} + +IMPL_LINK_NOARG(ScTextWndGroup, Impl_ScrollHdl, weld::ScrolledWindow&, void) +{ + mxTextWnd->DoScroll(); +} + +void ScTextWnd::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect ) +{ + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + Color aBgColor = rStyleSettings.GetWindowColor(); + rRenderContext.SetBackground(aBgColor); + + // tdf#137713 we rely on GetEditView creating it if it doesn't already exist so + // GetEditView() must be called unconditionally + if (EditView* pView = GetEditView()) + { + if (mbInvalidate) + { + pView->Invalidate(); + mbInvalidate = false; + } + } + + if (comphelper::LibreOfficeKit::isActive() && m_xEditEngine) + { + // EditEngine/EditView works in twips logical coordinates, so set the device map-mode to twips before painting + // and use twips version of the painting area 'rRect'. + // Document zoom should not be included in this conversion. + tools::Rectangle aLogicRect = OutputDevice::LogicToLogic(rRect, MapMode(MapUnit::MapPixel), MapMode(MapUnit::MapTwip)); + MapMode aOriginalMode = rRenderContext.GetMapMode(); + rRenderContext.SetMapMode(MapMode(MapUnit::MapTwip)); + WeldEditView::Paint(rRenderContext, aLogicRect); + rRenderContext.SetMapMode(aOriginalMode); + } + else + WeldEditView::Paint(rRenderContext, rRect); +} + +EditView* ScTextWnd::GetEditView() const +{ + if ( !m_xEditView ) + const_cast<ScTextWnd&>(*this).InitEditEngine(); + return m_xEditView.get(); +} + +bool ScTextWnd::HasEditView() const { return m_xEditView != nullptr; } + +const OutputDevice& ScTextWnd::GetEditViewDevice() const +{ + return EditViewOutputDevice(); +} + +int ScTextWnd::GetPixelHeightForLines(tools::Long nLines) +{ + OutputDevice& rDevice = GetDrawingArea()->get_ref_device(); + return rDevice.LogicToPixel(Size(0, nLines * rDevice.GetTextHeight())).Height() + 1; +} + +tools::Long ScTextWnd::GetNumLines() const +{ + ScViewData& rViewData = mpViewShell->GetViewData(); + return rViewData.GetFormulaBarLines(); +} + +void ScTextWnd::SetNumLines(tools::Long nLines) +{ + ScViewData& rViewData = mpViewShell->GetViewData(); + rViewData.SetFormulaBarLines(nLines); + if ( nLines > 1 ) + { + // SetFormulaBarLines sanitizes the height, so get the sanitized value + mnLastExpandedLines = rViewData.GetFormulaBarLines(); + Resize(); + } +} + +void ScTextWnd::Resize() +{ + if (m_xEditView) + { + Size aOutputSize = GetOutputSizePixel(); + OutputDevice& rDevice = GetDrawingArea()->get_ref_device(); + tools::Rectangle aOutputArea = rDevice.PixelToLogic( tools::Rectangle( Point(), aOutputSize )); + m_xEditView->SetOutputArea( aOutputArea ); + + // Don't leave an empty area at the bottom if we can move the text down. + tools::Long nMaxVisAreaTop = m_xEditEngine->GetTextHeight() - aOutputArea.GetHeight(); + if (m_xEditView->GetVisArea().Top() > nMaxVisAreaTop) + { + m_xEditView->Scroll(0, m_xEditView->GetVisArea().Top() - nMaxVisAreaTop); + } + + m_xEditEngine->SetPaperSize( rDevice.PixelToLogic( Size( aOutputSize.Width(), 10000 ) ) ); + } + + // skip WeldEditView's Resize(); + weld::CustomWidgetController::Resize(); + + SetScrollBarRange(); +} + +int ScTextWnd::GetEditEngTxtHeight() const +{ + return m_xEditView ? m_xEditView->GetEditEngine()->GetTextHeight() : 0; +} + +void ScTextWnd::SetScrollBarRange() +{ + if (!m_xEditView) + return; + + OutputDevice& rDevice = GetDrawingArea()->get_ref_device(); + Size aOutputSize = rDevice.GetOutputSize(); + + int nUpper = GetEditEngTxtHeight(); + int nCurrentDocPos = m_xEditView->GetVisArea().Top(); + int nStepIncrement = GetTextHeight(); + int nPageIncrement = aOutputSize.Height(); + int nPageSize = aOutputSize.Height(); + + /* limit the page size to below nUpper because gtk's gtk_scrolled_window_start_deceleration has + effectively... + + lower = gtk_adjustment_get_lower + upper = gtk_adjustment_get_upper - gtk_adjustment_get_page_size + + and requires that upper > lower or the deceleration animation never ends + */ + nPageSize = std::min(nPageSize, nUpper); + + weld::ScrolledWindow& rVBar = mrGroupBar.GetScrollWin(); + rVBar.vadjustment_configure(nCurrentDocPos, 0, nUpper, + nStepIncrement, nPageIncrement, nPageSize); +} + +void ScTextWnd::DoScroll() +{ + if (m_xEditView) + { + weld::ScrolledWindow& rVBar = mrGroupBar.GetScrollWin(); + auto currentDocPos = m_xEditView->GetVisArea().Top(); + auto nDiff = currentDocPos - rVBar.vadjustment_get_value(); + // we expect SetScrollBarRange callback to be triggered by Scroll + // to set where we ended up + m_xEditView->Scroll(0, nDiff); + } +} + +void ScTextWnd::StartEditEngine() +{ + // Don't activate if we're a modal dialog ourselves (Doc-modal dialog) + SfxObjectShell* pObjSh = SfxObjectShell::Current(); + if ( pObjSh && pObjSh->IsInModalMode() ) + return; + + if ( !m_xEditView || !m_xEditEngine ) + { + InitEditEngine(); + } + + ScInputHandler* pHdl = mpViewShell->GetInputHandler(); + if (pHdl) + pHdl->SetMode(SC_INPUT_TOP, nullptr, static_cast<ScEditEngineDefaulter*>(m_xEditEngine.get())); + + SfxViewFrame* pViewFrm = SfxViewFrame::Current(); + if (pViewFrm) + pViewFrm->GetBindings().Invalidate( SID_ATTR_INSERT ); +} + +static void lcl_ExtendEditFontAttribs( SfxItemSet& rSet ) +{ + const SfxPoolItem& rFontItem = rSet.Get( EE_CHAR_FONTINFO ); + std::unique_ptr<SfxPoolItem> pNewItem(rFontItem.Clone()); + pNewItem->SetWhich(EE_CHAR_FONTINFO_CJK); + rSet.Put( *pNewItem ); + pNewItem->SetWhich(EE_CHAR_FONTINFO_CTL); + rSet.Put( *pNewItem ); + const SfxPoolItem& rHeightItem = rSet.Get( EE_CHAR_FONTHEIGHT ); + pNewItem.reset(rHeightItem.Clone()); + pNewItem->SetWhich(EE_CHAR_FONTHEIGHT_CJK); + rSet.Put( *pNewItem ); + pNewItem->SetWhich(EE_CHAR_FONTHEIGHT_CTL); + rSet.Put( *pNewItem ); + const SfxPoolItem& rWeightItem = rSet.Get( EE_CHAR_WEIGHT ); + pNewItem.reset(rWeightItem.Clone()); + pNewItem->SetWhich(EE_CHAR_WEIGHT_CJK); + rSet.Put( *pNewItem ); + pNewItem->SetWhich(EE_CHAR_WEIGHT_CTL); + rSet.Put( *pNewItem ); + const SfxPoolItem& rItalicItem = rSet.Get( EE_CHAR_ITALIC ); + pNewItem.reset(rItalicItem.Clone()); + pNewItem->SetWhich(EE_CHAR_ITALIC_CJK); + rSet.Put( *pNewItem ); + pNewItem->SetWhich(EE_CHAR_ITALIC_CTL); + rSet.Put( *pNewItem ); + const SfxPoolItem& rLangItem = rSet.Get( EE_CHAR_LANGUAGE ); + pNewItem.reset(rLangItem.Clone()); + pNewItem->SetWhich(EE_CHAR_LANGUAGE_CJK); + rSet.Put( *pNewItem ); + pNewItem->SetWhich(EE_CHAR_LANGUAGE_CTL); + rSet.Put( *pNewItem ); +} + +static void lcl_ModifyRTLDefaults( SfxItemSet& rSet ) +{ + rSet.Put( SvxAdjustItem( SvxAdjust::Right, EE_PARA_JUST ) ); + + // always using rtl writing direction would break formulas + //rSet.Put( SvxFrameDirectionItem( SvxFrameDirection::Horizontal_RL_TB, EE_PARA_WRITINGDIR ) ); + + // PaperSize width is limited to USHRT_MAX in RTL mode (because of EditEngine's + // sal_uInt16 values in EditLine), so the text may be wrapped and line spacing must be + // increased to not see the beginning of the next line. + SvxLineSpacingItem aItem( LINE_SPACE_DEFAULT_HEIGHT, EE_PARA_SBL ); + aItem.SetPropLineSpace( 200 ); + rSet.Put( aItem ); +} + +static void lcl_ModifyRTLVisArea( EditView* pEditView ) +{ + tools::Rectangle aVisArea = pEditView->GetVisArea(); + Size aPaper = pEditView->GetEditEngine()->GetPaperSize(); + tools::Long nDiff = aPaper.Width() - aVisArea.Right(); + aVisArea.AdjustLeft(nDiff ); + aVisArea.AdjustRight(nDiff ); + pEditView->SetVisArea(aVisArea); +} + +void ScTextWnd::InitEditEngine() +{ + std::unique_ptr<ScFieldEditEngine> pNew; + ScDocShell* pDocSh = nullptr; + if ( mpViewShell ) + { + pDocSh = mpViewShell->GetViewData().GetDocShell(); + ScDocument& rDoc = mpViewShell->GetViewData().GetDocument(); + pNew = std::make_unique<ScFieldEditEngine>(&rDoc, rDoc.GetEnginePool(), rDoc.GetEditPool()); + } + else + pNew = std::make_unique<ScFieldEditEngine>(nullptr, EditEngine::CreatePool().get(), nullptr, true); + pNew->SetExecuteURL( false ); + m_xEditEngine = std::move(pNew); + + Size barSize = GetOutputSizePixel(); + m_xEditEngine->SetUpdateLayout( false ); + m_xEditEngine->SetPaperSize( GetDrawingArea()->get_ref_device().PixelToLogic(Size(barSize.Width(),10000)) ); + m_xEditEngine->SetWordDelimiters( + ScEditUtil::ModifyDelimiters( m_xEditEngine->GetWordDelimiters() ) ); + m_xEditEngine->SetReplaceLeadingSingleQuotationMark( false ); + + UpdateAutoCorrFlag(); + + { + auto pSet = std::make_unique<SfxItemSet>( m_xEditEngine->GetEmptyItemSet() ); + EditEngine::SetFontInfoInItemSet( *pSet, aTextFont ); + lcl_ExtendEditFontAttribs( *pSet ); + // turn off script spacing to match DrawText output + pSet->Put( SvxScriptSpaceItem( false, EE_PARA_ASIANCJKSPACING ) ); + if ( bIsRTL ) + lcl_ModifyRTLDefaults( *pSet ); + static_cast<ScEditEngineDefaulter*>(m_xEditEngine.get())->SetDefaults( std::move(pSet) ); + } + + // If the Cell contains URLFields, they need to be taken over into the entry row, + // or else the position is not correct anymore + bool bFilled = false; + ScInputHandler* pHdl = SC_MOD()->GetInputHdl(); + if ( pHdl ) //! Test if it's the right InputHdl? + bFilled = pHdl->GetTextAndFields(static_cast<ScEditEngineDefaulter&>(*m_xEditEngine)); + + m_xEditEngine->SetUpdateLayout( true ); + + // aString is the truth ... + if (bFilled && m_xEditEngine->GetText() == aString) + Invalidate(); // Repaint for (filled) Field + else + static_cast<ScEditEngineDefaulter*>(m_xEditEngine.get())->SetTextCurrentDefaults(aString); // At least the right text then + + m_xEditView = std::make_unique<EditView>(m_xEditEngine.get(), nullptr); + + // we get cursor, selection etc. messages from the VCL/window layer + // otherwise these are injected into the document causing confusion. + m_xEditView->SuppressLOKMessages(true); + + m_xEditView->setEditViewCallbacks(this); + m_xEditView->SetInsertMode(bIsInsertMode); + + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + Color aBgColor = rStyleSettings.GetWindowColor(); + m_xEditView->SetBackgroundColor(aBgColor); + + if (pAcc) + { + pAcc->InitAcc(nullptr, m_xEditView.get(), + ScResId(STR_ACC_EDITLINE_NAME), + ScResId(STR_ACC_EDITLINE_DESCR)); + } + + if (comphelper::LibreOfficeKit::isActive()) + m_xEditView->RegisterViewShell(mpViewShell); + + // Text from Clipboard is taken over as ASCII in a single row + EVControlBits n = m_xEditView->GetControlWord(); + m_xEditView->SetControlWord( n | EVControlBits::SINGLELINEPASTE ); + + m_xEditEngine->InsertView( m_xEditView.get(), EE_APPEND ); + + Resize(); + + if ( bIsRTL ) + lcl_ModifyRTLVisArea( m_xEditView.get() ); + + m_xEditEngine->SetModifyHdl(LINK(this, ScTextWnd, ModifyHdl)); + m_xEditEngine->SetStatusEventHdl(LINK(this, ScTextWnd, EditStatusHdl)); + + if (!maAccTextDatas.empty()) + maAccTextDatas.back()->StartEdit(); + + // as long as EditEngine and DrawText sometimes differ for CTL text, + // repaint now to have the EditEngine's version visible + if (pDocSh) + { + ScDocument& rDoc = pDocSh->GetDocument(); // any document + SvtScriptType nScript = rDoc.GetStringScriptType( aString ); + if ( nScript & SvtScriptType::COMPLEX ) + Invalidate(); + } +} + +ScTextWnd::ScTextWnd(ScTextWndGroup& rParent, ScTabViewShell* pViewSh) : + bIsRTL(AllSettings::GetLayoutRTL()), + bIsInsertMode(true), + bFormulaMode (false), + bInputMode (false), + mpViewShell(pViewSh), + mrGroupBar(rParent), + mnLastExpandedLines(INPUTWIN_MULTILINES), + mbInvalidate(false) +{ +} + +ScTextWnd::~ScTextWnd() +{ + while (!maAccTextDatas.empty()) { + maAccTextDatas.back()->Dispose(); + } +} + +bool ScTextWnd::MouseMove( const MouseEvent& rMEvt ) +{ + return m_xEditView && m_xEditView->MouseMove(rMEvt); +} + +bool ScTextWnd::CanFocus() const +{ + return SC_MOD()->IsEditMode(); +} + +bool ScTextWnd::MouseButtonDown( const MouseEvent& rMEvt ) +{ + if (!HasFocus()) + { + StartEditEngine(); + if (CanFocus()) + TextGrabFocus(); + } + + bool bClickOnSelection = false; + if (m_xEditView) + { + m_xEditView->SetEditEngineUpdateLayout( true ); + bClickOnSelection = m_xEditView->IsSelectionAtPoint(rMEvt.GetPosPixel()); + } + if (!bClickOnSelection) + { + rtl::Reference<TransferDataContainer> xTransferable(new TransferDataContainer); + GetDrawingArea()->enable_drag_source(xTransferable, DND_ACTION_NONE); + } + else + { + rtl::Reference<TransferDataContainer> xTransferable(m_xHelper); + GetDrawingArea()->enable_drag_source(xTransferable, DND_ACTION_COPY); + } + return WeldEditView::MouseButtonDown(rMEvt); +} + +bool ScTextWnd::MouseButtonUp( const MouseEvent& rMEvt ) +{ + bool bRet = WeldEditView::MouseButtonUp(rMEvt); + if (bRet) + { + if ( rMEvt.IsMiddle() && + Application::GetSettings().GetMouseSettings().GetMiddleButtonAction() == MouseMiddleButtonAction::PasteSelection ) + { + // EditView may have pasted from selection + SC_MOD()->InputChanged( m_xEditView.get() ); + } + else + SC_MOD()->InputSelection( m_xEditView.get() ); + } + return bRet; +} + +bool ScTextWnd::Command( const CommandEvent& rCEvt ) +{ + bool bConsumed = false; + + bInputMode = true; + CommandEventId nCommand = rCEvt.GetCommand(); + if (m_xEditView) + { + ScModule* pScMod = SC_MOD(); + ScTabViewShell* pStartViewSh = ScTabViewShell::GetActiveViewShell(); + + // don't modify the font defaults here - the right defaults are + // already set in StartEditEngine when the EditEngine is created + + // Prevent that the EditView is lost when switching between Views + pScMod->SetInEditCommand( true ); + m_xEditView->Command( rCEvt ); + pScMod->SetInEditCommand( false ); + + // CommandEventId::StartDrag does not mean by far that the content was actually changed, + // so don't trigger an InputChanged. + //! Detect if dragged with Move or forbid Drag&Move somehow + + if ( nCommand == CommandEventId::StartDrag ) + { + // Is dragged onto another View? + ScTabViewShell* pEndViewSh = ScTabViewShell::GetActiveViewShell(); + if ( pEndViewSh != pStartViewSh && pStartViewSh != nullptr ) + { + ScViewData& rViewData = pStartViewSh->GetViewData(); + ScInputHandler* pHdl = pScMod->GetInputHdl( pStartViewSh ); + if ( pHdl && rViewData.HasEditView( rViewData.GetActivePart() ) ) + { + pHdl->CancelHandler(); + rViewData.GetView()->ShowCursor(); // Missing for KillEditView, due to being inactive + } + } + } + else if ( nCommand == CommandEventId::EndExtTextInput ) + { + if (bFormulaMode) + { + ScInputHandler* pHdl = SC_MOD()->GetInputHdl(); + if (pHdl) + pHdl->InputCommand(rCEvt); + } + } + else if ( nCommand == CommandEventId::CursorPos ) + { + // don't call InputChanged for CommandEventId::CursorPos + } + else if ( nCommand == CommandEventId::InputLanguageChange ) + { + // #i55929# Font and font size state depends on input language if nothing is selected, + // so the slots have to be invalidated when the input language is changed. + + SfxViewFrame* pViewFrm = SfxViewFrame::Current(); + if (pViewFrm) + { + SfxBindings& rBindings = pViewFrm->GetBindings(); + rBindings.Invalidate( SID_ATTR_CHAR_FONT ); + rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT ); + } + } + else if ( nCommand == CommandEventId::ContextMenu ) + { + bConsumed = true; + SfxViewFrame* pViewFrm = SfxViewFrame::Current(); + if (pViewFrm) + { + Point aPos = rCEvt.GetMousePosPixel(); + if (!rCEvt.IsMouseEvent()) + { + Size aSize = GetOutputSizePixel(); + aPos = Point(aSize.Width() / 2, aSize.Height() / 2); + } + if (IsMouseCaptured()) + ReleaseMouse(); + pViewFrm->GetDispatcher()->ExecutePopup("formulabar", &mrGroupBar.GetVclParent(), &aPos); + } + } + else if ( nCommand == CommandEventId::Wheel ) + { + //don't call InputChanged for CommandEventId::Wheel + } + else if ( nCommand == CommandEventId::Swipe ) + { + //don't call InputChanged for CommandEventId::Swipe + } + else if ( nCommand == CommandEventId::LongPress ) + { + //don't call InputChanged for CommandEventId::LongPress + } + else if ( nCommand == CommandEventId::ModKeyChange ) + { + //pass alt press/release to parent impl + } + else + SC_MOD()->InputChanged( m_xEditView.get() ); + } + + if ( comphelper::LibreOfficeKit::isActive() && nCommand == CommandEventId::CursorPos ) + { + // LOK uses this to setup caret position because drawingarea is replaced + // with text input field, it sends logical caret position (start, end) not pixels + + StartEditEngine(); + TextGrabFocus(); + + if (!m_xEditView) + return true; + + Point aSelectionStartEnd = rCEvt.GetMousePosPixel(); + m_xEditView->SetSelection(ESelection(0, aSelectionStartEnd.X(), + 0, aSelectionStartEnd.Y())); + + SC_MOD()->InputSelection( m_xEditView.get() ); + + bConsumed = true; + } + + bInputMode = false; + + return bConsumed; +} + +bool ScTextWnd::StartDrag() +{ + // tdf#145248 don't start a drag if actively selecting + if (m_xEditView && !m_xEditEngine->IsInSelectionMode()) + { + OUString sSelection = m_xEditView->GetSelected(); + m_xHelper->SetData(sSelection); + return sSelection.isEmpty(); + } + return true; +} + +bool ScTextWnd::KeyInput(const KeyEvent& rKEvt) +{ + bool bUsed = true; + bInputMode = true; + if (!SC_MOD()->InputKeyEvent( rKEvt )) + { + bUsed = false; + ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell(); + if ( pViewSh ) + bUsed = pViewSh->SfxKeyInput(rKEvt); // Only accelerators, no input + } + bInputMode = false; + return bUsed; +} + +void ScTextWnd::GetFocus() +{ + ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell(); + if ( pViewSh ) + pViewSh->SetFormShellAtTop( false ); // focus in input line -> FormShell no longer on top + WeldEditView::GetFocus(); +} + +void ScTextWnd::SetFormulaMode( bool bSet ) +{ + if ( bSet != bFormulaMode ) + { + bFormulaMode = bSet; + UpdateAutoCorrFlag(); + } +} + +void ScTextWnd::UpdateAutoCorrFlag() +{ + if (m_xEditEngine) + { + EEControlBits nControl = m_xEditEngine->GetControlWord(); + EEControlBits nOld = nControl; + if ( bFormulaMode ) + nControl &= ~EEControlBits::AUTOCORRECT; // No AutoCorrect in Formulas + else + nControl |= EEControlBits::AUTOCORRECT; // Else do enable it + + if ( nControl != nOld ) + m_xEditEngine->SetControlWord( nControl ); + } +} + +void ScTextWnd::EditViewScrollStateChange() +{ + // editengine height has changed or editview scroll pos has changed + SetScrollBarRange(); +} + +IMPL_LINK_NOARG(ScTextWnd, ModifyHdl, LinkParamNone*, void) +{ + if (m_xEditView && !bInputMode) + { + ScInputHandler* pHdl = SC_MOD()->GetInputHdl(); + + // Use the InputHandler's InOwnChange flag to prevent calling InputChanged + // while an InputHandler method is modifying the EditEngine content + + if ( pHdl && !pHdl->IsInOwnChange() ) + pHdl->InputChanged( m_xEditView.get(), true ); // #i20282# InputChanged must know if called from modify handler + } +} + +IMPL_LINK_NOARG(ScTextWnd, EditStatusHdl, EditStatus&, void) +{ + SetScrollBarRange(); + DoScroll(); + Invalidate(); +} + +void ScTextWnd::StopEditEngine( bool bAll ) +{ + if (!m_xEditEngine) + return; + + if (m_xEditView) + { + if (!maAccTextDatas.empty()) + maAccTextDatas.back()->EndEdit(); + + ScModule* pScMod = SC_MOD(); + + if (!bAll) + pScMod->InputSelection( m_xEditView.get() ); + aString = m_xEditEngine->GetText(); + bIsInsertMode = m_xEditView->IsInsertMode(); + bool bSelection = m_xEditView->HasSelection(); + m_xEditEngine->SetStatusEventHdl(Link<EditStatus&, void>()); + m_xEditEngine->SetModifyHdl(Link<LinkParamNone*,void>()); + m_xEditView.reset(); + m_xEditEngine.reset(); + + ScInputHandler* pHdl = mpViewShell->GetInputHandler(); + + if (pHdl && pHdl->IsEditMode() && !bAll) + pHdl->SetMode(SC_INPUT_TABLE); + + SfxViewFrame* pViewFrm = SfxViewFrame::Current(); + if (pViewFrm) + pViewFrm->GetBindings().Invalidate( SID_ATTR_INSERT ); + + if (bSelection) + Invalidate(); // So that the Selection is not left there + } + + if (comphelper::LibreOfficeKit::isActive()) + { + // Clear + std::vector<ReferenceMark> aReferenceMarks; + ScInputHandler::SendReferenceMarks( mpViewShell, aReferenceMarks ); + } +} + +static sal_Int32 findFirstNonMatchingChar(const OUString& rStr1, const OUString& rStr2) +{ + // Search the string for unmatching chars + const sal_Unicode* pStr1 = rStr1.getStr(); + const sal_Unicode* pStr2 = rStr2.getStr(); + sal_Int32 i = 0; + while ( i < rStr1.getLength() ) + { + // Abort on the first unmatching char + if ( *pStr1 != *pStr2 ) + return i; + ++pStr1; + ++pStr2; + ++i; + } + + return i; +} + +void ScTextWnd::SetTextString( const OUString& rNewString ) +{ + if (comphelper::LibreOfficeKit::isActive()) + { + ESelection aSel = m_xEditView ? m_xEditView->GetSelection() : ESelection(); + ScInputHandler::LOKSendFormulabarUpdate(SfxViewShell::Current(), rNewString, aSel); + } + + // Ideally it would be best to create on demand the EditEngine/EditView here, but... for + // the initialisation scenario where a cell is first clicked on we end up with the text in the + // inputbar window scrolled to the bottom if we do that here ( because the tableview and topview + // are synced I guess ). + // should fix that I suppose :-/ need to look a bit further into that + mbInvalidate = true; // ensure next Paint ( that uses editengine ) call will call Invalidate first + + if ( rNewString != aString ) + { + bInputMode = true; + + // Find position of the change, only paint the rest + if (!m_xEditEngine) + { + bool bPaintAll = GetNumLines() > 1 || bIsRTL; + if (!bPaintAll) + { + // test if CTL script type is involved + SvtScriptType nOldScript = SvtScriptType::NONE; + SvtScriptType nNewScript = SvtScriptType::NONE; + SfxObjectShell* pObjSh = SfxObjectShell::Current(); + if ( auto pDocShell = dynamic_cast<ScDocShell*>( pObjSh) ) + { + // any document can be used (used only for its break iterator) + ScDocument& rDoc = pDocShell->GetDocument(); + nOldScript = rDoc.GetStringScriptType( aString ); + nNewScript = rDoc.GetStringScriptType( rNewString ); + } + bPaintAll = ( nOldScript & SvtScriptType::COMPLEX ) || ( nNewScript & SvtScriptType::COMPLEX ); + } + + if ( bPaintAll ) + { + // In multiline mode, or if CTL is involved, the whole text has to be redrawn + Invalidate(); + } + else + { + tools::Long nTextSize = 0; + sal_Int32 nDifPos; + if (rNewString.getLength() > aString.getLength()) + nDifPos = findFirstNonMatchingChar(rNewString, aString); + else + nDifPos = findFirstNonMatchingChar(aString, rNewString); + + tools::Long nSize1 = GetTextWidth(aString); + tools::Long nSize2 = GetTextWidth(rNewString); + if ( nSize1>0 && nSize2>0 ) + nTextSize = std::max( nSize1, nSize2 ); + else + nTextSize = GetOutputSizePixel().Width(); // Overflow + + Point aLogicStart = GetDrawingArea()->get_ref_device().PixelToLogic(Point(0,0)); + tools::Long nStartPos = aLogicStart.X(); + tools::Long nInvPos = nStartPos; + if (nDifPos) + nInvPos += GetTextWidth(aString.copy(0,nDifPos)); + + Invalidate(tools::Rectangle(nInvPos, 0, nStartPos+nTextSize, GetOutputSizePixel().Height() - 1)); + } + } + else + { + static_cast<ScEditEngineDefaulter*>(m_xEditEngine.get())->SetTextCurrentDefaults(rNewString); + } + + aString = rNewString; + + if (!maAccTextDatas.empty()) + maAccTextDatas.back()->TextChanged(); + + bInputMode = false; + } + + SetScrollBarRange(); + DoScroll(); +} + +const OUString& ScTextWnd::GetTextString() const +{ + return aString; +} + +bool ScTextWnd::IsInputActive() +{ + return HasFocus(); +} + +void ScTextWnd::MakeDialogEditView() +{ + if ( m_xEditView ) return; + + std::unique_ptr<ScFieldEditEngine> pNew; + ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell(); + if ( pViewSh ) + { + ScDocument& rDoc = pViewSh->GetViewData().GetDocument(); + pNew = std::make_unique<ScFieldEditEngine>(&rDoc, rDoc.GetEnginePool(), rDoc.GetEditPool()); + } + else + pNew = std::make_unique<ScFieldEditEngine>(nullptr, EditEngine::CreatePool().get(), nullptr, true); + pNew->SetExecuteURL( false ); + m_xEditEngine = std::move(pNew); + + const bool bPrevUpdateLayout = m_xEditEngine->SetUpdateLayout( false ); + m_xEditEngine->SetWordDelimiters( m_xEditEngine->GetWordDelimiters() + "=" ); + m_xEditEngine->SetPaperSize( Size( bIsRTL ? USHRT_MAX : THESIZE, 300 ) ); + + auto pSet = std::make_unique<SfxItemSet>( m_xEditEngine->GetEmptyItemSet() ); + EditEngine::SetFontInfoInItemSet( *pSet, aTextFont ); + lcl_ExtendEditFontAttribs( *pSet ); + if ( bIsRTL ) + lcl_ModifyRTLDefaults( *pSet ); + static_cast<ScEditEngineDefaulter*>(m_xEditEngine.get())->SetDefaults( std::move(pSet) ); + m_xEditEngine->SetUpdateLayout( bPrevUpdateLayout ); + + m_xEditView = std::make_unique<EditView>(m_xEditEngine.get(), nullptr); + m_xEditView->setEditViewCallbacks(this); + + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + Color aBgColor = rStyleSettings.GetWindowColor(); + m_xEditView->SetBackgroundColor(aBgColor); + + if (pAcc) + { + pAcc->InitAcc(nullptr, m_xEditView.get(), + ScResId(STR_ACC_EDITLINE_NAME), + ScResId(STR_ACC_EDITLINE_DESCR)); + } + + if (comphelper::LibreOfficeKit::isActive()) + m_xEditView->RegisterViewShell(mpViewShell); + m_xEditEngine->InsertView( m_xEditView.get(), EE_APPEND ); + + Resize(); + + if ( bIsRTL ) + lcl_ModifyRTLVisArea( m_xEditView.get() ); + + if (!maAccTextDatas.empty()) + maAccTextDatas.back()->StartEdit(); +} + +void ScTextWnd::ImplInitSettings() +{ + bIsRTL = AllSettings::GetLayoutRTL(); + + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + + Color aBgColor= rStyleSettings.GetWindowColor(); + Color aTxtColor= rStyleSettings.GetWindowTextColor(); + + aTextFont.SetFillColor ( aBgColor ); + aTextFont.SetColor (aTxtColor); + Invalidate(); +} + +void ScTextWnd::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + // bypass WeldEditView::SetDrawingArea + weld::CustomWidgetController::SetDrawingArea(pDrawingArea); + + // set cursor + pDrawingArea->set_cursor(PointerStyle::Text); + + // initialize dnd, deliberately just a simple string so + // we don't transfer the happenstance formatting in + // the input line + m_xHelper.set(new svt::OStringTransferable(OUString())); + rtl::Reference<TransferDataContainer> xHelper(m_xHelper); + SetDragDataTransferrable(xHelper, DND_ACTION_COPY); + + OutputDevice& rDevice = pDrawingArea->get_ref_device(); + pDrawingArea->set_margin_start(gnBorderWidth); + pDrawingArea->set_margin_end(gnBorderWidth); + // leave 1 for the width of the scrolledwindow border + pDrawingArea->set_margin_top(gnBorderHeight - 1); + pDrawingArea->set_margin_bottom(gnBorderHeight - 1); + + // always use application font, so a font with cjk chars can be installed + vcl::Font aAppFont = Application::GetSettings().GetStyleSettings().GetAppFont(); + weld::SetPointFont(rDevice, aAppFont); + + aTextFont = rDevice.GetFont(); + Size aFontSize = aTextFont.GetFontSize(); + aTextFont.SetFontSize(rDevice.PixelToLogic(aFontSize, MapMode(MapUnit::MapTwip))); + + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + + Color aBgColor = rStyleSettings.GetWindowColor(); + Color aTxtColor = rStyleSettings.GetWindowTextColor(); + + aTextFont.SetTransparent(true); + aTextFont.SetFillColor(aBgColor); + aTextFont.SetColor(aTxtColor); + aTextFont.SetWeight(WEIGHT_NORMAL); + + Size aSize(1, GetPixelHeightForLines(1)); + pDrawingArea->set_size_request(aSize.Width(), aSize.Height()); + + rDevice.SetBackground(aBgColor); + rDevice.SetLineColor(COL_BLACK); + rDevice.SetMapMode(MapMode(MapUnit::MapTwip)); + rDevice.SetFont(aTextFont); + + EnableRTL(false); // EditEngine can't be used with VCL EnableRTL +} + +css::uno::Reference< css::accessibility::XAccessible > ScTextWnd::CreateAccessible() +{ + pAcc = new ScAccessibleEditLineObject(this); + return pAcc; +} + +void ScTextWnd::InsertAccessibleTextData( ScAccessibleEditLineTextData& rTextData ) +{ + OSL_ENSURE( ::std::find( maAccTextDatas.begin(), maAccTextDatas.end(), &rTextData ) == maAccTextDatas.end(), + "ScTextWnd::InsertAccessibleTextData - passed object already registered" ); + maAccTextDatas.push_back( &rTextData ); +} + +void ScTextWnd::RemoveAccessibleTextData( ScAccessibleEditLineTextData& rTextData ) +{ + AccTextDataVector::iterator aEnd = maAccTextDatas.end(); + AccTextDataVector::iterator aIt = ::std::find( maAccTextDatas.begin(), aEnd, &rTextData ); + OSL_ENSURE( aIt != aEnd, "ScTextWnd::RemoveAccessibleTextData - passed object not registered" ); + if( aIt != aEnd ) + maAccTextDatas.erase( aIt ); +} + +void ScTextWnd::StyleUpdated() +{ + ImplInitSettings(); + CustomWidgetController::Invalidate(); +} + +void ScTextWnd::TextGrabFocus() +{ + GrabFocus(); +} + +// Position window +ScPosWnd::ScPosWnd(vcl::Window* pParent) + : InterimItemWindow(pParent, "modules/scalc/ui/posbox.ui", "PosBox") + , m_xWidget(m_xBuilder->weld_combo_box("pos_window")) + , m_nAsyncGetFocusId(nullptr) + , nTipVisible(nullptr) + , bFormulaMode(false) +{ + InitControlBase(m_xWidget.get()); + + // Use calculation according to tdf#132338 to align combobox width to width of fontname combobox within formatting toolbar; + // formatting toolbar is placed above formulabar when using multiple toolbars typically + + m_xWidget->set_entry_width_chars(1); + Size aSize(LogicToPixel(Size(POSITION_COMBOBOX_WIDTH * 4, 0), MapMode(MapUnit::MapAppFont))); + m_xWidget->set_size_request(aSize.Width(), -1); + SetSizePixel(m_xContainer->get_preferred_size()); + + FillRangeNames(); + + StartListening( *SfxGetpApp() ); // For Navigator rangename updates + + m_xWidget->connect_key_press(LINK(this, ScPosWnd, KeyInputHdl)); + m_xWidget->connect_entry_activate(LINK(this, ScPosWnd, ActivateHdl)); + m_xWidget->connect_changed(LINK(this, ScPosWnd, ModifyHdl)); + m_xWidget->connect_focus_in(LINK(this, ScPosWnd, FocusInHdl)); + m_xWidget->connect_focus_out(LINK(this, ScPosWnd, FocusOutHdl)); +} + +ScPosWnd::~ScPosWnd() +{ + disposeOnce(); +} + +void ScPosWnd::dispose() +{ + EndListening( *SfxGetpApp() ); + + HideTip(); + + if (m_nAsyncGetFocusId) + { + Application::RemoveUserEvent(m_nAsyncGetFocusId); + m_nAsyncGetFocusId = nullptr; + } + m_xWidget.reset(); + + InterimItemWindow::dispose(); +} + +void ScPosWnd::SetFormulaMode( bool bSet ) +{ + if ( bSet != bFormulaMode ) + { + bFormulaMode = bSet; + + if ( bSet ) + FillFunctions(); + else + FillRangeNames(); + + HideTip(); + } +} + +void ScPosWnd::SetPos( const OUString& rPosStr ) +{ + if ( aPosStr != rPosStr ) + { + aPosStr = rPosStr; + m_xWidget->set_entry_text(aPosStr); + } +} + +// static +OUString ScPosWnd::createLocalRangeName(std::u16string_view rName, std::u16string_view rTableName) +{ + return OUString::Concat(rName) + " (" + rTableName + ")"; +} + +void ScPosWnd::FillRangeNames() +{ + m_xWidget->clear(); + m_xWidget->freeze(); + + SfxObjectShell* pObjSh = SfxObjectShell::Current(); + if ( auto pDocShell = dynamic_cast<ScDocShell*>( pObjSh) ) + { + ScDocument& rDoc = pDocShell->GetDocument(); + + m_xWidget->append_text(ScResId(STR_MANAGE_NAMES)); + m_xWidget->append_separator("separator"); + + ScRange aDummy; + std::set<OUString> aSet; + ScRangeName* pRangeNames = rDoc.GetRangeName(); + for (const auto& rEntry : *pRangeNames) + { + if (rEntry.second->IsValidReference(aDummy)) + aSet.insert(rEntry.second->GetName()); + } + for (SCTAB i = 0; i < rDoc.GetTableCount(); ++i) + { + ScRangeName* pLocalRangeName = rDoc.GetRangeName(i); + if (pLocalRangeName && !pLocalRangeName->empty()) + { + OUString aTableName; + rDoc.GetName(i, aTableName); + for (const auto& rEntry : *pLocalRangeName) + { + if (rEntry.second->IsValidReference(aDummy)) + aSet.insert(createLocalRangeName(rEntry.second->GetName(), aTableName)); + } + } + } + + for (const auto& rItem : aSet) + { + m_xWidget->append_text(rItem); + } + } + m_xWidget->thaw(); + m_xWidget->set_entry_text(aPosStr); +} + +void ScPosWnd::FillFunctions() +{ + m_xWidget->clear(); + m_xWidget->freeze(); + + OUString aFirstName; + const ScAppOptions& rOpt = SC_MOD()->GetAppOptions(); + sal_uInt16 nMRUCount = rOpt.GetLRUFuncListCount(); + const sal_uInt16* pMRUList = rOpt.GetLRUFuncList(); + if (pMRUList) + { + const ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList(); + sal_uInt32 nListCount = pFuncList->GetCount(); + for (sal_uInt16 i=0; i<nMRUCount; i++) + { + sal_uInt16 nId = pMRUList[i]; + for (sal_uInt32 j=0; j<nListCount; j++) + { + const ScFuncDesc* pDesc = pFuncList->GetFunction( j ); + if ( pDesc->nFIndex == nId && pDesc->mxFuncName ) + { + m_xWidget->append_text(*pDesc->mxFuncName); + if (aFirstName.isEmpty()) + aFirstName = *pDesc->mxFuncName; + break; // Stop searching + } + } + } + } + + //! Re-add entry "Other..." for Function AutoPilot if it can work with text that + // has been entered so far + + // m_xWidget->append_text(ScResId(STR_FUNCTIONLIST_MORE)); + + m_xWidget->thaw(); + m_xWidget->set_entry_text(aFirstName); +} + +void ScPosWnd::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if ( bFormulaMode ) + return; + + // Does the list of range names need updating? + if ( auto pEventHint = dynamic_cast<const SfxEventHint*>(&rHint) ) + { + SfxEventHintId nEventId = pEventHint->GetEventId(); + if ( nEventId == SfxEventHintId::ActivateDoc ) + FillRangeNames(); + } + else + { + const SfxHintId nHintId = rHint.GetId(); + if (nHintId == SfxHintId::ScAreasChanged || nHintId == SfxHintId::ScNavigatorUpdateAll) + FillRangeNames(); + } +} + +void ScPosWnd::HideTip() +{ + if (nTipVisible) + { + Help::HidePopover(this, nTipVisible); + nTipVisible = nullptr; + } +} + +static ScNameInputType lcl_GetInputType( const OUString& rText ) +{ + ScNameInputType eRet = SC_NAME_INPUT_BAD_NAME; // the more general error + + ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell(); + if ( pViewSh ) + { + ScViewData& rViewData = pViewSh->GetViewData(); + ScDocument& rDoc = rViewData.GetDocument(); + SCTAB nTab = rViewData.GetTabNo(); + ScAddress::Details aDetails( rDoc.GetAddressConvention()); + + // test in same order as in SID_CURRENTCELL execute + + ScRange aRange; + ScAddress aAddress; + SCTAB nNameTab; + sal_Int32 nNumeric; + + // From the context we know that when testing for a range name + // sheet-local scope names have " (sheetname)" appended and global + // names don't and can't contain ')', so we can force one or the other. + const RutlNameScope eNameScope = + ((!rText.isEmpty() && rText[rText.getLength()-1] == ')') ? RUTL_NAMES_LOCAL : RUTL_NAMES_GLOBAL); + + if (rText == ScResId(STR_MANAGE_NAMES)) + eRet = SC_MANAGE_NAMES; + else if ( aRange.Parse( rText, rDoc, aDetails ) & ScRefFlags::VALID ) + eRet = SC_NAME_INPUT_RANGE; + else if ( aAddress.Parse( rText, rDoc, aDetails ) & ScRefFlags::VALID ) + eRet = SC_NAME_INPUT_CELL; + else if ( ScRangeUtil::MakeRangeFromName( rText, rDoc, nTab, aRange, eNameScope, aDetails ) ) + { + eRet = ((eNameScope == RUTL_NAMES_LOCAL) ? SC_NAME_INPUT_NAMEDRANGE_LOCAL : + SC_NAME_INPUT_NAMEDRANGE_GLOBAL); + } + else if ( ScRangeUtil::MakeRangeFromName( rText, rDoc, nTab, aRange, RUTL_DBASE, aDetails ) ) + eRet = SC_NAME_INPUT_DATABASE; + else if ( comphelper::string::isdigitAsciiString( rText ) && + ( nNumeric = rText.toInt32() ) > 0 && nNumeric <= rDoc.MaxRow()+1 ) + eRet = SC_NAME_INPUT_ROW; + else if ( rDoc.GetTable( rText, nNameTab ) ) + eRet = SC_NAME_INPUT_SHEET; + else if (ScRangeData::IsNameValid(rText, rDoc) + == ScRangeData::IsNameValidType::NAME_VALID) // nothing found, create new range? + { + if ( rViewData.GetSimpleArea( aRange ) == SC_MARK_SIMPLE ) + eRet = SC_NAME_INPUT_DEFINE; + else + eRet = SC_NAME_INPUT_BAD_SELECTION; + } + else + eRet = SC_NAME_INPUT_BAD_NAME; + } + + return eRet; +} + +IMPL_LINK_NOARG(ScPosWnd, ModifyHdl, weld::ComboBox&, void) +{ + HideTip(); + + if (m_xWidget->changed_by_direct_pick()) + { + DoEnter(); + return; + } + + if (bFormulaMode) + return; + + // determine the action that would be taken for the current input + + ScNameInputType eType = lcl_GetInputType(m_xWidget->get_active_text()); // uses current view + TranslateId pStrId; + switch ( eType ) + { + case SC_NAME_INPUT_CELL: + pStrId = STR_NAME_INPUT_CELL; + break; + case SC_NAME_INPUT_RANGE: + case SC_NAME_INPUT_NAMEDRANGE_LOCAL: + case SC_NAME_INPUT_NAMEDRANGE_GLOBAL: + pStrId = STR_NAME_INPUT_RANGE; // named range or range reference + break; + case SC_NAME_INPUT_DATABASE: + pStrId = STR_NAME_INPUT_DBRANGE; + break; + case SC_NAME_INPUT_ROW: + pStrId = STR_NAME_INPUT_ROW; + break; + case SC_NAME_INPUT_SHEET: + pStrId = STR_NAME_INPUT_SHEET; + break; + case SC_NAME_INPUT_DEFINE: + pStrId = STR_NAME_INPUT_DEFINE; + break; + default: + // other cases (error): no tip help + break; + } + + if (!pStrId) + return; + + // show the help tip at the text cursor position + Point aPos; + vcl::Cursor* pCur = GetCursor(); + if (pCur) + aPos = LogicToPixel( pCur->GetPos() ); + aPos = OutputToScreenPixel( aPos ); + tools::Rectangle aRect( aPos, aPos ); + + OUString aText = ScResId(pStrId); + QuickHelpFlags const nAlign = QuickHelpFlags::Left|QuickHelpFlags::Bottom; + nTipVisible = Help::ShowPopover(this, aRect, aText, nAlign); +} + +void ScPosWnd::DoEnter() +{ + OUString aText = m_xWidget->get_active_text(); + if ( !aText.isEmpty() ) + { + if ( bFormulaMode ) + { + ScModule* pScMod = SC_MOD(); + if ( aText == ScResId(STR_FUNCTIONLIST_MORE) ) + { + // Function AutoPilot + //! Continue working with the text entered so far + + //! new method at ScModule to query if function autopilot is open + SfxViewFrame* pViewFrm = SfxViewFrame::Current(); + if ( pViewFrm && !pViewFrm->GetChildWindow( SID_OPENDLG_FUNCTION ) ) + pViewFrm->GetDispatcher()->Execute( SID_OPENDLG_FUNCTION, + SfxCallMode::SYNCHRON | SfxCallMode::RECORD ); + } + else + { + ScTabViewShell* pViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() ); + ScInputHandler* pHdl = pScMod->GetInputHdl( pViewSh ); + if (pHdl) + pHdl->InsertFunction( aText ); + } + } + else + { + // depending on the input, select something or create a new named range + + ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell(); + if ( pViewSh ) + { + ScViewData& rViewData = pViewSh->GetViewData(); + ScDocShell* pDocShell = rViewData.GetDocShell(); + ScDocument& rDoc = pDocShell->GetDocument(); + + ScNameInputType eType = lcl_GetInputType( aText ); + if ( eType == SC_NAME_INPUT_BAD_NAME || eType == SC_NAME_INPUT_BAD_SELECTION ) + { + TranslateId pId = (eType == SC_NAME_INPUT_BAD_NAME) ? STR_NAME_ERROR_NAME : STR_NAME_ERROR_SELECTION; + pViewSh->ErrorMessage(pId); + } + else if ( eType == SC_NAME_INPUT_DEFINE ) + { + ScRangeName* pNames = rDoc.GetRangeName(); + ScRange aSelection; + if ( pNames && !pNames->findByUpperName(ScGlobal::getCharClass().uppercase(aText)) && + (rViewData.GetSimpleArea( aSelection ) == SC_MARK_SIMPLE) ) + { + ScRangeName aNewRanges( *pNames ); + ScAddress aCursor( rViewData.GetCurX(), rViewData.GetCurY(), rViewData.GetTabNo() ); + OUString aContent(aSelection.Format(rDoc, ScRefFlags::RANGE_ABS_3D, rDoc.GetAddressConvention())); + ScRangeData* pNew = new ScRangeData( rDoc, aText, aContent, aCursor ); + if ( aNewRanges.insert(pNew) ) + { + pDocShell->GetDocFunc().ModifyRangeNames( aNewRanges ); + pViewSh->UpdateInputHandler(true); + } + } + } + else if (eType == SC_MANAGE_NAMES) + { + sal_uInt16 nId = ScNameDlgWrapper::GetChildWindowId(); + SfxViewFrame* pViewFrm = pViewSh->GetViewFrame(); + SfxChildWindow* pWnd = pViewFrm->GetChildWindow( nId ); + + SC_MOD()->SetRefDialog( nId, pWnd == nullptr ); + } + else + { + bool bForceGlobalName = false; + // for all selection types, execute the SID_CURRENTCELL slot. + if (eType == SC_NAME_INPUT_CELL || eType == SC_NAME_INPUT_RANGE) + { + // Note that SID_CURRENTCELL always expects address to + // be in Calc A1 format. Convert the text. + ScRange aRange(0,0, rViewData.GetTabNo()); + aRange.ParseAny(aText, rDoc, rDoc.GetAddressConvention()); + aText = aRange.Format(rDoc, ScRefFlags::RANGE_ABS_3D, ::formula::FormulaGrammar::CONV_OOO); + } + else if (eType == SC_NAME_INPUT_NAMEDRANGE_GLOBAL) + { + bForceGlobalName = true; + } + + SfxStringItem aPosItem( SID_CURRENTCELL, aText ); + SfxBoolItem aUnmarkItem( FN_PARAM_1, true ); // remove existing selection + // FN_PARAM_2 reserved for AlignToCursor + SfxBoolItem aForceGlobalName( FN_PARAM_3, bForceGlobalName ); + + pViewSh->GetViewData().GetDispatcher().ExecuteList( SID_CURRENTCELL, + SfxCallMode::SYNCHRON | SfxCallMode::RECORD, + { &aPosItem, &aUnmarkItem, &aForceGlobalName }); + } + } + } + } + else + m_xWidget->set_entry_text(aPosStr); + + ReleaseFocus_Impl(); +} + +IMPL_LINK_NOARG(ScPosWnd, ActivateHdl, weld::ComboBox&, bool) +{ + DoEnter(); + return true; +} + +IMPL_LINK(ScPosWnd, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + bool bHandled = true; + + switch (rKEvt.GetKeyCode().GetCode()) + { + case KEY_RETURN: + bHandled = ActivateHdl(*m_xWidget); + break; + case KEY_ESCAPE: + if (nTipVisible) + { + // escape when the tip help is shown: only hide the tip + HideTip(); + } + else + { + if (!bFormulaMode) + m_xWidget->set_entry_text(aPosStr); + ReleaseFocus_Impl(); + } + break; + default: + bHandled = false; + break; + } + + return bHandled || ChildKeyInput(rKEvt); +} + +IMPL_LINK_NOARG(ScPosWnd, OnAsyncGetFocus, void*, void) +{ + m_nAsyncGetFocusId = nullptr; + m_xWidget->select_entry_region(0, -1); +} + +IMPL_LINK_NOARG(ScPosWnd, FocusInHdl, weld::Widget&, void) +{ + if (m_nAsyncGetFocusId) + return; + // do it async to defeat entry in combobox having its own ideas about the focus + m_nAsyncGetFocusId = Application::PostUserEvent(LINK(this, ScPosWnd, OnAsyncGetFocus)); +} + +IMPL_LINK_NOARG(ScPosWnd, FocusOutHdl, weld::Widget&, void) +{ + if (m_nAsyncGetFocusId) + { + Application::RemoveUserEvent(m_nAsyncGetFocusId); + m_nAsyncGetFocusId = nullptr; + } + + HideTip(); +} + +void ScPosWnd::ReleaseFocus_Impl() +{ + HideTip(); + + SfxViewShell* pCurSh = SfxViewShell::Current(); + ScInputHandler* pHdl = SC_MOD()->GetInputHdl( dynamic_cast<ScTabViewShell*>( pCurSh ) ); + if ( pHdl && pHdl->IsTopMode() ) + { + // Focus back in input row? + ScInputWindow* pInputWin = pHdl->GetInputWindow(); + if (pInputWin) + { + pInputWin->TextGrabFocus(); + return; + } + } + + // Set focus to active View + if ( pCurSh ) + { + vcl::Window* pShellWnd = pCurSh->GetWindow(); + + if ( pShellWnd ) + pShellWnd->GrabFocus(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/app/lnktrans.cxx b/sc/source/ui/app/lnktrans.cxx new file mode 100644 index 000000000..1ff48e7fe --- /dev/null +++ b/sc/source/ui/app/lnktrans.cxx @@ -0,0 +1,76 @@ +/* -*- 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 <svl/urlbmk.hxx> + +#include <lnktrans.hxx> +#include <scmod.hxx> + +using namespace com::sun::star; + +ScLinkTransferObj::ScLinkTransferObj() +{ +} + +ScLinkTransferObj::~ScLinkTransferObj() +{ +} + +void ScLinkTransferObj::SetLinkURL( const OUString& rURL, const OUString& rText ) +{ + aLinkURL = rURL; + aLinkText = rText; +} + +void ScLinkTransferObj::AddSupportedFormats() +{ + if ( !aLinkURL.isEmpty() ) + { + // TransferableHelper::SetINetBookmark formats + + AddFormat( SotClipboardFormatId::SOLK ); + AddFormat( SotClipboardFormatId::STRING ); + AddFormat( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ); + AddFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK ); + AddFormat( SotClipboardFormatId::FILEGRPDESCRIPTOR ); + AddFormat( SotClipboardFormatId::FILECONTENT ); + } +} + +bool ScLinkTransferObj::GetData( + const css::datatransfer::DataFlavor& rFlavor, const OUString& /*rDestDoc*/ ) +{ + bool bOK = false; + if ( !aLinkURL.isEmpty() ) + { + INetBookmark aBmk( aLinkURL, aLinkText ); + bOK = SetINetBookmark( aBmk, rFlavor ); + } + return bOK; +} + +void ScLinkTransferObj::DragFinished( sal_Int8 nDropAction ) +{ + ScModule* pScMod = SC_MOD(); + pScMod->ResetDragObject(); + + TransferDataContainer::DragFinished( nDropAction ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/app/msgpool.cxx b/sc/source/ui/app/msgpool.cxx new file mode 100644 index 000000000..58daba682 --- /dev/null +++ b/sc/source/ui/app/msgpool.cxx @@ -0,0 +1,93 @@ +/* -*- 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 <scitems.hxx> + +#include <sc.hrc> +#include <docpool.hxx> +#include <msgpool.hxx> + +SfxItemInfo const aMsgItemInfos[] = +{ + { 0, true }, // SCITEM_STRING + { 0, true }, // SCITEM_SEARCHDATA - stop using this! + { SID_SORT, true }, // SCITEM_SORTDATA + { SID_QUERY, true }, // SCITEM_QUERYDATA + { SID_SUBTOTALS, true }, // SCITEM_SUBTDATA + { SID_CONSOLIDATE, true }, // SCITEM_CONSOLIDATEDATA + { SID_PIVOT_TABLE, true }, // SCITEM_PIVOTDATA + { SID_SOLVE, true }, // SCITEM_SOLVEDATA + { SID_SCUSERLISTS, true }, // SCITEM_USERLIST + { 0, false } // SCITEM_CONDFORMATDLGDATA +}; + +ScMessagePool::ScMessagePool() + : SfxItemPool ( "ScMessagePool", + MSGPOOL_START, MSGPOOL_END, + aMsgItemInfos, nullptr ), + + aGlobalStringItem ( SfxStringItem ( SCITEM_STRING, OUString() ) ), + aGlobalSearchItem ( SvxSearchItem ( SCITEM_SEARCHDATA ) ), + aGlobalSortItem ( ScSortItem ( SCITEM_SORTDATA, nullptr ) ), + aGlobalQueryItem ( ScQueryItem ( SCITEM_QUERYDATA, nullptr, nullptr ) ), + aGlobalSubTotalItem ( ScSubTotalItem ( SCITEM_SUBTDATA, nullptr, nullptr ) ), + aGlobalConsolidateItem ( ScConsolidateItem ( SCITEM_CONSOLIDATEDATA, nullptr ) ), + aGlobalPivotItem ( ScPivotItem ( SCITEM_PIVOTDATA, nullptr, nullptr, false ) ), + aGlobalSolveItem ( ScSolveItem ( SCITEM_SOLVEDATA, nullptr ) ), + aGlobalUserListItem ( ScUserListItem ( SCITEM_USERLIST ) ), + aCondFormatDlgItem ( ScCondFormatDlgItem ( nullptr, -1, false ) ), + + mvPoolDefaults(MSGPOOL_END - MSGPOOL_START + 1), + pDocPool(new ScDocumentPool) +{ + mvPoolDefaults[SCITEM_STRING - MSGPOOL_START] = &aGlobalStringItem; + mvPoolDefaults[SCITEM_SEARCHDATA - MSGPOOL_START] = &aGlobalSearchItem; + mvPoolDefaults[SCITEM_SORTDATA - MSGPOOL_START] = &aGlobalSortItem; + mvPoolDefaults[SCITEM_QUERYDATA - MSGPOOL_START] = &aGlobalQueryItem; + mvPoolDefaults[SCITEM_SUBTDATA - MSGPOOL_START] = &aGlobalSubTotalItem; + mvPoolDefaults[SCITEM_CONSOLIDATEDATA - MSGPOOL_START] = &aGlobalConsolidateItem; + mvPoolDefaults[SCITEM_PIVOTDATA - MSGPOOL_START] = &aGlobalPivotItem; + mvPoolDefaults[SCITEM_SOLVEDATA - MSGPOOL_START] = &aGlobalSolveItem; + mvPoolDefaults[SCITEM_USERLIST - MSGPOOL_START] = &aGlobalUserListItem; + mvPoolDefaults[SCITEM_CONDFORMATDLGDATA - MSGPOOL_START] = &aCondFormatDlgItem; + + SetDefaults( &mvPoolDefaults ); + + SetSecondaryPool( pDocPool.get() ); +} + +ScMessagePool::~ScMessagePool() +{ + Delete(); + SetSecondaryPool( nullptr ); // before deleting defaults (accesses defaults) + + for ( sal_uInt16 i=0; i <= MSGPOOL_END-MSGPOOL_START; i++ ) + ClearRefCount( *mvPoolDefaults[i] ); +} + +MapUnit ScMessagePool::GetMetric( sal_uInt16 nWhich ) const +{ + // Own attributes: Twips, everything else 1/100 mm + if ( nWhich >= ATTR_STARTINDEX && nWhich <= ATTR_ENDINDEX ) + return MapUnit::MapTwip; + else + return MapUnit::Map100thMM; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/app/rfindlst.cxx b/sc/source/ui/app/rfindlst.cxx new file mode 100644 index 000000000..330b8479b --- /dev/null +++ b/sc/source/ui/app/rfindlst.cxx @@ -0,0 +1,90 @@ +/* -*- 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 <rfindlst.hxx> +#include <tools/debug.hxx> + +#define SC_RANGECOLORS 8 + +const Color aColNames[SC_RANGECOLORS] = + { COL_LIGHTBLUE, COL_LIGHTRED, COL_LIGHTMAGENTA, COL_GREEN, + COL_BLUE, COL_RED, COL_MAGENTA, COL_BROWN }; + +ScRangeFindList::ScRangeFindList(const OUString& rName) : + aDocName( rName ), + bHidden( false ), + nIndexColor( 0 ) +{ +} + +Color ScRangeFindList::Insert( const ScRangeFindData &rNew ) +{ + auto it = std::find_if(maEntries.begin(), maEntries.end(), + [&rNew](const ScRangeFindData& rEntry) { return rEntry.aRef == rNew.aRef; }); + ScRangeFindData insertData(rNew); + insertData.nColor = ( it != maEntries.end() ? it->nColor : + ScRangeFindList::GetColorName( maEntries.size() ) ); + maEntries.push_back(insertData); + nIndexColor = maEntries.size() - 1; + return insertData.nColor; +} + +Color ScRangeFindList::GetColorName( const size_t nIndex ) +{ + return aColNames[nIndex % SC_RANGECOLORS]; +} + +Color ScRangeFindList::FindColor( const ScRange& rRef, const size_t nIndex ) +{ + sal_Int32 nOldCntr = 0; + sal_Int32 nNewCntr = 0; + Color nOldColor(0); + Color nNewColor(0); + + DBG_ASSERT( (nIndex < maEntries.size()), "nIndex out of range!" ); + + nOldColor = maEntries[nIndex].nColor; + nNewColor = ScRangeFindList::GetColorName( nIndex ); + + std::vector<ScRangeFindData>::iterator it=maEntries.begin(); + for( ;it!=maEntries.end(); ++it) + { + if(it->aRef == rRef) + break; + + if (it->nColor == nOldColor ) + nOldCntr++; + + if (it->nColor == nNewColor ) + nNewCntr++; + } + + if ( it != maEntries.end() ) + return it->nColor; + + if ( nOldCntr == 1 ) + return nOldColor; + + if ( nNewCntr > 0 ) + return ScRangeFindList::GetColorName( ++nIndexColor ); + + return nNewColor; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/app/scdll.cxx b/sc/source/ui/app/scdll.cxx new file mode 100644 index 000000000..2256ee2e1 --- /dev/null +++ b/sc/source/ui/app/scdll.cxx @@ -0,0 +1,257 @@ +/* -*- 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 <config_features.h> + +#include <svx/fmobjfac.hxx> +#include <svx/objfac3d.hxx> + +#include <comphelper/lok.hxx> +#include <sfx2/sidebar/SidebarChildWindow.hxx> +#include <sfx2/app.hxx> +#include <sfx2/devtools/DevelopmentToolChildWindow.hxx> +#include <avmedia/mediatoolbox.hxx> +#include <NumberFormatControl.hxx> + +#include <unotools/resmgr.hxx> + +#include <scmod.hxx> +#include <scresid.hxx> +#include <sc.hrc> + +#include <docsh.hxx> +#include <tabvwsh.hxx> +#include <prevwsh.hxx> +#include <drawsh.hxx> +#include <drformsh.hxx> +#include <drtxtob.hxx> +#include <editsh.hxx> +#include <pivotsh.hxx> +#include <auditsh.hxx> +#include <cellsh.hxx> +#include <oleobjsh.hxx> +#include <chartsh.hxx> +#include <graphsh.hxx> +#include <mediash.hxx> +#include <pgbrksh.hxx> +#include <scdll.hxx> +#include <SparklineShell.hxx> + +#include <appoptio.hxx> +#include <searchresults.hxx> + +// Controls + +#include <svx/tbxctl.hxx> +#include <svx/fillctrl.hxx> +#include <svx/linectrl.hxx> +//#include <svx/tbcontrl.hxx> +#include <svx/selctrl.hxx> +#include <svx/insctrl.hxx> +#include <svx/zoomctrl.hxx> +#include <svx/modctrl.hxx> +#include <svx/pszctrl.hxx> +#include <svx/grafctrl.hxx> +#include <svx/clipboardctl.hxx> +#include <svx/formatpaintbrushctrl.hxx> +#include <tbzoomsliderctrl.hxx> +#include <svx/zoomsliderctrl.hxx> + +#include <svx/xmlsecctrl.hxx> +// Child windows +#include <reffact.hxx> +#include <navipi.hxx> +#include <inputwin.hxx> +#include <spelldialog.hxx> +#include <svx/fontwork.hxx> +#include <svx/srchdlg.hxx> +#include <svx/hyperdlg.hxx> +#include <svx/imapdlg.hxx> + +#include <filter.hxx> +#include <scabstdlg.hxx> + +OUString ScResId(TranslateId aId) +{ + return Translate::get(aId, SC_MOD()->GetResLocale()); +} + +OUString ScResId(TranslateNId aContextSingularPlural, int nCardinality) +{ + return Translate::nget(aContextSingularPlural, nCardinality, SC_MOD()->GetResLocale()); +} + +void ScDLL::Init() +{ + if ( SfxApplication::GetModule(SfxToolsModule::Calc) ) // Module already active + return; + + auto pUniqueModule = std::make_unique<ScModule>(&ScDocShell::Factory()); + ScModule* pMod = pUniqueModule.get(); + SfxApplication::SetModule(SfxToolsModule::Calc, std::move(pUniqueModule)); + + ScDocShell::Factory().SetDocumentServiceName( "com.sun.star.sheet.SpreadsheetDocument" ); + + // Not until the ResManager is initialized + // The AppOptions must be initialized not until after ScGlobal::Init + ScGlobal::Init(); + + // register your view-factories here + ScTabViewShell ::RegisterFactory(SFX_INTERFACE_SFXAPP); + ScPreviewShell ::RegisterFactory(SFX_INTERFACE_SFXDOCSH); + + // register your shell-interfaces here + ScModule ::RegisterInterface(pMod); + ScDocShell ::RegisterInterface(pMod); + ScTabViewShell ::RegisterInterface(pMod); + ScPreviewShell ::RegisterInterface(pMod); + ScDrawShell ::RegisterInterface(pMod); + ScDrawFormShell ::RegisterInterface(pMod); + ScDrawTextObjectBar ::RegisterInterface(pMod); + ScEditShell ::RegisterInterface(pMod); + ScPivotShell ::RegisterInterface(pMod); + sc::SparklineShell ::RegisterInterface(pMod); + ScAuditingShell ::RegisterInterface(pMod); + ScFormatShell ::RegisterInterface(pMod); + ScCellShell ::RegisterInterface(pMod); + ScOleObjectShell ::RegisterInterface(pMod); + ScChartShell ::RegisterInterface(pMod); + ScGraphicShell ::RegisterInterface(pMod); + ScMediaShell ::RegisterInterface(pMod); + ScPageBreakShell ::RegisterInterface(pMod); + + // Own Controller + ScZoomSliderControl ::RegisterControl(SID_PREVIEW_SCALINGFACTOR, pMod); + + // SvxToolboxController + SvxTbxCtlDraw ::RegisterControl(SID_INSERT_DRAW, pMod); + SvxFillToolBoxControl ::RegisterControl(0, pMod); + SvxLineWidthToolBoxControl ::RegisterControl(0, pMod); + SvxClipBoardControl ::RegisterControl(SID_PASTE, pMod ); + SvxClipBoardControl ::RegisterControl(SID_PASTE_UNFORMATTED, pMod ); + svx::FormatPaintBrushToolBoxControl::RegisterControl(SID_FORMATPAINTBRUSH, pMod ); + sc::ScNumberFormatControl ::RegisterControl(SID_NUMBER_TYPE_FORMAT, pMod ); + + SvxGrafModeToolBoxControl ::RegisterControl(SID_ATTR_GRAF_MODE, pMod); + SvxGrafRedToolBoxControl ::RegisterControl(SID_ATTR_GRAF_RED, pMod); + SvxGrafGreenToolBoxControl ::RegisterControl(SID_ATTR_GRAF_GREEN, pMod); + SvxGrafBlueToolBoxControl ::RegisterControl(SID_ATTR_GRAF_BLUE, pMod); + SvxGrafLuminanceToolBoxControl ::RegisterControl(SID_ATTR_GRAF_LUMINANCE, pMod); + SvxGrafContrastToolBoxControl ::RegisterControl(SID_ATTR_GRAF_CONTRAST, pMod); + SvxGrafGammaToolBoxControl ::RegisterControl(SID_ATTR_GRAF_GAMMA, pMod); + SvxGrafTransparenceToolBoxControl::RegisterControl(SID_ATTR_GRAF_TRANSPARENCE, pMod); + + // Media Controller +#if HAVE_FEATURE_AVMEDIA + ::avmedia::MediaToolBoxControl::RegisterControl( SID_AVMEDIA_TOOLBOX, pMod ); +#endif + + // Common SFX Controller + sfx2::sidebar::SidebarChildWindow::RegisterChildWindow(false, pMod); + DevelopmentToolChildWindow::RegisterChildWindow(false, pMod); + + // SvxStatusBar Controller + SvxInsertStatusBarControl ::RegisterControl(SID_ATTR_INSERT, pMod); + SvxSelectionModeControl ::RegisterControl(SID_STATUS_SELMODE, pMod); + SvxZoomStatusBarControl ::RegisterControl(SID_ATTR_ZOOM, pMod); + SvxZoomSliderControl ::RegisterControl(SID_ATTR_ZOOMSLIDER, pMod); + SvxModifyControl ::RegisterControl(SID_DOC_MODIFIED, pMod); + XmlSecStatusBarControl ::RegisterControl( SID_SIGNATURE, pMod ); + + SvxPosSizeStatusBarControl ::RegisterControl(SID_ATTR_SIZE, pMod); + + // Child Windows + + ScInputWindowWrapper ::RegisterChildWindow(true, pMod, SfxChildWindowFlags::TASK|SfxChildWindowFlags::FORCEDOCK); + ScSolverDlgWrapper ::RegisterChildWindow(false, pMod); + ScOptSolverDlgWrapper ::RegisterChildWindow(false, pMod); + ScXMLSourceDlgWrapper ::RegisterChildWindow(false, pMod); + ScNameDlgWrapper ::RegisterChildWindow(false, pMod); + ScNameDefDlgWrapper ::RegisterChildWindow(false, pMod); + ScPivotLayoutWrapper ::RegisterChildWindow(false, pMod); + ScTabOpDlgWrapper ::RegisterChildWindow(false, pMod); + ScFilterDlgWrapper ::RegisterChildWindow(false, pMod); + ScSpecialFilterDlgWrapper ::RegisterChildWindow(false, pMod); + ScDbNameDlgWrapper ::RegisterChildWindow(false, pMod); + ScConsolidateDlgWrapper ::RegisterChildWindow(false, pMod); + ScPrintAreasDlgWrapper ::RegisterChildWindow(false, pMod); + ScColRowNameRangesDlgWrapper::RegisterChildWindow(false, pMod); + ScFormulaDlgWrapper ::RegisterChildWindow(false, pMod); + + ScRandomNumberGeneratorDialogWrapper::RegisterChildWindow(false, pMod); + ScSamplingDialogWrapper ::RegisterChildWindow(false, pMod); + ScDescriptiveStatisticsDialogWrapper::RegisterChildWindow(false, pMod); + ScAnalysisOfVarianceDialogWrapper ::RegisterChildWindow(false, pMod); + ScCorrelationDialogWrapper ::RegisterChildWindow(false, pMod); + ScCovarianceDialogWrapper ::RegisterChildWindow(false, pMod); + ScExponentialSmoothingDialogWrapper ::RegisterChildWindow(false, pMod); + ScMovingAverageDialogWrapper ::RegisterChildWindow(false, pMod); + ScRegressionDialogWrapper ::RegisterChildWindow(false, pMod); + ScTTestDialogWrapper ::RegisterChildWindow(false, pMod); + ScFTestDialogWrapper ::RegisterChildWindow(false, pMod); + ScZTestDialogWrapper ::RegisterChildWindow(false, pMod); + ScChiSquareTestDialogWrapper ::RegisterChildWindow(false, pMod); + ScFourierAnalysisDialogWrapper ::RegisterChildWindow(false, pMod); + sc::SparklineDialogWrapper ::RegisterChildWindow(false, pMod); + sc::SparklineDataRangeDialogWrapper ::RegisterChildWindow(false, pMod); + + // Redlining Window + ScAcceptChgDlgWrapper ::RegisterChildWindow(false, pMod); + ScSimpleRefDlgWrapper ::RegisterChildWindow(false, pMod, SfxChildWindowFlags::ALWAYSAVAILABLE|SfxChildWindowFlags::NEVERHIDE ); + ScHighlightChgDlgWrapper ::RegisterChildWindow(false, pMod); + + SvxSearchDialogWrapper ::RegisterChildWindow(false, pMod); + SvxHlinkDlgWrapper ::RegisterChildWindow(false, pMod); + SvxFontWorkChildWindow ::RegisterChildWindow(false, pMod); + SvxIMapDlgChildWindow ::RegisterChildWindow(false, pMod); + ScSpellDialogChildWindow::RegisterChildWindow( + false, pMod, comphelper::LibreOfficeKit::isActive() ? SfxChildWindowFlags::NEVERCLONE + : SfxChildWindowFlags::NONE); + + ScValidityRefChildWin::RegisterChildWindow(false, pMod); + sc::SearchResultsDlgWrapper::RegisterChildWindow(false, pMod); + ScCondFormatDlgWrapper::RegisterChildWindow(false, pMod); + + ScNavigatorWrapper::RegisterChildWindow(false, pMod, SfxChildWindowFlags::NEVERHIDE); + + // Add 3DObject Factory + E3dObjFactory(); + + // Add css::form::component::FormObject Factory + FmFormObjFactory(); + + pMod->PutItem( SfxUInt16Item( SID_ATTR_METRIC, sal::static_int_cast<sal_uInt16>(pMod->GetAppOptions().GetAppMetric()) ) ); + + // StarOne Services are now handled in the registry +} + +#ifndef DISABLE_DYNLOADING + +extern "C" SAL_DLLPUBLIC_EXPORT +void lok_preload_hook() +{ + // scfilt + ScFormatFilter::Get(); + // scui + ScAbstractDialogFactory::Create(); +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/app/scmod.cxx b/sc/source/ui/app/scmod.cxx new file mode 100644 index 000000000..1529f9920 --- /dev/null +++ b/sc/source/ui/app/scmod.cxx @@ -0,0 +1,2325 @@ +/* -*- 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/ui/dialogs/XSLTFilterDialog.hpp> +#include <comphelper/lok.hxx> +#include <comphelper/processfactory.hxx> +#include <scitems.hxx> +#include <sfx2/app.hxx> + +#include <editeng/flditem.hxx> +#include <editeng/outliner.hxx> + +#include <sfx2/viewfrm.hxx> +#include <sfx2/objface.hxx> + +#include <IAnyRefDialog.hxx> + +#include <svtools/ehdl.hxx> +#include <svtools/accessibilityoptions.hxx> +#include <svl/ctloptions.hxx> +#include <unotools/useroptions.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/request.hxx> +#include <sfx2/printer.hxx> +#include <editeng/langitem.hxx> +#include <svtools/colorcfg.hxx> + +#include <svl/whiter.hxx> +#include <svx/dialogs.hrc> +#include <svl/inethist.hxx> +#include <vcl/svapp.hxx> +#include <svx/svxerr.hxx> +#include <tools/diagnose_ex.h> + +#include <editeng/unolingu.hxx> +#include <unotools/lingucfg.hxx> +#include <i18nlangtag/mslangid.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <com/sun/star/i18n/ScriptType.hpp> +#include <com/sun/star/linguistic2/XThesaurus.hpp> +#include <ooo/vba/XSinkCaller.hpp> + +#include <scmod.hxx> +#include <global.hxx> +#include <viewopti.hxx> +#include <docoptio.hxx> +#include <appoptio.hxx> +#include <defaultsoptions.hxx> +#include <formulaopt.hxx> +#include <inputopt.hxx> +#include <printopt.hxx> +#include <navicfg.hxx> +#include <addincfg.hxx> +#include <tabvwsh.hxx> +#include <prevwsh.hxx> +#include <docsh.hxx> +#include <drwlayer.hxx> +#include <uiitems.hxx> +#include <sc.hrc> +#include <scerrors.hrc> +#include <scstyles.hrc> +#include <globstr.hrc> +#include <scresid.hxx> +#include <bitmaps.hlst> +#include <inputhdl.hxx> +#include <inputwin.hxx> +#include <msgpool.hxx> +#include <detfunc.hxx> +#include <preview.hxx> +#include <dragdata.hxx> +#include <markdata.hxx> +#include <transobj.hxx> +#include <funcdesc.hxx> + +#define ShellClass_ScModule +#include <scslots.hxx> + +#include <scabstdlg.hxx> +#include <formula/errorcodes.hxx> +#include <documentlinkmgr.hxx> + +#define SC_IDLE_MIN 150 +#define SC_IDLE_MAX 3000 +#define SC_IDLE_STEP 75 +#define SC_IDLE_COUNT 50 + +static sal_uInt16 nIdleCount = 0; + +SFX_IMPL_INTERFACE(ScModule, SfxShell) + +void ScModule::InitInterface_Impl() +{ + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_APPLICATION, + SfxVisibilityFlags::Standard | SfxVisibilityFlags::Client | SfxVisibilityFlags::Viewer, + ToolbarId::Objectbar_App); + + GetStaticInterface()->RegisterStatusBar(StatusBarId::CalcStatusBar); +} + +ScModule::ScModule( SfxObjectFactory* pFact ) : + SfxModule("sc", {pFact}), + m_aIdleTimer("sc ScModule IdleTimer"), + m_pDragData(new ScDragData), + m_pSelTransfer( nullptr ), + m_pRefInputHandler( nullptr ), + m_nCurRefDlgId( 0 ), + m_bIsWaterCan( false ), + m_bIsInEditCommand( false ), + m_bIsInExecuteDrop( false ), + m_bIsInSharedDocLoading( false ), + m_bIsInSharedDocSaving( false ) +{ + // The ResManager (DLL data) is not yet initialized in the ctor! + SetName("StarCalc"); // for Basic + + ResetDragObject(); + + // InputHandler does not need to be created + + // Create ErrorHandler - was in Init() + // Between OfficeApplication::Init and ScGlobal::Init + SvxErrorHandler::ensure(); + m_pErrorHdl.reset( new SfxErrorHandler(RID_ERRHDLSC, + ErrCodeArea::Sc, + ErrCodeArea::Sc, + GetResLocale()) ); + + m_aIdleTimer.SetTimeout(SC_IDLE_MIN); + m_aIdleTimer.SetInvokeHandler( LINK( this, ScModule, IdleHandler ) ); + m_aIdleTimer.Start(); + + m_pMessagePool = new ScMessagePool; + m_pMessagePool->FreezeIdRanges(); + SetPool( m_pMessagePool.get() ); + ScGlobal::InitTextHeight( m_pMessagePool.get() ); + + StartListening( *SfxGetpApp() ); // for SfxHintId::Deinitializing +} + +ScModule::~ScModule() +{ + OSL_ENSURE( !m_pSelTransfer, "Selection Transfer object not deleted" ); + + // InputHandler does not need to be deleted (there's none in the App anymore) + + m_pMessagePool.clear(); + + m_pDragData.reset(); + m_pErrorHdl.reset(); + + ScGlobal::Clear(); // Also calls ScDocumentPool::DeleteVersionMaps(); + + DeleteCfg(); // Called from Exit() +} + +void ScModule::ConfigurationChanged( utl::ConfigurationBroadcaster* p, ConfigurationHints ) +{ + if ( p == m_pColorConfig.get() || p == m_pAccessOptions.get() ) + { + // Test if detective objects have to be updated with new colors + // (if the detective colors haven't been used yet, there's nothing to update) + if ( ScDetectiveFunc::IsColorsInitialized() ) + { + const svtools::ColorConfig& rColors = GetColorConfig(); + bool bArrows = + ( ScDetectiveFunc::GetArrowColor() != rColors.GetColorValue(svtools::CALCDETECTIVE).nColor || + ScDetectiveFunc::GetErrorColor() != rColors.GetColorValue(svtools::CALCDETECTIVEERROR).nColor ); + bool bComments = + ( ScDetectiveFunc::GetCommentColor() != rColors.GetColorValue(svtools::CALCNOTESBACKGROUND).nColor ); + if ( bArrows || bComments ) + { + ScDetectiveFunc::InitializeColors(); // get the new colors + + // update detective objects in all open documents + SfxObjectShell* pObjSh = SfxObjectShell::GetFirst(); + while ( pObjSh ) + { + if ( auto pDocSh = dynamic_cast<ScDocShell * >(pObjSh) ) + { + if ( bArrows ) + ScDetectiveFunc( pDocSh->GetDocument(), 0 ).UpdateAllArrowColors(); + if ( bComments ) + ScDetectiveFunc::UpdateAllComments( pDocSh->GetDocument() ); + } + pObjSh = SfxObjectShell::GetNext( *pObjSh ); + } + } + } + + // force all views to repaint, using the new options + SfxViewShell* pViewShell = SfxViewShell::GetFirst(); + while(pViewShell) + { + if (ScTabViewShell* pViewSh = dynamic_cast<ScTabViewShell*>(pViewShell)) + { + pViewSh->PaintGrid(); + pViewSh->PaintTop(); + pViewSh->PaintLeft(); + pViewSh->PaintExtras(); + + ScInputHandler* pHdl = pViewSh->GetInputHandler(); + if ( pHdl ) + pHdl->ForgetLastPattern(); // EditEngine BackgroundColor may change + } + else if ( dynamic_cast<const ScPreviewShell*>( pViewShell) != nullptr ) + { + vcl::Window* pWin = pViewShell->GetWindow(); + if (pWin) + pWin->Invalidate(); + } + pViewShell = SfxViewShell::GetNext( *pViewShell ); + } + } + else if ( p == m_pCTLOptions.get() ) + { + // for all documents: set digit language for printer, recalc output factor, update row heights + SfxObjectShell* pObjSh = SfxObjectShell::GetFirst(); + while ( pObjSh ) + { + if ( auto pDocSh = dynamic_cast<ScDocShell *>(pObjSh) ) + { + OutputDevice* pPrinter = pDocSh->GetPrinter(); + if ( pPrinter ) + pPrinter->SetDigitLanguage( GetOptDigitLanguage() ); + + pDocSh->CalcOutputFactor(); + + SCTAB nTabCount = pDocSh->GetDocument().GetTableCount(); + for (SCTAB nTab=0; nTab<nTabCount; nTab++) + pDocSh->AdjustRowHeight( 0, pDocSh->GetDocument().MaxRow(), nTab ); + } + pObjSh = SfxObjectShell::GetNext( *pObjSh ); + } + + // for all views (table and preview): update digit language + SfxViewShell* pSh = SfxViewShell::GetFirst(); + while ( pSh ) + { + if (ScTabViewShell* pViewSh = dynamic_cast<ScTabViewShell*>(pSh)) + { + // set ref-device for EditEngine (re-evaluates digit settings) + ScInputHandler* pHdl = GetInputHdl(pViewSh); + if (pHdl) + pHdl->UpdateRefDevice(); + + pViewSh->DigitLanguageChanged(); + pViewSh->PaintGrid(); + } + else if (ScPreviewShell* pPreviewSh = dynamic_cast<ScPreviewShell*>(pSh)) + { + ScPreview* pPreview = pPreviewSh->GetPreview(); + + pPreview->GetOutDev()->SetDigitLanguage( GetOptDigitLanguage() ); + pPreview->Invalidate(); + } + + pSh = SfxViewShell::GetNext( *pSh ); + } + } +} + +void ScModule::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if ( rHint.GetId() == SfxHintId::Deinitializing ) + { + // ConfigItems must be removed before ConfigManager + DeleteCfg(); + } +} + +void ScModule::DeleteCfg() +{ + m_pViewCfg.reset(); // Saving happens automatically before Exit() + m_pDocCfg.reset(); + m_pAppCfg.reset(); + m_pDefaultsCfg.reset(); + m_pFormulaCfg.reset(); + m_pInputCfg.reset(); + m_pPrintCfg.reset(); + m_pNavipiCfg.reset(); + m_pAddInCfg.reset(); + + if ( m_pColorConfig ) + { + m_pColorConfig->RemoveListener(this); + m_pColorConfig.reset(); + } + if ( m_pAccessOptions ) + { + m_pAccessOptions->RemoveListener(this); + m_pAccessOptions.reset(); + } + if ( m_pCTLOptions ) + { + m_pCTLOptions->RemoveListener(this); + m_pCTLOptions.reset(); + } + m_pUserOptions.reset(); +} + +// Moved here from the App + +void ScModule::Execute( SfxRequest& rReq ) +{ + SfxViewFrame* pViewFrm = SfxViewFrame::Current(); + SfxBindings* pBindings = pViewFrm ? &pViewFrm->GetBindings() : nullptr; + + const SfxItemSet* pReqArgs = rReq.GetArgs(); + sal_uInt16 nSlot = rReq.GetSlot(); + + switch ( nSlot ) + { + case SID_CHOOSE_DESIGN: + SfxApplication::CallAppBasic( "Template.Samples.ShowStyles" ); + break; + case SID_EURO_CONVERTER: + SfxApplication::CallAppBasic( "Euro.ConvertRun.Main" ); + break; + case SID_AUTOSPELL_CHECK: + { + bool bSet; + const SfxPoolItem* pItem; + if (pReqArgs && SfxItemState::SET == pReqArgs->GetItemState( FN_PARAM_1, true, &pItem )) + bSet = static_cast<const SfxBoolItem*>(pItem)->GetValue(); + else if ( pReqArgs && SfxItemState::SET == pReqArgs->GetItemState( nSlot, true, &pItem ) ) + bSet = static_cast<const SfxBoolItem*>(pItem)->GetValue(); + else + { // Toggle + ScDocShell* pDocSh = dynamic_cast<ScDocShell*>( SfxObjectShell::Current() ); + if ( pDocSh ) + bSet = !pDocSh->GetDocument().GetDocOptions().IsAutoSpell(); + else + bSet = !GetDocOptions().IsAutoSpell(); + } + + SfxItemSetFixed<SID_AUTOSPELL_CHECK, SID_AUTOSPELL_CHECK> aSet( GetPool() ); + aSet.Put( SfxBoolItem( SID_AUTOSPELL_CHECK, bSet ) ); + ModifyOptions( aSet ); + rReq.Done(); + } + break; + + case SID_ATTR_METRIC: + { + const SfxPoolItem* pItem; + if ( pReqArgs && SfxItemState::SET == pReqArgs->GetItemState( nSlot, true, &pItem ) ) + { + FieldUnit eUnit = static_cast<FieldUnit>(static_cast<const SfxUInt16Item*>(pItem)->GetValue()); + switch( eUnit ) + { + case FieldUnit::MM: // Just the units that are also in the dialog + case FieldUnit::CM: + case FieldUnit::INCH: + case FieldUnit::PICA: + case FieldUnit::POINT: + { + PutItem( *pItem ); + ScAppOptions aNewOpts( GetAppOptions() ); + aNewOpts.SetAppMetric( eUnit ); + SetAppOptions( aNewOpts ); + rReq.Done(); + } + break; + default: + { + // added to avoid warnings + } + } + } + } + break; + + case FID_AUTOCOMPLETE: + { + ScAppOptions aNewOpts( GetAppOptions() ); + bool bNew = !aNewOpts.GetAutoComplete(); + aNewOpts.SetAutoComplete( bNew ); + SetAppOptions( aNewOpts ); + if (pBindings) + pBindings->Invalidate( FID_AUTOCOMPLETE ); + rReq.Done(); + } + break; + + case SID_DETECTIVE_AUTO: + { + ScAppOptions aNewOpts( GetAppOptions() ); + bool bNew = !aNewOpts.GetDetectiveAuto(); + const SfxBoolItem* pAuto = rReq.GetArg<SfxBoolItem>(SID_DETECTIVE_AUTO); + if ( pAuto ) + bNew = pAuto->GetValue(); + + aNewOpts.SetDetectiveAuto( bNew ); + SetAppOptions( aNewOpts ); + if (pBindings) + pBindings->Invalidate( SID_DETECTIVE_AUTO ); + rReq.AppendItem( SfxBoolItem( SID_DETECTIVE_AUTO, bNew ) ); + rReq.Done(); + } + break; + + case SID_PSZ_FUNCTION: + if (pReqArgs) + { + auto const & p = pReqArgs->Get(SID_PSZ_FUNCTION); + assert(dynamic_cast<const SfxUInt32Item*>(&p) && "wrong Parameter"); + const SfxUInt32Item& rItem = static_cast<const SfxUInt32Item&>(p); + + ScAppOptions aNewOpts( GetAppOptions() ); + aNewOpts.SetStatusFunc( rItem.GetValue() ); + SetAppOptions( aNewOpts ); + + if (pBindings) + { + pBindings->Invalidate( SID_TABLE_CELL ); + pBindings->Update( SID_TABLE_CELL ); // Immediately + + pBindings->Invalidate( SID_PSZ_FUNCTION ); + pBindings->Update( SID_PSZ_FUNCTION ); + // If the menu is opened again immediately + } + } + break; + + case SID_ATTR_LANGUAGE: + case SID_ATTR_CHAR_CJK_LANGUAGE: + case SID_ATTR_CHAR_CTL_LANGUAGE: + { + const SfxPoolItem* pItem; + if ( pReqArgs && SfxItemState::SET == pReqArgs->GetItemState( GetPool().GetWhich(nSlot), true, &pItem ) ) + { + ScDocShell* pDocSh = dynamic_cast<ScDocShell*>( SfxObjectShell::Current() ); + if ( pDocSh ) + { + ScDocument& rDoc = pDocSh->GetDocument(); + LanguageType eNewLang = static_cast<const SvxLanguageItem*>(pItem)->GetLanguage(); + LanguageType eLatin, eCjk, eCtl; + rDoc.GetLanguage( eLatin, eCjk, eCtl ); + LanguageType eOld = ( nSlot == SID_ATTR_CHAR_CJK_LANGUAGE ) ? eCjk : + ( ( nSlot == SID_ATTR_CHAR_CTL_LANGUAGE ) ? eCtl : eLatin ); + if ( eNewLang != eOld ) + { + if ( nSlot == SID_ATTR_CHAR_CJK_LANGUAGE ) + eCjk = eNewLang; + else if ( nSlot == SID_ATTR_CHAR_CTL_LANGUAGE ) + eCtl = eNewLang; + else + eLatin = eNewLang; + + rDoc.SetLanguage( eLatin, eCjk, eCtl ); + + ScInputHandler* pInputHandler = GetInputHdl(); + if ( pInputHandler ) + pInputHandler->UpdateSpellSettings(); // EditEngine flags + ScTabViewShell* pViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() ); + if ( pViewSh ) + pViewSh->UpdateDrawTextOutliner(); // EditEngine flags + + pDocSh->SetDocumentModified(); + } + } + } + } + break; + + case FID_FOCUS_POSWND: + { + ScInputHandler* pHdl = GetInputHdl(); + if (pHdl) + { + ScInputWindow* pWin = pHdl->GetInputWindow(); + if (pWin) + pWin->PosGrabFocus(); + } + rReq.Done(); + } + break; + + case SID_OPEN_XML_FILTERSETTINGS: + { + try + { + css::uno::Reference < css::ui::dialogs::XExecutableDialog > xDialog = css::ui::dialogs::XSLTFilterDialog::create( ::comphelper::getProcessComponentContext()); + xDialog->execute(); + } + catch( css::uno::RuntimeException& ) + { + DBG_UNHANDLED_EXCEPTION("sc.ui"); + } + } + break; + + default: + OSL_FAIL( "ScApplication: Unknown Message." ); + break; + } +} + +void ScModule::GetState( SfxItemSet& rSet ) +{ + ScDocShell* pDocSh = dynamic_cast<ScDocShell*>( SfxObjectShell::Current() ); + bool bTabView = pDocSh && (pDocSh->GetBestViewShell() != nullptr); + + SfxWhichIter aIter(rSet); + for (sal_uInt16 nWhich = aIter.FirstWhich(); nWhich; nWhich = aIter.NextWhich()) + { + if (!bTabView) + { + // Not in the normal calc view shell (most likely in preview shell). Disable all actions. + rSet.DisableItem(nWhich); + continue; + } + + switch ( nWhich ) + { + case FID_AUTOCOMPLETE: + rSet.Put( SfxBoolItem( nWhich, GetAppOptions().GetAutoComplete() ) ); + break; + case SID_DETECTIVE_AUTO: + rSet.Put( SfxBoolItem( nWhich, GetAppOptions().GetDetectiveAuto() ) ); + break; + case SID_PSZ_FUNCTION: + rSet.Put( SfxUInt32Item( nWhich, GetAppOptions().GetStatusFunc() ) ); + break; + case SID_ATTR_METRIC: + rSet.Put( SfxUInt16Item( nWhich, sal::static_int_cast<sal_uInt16>(GetAppOptions().GetAppMetric()) ) ); + break; + case SID_AUTOSPELL_CHECK: + rSet.Put( SfxBoolItem( nWhich, pDocSh->GetDocument().GetDocOptions().IsAutoSpell()) ); + break; + case SID_ATTR_LANGUAGE: + case ATTR_CJK_FONT_LANGUAGE: // WID for SID_ATTR_CHAR_CJK_LANGUAGE + case ATTR_CTL_FONT_LANGUAGE: // WID for SID_ATTR_CHAR_CTL_LANGUAGE + { + LanguageType eLatin, eCjk, eCtl; + pDocSh->GetDocument().GetLanguage( eLatin, eCjk, eCtl ); + LanguageType eLang = ( nWhich == ATTR_CJK_FONT_LANGUAGE ) ? eCjk : + ( ( nWhich == ATTR_CTL_FONT_LANGUAGE ) ? eCtl : eLatin ); + rSet.Put( SvxLanguageItem( eLang, nWhich ) ); + } + break; + } + } +} + +void ScModule::HideDisabledSlots( SfxItemSet& rSet ) +{ + if( SfxViewFrame* pViewFrm = SfxViewFrame::Current() ) + { + SfxBindings& rBindings = pViewFrm->GetBindings(); + SfxWhichIter aIter( rSet ); + for( sal_uInt16 nWhich = aIter.FirstWhich(); nWhich != 0; nWhich = aIter.NextWhich() ) + { + ScViewUtil::HideDisabledSlot( rSet, rBindings, nWhich ); + // always disable the slots + rSet.DisableItem( nWhich ); + } + } +} + +void ScModule::ResetDragObject() +{ + if (comphelper::LibreOfficeKit::isActive()) + { + ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell(); + if (pViewShell) + pViewShell->ResetDragObject(); + } + else + { + m_pDragData->pCellTransfer = nullptr; + m_pDragData->pDrawTransfer = nullptr; + m_pDragData->pJumpLocalDoc = nullptr; + m_pDragData->aLinkDoc.clear(); + m_pDragData->aLinkTable.clear(); + m_pDragData->aLinkArea.clear(); + m_pDragData->aJumpTarget.clear(); + m_pDragData->aJumpText.clear(); + } +} + +const ScDragData& ScModule::GetDragData() const +{ + if (comphelper::LibreOfficeKit::isActive()) + { + ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell(); + assert(pViewShell); + return pViewShell->GetDragData(); + } + else + return *m_pDragData; +} + +void ScModule::SetDragObject( ScTransferObj* pCellObj, ScDrawTransferObj* pDrawObj ) +{ + if (comphelper::LibreOfficeKit::isActive()) + { + ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell(); + if (pViewShell) + pViewShell->SetDragObject(pCellObj, pDrawObj); + } + else + { + ResetDragObject(); + m_pDragData->pCellTransfer = pCellObj; + m_pDragData->pDrawTransfer = pDrawObj; + } +} + +void ScModule::SetDragLink( + const OUString& rDoc, const OUString& rTab, const OUString& rArea ) +{ + if (comphelper::LibreOfficeKit::isActive()) + { + ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell(); + if (pViewShell) + pViewShell->SetDragLink(rDoc, rTab, rArea); + } + else + { + ResetDragObject(); + m_pDragData->aLinkDoc = rDoc; + m_pDragData->aLinkTable = rTab; + m_pDragData->aLinkArea = rArea; + } +} + +void ScModule::SetDragJump( + ScDocument* pLocalDoc, const OUString& rTarget, const OUString& rText ) +{ + if (comphelper::LibreOfficeKit::isActive()) + { + ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell(); + if (pViewShell) + pViewShell->SetDragJump(pLocalDoc, rTarget, rText); + } + else + { + ResetDragObject(); + + m_pDragData->pJumpLocalDoc = pLocalDoc; + m_pDragData->aJumpTarget = rTarget; + m_pDragData->aJumpText = rText; + } +} + +ScDocument* ScModule::GetClipDoc() +{ + // called from document + SfxViewFrame* pViewFrame = nullptr; + ScTabViewShell* pViewShell = nullptr; + css::uno::Reference<css::datatransfer::XTransferable2> xTransferable; + + if ((pViewShell = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current()))) + xTransferable.set(ScTabViewShell::GetClipData(pViewShell->GetViewData().GetActiveWin())); + else if ((pViewShell = dynamic_cast<ScTabViewShell*>(SfxViewShell::GetFirst()))) + xTransferable.set(ScTabViewShell::GetClipData(pViewShell->GetViewData().GetActiveWin())); + else if ((pViewFrame = SfxViewFrame::GetFirst())) + { + css::uno::Reference<css::datatransfer::clipboard::XClipboard> xClipboard = + pViewFrame->GetWindow().GetClipboard(); + xTransferable.set(xClipboard.is() ? xClipboard->getContents() : nullptr, css::uno::UNO_QUERY); + } + + const ScTransferObj* pObj = ScTransferObj::GetOwnClipboard(xTransferable); + if (pObj) + { + ScDocument* pDoc = pObj->GetDocument(); + assert((!pDoc || pDoc->IsClipboard()) && "Document is not clipboard, how can that be?"); + return pDoc; + } + + return nullptr; +} + +void ScModule::SetSelectionTransfer( ScSelectionTransferObj* pNew ) +{ + m_pSelTransfer = pNew; +} + +void ScModule::SetViewOptions( const ScViewOptions& rOpt ) +{ + if ( !m_pViewCfg ) + m_pViewCfg.reset(new ScViewCfg); + + m_pViewCfg->SetOptions( rOpt ); +} + +const ScViewOptions& ScModule::GetViewOptions() +{ + if ( !m_pViewCfg ) + m_pViewCfg.reset( new ScViewCfg ); + + return *m_pViewCfg; +} + +void ScModule::SetDocOptions( const ScDocOptions& rOpt ) +{ + if ( !m_pDocCfg ) + m_pDocCfg.reset( new ScDocCfg ); + + m_pDocCfg->SetOptions( rOpt ); +} + +const ScDocOptions& ScModule::GetDocOptions() +{ + if ( !m_pDocCfg ) + m_pDocCfg.reset( new ScDocCfg ); + + return *m_pDocCfg; +} + +void ScModule::InsertEntryToLRUList(sal_uInt16 nFIndex) +{ + if(nFIndex == 0) + return; + + const ScAppOptions& rAppOpt = GetAppOptions(); + sal_uInt16 nLRUFuncCount = std::min( rAppOpt.GetLRUFuncListCount(), sal_uInt16(LRU_MAX) ); + sal_uInt16* pLRUListIds = rAppOpt.GetLRUFuncList(); + + sal_uInt16 aIdxList[LRU_MAX]; + sal_uInt16 n = 0; + bool bFound = false; + + while ((n < LRU_MAX) && n<nLRUFuncCount) // Iterate through old list + { + if (!bFound && (pLRUListIds[n]== nFIndex)) + bFound = true; // First hit! + else if (bFound) + aIdxList[n ] = pLRUListIds[n]; // Copy after hit + else if ((n+1) < LRU_MAX) + aIdxList[n+1] = pLRUListIds[n]; // Move before hit + n++; + } + if (!bFound && (n < LRU_MAX)) // Entry not found? + n++; // One more + aIdxList[0] = nFIndex; // Current on Top + + ScAppOptions aNewOpts(rAppOpt); // Let App know + aNewOpts.SetLRUFuncList(aIdxList, n); + SetAppOptions(aNewOpts); +} + +void ScModule::SetAppOptions( const ScAppOptions& rOpt ) +{ + if ( !m_pAppCfg ) + m_pAppCfg.reset( new ScAppCfg ); + + m_pAppCfg->SetOptions( rOpt ); +} + +void global_InitAppOptions() +{ + SC_MOD()->GetAppOptions(); +} + +const ScAppOptions& ScModule::GetAppOptions() +{ + if ( !m_pAppCfg ) + m_pAppCfg.reset( new ScAppCfg ); + + return m_pAppCfg->GetOptions(); +} + +void ScModule::SetDefaultsOptions( const ScDefaultsOptions& rOpt ) +{ + if ( !m_pDefaultsCfg ) + m_pDefaultsCfg.reset( new ScDefaultsCfg ); + + m_pDefaultsCfg->SetOptions( rOpt ); +} + +const ScDefaultsOptions& ScModule::GetDefaultsOptions() +{ + if ( !m_pDefaultsCfg ) + m_pDefaultsCfg.reset( new ScDefaultsCfg ); + + return *m_pDefaultsCfg; +} + +void ScModule::SetFormulaOptions( const ScFormulaOptions& rOpt ) +{ + if ( !m_pFormulaCfg ) + m_pFormulaCfg.reset( new ScFormulaCfg ); + + m_pFormulaCfg->SetOptions( rOpt ); +} + +const ScFormulaOptions& ScModule::GetFormulaOptions() +{ + if ( !m_pFormulaCfg ) + m_pFormulaCfg.reset( new ScFormulaCfg ); + + return *m_pFormulaCfg; +} + +void ScModule::SetInputOptions( const ScInputOptions& rOpt ) +{ + if ( !m_pInputCfg ) + m_pInputCfg.reset( new ScInputCfg ); + + m_pInputCfg->SetOptions( rOpt ); +} + +const ScInputOptions& ScModule::GetInputOptions() +{ + if ( !m_pInputCfg ) + m_pInputCfg.reset( new ScInputCfg ); + + return m_pInputCfg->GetOptions(); +} + +void ScModule::SetPrintOptions( const ScPrintOptions& rOpt ) +{ + if ( !m_pPrintCfg ) + m_pPrintCfg.reset( new ScPrintCfg ); + + m_pPrintCfg->SetOptions( rOpt ); +} + +const ScPrintOptions& ScModule::GetPrintOptions() +{ + if ( !m_pPrintCfg ) + m_pPrintCfg.reset( new ScPrintCfg ); + + return m_pPrintCfg->GetOptions(); +} + +ScNavipiCfg& ScModule::GetNavipiCfg() +{ + if ( !m_pNavipiCfg ) + m_pNavipiCfg.reset( new ScNavipiCfg ); + + return *m_pNavipiCfg; +} + +ScAddInCfg& ScModule::GetAddInCfg() +{ + if ( !m_pAddInCfg ) + m_pAddInCfg.reset( new ScAddInCfg ); + + return *m_pAddInCfg; +} + +svtools::ColorConfig& ScModule::GetColorConfig() +{ + if ( !m_pColorConfig ) + { + m_pColorConfig.reset( new svtools::ColorConfig ); + m_pColorConfig->AddListener(this); + } + + return *m_pColorConfig; +} + +SvtAccessibilityOptions& ScModule::GetAccessOptions() +{ + if ( !m_pAccessOptions ) + { + m_pAccessOptions.reset( new SvtAccessibilityOptions ); + m_pAccessOptions->AddListener(this); + } + + return *m_pAccessOptions; +} + +SvtCTLOptions& ScModule::GetCTLOptions() +{ + if ( !m_pCTLOptions ) + { + m_pCTLOptions.reset( new SvtCTLOptions ); + m_pCTLOptions->AddListener(this); + } + + return *m_pCTLOptions; +} + +SvtUserOptions& ScModule::GetUserOptions() +{ + if( !m_pUserOptions ) + { + m_pUserOptions.reset( new SvtUserOptions ); + } + return *m_pUserOptions; +} + +LanguageType ScModule::GetOptDigitLanguage() +{ + SvtCTLOptions::TextNumerals eNumerals = GetCTLOptions().GetCTLTextNumerals(); + return ( eNumerals == SvtCTLOptions::NUMERALS_ARABIC ) ? LANGUAGE_ENGLISH_US : + ( eNumerals == SvtCTLOptions::NUMERALS_HINDI) ? LANGUAGE_ARABIC_SAUDI_ARABIA : + LANGUAGE_SYSTEM; +} + +/** + * Options + * + * Items from Calc options dialog and SID_AUTOSPELL_CHECK + */ +void ScModule::ModifyOptions( const SfxItemSet& rOptSet ) +{ + LanguageType nOldSpellLang, nOldCjkLang, nOldCtlLang; + bool bOldAutoSpell; + GetSpellSettings( nOldSpellLang, nOldCjkLang, nOldCtlLang, bOldAutoSpell ); + + if (!m_pAppCfg) + GetAppOptions(); + OSL_ENSURE( m_pAppCfg, "AppOptions not initialised :-(" ); + + if (!m_pInputCfg) + GetInputOptions(); + OSL_ENSURE( m_pInputCfg, "InputOptions not initialised :-(" ); + + SfxViewFrame* pViewFrm = SfxViewFrame::Current(); + SfxBindings* pBindings = pViewFrm ? &pViewFrm->GetBindings() : nullptr; + + ScTabViewShell* pViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() ); + ScDocShell* pDocSh = dynamic_cast<ScDocShell*>( SfxObjectShell::Current() ); + ScDocument* pDoc = pDocSh ? &pDocSh->GetDocument() : nullptr; + bool bRepaint = false; + bool bUpdateMarks = false; + bool bUpdateRefDev = false; + bool bCalcAll = false; + bool bSaveAppOptions = false; + bool bSaveInputOptions = false; + bool bCompileErrorCells = false; + + // SfxGetpApp()->SetOptions( rOptSet ); + + ScAppOptions aAppOptions = m_pAppCfg->GetOptions(); + + // No more linguistics + if (const SfxUInt16Item* pItem = rOptSet.GetItemIfSet(SID_ATTR_METRIC)) + { + PutItem( *pItem ); + aAppOptions.SetAppMetric( static_cast<FieldUnit>(pItem->GetValue()) ); + bSaveAppOptions = true; + } + + if (const ScUserListItem* pItem = rOptSet.GetItemIfSet(SCITEM_USERLIST)) + { + ScGlobal::SetUserList( pItem->GetUserList() ); + bSaveAppOptions = true; + } + + if (const SfxBoolItem* pItem = rOptSet.GetItemIfSet(SID_SC_OPT_SYNCZOOM)) + { + aAppOptions.SetSynchronizeZoom( pItem->GetValue() ); + bSaveAppOptions = true; + } + + if (const SfxUInt16Item* pItem = rOptSet.GetItemIfSet(SID_SC_OPT_KEY_BINDING_COMPAT)) + { + sal_uInt16 nVal = pItem->GetValue(); + ScOptionsUtil::KeyBindingType eOld = aAppOptions.GetKeyBindingType(); + ScOptionsUtil::KeyBindingType eNew = static_cast<ScOptionsUtil::KeyBindingType>(nVal); + if (eOld != eNew) + { + aAppOptions.SetKeyBindingType(eNew); + bSaveAppOptions = true; + ScDocShell::ResetKeyBindings(eNew); + } + } + + // DefaultsOptions + if (const ScTpDefaultsItem* pItem = rOptSet.GetItemIfSet(SID_SCDEFAULTSOPTIONS)) + { + const ScDefaultsOptions& rOpt = pItem->GetDefaultsOptions(); + SetDefaultsOptions( rOpt ); + } + + // FormulaOptions + if (const ScTpFormulaItem* pItem = rOptSet.GetItemIfSet(SID_SCFORMULAOPTIONS)) + { + const ScFormulaOptions& rOpt = pItem->GetFormulaOptions(); + + if (!m_pFormulaCfg || (*m_pFormulaCfg != rOpt)) + // Formula options have changed. Repaint the column headers. + bRepaint = true; + + if (m_pFormulaCfg && m_pFormulaCfg->GetUseEnglishFuncName() != rOpt.GetUseEnglishFuncName()) + { + // Re-compile formula cells with error as the error may have been + // caused by unresolved function names. + bCompileErrorCells = true; + } + + // Recalc for interpreter options changes. + if (m_pFormulaCfg && m_pFormulaCfg->GetCalcConfig() != rOpt.GetCalcConfig()) + bCalcAll = true; + + if ( pDocSh ) + { + pDocSh->SetFormulaOptions( rOpt ); + pDocSh->SetDocumentModified(); + } + + // ScDocShell::SetFormulaOptions() may check for changed settings, so + // set the new options here after that has been called. + if (!bCalcAll || rOpt.GetWriteCalcConfig()) + { + // CalcConfig is new, didn't change or is global, simply set all. + SetFormulaOptions( rOpt ); + } + else + { + // If "only for current document" was checked, reset those affected + // by that setting to previous values. + ScFormulaOptions aNewOpt( rOpt); + aNewOpt.GetCalcConfig().MergeDocumentSpecific( m_pFormulaCfg->GetCalcConfig()); + SetFormulaOptions( aNewOpt); + } + } + + // ViewOptions + if (const ScTpViewItem* pItem = rOptSet.GetItemIfSet(SID_SCVIEWOPTIONS)) + { + const ScViewOptions& rNewOpt = pItem->GetViewOptions(); + + if ( pViewSh ) + { + ScViewData& rViewData = pViewSh->GetViewData(); + const ScViewOptions& rOldOpt = rViewData.GetOptions(); + + bool bAnchorList = ( rOldOpt.GetOption( VOPT_ANCHOR ) != + rNewOpt.GetOption( VOPT_ANCHOR ) ); + + if ( rOldOpt != rNewOpt ) + { + rViewData.SetOptions( rNewOpt ); // Changes rOldOpt + rViewData.GetDocument().SetViewOptions( rNewOpt ); + if (pDocSh) + pDocSh->SetDocumentModified(); + bRepaint = true; + } + if ( bAnchorList ) + pViewSh->UpdateAnchorHandles(); + } + SetViewOptions( rNewOpt ); + if (pBindings) + { + pBindings->Invalidate(SID_HELPLINES_MOVE); + } + } + + // GridOptions + // Evaluate after ViewOptions, as GridOptions is a member of ViewOptions + if ( const SvxGridItem* pItem = rOptSet.GetItemIfSet(SID_ATTR_GRID_OPTIONS) ) + { + ScGridOptions aNewGridOpt( *pItem ); + + if ( pViewSh ) + { + ScViewData& rViewData = pViewSh->GetViewData(); + ScViewOptions aNewViewOpt( rViewData.GetOptions() ); + const ScGridOptions& rOldGridOpt = aNewViewOpt.GetGridOptions(); + + if ( rOldGridOpt != aNewGridOpt ) + { + aNewViewOpt.SetGridOptions( aNewGridOpt ); + rViewData.SetOptions( aNewViewOpt ); + rViewData.GetDocument().SetViewOptions( aNewViewOpt ); + if (pDocSh) + pDocSh->SetDocumentModified(); + bRepaint = true; + } + } + ScViewOptions aNewViewOpt ( GetViewOptions() ); + aNewViewOpt.SetGridOptions( aNewGridOpt ); + SetViewOptions( aNewViewOpt ); + if (pBindings) + { + pBindings->Invalidate(SID_GRID_VISIBLE); + pBindings->Invalidate(SID_GRID_USE); + } + } + + // DocOptions + if ( const ScTpCalcItem* pItem = rOptSet.GetItemIfSet(SID_SCDOCOPTIONS) ) + { + const ScDocOptions& rNewOpt = pItem->GetDocOptions(); + + if ( pDoc ) + { + const ScDocOptions& rOldOpt = pDoc->GetDocOptions(); + + bRepaint = ( bRepaint || ( rOldOpt != rNewOpt ) ); + bCalcAll = bRepaint && + ( rOldOpt.IsIter() != rNewOpt.IsIter() + || rOldOpt.GetIterCount() != rNewOpt.GetIterCount() + || rOldOpt.GetIterEps() != rNewOpt.GetIterEps() + || rOldOpt.IsIgnoreCase() != rNewOpt.IsIgnoreCase() + || rOldOpt.IsCalcAsShown() != rNewOpt.IsCalcAsShown() + || (rNewOpt.IsCalcAsShown() && + rOldOpt.GetStdPrecision() != rNewOpt.GetStdPrecision()) + || rOldOpt.IsMatchWholeCell() != rNewOpt.IsMatchWholeCell() + || rOldOpt.GetYear2000() != rNewOpt.GetYear2000() + || rOldOpt.IsFormulaRegexEnabled() != rNewOpt.IsFormulaRegexEnabled() + || rOldOpt.IsFormulaWildcardsEnabled() != rNewOpt.IsFormulaWildcardsEnabled() + ); + pDoc->SetDocOptions( rNewOpt ); + pDocSh->SetDocumentModified(); + } + SetDocOptions( rNewOpt ); + } + + // Set TabDistance after the actual DocOptions + if ( const SfxUInt16Item* pItem = rOptSet.GetItemIfSet(SID_ATTR_DEFTABSTOP) ) + { + sal_uInt16 nTabDist = pItem->GetValue(); + ScDocOptions aOpt(GetDocOptions()); + aOpt.SetTabDistance(nTabDist); + SetDocOptions( aOpt ); + + if ( pDoc ) + { + ScDocOptions aDocOpt(pDoc->GetDocOptions()); + aDocOpt.SetTabDistance(nTabDist); + pDoc->SetDocOptions( aDocOpt ); + pDocSh->SetDocumentModified(); + if(pDoc->GetDrawLayer()) + pDoc->GetDrawLayer()->SetDefaultTabulator(nTabDist); + } + } + + // AutoSpell after the DocOptions (due to being a member) + if ( const SfxBoolItem* pItem = rOptSet.GetItemIfSet(SID_AUTOSPELL_CHECK) ) // At DocOptions + { + bool bDoAutoSpell = pItem->GetValue(); + + if (pDoc) + { + ScDocOptions aNewOpt = pDoc->GetDocOptions(); + if ( aNewOpt.IsAutoSpell() != bDoAutoSpell ) + { + aNewOpt.SetAutoSpell( bDoAutoSpell ); + pDoc->SetDocOptions( aNewOpt ); + + if (pViewSh) + pViewSh->EnableAutoSpell(bDoAutoSpell); + + bRepaint = true; // Because HideAutoSpell might be invalid + //TODO: Paint all Views? + } + } + + if ( bOldAutoSpell != bDoAutoSpell ) + SetAutoSpellProperty( bDoAutoSpell ); + if ( pDocSh ) + pDocSh->PostPaintGridAll(); // Due to marks + ScInputHandler* pInputHandler = GetInputHdl(); + if ( pInputHandler ) + pInputHandler->UpdateSpellSettings(); // EditEngine flags + if ( pViewSh ) + pViewSh->UpdateDrawTextOutliner(); // EditEngine flags + + if (pBindings) + pBindings->Invalidate( SID_AUTOSPELL_CHECK ); + } + + // InputOptions + ScInputOptions aInputOptions = m_pInputCfg->GetOptions(); + if ( const SfxUInt16Item* pItem = rOptSet.GetItemIfSet(SID_SC_INPUT_SELECTIONPOS) ) + { + aInputOptions.SetMoveDir( pItem->GetValue() ); + bSaveInputOptions = true; + } + if ( const SfxBoolItem* pItem = rOptSet.GetItemIfSet(SID_SC_INPUT_SELECTION) ) + { + aInputOptions.SetMoveSelection( pItem->GetValue() ); + bSaveInputOptions = true; + } + if ( const SfxBoolItem* pItem = rOptSet.GetItemIfSet(SID_SC_INPUT_EDITMODE) ) + { + aInputOptions.SetEnterEdit( pItem->GetValue() ); + bSaveInputOptions = true; + } + if ( const SfxBoolItem* pItem = rOptSet.GetItemIfSet(SID_SC_INPUT_FMT_EXPAND) ) + { + aInputOptions.SetExtendFormat( pItem->GetValue() ); + bSaveInputOptions = true; + } + if ( const SfxBoolItem* pItem = rOptSet.GetItemIfSet(SID_SC_INPUT_RANGEFINDER) ) + { + aInputOptions.SetRangeFinder( pItem->GetValue() ); + bSaveInputOptions = true; + } + if ( const SfxBoolItem* pItem = rOptSet.GetItemIfSet(SID_SC_INPUT_REF_EXPAND) ) + { + aInputOptions.SetExpandRefs( pItem->GetValue() ); + bSaveInputOptions = true; + } + if (const SfxBoolItem* pItem = rOptSet.GetItemIfSet(SID_SC_OPT_SORT_REF_UPDATE)) + { + aInputOptions.SetSortRefUpdate( pItem->GetValue()); + bSaveInputOptions = true; + } + + if ( const SfxBoolItem* pItem = rOptSet.GetItemIfSet(SID_SC_INPUT_MARK_HEADER) ) + { + aInputOptions.SetMarkHeader( pItem->GetValue() ); + bSaveInputOptions = true; + bUpdateMarks = true; + } + if ( const SfxBoolItem* pItem = rOptSet.GetItemIfSet(SID_SC_INPUT_TEXTWYSIWYG) ) + { + bool bNew = pItem->GetValue(); + if ( bNew != aInputOptions.GetTextWysiwyg() ) + { + aInputOptions.SetTextWysiwyg( bNew ); + bSaveInputOptions = true; + bUpdateRefDev = true; + } + } + if( const SfxBoolItem* pItem = rOptSet.GetItemIfSet( SID_SC_INPUT_REPLCELLSWARN ) ) + { + aInputOptions.SetReplaceCellsWarn( pItem->GetValue() ); + bSaveInputOptions = true; + } + + if( const SfxBoolItem* pItem = rOptSet.GetItemIfSet( SID_SC_INPUT_LEGACY_CELL_SELECTION ) ) + { + aInputOptions.SetLegacyCellSelection( pItem->GetValue() ); + bSaveInputOptions = true; + } + + if( const SfxBoolItem* pItem = rOptSet.GetItemIfSet( SID_SC_INPUT_ENTER_PASTE_MODE ) ) + { + aInputOptions.SetEnterPasteMode( pItem->GetValue() ); + bSaveInputOptions = true; + } + + // PrintOptions + if ( const ScTpPrintItem* pItem = rOptSet.GetItemIfSet(SID_SCPRINTOPTIONS) ) + { + const ScPrintOptions& rNewOpt = pItem->GetPrintOptions(); + SetPrintOptions( rNewOpt ); + + // broadcast causes all previews to recalc page numbers + SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScPrintOptions ) ); + } + + if ( bSaveAppOptions ) + m_pAppCfg->SetOptions(aAppOptions); + + if ( bSaveInputOptions ) + m_pInputCfg->SetOptions(aInputOptions); + + // Kick off recalculation? + if (pDoc && bCompileErrorCells) + { + // Re-compile cells with name error, and recalc if at least one cell + // has been re-compiled. In the future we may want to find a way to + // recalc only those that are affected. + if (pDoc->CompileErrorCells(FormulaError::NoName)) + bCalcAll = true; + } + + if ( pDoc && bCalcAll ) + { + weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); + pDoc->CalcAll(); + if ( pViewSh ) + pViewSh->UpdateCharts( true ); + else + ScDBFunc::DoUpdateCharts( ScAddress(), *pDoc, true ); + if (pBindings) + pBindings->Invalidate( SID_ATTR_SIZE ); //SvxPosSize StatusControl Update + } + + if ( pViewSh && bUpdateMarks ) + pViewSh->UpdateAutoFillMark(); + + // Repaint View? + if ( pViewSh && bRepaint ) + { + pViewSh->UpdateFixPos(); + pViewSh->PaintGrid(); + pViewSh->PaintTop(); + pViewSh->PaintLeft(); + pViewSh->PaintExtras(); + pViewSh->InvalidateBorder(); + if (pBindings) + { + pBindings->Invalidate( FID_TOGGLEHEADERS ); // -> Checks in menu + pBindings->Invalidate( FID_TOGGLESYNTAX ); + } + } + + // update ref device (for all documents) + if ( !bUpdateRefDev ) + return; + + // for all documents: recalc output factor, update row heights + SfxObjectShell* pObjSh = SfxObjectShell::GetFirst(); + while ( pObjSh ) + { + if ( auto pOneDocSh = dynamic_cast<ScDocShell *>(pObjSh) ) + { + pOneDocSh->CalcOutputFactor(); + SCTAB nTabCount = pOneDocSh->GetDocument().GetTableCount(); + for (SCTAB nTab=0; nTab<nTabCount; nTab++) + pOneDocSh->AdjustRowHeight( 0, pDocSh->GetDocument().MaxRow(), nTab ); + } + pObjSh = SfxObjectShell::GetNext( *pObjSh ); + } + + // for all (tab-) views: + SfxViewShell* pSh = SfxViewShell::GetFirst( true, checkSfxViewShell<ScTabViewShell> ); + while ( pSh ) + { + ScTabViewShell* pOneViewSh = static_cast<ScTabViewShell*>(pSh); + + // set ref-device for EditEngine + ScInputHandler* pHdl = GetInputHdl(pOneViewSh); + if (pHdl) + pHdl->UpdateRefDevice(); + + // update view scale + ScViewData& rViewData = pOneViewSh->GetViewData(); + pOneViewSh->SetZoom( rViewData.GetZoomX(), rViewData.GetZoomY(), false ); + + // repaint + pOneViewSh->PaintGrid(); + pOneViewSh->PaintTop(); + pOneViewSh->PaintLeft(); + + pSh = SfxViewShell::GetNext( *pSh, true, checkSfxViewShell<ScTabViewShell> ); + } +} + +/** + * Input-Handler + */ +ScInputHandler* ScModule::GetInputHdl( ScTabViewShell* pViewSh, bool bUseRef ) +{ + if ( !comphelper::LibreOfficeKit::isActive() && m_pRefInputHandler && bUseRef ) + return m_pRefInputHandler; + + ScInputHandler* pHdl = nullptr; + if ( !pViewSh ) + { + // in case a UIActive embedded object has no ViewShell (UNO component) + // the own calc view shell will be set as current, but no handling should happen + ScTabViewShell* pCurViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() ); + if ( pCurViewSh && !pCurViewSh->GetUIActiveClient() ) + pViewSh = pCurViewSh; + } + + if ( pViewSh ) + pHdl = pViewSh->GetInputHandler(); // Viewshell always has one, from now on + + // If no ViewShell passed or active, we can get NULL + OSL_ENSURE( pHdl || !pViewSh, "GetInputHdl: no InputHandler found!" ); + return pHdl; +} + +void ScModule::ViewShellChanged(bool bStopEditing /*=true*/) +{ + ScInputHandler* pHdl = GetInputHdl(); + ScTabViewShell* pShell = ScTabViewShell::GetActiveViewShell(); + if ( pShell && pHdl ) + pShell->UpdateInputHandler(false, bStopEditing); +} + +void ScModule::SetInputMode( ScInputMode eMode, const OUString* pInitText ) +{ + ScInputHandler* pHdl = GetInputHdl(); + if (pHdl) + pHdl->SetMode(eMode, pInitText); +} + +bool ScModule::IsEditMode() +{ + ScInputHandler* pHdl = GetInputHdl(); + return pHdl && pHdl->IsEditMode(); +} + +bool ScModule::IsInputMode() +{ + ScInputHandler* pHdl = GetInputHdl(); + return pHdl && pHdl->IsInputMode(); +} + +bool ScModule::InputKeyEvent( const KeyEvent& rKEvt, bool bStartEdit ) +{ + ScInputHandler* pHdl = GetInputHdl(); + return pHdl && pHdl->KeyInput( rKEvt, bStartEdit ); +} + +void ScModule::InputEnterHandler( ScEnterMode nBlockMode, bool bBeforeSavingInLOK ) +{ + if ( !SfxGetpApp()->IsDowning() ) // Not when quitting the program + { + ScInputHandler* pHdl = GetInputHdl(); + if (pHdl) + pHdl->EnterHandler( nBlockMode, bBeforeSavingInLOK ); + } +} + +void ScModule::InputCancelHandler() +{ + ScInputHandler* pHdl = GetInputHdl(); + if (pHdl) + pHdl->CancelHandler(); +} + +void ScModule::InputSelection( const EditView* pView ) +{ + ScInputHandler* pHdl = GetInputHdl(); + if (pHdl) + pHdl->InputSelection( pView ); +} + +void ScModule::InputChanged( const EditView* pView ) +{ + ScInputHandler* pHdl = GetInputHdl(); + if (pHdl) + pHdl->InputChanged( pView, false ); +} + +void ScModule::ViewShellGone( const ScTabViewShell* pViewSh ) +{ + ScInputHandler* pHdl = GetInputHdl(); + if (pHdl) + pHdl->ViewShellGone( pViewSh ); +} + +void ScModule::SetRefInputHdl( ScInputHandler* pNew ) +{ + m_pRefInputHandler = pNew; +} + +void ScModule::InputGetSelection( sal_Int32& rStart, sal_Int32& rEnd ) +{ + ScInputHandler* pHdl = GetInputHdl(); + if (pHdl) + pHdl->InputGetSelection( rStart, rEnd ); +} + +void ScModule::InputSetSelection( sal_Int32 nStart, sal_Int32 nEnd ) +{ + ScInputHandler* pHdl = GetInputHdl(); + if (pHdl) + pHdl->InputSetSelection( nStart, nEnd ); +} + +void ScModule::InputReplaceSelection( const OUString& rStr ) +{ + ScInputHandler* pHdl = GetInputHdl(); + if (pHdl) + pHdl->InputReplaceSelection( rStr ); +} + +void ScModule::InputTurnOffWinEngine() +{ + ScInputHandler* pHdl = GetInputHdl(); + if (pHdl) + pHdl->InputTurnOffWinEngine(); +} + +void ScModule::ActivateInputWindow( const OUString* pStrFormula, bool bMatrix ) +{ + ScInputHandler* pHdl = GetInputHdl(); + if ( !pHdl ) + return; + + ScInputWindow* pWin = pHdl->GetInputWindow(); + if ( pStrFormula ) + { + // Take over formula + if ( pWin ) + { + pWin->SetFuncString( *pStrFormula, false ); + // SetSumAssignMode due to sal_False not necessary + } + ScEnterMode nMode = bMatrix ? ScEnterMode::MATRIX : ScEnterMode::NORMAL; + pHdl->EnterHandler( nMode ); + + // Without Invalidate the selection remains active, if the formula has not changed + if (pWin) + pWin->TextInvalidate(); + } + else + { + // Cancel + if ( pWin ) + { + pWin->SetFuncString( OUString(), false ); + // SetSumAssignMode due to sal_False no necessary + } + pHdl->CancelHandler(); + } +} + +/** + * Reference dialogs + */ +void ScModule::SetRefDialog( sal_uInt16 nId, bool bVis, SfxViewFrame* pViewFrm ) +{ + //TODO: Move reference dialog handling to view + // Just keep function autopilot here for references to other documents + if ( !(m_nCurRefDlgId == 0 || ( nId == m_nCurRefDlgId && !bVis ) + || ( comphelper::LibreOfficeKit::isActive() )) ) + return; + + if ( !pViewFrm ) + pViewFrm = SfxViewFrame::Current(); + + // bindings update causes problems with update of stylist if + // current style family has changed + //if ( pViewFrm ) + // pViewFrm->GetBindings().Update(); // to avoid trouble in LockDispatcher + + // before SetChildWindow + if ( comphelper::LibreOfficeKit::isActive() ) + { + if ( bVis ) + m_nCurRefDlgId = nId; + } + else + { + m_nCurRefDlgId = bVis ? nId : 0; + } + + if ( pViewFrm ) + { + // store the dialog id also in the view shell + SfxViewShell* pViewSh = pViewFrm->GetViewShell(); + if (ScTabViewShell* pTabViewSh = dynamic_cast<ScTabViewShell*>(pViewSh)) + pTabViewSh->SetCurRefDlgId(m_nCurRefDlgId); + else + { + // no ScTabViewShell - possible for example from a Basic macro + bVis = false; + m_nCurRefDlgId = 0; // don't set nCurRefDlgId if no dialog is created + } + + pViewFrm->SetChildWindow( nId, bVis ); + } + + SfxApplication* pSfxApp = SfxGetpApp(); + pSfxApp->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) ); +} + +static SfxChildWindow* lcl_GetChildWinFromCurrentView( sal_uInt16 nId ) +{ + SfxViewFrame* pViewFrm = SfxViewFrame::Current(); + + // #i46999# current view frame can be null (for example, when closing help) + return pViewFrm ? pViewFrm->GetChildWindow( nId ) : nullptr; +} + +static SfxChildWindow* lcl_GetChildWinFromAnyView( sal_uInt16 nId ) +{ + // First, try the current view + SfxChildWindow* pChildWnd = lcl_GetChildWinFromCurrentView( nId ); + if ( pChildWnd ) + return pChildWnd; // found in the current view + + // if not found there, get the child window from any open view + // it can be open only in one view because nCurRefDlgId is global + + SfxViewFrame* pViewFrm = SfxViewFrame::GetFirst(); + while ( pViewFrm ) + { + pChildWnd = pViewFrm->GetChildWindow( nId ); + if ( pChildWnd ) + return pChildWnd; // found in any view + + pViewFrm = SfxViewFrame::GetNext( *pViewFrm ); + } + + return nullptr; // none found +} + +bool ScModule::IsModalMode(SfxObjectShell* pDocSh) +{ + //TODO: Move reference dialog handling to view + // Just keep function autopilot here for references to other documents + bool bIsModal = false; + + if ( m_nCurRefDlgId ) + { + SfxChildWindow* pChildWnd = lcl_GetChildWinFromCurrentView( m_nCurRefDlgId ); + if ( pChildWnd ) + { + if (pChildWnd->GetController()) + { + IAnyRefDialog* pRefDlg = dynamic_cast<IAnyRefDialog*>(pChildWnd->GetController().get()); + assert(pRefDlg); + bIsModal = pChildWnd->IsVisible() && pRefDlg && + !( pRefDlg->IsRefInputMode() && pRefDlg->IsDocAllowed(pDocSh) ); + } + } + else if ( pDocSh && comphelper::LibreOfficeKit::isActive() ) + { + // m_nCurRefDlgId is not deglobalized so it can be set by other view + // in LOK case when no ChildWindow for this view was detected -> fallback + ScInputHandler* pHdl = GetInputHdl(); + if ( pHdl ) + bIsModal = pHdl->IsModalMode(pDocSh); + } + } + else if (pDocSh) + { + ScInputHandler* pHdl = GetInputHdl(); + if ( pHdl ) + bIsModal = pHdl->IsModalMode(pDocSh); + } + + return bIsModal; +} + +bool ScModule::IsTableLocked() +{ + //TODO: Move reference dialog handling to view + // Just keep function autopilot here for references to other documents + bool bLocked = false; + + // Up until now just for ScAnyRefDlg + if ( m_nCurRefDlgId ) + { + SfxChildWindow* pChildWnd = lcl_GetChildWinFromAnyView( m_nCurRefDlgId ); + if ( pChildWnd ) + { + if (pChildWnd->GetController()) + { + IAnyRefDialog* pRefDlg = dynamic_cast<IAnyRefDialog*>(pChildWnd->GetController().get()); + assert(pRefDlg); + if (pRefDlg) + bLocked = pRefDlg->IsTableLocked(); + } + } + else if (!comphelper::LibreOfficeKit::isActive()) + bLocked = true; // for other views, see IsModalMode + } + + // We can't stop LOK clients from switching part, and getting out of sync. + assert(!bLocked || !comphelper::LibreOfficeKit::isActive()); + + return bLocked; +} + +bool ScModule::IsRefDialogOpen() +{ + //TODO: Move reference dialog handling to view + // Just keep function autopilot here for references to other documents + bool bIsOpen = false; + + if ( m_nCurRefDlgId ) + { + SfxChildWindow* pChildWnd = lcl_GetChildWinFromCurrentView( m_nCurRefDlgId ); + if ( pChildWnd ) + bIsOpen = pChildWnd->IsVisible(); + } + + return bIsOpen; +} + +bool ScModule::IsFormulaMode() +{ + //TODO: Move reference dialog handling to view + // Just keep function autopilot here for references to other documents + bool bIsFormula = false; + + if ( m_nCurRefDlgId ) + { + SfxChildWindow* pChildWnd = nullptr; + + if ( comphelper::LibreOfficeKit::isActive() ) + pChildWnd = lcl_GetChildWinFromCurrentView( m_nCurRefDlgId ); + else + pChildWnd = lcl_GetChildWinFromAnyView( m_nCurRefDlgId ); + + if ( pChildWnd ) + { + if (pChildWnd->GetController()) + { + IAnyRefDialog* pRefDlg = dynamic_cast<IAnyRefDialog*>(pChildWnd->GetController().get()); + assert(pRefDlg); + bIsFormula = pChildWnd->IsVisible() && pRefDlg && pRefDlg->IsRefInputMode(); + } + } + else if ( comphelper::LibreOfficeKit::isActive() ) + { + // m_nCurRefDlgId is not deglobalized so it can be set by other view + // in LOK case when no ChildWindow for this view was detected -> fallback + ScInputHandler* pHdl = GetInputHdl(); + if ( pHdl ) + bIsFormula = pHdl->IsFormulaMode(); + } + } + else + { + ScInputHandler* pHdl = GetInputHdl(); + if ( pHdl ) + bIsFormula = pHdl->IsFormulaMode(); + } + + if (m_bIsInEditCommand) + bIsFormula = true; + + return bIsFormula; +} + +static void lcl_MarkedTabs( const ScMarkData& rMark, SCTAB& rStartTab, SCTAB& rEndTab ) +{ + if (rMark.GetSelectCount() > 1) + { + rEndTab = rMark.GetLastSelected(); + rStartTab = rMark.GetFirstSelected(); + } +} + +void ScModule::SetReference( const ScRange& rRef, ScDocument& rDoc, + const ScMarkData* pMarkData ) +{ + //TODO: Move reference dialog handling to view + // Just keep function autopilot here for references to other documents + + // In RefDialogs we also trigger the ZoomIn, if the Ref's Start and End are different + ScRange aNew = rRef; + aNew.PutInOrder(); // Always in the right direction + + if( m_nCurRefDlgId ) + { + SfxChildWindow* pChildWnd = nullptr; + + if ( comphelper::LibreOfficeKit::isActive() ) + pChildWnd = lcl_GetChildWinFromCurrentView( m_nCurRefDlgId ); + else + pChildWnd = lcl_GetChildWinFromAnyView( m_nCurRefDlgId ); + + OSL_ENSURE( pChildWnd, "NoChildWin" ); + if ( pChildWnd ) + { + if ( m_nCurRefDlgId == SID_OPENDLG_CONSOLIDATE && pMarkData ) + { + SCTAB nStartTab = aNew.aStart.Tab(); + SCTAB nEndTab = aNew.aEnd.Tab(); + lcl_MarkedTabs( *pMarkData, nStartTab, nEndTab ); + aNew.aStart.SetTab(nStartTab); + aNew.aEnd.SetTab(nEndTab); + } + + if (pChildWnd->GetController()) + { + IAnyRefDialog* pRefDlg = dynamic_cast<IAnyRefDialog*>(pChildWnd->GetController().get()); + assert(pRefDlg); + if(pRefDlg) + { + // hide the (color) selection now instead of later from LoseFocus, + // don't abort the ref input that causes this call (bDoneRefMode = sal_False) + pRefDlg->HideReference( false ); + pRefDlg->SetReference( aNew, rDoc ); + } + } + } + else if ( comphelper::LibreOfficeKit::isActive() ) + { + // m_nCurRefDlgId is not deglobalized so it can be set by other view + // in LOK case when no ChildWindow for this view was detected -> fallback + ScInputHandler* pHdl = GetInputHdl(); + if (pHdl) + pHdl->SetReference( aNew, rDoc ); + } + } + else + { + ScInputHandler* pHdl = GetInputHdl(); + if (pHdl) + pHdl->SetReference( aNew, rDoc ); + else + { + OSL_FAIL("SetReference without receiver"); + } + } +} + +/** + * Multiple selection + */ +void ScModule::AddRefEntry() +{ + //TODO: Move reference dialog handling to view + // Just keep function autopilot here for references to other documents + if ( m_nCurRefDlgId ) + { + SfxChildWindow* pChildWnd = lcl_GetChildWinFromAnyView( m_nCurRefDlgId ); + OSL_ENSURE( pChildWnd, "NoChildWin" ); + if ( pChildWnd ) + { + if (pChildWnd->GetController()) + { + IAnyRefDialog* pRefDlg = dynamic_cast<IAnyRefDialog*>(pChildWnd->GetController().get()); + assert(pRefDlg); + if (pRefDlg) + { + pRefDlg->AddRefEntry(); + } + } + } + } + else + { + ScInputHandler* pHdl = GetInputHdl(); + if (pHdl) + pHdl->AddRefEntry(); + } +} + +void ScModule::EndReference() +{ + //TODO: Move reference dialog handling to view + // Just keep function autopilot here for references to other documents + + // We also annul the ZoomIn again in RefDialogs + + //FIXME: ShowRefFrame at InputHdl, if the Function AutoPilot is open? + if ( !m_nCurRefDlgId ) + return; + + SfxChildWindow* pChildWnd = nullptr; + + if ( comphelper::LibreOfficeKit::isActive() ) + pChildWnd = lcl_GetChildWinFromCurrentView( m_nCurRefDlgId ); + else + pChildWnd = lcl_GetChildWinFromAnyView( m_nCurRefDlgId ); + + OSL_ENSURE( pChildWnd, "NoChildWin" ); + if ( pChildWnd ) + { + if (pChildWnd->GetController()) + { + IAnyRefDialog* pRefDlg = dynamic_cast<IAnyRefDialog*>(pChildWnd->GetController().get()); + assert(pRefDlg); + if(pRefDlg) + { + pRefDlg->SetActive(); + } + } + } +} + +/** + * Idle/OnlineSpelling + */ +void ScModule::AnythingChanged() +{ + sal_uInt64 nOldTime = m_aIdleTimer.GetTimeout(); + if ( nOldTime != SC_IDLE_MIN ) + m_aIdleTimer.SetTimeout( SC_IDLE_MIN ); + + nIdleCount = 0; +} + +static void lcl_CheckNeedsRepaint( const ScDocShell* pDocShell ) +{ + SfxViewFrame* pFrame = SfxViewFrame::GetFirst( pDocShell ); + while ( pFrame ) + { + SfxViewShell* p = pFrame->GetViewShell(); + ScTabViewShell* pViewSh = dynamic_cast< ScTabViewShell *>( p ); + if ( pViewSh ) + pViewSh->CheckNeedsRepaint(); + pFrame = SfxViewFrame::GetNext( *pFrame, pDocShell ); + } +} + +IMPL_LINK_NOARG(ScModule, IdleHandler, Timer *, void) +{ + if ( Application::AnyInput( VclInputFlags::MOUSE | VclInputFlags::KEYBOARD ) ) + { + m_aIdleTimer.Start(); // Timeout unchanged + return; + } + + bool bMore = false; + ScDocShell* pDocSh = dynamic_cast<ScDocShell*>(SfxObjectShell::Current()); + + if ( pDocSh ) + { + ScDocument& rDoc = pDocSh->GetDocument(); + sc::DocumentLinkManager& rLinkMgr = rDoc.GetDocLinkManager(); + bool bLinks = rLinkMgr.idleCheckLinks(); + bool bWidth = rDoc.IdleCalcTextWidth(); + + bMore = bLinks || bWidth; // Still something at all? + + // While calculating a Basic formula, a paint event may have occurred, + // so check the bNeedsRepaint flags for this document's views + if (bWidth) + lcl_CheckNeedsRepaint( pDocSh ); + } + + + sal_uInt64 nOldTime = m_aIdleTimer.GetTimeout(); + sal_uInt64 nNewTime = nOldTime; + if ( bMore ) + { + nNewTime = SC_IDLE_MIN; + nIdleCount = 0; + } + else + { + // Set SC_IDLE_COUNT to initial Timeout - increase afterwards + if ( nIdleCount < SC_IDLE_COUNT ) + ++nIdleCount; + else + { + nNewTime += SC_IDLE_STEP; + if ( nNewTime > SC_IDLE_MAX ) + nNewTime = SC_IDLE_MAX; + } + } + if ( nNewTime != nOldTime ) + m_aIdleTimer.SetTimeout( nNewTime ); + + + m_aIdleTimer.Start(); +} + +/** + * Virtual methods for the OptionsDialog + */ +std::optional<SfxItemSet> ScModule::CreateItemSet( sal_uInt16 nId ) +{ + std::optional<SfxItemSet> pRet; + if(SID_SC_EDITOPTIONS == nId) + { + pRet.emplace( + GetPool(), + svl::Items< + // TP_USERLISTS: + SCITEM_USERLIST, SCITEM_USERLIST, + // TP_GRID: + SID_ATTR_GRID_OPTIONS, SID_ATTR_GRID_OPTIONS, + SID_ATTR_METRIC, SID_ATTR_METRIC, + SID_ATTR_DEFTABSTOP, SID_ATTR_DEFTABSTOP, + // TP_INPUT: + SID_SC_INPUT_LEGACY_CELL_SELECTION, SID_SC_OPT_SORT_REF_UPDATE, + // TP_FORMULA, TP_DEFAULTS: + SID_SCFORMULAOPTIONS, SID_SCDEFAULTSOPTIONS, + // TP_VIEW, TP_CALC: + SID_SCVIEWOPTIONS, SID_SCDOCOPTIONS, + // TP_INPUT: + SID_SC_INPUT_ENTER_PASTE_MODE, SID_SC_INPUT_ENTER_PASTE_MODE, + // TP_PRINT: + SID_SCPRINTOPTIONS, SID_SCPRINTOPTIONS, + // TP_INPUT: + SID_SC_INPUT_SELECTION, SID_SC_INPUT_MARK_HEADER, + SID_SC_INPUT_TEXTWYSIWYG, SID_SC_INPUT_TEXTWYSIWYG, + SID_SC_INPUT_REPLCELLSWARN, SID_SC_INPUT_REPLCELLSWARN, + // TP_VIEW: + SID_SC_OPT_SYNCZOOM, SID_SC_OPT_KEY_BINDING_COMPAT>); + + const ScAppOptions& rAppOpt = GetAppOptions(); + + ScDocShell* pDocSh = dynamic_cast< ScDocShell *>( SfxObjectShell::Current() ); + ScDocOptions aCalcOpt = pDocSh + ? pDocSh->GetDocument().GetDocOptions() + : GetDocOptions(); + + ScTabViewShell* pViewSh = dynamic_cast< ScTabViewShell *>( SfxViewShell::Current() ); + ScViewOptions aViewOpt = pViewSh + ? pViewSh->GetViewData().GetOptions() + : GetViewOptions(); + + ScUserListItem aULItem( SCITEM_USERLIST ); + ScUserList* pUL = ScGlobal::GetUserList(); + + // SfxGetpApp()->GetOptions( aSet ); + + pRet->Put( SfxUInt16Item( SID_ATTR_METRIC, + sal::static_int_cast<sal_uInt16>(rAppOpt.GetAppMetric()) ) ); + + // TP_CALC + pRet->Put( SfxUInt16Item( SID_ATTR_DEFTABSTOP, + aCalcOpt.GetTabDistance())); + pRet->Put( ScTpCalcItem( SID_SCDOCOPTIONS, aCalcOpt ) ); + + // TP_VIEW + pRet->Put( ScTpViewItem( aViewOpt ) ); + pRet->Put( SfxBoolItem( SID_SC_OPT_SYNCZOOM, rAppOpt.GetSynchronizeZoom() ) ); + + // TP_INPUT + const ScInputOptions& rInpOpt = GetInputOptions(); + pRet->Put( SfxUInt16Item( SID_SC_INPUT_SELECTIONPOS, + rInpOpt.GetMoveDir() ) ); + pRet->Put( SfxBoolItem( SID_SC_INPUT_SELECTION, + rInpOpt.GetMoveSelection() ) ); + pRet->Put( SfxBoolItem( SID_SC_INPUT_EDITMODE, + rInpOpt.GetEnterEdit() ) ); + pRet->Put( SfxBoolItem( SID_SC_INPUT_FMT_EXPAND, + rInpOpt.GetExtendFormat() ) ); + pRet->Put( SfxBoolItem( SID_SC_INPUT_RANGEFINDER, + rInpOpt.GetRangeFinder() ) ); + pRet->Put( SfxBoolItem( SID_SC_INPUT_REF_EXPAND, + rInpOpt.GetExpandRefs() ) ); + pRet->Put( SfxBoolItem(SID_SC_OPT_SORT_REF_UPDATE, rInpOpt.GetSortRefUpdate())); + pRet->Put( SfxBoolItem( SID_SC_INPUT_MARK_HEADER, + rInpOpt.GetMarkHeader() ) ); + pRet->Put( SfxBoolItem( SID_SC_INPUT_TEXTWYSIWYG, + rInpOpt.GetTextWysiwyg() ) ); + pRet->Put( SfxBoolItem( SID_SC_INPUT_REPLCELLSWARN, + rInpOpt.GetReplaceCellsWarn() ) ); + pRet->Put( SfxBoolItem( SID_SC_INPUT_LEGACY_CELL_SELECTION, + rInpOpt.GetLegacyCellSelection() ) ); + pRet->Put( SfxBoolItem( SID_SC_INPUT_ENTER_PASTE_MODE, + rInpOpt.GetEnterPasteMode() ) ); + + // RID_SC_TP_PRINT + pRet->Put( ScTpPrintItem( GetPrintOptions() ) ); + + // TP_GRID + pRet->Put( aViewOpt.CreateGridItem() ); + + // TP_USERLISTS + if ( pUL ) + { + aULItem.SetUserList( *pUL ); + pRet->Put(aULItem); + } + + // TP_COMPATIBILITY + pRet->Put( SfxUInt16Item( SID_SC_OPT_KEY_BINDING_COMPAT, + rAppOpt.GetKeyBindingType() ) ); + + // TP_DEFAULTS + pRet->Put( ScTpDefaultsItem( GetDefaultsOptions() ) ); + + // TP_FORMULA + ScFormulaOptions aOptions = GetFormulaOptions(); + if (pDocSh) + { + ScCalcConfig aConfig( aOptions.GetCalcConfig()); + aConfig.MergeDocumentSpecific( pDocSh->GetDocument().GetCalcConfig()); + aOptions.SetCalcConfig( aConfig); + } + pRet->Put( ScTpFormulaItem( aOptions ) ); + } + return pRet; +} + +void ScModule::ApplyItemSet( sal_uInt16 nId, const SfxItemSet& rSet ) +{ + if(SID_SC_EDITOPTIONS == nId) + { + ModifyOptions( rSet ); + } +} + +std::unique_ptr<SfxTabPage> ScModule::CreateTabPage( sal_uInt16 nId, weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet ) +{ + std::unique_ptr<SfxTabPage> xRet; + ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create(); + switch(nId) + { + case SID_SC_TP_LAYOUT: + { + ::CreateTabPage ScTpLayoutOptionsCreate = pFact->GetTabPageCreatorFunc(SID_SC_TP_LAYOUT); + if (ScTpLayoutOptionsCreate) + xRet = (*ScTpLayoutOptionsCreate)(pPage, pController, &rSet); + break; + } + case SID_SC_TP_CONTENT: + { + ::CreateTabPage ScTpContentOptionsCreate = pFact->GetTabPageCreatorFunc(SID_SC_TP_CONTENT); + if (ScTpContentOptionsCreate) + xRet = (*ScTpContentOptionsCreate)(pPage, pController, &rSet); + break; + } + case SID_SC_TP_GRID: + xRet = SvxGridTabPage::Create(pPage, pController, rSet); + break; + case SID_SC_TP_USERLISTS: + { + ::CreateTabPage ScTpUserListsCreate = pFact->GetTabPageCreatorFunc(SID_SC_TP_USERLISTS); + if (ScTpUserListsCreate) + xRet = (*ScTpUserListsCreate)(pPage, pController, &rSet); + break; + } + case SID_SC_TP_CALC: + { + ::CreateTabPage ScTpCalcOptionsCreate = pFact->GetTabPageCreatorFunc(SID_SC_TP_CALC); + if (ScTpCalcOptionsCreate) + xRet = (*ScTpCalcOptionsCreate)(pPage, pController, &rSet); + break; + } + case SID_SC_TP_FORMULA: + { + ::CreateTabPage ScTpFormulaOptionsCreate = pFact->GetTabPageCreatorFunc(SID_SC_TP_FORMULA); + if (ScTpFormulaOptionsCreate) + xRet = (*ScTpFormulaOptionsCreate)(pPage, pController, &rSet); + break; + } + case SID_SC_TP_COMPATIBILITY: + { + ::CreateTabPage ScTpCompatOptionsCreate = pFact->GetTabPageCreatorFunc(SID_SC_TP_COMPATIBILITY); + if (ScTpCompatOptionsCreate) + xRet = (*ScTpCompatOptionsCreate)(pPage, pController, &rSet); + break; + } + case SID_SC_TP_CHANGES: + { + ::CreateTabPage ScRedlineOptionsTabPageCreate = pFact->GetTabPageCreatorFunc(SID_SC_TP_CHANGES); + if (ScRedlineOptionsTabPageCreate) + xRet =(*ScRedlineOptionsTabPageCreate)(pPage, pController, &rSet); + break; + } + case RID_SC_TP_PRINT: + { + ::CreateTabPage ScTpPrintOptionsCreate = pFact->GetTabPageCreatorFunc(RID_SC_TP_PRINT); + if (ScTpPrintOptionsCreate) + xRet = (*ScTpPrintOptionsCreate)(pPage, pController, &rSet); + break; + } + case RID_SC_TP_DEFAULTS: + { + ::CreateTabPage ScTpDefaultsOptionsCreate = pFact->GetTabPageCreatorFunc(RID_SC_TP_DEFAULTS); + if (ScTpDefaultsOptionsCreate) + xRet = (*ScTpDefaultsOptionsCreate)(pPage, pController, &rSet); + break; + } + } + + OSL_ENSURE( xRet, "ScModule::CreateTabPage(): no valid ID for TabPage!" ); + + return xRet; +} + +IMPL_LINK( ScModule, CalcFieldValueHdl, EditFieldInfo*, pInfo, void ) +{ + //TODO: Merge with ScFieldEditEngine! + if (!pInfo) + return; + + const SvxFieldItem& rField = pInfo->GetField(); + const SvxFieldData* pField = rField.GetField(); + + if (const SvxURLField* pURLField = dynamic_cast<const SvxURLField*>(pField)) + { + // URLField + const OUString& aURL = pURLField->GetURL(); + + switch ( pURLField->GetFormat() ) + { + case SvxURLFormat::AppDefault: //TODO: Settable in the App? + case SvxURLFormat::Repr: + { + pInfo->SetRepresentation( pURLField->GetRepresentation() ); + } + break; + + case SvxURLFormat::Url: + { + pInfo->SetRepresentation( aURL ); + } + break; + } + + svtools::ColorConfigEntry eEntry = + INetURLHistory::GetOrCreate()->QueryUrl( aURL ) ? svtools::LINKSVISITED : svtools::LINKS; + pInfo->SetTextColor( GetColorConfig().GetColorValue(eEntry).nColor ); + } + else + { + OSL_FAIL("Unknown Field"); + pInfo->SetRepresentation(OUString('?')); + } +} + +void ScModule::RegisterRefController(sal_uInt16 nSlotId, std::shared_ptr<SfxDialogController>& rWnd, weld::Window* pWndAncestor) +{ + std::vector<std::pair<std::shared_ptr<SfxDialogController>, weld::Window*>> & rlRefWindow = m_mapRefController[nSlotId]; + + if (std::find_if(rlRefWindow.begin(), rlRefWindow.end(), + [rWnd](const std::pair<std::shared_ptr<SfxDialogController>, weld::Window*>& rCandidate) + { + return rCandidate.first.get() == rWnd.get(); + }) == rlRefWindow.end()) + { + rlRefWindow.emplace_back(rWnd, pWndAncestor); + } +} + +void ScModule::UnregisterRefController(sal_uInt16 nSlotId, const std::shared_ptr<SfxDialogController>& rWnd) +{ + auto iSlot = m_mapRefController.find( nSlotId ); + + if( iSlot == m_mapRefController.end() ) + return; + + std::vector<std::pair<std::shared_ptr<SfxDialogController>, weld::Window*>> & rlRefWindow = iSlot->second; + + auto i = std::find_if(rlRefWindow.begin(), rlRefWindow.end(), + [rWnd](const std::pair<std::shared_ptr<SfxDialogController>, weld::Window*>& rCandidate) + { + return rCandidate.first.get() == rWnd.get(); + }); + + if( i == rlRefWindow.end() ) + return; + + rlRefWindow.erase( i ); + + if( rlRefWindow.empty() ) + m_mapRefController.erase( nSlotId ); +} + +std::shared_ptr<SfxDialogController> ScModule::Find1RefWindow(sal_uInt16 nSlotId, const weld::Window *pWndAncestor) +{ + if (!pWndAncestor) + return nullptr; + + auto iSlot = m_mapRefController.find( nSlotId ); + + if( iSlot == m_mapRefController.end() ) + return nullptr; + + std::vector<std::pair<std::shared_ptr<SfxDialogController>, weld::Window*>> & rlRefWindow = iSlot->second; + + for (auto const& refWindow : rlRefWindow) + if ( refWindow.second == pWndAncestor ) + return refWindow.first; + + return nullptr; +} + +using namespace com::sun::star; + +constexpr OUStringLiteral LINGUPROP_AUTOSPELL = u"IsSpellAuto"; + +void ScModule::GetSpellSettings( LanguageType& rDefLang, LanguageType& rCjkLang, LanguageType& rCtlLang, + bool& rAutoSpell ) +{ + // use SvtLinguConfig instead of service LinguProperties to avoid + // loading the linguistic component + SvtLinguConfig aConfig; + + SvtLinguOptions aOptions; + aConfig.GetOptions( aOptions ); + + rDefLang = MsLangId::resolveSystemLanguageByScriptType(aOptions.nDefaultLanguage, css::i18n::ScriptType::LATIN); + rCjkLang = MsLangId::resolveSystemLanguageByScriptType(aOptions.nDefaultLanguage_CJK, css::i18n::ScriptType::ASIAN); + rCtlLang = MsLangId::resolveSystemLanguageByScriptType(aOptions.nDefaultLanguage_CTL, css::i18n::ScriptType::COMPLEX); + rAutoSpell = aOptions.bIsSpellAuto; +} + +void ScModule::SetAutoSpellProperty( bool bSet ) +{ + // use SvtLinguConfig instead of service LinguProperties to avoid + // loading the linguistic component + SvtLinguConfig aConfig; + + aConfig.SetProperty( LINGUPROP_AUTOSPELL, uno::Any(bSet) ); +} + +bool ScModule::HasThesaurusLanguage( LanguageType nLang ) +{ + if ( nLang == LANGUAGE_NONE ) + return false; + + bool bHasLang = false; + try + { + uno::Reference< linguistic2::XThesaurus > xThes(LinguMgr::GetThesaurus()); + if ( xThes.is() ) + bHasLang = xThes->hasLocale( LanguageTag::convertToLocale( nLang ) ); + } + catch( uno::Exception& ) + { + OSL_FAIL("Error in Thesaurus"); + } + + return bHasLang; +} + +std::optional<SfxStyleFamilies> ScModule::CreateStyleFamilies() +{ + SfxStyleFamilies aStyleFamilies; + + aStyleFamilies.emplace_back(SfxStyleFamilyItem(SfxStyleFamily::Para, + ScResId(STR_STYLE_FAMILY_CELL), + BMP_STYLES_FAMILY_CELL, + RID_CELLSTYLEFAMILY, SC_MOD()->GetResLocale())); + + aStyleFamilies.emplace_back(SfxStyleFamilyItem(SfxStyleFamily::Page, + ScResId(STR_STYLE_FAMILY_PAGE), + BMP_STYLES_FAMILY_PAGE, + RID_PAGESTYLEFAMILY, SC_MOD()->GetResLocale())); + + return aStyleFamilies; +} + +void ScModule::RegisterAutomationApplicationEventsCaller(css::uno::Reference< ooo::vba::XSinkCaller > const& xCaller) +{ + mxAutomationApplicationEventsCaller = xCaller; +} + +void ScModule::CallAutomationApplicationEventSinks(const OUString& Method, css::uno::Sequence< css::uno::Any >& Arguments) +{ + if (mxAutomationApplicationEventsCaller.is()) + mxAutomationApplicationEventsCaller->CallSinks(Method, Arguments); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/app/seltrans.cxx b/sc/source/ui/app/seltrans.cxx new file mode 100644 index 000000000..6ba63e4dc --- /dev/null +++ b/sc/source/ui/app/seltrans.cxx @@ -0,0 +1,429 @@ +/* -*- 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/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XPropertySetInfo.hpp> +#include <com/sun/star/form/FormButtonType.hpp> + +#include <tools/urlobj.hxx> +#include <sfx2/docfile.hxx> +#include <svx/svdograf.hxx> +#include <svx/svdouno.hxx> +#include <osl/diagnose.h> + +#include <seltrans.hxx> +#include <transobj.hxx> +#include <drwtrans.hxx> +#include <scmod.hxx> +#include <dbfunc.hxx> +#include <docsh.hxx> +#include <drawview.hxx> +#include <drwlayer.hxx> +#include <markdata.hxx> + +using namespace com::sun::star; + +static bool lcl_IsURLButton( SdrObject* pObject ) +{ + bool bRet = false; + + SdrUnoObj* pUnoCtrl = dynamic_cast<SdrUnoObj*>( pObject ); + if (pUnoCtrl && SdrInventor::FmForm == pUnoCtrl->GetObjInventor()) + { + const uno::Reference<awt::XControlModel>& xControlModel = pUnoCtrl->GetUnoControlModel(); + OSL_ENSURE( xControlModel.is(), "uno control without model" ); + if ( xControlModel.is() ) + { + uno::Reference< beans::XPropertySet > xPropSet( xControlModel, uno::UNO_QUERY ); + uno::Reference< beans::XPropertySetInfo > xInfo = xPropSet->getPropertySetInfo(); + + OUString sPropButtonType( "ButtonType" ); + if(xInfo->hasPropertyByName( sPropButtonType )) + { + uno::Any aAny = xPropSet->getPropertyValue( sPropButtonType ); + form::FormButtonType eTmp; + if ( (aAny >>= eTmp) && eTmp == form::FormButtonType_URL ) + bRet = true; + } + } + } + + return bRet; +} + +rtl::Reference<ScSelectionTransferObj> ScSelectionTransferObj::CreateFromView( ScTabView* pView ) +{ + rtl::Reference<ScSelectionTransferObj> pRet; + + try + { + if ( pView ) + { + ScSelectionTransferMode eMode = SC_SELTRANS_INVALID; + + SdrView* pSdrView = pView->GetScDrawView(); + if ( pSdrView ) + { + // handle selection on drawing layer + const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList(); + const size_t nMarkCount = rMarkList.GetMarkCount(); + if ( nMarkCount ) + { + if ( nMarkCount == 1 ) + { + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + SdrObjKind nSdrObjKind = pObj->GetObjIdentifier(); + + if ( nSdrObjKind == SdrObjKind::Graphic ) + { + if ( static_cast<SdrGrafObj*>(pObj)->GetGraphic().GetType() == GraphicType::Bitmap ) + eMode = SC_SELTRANS_DRAW_BITMAP; + else + eMode = SC_SELTRANS_DRAW_GRAPHIC; + } + else if ( nSdrObjKind == SdrObjKind::OLE2 ) + eMode = SC_SELTRANS_DRAW_OLE; + else if ( lcl_IsURLButton( pObj ) ) + eMode = SC_SELTRANS_DRAW_BOOKMARK; + } + + if ( eMode == SC_SELTRANS_INVALID ) + eMode = SC_SELTRANS_DRAW_OTHER; // something selected but no special selection + } + } + if ( eMode == SC_SELTRANS_INVALID ) // no drawing object selected + { + ScViewData& rViewData = pView->GetViewData(); + const ScMarkData& rMark = rViewData.GetMarkData(); + // allow MultiMarked because GetSimpleArea may be able to merge into a simple range + // (GetSimpleArea modifies a local copy of MarkData) + // Also allow simple filtered area. + if ( rMark.IsMarked() || rMark.IsMultiMarked() ) + { + ScRange aRange; + ScMarkType eMarkType = rViewData.GetSimpleArea( aRange ); + if (eMarkType == SC_MARK_SIMPLE || eMarkType == SC_MARK_SIMPLE_FILTERED) + { + // only for "real" selection, cursor alone isn't used + if ( aRange.aStart == aRange.aEnd ) + eMode = SC_SELTRANS_CELL; + else + eMode = SC_SELTRANS_CELLS; + } + } + } + + if ( eMode != SC_SELTRANS_INVALID ) + pRet = new ScSelectionTransferObj( pView, eMode ); + } + } + catch (...) + { + } + + return pRet; +} + +ScSelectionTransferObj::ScSelectionTransferObj( ScTabView* pSource, ScSelectionTransferMode eNewMode ) : + pView( pSource ), + eMode( eNewMode ) +{ + //! store range for StillValid +} + +ScSelectionTransferObj::~ScSelectionTransferObj() +{ + ScModule* pScMod = SC_MOD(); + if (pScMod && pScMod->GetSelectionTransfer() == this) + { + // this is reached when the object wasn't really copied to the selection + // (CopyToSelection has no effect under Windows) + + ForgetView(); + pScMod->SetSelectionTransfer( nullptr ); + } + + OSL_ENSURE( !pView, "ScSelectionTransferObj dtor: ForgetView not called" ); +} + +void ScSelectionTransferObj::ForgetView() +{ + pView = nullptr; + eMode = SC_SELTRANS_INVALID; + + mxCellData.clear(); + mxDrawData.clear(); +} + +void ScSelectionTransferObj::AddSupportedFormats() +{ + // AddSupportedFormats must work without actually creating the + // "real" transfer object + + switch (eMode) + { + case SC_SELTRANS_CELL: + case SC_SELTRANS_CELLS: + // same formats as in ScTransferObj::AddSupportedFormats + AddFormat( SotClipboardFormatId::EMBED_SOURCE ); + AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ); + AddFormat( SotClipboardFormatId::GDIMETAFILE ); + AddFormat( SotClipboardFormatId::PNG ); + AddFormat( SotClipboardFormatId::BITMAP ); + AddFormat( SotClipboardFormatId::HTML ); + AddFormat( SotClipboardFormatId::SYLK ); + AddFormat( SotClipboardFormatId::LINK ); + AddFormat( SotClipboardFormatId::DIF ); + AddFormat( SotClipboardFormatId::STRING ); + AddFormat( SotClipboardFormatId::STRING_TSVC ); + AddFormat( SotClipboardFormatId::RTF ); + AddFormat( SotClipboardFormatId::RICHTEXT ); + if ( eMode == SC_SELTRANS_CELL ) + { + AddFormat( SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT ); + } + break; + + // different graphic formats as in ScDrawTransferObj::AddSupportedFormats: + + case SC_SELTRANS_DRAW_BITMAP: + AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ); + AddFormat( SotClipboardFormatId::SVXB ); + AddFormat( SotClipboardFormatId::PNG ); + AddFormat( SotClipboardFormatId::BITMAP ); + AddFormat( SotClipboardFormatId::GDIMETAFILE ); + break; + + case SC_SELTRANS_DRAW_GRAPHIC: + AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ); + AddFormat( SotClipboardFormatId::SVXB ); + AddFormat( SotClipboardFormatId::GDIMETAFILE ); + AddFormat( SotClipboardFormatId::PNG ); + AddFormat( SotClipboardFormatId::BITMAP ); + break; + + case SC_SELTRANS_DRAW_BOOKMARK: + AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ); + AddFormat( SotClipboardFormatId::SOLK ); + AddFormat( SotClipboardFormatId::STRING ); + AddFormat( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ); + AddFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK ); + AddFormat( SotClipboardFormatId::DRAWING ); + break; + + case SC_SELTRANS_DRAW_OLE: + AddFormat( SotClipboardFormatId::EMBED_SOURCE ); + AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ); + AddFormat( SotClipboardFormatId::GDIMETAFILE ); + break; + + case SC_SELTRANS_DRAW_OTHER: + // other drawing objects + AddFormat( SotClipboardFormatId::EMBED_SOURCE ); + AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ); + AddFormat( SotClipboardFormatId::DRAWING ); + AddFormat( SotClipboardFormatId::PNG ); + AddFormat( SotClipboardFormatId::BITMAP ); + AddFormat( SotClipboardFormatId::GDIMETAFILE ); + break; + + default: + { + // added to avoid warnings + } + } +} + +void ScSelectionTransferObj::CreateCellData() +{ + OSL_ENSURE( !mxCellData.is(), "CreateCellData twice" ); + if ( pView ) + { + ScViewData& rViewData = pView->GetViewData(); + ScMarkData aNewMark( rViewData.GetMarkData() ); // use local copy for MarkToSimple + aNewMark.MarkToSimple(); + + // similar to ScViewFunctionSet::BeginDrag + if ( aNewMark.IsMarked() && !aNewMark.IsMultiMarked() ) + { + ScDocShell* pDocSh = rViewData.GetDocShell(); + + const ScRange& aSelRange = aNewMark.GetMarkArea(); + ScDocShellRef aDragShellRef; + if ( pDocSh->GetDocument().HasOLEObjectsInArea( aSelRange, &aNewMark ) ) + { + aDragShellRef = new ScDocShell; // DocShell needs a Ref immediately + aDragShellRef->DoInitNew(); + } + ScDrawLayer::SetGlobalDrawPersist( aDragShellRef.get() ); + + ScDocumentUniquePtr pClipDoc(new ScDocument( SCDOCMODE_CLIP )); + // bApi = sal_True -> no error messages + // #i18364# bStopEdit = sal_False -> don't end edit mode + // (this may be called from pasting into the edit line) + bool bCopied = rViewData.GetView()->CopyToClip( pClipDoc.get(), false, true, true, false ); + + ScDrawLayer::SetGlobalDrawPersist(nullptr); + + if ( bCopied ) + { + TransferableObjectDescriptor aObjDesc; + pDocSh->FillTransferableObjectDescriptor( aObjDesc ); + aObjDesc.maDisplayName = pDocSh->GetMedium()->GetURLObject().GetURLNoPass(); + // maSize is set in ScTransferObj ctor + + rtl::Reference<ScTransferObj> pTransferObj = new ScTransferObj( std::move(pClipDoc), aObjDesc ); + + // SetDragHandlePos is not used - there is no mouse position + //? pTransferObj->SetVisibleTab( nTab ); + + SfxObjectShellRef aPersistRef( aDragShellRef.get() ); + pTransferObj->SetDrawPersist( aPersistRef ); // keep persist for ole objects alive + + pTransferObj->SetDragSource( pDocSh, aNewMark ); + + mxCellData = pTransferObj; + } + } + } + OSL_ENSURE( mxCellData.is(), "can't create CellData" ); +} + +void ScSelectionTransferObj::CreateDrawData() +{ + OSL_ENSURE( !mxDrawData.is(), "CreateDrawData twice" ); + if ( pView ) + { + // similar to ScDrawView::BeginDrag + + ScDrawView* pDrawView = pView->GetScDrawView(); + if ( pDrawView ) + { + bool bAnyOle, bOneOle; + const SdrMarkList& rMarkList = pDrawView->GetMarkedObjectList(); + ScDrawView::CheckOle( rMarkList, bAnyOle, bOneOle ); + + ScDocShellRef aDragShellRef; + if (bAnyOle) + { + aDragShellRef = new ScDocShell; // Without Ref the DocShell does not live + aDragShellRef->DoInitNew(); + } + + ScDrawLayer::SetGlobalDrawPersist( aDragShellRef.get() ); + std::unique_ptr<SdrModel> pModel(pDrawView->CreateMarkedObjModel()); + ScDrawLayer::SetGlobalDrawPersist(nullptr); + + ScViewData& rViewData = pView->GetViewData(); + ScDocShell* pDocSh = rViewData.GetDocShell(); + + TransferableObjectDescriptor aObjDesc; + pDocSh->FillTransferableObjectDescriptor( aObjDesc ); + aObjDesc.maDisplayName = pDocSh->GetMedium()->GetURLObject().GetURLNoPass(); + // maSize is set in ScDrawTransferObj ctor + + rtl::Reference<ScDrawTransferObj> pTransferObj = new ScDrawTransferObj( std::move(pModel), pDocSh, aObjDesc ); + + SfxObjectShellRef aPersistRef( aDragShellRef.get() ); + pTransferObj->SetDrawPersist( aPersistRef ); // keep persist for ole objects alive + pTransferObj->SetDragSource( pDrawView ); // copies selection + + mxDrawData = pTransferObj; + } + } + OSL_ENSURE( mxDrawData.is(), "can't create DrawData" ); +} + +ScTransferObj* ScSelectionTransferObj::GetCellData() +{ + if ( !mxCellData.is() && ( eMode == SC_SELTRANS_CELL || eMode == SC_SELTRANS_CELLS ) ) + CreateCellData(); + return mxCellData.get(); +} + +ScDrawTransferObj* ScSelectionTransferObj::GetDrawData() +{ + if ( !mxDrawData.is() && ( eMode == SC_SELTRANS_DRAW_BITMAP || eMode == SC_SELTRANS_DRAW_GRAPHIC || + eMode == SC_SELTRANS_DRAW_BOOKMARK || eMode == SC_SELTRANS_DRAW_OLE || + eMode == SC_SELTRANS_DRAW_OTHER ) ) + CreateDrawData(); + return mxDrawData.get(); +} + +bool ScSelectionTransferObj::GetData( + const css::datatransfer::DataFlavor& rFlavor, const OUString& rDestDoc ) +{ + bool bOK = false; + + uno::Reference<datatransfer::XTransferable> xSource; + switch (eMode) + { + case SC_SELTRANS_CELL: + case SC_SELTRANS_CELLS: + xSource = GetCellData(); + break; + case SC_SELTRANS_DRAW_BITMAP: + case SC_SELTRANS_DRAW_GRAPHIC: + case SC_SELTRANS_DRAW_BOOKMARK: + case SC_SELTRANS_DRAW_OLE: + case SC_SELTRANS_DRAW_OTHER: + xSource = GetDrawData(); + break; + default: + { + // added to avoid warnings + } + } + + if ( xSource.is() ) + { + TransferableDataHelper aHelper( xSource ); + uno::Any aAny = aHelper.GetAny(rFlavor, rDestDoc); + bOK = SetAny( aAny ); + } + + return bOK; +} + +void ScSelectionTransferObj::ObjectReleased() +{ + // called when another selection is set from outside + + ForgetView(); + + ScModule* pScMod = SC_MOD(); + if ( pScMod->GetSelectionTransfer() == this ) + pScMod->SetSelectionTransfer( nullptr ); + + TransferableHelper::ObjectReleased(); +} + +sal_Bool SAL_CALL ScSelectionTransferObj::isComplex() +{ + switch (eMode) + { + case SC_SELTRANS_CELL: + case SC_SELTRANS_CELLS: + return false; + default: + return true; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/app/transobj.cxx b/sc/source/ui/app/transobj.cxx new file mode 100644 index 000000000..d0be7e0ae --- /dev/null +++ b/sc/source/ui/app/transobj.cxx @@ -0,0 +1,921 @@ +/* -*- 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 <scitems.hxx> +#include <editeng/justifyitem.hxx> + +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/embed/XTransactedObject.hpp> + +#include <o3tl/unit_conversion.hxx> +#include <osl/diagnose.h> +#include <unotools/tempfile.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <comphelper/fileformat.h> +#include <comphelper/lok.hxx> +#include <comphelper/storagehelper.hxx> +#include <comphelper/servicehelper.hxx> +#include <sot/storage.hxx> +#include <vcl/gdimtf.hxx> +#include <vcl/jobset.hxx> +#include <vcl/svapp.hxx> +#include <vcl/virdev.hxx> +#include <sfx2/docfile.hxx> + +#include <transobj.hxx> +#include <patattr.hxx> +#include <cellvalue.hxx> +#include <cellform.hxx> +#include <document.hxx> +#include <viewopti.hxx> +#include <editutil.hxx> +#include <impex.hxx> +#include <formulacell.hxx> +#include <printfun.hxx> +#include <docfunc.hxx> +#include <scmod.hxx> +#include <dragdata.hxx> +#include <sortparam.hxx> +#include <tabvwsh.hxx> + +#include <editeng/paperinf.hxx> +#include <editeng/sizeitem.hxx> +#include <formula/errorcodes.hxx> +#include <docsh.hxx> +#include <markdata.hxx> +#include <stlpool.hxx> +#include <viewdata.hxx> +#include <dociter.hxx> +#include <cellsuno.hxx> +#include <stringutil.hxx> +#include <formulaiter.hxx> + +using namespace com::sun::star; + +constexpr sal_uInt32 SCTRANS_TYPE_IMPEX = 1; +constexpr sal_uInt32 SCTRANS_TYPE_EDIT_RTF = 2; +constexpr sal_uInt32 SCTRANS_TYPE_EDIT_BIN = 3; +constexpr sal_uInt32 SCTRANS_TYPE_EMBOBJ = 4; +constexpr sal_uInt32 SCTRANS_TYPE_EDIT_ODF_TEXT_FLAT = 5; + +void ScTransferObj::GetAreaSize( const ScDocument& rDoc, SCTAB nTab1, SCTAB nTab2, SCROW& nRow, SCCOL& nCol ) +{ + SCCOL nMaxCol = 0; + SCROW nMaxRow = 0; + for( SCTAB nTab = nTab1; nTab <= nTab2; nTab++ ) + { + SCCOL nLastCol = 0; + SCROW nLastRow = 0; + // GetPrintArea instead of GetCellArea - include drawing objects + if( rDoc.GetPrintArea( nTab, nLastCol, nLastRow ) ) + { + if( nLastCol > nMaxCol ) + nMaxCol = nLastCol; + if( nLastRow > nMaxRow ) + nMaxRow = nLastRow; + } + } + nRow = nMaxRow; + nCol = nMaxCol; +} + +void ScTransferObj::PaintToDev( OutputDevice* pDev, ScDocument& rDoc, double nPrintFactor, + const ScRange& rBlock ) +{ + tools::Rectangle aBound( Point(), pDev->GetOutputSize() ); //! use size from clip area? + + ScViewData aViewData(rDoc); + + aViewData.SetTabNo( rBlock.aEnd.Tab() ); + aViewData.SetScreen( rBlock.aStart.Col(), rBlock.aStart.Row(), + rBlock.aEnd.Col(), rBlock.aEnd.Row() ); + + ScPrintFunc::DrawToDev( rDoc, pDev, nPrintFactor, aBound, &aViewData, false/*bMetaFile*/ ); +} + +ScTransferObj::ScTransferObj( const std::shared_ptr<ScDocument>& pClipDoc, const TransferableObjectDescriptor& rDesc ) : + m_pDoc( pClipDoc ), + m_nNonFiltered(0), + m_aObjDesc( rDesc ), + m_nDragHandleX( 0 ), + m_nDragHandleY( 0 ), + m_nSourceCursorX( m_pDoc->MaxCol() + 1 ), + m_nSourceCursorY( m_pDoc->MaxRow() + 1 ), + m_nDragSourceFlags( ScDragSrc::Undefined ), + m_bDragWasInternal( false ), + m_bUsedForLink( false ), + m_bUseInApi( false ) +{ + OSL_ENSURE(m_pDoc->IsClipboard(), "wrong document"); + + // get aBlock from clipboard doc + + SCCOL nCol1; + SCROW nRow1; + SCCOL nCol2; + SCROW nRow2; + m_pDoc->GetClipStart( nCol1, nRow1 ); + m_pDoc->GetClipArea( nCol2, nRow2, true ); // real source area - include filtered rows + nCol2 = sal::static_int_cast<SCCOL>( nCol2 + nCol1 ); + nRow2 = sal::static_int_cast<SCROW>( nRow2 + nRow1 ); + + SCCOL nDummy; + m_pDoc->GetClipArea( nDummy, m_nNonFiltered, false ); + m_bHasFiltered = (m_nNonFiltered < (nRow2 - nRow1)); + ++m_nNonFiltered; // to get count instead of diff + + SCTAB nTab1=0; + SCTAB nTab2=0; + bool bFirst = true; + for (SCTAB i=0; i< m_pDoc->GetTableCount(); i++) + if (m_pDoc->HasTable(i)) + { + if (bFirst) + nTab1 = i; + nTab2 = i; + bFirst = false; + } + OSL_ENSURE(!bFirst, "no sheet selected"); + + // only limit to used cells if whole sheet was marked + // (so empty cell areas can be copied) + if ( nCol2>=m_pDoc->MaxCol() && nRow2>=m_pDoc->MaxRow() ) + { + SCROW nMaxRow; + SCCOL nMaxCol; + GetAreaSize( *m_pDoc, nTab1, nTab2, nMaxRow, nMaxCol ); + if( nMaxRow < nRow2 ) + nRow2 = nMaxRow; + if( nMaxCol < nCol2 ) + nCol2 = nMaxCol; + } + + m_aBlock = ScRange( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); + m_nVisibleTab = nTab1; // valid table as default + + tools::Rectangle aMMRect = m_pDoc->GetMMRect( nCol1,nRow1, nCol2,nRow2, nTab1 ); + m_aObjDesc.maSize = aMMRect.GetSize(); + PrepareOLE( m_aObjDesc ); +} + +ScTransferObj::~ScTransferObj() +{ + SolarMutexGuard aSolarGuard; + + bool bIsDisposing = comphelper::LibreOfficeKit::isActive() && !ScTabViewShell::GetActiveViewShell(); + ScModule* pScMod = SC_MOD(); + if (pScMod && !bIsDisposing && pScMod->GetDragData().pCellTransfer == this) + { + OSL_FAIL("ScTransferObj wasn't released"); + pScMod->ResetDragObject(); + } + + m_pDoc.reset(); // ScTransferObj is owner of clipboard document + + m_aDocShellRef.clear(); // before releasing the mutex + + m_aDrawPersistRef.clear(); // after the model + +} + +ScTransferObj* ScTransferObj::GetOwnClipboard(const uno::Reference<datatransfer::XTransferable2>& xTransferable) +{ + return comphelper::getFromUnoTunnel<ScTransferObj>(xTransferable); +} + +void ScTransferObj::AddSupportedFormats() +{ + // same formats as in ScSelectionTransferObj::AddSupportedFormats + AddFormat( SotClipboardFormatId::EMBED_SOURCE ); + AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ); + AddFormat( SotClipboardFormatId::GDIMETAFILE ); + AddFormat( SotClipboardFormatId::PNG ); + AddFormat( SotClipboardFormatId::BITMAP ); + + // ScImportExport formats + AddFormat( SotClipboardFormatId::HTML ); + AddFormat( SotClipboardFormatId::SYLK ); + AddFormat( SotClipboardFormatId::LINK ); + AddFormat( SotClipboardFormatId::DIF ); + AddFormat( SotClipboardFormatId::STRING ); + AddFormat( SotClipboardFormatId::STRING_TSVC ); + + AddFormat( SotClipboardFormatId::RTF ); + AddFormat( SotClipboardFormatId::RICHTEXT ); + if ( m_aBlock.aStart == m_aBlock.aEnd ) + { + AddFormat( SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT ); + } +} + +static ScRange lcl_reduceBlock(const ScDocument& rDoc, ScRange aReducedBlock, bool bIncludeVisual = false) +{ + if ((aReducedBlock.aEnd.Col() == rDoc.MaxCol() || aReducedBlock.aEnd.Row() == rDoc.MaxRow()) && + aReducedBlock.aStart.Tab() == aReducedBlock.aEnd.Tab()) + { + // Shrink the block here so we don't waste time creating huge + // output when whole columns or rows are selected. + + SCCOL nPrintAreaEndCol = 0; + SCROW nPrintAreaEndRow = 0; + if (bIncludeVisual) + rDoc.GetPrintArea( aReducedBlock.aStart.Tab(), nPrintAreaEndCol, nPrintAreaEndRow, true ); + + // Shrink the area to allow pasting to external applications. + // Shrink to real data area for HTML, RTF and RICHTEXT, but include + // all objects and top-left area for BITMAP and PNG. + SCCOL nStartCol = aReducedBlock.aStart.Col(); + SCROW nStartRow = aReducedBlock.aStart.Row(); + SCCOL nEndCol = aReducedBlock.aEnd.Col(); + SCROW nEndRow = aReducedBlock.aEnd.Row(); + + if (bIncludeVisual) + { + ScDataAreaExtras aDataAreaExtras; + aDataAreaExtras.mbCellNotes = true; + aDataAreaExtras.mbCellDrawObjects = true; + bool bShrunk = false; + rDoc.ShrinkToUsedDataArea( bShrunk, aReducedBlock.aStart.Tab(), nStartCol, nStartRow, nEndCol, nEndRow, + false, true /*bStickyTopRow*/, true /*bStickyLeftCol*/, &aDataAreaExtras); + aDataAreaExtras.GetOverallRange( nStartCol, nStartRow, nEndCol, nEndRow, ScDataAreaExtras::Clip::None); + } + else + { + bool bShrunk = false; + rDoc.ShrinkToUsedDataArea( bShrunk, aReducedBlock.aStart.Tab(), nStartCol, nStartRow, nEndCol, nEndRow, + false, false /*bStickyTopRow*/, false /*bStickyLeftCol*/); + } + + if ( nPrintAreaEndRow > nEndRow ) + nEndRow = nPrintAreaEndRow; + + if ( nPrintAreaEndCol > nEndCol ) + nEndCol = nPrintAreaEndCol; + + aReducedBlock = ScRange(nStartCol, nStartRow, aReducedBlock.aStart.Tab(), nEndCol, nEndRow, aReducedBlock.aEnd.Tab()); + } + return aReducedBlock; +} + +bool ScTransferObj::GetData( const datatransfer::DataFlavor& rFlavor, const OUString& /*rDestDoc*/ ) +{ + SotClipboardFormatId nFormat = SotExchange::GetFormat( rFlavor ); + bool bOK = false; + + if( HasFormat( nFormat ) ) + { + ScRange aReducedBlock = m_aBlock; + + bool bReduceBlockFormat = + nFormat == SotClipboardFormatId::HTML + || nFormat == SotClipboardFormatId::RTF + || nFormat == SotClipboardFormatId::RICHTEXT + || nFormat == SotClipboardFormatId::BITMAP + || nFormat == SotClipboardFormatId::PNG; + + const bool bIncludeVisual = (nFormat == SotClipboardFormatId::BITMAP || + nFormat == SotClipboardFormatId::PNG); + + if (bReduceBlockFormat) + aReducedBlock = lcl_reduceBlock(*m_pDoc, m_aBlock, bIncludeVisual); + + if ( nFormat == SotClipboardFormatId::LINKSRCDESCRIPTOR || nFormat == SotClipboardFormatId::OBJECTDESCRIPTOR ) + { + bOK = SetTransferableObjectDescriptor( m_aObjDesc ); + } + else if ( ( nFormat == SotClipboardFormatId::RTF || nFormat == SotClipboardFormatId::RICHTEXT || + nFormat == SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT ) && m_aBlock.aStart == m_aBlock.aEnd ) + { + // RTF from a single cell is handled by EditEngine + + SCCOL nCol = m_aBlock.aStart.Col(); + SCROW nRow = m_aBlock.aStart.Row(); + SCTAB nTab = m_aBlock.aStart.Tab(); + ScAddress aPos(nCol, nRow, nTab); + + const ScPatternAttr* pPattern = m_pDoc->GetPattern( nCol, nRow, nTab ); + ScTabEditEngine aEngine( *pPattern, m_pDoc->GetEditPool(), m_pDoc.get() ); + ScRefCellValue aCell(*m_pDoc, aPos); + if (aCell.meType == CELLTYPE_EDIT) + { + const EditTextObject* pObj = aCell.mpEditText; + aEngine.SetTextCurrentDefaults(*pObj); + } + else + { + SvNumberFormatter* pFormatter = m_pDoc->GetFormatTable(); + sal_uInt32 nNumFmt = pPattern->GetNumberFormat(pFormatter); + const Color* pColor; + OUString aText = ScCellFormat::GetString(aCell, nNumFmt, &pColor, *pFormatter, *m_pDoc); + if (!aText.isEmpty()) + aEngine.SetTextCurrentDefaults(aText); + } + + bOK = SetObject( &aEngine, + ((nFormat == SotClipboardFormatId::RTF) ? SCTRANS_TYPE_EDIT_RTF : + ((nFormat == SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT) ? + SCTRANS_TYPE_EDIT_ODF_TEXT_FLAT : SCTRANS_TYPE_EDIT_BIN)), + rFlavor ); + } + else if ( ScImportExport::IsFormatSupported( nFormat ) || nFormat == SotClipboardFormatId::RTF + || nFormat == SotClipboardFormatId::RICHTEXT ) + { + // if this transfer object was used to create a DDE link, filtered rows + // have to be included for subsequent calls (to be consistent with link data) + if ( nFormat == SotClipboardFormatId::LINK ) + m_bUsedForLink = true; + + bool bIncludeFiltered = m_pDoc->IsCutMode() || m_bUsedForLink; + + ScImportExport aObj( *m_pDoc, aReducedBlock ); + // Plain text ("Unformatted text") may contain embedded tabs and + // line breaks but is not enclosed in quotes. Which makes it + // unsuitable for multiple cells, especially if one of them is + // multi-line, but otherwise is expected behavior for plain text. + // For multiple cells replace embedded line breaks (and tabs) with + // space character, otherwise pasting would yield odd results. + /* XXX: it's debatable whether this is actually expected, but + * there's no way to satisfy all possible requirements when + * copy/pasting unformatted text. */ + const bool bPlainMulti = (nFormat == SotClipboardFormatId::STRING && + aReducedBlock.aStart != aReducedBlock.aEnd); + // Add quotes only for STRING_TSVC. + /* TODO: a possible future STRING_TSV should not contain embedded + * line breaks nor tab (separator) characters and not be quoted. + * A possible STRING_CSV should. */ + ScExportTextOptions aTextOptions( ScExportTextOptions::None, 0, + (nFormat == SotClipboardFormatId::STRING_TSVC)); + if ( bPlainMulti || m_bUsedForLink ) + { + // For a DDE link or plain text multiple cells, convert line + // breaks and separators to space. + aTextOptions.meNewlineConversion = ScExportTextOptions::ToSpace; + aTextOptions.mcSeparatorConvertTo = ' '; + aTextOptions.mbAddQuotes = false; + } + aObj.SetExportTextOptions(aTextOptions); + aObj.SetFormulas( m_pDoc->GetViewOptions().GetOption( VOPT_FORMULAS ) ); + aObj.SetIncludeFiltered( bIncludeFiltered ); + + // DataType depends on format type: + + if ( rFlavor.DataType.equals( ::cppu::UnoType<OUString>::get() ) ) + { + OUString aString; + if ( aObj.ExportString( aString, nFormat ) ) + bOK = SetString( aString ); + } + else if ( rFlavor.DataType.equals( cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ) ) + { + // SetObject converts a stream into an Int8-Sequence + bOK = SetObject( &aObj, SCTRANS_TYPE_IMPEX, rFlavor ); + } + else + { + OSL_FAIL("unknown DataType"); + } + } + else if ( nFormat == SotClipboardFormatId::BITMAP || nFormat == SotClipboardFormatId::PNG ) + { + tools::Rectangle aMMRect = m_pDoc->GetMMRect( aReducedBlock.aStart.Col(), aReducedBlock.aStart.Row(), + aReducedBlock.aEnd.Col(), aReducedBlock.aEnd.Row(), + aReducedBlock.aStart.Tab() ); + ScopedVclPtrInstance< VirtualDevice > pVirtDev; + pVirtDev->SetOutputSizePixel(pVirtDev->LogicToPixel(aMMRect.GetSize(), MapMode(MapUnit::Map100thMM))); + + PaintToDev( pVirtDev, *m_pDoc, 1.0, aReducedBlock ); + + pVirtDev->SetMapMode( MapMode( MapUnit::MapPixel ) ); + BitmapEx aBmp = pVirtDev->GetBitmapEx( Point(), pVirtDev->GetOutputSize() ); + bOK = SetBitmapEx( aBmp, rFlavor ); + } + else if ( nFormat == SotClipboardFormatId::GDIMETAFILE ) + { + // #i123405# Do not limit visual size calculation for metafile creation. + // It seems unlikely that removing the limitation causes problems since + // metafile creation means that no real pixel device in the needed size is + // created. + InitDocShell(false); + + SfxObjectShell* pEmbObj = m_aDocShellRef.get(); + + // like SvEmbeddedTransfer::GetData: + GDIMetaFile aMtf; + ScopedVclPtrInstance< VirtualDevice > pVDev; + MapMode aMapMode( pEmbObj->GetMapUnit() ); + tools::Rectangle aVisArea( pEmbObj->GetVisArea( ASPECT_CONTENT ) ); + + pVDev->EnableOutput( false ); + pVDev->SetMapMode( aMapMode ); + aMtf.SetPrefSize( aVisArea.GetSize() ); + aMtf.SetPrefMapMode( aMapMode ); + aMtf.Record( pVDev ); + + pEmbObj->DoDraw( pVDev, Point(), aVisArea.GetSize(), JobSetup() ); + + aMtf.Stop(); + aMtf.WindStart(); + + bOK = SetGDIMetaFile( aMtf ); + } + else if ( nFormat == SotClipboardFormatId::EMBED_SOURCE ) + { + //TODO/LATER: differentiate between formats?! + // #i123405# Do limit visual size calculation to PageSize + InitDocShell(true); // set aDocShellRef + + SfxObjectShell* pEmbObj = m_aDocShellRef.get(); + bOK = SetObject( pEmbObj, SCTRANS_TYPE_EMBOBJ, rFlavor ); + } + } + return bOK; +} + +bool ScTransferObj::WriteObject( tools::SvRef<SotTempStream>& rxOStm, void* pUserObject, sal_uInt32 nUserObjectId, + const datatransfer::DataFlavor& rFlavor ) +{ + // called from SetObject, put data into stream + + bool bRet = false; + switch (nUserObjectId) + { + case SCTRANS_TYPE_IMPEX: + { + ScImportExport* pImpEx = static_cast<ScImportExport*>(pUserObject); + + SotClipboardFormatId nFormat = SotExchange::GetFormat( rFlavor ); + // mba: no BaseURL for data exchange + if ( pImpEx->ExportStream( *rxOStm, OUString(), nFormat ) ) + bRet = ( rxOStm->GetError() == ERRCODE_NONE ); + } + break; + + case SCTRANS_TYPE_EDIT_RTF: + case SCTRANS_TYPE_EDIT_BIN: + { + ScTabEditEngine* pEngine = static_cast<ScTabEditEngine*>(pUserObject); + if ( nUserObjectId == SCTRANS_TYPE_EDIT_RTF ) + { + pEngine->Write( *rxOStm, EETextFormat::Rtf ); + bRet = ( rxOStm->GetError() == ERRCODE_NONE ); + } + else + { + // can't use Write for EditEngine format because that would + // write old format without support for unicode characters. + // Get the data from the EditEngine's transferable instead. + + sal_Int32 nParCnt = pEngine->GetParagraphCount(); + if ( nParCnt == 0 ) + nParCnt = 1; + ESelection aSel( 0, 0, nParCnt-1, pEngine->GetTextLen(nParCnt-1) ); + + uno::Reference<datatransfer::XTransferable> xEditTrans = pEngine->CreateTransferable( aSel ); + TransferableDataHelper aEditHelper( xEditTrans ); + + bRet = aEditHelper.GetSotStorageStream( rFlavor, rxOStm ); + } + } + break; + + case SCTRANS_TYPE_EDIT_ODF_TEXT_FLAT: + { + ScTabEditEngine* pEngine = static_cast<ScTabEditEngine*>(pUserObject); + pEngine->Write(*rxOStm, EETextFormat::Xml); + bRet = (rxOStm->GetError() == ERRCODE_NONE); + } + break; + + case SCTRANS_TYPE_EMBOBJ: + { + // TODO/MBA: testing + SfxObjectShell* pEmbObj = static_cast<SfxObjectShell*>(pUserObject); + ::utl::TempFile aTempFile; + aTempFile.EnableKillingFile(); + uno::Reference< embed::XStorage > xWorkStore = + ::comphelper::OStorageHelper::GetStorageFromURL( aTempFile.GetURL(), embed::ElementModes::READWRITE ); + + // write document storage + pEmbObj->SetupStorage( xWorkStore, SOFFICE_FILEFORMAT_CURRENT, false ); + + // mba: no relative URLs for clipboard! + SfxMedium aMedium( xWorkStore, OUString() ); + pEmbObj->DoSaveObjectAs( aMedium, false ); + pEmbObj->DoSaveCompleted(); + + uno::Reference< embed::XTransactedObject > xTransact( xWorkStore, uno::UNO_QUERY ); + if ( xTransact.is() ) + xTransact->commit(); + + std::unique_ptr<SvStream> pSrcStm = ::utl::UcbStreamHelper::CreateStream( aTempFile.GetURL(), StreamMode::READ ); + if( pSrcStm ) + { + rxOStm->SetBufferSize( 0xff00 ); + rxOStm->WriteStream( *pSrcStm ); + pSrcStm.reset(); + } + + bRet = true; + + xWorkStore->dispose(); + xWorkStore.clear(); + } + break; + + default: + OSL_FAIL("unknown object id"); + } + return bRet; +} + +sal_Bool SAL_CALL ScTransferObj::isComplex() +{ + ScRange aReduced = lcl_reduceBlock(*m_pDoc, m_aBlock); + size_t nCells = (aReduced.aEnd.Col() - aReduced.aStart.Col() + 1) * + (aReduced.aEnd.Row() - aReduced.aStart.Row() + 1) * + (aReduced.aEnd.Tab() - aReduced.aStart.Tab() + 1); + return nCells > 1000; +} + +void ScTransferObj::DragFinished( sal_Int8 nDropAction ) +{ + if ( nDropAction == DND_ACTION_MOVE && !m_bDragWasInternal && !(m_nDragSourceFlags & ScDragSrc::Navigator) ) + { + // move: delete source data + ScDocShell* pSourceSh = GetSourceDocShell(); + if (pSourceSh) + { + ScMarkData aMarkData = GetSourceMarkData(); + // external drag&drop doesn't copy objects, so they also aren't deleted: + // bApi=TRUE, don't show error messages from drag&drop + pSourceSh->GetDocFunc().DeleteContents( aMarkData, InsertDeleteFlags::ALL & ~InsertDeleteFlags::OBJECTS, true, true ); + } + } + + ScModule* pScMod = SC_MOD(); + if ( pScMod->GetDragData().pCellTransfer == this ) + pScMod->ResetDragObject(); + + m_xDragSourceRanges = nullptr; // don't keep source after dropping + + TransferDataContainer::DragFinished( nDropAction ); +} + +void ScTransferObj::SetDragHandlePos( SCCOL nX, SCROW nY ) +{ + m_nDragHandleX = nX; + m_nDragHandleY = nY; +} + +void ScTransferObj::SetSourceCursorPos( SCCOL nX, SCROW nY ) +{ + m_nSourceCursorX = nX; + m_nSourceCursorY = nY; +} + +bool ScTransferObj::WasSourceCursorInSelection() const +{ + return + m_nSourceCursorX >= m_aBlock.aStart.Col() && m_nSourceCursorX <= m_aBlock.aEnd.Col() && + m_nSourceCursorY >= m_aBlock.aStart.Row() && m_nSourceCursorY <= m_aBlock.aEnd.Row(); +} + +void ScTransferObj::SetVisibleTab( SCTAB nNew ) +{ + m_nVisibleTab = nNew; +} + +void ScTransferObj::SetDrawPersist( const SfxObjectShellRef& rRef ) +{ + m_aDrawPersistRef = rRef; +} + +void ScTransferObj::SetDragSource( ScDocShell* pSourceShell, const ScMarkData& rMark ) +{ + ScRangeList aRanges; + rMark.FillRangeListWithMarks( &aRanges, false ); + m_xDragSourceRanges = new ScCellRangesObj( pSourceShell, aRanges ); +} + +void ScTransferObj::SetDragSourceFlags(ScDragSrc nFlags) +{ + m_nDragSourceFlags = nFlags; +} + +void ScTransferObj::SetDragWasInternal() +{ + m_bDragWasInternal = true; +} + +void ScTransferObj::SetUseInApi( bool bSet ) +{ + m_bUseInApi = bSet; +} + +ScDocument* ScTransferObj::GetSourceDocument() +{ + ScDocShell* pSourceDocSh = GetSourceDocShell(); + if (pSourceDocSh) + return &pSourceDocSh->GetDocument(); + return nullptr; +} + +ScDocShell* ScTransferObj::GetSourceDocShell() +{ + ScCellRangesBase* pRangesObj = comphelper::getFromUnoTunnel<ScCellRangesBase>( m_xDragSourceRanges ); + if (pRangesObj) + return pRangesObj->GetDocShell(); + + return nullptr; // none set +} + +ScMarkData ScTransferObj::GetSourceMarkData() const +{ + ScMarkData aMarkData(m_pDoc->GetSheetLimits()); + ScCellRangesBase* pRangesObj = comphelper::getFromUnoTunnel<ScCellRangesBase>( m_xDragSourceRanges ); + if (pRangesObj) + { + const ScRangeList& rRanges = pRangesObj->GetRangeList(); + aMarkData.MarkFromRangeList( rRanges, false ); + } + return aMarkData; +} + +// initialize aDocShellRef with a live document from the ClipDoc + +// #i123405# added parameter to allow size calculation without limitation +// to PageSize, e.g. used for Metafile creation for clipboard. + +void ScTransferObj::InitDocShell(bool bLimitToPageSize) +{ + if ( m_aDocShellRef.is() ) + return; + + ScDocShell* pDocSh = new ScDocShell; + m_aDocShellRef = pDocSh; // ref must be there before InitNew + + pDocSh->DoInitNew(); + + ScDocument& rDestDoc = pDocSh->GetDocument(); + ScMarkData aDestMark(rDestDoc.GetSheetLimits()); + aDestMark.SelectTable( 0, true ); + + rDestDoc.SetDocOptions( m_pDoc->GetDocOptions() ); // #i42666# + + OUString aTabName; + m_pDoc->GetName( m_aBlock.aStart.Tab(), aTabName ); + rDestDoc.RenameTab( 0, aTabName ); + + rDestDoc.CopyStdStylesFrom(*m_pDoc); + + SCCOL nStartX = m_aBlock.aStart.Col(); + SCROW nStartY = m_aBlock.aStart.Row(); + SCCOL nEndX = m_aBlock.aEnd.Col(); + SCROW nEndY = m_aBlock.aEnd.Row(); + + // widths / heights + // (must be copied before CopyFromClip, for drawing objects) + + SCCOL nCol; + SCTAB nSrcTab = m_aBlock.aStart.Tab(); + rDestDoc.SetLayoutRTL(0, m_pDoc->IsLayoutRTL(nSrcTab)); + for (nCol=nStartX; nCol<=nEndX; nCol++) + if ( m_pDoc->ColHidden(nCol, nSrcTab) ) + rDestDoc.ShowCol( nCol, 0, false ); + else + rDestDoc.SetColWidth( nCol, 0, m_pDoc->GetColWidth( nCol, nSrcTab ) ); + + if (nStartY > 0) + { + // Set manual height for all previous rows so we can ensure + // that visible area will not change due to autoheight + rDestDoc.SetManualHeight(0, nStartY - 1, 0, true); + } + for (SCROW nRow = nStartY; nRow <= nEndY; ++nRow) + { + if ( m_pDoc->RowHidden(nRow, nSrcTab) ) + rDestDoc.ShowRow( nRow, 0, false ); + else + { + rDestDoc.SetRowHeight( nRow, 0, m_pDoc->GetOriginalHeight( nRow, nSrcTab ) ); + + // if height was set manually, that flag has to be copied, too + bool bManual = m_pDoc->IsManualRowHeight(nRow, nSrcTab); + rDestDoc.SetManualHeight(nRow, nRow, 0, bManual); + } + } + + if (m_pDoc->GetDrawLayer() || m_pDoc->HasNotes()) + pDocSh->MakeDrawLayer(); + + // cell range is copied to the original position, but on the first sheet + // -> bCutMode must be set + // pDoc is always a Clipboard-document + + ScRange aDestRange( nStartX,nStartY,0, nEndX,nEndY,0 ); + bool bWasCut = m_pDoc->IsCutMode(); + if (!bWasCut) + m_pDoc->SetClipArea( aDestRange, true ); // Cut + rDestDoc.CopyFromClip( aDestRange, aDestMark, InsertDeleteFlags::ALL, nullptr, m_pDoc.get(), false ); + m_pDoc->SetClipArea( aDestRange, bWasCut ); + + StripRefs(*m_pDoc, nStartX,nStartY, nEndX,nEndY, rDestDoc); + + ScRange aMergeRange = aDestRange; + rDestDoc.ExtendMerge( aMergeRange, true ); + + m_pDoc->CopyDdeLinks( rDestDoc ); // copy values of DDE Links + + // page format (grid etc) and page size (maximum size for ole object) + + Size aPaperSize = SvxPaperInfo::GetPaperSize( PAPER_A4 ); // Twips + ScStyleSheetPool* pStylePool = m_pDoc->GetStyleSheetPool(); + OUString aStyleName = m_pDoc->GetPageStyle( m_aBlock.aStart.Tab() ); + SfxStyleSheetBase* pStyleSheet = pStylePool->Find( aStyleName, SfxStyleFamily::Page ); + if (pStyleSheet) + { + const SfxItemSet& rSourceSet = pStyleSheet->GetItemSet(); + aPaperSize = rSourceSet.Get(ATTR_PAGE_SIZE).GetSize(); + + // CopyStyleFrom copies SetItems with correct pool + ScStyleSheetPool* pDestPool = rDestDoc.GetStyleSheetPool(); + pDestPool->CopyStyleFrom( pStylePool, aStyleName, SfxStyleFamily::Page ); + } + + ScViewData aViewData( *pDocSh, nullptr ); + aViewData.SetScreen( nStartX,nStartY, nEndX,nEndY ); + aViewData.SetCurX( nStartX ); + aViewData.SetCurY( nStartY ); + + rDestDoc.SetViewOptions( m_pDoc->GetViewOptions() ); + + // Size + //! get while copying sizes + + tools::Long nPosX = 0; + tools::Long nPosY = 0; + + for (nCol=0; nCol<nStartX; nCol++) + nPosX += rDestDoc.GetColWidth( nCol, 0 ); + nPosY += rDestDoc.GetRowHeight( 0, nStartY-1, 0 ); + nPosX = o3tl::convert(nPosX, o3tl::Length::twip, o3tl::Length::mm100); + nPosY = o3tl::convert(nPosY, o3tl::Length::twip, o3tl::Length::mm100); + + aPaperSize.setWidth( aPaperSize.Width() * 2 ); // limit OLE object to double of page size + aPaperSize.setHeight( aPaperSize.Height() * 2 ); + + tools::Long nSizeX = 0; + tools::Long nSizeY = 0; + for (nCol=nStartX; nCol<=nEndX; nCol++) + { + tools::Long nAdd = rDestDoc.GetColWidth( nCol, 0 ); + if ( bLimitToPageSize && nSizeX+nAdd > aPaperSize.Width() && nSizeX ) // above limit? + break; + nSizeX += nAdd; + } + for (SCROW nRow=nStartY; nRow<=nEndY; nRow++) + { + tools::Long nAdd = rDestDoc.GetRowHeight( nRow, 0 ); + if ( bLimitToPageSize && nSizeY+nAdd > aPaperSize.Height() && nSizeY ) // above limit? + break; + nSizeY += nAdd; + } + nSizeX = o3tl::convert(nSizeX, o3tl::Length::twip, o3tl::Length::mm100); + nSizeY = o3tl::convert(nSizeY, o3tl::Length::twip, o3tl::Length::mm100); + +// pDocSh->SetVisAreaSize( Size(nSizeX,nSizeY) ); + + tools::Rectangle aNewArea( Point(nPosX,nPosY), Size(nSizeX,nSizeY) ); + //TODO/LATER: why twice?! + //pDocSh->SvInPlaceObject::SetVisArea( aNewArea ); + pDocSh->SetVisArea( aNewArea ); + + pDocSh->UpdateOle(aViewData, true); + + //! SetDocumentModified? + if ( rDestDoc.IsChartListenerCollectionNeedsUpdate() ) + rDestDoc.UpdateChartListenerCollection(); +} + +SfxObjectShell* ScTransferObj::SetDrawClipDoc( bool bAnyOle, const std::shared_ptr<ScDocument>& pDoc ) +{ + // update ScGlobal::xDrawClipDocShellRef + + ScGlobal::xDrawClipDocShellRef.clear(); + if (bAnyOle) + { + ScGlobal::xDrawClipDocShellRef = new ScDocShell(SfxModelFlags::EMBEDDED_OBJECT | SfxModelFlags::DISABLE_EMBEDDED_SCRIPTS, pDoc); // there must be a ref + ScGlobal::xDrawClipDocShellRef->DoInitNew(); + } + + return ScGlobal::xDrawClipDocShellRef.get(); +} + +void ScTransferObj::StripRefs( ScDocument& rDoc, + SCCOL nStartX, SCROW nStartY, SCCOL nEndX, SCROW nEndY, + ScDocument& rDestDoc ) +{ + // In a clipboard doc the data don't have to be on the first sheet + + SCTAB nSrcTab = 0; + while (nSrcTab < rDoc.GetTableCount() && !rDoc.HasTable(nSrcTab)) + ++nSrcTab; + SCTAB nDestTab = 0; + while (nDestTab < rDestDoc.GetTableCount() && !rDestDoc.HasTable(nDestTab)) + ++nDestTab; + + if (!rDoc.HasTable(nSrcTab) || !rDestDoc.HasTable(nDestTab)) + { + OSL_FAIL("Sheet not found in ScTransferObj::StripRefs"); + return; + } + + ScRange aRef; + + ScCellIterator aIter( rDoc, ScRange(nStartX, nStartY, nSrcTab, nEndX, nEndY, nSrcTab) ); + for (bool bHas = aIter.first(); bHas; bHas = aIter.next()) + { + if (aIter.getType() != CELLTYPE_FORMULA) + continue; + + ScFormulaCell* pFCell = aIter.getFormulaCell(); + bool bOut = false; + ScDetectiveRefIter aRefIter( rDoc, pFCell ); + while ( !bOut && aRefIter.GetNextRef( aRef ) ) + { + if ( aRef.aStart.Tab() != nSrcTab || aRef.aEnd.Tab() != nSrcTab || + aRef.aStart.Col() < nStartX || aRef.aEnd.Col() > nEndX || + aRef.aStart.Row() < nStartY || aRef.aEnd.Row() > nEndY ) + bOut = true; + } + if (bOut) + { + SCCOL nCol = aIter.GetPos().Col(); + SCROW nRow = aIter.GetPos().Row(); + + FormulaError nErrCode = pFCell->GetErrCode(); + ScAddress aPos(nCol, nRow, nDestTab); + if (nErrCode != FormulaError::NONE) + { + if ( rDestDoc.GetAttr( nCol,nRow,nDestTab, ATTR_HOR_JUSTIFY)->GetValue() == + SvxCellHorJustify::Standard ) + rDestDoc.ApplyAttr( nCol,nRow,nDestTab, + SvxHorJustifyItem(SvxCellHorJustify::Right, ATTR_HOR_JUSTIFY) ); + + ScSetStringParam aParam; + aParam.setTextInput(); + rDestDoc.SetString(aPos, ScGlobal::GetErrorString(nErrCode), &aParam); + } + else if (pFCell->IsValue()) + { + rDestDoc.SetValue(aPos, pFCell->GetValue()); + } + else + { + OUString aStr = pFCell->GetString().getString(); + if ( pFCell->IsMultilineResult() ) + { + ScFieldEditEngine& rEngine = rDestDoc.GetEditEngine(); + rEngine.SetTextCurrentDefaults(aStr); + rDestDoc.SetEditText(ScAddress(nCol,nRow,nDestTab), rEngine.CreateTextObject()); + } + else + { + ScSetStringParam aParam; + aParam.setTextInput(); + rDestDoc.SetString(aPos, aStr, &aParam); + } + } + } + } +} + +const css::uno::Sequence< sal_Int8 >& ScTransferObj::getUnoTunnelId() +{ + static const comphelper::UnoIdInit theScTransferUnoTunnelId; + return theScTransferUnoTunnelId.getSeq(); +} + +sal_Int64 SAL_CALL ScTransferObj::getSomething( const css::uno::Sequence< sal_Int8 >& rId ) +{ + return comphelper::getSomethingImpl( + rId, this, comphelper::FallbackToGetSomethingOf<TransferDataContainer>{}); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/app/typemap.cxx b/sc/source/ui/app/typemap.cxx new file mode 100644 index 000000000..ac98c6fc4 --- /dev/null +++ b/sc/source/ui/app/typemap.cxx @@ -0,0 +1,140 @@ +/* -*- 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 <mid.h> +#include <editeng/memberids.h> +#include <svx/unomid.hxx> + +#include <sfx2/msg.hxx> +#include <svl/slstitm.hxx> +#include <editeng/fontitem.hxx> +#include <svx/hlnkitem.hxx> +#include <svl/srchitem.hxx> +#include <svx/postattr.hxx> +#include <editeng/postitem.hxx> +#include <sfx2/tplpitem.hxx> +#include <sfx2/zoomitem.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/wghtitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/colritem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/lineitem.hxx> +#include <svl/ptitem.hxx> +#include <editeng/sizeitem.hxx> +#include <svx/algitem.hxx> +#include <svx/clipfmtitem.hxx> +#include <editeng/udlnitem.hxx> +#include <svx/xlnstit.hxx> +#include <svx/xlnedit.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xflclit.hxx> +#include <svx/xflhtit.hxx> +#include <svx/xbtmpit.hxx> +#include <svx/xlineit0.hxx> +#include <svx/xlndsit.hxx> +#include <svx/xlnwtit.hxx> +#include <svx/xlnclit.hxx> +#include <svx/xtextit0.hxx> +#include <svx/xftadit.hxx> +#include <svx/xftdiit.hxx> +#include <svx/xftstit.hxx> +#include <svx/xftmrit.hxx> +#include <svx/xftouit.hxx> +#include <svx/xftshit.hxx> +#include <svx/xftshcit.hxx> +#include <svx/xftshxy.hxx> +#include <editeng/langitem.hxx> +#include <editeng/justifyitem.hxx> +#include <editeng/shdditem.hxx> +#include <editeng/shaditem.hxx> +#include <editeng/contouritem.hxx> +#include <editeng/crossedoutitem.hxx> +#include <editeng/charreliefitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/lspcitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/kernitem.hxx> +#include <svx/rotmodit.hxx> +#include <svx/drawitem.hxx> +#include <svl/ilstitem.hxx> +#include <svl/globalnameitem.hxx> +#include <svx/chrtitem.hxx> +#include <svx/zoomslideritem.hxx> +#include <svx/xflftrit.hxx> +#include <svx/xlncapit.hxx> +#include <svx/xlinjoit.hxx> +#include <svx/galleryitem.hxx> +#include <svx/sdooitm.hxx> +#include <avmedia/mediaitem.hxx> +#include <sfx2/frame.hxx> +#include <attrib.hxx> +#include <svx/sdprcitm.hxx> +#include <svx/sdmetitm.hxx> + +#define avmedia_MediaItem ::avmedia::MediaItem + +#ifdef DISABLE_DYNLOADING +/* Avoid clash with the ones from svx/source/form/typemap.cxx */ +#define aSfxBoolItem_Impl sc_source_ui_appl_typemap_aSfxBoolItem_Impl +#define aSfxInt32Item_Impl sc_source_ui_appl_typemap_aSfxInt32Item_Impl +#define aSfxStringItem_Impl sc_source_ui_appl_typemap_aSfxStringItem_Impl +#define aSfxUInt16Item_Impl sc_source_ui_appl_typemap_aSfxUInt16Item_Impl +#define aSfxUInt32Item_Impl sc_source_ui_appl_typemap_aSfxUInt32Item_Impl +#define aSfxVoidItem_Impl sc_source_ui_appl_typemap_aSfxVoidItem_Impl +#define aSvxCharReliefItem_Impl sc_source_ui_appl_typemap_aSvxCharReliefItem_Impl +#define aSvxClipboardFormatItem_Impl sc_source_ui_appl_typemap_aSvxClipboardFormatItem_Impl +#define aSvxColorItem_Impl sc_source_ui_appl_typemap_aSvxColorItem_Impl +#define aSvxContourItem_Impl sc_source_ui_appl_typemap_aSvxContourItem_Impl +#define aSvxCrossedOutItem_Impl sc_source_ui_appl_typemap_aSvxCrossedOutItem_Impl +#define aSvxFontHeightItem_Impl sc_source_ui_appl_typemap_aSvxFontHeightItem_Impl +#define aSvxFontItem_Impl sc_source_ui_appl_typemap_aSvxFontItem_Impl +#define aSvxLanguageItem_Impl sc_source_ui_appl_typemap_aSvxLanguageItem_Impl +#define aSvxPostureItem_Impl sc_source_ui_appl_typemap_aSvxPostureItem_Impl +#define aSvxShadowedItem_Impl sc_source_ui_appl_typemap_aSvxShadowedItem_Impl +#define aSvxUnderlineItem_Impl sc_source_ui_appl_typemap_aSvxUnderlineItem_Impl +#define aSvxOverlineItem_Impl sc_source_ui_appl_typemap_aSvxOverlineItem_Impl +#define aSvxWeightItem_Impl sc_source_ui_appl_typemap_aSvxWeightItem_Impl +#endif + +#define SFX_TYPEMAP +#include <scslots.hxx> + +#ifdef DISABLE_DYNLOADING +#undef aSfxBoolItem_Impl +#undef aSfxInt32Item_Impl +#undef aSfxStringItem_Impl +#undef aSfxUInt16Item_Impl +#undef aSfxUInt32Item_Impl +#undef aSfxVoidItem_Impl +#undef aSvxCharReliefItem_Impl +#undef aSvxClipboardFormatItem_Impl +#undef aSvxColorItem_Impl +#undef aSvxContourItem_Impl +#undef aSvxCrossedOutItem_Impl +#undef aSvxFontHeightItem_Impl +#undef aSvxFontItem_Impl +#undef aSvxLanguageItem_Impl +#undef aSvxPostureItem_Impl +#undef aSvxShadowedItem_Impl +#undef aSvxTextLineItem_Impl +#undef aSvxWeightItem_Impl +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/app/uiitems.cxx b/sc/source/ui/app/uiitems.cxx new file mode 100644 index 000000000..91cb50b07 --- /dev/null +++ b/sc/source/ui/app/uiitems.cxx @@ -0,0 +1,438 @@ +/* -*- 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 <uiitems.hxx> + +#include <userlist.hxx> +#include <dpsave.hxx> +#include <queryparam.hxx> + +#include <osl/diagnose.h> +#include <editeng/editobj.hxx> + +/** + * Status update for entry field + */ +ScInputStatusItem::ScInputStatusItem( + sal_uInt16 nWhichP, const ScAddress& rCurPos, const ScAddress& rStartPos, + const ScAddress& rEndPos, const OUString& rString, const EditTextObject* pData ) : + SfxPoolItem ( nWhichP ), + aCursorPos ( rCurPos ), + aStartPos ( rStartPos ), + aEndPos ( rEndPos ), + aString ( rString ), + pEditData ( pData ? pData->Clone() : nullptr ), + mpMisspellRanges(nullptr) +{ +} + +ScInputStatusItem::ScInputStatusItem( const ScInputStatusItem& rItem ) : + SfxPoolItem ( rItem ), + aCursorPos ( rItem.aCursorPos ), + aStartPos ( rItem.aStartPos ), + aEndPos ( rItem.aEndPos ), + aString ( rItem.aString ), + pEditData ( rItem.pEditData ? rItem.pEditData->Clone() : nullptr ), + mpMisspellRanges(rItem.mpMisspellRanges) +{ +} + +ScInputStatusItem::~ScInputStatusItem() +{ +} + +bool ScInputStatusItem::operator==( const SfxPoolItem& rItem ) const +{ + assert(SfxPoolItem::operator==(rItem)); + + return (aStartPos == static_cast<const ScInputStatusItem&>(rItem).aStartPos) + && (aEndPos == static_cast<const ScInputStatusItem&>(rItem).aEndPos) + && (aCursorPos == static_cast<const ScInputStatusItem&>(rItem).aCursorPos) + && (aString == static_cast<const ScInputStatusItem&>(rItem).aString); + //TODO: Compare Edit data! +} + +ScInputStatusItem* ScInputStatusItem::Clone( SfxItemPool * ) const +{ + return new ScInputStatusItem( *this ); +} + +void ScInputStatusItem::SetMisspellRanges( const std::vector<editeng::MisspellRanges>* pRanges ) +{ + mpMisspellRanges = pRanges; +} + +// ScPaintHint was moved to hints.cxx + +/** + * Adapt Views when inserting/deleting a table + */ +ScTablesHint::ScTablesHint(sal_uInt16 nNewId, SCTAB nTable1, SCTAB nTable2) : + nId( nNewId ), + nTab1( nTable1 ), + nTab2( nTable2 ) +{ +} + +ScTablesHint::~ScTablesHint() +{ +} + +ScIndexHint::ScIndexHint(SfxHintId nNewId, sal_uInt16 nIdx) : + SfxHint( nNewId ), + nIndex( nIdx ) +{ +} + +ScIndexHint::~ScIndexHint() +{ +} + +/** + * Create new EditView for Cursorposition + */ +ScEditViewHint::ScEditViewHint( ScEditEngineDefaulter* pEngine, const ScAddress& rCurPos ) : + pEditEngine( pEngine ), + aCursorPos( rCurPos ) +{ +} + +ScEditViewHint::~ScEditViewHint() +{ +} + +/** + * Data for the sorting dialog + */ +ScSortItem::ScSortItem( sal_uInt16 nWhichP, + ScViewData* ptrViewData, + const ScSortParam* pSortData ) : + SfxPoolItem ( nWhichP ), + pViewData ( ptrViewData ) +{ + if ( pSortData ) theSortData = *pSortData; +} + +ScSortItem::ScSortItem( sal_uInt16 nWhichP, + const ScSortParam* pSortData ) : + SfxPoolItem ( nWhichP ), + pViewData ( nullptr ) +{ + if ( pSortData ) theSortData = *pSortData; +} + +bool ScSortItem::operator==( const SfxPoolItem& rItem ) const +{ + assert(SfxPoolItem::operator==(rItem)); + + const ScSortItem& rOther = static_cast<const ScSortItem&>(rItem); + + return ( (pViewData == rOther.pViewData) + && (theSortData == rOther.theSortData) ); +} + +ScSortItem* ScSortItem::Clone( SfxItemPool * ) const +{ + return new ScSortItem( *this ); +} + +bool ScSortItem::QueryValue( css::uno::Any& rVal, sal_uInt8 /* nMemberUd */ ) const +{ + // Return empty value as there is no useful conversion + rVal = css::uno::Any(); + return true; +} + +/** + * Data for the Filter dialog + */ +ScQueryItem::ScQueryItem( sal_uInt16 nWhichP, + ScViewData* ptrViewData, + const ScQueryParam* pQueryData ) : + SfxPoolItem ( nWhichP ), + pViewData ( ptrViewData ), + bIsAdvanced ( false ) +{ + if (pQueryData) + mpQueryData.reset(new ScQueryParam(*pQueryData)); + else + mpQueryData.reset(new ScQueryParam); +} + +ScQueryItem::ScQueryItem( sal_uInt16 nWhichP, + const ScQueryParam* pQueryData ) : + SfxPoolItem ( nWhichP ), + pViewData ( nullptr ), + bIsAdvanced ( false ) +{ + if (pQueryData) + mpQueryData.reset(new ScQueryParam(*pQueryData)); + else + mpQueryData.reset(new ScQueryParam); +} + +ScQueryItem::ScQueryItem( const ScQueryItem& rItem ) : + SfxPoolItem ( rItem ), + mpQueryData(new ScQueryParam(*rItem.mpQueryData)), + pViewData ( rItem.pViewData ), + aAdvSource ( rItem.aAdvSource ), + bIsAdvanced ( rItem.bIsAdvanced ) +{ +} + +ScQueryItem::~ScQueryItem() +{ +} + +void ScQueryItem::SetAdvancedQuerySource(const ScRange* pSource) +{ + if (pSource) + { + aAdvSource = *pSource; + bIsAdvanced = true; + } + else + bIsAdvanced = false; +} + +const ScQueryParam& ScQueryItem::GetQueryData() const +{ + return *mpQueryData; +} + +bool ScQueryItem::GetAdvancedQuerySource(ScRange& rSource) const +{ + rSource = aAdvSource; + return bIsAdvanced; +} + +bool ScQueryItem::operator==( const SfxPoolItem& rItem ) const +{ + assert(SfxPoolItem::operator==(rItem)); + + const ScQueryItem& rQueryItem = static_cast<const ScQueryItem&>(rItem); + + return ( (pViewData == rQueryItem.pViewData) + && (bIsAdvanced == rQueryItem.bIsAdvanced) + && (aAdvSource == rQueryItem.aAdvSource) + && (*mpQueryData == *rQueryItem.mpQueryData) ); +} + +ScQueryItem* ScQueryItem::Clone( SfxItemPool * ) const +{ + return new ScQueryItem( *this ); +} + +/** + * Data for the SubTotal dialog + */ +ScSubTotalItem::ScSubTotalItem( sal_uInt16 nWhichP, + ScViewData* ptrViewData, + const ScSubTotalParam* pSubTotalData ) : + SfxPoolItem ( nWhichP ), + pViewData ( ptrViewData ) +{ + if ( pSubTotalData ) theSubTotalData = *pSubTotalData; +} + +bool ScSubTotalItem::operator==( const SfxPoolItem& rItem ) const +{ + assert(SfxPoolItem::operator==(rItem)); + + const ScSubTotalItem& rSTItem = static_cast<const ScSubTotalItem&>(rItem); + + return ( (pViewData == rSTItem.pViewData) + && (theSubTotalData == rSTItem.theSubTotalData) ); +} + +ScSubTotalItem* ScSubTotalItem::Clone( SfxItemPool * ) const +{ + return new ScSubTotalItem( *this ); +} + +bool ScSubTotalItem::QueryValue( css::uno::Any& rVal, sal_uInt8 /* nMemberUd */ ) const +{ + // Return empty value as there is no useful conversion + rVal = css::uno::Any(); + return true; +} + +/** + * Transporter for the UserLIst dialog + */ +ScUserListItem::ScUserListItem( sal_uInt16 nWhichP ) + : SfxPoolItem ( nWhichP ) +{ +} + +ScUserListItem::ScUserListItem( const ScUserListItem& rItem ) + : SfxPoolItem ( rItem ) +{ + if ( rItem.pUserList ) + pUserList.reset( new ScUserList( *(rItem.pUserList) ) ); +} + +ScUserListItem::~ScUserListItem() +{ +} + +bool ScUserListItem::operator==( const SfxPoolItem& rItem ) const +{ + assert(SfxPoolItem::operator==(rItem)); + + const ScUserListItem& r = static_cast<const ScUserListItem&>(rItem); + bool bEqual = false; + + if ( !pUserList || !r.pUserList ) + bEqual = ( !pUserList && !r.pUserList ); + else + bEqual = ( *pUserList == *(r.pUserList) ); + + return bEqual; +} + +ScUserListItem* ScUserListItem::Clone( SfxItemPool * ) const +{ + return new ScUserListItem( *this ); +} + +void ScUserListItem::SetUserList( const ScUserList& rUserList ) +{ + pUserList.reset( new ScUserList( rUserList ) ); +} + +/** + * Data for the Consolidate dialog + */ +ScConsolidateItem::ScConsolidateItem( + sal_uInt16 nWhichP, + const ScConsolidateParam* pConsolidateData ) : + SfxPoolItem ( nWhichP ) +{ + if ( pConsolidateData ) theConsData = *pConsolidateData; +} + +bool ScConsolidateItem::operator==( const SfxPoolItem& rItem ) const +{ + assert(SfxPoolItem::operator==(rItem)); + + const ScConsolidateItem& rCItem = static_cast<const ScConsolidateItem&>(rItem); + + return ( theConsData == rCItem.theConsData); +} + +ScConsolidateItem* ScConsolidateItem::Clone( SfxItemPool * ) const +{ + return new ScConsolidateItem( *this ); +} + +/** + * Data for the Pivot dialog + */ +ScPivotItem::ScPivotItem( sal_uInt16 nWhichP, const ScDPSaveData* pData, + const ScRange* pRange, bool bNew ) : + SfxPoolItem ( nWhichP ) +{ + // pSaveData must always exist + if ( pData ) + pSaveData.reset( new ScDPSaveData(*pData) ); + else + pSaveData.reset( new ScDPSaveData ); + if ( pRange ) aDestRange = *pRange; + bNewSheet = bNew; +} + +ScPivotItem::ScPivotItem( const ScPivotItem& rItem ) : + SfxPoolItem ( rItem ), + aDestRange ( rItem.aDestRange ), + bNewSheet ( rItem.bNewSheet ) +{ + assert(rItem.pSaveData && "pSaveData"); + pSaveData.reset( new ScDPSaveData(*rItem.pSaveData) ); +} + +ScPivotItem::~ScPivotItem() +{ +} + +bool ScPivotItem::operator==( const SfxPoolItem& rItem ) const +{ + assert(SfxPoolItem::operator==(rItem)); + + const ScPivotItem& rPItem = static_cast<const ScPivotItem&>(rItem); + OSL_ENSURE( pSaveData && rPItem.pSaveData, "pSaveData" ); + return ( *pSaveData == *rPItem.pSaveData && + aDestRange == rPItem.aDestRange && + bNewSheet == rPItem.bNewSheet ); +} + +ScPivotItem* ScPivotItem::Clone( SfxItemPool * ) const +{ + return new ScPivotItem( *this ); +} + +/** + * Data for the Solver dialog + */ +ScSolveItem::ScSolveItem( sal_uInt16 nWhichP, + const ScSolveParam* pSolveData ) + : SfxPoolItem ( nWhichP ) +{ + if ( pSolveData ) theSolveData = *pSolveData; +} + +bool ScSolveItem::operator==( const SfxPoolItem& rItem ) const +{ + assert(SfxPoolItem::operator==(rItem)); + + const ScSolveItem& rPItem = static_cast<const ScSolveItem&>(rItem); + + return ( theSolveData == rPItem.theSolveData ); +} + +ScSolveItem* ScSolveItem::Clone( SfxItemPool * ) const +{ + return new ScSolveItem( *this ); +} + +/** + * Data for the TabOp dialog + */ +ScTabOpItem::ScTabOpItem( sal_uInt16 nWhichP, + const ScTabOpParam* pTabOpData ) + : SfxPoolItem ( nWhichP ) +{ + if ( pTabOpData ) theTabOpData = *pTabOpData; +} + +bool ScTabOpItem::operator==( const SfxPoolItem& rItem ) const +{ + assert(SfxPoolItem::operator==(rItem)); + + const ScTabOpItem& rPItem = static_cast<const ScTabOpItem&>(rItem); + + return ( theTabOpData == rPItem.theTabOpData ); +} + +ScTabOpItem* ScTabOpItem::Clone( SfxItemPool * ) const +{ + return new ScTabOpItem( *this ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |