/* -*- 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 "componenttools.hxx" #include "DatabaseForm.hxx" #include "EventThread.hxx" #include "frm_module.hxx" #include "frm_resource.hrc" #include "frm_resource.hxx" #include "GroupManager.hxx" #include "property.hrc" #include "property.hxx" #include "services.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // compatiblity: DatabaseCursorType is dead, but for compatiblity reasons we still have to write it ... namespace com { namespace sun { namespace star { namespace data { enum DatabaseCursorType { DatabaseCursorType_FORWARD = 0, DatabaseCursorType_SNAPSHOT = 1, DatabaseCursorType_KEYSET = 2, DatabaseCursorType_DYNAMIC = 3, DatabaseCursorType_MAKE_FIXED_SIZE = SAL_MAX_ENUM }; } } } } using namespace ::dbtools; using namespace ::comphelper; using namespace ::com::sun::star::uno; using namespace ::com::sun::star::sdb; using namespace ::com::sun::star::sdbc; using namespace ::com::sun::star::sdbcx; using namespace ::com::sun::star::beans; using namespace ::com::sun::star::container; using namespace ::com::sun::star::task; using namespace ::com::sun::star::frame; using namespace ::com::sun::star::form; using namespace ::com::sun::star::awt; using namespace ::com::sun::star::io; using namespace ::com::sun::star::lang; using namespace ::com::sun::star::data; using namespace ::com::sun::star::util; //-------------------------------------------------------------------------- extern "C" void SAL_CALL createRegistryInfo_ODatabaseForm() { static ::frm::OMultiInstanceAutoRegistration< ::frm::ODatabaseForm > aAutoRegistration; } //......................................................................... namespace frm { //......................................................................... //================================================================== //= DocumentModifyGuard //================================================================== class DocumentModifyGuard { public: DocumentModifyGuard( const Reference< XInterface >& _rxFormComponent ) :m_xDocumentModify( getXModel( _rxFormComponent ), UNO_QUERY ) { impl_changeModifiableFlag_nothrow( false ); } ~DocumentModifyGuard() { impl_changeModifiableFlag_nothrow( true ); } private: void impl_changeModifiableFlag_nothrow( const bool _enable ) { try { if ( m_xDocumentModify.is() ) _enable ? m_xDocumentModify->enableSetModified() : m_xDocumentModify->disableSetModified(); } catch(const Exception&) { DBG_UNHANDLED_EXCEPTION(); } } private: Reference< XModifiable2 > m_xDocumentModify; }; //================================================================== //= OFormSubmitResetThread //=----------------------------------------------------------------- //= submitting and resetting html-forms asynchronously //================================================================== //------------------------------------------------------------------ class OFormSubmitResetThread: public OComponentEventThread { protected: // duplicate an event with respect to it's type virtual EventObject *cloneEvent( const EventObject *pEvt ) const; // process an event. while processing the mutex isn't locked, and pCompImpl // is made sure to remain valid virtual void processEvent( ::cppu::OComponentHelper* _pCompImpl, const EventObject* _pEvt, const Reference& _rControl, sal_Bool _bSubmit); public: OFormSubmitResetThread(ODatabaseForm* pControl) : OComponentEventThread(pControl) { } }; //------------------------------------------------------------------ EventObject* OFormSubmitResetThread::cloneEvent( const EventObject *pEvt ) const { return new ::com::sun::star::awt::MouseEvent( *(::com::sun::star::awt::MouseEvent *)pEvt ); } //------------------------------------------------------------------ void OFormSubmitResetThread::processEvent( ::cppu::OComponentHelper* pCompImpl, const EventObject *_pEvt, const Reference& _rControl, sal_Bool _bSubmit) { if (_bSubmit) ((ODatabaseForm *)pCompImpl)->submit_impl(_rControl, *static_cast(_pEvt), true); else ((ODatabaseForm *)pCompImpl)->reset_impl(true); } //================================================================== //= ODatabaseForm //================================================================== //------------------------------------------------------------------ Reference< XInterface > SAL_CALL ODatabaseForm::Create( const Reference< XMultiServiceFactory >& _rxFactory ) { return *( new ODatabaseForm( _rxFactory ) ); } //------------------------------------------------------------------------------ Sequence SAL_CALL ODatabaseForm::getImplementationId() throw(RuntimeException) { return OImplementationIds::getImplementationId(getTypes()); } //------------------------------------------------------------------ Sequence SAL_CALL ODatabaseForm::getTypes() throw(RuntimeException) { // ask the aggregate Sequence aAggregateTypes; Reference xAggregateTypes; if (query_aggregation(m_xAggregate, xAggregateTypes)) aAggregateTypes = xAggregateTypes->getTypes(); Sequence< Type > aRet = concatSequences( aAggregateTypes, ODatabaseForm_BASE1::getTypes(), OFormComponents::getTypes() ); aRet = concatSequences( aRet, ODatabaseForm_BASE2::getTypes(), ODatabaseForm_BASE3::getTypes() ); return concatSequences( aRet, OPropertySetAggregationHelper::getTypes() ); } //------------------------------------------------------------------ Any SAL_CALL ODatabaseForm::queryAggregation(const Type& _rType) throw(RuntimeException) { 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 aggreagtion) if (!aReturn.hasValue() && m_xAggregate.is()) aReturn = m_xAggregate->queryAggregation(_rType); } } } return aReturn; } DBG_NAME(ODatabaseForm); //------------------------------------------------------------------ ODatabaseForm::ODatabaseForm(const Reference& _rxFactory) :OFormComponents(_rxFactory) ,OPropertySetAggregationHelper(OComponentHelper::rBHelper) ,OPropertyChangeListener(m_aMutex) ,m_aLoadListeners(m_aMutex) ,m_aRowSetApproveListeners(m_aMutex) ,m_aRowSetListeners(m_aMutex) ,m_aSubmitListeners(m_aMutex) ,m_aErrorListeners(m_aMutex) ,m_aResetListeners( *this, m_aMutex ) ,m_aPropertyBagHelper( *this ) ,m_pAggregatePropertyMultiplexer(NULL) ,m_pGroupManager( NULL ) ,m_aParameterManager( m_aMutex, comphelper::getComponentContext(_rxFactory) ) ,m_aFilterManager( _rxFactory ) ,m_pLoadTimer(NULL) ,m_pThread(NULL) ,m_nResetsPending(0) ,m_nPrivileges(0) ,m_bInsertOnly( sal_False ) ,m_eSubmitMethod(FormSubmitMethod_GET) ,m_eSubmitEncoding(FormSubmitEncoding_URL) ,m_eNavigation(NavigationBarMode_CURRENT) ,m_bAllowInsert(sal_True) ,m_bAllowUpdate(sal_True) ,m_bAllowDelete(sal_True) ,m_bLoaded(sal_False) ,m_bSubForm(sal_False) ,m_bForwardingConnection(sal_False) ,m_bSharingConnection( sal_False ) { DBG_CTOR( ODatabaseForm, NULL ); impl_construct(); } //------------------------------------------------------------------ ODatabaseForm::ODatabaseForm( const ODatabaseForm& _cloneSource ) :OFormComponents( _cloneSource ) ,OPropertySetAggregationHelper( OComponentHelper::rBHelper ) ,OPropertyChangeListener( m_aMutex ) ,ODatabaseForm_BASE1() ,ODatabaseForm_BASE2() ,ODatabaseForm_BASE3() ,IPropertyBagHelperContext() ,m_aLoadListeners( m_aMutex ) ,m_aRowSetApproveListeners( m_aMutex ) ,m_aRowSetListeners( m_aMutex ) ,m_aSubmitListeners( m_aMutex ) ,m_aErrorListeners( m_aMutex ) ,m_aResetListeners( *this, m_aMutex ) ,m_aPropertyBagHelper( *this ) ,m_pAggregatePropertyMultiplexer( NULL ) ,m_pGroupManager( NULL ) ,m_aParameterManager( m_aMutex, comphelper::getComponentContext(_cloneSource.m_xServiceFactory) ) ,m_aFilterManager( _cloneSource.m_xServiceFactory ) ,m_pLoadTimer( NULL ) ,m_pThread( NULL ) ,m_nResetsPending( 0 ) ,m_nPrivileges( 0 ) ,m_bInsertOnly( _cloneSource.m_bInsertOnly ) ,m_aControlBorderColorFocus( _cloneSource.m_aControlBorderColorFocus ) ,m_aControlBorderColorMouse( _cloneSource.m_aControlBorderColorMouse ) ,m_aControlBorderColorInvalid( _cloneSource.m_aControlBorderColorInvalid ) ,m_aDynamicControlBorder( _cloneSource.m_aDynamicControlBorder ) ,m_sName( _cloneSource.m_sName ) ,m_aTargetURL( _cloneSource.m_aTargetURL ) ,m_aTargetFrame( _cloneSource.m_aTargetFrame ) ,m_eSubmitMethod( _cloneSource.m_eSubmitMethod ) ,m_eSubmitEncoding( _cloneSource.m_eSubmitEncoding ) ,m_eNavigation( _cloneSource.m_eNavigation ) ,m_bAllowInsert( _cloneSource.m_bAllowInsert ) ,m_bAllowUpdate( _cloneSource.m_bAllowUpdate ) ,m_bAllowDelete( _cloneSource.m_bAllowDelete ) ,m_bLoaded( sal_False ) ,m_bSubForm( sal_False ) ,m_bForwardingConnection( sal_False ) ,m_bSharingConnection( sal_False ) { DBG_CTOR( ODatabaseForm, NULL ); impl_construct(); 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( XPropertySet::static_type() ), UNO_QUERY_THROW ); Reference< XPropertySetInfo > xSourcePSI( xSourceProps->getPropertySetInfo(), UNO_SET_THROW ); Reference< XPropertyState > xSourcePropState( xSourceProps, UNO_QUERY ); Reference< XPropertySetInfo > xDestPSI( getPropertySetInfo(), UNO_QUERY_THROW ); Sequence< Property > aSourceProperties( xSourcePSI->getProperties() ); for ( const Property* pSourceProperty = aSourceProperties.getConstArray(); pSourceProperty != aSourceProperties.getConstArray() + aSourceProperties.getLength(); ++pSourceProperty ) { if ( xDestPSI->hasPropertyByName( pSourceProperty->Name ) ) continue; // 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( pSourceProperty->Name ); } else { aInitialValue = xSourceProps->getPropertyValue( pSourceProperty->Name ); } addProperty( pSourceProperty->Name, pSourceProperty->Attributes, aInitialValue ); setPropertyValue( pSourceProperty->Name, xSourceProps->getPropertyValue( pSourceProperty->Name ) ); } } catch(const Exception&) { throw WrappedTargetException( ::rtl::OUString( "Could not clone the given database form." ), *const_cast< ODatabaseForm* >( &_cloneSource ), ::cppu::getCaughtException() ); } } osl_atomic_decrement( &m_refCount ); } //------------------------------------------------------------------ void ODatabaseForm::impl_construct() { // aggregate a row set increment(m_refCount); { m_xAggregate = Reference< XAggregation >( m_xServiceFactory->createInstance( SRV_SDB_ROWSET ), UNO_QUERY_THROW ); m_xAggregateAsRowSet.set( m_xAggregate, UNO_QUERY_THROW ); setAggregation( m_xAggregate ); } // listen for the properties, important for Parameters if ( m_xAggregateSet.is() ) { m_pAggregatePropertyMultiplexer = new OPropertyChangeMultiplexer(this, m_xAggregateSet, sal_False); m_pAggregatePropertyMultiplexer->acquire(); m_pAggregatePropertyMultiplexer->addProperty(PROPERTY_COMMAND); m_pAggregatePropertyMultiplexer->addProperty(PROPERTY_ACTIVE_CONNECTION); } { Reference< XWarningsSupplier > xRowSetWarnings( m_xAggregate, UNO_QUERY ); m_aWarnings.setExternalWarnings( xRowSetWarnings ); } if ( m_xAggregate.is() ) { m_xAggregate->setDelegator( static_cast< XWeak* >( this ) ); } { m_aFilterManager.initialize( m_xAggregateSet ); m_aParameterManager.initialize( this, m_xAggregate ); declareForwardedProperty( PROPERTY_ID_ACTIVE_CONNECTION ); } decrement( m_refCount ); m_pGroupManager = new OGroupManager( this ); m_pGroupManager->acquire(); } //------------------------------------------------------------------ ODatabaseForm::~ODatabaseForm() { DBG_DTOR(ODatabaseForm,NULL); m_pGroupManager->release(); m_pGroupManager = NULL; if (m_xAggregate.is()) m_xAggregate->setDelegator( NULL ); m_aWarnings.setExternalWarnings( NULL ); if (m_pAggregatePropertyMultiplexer) { m_pAggregatePropertyMultiplexer->dispose(); m_pAggregatePropertyMultiplexer->release(); m_pAggregatePropertyMultiplexer = NULL; } } //============================================================================== // html tools //------------------------------------------------------------------------ ::rtl::OUString ODatabaseForm::GetDataURLEncoded(const Reference& SubmitButton, const ::com::sun::star::awt::MouseEvent& MouseEvt) { return GetDataEncoded(true,SubmitButton,MouseEvt); } // ----------------------------------------------------------------------------- ::rtl::OUString ODatabaseForm::GetDataEncoded(bool _bURLEncoded,const Reference& SubmitButton, const ::com::sun::star::awt::MouseEvent& MouseEvt) { // Fill List of successful Controls HtmlSuccessfulObjList aSuccObjList; FillSuccessfulList( aSuccObjList, SubmitButton, MouseEvt ); // Aggregate Liste to ::rtl::OUString ::rtl::OUStringBuffer aResult; ::rtl::OUString aName; ::rtl::OUString aValue; for ( HtmlSuccessfulObjListIterator 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(INET_PROT_FILE); aURL.SetSmartURL(aValue); if( INET_PROT_FILE == aURL.GetProtocol() ) aValue = INetURLObject::decode(aURL.PathToFileName(), '%', INetURLObject::DECODE_UNAMBIGUOUS); } Encode( aName ); Encode( aValue ); aResult.append(aName); aResult.append(sal_Unicode('=')); aResult.append(aValue); if (pSuccObj < aSuccObjList.end() - 1) { if ( _bURLEncoded ) aResult.append(sal_Unicode('&')); else aResult.appendAscii("\r\n"); } } aSuccObjList.clear(); return aResult.makeStringAndClear(); } //============================================================================== // html tools //------------------------------------------------------------------------ ::rtl::OUString ODatabaseForm::GetDataTextEncoded(const Reference& SubmitButton, const ::com::sun::star::awt::MouseEvent& MouseEvt) { return GetDataEncoded(false,SubmitButton,MouseEvt); } //------------------------------------------------------------------------ Sequence ODatabaseForm::GetDataMultiPartEncoded(const Reference& SubmitButton, const ::com::sun::star::awt::MouseEvent& MouseEvt, ::rtl::OUString& rContentType) { // Create Parent INetMIMEMessage aParent; aParent.EnableAttachChild( INETMSG_MULTIPART_FORM_DATA ); // Fill List of successful Controls HtmlSuccessfulObjList aSuccObjList; FillSuccessfulList( aSuccObjList, SubmitButton, MouseEvt ); // Aggregate Liste to ::rtl::OUString for ( HtmlSuccessfulObjListIterator pSuccObj = aSuccObjList.begin(); pSuccObj < aSuccObjList.end(); ++pSuccObj ) { if( pSuccObj->nRepresentation == SUCCESSFUL_REPRESENT_TEXT ) InsertTextPart( aParent, pSuccObj->aName, pSuccObj->aValue ); else if( pSuccObj->nRepresentation == SUCCESSFUL_REPRESENT_FILE ) InsertFilePart( aParent, pSuccObj->aName, pSuccObj->aValue ); } // Delete List aSuccObjList.clear(); // Create MessageStream for parent INetMIMEMessageStream aMessStream; aMessStream.SetSourceMessage( &aParent ); aMessStream.GenerateHeader( sal_False ); // Copy MessageStream to SvStream SvMemoryStream aMemStream; char* pBuf = new char[1025]; int nRead; while( (nRead = aMessStream.Read(pBuf, 1024)) > 0 ) aMemStream.Write( pBuf, nRead ); delete[] pBuf; aMemStream.Flush(); aMemStream.Seek( 0 ); void* pData = (void*)aMemStream.GetData(); sal_Int32 nLen = aMemStream.Seek(STREAM_SEEK_TO_END); rContentType = aParent.GetContentType(); return Sequence((sal_Int8*)pData, nLen); } //------------------------------------------------------------------------ namespace { static void appendDigits( sal_Int32 _nNumber, sal_Int8 nDigits, ::rtl::OUStringBuffer& _rOut ) { sal_Int32 nCurLen = _rOut.getLength(); _rOut.append( _nNumber ); while ( _rOut.getLength() - nCurLen < nDigits ) _rOut.insert( nCurLen, (sal_Unicode)'0' ); } } //------------------------------------------------------------------------ void ODatabaseForm::AppendComponent(HtmlSuccessfulObjList& rList, const Reference& xComponentSet, const ::rtl::OUString& rNamePrefix, const Reference& rxSubmitButton, const ::com::sun::star::awt::MouseEvent& MouseEvt) { if (!xComponentSet.is()) return; // MIB 25.6.98: Catch nested Forms; or would we need to submit them? if (!hasProperty(PROPERTY_CLASSID, xComponentSet)) return; // Get names if (!hasProperty(PROPERTY_NAME, xComponentSet)) return; sal_Int16 nClassId = 0; xComponentSet->getPropertyValue(PROPERTY_CLASSID) >>= nClassId; ::rtl::OUString aName; xComponentSet->getPropertyValue( PROPERTY_NAME ) >>= aName; if( aName.isEmpty() && nClassId != FormComponentType::IMAGEBUTTON) return; else // Extend name with the prefix aName = rNamePrefix + aName; switch( nClassId ) { // Buttons case FormComponentType::COMMANDBUTTON: { // We only evaluate the pressed Submit button // MIB: If one is passed at all if( rxSubmitButton.is() ) { Reference xSubmitButtonComponent(rxSubmitButton->getModel(), UNO_QUERY); if (xSubmitButtonComponent == xComponentSet && hasProperty(PROPERTY_LABEL, xComponentSet)) { // =