/* -*- 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 .
*/
// submitting and resetting html-forms asynchronously class OFormSubmitResetThread: public OComponentEventThread
{ protected:
// process an event. while processing the mutex isn't locked, and pCompImpl // is made sure to remain valid virtualvoid processEvent( ::cppu::OComponentHelper* _pCompImpl, const EventObject* _pEvt, const Reference<XControl>& _rControl, bool _bSubmit) override;
Any SAL_CALL ODatabaseForm::queryAggregation(const Type& _rType)
{
Any aReturn = ODatabaseForm_BASE1::queryInterface(_rType); // our own interfaces if (!aReturn.hasValue())
{
aReturn = ODatabaseForm_BASE2::queryInterface(_rType); // property set related interfaces if (!aReturn.hasValue())
{
aReturn = OPropertySetAggregationHelper::queryInterface(_rType);
// form component collection related interfaces if (!aReturn.hasValue())
{
aReturn = OFormComponents::queryAggregation(_rType);
// interfaces already present in the aggregate which we want to reroute // only available if we could create the aggregate if (!aReturn.hasValue() && m_xAggregateAsRowSet.is())
aReturn = ODatabaseForm_BASE3::queryInterface(_rType);
// aggregate interfaces // (ask the aggregated object _after_ the OComponentHelper (base of OFormComponents), // so calls to the XComponent interface reach us and not the aggregation) if (!aReturn.hasValue() && m_xAggregate.is())
aReturn = m_xAggregate->queryAggregation(_rType);
}
}
}
osl_atomic_increment( &m_refCount );
{ // our aggregated rowset itself is not cloneable, so simply copy the properties
::comphelper::copyProperties( _cloneSource.m_xAggregateSet, m_xAggregateSet );
// also care for the dynamic properties: If the clone source has properties which we do not have, // then add them try
{
Reference< XPropertySet > xSourceProps( const_cast< ODatabaseForm& >( _cloneSource ).queryAggregation(
cppu::UnoType<XPropertySet>::get() ), UNO_QUERY_THROW );
Reference< XPropertySetInfo > xSourcePSI( xSourceProps->getPropertySetInfo(), UNO_SET_THROW );
Reference< XPropertyState > xSourcePropState( xSourceProps, UNO_QUERY );
// the initial value passed to XPropertyContainer is also used as default, usually. So, try // to retrieve the default of the source property
Any aInitialValue; if ( xSourcePropState.is() )
{
aInitialValue = xSourcePropState->getPropertyDefault( sourceProperty.Name );
} else
{
aInitialValue = xSourceProps->getPropertyValue( sourceProperty.Name );
}
addProperty( sourceProperty.Name, sourceProperty.Attributes, aInitialValue );
setPropertyValue( sourceProperty.Name, xSourceProps->getPropertyValue( sourceProperty.Name ) );
}
} catch(const RuntimeException&)
{ throw;
} catch(const Exception&)
{
css::uno::Any a(cppu::getCaughtException()); throw WrappedTargetRuntimeException(
u"Could not clone the given database form."_ustr,
*const_cast< ODatabaseForm* >( &_cloneSource ),
a
);
}
}
osl_atomic_decrement( &m_refCount );
}
// listen for the properties, important for Parameters if ( m_xAggregateSet.is() )
{
m_xAggregatePropertyMultiplexer = new OPropertyChangeMultiplexer(this, m_xAggregateSet, false);
m_xAggregatePropertyMultiplexer->addProperty(PROPERTY_COMMAND);
m_xAggregatePropertyMultiplexer->addProperty(PROPERTY_ACTIVE_CONNECTION);
}
if (m_xAggregate.is())
m_xAggregate->setDelegator( nullptr );
m_aWarnings.setExternalWarnings( nullptr );
if (m_xAggregatePropertyMultiplexer.is())
{
m_xAggregatePropertyMultiplexer->dispose();
m_xAggregatePropertyMultiplexer.clear();
}
}
// html tools
OUString ODatabaseForm::GetDataEncoded(bool _bURLEncoded,const Reference<XControl>& SubmitButton, const css::awt::MouseEvent& MouseEvt)
{ // Fill List of successful Controls
HtmlSuccessfulObjList aSuccObjList;
FillSuccessfulList( aSuccObjList, SubmitButton, MouseEvt );
// Aggregate the list to OUString
OUStringBuffer aResult;
OUString aName;
OUString aValue;
for ( HtmlSuccessfulObjList::iterator pSuccObj = aSuccObjList.begin();
pSuccObj < aSuccObjList.end();
++pSuccObj
)
{
aName = pSuccObj->aName;
aValue = pSuccObj->aValue; if( pSuccObj->nRepresentation == SUCCESSFUL_REPRESENT_FILE && !aValue.isEmpty() )
{ // For File URLs we transfer the file name and not a URL, because Netscape does it like that
INetURLObject aURL;
aURL.SetSmartProtocol(INetProtocol::File);
aURL.SetSmartURL(aValue); if( INetProtocol::File == aURL.GetProtocol() )
aValue = INetURLObject::decode(aURL.PathToFileName(), INetURLObject::DecodeMechanism::Unambiguous);
}
Encode( aName );
Encode( aValue );
aResult.append(aName + "=" + aValue);
if (pSuccObj < aSuccObjList.end() - 1)
{ if ( _bURLEncoded )
aResult.append('&'); else
aResult.append("\r\n");
}
}
switch( nClassId )
{ // Buttons case FormComponentType::COMMANDBUTTON:
{ // We only evaluate the pressed Submit button // If one is passed at all if( rxSubmitButton.is() )
{
Reference<XPropertySet> xSubmitButtonComponent(rxSubmitButton->getModel(), UNO_QUERY); if (xSubmitButtonComponent == xComponentSet && hasProperty(PROPERTY_LABEL, xComponentSet))
{ // <name>=<label>
OUString aLabel;
xComponentSet->getPropertyValue( PROPERTY_LABEL ) >>= aLabel;
rList.emplace_back(aName, aLabel );
}
}
} break;
// ImageButtons case FormComponentType::IMAGEBUTTON:
{ // We only evaluate the pressed Submit button // If one is passed at all if( rxSubmitButton.is() )
{
Reference<XPropertySet> xSubmitButtonComponent(rxSubmitButton->getModel(), UNO_QUERY); if (xSubmitButtonComponent == xComponentSet)
{ // <name>.x=<pos.X>&<name>.y=<pos.Y>
OUString aRhs = OUString::number( MouseEvt.X );
// Only if a name is available we have a name.x
OUStringBuffer aLhs(aName); if (!aName.isEmpty())
aLhs.append(".x"); else
aLhs.append("x");
rList.emplace_back(aLhs.makeStringAndClear(), aRhs );
// Special treatment for multiline edit only if we have a control for it
Any aTmp = xComponentSet->getPropertyValue( PROPERTY_MULTILINE ); bool bMulti = rxSubmitButton.is()
&& (aTmp.getValueTypeClass() == TypeClass_BOOLEAN)
&& getBOOL(aTmp);
OUString sText; if ( bMulti ) // For multiline edit, get the text at the control
{
// Find the right control bool bFound = false; const Sequence<Reference<XControl>> aControls = xControlContainer->getControls(); for( autoconst& xControl : aControls )
{
Reference<XPropertySet> xModel(xControl->getModel(), UNO_QUERY); if ((bFound = xModel == xComponentSet))
{
Reference<XTextComponent> xTextComponent(xControl, UNO_QUERY); if( xTextComponent.is() )
sText = xTextComponent->getText(); break;
}
} // Couldn't find control or it does not exist (edit in the grid) if (!bFound)
xComponentSet->getPropertyValue( PROPERTY_TEXT ) >>= sText;
} else
xComponentSet->getPropertyValue( PROPERTY_TEXT ) >>= sText;
rList.emplace_back(aName, sText );
} break;
// ComboBox, Patternfield case FormComponentType::COMBOBOX: case FormComponentType::PATTERNFIELD:
{ // <name>=<text> if( hasProperty(PROPERTY_TEXT, xComponentSet) )
{
OUString aText;
xComponentSet->getPropertyValue( PROPERTY_TEXT ) >>= aText;
rList.emplace_back(aName, aText );
}
} break; case FormComponentType::CURRENCYFIELD: case FormComponentType::NUMERICFIELD:
{ // <name>=<value> // Value is a double with dot as decimal delimiter // no value (NULL) means empty value if( hasProperty(PROPERTY_VALUE, xComponentSet) )
{
OUString aText;
Any aVal = xComponentSet->getPropertyValue( PROPERTY_VALUE );
double aDoubleVal = 0; if (aVal >>= aDoubleVal)
{
sal_Int16 nScale = 0;
xComponentSet->getPropertyValue( PROPERTY_DECIMAL_ACCURACY ) >>= nScale;
aText = ::rtl::math::doubleToUString(aDoubleVal, rtl_math_StringFormat_F, nScale, '.', true);
}
rList.emplace_back(aName, aText );
}
} break; case FormComponentType::DATEFIELD:
{ // <name>=<value> // Value is a Date with the format MM-DD-YYYY // no value (NULL) means empty value if( hasProperty(PROPERTY_DATE, xComponentSet) )
{
OUString aText;
Any aVal = xComponentSet->getPropertyValue( PROPERTY_DATE );
sal_Int32 nInt32Val = 0; if (aVal >>= nInt32Val)
{
::Date aDate( nInt32Val );
OUStringBuffer aBuffer;
appendDigits( aDate.GetMonth(), 2, aBuffer );
aBuffer.append( '-' );
appendDigits( aDate.GetDay(), 2, aBuffer );
aBuffer.append( '-' );
appendDigits( aDate.GetYear(), 4, aBuffer );
aText = aBuffer.makeStringAndClear();
}
rList.emplace_back(aName, aText );
}
} break; case FormComponentType::TIMEFIELD:
{ // <name>=<value> // Value is a Time with the format HH:MM:SS // no value (NULL) means empty value if( hasProperty(PROPERTY_TIME, xComponentSet) )
{
OUString aText;
Any aVal = xComponentSet->getPropertyValue( PROPERTY_TIME );
sal_Int32 nInt32Val = 0; if (aVal >>= nInt32Val)
{ // Is this 32-bit number actually encoded time? Or should rather // Time::MakeTimeFromNS be used here?
::tools::Time aTime(tools::Time::fromEncodedTime(nInt32Val));
OUStringBuffer aBuffer;
appendDigits( aTime.GetHour(), 2, aBuffer );
aBuffer.append( '-' );
appendDigits( aTime.GetMin(), 2, aBuffer );
aBuffer.append( '-' );
appendDigits( aTime.GetSec(), 2, aBuffer );
aText = aBuffer.makeStringAndClear();
}
rList.emplace_back(aName, aText );
}
} break;
// starform case FormComponentType::HIDDENCONTROL:
{
// Simple or multiple selection // For simple selections MT only accounts for the list's first entry. if (nSelCount > 1 && !getBOOL(xComponentSet->getPropertyValue(PROPERTY_MULTISELECTION)))
nSelCount = 1;
// The indices in the selection list can also be invalid, so we first have to // find the valid ones to determine the length of the new list.
sal_Int32 nCurCnt = 0;
sal_Int32 i; for( i=0; i<nSelCount; ++i )
{ if( pSels[i] < nStringCnt )
++nCurCnt;
}
OUString aSubValue; for(i=0; i<nCurCnt; ++i )
{
sal_Int16 nSelPos = pSels[i]; if (nSelPos < nValCnt && !pVals[nSelPos].isEmpty())
{
aSubValue = pVals[nSelPos];
} else
{
aSubValue = pStrings[nSelPos];
}
rList.emplace_back(aName, aSubValue );
}
} break; case FormComponentType::GRIDCONTROL:
{ // Each of the column values is sent; // the name is extended by the grid's prefix.
Reference<XIndexAccess> xContainer(xComponentSet, UNO_QUERY); if (!xContainer.is()) break;
aName += ".";
Reference<XPropertySet> xSet;
sal_Int32 nCount = xContainer->getCount(); // we know already how many objects should be appended, // so why not allocate the space for them
rList.reserve( nCount + rList.capacity() ); // not size() for (sal_Int32 i = 0; i < nCount; ++i)
{
xContainer->getByIndex(i) >>= xSet; if (xSet.is())
AppendComponent(rList, xSet, aName, rxSubmitButton, MouseEvt);
}
}
}
}
// we know already how many objects should be appended, // so why not allocate the space for them
rList.reserve( getCount() ); for( sal_Int32 nIndex=0; nIndex < getCount(); nIndex++ )
{
getByIndex( nIndex ) >>= xComponentSet;
AppendComponent(rList, xComponentSet, std::u16string_view(), rxSubmitButton, MouseEvt);
}
}
// Handle chars, which are not an alphanumeric character and character codes > 127 if( (!rtl::isAsciiAlphanumeric(nCharCode) && nCharCode != ' ')
|| nCharCode > 127 )
{ switch( nCharCode )
{ case 13: // CR
aResult.append("%0D%0A"); // CR LF in hex break;
// Special treatment for Netscape case 42: // '*' case 45: // '-' case 46: // '.' case 64: // '@' case 95: // '_'
aResult.append(nCharCode); break;
bool ODatabaseForm::hasValidParent() const
{ // do we have to fill the parameters again? if (!m_bSubForm) returntrue;
Reference<XResultSet> xResultSet(m_xParent, UNO_QUERY); if (!xResultSet.is())
{
OSL_FAIL("ODatabaseForm::hasValidParent() : no parent resultset !"); returnfalse;
} try
{
Reference< XPropertySet > xSet( m_xParent, UNO_QUERY );
Reference< XLoadable > xLoad( m_xParent, UNO_QUERY ); if ( xLoad->isLoaded()
&& ( xResultSet->isBeforeFirst()
|| xResultSet->isAfterLast()
|| getBOOL( xSet->getPropertyValue( PROPERTY_ISNEW ) )
)
) // the parent form is loaded and on a "virtual" row -> not valid returnfalse;
} catch(const Exception&)
{ // parent could be forwardonly? returnfalse;
} returntrue;
}
bool ODatabaseForm::fillParameters( ::osl::ResettableMutexGuard& _rClearForNotifies, const Reference< XInteractionHandler >& _rxCompletionHandler )
{ // do we have to fill the parameters again? if ( !m_aParameterManager.isUpToDate() )
updateParameterInfo();
// is there a valid parent? if ( m_bSubForm && !hasValidParent() ) returntrue;
// ensure we're connected if ( !implEnsureConnection() ) returnfalse;
if ( m_aParameterManager.isUpToDate() ) return m_aParameterManager.fillParameterValues( _rxCompletionHandler, _rClearForNotifies );
if (!fillParameters(_rClearForNotifies, _rxCompletionHandler)) returnfalse;
restoreInsertOnlyState( );
// ensure the aggregated row set has the correct properties
sal_Int32 nConcurrency = ResultSetConcurrency::READ_ONLY;
// if we have a parent, who is not positioned on a valid row // we can't be updatable! if (m_bSubForm && !hasValidParent())
{ // don't use any parameters if we don't have a valid parent
m_aParameterManager.setAllParametersNull();
if (bSuccess)
{ // adjust the privilege property // m_nPrivileges;
m_xAggregateSet->getPropertyValue(PROPERTY_PRIVILEGES) >>= m_nPrivileges; if (!m_bAllowInsert)
m_nPrivileges &= ~Privilege::INSERT; if (!m_bAllowUpdate)
m_nPrivileges &= ~Privilege::UPDATE; if (!m_bAllowDelete)
m_nPrivileges &= ~Privilege::DELETE;
if (bMoveToFirst)
{ // the row set is positioned _before_ the first row (per definitionem), so move the set ... try
{ // if we have an insert only rowset we move to the insert row
next(); if (((m_nPrivileges & Privilege::INSERT) == Privilege::INSERT)
&& isAfterLast())
{ // move on the insert row of set // resetting must be done later, after the load events have been posted // see: moveToInsertRow and load , reload if (auto xUpdate = query_aggregation<XResultSetUpdate>(m_xAggregate))
xUpdate->moveToInsertRow();
}
} catch(const SQLException& eDB)
{
_rClearForNotifies.clear(); if (!m_sCurrentErrorContext.isEmpty())
onError(eDB, m_sCurrentErrorContext); else
onError(eDB, ResourceManager::loadString(RID_STR_READERROR));
_rClearForNotifies.reset();
bSuccess = false;
}
}
} return bSuccess;
}
void ODatabaseForm::disposing()
{ if (m_xAggregatePropertyMultiplexer.is())
m_xAggregatePropertyMultiplexer->dispose();
if (m_xAggregateSet.is())
_rAggregateProps = m_xAggregateSet->getPropertySetInfo()->getProperties();
// we want to "override" the privileges, since we have additional "AllowInsert" etc. properties
RemoveProperty( _rAggregateProps, PROPERTY_PRIVILEGES );
// InsertOnly is also to be overridden, since we sometimes change it ourself
RemoveProperty( _rAggregateProps, PROPERTY_INSERTONLY );
// we remove and re-declare the DataSourceName property, 'cause we want it to be constrained, and the // original property of our aggregate isn't
RemoveProperty( _rAggregateProps, PROPERTY_DATASOURCENAME );
// for connection sharing, we need to override the ActiveConnection property, too
RemoveProperty( _rAggregateProps, PROPERTY_ACTIVE_CONNECTION );
// the Filter property is also overwritten, since we have some implicit filters // (e.g. the ones which result from linking master fields to detail fields // via column names instead of parameters)
RemoveProperty( _rAggregateProps, PROPERTY_FILTER );
RemoveProperty( _rAggregateProps, PROPERTY_HAVINGCLAUSE );
RemoveProperty( _rAggregateProps, PROPERTY_APPLYFILTER );
void ODatabaseForm::fire( sal_Int32* pnHandles, const Any* pNewValues, const Any* pOldValues, sal_Int32 nCount )
{ // same as in getFastPropertyValue(sal_Int32) : if we're resetting currently don't fire any changes of the // IsModified property from sal_False to sal_True, as this is only temporary 'til the reset is done if (m_nResetsPending > 0)
{ // look for the PROPERTY_ID_ISMODIFIED
sal_Int32 nPos = 0; for (nPos=0; nPos<nCount; ++nPos) if (pnHandles[nPos] == PROPERTY_ID_ISMODIFIED) break;
if ((nPos < nCount) && (pNewValues[nPos].getValueTypeClass() == TypeClass_BOOLEAN) && getBOOL(pNewValues[nPos]))
{ // yeah, we found it, and it changed to TRUE if (nPos == 0)
{ // just cut the first element
++pnHandles;
++pNewValues;
++pOldValues;
--nCount;
} elseif (nPos == nCount - 1) // just cut the last element
--nCount; else
{ // split into two base class calls
OPropertySetAggregationHelper::fire(pnHandles, pNewValues, pOldValues, nPos, false/*bVetoable*/);
++nPos;
OPropertySetAggregationHelper::fire(pnHandles + nPos, pNewValues + nPos, pOldValues + nPos, nCount - nPos, false/*bVetoable*/); return;
}
}
}
Any SAL_CALL ODatabaseForm::getFastPropertyValue( sal_Int32 nHandle )
{ if ((nHandle == PROPERTY_ID_ISMODIFIED) && (m_nResetsPending > 0)) return css::uno::Any(false); // don't allow the aggregate which is currently being reset to return a (temporary) "yes" else return OPropertySetAggregationHelper::getFastPropertyValue(nHandle);
}
case PROPERTY_ID_DATASOURCE:
{
css::uno::Reference<XInterface> xParent = getParent();
{ // prevent ABBA deadlock between this mutex and the SolarMutex
comphelper::MutexRelease aReleaser(m_aMutex);
Reference< XConnection > xSomeConnection; if ( ::dbtools::isEmbeddedInDatabase( xParent, xSomeConnection ) ) throw PropertyVetoException();
} try
{
m_xAggregateSet->setPropertyValue(PROPERTY_DATASOURCENAME, rValue);
} catch(const Exception&)
{
}
} break; case PROPERTY_ID_TARGET_URL:
rValue >>= m_aTargetURL; break; case PROPERTY_ID_TARGET_FRAME:
rValue >>= m_aTargetFrame; break; case PROPERTY_ID_SUBMIT_METHOD:
rValue >>= m_eSubmitMethod; break; case PROPERTY_ID_SUBMIT_ENCODING:
rValue >>= m_eSubmitEncoding; break; case PROPERTY_ID_NAME:
rValue >>= m_sName; break; case PROPERTY_ID_MASTERFIELDS:
rValue >>= m_aMasterFields;
invalidateParameters(); break; case PROPERTY_ID_DETAILFIELDS:
rValue >>= m_aDetailFields;
invalidateParameters(); break; case PROPERTY_ID_CYCLE:
m_aCycle = rValue; break; case PROPERTY_ID_NAVIGATION:
rValue >>= m_eNavigation; break; case PROPERTY_ID_ALLOWADDITIONS:
m_bAllowInsert = getBOOL(rValue); break; case PROPERTY_ID_ALLOWEDITS:
m_bAllowUpdate = getBOOL(rValue); break; case PROPERTY_ID_ALLOWDELETIONS:
m_bAllowDelete = getBOOL(rValue); break; case PROPERTY_ID_DYNAMIC_CONTROL_BORDER:
m_aDynamicControlBorder = rValue; break; case PROPERTY_ID_CONTROL_BORDER_COLOR_FOCUS:
m_aControlBorderColorFocus = rValue; break; case PROPERTY_ID_CONTROL_BORDER_COLOR_MOUSE:
m_aControlBorderColorMouse = rValue; break; case PROPERTY_ID_CONTROL_BORDER_COLOR_INVALID:
m_aControlBorderColorInvalid = rValue; break;
case PROPERTY_ID_ACTIVE_CONNECTION:
{ bool bIsEmbeddedInDatabase;
Reference< XConnection > xOuterConnection;
css::uno::Reference<XInterface> xParent = getParent();
{ // prevent ABBA deadlock between this mutex and the SolarMutex
comphelper::MutexRelease aReleaser(m_aMutex);
bIsEmbeddedInDatabase = ::dbtools::isEmbeddedInDatabase( xParent, xOuterConnection );
} if (bIsEmbeddedInDatabase)
{ if ( xOuterConnection != Reference< XConnection >( rValue, UNO_QUERY ) ) // somebody's trying to set a connection which is not equal the connection // implied by the database we're embedded in throw PropertyVetoException();
}
OPropertySetAggregationHelper::setFastPropertyValue_NoBroadcast( nHandle, rValue ); break;
}
void ODatabaseForm::setPropertyToDefaultByHandle(sal_Int32 nHandle)
{ switch (nHandle)
{ case PROPERTY_ID_INSERTONLY: case PROPERTY_ID_FILTER: case PROPERTY_ID_HAVINGCLAUSE: case PROPERTY_ID_APPLYFILTER: case PROPERTY_ID_NAVIGATION: case PROPERTY_ID_CYCLE: case PROPERTY_ID_DYNAMIC_CONTROL_BORDER: case PROPERTY_ID_CONTROL_BORDER_COLOR_FOCUS: case PROPERTY_ID_CONTROL_BORDER_COLOR_MOUSE: case PROPERTY_ID_CONTROL_BORDER_COLOR_INVALID:
setFastPropertyValue( nHandle, getPropertyDefaultByHandle( nHandle ) ); break;
Any ODatabaseForm::getPropertyDefaultByHandle( sal_Int32 nHandle ) const
{
Any aReturn; switch (nHandle)
{ case PROPERTY_ID_INSERTONLY: case PROPERTY_ID_DYNAMIC_CONTROL_BORDER:
aReturn <<= false; break;
case PROPERTY_ID_FILTER:
aReturn <<= OUString(); break;
case PROPERTY_ID_HAVINGCLAUSE:
aReturn <<= OUString(); break;
case PROPERTY_ID_APPLYFILTER:
aReturn <<= true; break;
case PROPERTY_ID_NAVIGATION:
aReturn <<= NavigationBarMode_CURRENT; break;
case PROPERTY_ID_CYCLE: case PROPERTY_ID_CONTROL_BORDER_COLOR_FOCUS: case PROPERTY_ID_CONTROL_BORDER_COLOR_MOUSE: case PROPERTY_ID_CONTROL_BORDER_COLOR_INVALID: break;
if (isLoaded())
{
::osl::MutexGuard aResetGuard(m_aResetSafety);
++m_nResetsPending;
reset_impl(true); return;
}
if ( m_aResetListeners.getLength() )
{
::osl::MutexGuard aResetGuard(m_aResetSafety);
++m_nResetsPending; // create an own thread if we have (approve-)reset-listeners (so the listeners can't do that much damage // to this thread which is probably the main one) if (!m_pThread.is())
{
m_pThread = new OFormSubmitResetThread(this);
m_pThread->create();
}
m_pThread->addEvent(std::make_unique<EventObject>());
} else
{ // direct call without any approving by the listeners
aGuard.clear();
double value = xFormatter->convertStringToNumber(nFormatKey, sDefault); switch (dataType)
{ case css::sdbc::DataType::DATE:
{
d.AddDays(value);
def <<= d.GetUNODate(); break;
} case css::sdbc::DataType::TIMESTAMP:
{
::DateTime dt(d);
dt.AddTime(value);
def <<= dt.GetUNODateTime(); break;
}
}
}
void ODatabaseForm::reset_impl(bool _bApproveByListeners)
{ if ( _bApproveByListeners )
{
::comphelper::OInterfaceIteratorHelper3 aIter(m_aResetListeners);
EventObject aEvent(*this); while (aIter.hasMoreElements()) if (!aIter.next()->approveReset(aEvent)) return;
}
::osl::ResettableMutexGuard aResetGuard(m_aResetSafety); // do we have a database connected form and stay on the insert row bool bInsertRow = false; if (m_xAggregateSet.is())
bInsertRow = getBOOL(m_xAggregateSet->getPropertyValue(PROPERTY_ISNEW)); if (bInsertRow)
{ try
{ // Iterate through all columns and set the default value
Reference< XColumnsSupplier > xColsSuppl( m_xAggregateSet, UNO_QUERY );
Reference< XIndexAccess > xIndexCols( xColsSuppl->getColumns(), UNO_QUERY ); for (sal_Int32 i = 0; i < xIndexCols->getCount(); ++i)
{
Reference< XPropertySet > xColProps;
xIndexCols->getByIndex(i) >>= xColProps;
auto getPropValOrDefault = [&xColProps, xPSI = xColProps->getPropertySetInfo()]( const OUString& propname, auto default_value)
{ if (xPSI && xPSI->hasPropertyByName(propname))
fromAny(xColProps->getPropertyValue(propname), &default_value); return default_value;
};
Any aDefault = getPropValOrDefault(u"ControlDefault"_ustr, Any()); if (!aDefault.hasValue()) continue;
if (getPropValOrDefault(PROPERTY_ISREADONLY, false)) continue;
// If the column is a date, and the default is a string, it may be // impossible to convert the string to date at a later stage (namely, in // DBTypeConversion::getValue, where the column formatting is unknown).
maybeConvertDefaultStringToDate(aDefault, getPropValOrDefault,
[this]() { return getFormatter(); });
if ( xParentCols.is() && xParentCols->hasElements() && m_aMasterFields.hasElements() )
{ try
{ // analyze our parameters if ( !m_aParameterManager.isUpToDate() )
updateParameterInfo();
m_aParameterManager.resetParameterValues( );
} catch(const Exception&)
{
OSL_FAIL("ODatabaseForm::reset_impl: could not initialize the master-detail-driven parameters!");
}
}
}
}
aResetGuard.clear(); // iterate through all components. don't use an XIndexAccess as this will cause massive // problems with the count.
Reference<XEnumeration> xIter = createEnumeration(); while (xIter->hasMoreElements())
{
Reference<XReset> xReset;
xIter->nextElement() >>= xReset; if (xReset.is())
{ // TODO: all reset-methods have to be thread-safe
xReset->reset();
}
}
aResetGuard.reset(); // ensure that the row isn't modified // (do this _before_ the listeners are notified ! their reaction (maybe asynchronous) may depend // on the modified state of the row) if (bInsertRow)
m_xAggregateSet->setPropertyValue(PROPERTY_ISMODIFIED, css::uno::Any(false));
aResetGuard.reset(); // and again : ensure the row isn't modified // we already did this after we (and maybe our dependents) reset the values, but the listeners may have changed the row, too if (bInsertRow)
m_xAggregateSet->setPropertyValue(PROPERTY_ISMODIFIED, css::uno::Any(false));
void SAL_CALL ODatabaseForm::submit( const Reference<XControl>& Control, const css::awt::MouseEvent& MouseEvt )
{
{
::osl::MutexGuard aGuard(m_aMutex); // Do we have controls and a Submit URL? if( !getCount() || m_aTargetURL.isEmpty() ) return;
}
::osl::ClearableMutexGuard aGuard(m_aMutex); if (m_aSubmitListeners.getLength())
{ // create an own thread if we have (approve-)submit-listeners (so the listeners can't do that much damage // to this thread which is probably the main one) if (!m_pThread.is())
{
m_pThread = new OFormSubmitResetThread(this);
m_pThread->create();
}
m_pThread->addEvent(std::make_unique<css::awt::MouseEvent>(MouseEvt), Control, true);
} else
{ // direct call without any approving by the listeners
aGuard.clear();
submit_impl( Control, MouseEvt );
}
}
void ODatabaseForm::_propertyChanged(const PropertyChangeEvent& evt)
{ if (evt.PropertyName == PROPERTY_ACTIVE_CONNECTION && !m_bForwardingConnection)
{ // the rowset changed its active connection itself (without interaction from our side), so // we need to fire this event, too
sal_Int32 nHandle = PROPERTY_ID_ACTIVE_CONNECTION;
fire(&nHandle, &evt.NewValue, &evt.OldValue, 1);
} else// it was one of the statement relevant props
{ // if the statement has changed we have to delete the parameter info
invalidateParameters();
}
}
// Should controls be combined into a TabOrder group? if (m_aCycle.hasValue())
{
sal_Int32 nCycle = 0;
::cppu::enum2int(nCycle, m_aCycle); returnstatic_cast<TabulatorCycle>(nCycle) != TabulatorCycle_PAGE;
}
if (isLoaded() && getConnection().is()) returntrue;
// The controls are grouped by adjusting their names to the name of the // first control of the sequence
Reference< XPropertySet > xSet;
OUString sGroupName( Name );
for( autoconst& rControl : _rGroup )
{
xSet.set(rControl, css::uno::UNO_QUERY); if ( !xSet.is() )
{ // can't throw an exception other than a RuntimeException (which would not be appropriate), // so we ignore (and only assert) this
OSL_FAIL( "ODatabaseForm::setGroup: invalid arguments!" ); continue;
}
if (sGroupName.isEmpty())
xSet->getPropertyValue(PROPERTY_NAME) >>= sGroupName; else
xSet->setPropertyValue(PROPERTY_NAME, Any(sGroupName));
}
}
void SAL_CALL ODatabaseForm::disposing(const EventObject& Source)
{ // does the call come from the connection which we are sharing with our parent? if ( isSharingConnection() )
{
Reference< XConnection > xConnSource( Source.Source, UNO_QUERY ); if ( xConnSource.is() )
{ #if OSL_DEBUG_LEVEL > 0
Reference< XConnection > xActiveConn;
m_xAggregateSet->getPropertyValue( PROPERTY_ACTIVE_CONNECTION ) >>= xActiveConn;
OSL_ENSURE( xActiveConn.get() == xConnSource.get(), "ODatabaseForm::disposing: where did this come from?" ); // there should be exactly one XConnection object we're listening at - our aggregate connection #endif
disposingSharedConnection( xConnSource );
}
}
OInterfaceContainer::disposing(Source);
// does the disposing come from the aggregate ? if (m_xAggregate.is())
{ // no -> forward it if (auto xListener = query_aggregation<css::lang::XEventListener>(m_xAggregate))
xListener->disposing(Source);
}
}
void SAL_CALL ODatabaseForm::unloading(const EventObject& /*aEvent*/)
{
{ // now stop the rowset listening if we are a subform
::osl::MutexGuard aGuard( m_aMutex );
if ( m_pLoadTimer && m_pLoadTimer->IsActive() )
m_pLoadTimer->Stop();
m_pLoadTimer.reset();
void SAL_CALL ODatabaseForm::unloaded(const EventObject& /*aEvent*/)
{ // nothing to do
}
void SAL_CALL ODatabaseForm::reloading(const EventObject& /*aEvent*/)
{ // now stop the rowset listening if we are a subform
::osl::MutexGuard aGuard(m_aMutex);
Reference<XRowSet> xParentRowSet(m_xParent, UNO_QUERY); if (xParentRowSet.is())
xParentRowSet->removeRowSetListener(this);
if (m_pLoadTimer && m_pLoadTimer->IsActive())
m_pLoadTimer->Stop();
}
// both rowsets share are connected to the same data source if ( sParentDataSource == sOwnDatasource )
{ if ( !sParentDataSource.isEmpty() ) // and it's really a data source name (not empty)
bCanShareConnection = true; else
{ // the data source name is empty // -> ok for the URL
OUString sParentURL;
OUString sMyURL;
_rxParentProps->getPropertyValue( PROPERTY_URL ) >>= sParentURL;
m_xAggregateSet->getPropertyValue( PROPERTY_URL ) >>= sMyURL;
bCanShareConnection = (sParentURL == sMyURL);
}
}
if ( bCanShareConnection )
{ // check for the user/password
// take the user property on the rowset (if any) into account
OUString sParentUser, sParentPwd;
_rxParentProps->getPropertyValue( PROPERTY_USER ) >>= sParentUser;
_rxParentProps->getPropertyValue( PROPERTY_PASSWORD ) >>= sParentPwd;
void ODatabaseForm::doShareConnection( const Reference< XPropertySet >& _rxParentProps )
{ // get the connection of the parent
Reference< XConnection > xParentConn;
_rxParentProps->getPropertyValue( PROPERTY_ACTIVE_CONNECTION ) >>= xParentConn;
OSL_ENSURE( xParentConn.is(), "ODatabaseForm::doShareConnection: we're a valid sub-form, but the parent has no connection?!" );
if ( xParentConn.is() )
{ // add as dispose listener to the connection
Reference< XComponent > xParentConnComp( xParentConn, UNO_QUERY );
OSL_ENSURE( xParentConnComp.is(), "ODatabaseForm::doShareConnection: invalid connection!" );
xParentConnComp->addEventListener( static_cast< XLoadListener* >( this ) );
// forward the connection to our own aggregate
m_bForwardingConnection = true;
m_xAggregateSet->setPropertyValue( PROPERTY_ACTIVE_CONNECTION, Any( xParentConn ) );
m_bForwardingConnection = false;
// get the connection
Reference< XConnection > xSharedConn;
m_xAggregateSet->getPropertyValue( PROPERTY_ACTIVE_CONNECTION ) >>= xSharedConn;
OSL_ENSURE( xSharedConn.is(), "ODatabaseForm::stopSharingConnection: there's no conn!" );
// remove ourself as event listener
Reference< XComponent > xSharedConnComp( xSharedConn, UNO_QUERY ); if ( xSharedConnComp.is() )
xSharedConnComp->removeEventListener( static_cast< XLoadListener* >( this ) );
// no need to dispose the conn: we're not the owner, this is our parent // (in addition, this method may be called if the connection is being disposed while we use it)
namespace
{
Reference<css::awt::XWindow> GetDialogParentWindow(const Reference<XModel>& rModel)
{ if (!rModel.is()) return nullptr;
Reference<XController> xController(rModel->getCurrentController()); if (!xController.is()) return nullptr;
Reference<XFrame> xFrame(xController->getFrame()); if (!xFrame.is()) return nullptr; return xFrame->getContainerWindow();
}
}
bool ODatabaseForm::implEnsureConnection()
{ try
{ if ( getConnection( ).is() ) // if our aggregate already has a connection, nothing needs to be done about it returntrue;
// see whether we're an embedded form
Reference< XConnection > xOuterConnection; if ( ::dbtools::isEmbeddedInDatabase( getParent(), xOuterConnection ) )
{
m_xAggregateSet->setPropertyValue( PROPERTY_ACTIVE_CONNECTION, Any( xOuterConnection ) ); return xOuterConnection.is();
}
m_bSharingConnection = false;
// if we're a sub form, we try to re-use the connection of our parent if (m_bSubForm)
{
OSL_ENSURE( Reference< XForm >( getParent(), UNO_QUERY ).is(), "ODatabaseForm::implEnsureConnection: m_bSubForm is TRUE, but the parent is no form?" );
// can we re-use (aka share) the connection of the parent? if ( canShareConnection( xParentProps ) )
{ // yep -> do it
doShareConnection( xParentProps ); // success? if ( m_bSharingConnection ) // yes -> outta here returntrue;
}
}
if (m_xAggregateSet.is())
{ //Dig out a suitable parent for any warning dialogs
Reference<css::awt::XWindow> m_xDialogParent;
Reference<XChild> xParent(m_xParent, UNO_QUERY); if (xParent.is())
m_xDialogParent = GetDialogParentWindow(getXModel(xParent->getParent()));
// if we don't have a connection, we are not intended to be a database form or the aggregate was not able // to establish a connection bool bConnected = implEnsureConnection();
// we don't have to execute if we do not have a command to execute bool bExecute = bConnected && m_xAggregateSet.is() && !getString(m_xAggregateSet->getPropertyValue(PROPERTY_COMMAND)).isEmpty();
// a database form always uses caching // we use starting fetchsize with at least 10 rows if (bConnected)
m_xAggregateSet->setPropertyValue(PROPERTY_FETCHSIZE, Any(sal_Int32(40)));
// if we're loaded as sub form we got a "rowSetChanged" from the parent rowset _before_ we got the "loaded" // so we don't need to execute the statement again, this was already done // (and there were no relevant changes between these two listener calls, the "load" of a form is quite an // atomic operation.)
// if we are on the insert row, we have to reset all controls // to set the default values if (bExecute && getBOOL(m_xAggregateSet->getPropertyValue(PROPERTY_ISNEW)))
reset();
}
}
void SAL_CALL ODatabaseForm::unload()
{
::osl::ResettableMutexGuard aGuard(m_aMutex); if (!isLoaded()) return;
DocumentModifyGuard aModifyGuard( *this ); // ensures the document is not marked as "modified" just because we change some control's content during // reloading...
EventObject aEvent(static_cast<XWeak*>(this));
{ // only if there is no approve listener we can post the event at this time // otherwise see approveRowsetChange // the approval is done by the aggregate if (!m_aRowSetApproveListeners.getLength())
{
aGuard.clear();
m_aLoadListeners.notifyEach( &XLoadListener::reloading, aEvent);
aGuard.reset();
}
}
if (bSuccess)
{
aGuard.clear();
m_aLoadListeners.notifyEach( &XLoadListener::reloaded, aEvent);
// if we are on the insert row, we have to reset all controls // to set the default values if (getBOOL(m_xAggregateSet->getPropertyValue(PROPERTY_ISNEW)))
reset();
} else
m_bLoaded = false;
}
// css::sdbc::XCloseable void SAL_CALL ODatabaseForm::close()
{ // unload will close the aggregate
unload();
}
// css::sdbc::XRowSetListener
void SAL_CALL ODatabaseForm::cursorMoved(const EventObject& /*event*/)
{ // reload the subform with the new parameters of the parent // do this handling delayed to provide of execute too many SQL Statements
osl::MutexGuard aGuard(m_aMutex);
DBG_ASSERT( m_pLoadTimer, "ODatabaseForm::cursorMoved: how can this happen?!" ); if ( !m_pLoadTimer )
impl_createLoadTimer();
if ( m_pLoadTimer->IsActive() )
m_pLoadTimer->Stop();
// and start the timer again
m_pLoadTimer->Start();
}
void SAL_CALL ODatabaseForm::rowChanged(const EventObject& /*event*/)
{ // ignore it
}
void SAL_CALL ODatabaseForm::rowSetChanged(const EventObject& /*event*/)
{ // not interested in : // if our parent is an ODatabaseForm, too, then after this rowSetChanged we'll get a "reloaded" // or a "loaded" event. // If somebody gave us another parent which is an XRowSet but doesn't handle an execute as // "load" respectively "reload"... can't do anything...
}
sal_Bool SAL_CALL ODatabaseForm::approveCursorMove(const EventObject& event)
{ // is our aggregate calling? if (event.Source == css::uno::Reference<css::uno::XInterface>(static_cast<XWeak*>(this)))
{ // Our aggregate doesn't have any ApproveRowSetListeners (expect ourself), as we re-routed the queryInterface // for XRowSetApproveBroadcaster-interface. // So we have to multiplex this approve request.
::comphelper::OInterfaceIteratorHelper3 aIter( m_aRowSetApproveListeners ); while ( aIter.hasMoreElements() )
{
Reference< XRowSetApproveListener > xListener( aIter.next() ); try
{ if ( !xListener->approveCursorMove( event ) ) returnfalse;
} catch (const DisposedException& e)
{ if ( e.Context == xListener )
aIter.remove();
} catch (const RuntimeException&)
{ throw;
} catch (const Exception&)
{
DBG_UNHANDLED_EXCEPTION("forms.component");
}
} returntrue;
} else
{ // this is a call from our parent ... // a parent's cursor move will result in a re-execute of our own row-set, so we have to // ask our own RowSetChangesListeners, too
::osl::ClearableMutexGuard aGuard( m_aMutex ); if ( !impl_approveRowChange_throw( event, false, aGuard ) ) returnfalse;
} returntrue;
}
sal_Bool SAL_CALL ODatabaseForm::approveRowChange(const RowChangeEvent& event)
{ // is our aggregate calling? if (event.Source != css::uno::Reference<css::uno::XInterface>(static_cast<XWeak*>(this))) returntrue;
// Our aggregate doesn't have any ApproveRowSetListeners (expect ourself), as we re-routed the queryInterface // for XRowSetApproveBroadcaster-interface. // So we have to multiplex this approve request.
::comphelper::OInterfaceIteratorHelper3 aIter( m_aRowSetApproveListeners ); while ( aIter.hasMoreElements() )
{
Reference< XRowSetApproveListener > xListener( aIter.next() ); try
{ if ( !xListener->approveRowChange( event ) ) returnfalse;
} catch (const DisposedException& e)
{ if ( e.Context == xListener )
aIter.remove();
} catch (const RuntimeException&)
{ throw;
} catch (const Exception&)
{
DBG_UNHANDLED_EXCEPTION("forms.component");
}
} returntrue;
}
sal_Bool SAL_CALL ODatabaseForm::approveRowSetChange(const EventObject& event)
{ if (event.Source == css::uno::Reference<css::uno::XInterface>(static_cast<XWeak*>(this))) // ignore our aggregate as we handle this approve ourself
{
::osl::ClearableMutexGuard aGuard( m_aMutex ); bool bWasLoaded = isLoaded(); if ( !impl_approveRowChange_throw( event, false, aGuard ) ) returnfalse;
if ( bWasLoaded )
{
m_aLoadListeners.notifyEach( &XLoadListener::reloading, event );
}
} else
{ // this is a call from our parent ... // a parent's cursor move will result in a re-execute of our own row-set, so we have to // ask our own RowSetChangesListeners, too
::osl::ClearableMutexGuard aGuard( m_aMutex ); if ( !impl_approveRowChange_throw( event, false, aGuard ) ) returnfalse;
} returntrue;
}
// do we have to multiplex ? if (m_aRowSetApproveListeners.getLength() == 1)
{ if (auto xBroadcaster = query_aggregation<XRowSetApproveBroadcaster>(m_xAggregate))
{
Reference<XRowSetApproveListener> xListener(static_cast<XRowSetApproveListener*>(this));
xBroadcaster->addRowSetApproveListener(xListener);
}
}
}
void SAL_CALL ODatabaseForm::removeRowSetApproveListener(const Reference<XRowSetApproveListener>& _rListener)
{
osl::MutexGuard aGuard(m_aMutex); // do we have to remove the multiplex ?
m_aRowSetApproveListeners.removeInterface(_rListener); if ( m_aRowSetApproveListeners.getLength() == 0 )
{ if (auto xBroadcaster = query_aggregation<XRowSetApproveBroadcaster>(m_xAggregate))
{
Reference<XRowSetApproveListener> xListener(static_cast<XRowSetApproveListener*>(this));
xBroadcaster->removeRowSetApproveListener(xListener);
}
}
}
void SAL_CALL ODatabaseForm::executeWithCompletion( const Reference< XInteractionHandler >& _rxHandler )
{
::osl::ClearableMutexGuard aGuard(m_aMutex); // the difference between execute and load is, that we position on the first row in case of load // after execute we remain before the first row if (!isLoaded())
{
aGuard.clear();
load_impl(false, false, _rxHandler);
} else
{
EventObject event(static_cast< XWeak* >(this)); if ( !impl_approveRowChange_throw( event, true, aGuard ) ) return;
// we're loaded and somebody wants to execute ourself -> this means a reload
reload_impl(false, _rxHandler);
}
}
// css::sdbc::XRowSet
void SAL_CALL ODatabaseForm::execute()
{
osl::ClearableMutexGuard aGuard(m_aMutex); // if somebody calls an execute and we're not loaded we reroute this call to our load method.
// the difference between execute and load is, that we position on the first row in case of load // after execute we remain before the first row if (!isLoaded())
{
aGuard.clear();
load_impl(false, false);
} else
{
EventObject event(static_cast< XWeak* >(this)); if ( !impl_approveRowChange_throw( event, true, aGuard ) ) return;
// we're loaded and somebody wants to execute ourself -> this means a reload
reload_impl(false);
}
}
void SAL_CALL ODatabaseForm::addRowSetListener(const Reference<XRowSetListener>& _rListener)
{ if (m_xAggregateAsRowSet.is())
m_xAggregateAsRowSet->addRowSetListener(_rListener);
}
void SAL_CALL ODatabaseForm::removeRowSetListener(const Reference<XRowSetListener>& _rListener)
{ if (m_xAggregateAsRowSet.is())
m_xAggregateAsRowSet->removeRowSetListener(_rListener);
}
void SAL_CALL ODatabaseForm::moveToInsertRow()
{ auto xUpdate = query_aggregation<XResultSetUpdate>(m_xAggregate); if (!xUpdate) return;
// _always_ move to the insert row // // Formerly, the following line was conditioned with a "not is new", means we did not move the aggregate // to the insert row if it was already positioned there. // // This prevented the RowSet implementation from resetting its column values. We, ourself, formerly // did this reset of columns in reset_impl, where we set every column to the ControlDefault, or, if this // was not present, to NULL. However, the problem with setting to NULL was #88888#, the problem with // _not_ setting to NULL (which was the original fix for #88888#) was #97955#. // // So now we // * move our aggregate to the insert row // * in reset_impl // - set the control defaults into the columns if not void // - do _not_ set the columns to NULL if no control default is set // // Still, there is #72756#. During fixing this bug, DG introduced not calling the aggregate here. So // in theory, we re-introduced #72756#. But the bug described therein does not happen anymore, as the // preliminaries for it changed (no display of guessed values for new records with autoinc fields) // // BTW: the public Issuezilla bug is #i2815# //
xUpdate->moveToInsertRow();
// then set the default values and the parameters given from the parent
reset();
}
OUString ODatabaseForm::getServiceName()
{ return FRM_COMPONENT_FORM; // old (non-sun) name for compatibility !
}
void SAL_CALL ODatabaseForm::write(const Reference<XObjectOutputStream>& _rxOutStream)
{
DBG_ASSERT(m_xAggregateSet.is(), "ODatabaseForm::write : only to be called if the aggregate exists !");
// all children
OFormComponents::write(_rxOutStream);
// version
_rxOutStream->writeShort(0x0005);
// Name
_rxOutStream << m_sName;
OUString sDataSource; if (m_xAggregateSet.is())
m_xAggregateSet->getPropertyValue(PROPERTY_DATASOURCENAME) >>= sDataSource;
_rxOutStream << sDataSource;
// former CursorSource
OUString sCommand; if (m_xAggregateSet.is())
m_xAggregateSet->getPropertyValue(PROPERTY_COMMAND) >>= sCommand;
_rxOutStream << sCommand;
// former MasterFields
_rxOutStream << m_aMasterFields; // former DetailFields
_rxOutStream << m_aDetailFields;
// former DataEntry if (m_xAggregateSet.is())
_rxOutStream->writeBoolean(getBOOL(m_xAggregateSet->getPropertyValue(PROPERTY_INSERTONLY))); else
_rxOutStream->writeBoolean(false);
// version 2 didn't know some options and the "default" state
sal_Int32 nCycle = sal_Int32(TabulatorCycle_RECORDS); if (m_aCycle.hasValue())
{
::cppu::enum2int(nCycle, m_aCycle); if (m_aCycle == TabulatorCycle_PAGE) // unknown in earlier versions
nCycle = sal_Int32(TabulatorCycle_RECORDS);
}
_rxOutStream->writeShort(static_cast<sal_Int16>(nCycle));
// version 5
OUString sHaving; if (m_xAggregateSet.is())
m_xAggregateSet->getPropertyValue(PROPERTY_HAVINGCLAUSE) >>= sHaving;
_rxOutStream << sHaving;
}
void SAL_CALL ODatabaseForm::read(const Reference<XObjectInputStream>& _rxInStream)
{
DBG_ASSERT(m_xAggregateSet.is(), "ODatabaseForm::read : only to be called if the aggregate exists !");
OFormComponents::read(_rxInStream);
// version
sal_uInt16 nVersion = _rxInStream->readShort();
_rxInStream >> m_sName;
OUString sAggregateProp;
_rxInStream >> sAggregateProp; if (m_xAggregateSet.is())
m_xAggregateSet->setPropertyValue(PROPERTY_DATASOURCENAME, Any(sAggregateProp));
_rxInStream >> sAggregateProp; if (m_xAggregateSet.is())
m_xAggregateSet->setPropertyValue(PROPERTY_COMMAND, Any(sAggregateProp));
// navigation mode was a boolean in version 1 // was a sal_Bool in version 1 bool bNavigation = _rxInStream->readBoolean(); if (nVersion == 1)
m_eNavigation = bNavigation ? NavigationBarMode_CURRENT : NavigationBarMode_NONE;
bool bInsertOnly = _rxInStream->readBoolean(); if (m_xAggregateSet.is())
m_xAggregateSet->setPropertyValue(PROPERTY_INSERTONLY, Any(bInsertOnly));
if ( xBroadcaster.is() && !xForm.is() )
{ // the object is an error broadcaster, but no form itself -> add ourself as listener
xBroadcaster->addSQLErrorListener( this );
}
}
Reference<XSQLErrorBroadcaster> xBroadcaster(_rxObject, UNO_QUERY);
Reference<XForm> xForm(_rxObject, UNO_QUERY); if (xBroadcaster.is() && !xForm.is())
{ // the object is an error broadcaster, but no form itself -> remove ourself as listener
xBroadcaster->removeSQLErrorListener(this);
}
}
void SAL_CALL ODatabaseForm::errorOccured(const SQLErrorEvent& _rEvent)
{ // give it to my own error listener
onError(_rEvent); // TODO: think about extending the chain with an SQLContext object saying // "this was an error of one of my children"
}
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.74Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-05-04)
¤
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.