/* -*- 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 .
*/
struct PropertyInfo
{ bool bIsTransientOrReadOnly : 1; // the property is transient or read-only, thus we need no undo action for it bool bIsValueProperty : 1; // the property is the special value property, thus it may be handled // as if it's transient or persistent
};
AllProperties aProps; // all properties of this set which we know so far bool bHasEmptyControlSource; // sal_True -> the set has a DataField property, and the current value is an empty string // sal_False -> the set has _no_ such property or its value isn't empty
};
// is the control still assigned to a form
Reference< XInterface > xModel(pObj->GetUnoControlModel(), UNO_QUERY);
Reference< XFormComponent > xContent(xModel, UNO_QUERY); if (!(xContent.is() && pObj->getSdrPageFromSdrObject())) return;
// if the component doesn't belong to a form, yet, find one to insert into if (!xContent->getParent().is())
{ try
{ const Reference< XIndexContainer >& xObjectParent = pObj->GetOriginalParent();
Reference< XIndexContainer > xNewParent;
Reference< XForm > xForm;
sal_Int32 nPos = -1; if ( lcl_searchElement( xForms, xObjectParent ) )
{ // the form which was the parent of the object when it was removed is still // part of the form component hierarchy of the current page
xNewParent = xObjectParent;
xForm.set( xNewParent, UNO_QUERY_THROW );
nPos = ::std::min( pObj->GetOriginalIndex(), xNewParent->getCount() );
} else
{
xForm.set( rPage.GetImpl().findPlaceInFormComponentHierarchy( xContent ), UNO_SET_THROW );
xNewParent.set( xForm, UNO_QUERY_THROW );
nPos = xNewParent->getCount();
}
void FmXUndoEnvironment::Removed(SdrObject* pObj)
{ if ( pObj->IsVirtualObj() ) // for virtual objects, we've already been notified of the removal of the master // object, which is sufficient here return;
// is the control still assigned to a form
Reference< XFormComponent > xContent(pObj->GetUnoControlModel(), UNO_QUERY); if (!xContent.is()) return;
// The object is taken out of a list. // If a father exists, the object is removed at the father and // noted at the FormObject!
// If the object is reinserted and a parent exists, this parent is set though.
Reference< XIndexContainer > xForm(xContent->getParent(), UNO_QUERY); if (!xForm.is()) return;
// determine which position the child was at const sal_Int32 nPos = getElementPos(xForm, xContent); if (nPos < 0) return;
void SAL_CALL FmXUndoEnvironment::disposing(const EventObject& e)
{ // check if it's an object we have cached information about if (m_pPropertySetCache)
{
Reference< XPropertySet > xSourceSet(e.Source, UNO_QUERY); if (xSourceSet.is())
{
PropertySetInfoCache::iterator aSetPos = m_pPropertySetCache->find(xSourceSet); if (aSetPos != m_pPropertySetCache->end())
m_pPropertySetCache->erase(aSetPos);
}
}
}
if (!IsLocked())
{
Reference< XPropertySet > xSet(evt.Source, UNO_QUERY); if (!xSet.is()) return;
// if it's a "default value" property of a control model, set the according "value" property static constexpr OUString pDefaultValueProperties[] = {
FM_PROP_DEFAULT_TEXT, FM_PROP_DEFAULTCHECKED, FM_PROP_DEFAULT_DATE, FM_PROP_DEFAULT_TIME,
FM_PROP_DEFAULT_VALUE, FM_PROP_DEFAULT_SELECT_SEQ, FM_PROP_EFFECTIVE_DEFAULT
}; static constexpr OUString aValueProperties[] = {
FM_PROP_TEXT, FM_PROP_STATE, FM_PROP_DATE, FM_PROP_TIME,
FM_PROP_VALUE, FM_PROP_SELECT_SEQ, FM_PROP_EFFECTIVE_VALUE
};
sal_Int32 nDefaultValueProps = SAL_N_ELEMENTS(pDefaultValueProperties);
OSL_ENSURE(SAL_N_ELEMENTS(aValueProperties) == nDefaultValueProps, "FmXUndoEnvironment::propertyChange: inconsistence!"); for (sal_Int32 i=0; i<nDefaultValueProps; ++i)
{ if (evt.PropertyName == pDefaultValueProperties[i])
{ try
{
xSet->setPropertyValue(aValueProperties[i], evt.NewValue);
} catch(const Exception&)
{
OSL_FAIL("FmXUndoEnvironment::propertyChange: could not adjust the value property!");
}
}
}
// no Undo for transient and readonly props. But unfortunately "transient" is not only that the // "transient" flag is set for the property in question, instead it is somewhat more complex // Transience criterions are: // - the "transient" flag is set for the property // - OR the control has a non-empty COntrolSource property, i.e. is intended to be bound // to a database column. Note that it doesn't matter here whether the control actually // *is* bound to a column // - OR the control is bound to an external value via XBindableValue/XValueBinding // which does not have a "ExternalData" property being <TRUE/>
if (!m_pPropertySetCache)
m_pPropertySetCache = std::make_unique<PropertySetInfoCache>();
// let's see if we know something about the set
PropertySetInfoCache::iterator aSetPos = m_pPropertySetCache->find(xSet); if (aSetPos == m_pPropertySetCache->end())
{
PropertySetInfo aNewEntry; if (!::comphelper::hasProperty(FM_PROP_CONTROLSOURCE, xSet))
{
aNewEntry.bHasEmptyControlSource = false;
} else
{ try
{
Any aCurrentControlSource = xSet->getPropertyValue(FM_PROP_CONTROLSOURCE);
aNewEntry.bHasEmptyControlSource = !aCurrentControlSource.hasValue() || ::comphelper::getString(aCurrentControlSource).isEmpty();
} catch(const Exception&)
{
DBG_UNHANDLED_EXCEPTION("svx");
}
}
aSetPos = m_pPropertySetCache->emplace(xSet,aNewEntry).first;
DBG_ASSERT(aSetPos != m_pPropertySetCache->end(), "FmXUndoEnvironment::propertyChange : just inserted it ... why it's not there ?");
} else
{ // is it the DataField property ? if (evt.PropertyName == FM_PROP_CONTROLSOURCE)
{
aSetPos->second.bHasEmptyControlSource = !evt.NewValue.hasValue() || ::comphelper::getString(evt.NewValue).isEmpty();
}
}
// now we have access to the cached info about the set // let's see what we know about the property
PropertySetInfo::AllProperties& rPropInfos = aSetPos->second.aProps;
PropertySetInfo::AllProperties::iterator aPropertyPos = rPropInfos.find(evt.PropertyName); if (aPropertyPos == rPropInfos.end())
{ // nothing 'til now ... have to change this...
PropertyInfo aNewEntry;
// check if it is the special "DataFieldProperty"
aNewEntry.bIsValueProperty = false; try
{ if (::comphelper::hasProperty(FM_PROP_CONTROLSOURCEPROPERTY, xSet))
{
Any aControlSourceProperty = xSet->getPropertyValue(FM_PROP_CONTROLSOURCEPROPERTY);
OUString sControlSourceProperty;
aControlSourceProperty >>= sControlSourceProperty;
// insert the new entry
aPropertyPos = rPropInfos.emplace(evt.PropertyName,aNewEntry).first;
DBG_ASSERT(aPropertyPos != rPropInfos.end(), "FmXUndoEnvironment::propertyChange : just inserted it ... why it's not there ?");
}
// now we have access to the cached info about the property affected // and are able to decide whether or not we need an undo action
bool bAddUndoAction = rModel.IsUndoEnabled(); // no UNDO for transient/readonly properties if ( bAddUndoAction && aPropertyPos->second.bIsTransientOrReadOnly )
bAddUndoAction = false;
if ( bAddUndoAction && aPropertyPos->second.bIsValueProperty )
{ // no UNDO when the "value" property changes, but the ControlSource is non-empty // (in this case the control is intended to be bound to a database column) if ( !aSetPos->second.bHasEmptyControlSource )
bAddUndoAction = false;
// no UNDO if the control is currently bound to an external value if ( bAddUndoAction )
{
Reference< XBindableValue > xBindable( evt.Source, UNO_QUERY );
Reference< XValueBinding > xBinding; if ( xBindable.is() )
xBinding = xBindable->getValueBinding();
Reference< XPropertySet > xBindingProps;
Reference< XPropertySetInfo > xBindingPropsPSI; if ( xBindable.is() )
xBindingProps.set( xBinding, UNO_QUERY ); if ( xBindingProps.is() )
xBindingPropsPSI = xBindingProps->getPropertySetInfo(); // TODO: we should cache all those things, else this might be too expensive. // However, this requires we're notified of changes in the value binding
if ( bAddUndoAction )
{
aGuard.clear(); // TODO: this is a potential race condition: two threads here could in theory // add their undo actions out-of-order
SolarMutexGuard aSolarGuard;
rModel.AddUndo(std::make_unique<FmUndoPropertyAction>(rModel, evt));
}
} else
{ // if it's the DataField property we may have to adjust our cache if (m_pPropertySetCache && evt.PropertyName == FM_PROP_CONTROLSOURCE)
{
Reference< XPropertySet > xSet(evt.Source, UNO_QUERY);
PropertySetInfo& rSetInfo = (*m_pPropertySetCache)[xSet];
rSetInfo.bHasEmptyControlSource = !evt.NewValue.hasValue() || ::comphelper::getString(evt.NewValue).isEmpty();
}
}
}
try
{ // if it's an EventAttacherManager, then we need to listen for // script events
Reference< XEventAttacherManager > xManager( _rxContainer, UNO_QUERY ); if ( xManager.is() )
{ if ( _bStartListening )
{
m_pScriptingEnv->registerEventAttacherManager( xManager ); if ( m_vbaListener.is() )
xManager->addScriptListener( m_vbaListener );
} else
{
m_pScriptingEnv->revokeEventAttacherManager( xManager ); if ( m_vbaListener.is() )
xManager->removeScriptListener( m_vbaListener );
}
}
// also handle all children of this element
sal_uInt32 nCount = _rxContainer->getCount();
Reference< XInterface > xInterface; for ( sal_uInt32 i = 0; i < nCount; ++i )
{
_rxContainer->getByIndex( i ) >>= xInterface; if ( _bStartListening )
AddElement( xInterface ); else
RemoveElement( xInterface );
}
// be notified of any changes in the container elements
Reference< XContainer > xSimpleContainer( _rxContainer, UNO_QUERY );
OSL_ENSURE( xSimpleContainer.is(), "FmXUndoEnvironment::switchListening: how are we expected to be notified of changes in the container?" ); if ( xSimpleContainer.is() )
{ if ( _bStartListening )
xSimpleContainer->addContainerListener( this ); else
xSimpleContainer->removeContainerListener( this );
}
} catch( const Exception& )
{
TOOLS_WARN_EXCEPTION( "svx", "FmXUndoEnvironment::switchListening" );
}
}
void FmXUndoEnvironment::switchListening( const Reference< XInterface >& _rxObject, bool _bStartListening )
{
OSL_PRECOND( _rxObject.is(), "FmXUndoEnvironment::switchListening: how should I listen at a NULL object?" );
try
{ if ( !bReadOnly )
{
Reference< XPropertySet > xProps( _rxObject, UNO_QUERY ); if ( xProps.is() )
{ if ( _bStartListening )
xProps->addPropertyChangeListener( OUString(), this ); else
xProps->removePropertyChangeListener( OUString(), this );
}
}
Reference< XModifyBroadcaster > xBroadcaster( _rxObject, UNO_QUERY ); if ( xBroadcaster.is() )
{ if ( _bStartListening )
xBroadcaster->addModifyListener( this ); else
xBroadcaster->removeModifyListener( this );
}
} catch( const Exception& )
{
TOOLS_WARN_EXCEPTION( "svx", "FmXUndoEnvironment::switchListening" );
}
}
void FmXUndoEnvironment::AddElement(const Reference< XInterface >& _rxElement )
{
OSL_ENSURE( !m_bDisposed, "FmXUndoEnvironment::AddElement: not when I'm already disposed!" );
// listen at the container
Reference< XIndexContainer > xContainer( _rxElement, UNO_QUERY ); if ( xContainer.is() )
switchListening( xContainer, true );
if (!bReadOnly)
{ // reset the ActiveConnection if the form is to be removed. This will (should) free the resources // associated with this connection // 86299 - 05/02/2001 - frank.schoenheit@germany.sun.com
Reference< XForm > xForm( _rxElement, UNO_QUERY );
Reference< XPropertySet > xFormProperties( xForm, UNO_QUERY ); if ( xFormProperties.is() )
{
Reference< XConnection > xDummy; if ( !isEmbeddedInDatabase( _rxElement, xDummy ) ) // (if there is a connection in the context of the component, setting // a new connection would be vetoed, anyway) // #i34196#
xFormProperties->setPropertyValue( FM_PROP_ACTIVE_CONNECTION, Any() );
}
}
FmUndoContainerAction::FmUndoContainerAction(FmFormModel& _rMod,
Action _eAction, const Reference< XIndexContainer > & xCont, const Reference< XInterface > & xElem,
sal_Int32 nIdx)
:SdrUndoAction( _rMod )
,m_xContainer( xCont )
,m_nIndex( nIdx )
,m_eAction( _eAction )
{
OSL_ENSURE( nIdx >= 0, "FmUndoContainerAction::FmUndoContainerAction: invalid index!"); // some old code suggested this could be a valid argument. However, this code was // buggy, and it *seemed* that nobody used it - so it was removed.
if ( xElement != m_xElement )
{ // the indexes in the container changed. Okay, so go the long way and // manually determine the index
m_nIndex = getElementPos( m_xContainer, m_xElement ); if ( m_nIndex != -1 )
xElement = m_xElement;
}
OSL_ENSURE( xElement == m_xElement, "FmUndoContainerAction::implReRemove: cannot find the element which I'm responsible for!" ); if ( xElement == m_xElement )
{
Reference< XEventAttacherManager > xManager( m_xContainer, UNO_QUERY ); if ( xManager.is() )
m_aEvents = xManager->getScriptEvents( m_nIndex );
m_xContainer->removeByIndex( m_nIndex ); // from now on, we own this object
m_xOwnElement = m_xElement;
}
}
// replace the model within the parent
Reference< XChild > xCurrentAsChild( xCurrentModel, UNO_QUERY );
Reference< XNameContainer > xCurrentsParent; if ( xCurrentAsChild.is() )
xCurrentsParent.set(xCurrentAsChild->getParent(), css::uno::UNO_QUERY);
DBG_ASSERT( xCurrentsParent.is(), "FmUndoModelReplaceAction::Undo: invalid current model!" );
if ( xCurrentsParent.is() )
{ // the form container works with FormComponents
Reference< XFormComponent > xComponent( m_xReplaced, UNO_QUERY );
DBG_ASSERT( xComponent.is(), "FmUndoModelReplaceAction::Undo: the new model is no form component !" );
Reference< XPropertySet > xCurrentAsSet( xCurrentModel, UNO_QUERY );
DBG_ASSERT( ::comphelper::hasProperty(FM_PROP_NAME, xCurrentAsSet ), "FmUndoModelReplaceAction::Undo : one of the models is invalid !");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.