summaryrefslogtreecommitdiffstats
path: root/svx/source/form
diff options
context:
space:
mode:
Diffstat (limited to 'svx/source/form')
-rw-r--r--svx/source/form/ParseContext.cxx214
-rw-r--r--svx/source/form/dataaccessdescriptor.cxx356
-rw-r--r--svx/source/form/databaselocationinput.cxx245
-rw-r--r--svx/source/form/datalistener.cxx91
-rw-r--r--svx/source/form/datanavi.cxx3123
-rw-r--r--svx/source/form/dbcharsethelper.cxx48
-rw-r--r--svx/source/form/delayedevent.cxx48
-rw-r--r--svx/source/form/filtnav.cxx1849
-rw-r--r--svx/source/form/fmPropBrw.cxx583
-rw-r--r--svx/source/form/fmcontrolbordermanager.cxx425
-rw-r--r--svx/source/form/fmcontrollayout.cxx312
-rw-r--r--svx/source/form/fmdmod.cxx92
-rw-r--r--svx/source/form/fmdocumentclassification.cxx197
-rw-r--r--svx/source/form/fmexch.cxx347
-rw-r--r--svx/source/form/fmexpl.cxx529
-rw-r--r--svx/source/form/fmmodel.cxx205
-rw-r--r--svx/source/form/fmobj.cxx653
-rw-r--r--svx/source/form/fmobjfac.cxx231
-rw-r--r--svx/source/form/fmpage.cxx170
-rw-r--r--svx/source/form/fmpgeimp.cxx713
-rw-r--r--svx/source/form/fmscriptingenv.cxx928
-rw-r--r--svx/source/form/fmservs.cxx74
-rw-r--r--svx/source/form/fmshell.cxx1416
-rw-r--r--svx/source/form/fmshimp.cxx3946
-rw-r--r--svx/source/form/fmsrccfg.cxx286
-rw-r--r--svx/source/form/fmsrcimp.cxx1058
-rw-r--r--svx/source/form/fmtextcontroldialogs.cxx79
-rw-r--r--svx/source/form/fmtextcontrolfeature.cxx119
-rw-r--r--svx/source/form/fmtextcontrolshell.cxx1309
-rw-r--r--svx/source/form/fmtools.cxx371
-rw-r--r--svx/source/form/fmundo.cxx1254
-rw-r--r--svx/source/form/fmview.cxx595
-rw-r--r--svx/source/form/fmvwimp.cxx1919
-rw-r--r--svx/source/form/formcontrolfactory.cxx694
-rw-r--r--svx/source/form/formcontroller.cxx4161
-rw-r--r--svx/source/form/formcontrolling.cxx497
-rw-r--r--svx/source/form/formdispatchinterceptor.cxx176
-rw-r--r--svx/source/form/formfeaturedispatcher.cxx195
-rw-r--r--svx/source/form/formtoolbars.cxx93
-rw-r--r--svx/source/form/labelitemwindow.cxx67
-rw-r--r--svx/source/form/legacyformcontroller.cxx201
-rw-r--r--svx/source/form/navigatortree.cxx2045
-rw-r--r--svx/source/form/navigatortreemodel.cxx906
-rw-r--r--svx/source/form/sdbdatacolumn.cxx55
-rw-r--r--svx/source/form/sqlparserclient.cxx50
-rw-r--r--svx/source/form/tabwin.cxx309
-rw-r--r--svx/source/form/tbxform.cxx208
-rw-r--r--svx/source/form/typemap.cxx52
-rw-r--r--svx/source/form/xfm_addcondition.cxx157
49 files changed, 33651 insertions, 0 deletions
diff --git a/svx/source/form/ParseContext.cxx b/svx/source/form/ParseContext.cxx
new file mode 100644
index 0000000000..d231bfb3cd
--- /dev/null
+++ b/svx/source/form/ParseContext.cxx
@@ -0,0 +1,214 @@
+/* -*- 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 <sal/macros.h>
+#include <svx/ParseContext.hxx>
+#include <svx/strings.hrc>
+
+#include <svx/dialmgr.hxx>
+
+#include <i18nlangtag/languagetag.hxx>
+#include <unotools/syslocale.hxx>
+#include <vcl/svapp.hxx>
+#include <osl/diagnose.h>
+#include <fmstring.hrc>
+#include <mutex>
+
+using namespace svxform;
+using namespace ::connectivity;
+
+OSystemParseContext::OSystemParseContext()
+ : IParseContext()
+{
+ for (size_t i = 0; i < SAL_N_ELEMENTS(RID_RSC_SQL_INTERNATIONAL); ++i)
+ m_aLocalizedKeywords.push_back(SvxResId(RID_RSC_SQL_INTERNATIONAL[i]));
+}
+
+OSystemParseContext::OSystemParseContext(bool /*bInit*/)
+ : IParseContext()
+{
+}
+
+OSystemParseContext::~OSystemParseContext()
+{
+}
+
+css::lang::Locale OSystemParseContext::getPreferredLocale( ) const
+{
+ return SvtSysLocale().GetLanguageTag().getLocale();
+}
+
+OUString OSystemParseContext::getErrorMessage(ErrorCode _eCode) const
+{
+ OUString aMsg;
+ SolarMutexGuard aGuard;
+ switch (_eCode)
+ {
+ case ErrorCode::General: aMsg = SvxResId(RID_STR_SVT_SQL_SYNTAX_ERROR); break;
+ case ErrorCode::ValueNoLike: aMsg = SvxResId(RID_STR_SVT_SQL_SYNTAX_VALUE_NO_LIKE); break;
+ case ErrorCode::FieldNoLike: aMsg = SvxResId(RID_STR_SVT_SQL_SYNTAX_FIELD_NO_LIKE); break;
+ case ErrorCode::InvalidCompare: aMsg = SvxResId(RID_STR_SVT_SQL_SYNTAX_CRIT_NO_COMPARE); break;
+ case ErrorCode::InvalidIntCompare: aMsg = SvxResId(RID_STR_SVT_SQL_SYNTAX_INT_NO_VALID); break;
+ case ErrorCode::InvalidDateCompare: aMsg = SvxResId(RID_STR_SVT_SQL_SYNTAX_ACCESS_DAT_NO_VALID); break;
+ case ErrorCode::InvalidRealCompare: aMsg = SvxResId(RID_STR_SVT_SQL_SYNTAX_REAL_NO_VALID); break;
+ case ErrorCode::InvalidTableNosuch: aMsg = SvxResId(RID_STR_SVT_SQL_SYNTAX_TABLE); break;
+ case ErrorCode::InvalidTableOrQuery: aMsg = SvxResId(RID_STR_SVT_SQL_SYNTAX_TABLE_OR_QUERY); break;
+ case ErrorCode::InvalidColumn: aMsg = SvxResId(RID_STR_SVT_SQL_SYNTAX_COLUMN); break;
+ case ErrorCode::InvalidTableExist: aMsg = SvxResId(RID_STR_SVT_SQL_SYNTAX_TABLE_EXISTS); break;
+ case ErrorCode::InvalidQueryExist: aMsg = SvxResId(RID_STR_SVT_SQL_SYNTAX_QUERY_EXISTS); break;
+ default: break;
+ }
+ return aMsg;
+}
+
+OString OSystemParseContext::getIntlKeywordAscii(InternationalKeyCode _eKey) const
+{
+ size_t nIndex = 0;
+ switch ( _eKey )
+ {
+ case InternationalKeyCode::Like: nIndex = 0; break;
+ case InternationalKeyCode::Not: nIndex = 1; break;
+ case InternationalKeyCode::Null: nIndex = 2; break;
+ case InternationalKeyCode::True: nIndex = 3; break;
+ case InternationalKeyCode::False: nIndex = 4; break;
+ case InternationalKeyCode::Is: nIndex = 5; break;
+ case InternationalKeyCode::Between: nIndex = 6; break;
+ case InternationalKeyCode::Or: nIndex = 7; break;
+ case InternationalKeyCode::And: nIndex = 8; break;
+ case InternationalKeyCode::Avg: nIndex = 9; break;
+ case InternationalKeyCode::Count: nIndex = 10; break;
+ case InternationalKeyCode::Max: nIndex = 11; break;
+ case InternationalKeyCode::Min: nIndex = 12; break;
+ case InternationalKeyCode::Sum: nIndex = 13; break;
+ case InternationalKeyCode::Every: nIndex = 14; break;
+ case InternationalKeyCode::Any: nIndex = 15; break;
+ case InternationalKeyCode::Some: nIndex = 16; break;
+ case InternationalKeyCode::StdDevPop: nIndex = 17; break;
+ case InternationalKeyCode::StdDevSamp: nIndex = 18; break;
+ case InternationalKeyCode::VarSamp: nIndex = 19; break;
+ case InternationalKeyCode::VarPop: nIndex = 20; break;
+ case InternationalKeyCode::Collect: nIndex = 21; break;
+ case InternationalKeyCode::Fusion: nIndex = 22; break;
+ case InternationalKeyCode::Intersection: nIndex = 23; break;
+ case InternationalKeyCode::None:
+ OSL_FAIL( "OSystemParseContext::getIntlKeywordAscii: illegal argument!" );
+ break;
+ }
+
+ OSL_ENSURE( nIndex < m_aLocalizedKeywords.size(), "OSystemParseContext::getIntlKeywordAscii: invalid index!" );
+
+ OString sKeyword;
+ if ( nIndex < m_aLocalizedKeywords.size() )
+ sKeyword = OUStringToOString(m_aLocalizedKeywords[nIndex], RTL_TEXTENCODING_UTF8);
+ return sKeyword;
+}
+
+
+IParseContext::InternationalKeyCode OSystemParseContext::getIntlKeyCode(const OString& rToken) const
+{
+ static const IParseContext::InternationalKeyCode Intl_TokenID[] =
+ {
+ InternationalKeyCode::Like, InternationalKeyCode::Not, InternationalKeyCode::Null, InternationalKeyCode::True,
+ InternationalKeyCode::False, InternationalKeyCode::Is, InternationalKeyCode::Between, InternationalKeyCode::Or,
+ InternationalKeyCode::And, InternationalKeyCode::Avg, InternationalKeyCode::Count, InternationalKeyCode::Max,
+ InternationalKeyCode::Min, InternationalKeyCode::Sum, InternationalKeyCode::Every,
+ InternationalKeyCode::Any, InternationalKeyCode::Some, InternationalKeyCode::StdDevPop,
+ InternationalKeyCode::StdDevSamp, InternationalKeyCode::VarSamp, InternationalKeyCode::VarPop,
+ InternationalKeyCode::Collect, InternationalKeyCode::Fusion, InternationalKeyCode::Intersection
+ };
+
+ sal_uInt32 const nCount = SAL_N_ELEMENTS(Intl_TokenID);
+ for (sal_uInt32 i = 0; i < nCount; i++)
+ {
+ OString aKey = getIntlKeywordAscii(Intl_TokenID[i]);
+ if (rToken.equalsIgnoreAsciiCase(aKey))
+ return Intl_TokenID[i];
+ }
+
+ return InternationalKeyCode::None;
+}
+
+ONeutralParseContext::ONeutralParseContext()
+ : OSystemParseContext(false)
+{
+ std::locale aLocale = Translate::Create("svx", LanguageTag("en-US"));
+ for (size_t i = 0; i < SAL_N_ELEMENTS(RID_RSC_SQL_INTERNATIONAL); ++i)
+ m_aLocalizedKeywords.push_back(Translate::get(RID_RSC_SQL_INTERNATIONAL[i], aLocale));
+}
+
+ONeutralParseContext::~ONeutralParseContext()
+{
+}
+
+namespace
+{
+
+ std::mutex& getSafetyMutex()
+ {
+ static ::std::mutex s_aSafety;
+ return s_aSafety;
+ }
+
+ int s_nCounter;
+
+ OSystemParseContext* getSharedContext(OSystemParseContext* _pContext, bool _bSet)
+ {
+ static OSystemParseContext* s_pSharedContext = nullptr;
+ if ( _pContext && !s_pSharedContext )
+ {
+ s_pSharedContext = _pContext;
+ return s_pSharedContext;
+ }
+ if ( _bSet )
+ {
+ OSystemParseContext* pReturn = _pContext ? _pContext : s_pSharedContext;
+ s_pSharedContext = _pContext;
+ return pReturn;
+ }
+ return s_pSharedContext;
+ }
+
+}
+
+OParseContextClient::OParseContextClient()
+{
+ std::scoped_lock aGuard( getSafetyMutex() );
+ ++s_nCounter;
+ if ( 1 == s_nCounter )
+ { // first instance
+ getSharedContext( new OSystemParseContext, false );
+ }
+}
+
+
+OParseContextClient::~OParseContextClient()
+{
+ std::scoped_lock aGuard( getSafetyMutex() );
+ --s_nCounter;
+ if ( 0 == s_nCounter )
+ delete getSharedContext(nullptr,true);
+}
+
+const OSystemParseContext* OParseContextClient::getParseContext() const
+{
+ return getSharedContext(nullptr, false);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/dataaccessdescriptor.cxx b/svx/source/form/dataaccessdescriptor.cxx
new file mode 100644
index 0000000000..78538fbebb
--- /dev/null
+++ b/svx/source/form/dataaccessdescriptor.cxx
@@ -0,0 +1,356 @@
+/* -*- 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 <svx/dataaccessdescriptor.hxx>
+#include <osl/diagnose.h>
+#include <com/sun/star/ucb/XContent.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <tools/urlobj.hxx>
+#include <map>
+
+namespace svx
+{
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::ucb;
+
+ typedef std::pair<OUString const, DataAccessDescriptorProperty> PropertyMapEntry;
+
+ class ODADescriptorImpl
+ {
+ protected:
+ bool m_bSetOutOfDate : 1;
+ bool m_bSequenceOutOfDate : 1;
+
+ public:
+ typedef ::std::map< DataAccessDescriptorProperty, Any > DescriptorValues;
+ DescriptorValues m_aValues;
+ Sequence< PropertyValue > m_aAsSequence;
+
+ typedef ::std::map< OUString, DataAccessDescriptorProperty > MapString2PropertyEntry;
+
+ public:
+ ODADescriptorImpl();
+ ODADescriptorImpl(const ODADescriptorImpl& _rSource);
+
+ void invalidateExternRepresentations();
+
+ void updateSequence();
+
+ /** builds the descriptor from a property value sequence
+ @return <TRUE/>
+ if and only if the sequence contained valid properties only
+ */
+ bool buildFrom( const Sequence< PropertyValue >& _rValues );
+
+ /** builds the descriptor from a property set
+ @return <TRUE/>
+ if and only if the set contained valid properties only
+ */
+ bool buildFrom( const Reference< XPropertySet >& _rValues );
+
+ protected:
+ static PropertyValue buildPropertyValue( const DescriptorValues::const_iterator& _rPos );
+ static const MapString2PropertyEntry& getPropertyMap( );
+ static PropertyMapEntry const * getPropertyMapEntry( const DescriptorValues::const_iterator& _rPos );
+ };
+
+ ODADescriptorImpl::ODADescriptorImpl()
+ :m_bSetOutOfDate(true)
+ ,m_bSequenceOutOfDate(true)
+ {
+ }
+
+ ODADescriptorImpl::ODADescriptorImpl(const ODADescriptorImpl& _rSource)
+ :m_bSetOutOfDate( _rSource.m_bSetOutOfDate )
+ ,m_bSequenceOutOfDate( _rSource.m_bSequenceOutOfDate )
+ ,m_aValues( _rSource.m_aValues )
+ {
+ if (!m_bSequenceOutOfDate)
+ m_aAsSequence = _rSource.m_aAsSequence;
+ }
+
+ bool ODADescriptorImpl::buildFrom( const Sequence< PropertyValue >& _rValues )
+ {
+ const MapString2PropertyEntry& rProperties = getPropertyMap();
+
+ bool bValidPropsOnly = true;
+
+ // loop through the sequence, and fill our m_aValues
+ for (const PropertyValue& rValue : _rValues)
+ {
+ MapString2PropertyEntry::const_iterator aPropPos = rProperties.find( rValue.Name );
+ if ( aPropPos != rProperties.end() )
+ {
+ DataAccessDescriptorProperty eProperty = aPropPos->second;
+ m_aValues[eProperty] = rValue.Value;
+ }
+ else
+ // unknown property
+ bValidPropsOnly = false;
+ }
+
+ if (bValidPropsOnly)
+ {
+ m_aAsSequence = _rValues;
+ m_bSequenceOutOfDate = false;
+ }
+ else
+ m_bSequenceOutOfDate = true;
+
+ return bValidPropsOnly;
+ }
+
+ bool ODADescriptorImpl::buildFrom( const Reference< XPropertySet >& _rxValues )
+ {
+ Reference< XPropertySetInfo > xPropInfo;
+ if (_rxValues.is())
+ xPropInfo = _rxValues->getPropertySetInfo();
+ if (!xPropInfo.is())
+ {
+ OSL_FAIL("ODADescriptorImpl::buildFrom: invalid property set!");
+ return false;
+ }
+
+ // build a PropertyValue sequence with the current values
+ const Sequence< Property > aProperties = xPropInfo->getProperties();
+
+ Sequence< PropertyValue > aValues(aProperties.getLength());
+ PropertyValue* pValues = aValues.getArray();
+
+ for (const Property& rProperty : aProperties)
+ {
+ pValues->Name = rProperty.Name;
+ pValues->Value = _rxValues->getPropertyValue(rProperty.Name);
+ ++pValues;
+ }
+
+ bool bValidPropsOnly = buildFrom(aValues);
+ m_bSetOutOfDate = !bValidPropsOnly;
+
+ return bValidPropsOnly;
+ }
+
+ void ODADescriptorImpl::invalidateExternRepresentations()
+ {
+ m_bSetOutOfDate = true;
+ m_bSequenceOutOfDate = true;
+ }
+
+ const ODADescriptorImpl::MapString2PropertyEntry& ODADescriptorImpl::getPropertyMap( )
+ {
+ // the properties we know
+ static MapString2PropertyEntry s_aProperties
+ {
+ { OUString("ActiveConnection"), DataAccessDescriptorProperty::Connection, },
+ { OUString("BookmarkSelection"), DataAccessDescriptorProperty::BookmarkSelection, },
+ { OUString("Column"), DataAccessDescriptorProperty::ColumnObject, },
+ { OUString("ColumnName"), DataAccessDescriptorProperty::ColumnName, },
+ { OUString("Command"), DataAccessDescriptorProperty::Command, },
+ { OUString("CommandType"), DataAccessDescriptorProperty::CommandType, },
+ { OUString("Component"), DataAccessDescriptorProperty::Component, },
+ { OUString("ConnectionResource"), DataAccessDescriptorProperty::ConnectionResource, },
+ { OUString("Cursor"), DataAccessDescriptorProperty::Cursor, },
+ { OUString("DataSourceName"), DataAccessDescriptorProperty::DataSource, },
+ { OUString("DatabaseLocation"), DataAccessDescriptorProperty::DatabaseLocation, },
+ { OUString("EscapeProcessing"), DataAccessDescriptorProperty::EscapeProcessing, },
+ { OUString("Filter"), DataAccessDescriptorProperty::Filter, },
+ { OUString("Selection"), DataAccessDescriptorProperty::Selection, }
+ };
+
+ return s_aProperties;
+ }
+
+ PropertyMapEntry const * ODADescriptorImpl::getPropertyMapEntry( const DescriptorValues::const_iterator& _rPos )
+ {
+ const MapString2PropertyEntry& rProperties = getPropertyMap();
+
+ DataAccessDescriptorProperty nNeededHandle = _rPos->first;
+
+ auto loop = std::find_if(rProperties.begin(), rProperties.end(),
+ [&nNeededHandle](const MapString2PropertyEntry::value_type& rProp) { return nNeededHandle == rProp.second; });
+ if (loop != rProperties.end())
+ return &*loop;
+ throw RuntimeException();
+ }
+
+ PropertyValue ODADescriptorImpl::buildPropertyValue( const DescriptorValues::const_iterator& _rPos )
+ {
+ // the map entry
+ PropertyMapEntry const * pProperty = getPropertyMapEntry( _rPos );
+
+ // build the property value
+ PropertyValue aReturn;
+ aReturn.Name = pProperty->first;
+ aReturn.Handle = static_cast<sal_Int32>(pProperty->second);
+ aReturn.Value = _rPos->second;
+ aReturn.State = PropertyState_DIRECT_VALUE;
+
+ // outta here
+ return aReturn;
+ }
+
+ void ODADescriptorImpl::updateSequence()
+ {
+ if (!m_bSequenceOutOfDate)
+ return;
+
+ m_aAsSequence.realloc(m_aValues.size());
+ PropertyValue* pValue = m_aAsSequence.getArray();
+
+ // loop through all our values
+ for ( DescriptorValues::const_iterator aLoop = m_aValues.begin();
+ aLoop != m_aValues.end();
+ ++aLoop, ++pValue
+ )
+ {
+ *pValue = buildPropertyValue(aLoop);
+ }
+
+ // don't need to rebuild next time
+ m_bSequenceOutOfDate = false;
+ }
+
+ ODataAccessDescriptor::ODataAccessDescriptor()
+ :m_pImpl(new ODADescriptorImpl)
+ {
+ }
+
+ ODataAccessDescriptor::ODataAccessDescriptor( const ODataAccessDescriptor& _rSource )
+ :m_pImpl(new ODADescriptorImpl(*_rSource.m_pImpl))
+ {
+ }
+
+ ODataAccessDescriptor::ODataAccessDescriptor(ODataAccessDescriptor&& _rSource) noexcept
+ :m_pImpl(std::move(_rSource.m_pImpl))
+ {
+ }
+
+ ODataAccessDescriptor& ODataAccessDescriptor::operator=(const ODataAccessDescriptor& _rSource)
+ {
+ if (this != &_rSource)
+ m_pImpl.reset(new ODADescriptorImpl(*_rSource.m_pImpl));
+ return *this;
+ }
+
+ ODataAccessDescriptor& ODataAccessDescriptor::operator=(ODataAccessDescriptor&& _rSource) noexcept
+ {
+ m_pImpl = std::move(_rSource.m_pImpl);
+ return *this;
+ }
+
+ ODataAccessDescriptor::ODataAccessDescriptor( const Reference< XPropertySet >& _rValues )
+ :m_pImpl(new ODADescriptorImpl)
+ {
+ m_pImpl->buildFrom(_rValues);
+ }
+
+ ODataAccessDescriptor::ODataAccessDescriptor( const Any& _rValues )
+ :m_pImpl(new ODADescriptorImpl)
+ {
+ // check if we know the format in the Any
+ Sequence< PropertyValue > aValues;
+ Reference< XPropertySet > xValues;
+ if ( _rValues >>= aValues )
+ m_pImpl->buildFrom( aValues );
+ else if ( _rValues >>= xValues )
+ m_pImpl->buildFrom( xValues );
+ }
+
+ ODataAccessDescriptor::ODataAccessDescriptor( const Sequence< PropertyValue >& _rValues )
+ :m_pImpl(new ODADescriptorImpl)
+ {
+ m_pImpl->buildFrom(_rValues);
+ }
+
+ ODataAccessDescriptor::~ODataAccessDescriptor()
+ {
+ }
+
+ void ODataAccessDescriptor::clear()
+ {
+ m_pImpl->m_aValues.clear();
+ }
+
+ void ODataAccessDescriptor::erase(DataAccessDescriptorProperty _eWhich)
+ {
+ OSL_ENSURE(has(_eWhich), "ODataAccessDescriptor::erase: invalid call!");
+ if (has(_eWhich))
+ m_pImpl->m_aValues.erase(_eWhich);
+ }
+
+ bool ODataAccessDescriptor::has(DataAccessDescriptorProperty _eWhich) const
+ {
+ return m_pImpl->m_aValues.find(_eWhich) != m_pImpl->m_aValues.end();
+ }
+
+ const Any& ODataAccessDescriptor::operator [] ( DataAccessDescriptorProperty _eWhich ) const
+ {
+ if (!has(_eWhich))
+ {
+ OSL_FAIL("ODataAccessDescriptor::operator[]: invalid accessor!");
+ static const Any aDummy;
+ return aDummy;
+ }
+
+ return m_pImpl->m_aValues[_eWhich];
+ }
+
+ Any& ODataAccessDescriptor::operator[] ( DataAccessDescriptorProperty _eWhich )
+ {
+ m_pImpl->invalidateExternRepresentations();
+ return m_pImpl->m_aValues[_eWhich];
+ }
+
+ void ODataAccessDescriptor::initializeFrom(const Sequence< PropertyValue >& _rValues)
+ {
+ clear();
+ m_pImpl->buildFrom(_rValues);
+ }
+
+ Sequence< PropertyValue > const & ODataAccessDescriptor::createPropertyValueSequence()
+ {
+ m_pImpl->updateSequence();
+ return m_pImpl->m_aAsSequence;
+ }
+
+ OUString ODataAccessDescriptor::getDataSource() const
+ {
+ OUString sDataSourceName;
+ if ( has(DataAccessDescriptorProperty::DataSource) )
+ (*this)[DataAccessDescriptorProperty::DataSource] >>= sDataSourceName;
+ else if ( has(DataAccessDescriptorProperty::DatabaseLocation) )
+ (*this)[DataAccessDescriptorProperty::DatabaseLocation] >>= sDataSourceName;
+ return sDataSourceName;
+ }
+
+ void ODataAccessDescriptor::setDataSource(const OUString& _sDataSourceNameOrLocation)
+ {
+ if ( !_sDataSourceNameOrLocation.isEmpty() )
+ {
+ INetURLObject aURL(_sDataSourceNameOrLocation);
+ (*this)[ (( aURL.GetProtocol() == INetProtocol::File ) ? DataAccessDescriptorProperty::DatabaseLocation : DataAccessDescriptorProperty::DataSource)] <<= _sDataSourceNameOrLocation;
+ }
+ else
+ (*this)[ DataAccessDescriptorProperty::DataSource ] <<= OUString();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/databaselocationinput.cxx b/svx/source/form/databaselocationinput.cxx
new file mode 100644
index 0000000000..aa4bab9a36
--- /dev/null
+++ b/svx/source/form/databaselocationinput.cxx
@@ -0,0 +1,245 @@
+/* -*- 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 <svx/databaselocationinput.hxx>
+#include <svx/dialmgr.hxx>
+
+#include <svx/strings.hrc>
+
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <comphelper/namedvaluecollection.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sfx2/filedlghelper.hxx>
+#include <svl/filenotation.hxx>
+#include <svtools/inettbc.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <unotools/confignode.hxx>
+#include <unotools/ucbhelper.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+
+namespace svx
+{
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::container::XNameAccess;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::uno::Exception;
+
+ namespace TemplateDescription = ::com::sun::star::ui::dialogs::TemplateDescription;
+
+ class DatabaseLocationInputController_Impl
+ {
+ public:
+ DatabaseLocationInputController_Impl(
+ const Reference<XComponentContext>& _rContext,
+ SvtURLBox& _rLocationInput,
+ weld::Button& _rBrowseButton,
+ weld::Window& _rDialog
+ );
+
+ bool prepareCommit();
+ void setURL( const OUString& _rURL );
+ OUString getURL() const;
+
+ private:
+ void impl_initFilterProperties_nothrow();
+ void impl_onBrowseButtonClicked();
+ OUString impl_getCurrentURL() const;
+
+ DECL_LINK( OnButtonAction, weld::Button&, void );
+
+ private:
+ const Reference<XComponentContext> m_xContext;
+ SvtURLBox& m_rLocationInput;
+ weld::Window& m_rDialog;
+ Sequence< OUString > m_aFilterExtensions;
+ OUString m_sFilterUIName;
+ bool m_bNeedExistenceCheck;
+ };
+
+ DatabaseLocationInputController_Impl::DatabaseLocationInputController_Impl(const Reference<XComponentContext>& _rContext,
+ SvtURLBox& _rLocationInput, weld::Button& _rBrowseButton, weld::Window& _rDialog)
+ :m_xContext( _rContext )
+ ,m_rLocationInput( _rLocationInput )
+ ,m_rDialog( _rDialog )
+ ,m_bNeedExistenceCheck( true )
+ {
+ impl_initFilterProperties_nothrow();
+
+ // forward the allowed extensions to the input control
+ OUStringBuffer aExtensionList;
+ for ( auto const & extension : std::as_const(m_aFilterExtensions) )
+ {
+ aExtensionList.append( extension + ";" );
+ }
+ m_rLocationInput.SetFilter( aExtensionList.makeStringAndClear() );
+ _rBrowseButton.connect_clicked(LINK(this, DatabaseLocationInputController_Impl, OnButtonAction));
+ }
+
+ bool DatabaseLocationInputController_Impl::prepareCommit()
+ {
+ OUString sURL( impl_getCurrentURL() );
+ if ( sURL.isEmpty() )
+ return false;
+
+ // check if the name exists
+ if ( m_bNeedExistenceCheck )
+ {
+ if ( ::utl::UCBContentHelper::Exists( sURL ) )
+ {
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(m_rLocationInput.getWidget(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ SvxResId(RID_STR_ALREADYEXISTOVERWRITE)));
+ if (xQueryBox->run() != RET_YES)
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ void DatabaseLocationInputController_Impl::setURL( const OUString& _rURL )
+ {
+ ::svt::OFileNotation aTransformer( _rURL );
+ m_rLocationInput.set_entry_text( aTransformer.get( ::svt::OFileNotation::N_SYSTEM ) );
+ }
+
+ OUString DatabaseLocationInputController_Impl::getURL() const
+ {
+ return impl_getCurrentURL();
+ }
+
+ void DatabaseLocationInputController_Impl::impl_initFilterProperties_nothrow()
+ {
+ try
+ {
+ // get the name of the default filter for database documents
+ ::utl::OConfigurationTreeRoot aConfig(
+ ::utl::OConfigurationTreeRoot::createWithComponentContext(
+ m_xContext,
+ "/org.openoffice.Setup/Office/Factories/com.sun.star.sdb.OfficeDatabaseDocument"
+ ) );
+ OUString sDatabaseFilter;
+ OSL_VERIFY( aConfig.getNodeValue( "ooSetupFactoryActualFilter" ) >>= sDatabaseFilter );
+
+ // get the type this filter is responsible for
+ Reference< XNameAccess > xFilterFactory(
+ m_xContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.FilterFactory", m_xContext),
+ UNO_QUERY_THROW );
+ ::comphelper::NamedValueCollection aFilterProperties( xFilterFactory->getByName( sDatabaseFilter ) );
+ OUString sDocumentType = aFilterProperties.getOrDefault( "Type", OUString() );
+
+ // get the extension(s) for this type
+ Reference< XNameAccess > xTypeDetection(
+ m_xContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.TypeDetection", m_xContext),
+ UNO_QUERY_THROW );
+
+ ::comphelper::NamedValueCollection aTypeProperties( xTypeDetection->getByName( sDocumentType ) );
+ m_aFilterExtensions = aTypeProperties.getOrDefault( "Extensions", m_aFilterExtensions );
+ m_sFilterUIName = aTypeProperties.getOrDefault( "UIName", m_sFilterUIName );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ // ensure we have at least one extension
+ OSL_ENSURE( m_aFilterExtensions.hasElements(),
+ "DatabaseLocationInputController_Impl::impl_initFilterProperties_nothrow: unable to determine the file extension(s)!" );
+ if ( !m_aFilterExtensions.hasElements() )
+ {
+ m_aFilterExtensions = { "*.odb" };
+ }
+ }
+
+ IMPL_LINK_NOARG(DatabaseLocationInputController_Impl, OnButtonAction, weld::Button&, void)
+ {
+ impl_onBrowseButtonClicked();
+ }
+
+ OUString DatabaseLocationInputController_Impl::impl_getCurrentURL() const
+ {
+ OUString sCurrentFile( m_rLocationInput.get_active_text() );
+ if ( !sCurrentFile.isEmpty() )
+ {
+ ::svt::OFileNotation aCurrentFile( sCurrentFile );
+ sCurrentFile = aCurrentFile.get( ::svt::OFileNotation::N_URL );
+ }
+ return sCurrentFile;
+ }
+
+ void DatabaseLocationInputController_Impl::impl_onBrowseButtonClicked()
+ {
+ ::sfx2::FileDialogHelper aFileDlg(
+ TemplateDescription::FILESAVE_AUTOEXTENSION,
+ FileDialogFlags::NONE,
+ &m_rDialog
+ );
+ aFileDlg.SetDisplayDirectory( impl_getCurrentURL() );
+
+ aFileDlg.AddFilter( m_sFilterUIName, "*." + m_aFilterExtensions[0] );
+ aFileDlg.SetCurrentFilter( m_sFilterUIName );
+
+ if ( aFileDlg.Execute() == ERRCODE_NONE )
+ {
+ INetURLObject aURL( aFileDlg.GetPath() );
+ if( aURL.GetProtocol() != INetProtocol::NotValid )
+ {
+ ::svt::OFileNotation aFileNotation( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ m_rLocationInput.set_entry_text(aFileNotation.get(::svt::OFileNotation::N_SYSTEM));
+ m_rLocationInput.trigger_changed();
+ // the dialog already checked for the file's existence, so we don't need to, again
+ m_bNeedExistenceCheck = false;
+ }
+ }
+ }
+
+ DatabaseLocationInputController::DatabaseLocationInputController( const Reference<XComponentContext>& _rContext,
+ SvtURLBox& _rLocationInput, weld::Button& _rBrowseButton, weld::Window& _rDialog )
+ :m_pImpl( new DatabaseLocationInputController_Impl( _rContext, _rLocationInput, _rBrowseButton, _rDialog ) )
+ {
+ }
+
+ DatabaseLocationInputController::~DatabaseLocationInputController()
+ {
+ }
+
+ bool DatabaseLocationInputController::prepareCommit()
+ {
+ return m_pImpl->prepareCommit();
+ }
+
+ void DatabaseLocationInputController::setURL( const OUString& _rURL )
+ {
+ m_pImpl->setURL( _rURL );
+ }
+
+ OUString DatabaseLocationInputController::getURL() const
+ {
+ return m_pImpl->getURL();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/datalistener.cxx b/svx/source/form/datalistener.cxx
new file mode 100644
index 0000000000..fda74a92c4
--- /dev/null
+++ b/svx/source/form/datalistener.cxx
@@ -0,0 +1,91 @@
+/* -*- 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 <datalistener.hxx>
+#include <datanavi.hxx>
+
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::xml::dom::events;
+
+
+namespace svxform
+{
+
+
+ DataListener::DataListener( DataNavigatorWindow* pNaviWin ) :
+
+ m_pNaviWin( pNaviWin )
+
+ {
+ DBG_ASSERT( m_pNaviWin, "DataListener::Ctor(): no navigator win" );
+ }
+
+ DataListener::~DataListener()
+ {
+ }
+
+ // XContainerListener
+ void SAL_CALL DataListener::elementInserted( const ContainerEvent& /*Event*/ )
+ {
+ m_pNaviWin->NotifyChanges();
+ }
+
+ void SAL_CALL DataListener::elementRemoved( const ContainerEvent& /*Event*/ )
+ {
+ m_pNaviWin->NotifyChanges();
+ }
+
+ void SAL_CALL DataListener::elementReplaced( const ContainerEvent& /*Event*/ )
+ {
+ m_pNaviWin->NotifyChanges();
+ }
+
+ // XFrameActionListener
+ void SAL_CALL DataListener::frameAction( const FrameActionEvent& rActionEvt )
+ {
+ if ( FrameAction_COMPONENT_ATTACHED == rActionEvt.Action ||
+ FrameAction_COMPONENT_REATTACHED == rActionEvt.Action )
+ {
+ m_pNaviWin->NotifyChanges( FrameAction_COMPONENT_REATTACHED == rActionEvt.Action );
+ }
+ }
+
+ // xml::dom::events::XEventListener
+ void SAL_CALL DataListener::handleEvent( const Reference< XEvent >& /*evt*/ )
+ {
+ m_pNaviWin->NotifyChanges();
+ }
+
+ // lang::XEventListener
+ void SAL_CALL DataListener::disposing( const EventObject& /*Source*/ )
+ {
+ SAL_WARN( "svx.form", "disposing" );
+ }
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/datanavi.cxx b/svx/source/form/datanavi.cxx
new file mode 100644
index 0000000000..0178a82c59
--- /dev/null
+++ b/svx/source/form/datanavi.cxx
@@ -0,0 +1,3123 @@
+/* -*- 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 <sal/config.h>
+
+#include <memory>
+
+#include <sal/log.hxx>
+#include <datanavi.hxx>
+#include <fmservs.hxx>
+
+#include <bitmaps.hlst>
+#include <fpicker/strings.hrc>
+#include <o3tl/safeint.hxx>
+#include <o3tl/string_view.hxx>
+#include <svx/svxids.hrc>
+#include <comphelper/diagnose_ex.hxx>
+#include <unotools/resmgr.hxx>
+#include <svx/xmlexchg.hxx>
+#include <unotools/viewoptions.hxx>
+#include <sfx2/filedlghelper.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <utility>
+#include <vcl/commandevent.hxx>
+#include <vcl/event.hxx>
+#include <vcl/weld.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/container/XSet.hpp>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <com/sun/star/xforms/XFormsSupplier.hpp>
+#include <com/sun/star/xml/dom/XDocument.hpp>
+#include <comphelper/string.hxx>
+
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::datatransfer;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::xml::dom::events;
+using namespace ::svx;
+
+constexpr OUString CFGNAME_DATANAVIGATOR = u"DataNavigator"_ustr;
+constexpr OUString CFGNAME_SHOWDETAILS = u"ShowDetails"_ustr;
+constexpr OUString MSG_VARIABLE = u"%1"_ustr;
+constexpr OUStringLiteral MODELNAME = u"$MODELNAME";
+constexpr OUStringLiteral INSTANCENAME = u"$INSTANCENAME";
+constexpr OUStringLiteral ELEMENTNAME = u"$ELEMENTNAME";
+constexpr OUStringLiteral ATTRIBUTENAME = u"$ATTRIBUTENAME";
+constexpr OUStringLiteral SUBMISSIONNAME = u"$SUBMISSIONNAME";
+constexpr OUStringLiteral BINDINGNAME = u"$BINDINGNAME";
+
+
+namespace svxform
+{
+
+ // properties of instance
+ constexpr OUStringLiteral PN_INSTANCE_MODEL = u"Instance";
+ constexpr OUString PN_INSTANCE_ID = u"ID"_ustr;
+ constexpr OUStringLiteral PN_INSTANCE_URL = u"URL";
+
+ // properties of binding
+ constexpr OUString PN_BINDING_ID = u"BindingID"_ustr;
+ constexpr OUString PN_BINDING_EXPR = u"BindingExpression"_ustr;
+ constexpr OUStringLiteral PN_BINDING_MODEL = u"Model";
+ constexpr OUString PN_BINDING_NAMESPACES = u"ModelNamespaces"_ustr;
+ constexpr OUString PN_READONLY_EXPR = u"ReadonlyExpression"_ustr;
+ constexpr OUString PN_RELEVANT_EXPR = u"RelevantExpression"_ustr;
+ constexpr OUString PN_REQUIRED_EXPR = u"RequiredExpression"_ustr;
+ constexpr OUString PN_CONSTRAINT_EXPR = u"ConstraintExpression"_ustr;
+ constexpr OUString PN_CALCULATE_EXPR = u"CalculateExpression"_ustr;
+ constexpr OUString PN_BINDING_TYPE = u"Type"_ustr;
+
+ // properties of submission
+ constexpr OUString PN_SUBMISSION_ID = u"ID"_ustr;
+ constexpr OUString PN_SUBMISSION_BIND = u"Bind"_ustr;
+ constexpr OUString PN_SUBMISSION_REF = u"Ref"_ustr;
+ constexpr OUString PN_SUBMISSION_ACTION = u"Action"_ustr;
+ constexpr OUString PN_SUBMISSION_METHOD = u"Method"_ustr;
+ constexpr OUString PN_SUBMISSION_REPLACE = u"Replace"_ustr;
+
+ // other const strings
+ constexpr OUString TRUE_VALUE = u"true()"_ustr;
+ constexpr OUStringLiteral NEW_ELEMENT = u"newElement";
+ constexpr OUStringLiteral NEW_ATTRIBUTE = u"newAttribute";
+ constexpr OUString EVENTTYPE_CHARDATA = u"DOMCharacterDataModified"_ustr;
+ constexpr OUString EVENTTYPE_ATTR = u"DOMAttrModified"_ustr;
+
+ #define MIN_PAGE_COUNT 3 // at least one instance, one submission and one binding page
+
+ struct ItemNode
+ {
+ Reference< css::xml::dom::XNode > m_xNode;
+ Reference< XPropertySet > m_xPropSet;
+
+ explicit ItemNode( const Reference< css::xml::dom::XNode >& _rxNode ) :
+ m_xNode( _rxNode ) {}
+ explicit ItemNode( const Reference< XPropertySet >& _rxSet ) :
+ m_xPropSet( _rxSet ) {}
+ };
+
+ DataTreeDropTarget::DataTreeDropTarget(weld::TreeView& rWidget)
+ : DropTargetHelper(rWidget.get_drop_target())
+ {
+ }
+
+ sal_Int8 DataTreeDropTarget::AcceptDrop( const AcceptDropEvent& /*rEvt*/ )
+ {
+ return DND_ACTION_NONE;
+ }
+
+ sal_Int8 DataTreeDropTarget::ExecuteDrop( const ExecuteDropEvent& /*rEvt*/ )
+ {
+ return DND_ACTION_NONE;
+ }
+
+ IMPL_LINK(XFormsPage, PopupMenuHdl, const CommandEvent&, rCEvt, bool)
+ {
+ if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
+ return false;
+
+ Point aPos(rCEvt.GetMousePosPixel());
+
+ if (m_xItemList->get_dest_row_at_pos(aPos, m_xScratchIter.get(), false) && !m_xItemList->is_selected(*m_xScratchIter))
+ {
+ m_xItemList->select(*m_xScratchIter);
+ ItemSelectHdl(*m_xItemList);
+ }
+
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xItemList.get(), "svx/ui/formdatamenu.ui"));
+ m_xMenu = xBuilder->weld_menu("menu");
+ m_aRemovedMenuEntries.clear();
+
+ if (DGTInstance == m_eGroup)
+ m_aRemovedMenuEntries.insert("additem");
+ else
+ {
+ m_aRemovedMenuEntries.insert("addelement");
+ m_aRemovedMenuEntries.insert("addattribute");
+
+ if (DGTSubmission == m_eGroup)
+ {
+ m_xMenu->set_label("additem", SvxResId(RID_STR_DATANAV_ADD_SUBMISSION));
+ m_xMenu->set_label("edit", SvxResId(RID_STR_DATANAV_EDIT_SUBMISSION));
+ m_xMenu->set_label("delete", SvxResId(RID_STR_DATANAV_REMOVE_SUBMISSION));
+ }
+ else
+ {
+ m_xMenu->set_label("additem", SvxResId(RID_STR_DATANAV_ADD_BINDING));
+ m_xMenu->set_label("edit", SvxResId(RID_STR_DATANAV_EDIT_BINDING));
+ m_xMenu->set_label("delete", SvxResId(RID_STR_DATANAV_REMOVE_BINDING));
+ }
+ }
+ for (const auto& rRemove : m_aRemovedMenuEntries)
+ m_xMenu->remove(rRemove);
+ EnableMenuItems();
+ OUString sCommand = m_xMenu->popup_at_rect(m_xItemList.get(), tools::Rectangle(aPos, Size(1,1)));
+ if (!sCommand.isEmpty())
+ DoMenuAction(sCommand);
+ m_xMenu.reset();
+ return true;
+ }
+
+ void XFormsPage::DeleteAndClearTree()
+ {
+ m_xItemList->all_foreach([this](weld::TreeIter& rEntry) {
+ delete weld::fromId<ItemNode*>(m_xItemList->get_id(rEntry));
+ return false;
+ });
+ m_xItemList->clear();
+ }
+
+ void XFormsPage::SelectFirstEntry()
+ {
+ if (m_xItemList->get_iter_first(*m_xScratchIter))
+ {
+ m_xItemList->select(*m_xScratchIter);
+ ItemSelectHdl(*m_xItemList);
+ }
+ }
+
+ XFormsPage::XFormsPage(weld::Container* pPage, DataNavigatorWindow* _pNaviWin, DataGroupType _eGroup)
+ : BuilderPage(pPage, nullptr, "svx/ui/xformspage.ui", "XFormsPage")
+ , m_pParent(pPage)
+ , m_xToolBox(m_xBuilder->weld_toolbar("toolbar"))
+ , m_xItemList(m_xBuilder->weld_tree_view("items"))
+ , m_xScratchIter(m_xItemList->make_iterator())
+ , m_aDropHelper(*m_xItemList)
+ , m_pNaviWin(_pNaviWin)
+ , m_bHasModel(false)
+ , m_eGroup(_eGroup)
+ , m_bLinkOnce(false)
+ {
+ m_xItemList->set_show_expanders(DGTInstance == m_eGroup || DGTSubmission == m_eGroup);
+
+ if ( DGTInstance == m_eGroup )
+ m_xToolBox->set_item_visible("additem", false);
+ else
+ {
+ m_xToolBox->set_item_visible("addelement", false);
+ m_xToolBox->set_item_visible("addattribute", false);
+
+ if ( DGTSubmission == m_eGroup )
+ {
+ m_xToolBox->set_item_label("additem", SvxResId(RID_STR_DATANAV_ADD_SUBMISSION));
+ m_xToolBox->set_item_label("edit", SvxResId(RID_STR_DATANAV_EDIT_SUBMISSION));
+ m_xToolBox->set_item_label("delete", SvxResId(RID_STR_DATANAV_REMOVE_SUBMISSION));
+ }
+ else
+ {
+ m_xToolBox->set_item_label("additem", SvxResId(RID_STR_DATANAV_ADD_BINDING));
+ m_xToolBox->set_item_label("edit", SvxResId(RID_STR_DATANAV_EDIT_BINDING));
+ m_xToolBox->set_item_label("delete", SvxResId(RID_STR_DATANAV_REMOVE_BINDING));
+ }
+ }
+
+ m_xToolBox->connect_clicked(LINK(this, XFormsPage, TbxSelectHdl));
+
+ m_xItemList->connect_changed(LINK(this, XFormsPage, ItemSelectHdl));
+ m_xItemList->connect_key_press(LINK(this, XFormsPage, KeyInputHdl));
+ m_xItemList->connect_popup_menu(LINK(this, XFormsPage, PopupMenuHdl));
+ ItemSelectHdl(*m_xItemList);
+ }
+
+ XFormsPage::~XFormsPage()
+ {
+ DeleteAndClearTree();
+ m_pNaviWin = nullptr;
+ m_pParent->move(m_xContainer.get(), nullptr);
+ }
+
+ IMPL_LINK(XFormsPage, TbxSelectHdl, const OUString&, rIdent, void)
+ {
+ DoToolBoxAction(rIdent);
+ }
+
+ IMPL_LINK_NOARG(XFormsPage, ItemSelectHdl, weld::TreeView&, void)
+ {
+ EnableMenuItems();
+ PrepDnD();
+ }
+
+ void XFormsPage::PrepDnD()
+ {
+ rtl::Reference<TransferDataContainer> xTransferable(new TransferDataContainer);
+ m_xItemList->enable_drag_source(xTransferable, DND_ACTION_NONE);
+
+ if (!m_xItemList->get_selected(m_xScratchIter.get()))
+ {
+ // no drag without an entry
+ return;
+ }
+
+ if ( m_eGroup == DGTBinding )
+ {
+ // for the moment, bindings cannot be dragged.
+ // #i59395# / 2005-12-15 / frank.schoenheit@sun.com
+ return;
+ }
+
+ // GetServiceNameForNode() requires a datatype repository which
+ // will be automatically build if requested???
+ Reference< css::xforms::XModel > xModel( GetXFormsHelper(), UNO_QUERY );
+ Reference< css::xforms::XDataTypeRepository > xDataTypes =
+ xModel->getDataTypeRepository();
+ if(!xDataTypes.is())
+ return;
+
+ ItemNode *pItemNode = weld::fromId<ItemNode*>(m_xItemList->get_id(*m_xScratchIter));
+ if (!pItemNode)
+ {
+ // the only known (and allowed?) case where this happens are sub-entries of a submission
+ // entry
+ DBG_ASSERT( DGTSubmission == m_eGroup, "DataTreeListBox::StartDrag: how this?" );
+ bool bSelected = m_xItemList->iter_parent(*m_xScratchIter);
+ DBG_ASSERT(bSelected && !m_xItemList->get_iter_depth(*m_xScratchIter), "DataTreeListBox::StartDrag: what kind of entry *is* this?");
+ // on the submission page, we have only top-level entries (the submission themself)
+ // plus direct children of those (facets of a submission)
+ pItemNode = bSelected ? weld::fromId<ItemNode*>(m_xItemList->get_id(*m_xScratchIter)) : nullptr;
+ if (!pItemNode)
+ return;
+ }
+
+ OUString szName = m_xItemList->get_text(*m_xScratchIter);
+ Reference<css::xml::dom::XNode> xNode(pItemNode->m_xNode);
+ Reference<XPropertySet> xPropSet(pItemNode->m_xPropSet);
+
+ // tdf#154535 create the OXFormsDescriptor on-demand so we don't cause an unwanted
+ // Binding to be created unless we are forced to.
+ auto fnCreateFormsDescriptor = [this, szName, xNode, xPropSet](){
+ OXFormsDescriptor desc;
+ desc.szName = szName;
+ if (xNode) {
+ // a valid node interface tells us that we need to create a control from a binding
+ desc.szServiceName = GetServiceNameForNode(xNode);
+ desc.xPropSet = GetBindingForNode(xNode);
+ DBG_ASSERT( desc.xPropSet.is(), "DataTreeListBox::StartDrag(): invalid node binding" );
+ }
+ else {
+ desc.szServiceName = FM_COMPONENT_COMMANDBUTTON;
+ desc.xPropSet = xPropSet;
+ }
+ return desc;
+ };
+
+ xTransferable = rtl::Reference<TransferDataContainer>(new OXFormsTransferable(fnCreateFormsDescriptor));
+ m_xItemList->enable_drag_source(xTransferable, DND_ACTION_COPY);
+ }
+
+ void XFormsPage::AddChildren(const weld::TreeIter* _pParent,
+ const Reference< css::xml::dom::XNode >& _xNode)
+ {
+ DBG_ASSERT( m_xUIHelper.is(), "XFormsPage::AddChildren(): invalid UIHelper" );
+
+ try
+ {
+ Reference< css::xml::dom::XNodeList > xNodeList = _xNode->getChildNodes();
+ if ( xNodeList.is() )
+ {
+ bool bShowDetails = m_pNaviWin->IsShowDetails();
+ sal_Int32 i, nNodeCount = xNodeList->getLength();
+ for ( i = 0; i < nNodeCount; ++i )
+ {
+ Reference< css::xml::dom::XNode > xChild = xNodeList->item(i);
+ css::xml::dom::NodeType eChildType = xChild->getNodeType();
+ OUString aExpImg;
+ switch ( eChildType )
+ {
+ case css::xml::dom::NodeType_ATTRIBUTE_NODE:
+ aExpImg = RID_SVXBMP_ATTRIBUTE;
+ break;
+ case css::xml::dom::NodeType_ELEMENT_NODE:
+ aExpImg = RID_SVXBMP_ELEMENT;
+ break;
+ case css::xml::dom::NodeType_TEXT_NODE:
+ aExpImg = RID_SVXBMP_TEXT;
+ break;
+ default:
+ aExpImg = RID_SVXBMP_OTHER;
+ }
+
+ OUString sName = m_xUIHelper->getNodeDisplayName( xChild, bShowDetails );
+ if ( !sName.isEmpty() )
+ {
+ ItemNode* pNode = new ItemNode( xChild );
+ OUString sId(weld::toId(pNode));
+ std::unique_ptr<weld::TreeIter> xEntry = m_xItemList->make_iterator();
+ m_xItemList->insert(_pParent, -1, &sName, &sId, nullptr, nullptr, false, xEntry.get());
+ m_xItemList->set_image(*xEntry, aExpImg);
+
+ if ( xChild->hasAttributes() )
+ {
+ Reference< css::xml::dom::XNamedNodeMap > xMap = xChild->getAttributes();
+ if ( xMap.is() )
+ {
+ aExpImg = RID_SVXBMP_ATTRIBUTE;
+ sal_Int32 j, nMapLen = xMap->getLength();
+ for ( j = 0; j < nMapLen; ++j )
+ {
+ Reference< css::xml::dom::XNode > xAttr = xMap->item(j);
+ pNode = new ItemNode( xAttr );
+ OUString sSubId(weld::toId(pNode));
+ OUString sAttrName = m_xUIHelper->getNodeDisplayName( xAttr, bShowDetails );
+ m_xItemList->insert(xEntry.get(), -1, &sAttrName, &sSubId, nullptr, nullptr, false, m_xScratchIter.get());
+ m_xItemList->set_image(*m_xScratchIter, aExpImg);
+ }
+ }
+ }
+ if ( xChild->hasChildNodes() )
+ AddChildren(xEntry.get(), xChild);
+ }
+ }
+ }
+ }
+ catch( Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+ bool XFormsPage::DoToolBoxAction(std::u16string_view rToolBoxID)
+ {
+ bool bHandled = false;
+ bool bIsDocModified = false;
+ m_pNaviWin->DisableNotify( true );
+
+ if (rToolBoxID == u"additem" || rToolBoxID == u"addelement" || rToolBoxID == u"addattribute")
+ {
+ bHandled = true;
+ Reference< css::xforms::XModel > xModel( m_xUIHelper, UNO_QUERY );
+ DBG_ASSERT( xModel.is(), "XFormsPage::DoToolBoxAction(): Action without model" );
+ if ( DGTSubmission == m_eGroup )
+ {
+ AddSubmissionDialog aDlg(m_pNaviWin->GetFrameWeld(), nullptr, m_xUIHelper);
+ if ( aDlg.run() == RET_OK && aDlg.GetNewSubmission().is() )
+ {
+ try
+ {
+ Reference< css::xforms::XSubmission > xNewSubmission = aDlg.GetNewSubmission();
+ Reference< XSet > xSubmissions = xModel->getSubmissions();
+ xSubmissions->insert( Any( xNewSubmission ) );
+ AddEntry(xNewSubmission, m_xScratchIter.get());
+ m_xItemList->select(*m_xScratchIter);
+ bIsDocModified = true;
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::DoToolBoxAction()" );
+ }
+ }
+ }
+ else
+ {
+ DataItemType eType = DITElement;
+
+ std::unique_ptr<weld::TreeIter> xEntry(m_xItemList->make_iterator());
+ bool bEntry = m_xItemList->get_selected(xEntry.get());
+
+ std::unique_ptr<ItemNode> pNode;
+ Reference< css::xml::dom::XNode > xParentNode;
+ Reference< XPropertySet > xNewBinding;
+ TranslateId pResId;
+ bool bIsElement = true;
+ if ( DGTInstance == m_eGroup )
+ {
+ if ( !m_sInstanceURL.isEmpty() )
+ {
+ LinkedInstanceWarningBox aMsgBox(m_pNaviWin->GetFrameWeld());
+ if (aMsgBox.run() != RET_OK)
+ return bHandled;
+ }
+
+ DBG_ASSERT( bEntry, "XFormsPage::DoToolBoxAction(): no entry" );
+ ItemNode* pParentNode = weld::fromId<ItemNode*>(m_xItemList->get_id(*xEntry));
+ DBG_ASSERT( pParentNode, "XFormsPage::DoToolBoxAction(): no parent node" );
+ xParentNode = pParentNode->m_xNode;
+ Reference< css::xml::dom::XNode > xNewNode;
+ if (rToolBoxID == u"addelement")
+ {
+ try
+ {
+ pResId = RID_STR_DATANAV_ADD_ELEMENT;
+ xNewNode = m_xUIHelper->createElement( xParentNode, NEW_ELEMENT );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::DoToolBoxAction(): exception while create element" );
+ }
+ }
+ else
+ {
+ pResId = RID_STR_DATANAV_ADD_ATTRIBUTE;
+ bIsElement = false;
+ eType = DITAttribute;
+ try
+ {
+ xNewNode = m_xUIHelper->createAttribute( xParentNode, NEW_ATTRIBUTE );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::DoToolBoxAction(): exception while create attribute" );
+ }
+ }
+
+ try
+ {
+ xNewNode = xParentNode->appendChild( xNewNode );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::DoToolBoxAction(): exception while append child" );
+ }
+
+ try
+ {
+ Reference< css::xml::dom::XNode > xPNode;
+ if ( xNewNode.is() )
+ xPNode = xNewNode->getParentNode();
+ // attributes don't have parents in the DOM model
+ DBG_ASSERT( rToolBoxID == u"addattribute"
+ || xPNode.is(), "XFormsPage::DoToolboxAction(): node not added" );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::DoToolboxAction()" );
+ }
+
+ try
+ {
+ m_xUIHelper->getBindingForNode( xNewNode, true );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::DoToolBoxAction(): exception while get binding for node" );
+ }
+ pNode.reset(new ItemNode( xNewNode ));
+ }
+ else
+ {
+ try
+ {
+ pResId = RID_STR_DATANAV_ADD_BINDING;
+ xNewBinding = xModel->createBinding();
+ Reference< XSet > xBindings = xModel->getBindings();
+ xBindings->insert( Any( xNewBinding ) );
+ pNode.reset(new ItemNode( xNewBinding ));
+ eType = DITBinding;
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::DoToolBoxAction(): exception while adding binding" );
+ }
+ }
+
+ AddDataItemDialog aDlg(m_pNaviWin->GetFrameWeld(), pNode.get(), m_xUIHelper);
+ aDlg.set_title(SvxResId(pResId));
+ aDlg.InitText( eType );
+ short nReturn = aDlg.run();
+ if ( DGTInstance == m_eGroup )
+ {
+ if ( RET_OK == nReturn )
+ {
+ AddEntry( std::move(pNode), bIsElement, m_xScratchIter.get());
+ m_xItemList->scroll_to_row(*m_xScratchIter);
+ m_xItemList->select(*m_xScratchIter);
+ bIsDocModified = true;
+ }
+ else
+ {
+ try
+ {
+ Reference< css::xml::dom::XNode > xPNode;
+ Reference< css::xml::dom::XNode > xNode =
+ xParentNode->removeChild( pNode->m_xNode );
+ if ( xNode.is() )
+ xPNode = xNode->getParentNode();
+ DBG_ASSERT( !xPNode.is(), "XFormsPage::RemoveEntry(): node not removed" );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::DoToolboxAction()" );
+ }
+ }
+ }
+ else
+ {
+ if ( RET_OK == nReturn )
+ {
+ AddEntry(xNewBinding, m_xScratchIter.get());
+ m_xItemList->select(*m_xScratchIter);
+ bIsDocModified = true;
+ }
+ else
+ {
+ try
+ {
+ Reference< XSet > xBindings = xModel->getBindings();
+ xBindings->remove( Any( xNewBinding ) );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::DoToolboxAction()" );
+ }
+ }
+ }
+ }
+ }
+ else if (rToolBoxID == u"edit")
+ {
+ bHandled = true;
+
+ std::unique_ptr<weld::TreeIter> xEntry(m_xItemList->make_iterator());
+ bool bEntry = m_xItemList->get_selected(xEntry.get());
+ if ( bEntry )
+ {
+ if ( DGTSubmission == m_eGroup && m_xItemList->get_iter_depth(*xEntry) )
+ {
+ m_xItemList->iter_parent(*xEntry);
+ }
+ ItemNode* pNode = weld::fromId<ItemNode*>(m_xItemList->get_id(*xEntry));
+ if ( DGTInstance == m_eGroup || DGTBinding == m_eGroup )
+ {
+ if ( DGTInstance == m_eGroup && !m_sInstanceURL.isEmpty() )
+ {
+ LinkedInstanceWarningBox aMsgBox(m_pNaviWin->GetFrameWeld());
+ if (aMsgBox.run() != RET_OK)
+ return bHandled;
+ }
+
+ AddDataItemDialog aDlg(m_pNaviWin->GetFrameWeld(), pNode, m_xUIHelper);
+ DataItemType eType = DITElement;
+ TranslateId pResId = RID_STR_DATANAV_EDIT_ELEMENT;
+ if ( pNode && pNode->m_xNode.is() )
+ {
+ try
+ {
+ css::xml::dom::NodeType eChildType = pNode->m_xNode->getNodeType();
+ if ( eChildType == css::xml::dom::NodeType_ATTRIBUTE_NODE )
+ {
+ pResId = RID_STR_DATANAV_EDIT_ATTRIBUTE;
+ eType = DITAttribute;
+ }
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::DoToolboxAction()" );
+ }
+ }
+ else if ( DGTBinding == m_eGroup )
+ {
+ pResId = RID_STR_DATANAV_EDIT_BINDING;
+ eType = DITBinding;
+ }
+ aDlg.set_title(SvxResId(pResId));
+ aDlg.InitText( eType );
+ if (aDlg.run() == RET_OK)
+ {
+ // Set the new name
+ OUString sNewName;
+ if ( DGTInstance == m_eGroup )
+ {
+ try
+ {
+ sNewName = m_xUIHelper->getNodeDisplayName(
+ pNode->m_xNode, m_pNaviWin->IsShowDetails() );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::DoToolboxAction()" );
+ }
+ }
+ else if (pNode)
+ {
+ try
+ {
+ OUString sTemp;
+ pNode->m_xPropSet->getPropertyValue( PN_BINDING_ID ) >>= sTemp;
+ sNewName += sTemp + ": ";
+ pNode->m_xPropSet->getPropertyValue( PN_BINDING_EXPR ) >>= sTemp;
+ sNewName += sTemp;
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::DoToolboxAction()" );
+ }
+ }
+
+ m_xItemList->set_text(*xEntry, sNewName);
+ bIsDocModified = true;
+ }
+ }
+ else
+ {
+ AddSubmissionDialog aDlg(m_pNaviWin->GetFrameWeld(), pNode, m_xUIHelper);
+ aDlg.set_title(SvxResId(RID_STR_DATANAV_EDIT_SUBMISSION));
+ if (aDlg.run() == RET_OK)
+ {
+ EditEntry( pNode->m_xPropSet );
+ bIsDocModified = true;
+ }
+ }
+ }
+ }
+ else if (rToolBoxID == u"delete")
+ {
+ bHandled = true;
+ if ( DGTInstance == m_eGroup && !m_sInstanceURL.isEmpty() )
+ {
+ LinkedInstanceWarningBox aMsgBox(m_pNaviWin->GetFrameWeld());
+ if (aMsgBox.run() != RET_OK)
+ return bHandled;
+ }
+ bIsDocModified = RemoveEntry();
+ }
+ else
+ {
+ OSL_FAIL( "XFormsPage::DoToolboxAction: unknown ID!" );
+ }
+
+ m_pNaviWin->DisableNotify( false );
+ EnableMenuItems();
+ if ( bIsDocModified )
+ svxform::DataNavigatorWindow::SetDocModified();
+ return bHandled;
+ }
+
+ void XFormsPage::AddEntry(std::unique_ptr<ItemNode> _pNewNode, bool _bIsElement, weld::TreeIter* pRet)
+ {
+ if (!pRet)
+ pRet = m_xScratchIter.get();
+
+ std::unique_ptr<weld::TreeIter> xParent(m_xItemList->make_iterator());
+ if (!m_xItemList->get_selected(xParent.get()))
+ xParent.reset();
+ OUString aImage(_bIsElement ? RID_SVXBMP_ELEMENT : RID_SVXBMP_ATTRIBUTE);
+ OUString sName;
+ try
+ {
+ sName = m_xUIHelper->getNodeDisplayName(
+ _pNewNode->m_xNode, m_pNaviWin->IsShowDetails() );
+ }
+ catch ( Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ OUString sId(weld::toId(_pNewNode.release()));
+ m_xItemList->insert(xParent.get(), -1, &sName, &sId, nullptr, nullptr, false, pRet);
+ m_xItemList->set_image(*pRet, aImage);
+ if (xParent && !m_xItemList->get_row_expanded(*xParent) && m_xItemList->iter_has_child(*xParent))
+ m_xItemList->expand_row(*xParent);
+ }
+
+ void XFormsPage::AddEntry(const Reference< XPropertySet >& _rEntry, weld::TreeIter* pRet)
+ {
+ if (!pRet)
+ pRet = m_xScratchIter.get();
+
+ OUString aImage(RID_SVXBMP_ELEMENT);
+
+ ItemNode* pNode = new ItemNode( _rEntry );
+ OUString sTemp;
+
+ if ( DGTSubmission == m_eGroup )
+ {
+ try
+ {
+ // ID
+ _rEntry->getPropertyValue( PN_SUBMISSION_ID ) >>= sTemp;
+ OUString sId(weld::toId(pNode));
+ m_xItemList->insert(nullptr, -1, &sTemp, &sId, nullptr, nullptr, false, pRet);
+ m_xItemList->set_image(*pRet, aImage);
+ std::unique_ptr<weld::TreeIter> xRes(m_xItemList->make_iterator());
+ // Action
+ _rEntry->getPropertyValue( PN_SUBMISSION_ACTION ) >>= sTemp;
+ OUString sEntry = SvxResId( RID_STR_DATANAV_SUBM_ACTION ) + sTemp;
+ m_xItemList->insert(pRet, -1, &sEntry, nullptr, nullptr, nullptr, false, xRes.get());
+ m_xItemList->set_image(*xRes, aImage);
+ // Method
+ _rEntry->getPropertyValue( PN_SUBMISSION_METHOD ) >>= sTemp;
+ sEntry = SvxResId( RID_STR_DATANAV_SUBM_METHOD ) +
+ m_aMethodString.toUI( sTemp );
+ m_xItemList->insert(pRet, -1, &sEntry, nullptr, nullptr, nullptr, false, xRes.get());
+ m_xItemList->set_image(*xRes, aImage);
+ // Ref
+ _rEntry->getPropertyValue( PN_SUBMISSION_REF ) >>= sTemp;
+ sEntry = SvxResId( RID_STR_DATANAV_SUBM_REF ) + sTemp;
+ m_xItemList->insert(pRet, -1, &sEntry, nullptr, nullptr, nullptr, false, xRes.get());
+ m_xItemList->set_image(*xRes, aImage);
+ // Bind
+ _rEntry->getPropertyValue( PN_SUBMISSION_BIND ) >>= sTemp;
+ sEntry = SvxResId( RID_STR_DATANAV_SUBM_BIND ) + sTemp;
+ m_xItemList->insert(pRet, -1, &sEntry, nullptr, nullptr, nullptr, false, xRes.get());
+ m_xItemList->set_image(*xRes, aImage);
+ // Replace
+ _rEntry->getPropertyValue( PN_SUBMISSION_REPLACE ) >>= sTemp;
+ sEntry = SvxResId( RID_STR_DATANAV_SUBM_REPLACE ) +
+ m_aReplaceString.toUI( sTemp );
+ m_xItemList->insert(pRet, -1, &sEntry, nullptr, nullptr, nullptr, false, xRes.get());
+ m_xItemList->set_image(*xRes, aImage);
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::AddEntry(Ref)" );
+ }
+ }
+ else // then Binding Page
+ {
+ try
+ {
+ OUString sName;
+ _rEntry->getPropertyValue( PN_BINDING_ID ) >>= sTemp;
+ sName += sTemp + ": ";
+ _rEntry->getPropertyValue( PN_BINDING_EXPR ) >>= sTemp;
+ sName += sTemp;
+
+ OUString sId(weld::toId(pNode));
+ m_xItemList->insert(nullptr, -1, &sName, &sId, nullptr, nullptr, false, pRet);
+ m_xItemList->set_image(*pRet, aImage);
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::AddEntry(Ref)" );
+ }
+ }
+ }
+
+ void XFormsPage::EditEntry( const Reference< XPropertySet >& _rEntry )
+ {
+ if ( DGTSubmission != m_eGroup )
+ return;
+
+ try
+ {
+ std::unique_ptr<weld::TreeIter> xEntry(m_xItemList->make_iterator());
+ if (!m_xItemList->get_selected(xEntry.get()))
+ {
+ SAL_WARN( "svx.form", "corrupt tree" );
+ return;
+ }
+
+ // #i36262# may be called for submission entry *or* for
+ // submission children. If we don't have any children, we
+ // assume the latter case and use the parent
+ if (!m_xItemList->iter_has_child(*xEntry))
+ m_xItemList->iter_parent(*xEntry);
+
+ OUString sTemp;
+ _rEntry->getPropertyValue( PN_SUBMISSION_ID ) >>= sTemp;
+ m_xItemList->set_text(*xEntry, sTemp);
+
+ _rEntry->getPropertyValue( PN_SUBMISSION_BIND ) >>= sTemp;
+ OUString sEntry = SvxResId( RID_STR_DATANAV_SUBM_BIND ) + sTemp;
+ if (!m_xItemList->iter_children(*xEntry))
+ {
+ SAL_WARN( "svx.form", "corrupt tree" );
+ return;
+ }
+ m_xItemList->set_text(*xEntry, sEntry);
+ _rEntry->getPropertyValue( PN_SUBMISSION_REF ) >>= sTemp;
+ sEntry = SvxResId( RID_STR_DATANAV_SUBM_REF ) + sTemp;
+ if (!m_xItemList->iter_next_sibling(*xEntry))
+ {
+ SAL_WARN( "svx.form", "corrupt tree" );
+ return;
+ }
+ m_xItemList->set_text(*xEntry, sEntry);
+ _rEntry->getPropertyValue( PN_SUBMISSION_ACTION ) >>= sTemp;
+ sEntry = SvxResId( RID_STR_DATANAV_SUBM_ACTION ) + sTemp;
+ if (!m_xItemList->iter_next_sibling(*xEntry))
+ {
+ SAL_WARN( "svx.form", "corrupt tree" );
+ return;
+ }
+ _rEntry->getPropertyValue( PN_SUBMISSION_METHOD ) >>= sTemp;
+ sEntry = SvxResId( RID_STR_DATANAV_SUBM_METHOD ) +
+ m_aMethodString.toUI( sTemp );
+ if (!m_xItemList->iter_next_sibling(*xEntry))
+ {
+ SAL_WARN( "svx.form", "corrupt tree" );
+ return;
+ }
+ m_xItemList->set_text(*xEntry, sEntry);
+ _rEntry->getPropertyValue( PN_SUBMISSION_REPLACE ) >>= sTemp;
+ sEntry = SvxResId( RID_STR_DATANAV_SUBM_REPLACE ) +
+ m_aReplaceString.toUI( sTemp );
+ if (!m_xItemList->iter_next_sibling(*xEntry))
+ {
+ SAL_WARN( "svx.form", "corrupt tree" );
+ return;
+ }
+ m_xItemList->set_text(*xEntry, sEntry);
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::EditEntry()" );
+ }
+ }
+
+ bool XFormsPage::RemoveEntry()
+ {
+ bool bRet = false;
+
+ std::unique_ptr<weld::TreeIter> xEntry(m_xItemList->make_iterator());
+ bool bEntry = m_xItemList->get_selected(xEntry.get());
+ if ( bEntry &&
+ ( DGTInstance != m_eGroup || m_xItemList->get_iter_depth(*xEntry) ) )
+ {
+ Reference< css::xforms::XModel > xModel( m_xUIHelper, UNO_QUERY );
+ DBG_ASSERT( xModel.is(), "XFormsPage::RemoveEntry(): no model" );
+ ItemNode* pNode = weld::fromId<ItemNode*>(m_xItemList->get_id(*xEntry));
+ DBG_ASSERT( pNode, "XFormsPage::RemoveEntry(): no node" );
+
+ if ( DGTInstance == m_eGroup )
+ {
+ try
+ {
+ DBG_ASSERT( pNode->m_xNode.is(), "XFormsPage::RemoveEntry(): no XNode" );
+ css::xml::dom::NodeType eChildType = pNode->m_xNode->getNodeType();
+ bool bIsElement = ( eChildType == css::xml::dom::NodeType_ELEMENT_NODE );
+ TranslateId pResId = bIsElement ? RID_STR_QRY_REMOVE_ELEMENT : RID_STR_QRY_REMOVE_ATTRIBUTE;
+ OUString sVar = bIsElement ? OUString(ELEMENTNAME) : OUString(ATTRIBUTENAME);
+ std::unique_ptr<weld::MessageDialog> xQBox(Application::CreateMessageDialog(m_pNaviWin->GetFrameWeld(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ SvxResId(pResId)));
+ OUString sMessText = xQBox->get_primary_text();
+ sMessText = sMessText.replaceFirst(
+ sVar, m_xUIHelper->getNodeDisplayName( pNode->m_xNode, false ) );
+ xQBox->set_primary_text(sMessText);
+ if (xQBox->run() == RET_YES)
+ {
+ std::unique_ptr<weld::TreeIter> xParent(m_xItemList->make_iterator(xEntry.get()));
+ bool bParent = m_xItemList->iter_parent(*xParent); (void)bParent;
+ assert(bParent && "XFormsPage::RemoveEntry(): no parent entry");
+ ItemNode* pParentNode = weld::fromId<ItemNode*>(m_xItemList->get_id(*xParent));
+ DBG_ASSERT( pParentNode && pParentNode->m_xNode.is(), "XFormsPage::RemoveEntry(): no parent XNode" );
+
+ Reference< css::xml::dom::XNode > xPNode;
+ Reference< css::xml::dom::XNode > xNode =
+ pParentNode->m_xNode->removeChild( pNode->m_xNode );
+ if ( xNode.is() )
+ xPNode = xNode->getParentNode();
+ DBG_ASSERT( !xPNode.is(), "XFormsPage::RemoveEntry(): node not removed" );
+ bRet = true;
+ }
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::RemoveEntry()" );
+ }
+ }
+ else
+ {
+ DBG_ASSERT( pNode->m_xPropSet.is(), "XFormsPage::RemoveEntry(): no propset" );
+ bool bSubmission = ( DGTSubmission == m_eGroup );
+ TranslateId pResId = bSubmission ? RID_STR_QRY_REMOVE_SUBMISSION : RID_STR_QRY_REMOVE_BINDING;
+ OUString sProperty = bSubmission ? PN_SUBMISSION_ID : PN_BINDING_ID;
+ OUString sSearch = bSubmission ? OUString(SUBMISSIONNAME) : OUString(BINDINGNAME);
+ OUString sName;
+ try
+ {
+ pNode->m_xPropSet->getPropertyValue( sProperty ) >>= sName;
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::RemoveEntry()" );
+ }
+ std::unique_ptr<weld::MessageDialog> xQBox(Application::CreateMessageDialog(m_pNaviWin->GetFrameWeld(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ SvxResId(pResId)));
+ OUString sMessText = xQBox->get_primary_text();
+ sMessText = sMessText.replaceFirst( sSearch, sName);
+ xQBox->set_primary_text(sMessText);
+ if (xQBox->run() == RET_YES)
+ {
+ try
+ {
+ if ( bSubmission )
+ xModel->getSubmissions()->remove( Any( pNode->m_xPropSet ) );
+ else // then Binding Page
+ xModel->getBindings()->remove( Any( pNode->m_xPropSet ) );
+ bRet = true;
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::RemoveEntry()" );
+ }
+ }
+ }
+
+ if (bRet)
+ {
+ m_xItemList->remove(*xEntry);
+ delete pNode;
+ }
+ }
+
+ return bRet;
+ }
+
+ IMPL_LINK(XFormsPage, KeyInputHdl, const KeyEvent&, rKEvt, bool)
+ {
+ bool bHandled = false;
+
+ sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
+ if (nCode == KEY_DELETE)
+ bHandled = DoMenuAction(u"delete");
+
+ return bHandled;
+ }
+
+ OUString XFormsPage::SetModel( const Reference< css::xforms::XModel >& _xModel, int _nPagePos )
+ {
+ DBG_ASSERT( _xModel.is(), "XFormsPage::SetModel(): invalid model" );
+
+ m_xUIHelper.set( _xModel, UNO_QUERY );
+ OUString sRet;
+ m_bHasModel = true;
+
+ switch ( m_eGroup )
+ {
+ case DGTInstance :
+ {
+ DBG_ASSERT( _nPagePos != -1, "XFormsPage::SetModel(): invalid page position" );
+ try
+ {
+ Reference< XContainer > xContainer( _xModel->getInstances(), UNO_QUERY );
+ if ( xContainer.is() )
+ m_pNaviWin->AddContainerBroadcaster( xContainer );
+
+ Reference< XEnumerationAccess > xNumAccess = _xModel->getInstances();
+ if ( xNumAccess.is() )
+ {
+ Reference < XEnumeration > xNum = xNumAccess->createEnumeration();
+ if ( xNum.is() && xNum->hasMoreElements() )
+ {
+ int nIter = 0;
+ while ( xNum->hasMoreElements() )
+ {
+ if ( nIter == _nPagePos )
+ {
+ Sequence< PropertyValue > xPropSeq;
+ Any aAny = xNum->nextElement();
+ if ( aAny >>= xPropSeq )
+ sRet = LoadInstance(xPropSeq);
+ else
+ {
+ SAL_WARN( "svx.form", "XFormsPage::SetModel(): invalid instance" );
+ }
+ break;
+ }
+ else
+ {
+ xNum->nextElement();
+ ++nIter;
+ }
+ }
+ }
+ }
+ }
+ catch( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::SetModel()" );
+ }
+ break;
+ }
+
+ case DGTSubmission :
+ {
+ DBG_ASSERT( _nPagePos == -1, "XFormsPage::SetModel(): invalid page position" );
+ try
+ {
+ Reference< XContainer > xContainer( _xModel->getSubmissions(), UNO_QUERY );
+ if ( xContainer.is() )
+ m_pNaviWin->AddContainerBroadcaster( xContainer );
+
+ Reference< XEnumerationAccess > xNumAccess = _xModel->getSubmissions();
+ if ( xNumAccess.is() )
+ {
+ Reference < XEnumeration > xNum = xNumAccess->createEnumeration();
+ if ( xNum.is() && xNum->hasMoreElements() )
+ {
+ while ( xNum->hasMoreElements() )
+ {
+ Reference< XPropertySet > xPropSet;
+ Any aAny = xNum->nextElement();
+ if ( aAny >>= xPropSet )
+ AddEntry( xPropSet );
+ }
+ }
+ }
+ }
+ catch( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::SetModel()" );
+ }
+ break;
+ }
+
+ case DGTBinding :
+ {
+ DBG_ASSERT( _nPagePos == -1, "XFormsPage::SetModel(): invalid page position" );
+ try
+ {
+ Reference< XContainer > xContainer( _xModel->getBindings(), UNO_QUERY );
+ if ( xContainer.is() )
+ m_pNaviWin->AddContainerBroadcaster( xContainer );
+
+ Reference< XEnumerationAccess > xNumAccess = _xModel->getBindings();
+ if ( xNumAccess.is() )
+ {
+ Reference < XEnumeration > xNum = xNumAccess->createEnumeration();
+ if ( xNum.is() && xNum->hasMoreElements() )
+ {
+ OUString aImage(RID_SVXBMP_ELEMENT);
+ std::unique_ptr<weld::TreeIter> xRes(m_xItemList->make_iterator());
+ while ( xNum->hasMoreElements() )
+ {
+ Reference< XPropertySet > xPropSet;
+ Any aAny = xNum->nextElement();
+ if ( aAny >>= xPropSet )
+ {
+ OUString sEntry;
+ OUString sTemp;
+ xPropSet->getPropertyValue( PN_BINDING_ID ) >>= sTemp;
+ sEntry += sTemp + ": ";
+ xPropSet->getPropertyValue( PN_BINDING_EXPR ) >>= sTemp;
+ sEntry += sTemp;
+
+ ItemNode* pNode = new ItemNode( xPropSet );
+
+ OUString sId(weld::toId(pNode));
+ m_xItemList->insert(nullptr, -1, &sEntry, &sId, nullptr, nullptr, false, xRes.get());
+ m_xItemList->set_image(*xRes, aImage);
+ }
+ }
+ }
+ }
+ }
+ catch( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::SetModel()" );
+ }
+ break;
+ }
+ default:
+ OSL_FAIL( "XFormsPage::SetModel: unknown group!" );
+ break;
+ }
+
+ EnableMenuItems();
+
+ return sRet;
+ }
+
+ void XFormsPage::ClearModel()
+ {
+ m_bHasModel = false;
+ DeleteAndClearTree();
+ }
+
+ OUString XFormsPage::LoadInstance(const Sequence< PropertyValue >& _xPropSeq)
+ {
+ OUString sRet;
+ OUString sTemp;
+ OUString sInstModel = PN_INSTANCE_MODEL;
+ OUString sInstName = PN_INSTANCE_ID;
+ OUString sInstURL = PN_INSTANCE_URL;
+ for ( const PropertyValue& rProp : _xPropSeq )
+ {
+ if ( sInstModel == rProp.Name )
+ {
+ Reference< css::xml::dom::XNode > xRoot;
+ if ( rProp.Value >>= xRoot )
+ {
+ try
+ {
+ Reference< XEventTarget > xTarget( xRoot, UNO_QUERY );
+ if ( xTarget.is() )
+ m_pNaviWin->AddEventBroadcaster( xTarget );
+
+ OUString sNodeName =
+ m_xUIHelper->getNodeDisplayName( xRoot, m_pNaviWin->IsShowDetails() );
+ if ( sNodeName.isEmpty() )
+ sNodeName = xRoot->getNodeName();
+ if ( xRoot->hasChildNodes() )
+ AddChildren(nullptr, xRoot);
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::LoadInstance()" );
+ }
+ }
+ }
+ else if ( sInstName == rProp.Name && ( rProp.Value >>= sTemp ) )
+ m_sInstanceName = sRet = sTemp;
+ else if ( sInstURL == rProp.Name && ( rProp.Value >>= sTemp ) )
+ m_sInstanceURL = sTemp;
+ }
+
+ return sRet;
+ }
+
+ bool XFormsPage::DoMenuAction(std::u16string_view rMenuID)
+ {
+ return DoToolBoxAction(rMenuID);
+ }
+
+ void XFormsPage::SetMenuEntrySensitive(const OUString& rIdent, bool bSensitive)
+ {
+ if (m_aRemovedMenuEntries.find(rIdent) != m_aRemovedMenuEntries.end())
+ return;
+ m_xMenu->set_sensitive(rIdent, bSensitive);
+ }
+
+ void XFormsPage::EnableMenuItems()
+ {
+ bool bEnableAdd = false;
+ bool bEnableEdit = false;
+ bool bEnableRemove = false;
+
+ std::unique_ptr<weld::TreeIter> xEntry(m_xItemList->make_iterator());
+ bool bEntry = m_xItemList->get_selected(xEntry.get());
+ if (bEntry)
+ {
+ bEnableAdd = true;
+ bool bSubmitChild = false;
+ if (DGTSubmission == m_eGroup && m_xItemList->get_iter_depth(*xEntry))
+ {
+ m_xItemList->iter_parent(*xEntry);
+ bSubmitChild = true;
+ }
+ ItemNode* pNode = weld::fromId<ItemNode*>(m_xItemList->get_id(*xEntry));
+ if ( pNode && ( pNode->m_xNode.is() || pNode->m_xPropSet.is() ) )
+ {
+ bEnableEdit = true;
+ bEnableRemove = !bSubmitChild;
+ if ( DGTInstance == m_eGroup && !m_xItemList->get_iter_depth(*xEntry) )
+ bEnableRemove = false;
+ if ( pNode->m_xNode.is() )
+ {
+ try
+ {
+ css::xml::dom::NodeType eChildType = pNode->m_xNode->getNodeType();
+ if ( eChildType != css::xml::dom::NodeType_ELEMENT_NODE
+ && eChildType != css::xml::dom::NodeType_DOCUMENT_NODE )
+ {
+ bEnableAdd = false;
+ }
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::EnableMenuItems()" );
+ }
+ }
+ }
+ }
+ else if ( m_eGroup != DGTInstance )
+ bEnableAdd = true;
+
+ m_xToolBox->set_item_sensitive("additem", bEnableAdd);
+ m_xToolBox->set_item_sensitive("addelement", bEnableAdd);
+ m_xToolBox->set_item_sensitive("addattribute", bEnableAdd);
+ m_xToolBox->set_item_sensitive("edit", bEnableEdit);
+ m_xToolBox->set_item_sensitive("delete", bEnableRemove);
+
+ if (m_xMenu)
+ {
+ SetMenuEntrySensitive("additem", bEnableAdd);
+ SetMenuEntrySensitive("addelement", bEnableAdd);
+ SetMenuEntrySensitive("addattribute", bEnableAdd);
+ SetMenuEntrySensitive("edit", bEnableEdit);
+ SetMenuEntrySensitive("delete", bEnableRemove);
+ }
+ if ( DGTInstance != m_eGroup )
+ return;
+
+ TranslateId pResId1 = RID_STR_DATANAV_EDIT_ELEMENT;
+ TranslateId pResId2 = RID_STR_DATANAV_REMOVE_ELEMENT;
+ if (bEntry)
+ {
+ ItemNode* pNode = weld::fromId<ItemNode*>(m_xItemList->get_id(*xEntry));
+ if ( pNode && pNode->m_xNode.is() )
+ {
+ try
+ {
+ css::xml::dom::NodeType eChildType = pNode->m_xNode->getNodeType();
+ if ( eChildType == css::xml::dom::NodeType_ATTRIBUTE_NODE )
+ {
+ pResId1 = RID_STR_DATANAV_EDIT_ATTRIBUTE;
+ pResId2 = RID_STR_DATANAV_REMOVE_ATTRIBUTE;
+ }
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::EnableMenuItems()" );
+ }
+ }
+ }
+ m_xToolBox->set_item_label("edit", SvxResId(pResId1));
+ m_xToolBox->set_item_label("delete", SvxResId(pResId2));
+ if (m_xMenu)
+ {
+ m_xMenu->set_label("edit", SvxResId( pResId1 ) );
+ m_xMenu->set_label("delete", SvxResId( pResId2 ) );
+ }
+ }
+
+ DataNavigatorWindow::DataNavigatorWindow(vcl::Window* pParent, weld::Builder& rBuilder, SfxBindings const * pBindings)
+ : m_xParent(pParent)
+ , m_xModelsBox(rBuilder.weld_combo_box("modelslist"))
+ , m_xModelBtn(rBuilder.weld_menu_button("modelsbutton"))
+ , m_xTabCtrl(rBuilder.weld_notebook("tabcontrol"))
+ , m_xInstanceBtn(rBuilder.weld_menu_button("instances"))
+ , m_nLastSelectedPos(-1)
+ , m_bShowDetails(false)
+ , m_bIsNotifyDisabled(false)
+ , m_aUpdateTimer("svx DataNavigatorWindow m_aUpdateTimer")
+ , m_xDataListener(new DataListener(this))
+ {
+ // handler
+ m_xModelsBox->connect_changed( LINK( this, DataNavigatorWindow, ModelSelectListBoxHdl ) );
+ Link<const OUString&, void> aLink1 = LINK( this, DataNavigatorWindow, MenuSelectHdl );
+ m_xModelBtn->connect_selected(aLink1);
+ m_xInstanceBtn->connect_selected(aLink1);
+ Link<weld::Toggleable&,void> aLink2 = LINK( this, DataNavigatorWindow, MenuActivateHdl );
+ m_xModelBtn->connect_toggled( aLink2 );
+ m_xInstanceBtn->connect_toggled( aLink2 );
+ m_xTabCtrl->connect_enter_page( LINK( this, DataNavigatorWindow, ActivatePageHdl ) );
+ m_aUpdateTimer.SetTimeout( 2000 );
+ m_aUpdateTimer.SetInvokeHandler( LINK( this, DataNavigatorWindow, UpdateHdl ) );
+
+ // init tabcontrol
+ OUString sPageId("instance");
+ SvtViewOptions aViewOpt( EViewType::TabDialog, CFGNAME_DATANAVIGATOR );
+ if ( aViewOpt.Exists() )
+ {
+ OUString sNewPageId = aViewOpt.GetPageID();
+ if (m_xTabCtrl->get_page_index(sNewPageId) != -1)
+ sPageId = sNewPageId;
+ aViewOpt.GetUserItem(CFGNAME_SHOWDETAILS) >>= m_bShowDetails;
+ }
+
+ m_xInstanceBtn->set_item_active("instancesdetails", m_bShowDetails);
+
+ m_xTabCtrl->set_current_page(sPageId);
+ ActivatePageHdl(sPageId);
+
+ // get our frame
+ DBG_ASSERT( pBindings != nullptr,
+ "DataNavigatorWindow::LoadModels(): no SfxBindings; can't get frame" );
+ m_xFrame = pBindings->GetDispatcher()->GetFrame()->GetFrame().GetFrameInterface();
+ DBG_ASSERT( m_xFrame.is(), "DataNavigatorWindow::LoadModels(): no frame" );
+ // add frameaction listener
+ Reference< XFrameActionListener > xListener = m_xDataListener;
+ m_xFrame->addFrameActionListener( xListener );
+
+ // load xforms models of the current document
+ LoadModels();
+
+ // tdf#154322 select the first entry of the current page by default
+ if (XFormsPage* pPage = GetPage(sPageId))
+ pPage->SelectFirstEntry();
+ }
+
+ DataNavigatorWindow::~DataNavigatorWindow()
+ {
+ Reference< XFrameActionListener > xListener = m_xDataListener;
+ m_xFrame->removeFrameActionListener( xListener );
+
+ SvtViewOptions aViewOpt( EViewType::TabDialog, CFGNAME_DATANAVIGATOR );
+ aViewOpt.SetPageID(m_xTabCtrl->get_current_page_ident());
+ aViewOpt.SetUserItem(CFGNAME_SHOWDETAILS, Any(m_bShowDetails));
+
+ m_xInstPage.reset();
+ m_xSubmissionPage.reset();
+ m_xBindingPage.reset();
+
+ sal_Int32 i, nCount = m_aPageList.size();
+ for ( i = 0; i < nCount; ++i )
+ m_aPageList[i].reset();
+ m_aPageList.clear();
+
+ RemoveBroadcaster();
+ m_xDataListener.clear();
+ }
+
+ IMPL_LINK( DataNavigatorWindow, ModelSelectListBoxHdl, weld::ComboBox&, rBox, void )
+ {
+ ModelSelectHdl(&rBox);
+ }
+
+ void DataNavigatorWindow::ModelSelectHdl(const weld::ComboBox* pBox)
+ {
+ sal_Int32 nPos = m_xModelsBox->get_active();
+ // pBox == NULL, if you want to force a new fill.
+ if ( nPos != m_nLastSelectedPos || !pBox )
+ {
+ m_nLastSelectedPos = nPos;
+ ClearAllPageModels( pBox != nullptr );
+ InitPages();
+ SetPageModel(GetCurrentPage());
+ }
+ }
+
+ IMPL_LINK(DataNavigatorWindow, MenuSelectHdl, const OUString&, rIdent, void)
+ {
+ bool bIsDocModified = false;
+ Reference< css::xforms::XFormsUIHelper1 > xUIHelper;
+ sal_Int32 nSelectedPos = m_xModelsBox->get_active();
+ OUString sSelectedModel(m_xModelsBox->get_text(nSelectedPos));
+ Reference< css::xforms::XModel > xModel;
+ try
+ {
+ Any aAny = m_xDataContainer->getByName( sSelectedModel );
+ if ( aAny >>= xModel )
+ xUIHelper.set( xModel, UNO_QUERY );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "DataNavigatorWindow::MenuSelectHdl()" );
+ }
+ DBG_ASSERT( xUIHelper.is(), "DataNavigatorWindow::MenuSelectHdl(): no UIHelper" );
+
+ m_bIsNotifyDisabled = true;
+
+ if (rIdent == "modelsadd")
+ {
+ AddModelDialog aDlg(GetFrameWeld(), false);
+ bool bShowDialog = true;
+ while ( bShowDialog )
+ {
+ bShowDialog = false;
+ if (aDlg.run() == RET_OK)
+ {
+ OUString sNewName = aDlg.GetName();
+ bool bDocumentData = aDlg.GetModifyDoc();
+
+ if (m_xModelsBox->find_text(sNewName) != -1)
+ {
+ // error: model name already exists
+ std::unique_ptr<weld::MessageDialog> xErrBox(Application::CreateMessageDialog(GetFrameWeld(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ SvxResId(RID_STR_DOUBLE_MODELNAME)));
+ xErrBox->set_primary_text(xErrBox->get_primary_text().replaceFirst(MSG_VARIABLE, sNewName));
+ xErrBox->run();
+ bShowDialog = true;
+ }
+ else
+ {
+ try
+ {
+ // add new model to frame model
+ Reference< css::xforms::XModel > xNewModel(
+ xUIHelper->newModel( m_xFrameModel, sNewName ), UNO_SET_THROW );
+
+ Reference< XPropertySet > xModelProps( xNewModel, UNO_QUERY_THROW );
+ xModelProps->setPropertyValue("ExternalData", Any( !bDocumentData ) );
+
+ m_xModelsBox->append_text(sNewName);
+ m_xModelsBox->set_active(m_xModelsBox->get_count() - 1);
+ ModelSelectHdl(m_xModelsBox.get());
+ bIsDocModified = true;
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "DataNavigatorWindow::MenuSelectHdl()" );
+ }
+ }
+ }
+ }
+ }
+ else if (rIdent == "modelsedit")
+ {
+ AddModelDialog aDlg(GetFrameWeld(), true);
+ aDlg.SetName( sSelectedModel );
+
+ bool bDocumentData( false );
+ try
+ {
+ Reference< css::xforms::XFormsSupplier > xFormsSupp( m_xFrameModel, UNO_QUERY_THROW );
+ Reference< XNameContainer > xXForms( xFormsSupp->getXForms(), UNO_SET_THROW );
+ Reference< XPropertySet > xModelProps( xXForms->getByName( sSelectedModel ), UNO_QUERY_THROW );
+ bool bExternalData = false;
+ OSL_VERIFY( xModelProps->getPropertyValue( "ExternalData" ) >>= bExternalData );
+ bDocumentData = !bExternalData;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ aDlg.SetModifyDoc( bDocumentData );
+
+ if (aDlg.run() == RET_OK)
+ {
+ if ( aDlg.GetModifyDoc() != bDocumentData )
+ {
+ bDocumentData = aDlg.GetModifyDoc();
+ try
+ {
+ Reference< css::xforms::XFormsSupplier > xFormsSupp( m_xFrameModel, UNO_QUERY_THROW );
+ Reference< XNameContainer > xXForms( xFormsSupp->getXForms(), UNO_SET_THROW );
+ Reference< XPropertySet > xModelProps( xXForms->getByName( sSelectedModel ), UNO_QUERY_THROW );
+ xModelProps->setPropertyValue( "ExternalData", Any( !bDocumentData ) );
+ bIsDocModified = true;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+ OUString sNewName = aDlg.GetName();
+ if ( !sNewName.isEmpty() && ( sNewName != sSelectedModel ) )
+ {
+ try
+ {
+ xUIHelper->renameModel( m_xFrameModel, sSelectedModel, sNewName );
+
+ m_xModelsBox->remove(nSelectedPos);
+ m_xModelsBox->append_text(sNewName);
+ m_xModelsBox->set_active(m_xModelsBox->get_count() - 1);
+ bIsDocModified = true;
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "DataNavigatorWindow::MenuSelectHdl()" );
+ }
+ }
+ }
+ }
+ else if (rIdent == "modelsremove")
+ {
+ std::unique_ptr<weld::MessageDialog> xQBox(Application::CreateMessageDialog(GetFrameWeld(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ SvxResId( RID_STR_QRY_REMOVE_MODEL)));
+ OUString sText = xQBox->get_primary_text();
+ sText = sText.replaceFirst( MODELNAME, sSelectedModel );
+ xQBox->set_primary_text(sText);
+ if (xQBox->run() == RET_YES)
+ {
+ try
+ {
+ xUIHelper->removeModel( m_xFrameModel, sSelectedModel );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "DataNavigatorWindow::MenuSelectHdl()" );
+ }
+ m_xModelsBox->remove(nSelectedPos);
+ if (m_xModelsBox->get_count() <= nSelectedPos)
+ nSelectedPos = m_xModelsBox->get_count() - 1;
+ m_xModelsBox->set_active(nSelectedPos);
+ ModelSelectHdl(m_xModelsBox.get());
+ bIsDocModified = true;
+ }
+ }
+ else if (rIdent == "instancesadd")
+ {
+ AddInstanceDialog aDlg(GetFrameWeld(), false);
+ if (aDlg.run() == RET_OK)
+ {
+ OUString sPageId = GetNewPageId(); // ModelSelectHdl will cause a page of this id to be created
+
+ OUString sName = aDlg.GetName();
+ if (sName.isEmpty())
+ {
+ SAL_WARN( "svx.form", "DataNavigatorWindow::CreateInstancePage(): instance without name" );
+ sName = "untitled";
+ }
+
+ OUString sURL = aDlg.GetURL();
+ bool bLinkOnce = aDlg.IsLinkInstance();
+ try
+ {
+ xUIHelper->newInstance( sName, sURL, !bLinkOnce );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "DataNavigatorWindow::MenuSelectHdl()" );
+ }
+ ModelSelectHdl( nullptr );
+
+ XFormsPage* pPage = GetPage(sPageId);
+ pPage->SetInstanceName(sName);
+ pPage->SetInstanceURL(sURL);
+ pPage->SetLinkOnce(bLinkOnce);
+ ActivatePageHdl(sPageId);
+
+ bIsDocModified = true;
+ }
+ }
+ else if (rIdent == "instancesedit")
+ {
+ OUString sIdent = GetCurrentPage();
+ XFormsPage* pPage = GetPage(sIdent);
+ if ( pPage )
+ {
+ AddInstanceDialog aDlg(GetFrameWeld(), true);
+ aDlg.SetName( pPage->GetInstanceName() );
+ aDlg.SetURL( pPage->GetInstanceURL() );
+ aDlg.SetLinkInstance( pPage->GetLinkOnce() );
+ OUString sOldName = aDlg.GetName();
+ if (aDlg.run() == RET_OK)
+ {
+ OUString sNewName = aDlg.GetName();
+ OUString sURL = aDlg.GetURL();
+ bool bLinkOnce = aDlg.IsLinkInstance();
+ try
+ {
+ xUIHelper->renameInstance( sOldName,
+ sNewName,
+ sURL,
+ !bLinkOnce );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "DataNavigatorWindow::MenuSelectHdl()" );
+ }
+ pPage->SetInstanceName(sNewName);
+ pPage->SetInstanceURL(sURL);
+ pPage->SetLinkOnce(bLinkOnce);
+ m_xTabCtrl->set_tab_label_text(sIdent, sNewName);
+ bIsDocModified = true;
+ }
+ }
+ }
+ else if (rIdent == "instancesremove")
+ {
+ OUString sIdent = GetCurrentPage();
+ XFormsPage* pPage = GetPage(sIdent);
+ if (pPage)
+ {
+ OUString sInstName = pPage->GetInstanceName();
+ std::unique_ptr<weld::MessageDialog> xQBox(Application::CreateMessageDialog(GetFrameWeld(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ SvxResId(RID_STR_QRY_REMOVE_INSTANCE)));
+ OUString sMessText = xQBox->get_primary_text();
+ sMessText = sMessText.replaceFirst( INSTANCENAME, sInstName );
+ xQBox->set_primary_text(sMessText);
+ if (xQBox->run() == RET_YES)
+ {
+ bool bDoRemove = false;
+ if (IsAdditionalPage(sIdent))
+ {
+ auto aPageListEnd = m_aPageList.end();
+ auto aFoundPage = std::find_if(m_aPageList.begin(), aPageListEnd,
+ [pPage](const auto&elem) { return elem.get() == pPage; });
+ if ( aFoundPage != aPageListEnd )
+ {
+ m_aPageList.erase( aFoundPage );
+ bDoRemove = true;
+ }
+ }
+ else
+ {
+ m_xInstPage.reset();
+ bDoRemove = true;
+ }
+
+ if ( bDoRemove )
+ {
+ try
+ {
+ xUIHelper->removeInstance( sInstName );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "DataNavigatorWindow::MenuSelectHdl()" );
+ }
+ m_xTabCtrl->remove_page(sIdent);
+ m_xTabCtrl->set_current_page("instance");
+ ModelSelectHdl( nullptr );
+ bIsDocModified = true;
+ }
+ }
+ }
+ }
+ else if (rIdent == "instancesdetails")
+ {
+ m_bShowDetails = !m_bShowDetails;
+ m_xInstanceBtn->set_item_active("instancesdetails", m_bShowDetails);
+ ModelSelectHdl(m_xModelsBox.get());
+ }
+ else
+ {
+ SAL_WARN( "svx.form", "DataNavigatorWindow::MenuSelectHdl(): wrong menu item" );
+ }
+
+ m_bIsNotifyDisabled = false;
+
+ if ( bIsDocModified )
+ SetDocModified();
+ }
+
+ bool DataNavigatorWindow::IsAdditionalPage(std::u16string_view rIdent)
+ {
+ return o3tl::starts_with(rIdent, u"additional");
+ }
+
+ IMPL_LINK( DataNavigatorWindow, MenuActivateHdl, weld::Toggleable&, rBtn, void )
+ {
+ if (m_xInstanceBtn.get() == &rBtn)
+ {
+ OUString sIdent(m_xTabCtrl->get_current_page_ident());
+ bool bIsInstPage = (IsAdditionalPage(sIdent) || sIdent == "instance");
+ m_xInstanceBtn->set_item_sensitive( "instancesedit", bIsInstPage );
+ m_xInstanceBtn->set_item_sensitive( "instancesremove",
+ bIsInstPage && m_xTabCtrl->get_n_pages() > MIN_PAGE_COUNT );
+ m_xInstanceBtn->set_item_sensitive( "instancesdetails", bIsInstPage );
+ }
+ else if (m_xModelBtn.get() == &rBtn)
+ {
+ // we need at least one model!
+ m_xModelBtn->set_item_sensitive("modelsremove", m_xModelsBox->get_count() > 1 );
+ }
+ else
+ {
+ SAL_WARN( "svx.form", "DataNavigatorWindow::MenuActivateHdl(): wrong button" );
+ }
+ }
+
+ IMPL_LINK(DataNavigatorWindow, ActivatePageHdl, const OUString&, rIdent, void)
+ {
+ XFormsPage* pPage = GetPage(rIdent);
+ if (!pPage)
+ return;
+ if (m_xDataContainer.is() && !pPage->HasModel())
+ SetPageModel(rIdent);
+ }
+
+ IMPL_LINK_NOARG(DataNavigatorWindow, UpdateHdl, Timer *, void)
+ {
+ ModelSelectHdl( nullptr );
+ }
+
+ XFormsPage* DataNavigatorWindow::GetPage(const OUString& rCurId)
+ {
+ XFormsPage* pPage = nullptr;
+ if (rCurId == "submissions")
+ {
+ if (!m_xSubmissionPage)
+ m_xSubmissionPage.reset(new XFormsPage(m_xTabCtrl->get_page(rCurId), this, DGTSubmission));
+ pPage = m_xSubmissionPage.get();
+ }
+ else if (rCurId == "bindings")
+ {
+ if (!m_xBindingPage)
+ m_xBindingPage.reset(new XFormsPage(m_xTabCtrl->get_page(rCurId), this, DGTBinding));
+ pPage = m_xBindingPage.get();
+ }
+ else if (rCurId == "instance")
+ {
+ if (!m_xInstPage)
+ m_xInstPage.reset(new XFormsPage(m_xTabCtrl->get_page(rCurId), this, DGTInstance));
+ pPage = m_xInstPage.get();
+ }
+ else
+ {
+ sal_uInt16 nPos = m_xTabCtrl->get_page_index(rCurId);
+ if (HasFirstInstancePage() && nPos > 0)
+ nPos--;
+ if (m_aPageList.size() > nPos)
+ pPage = m_aPageList[nPos].get();
+ else
+ {
+ m_aPageList.emplace_back(std::make_unique<XFormsPage>(m_xTabCtrl->get_page(rCurId), this, DGTInstance));
+ pPage = m_aPageList.back().get();
+ }
+ }
+ return pPage;
+ }
+
+ OUString DataNavigatorWindow::GetCurrentPage() const
+ {
+ return m_xTabCtrl->get_current_page_ident();
+ }
+
+ void DataNavigatorWindow::LoadModels()
+ {
+ if ( !m_xFrameModel.is() )
+ {
+ // get model of active frame
+ Reference< XController > xCtrl = m_xFrame->getController();
+ if ( xCtrl.is() )
+ {
+ try
+ {
+ m_xFrameModel = xCtrl->getModel();
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "DataNavigatorWindow::LoadModels()" );
+ }
+ }
+ }
+
+ if ( m_xFrameModel.is() )
+ {
+ try
+ {
+ Reference< css::xforms::XFormsSupplier > xFormsSupp( m_xFrameModel, UNO_QUERY );
+ if ( xFormsSupp.is() )
+ {
+ Reference< XNameContainer > xContainer = xFormsSupp->getXForms();
+ if ( xContainer.is() )
+ {
+ m_xDataContainer = xContainer;
+ const Sequence< OUString > aNameList = m_xDataContainer->getElementNames();
+ for ( const OUString& rName : aNameList )
+ {
+ Any aAny = m_xDataContainer->getByName( rName );
+ Reference< css::xforms::XModel > xFormsModel;
+ if ( aAny >>= xFormsModel )
+ m_xModelsBox->append_text(xFormsModel->getID());
+ }
+ }
+ }
+ }
+ catch( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "DataNavigatorWindow::LoadModels()" );
+ }
+ }
+
+ if (m_xModelsBox->get_count() > 0)
+ {
+ m_xModelsBox->set_active(0);
+ ModelSelectHdl(m_xModelsBox.get());
+ }
+ }
+
+ void DataNavigatorWindow::SetPageModel(const OUString& rIdent)
+ {
+ OUString sModel(m_xModelsBox->get_active_text());
+ try
+ {
+ Any aAny = m_xDataContainer->getByName( sModel );
+ Reference< css::xforms::XModel > xFormsModel;
+ if ( aAny >>= xFormsModel )
+ {
+ int nPagePos = -1;
+ XFormsPage* pPage = GetPage(rIdent);
+ DBG_ASSERT( pPage, "DataNavigatorWindow::SetPageModel(): no page" );
+ if (IsAdditionalPage(rIdent) || rIdent == "instance")
+ {
+ // instance page
+ nPagePos = m_xTabCtrl->get_page_index(rIdent);
+ }
+ m_bIsNotifyDisabled = true;
+ OUString sText = pPage->SetModel( xFormsModel, nPagePos );
+ m_bIsNotifyDisabled = false;
+ if (!sText.isEmpty())
+ m_xTabCtrl->set_tab_label_text(rIdent, sText);
+ }
+ }
+ catch (const NoSuchElementException& )
+ {
+ SAL_WARN( "svx.form", "DataNavigatorWindow::SetPageModel(): no such element" );
+ }
+ catch( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "DataNavigatorWindow::SetPageModel()" );
+ }
+ }
+
+ void DataNavigatorWindow::InitPages()
+ {
+ OUString sModel(m_xModelsBox->get_active_text());
+ try
+ {
+ Any aAny = m_xDataContainer->getByName( sModel );
+ Reference< css::xforms::XModel > xModel;
+ if ( aAny >>= xModel )
+ {
+ Reference< XEnumerationAccess > xNumAccess = xModel->getInstances();
+ if ( xNumAccess.is() )
+ {
+ Reference < XEnumeration > xNum = xNumAccess->createEnumeration();
+ if ( xNum.is() && xNum->hasMoreElements() )
+ {
+ sal_Int32 nAlreadyLoadedCount = m_aPageList.size();
+ if ( !HasFirstInstancePage() && nAlreadyLoadedCount > 0 )
+ nAlreadyLoadedCount--;
+ sal_Int32 nIdx = 0;
+ while ( xNum->hasMoreElements() )
+ {
+ if ( nIdx > nAlreadyLoadedCount )
+ {
+ Sequence< PropertyValue > xPropSeq;
+ if ( xNum->nextElement() >>= xPropSeq )
+ CreateInstancePage( xPropSeq );
+ else
+ {
+ SAL_WARN( "svx.form", "DataNavigator::InitPages(): invalid instance" );
+ }
+ }
+ else
+ xNum->nextElement();
+ nIdx++;
+ }
+ }
+ }
+ }
+ }
+ catch ( NoSuchElementException& )
+ {
+ SAL_WARN( "svx.form", "DataNavigatorWindow::SetPageModel(): no such element" );
+ }
+ catch( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "DataNavigatorWindow::SetPageModel()" );
+ }
+ }
+
+ void DataNavigatorWindow::ClearAllPageModels( bool bClearPages )
+ {
+ if ( m_xInstPage )
+ m_xInstPage->ClearModel();
+ if ( m_xSubmissionPage )
+ m_xSubmissionPage->ClearModel();
+ if ( m_xBindingPage )
+ m_xBindingPage->ClearModel();
+
+ sal_Int32 nCount = m_aPageList.size();
+ for (sal_Int32 i = 0; i < nCount; ++i)
+ {
+ XFormsPage* pPage = m_aPageList[i].get();
+ pPage->ClearModel();
+ }
+
+ if ( bClearPages )
+ {
+ m_aPageList.clear();
+ while ( m_xTabCtrl->get_n_pages() > MIN_PAGE_COUNT )
+ m_xTabCtrl->remove_page(m_xTabCtrl->get_page_ident(1));
+ }
+ }
+
+ void DataNavigatorWindow::CreateInstancePage( const Sequence< PropertyValue >& _xPropSeq )
+ {
+ OUString sInstName;
+ auto pProp = std::find_if(_xPropSeq.begin(), _xPropSeq.end(),
+ [](const PropertyValue& rProp) { return PN_INSTANCE_ID == rProp.Name; });
+ if (pProp != _xPropSeq.end())
+ pProp->Value >>= sInstName;
+
+ OUString sPageId = GetNewPageId();
+ if ( sInstName.isEmpty() )
+ {
+ SAL_WARN( "svx.form", "DataNavigatorWindow::CreateInstancePage(): instance without name" );
+ sInstName = "untitled";
+ }
+ m_xTabCtrl->insert_page(sPageId, sInstName, m_xTabCtrl->get_n_pages() - 2);
+ }
+
+ bool DataNavigatorWindow::HasFirstInstancePage() const
+ {
+ return m_xTabCtrl->get_page_ident(0) == "instance";
+ }
+
+ OUString DataNavigatorWindow::GetNewPageId() const
+ {
+ int nMax = 0;
+
+ int nCount = m_xTabCtrl->get_n_pages();
+ for (int i = 0; i < nCount; ++i)
+ {
+ OUString sIdent = m_xTabCtrl->get_page_ident(i);
+ OUString sNumber;
+ if (!sIdent.startsWith("additional", &sNumber))
+ continue;
+ int nPageId = sNumber.toInt32();
+ if (nMax < nPageId)
+ nMax = nPageId;
+ }
+
+ return "additional" + OUString::number(nMax + 1);
+ }
+
+ void DataNavigatorWindow::SetDocModified()
+ {
+ SfxObjectShell* pCurrentDoc = SfxObjectShell::Current();
+ DBG_ASSERT( pCurrentDoc, "DataNavigatorWindow::SetDocModified(): no objectshell" );
+ if (pCurrentDoc && !pCurrentDoc->IsModified() && pCurrentDoc->IsEnableSetModified())
+ pCurrentDoc->SetModified();
+ }
+
+ void DataNavigatorWindow::NotifyChanges( bool _bLoadAll )
+ {
+ if ( m_bIsNotifyDisabled )
+ return;
+
+ if ( _bLoadAll )
+ {
+ // reset all members
+ RemoveBroadcaster();
+ m_xDataContainer.clear();
+ m_xFrameModel.clear();
+ m_xModelsBox->clear();
+ m_nLastSelectedPos = -1;
+ // for a reload
+ LoadModels();
+ }
+ else
+ m_aUpdateTimer.Start();
+ }
+
+ void DataNavigatorWindow::AddContainerBroadcaster( const css::uno::Reference< css::container::XContainer >& xContainer )
+ {
+ Reference< XContainerListener > xListener = m_xDataListener;
+ xContainer->addContainerListener( xListener );
+ m_aContainerList.push_back( xContainer );
+ }
+
+
+ void DataNavigatorWindow::AddEventBroadcaster( const css::uno::Reference< css::xml::dom::events::XEventTarget >& xTarget )
+ {
+ Reference< XEventListener > xListener = m_xDataListener;
+ xTarget->addEventListener( EVENTTYPE_CHARDATA, xListener, true );
+ xTarget->addEventListener( EVENTTYPE_CHARDATA, xListener, false );
+ xTarget->addEventListener( EVENTTYPE_ATTR, xListener, true );
+ xTarget->addEventListener( EVENTTYPE_ATTR, xListener, false );
+ m_aEventTargetList.push_back( xTarget );
+ }
+
+ void DataNavigatorWindow::RemoveBroadcaster()
+ {
+ Reference< XContainerListener > xContainerListener = m_xDataListener;
+ sal_Int32 i, nCount = m_aContainerList.size();
+ for ( i = 0; i < nCount; ++i )
+ m_aContainerList[i]->removeContainerListener( xContainerListener );
+ Reference< XEventListener > xEventListener = m_xDataListener;
+ nCount = m_aEventTargetList.size();
+ for ( i = 0; i < nCount; ++i )
+ {
+ m_aEventTargetList[i]->removeEventListener( EVENTTYPE_CHARDATA, xEventListener, true );
+ m_aEventTargetList[i]->removeEventListener( EVENTTYPE_CHARDATA, xEventListener, false );
+ m_aEventTargetList[i]->removeEventListener( EVENTTYPE_ATTR, xEventListener, true );
+ m_aEventTargetList[i]->removeEventListener( EVENTTYPE_ATTR, xEventListener, false );
+ }
+ }
+
+ DataNavigator::DataNavigator(SfxBindings* _pBindings, SfxChildWindow* _pMgr, vcl::Window* _pParent)
+ : SfxDockingWindow(_pBindings, _pMgr, _pParent, "DataNavigator", "svx/ui/datanavigator.ui")
+ , SfxControllerItem(SID_FM_DATANAVIGATOR_CONTROL, *_pBindings)
+ , m_xDataWin(new DataNavigatorWindow(this, *m_xBuilder, _pBindings))
+ {
+ SetText( SvxResId( RID_STR_DATANAVIGATOR ) );
+
+ Size aSize = GetOptimalSize();
+ Size aLogSize = PixelToLogic(aSize, MapMode(MapUnit::MapAppFont));
+ SfxDockingWindow::SetFloatingSize( aLogSize );
+ }
+
+ DataNavigator::~DataNavigator()
+ {
+ disposeOnce();
+ }
+
+ void DataNavigator::dispose()
+ {
+ m_xDataWin.reset();
+ ::SfxControllerItem::dispose();
+ SfxDockingWindow::dispose();
+ }
+
+ void DataNavigator::StateChangedAtToolBoxControl( sal_uInt16 , SfxItemState , const SfxPoolItem* )
+ {
+ }
+
+ Size DataNavigator::CalcDockingSize( SfxChildAlignment eAlign )
+ {
+ if ( ( eAlign == SfxChildAlignment::TOP ) || ( eAlign == SfxChildAlignment::BOTTOM ) )
+ return Size();
+
+ return SfxDockingWindow::CalcDockingSize( eAlign );
+ }
+
+ SfxChildAlignment DataNavigator::CheckAlignment( SfxChildAlignment eActAlign, SfxChildAlignment eAlign )
+ {
+ switch ( eAlign )
+ {
+ case SfxChildAlignment::LEFT:
+ case SfxChildAlignment::RIGHT:
+ case SfxChildAlignment::NOALIGNMENT:
+ return eAlign;
+ default:
+ break;
+ }
+ return eActAlign;
+ }
+
+ SFX_IMPL_DOCKINGWINDOW( DataNavigatorManager, SID_FM_SHOW_DATANAVIGATOR )
+
+ DataNavigatorManager::DataNavigatorManager(
+ vcl::Window* _pParent, sal_uInt16 _nId, SfxBindings* _pBindings, SfxChildWinInfo* _pInfo ) :
+
+ SfxChildWindow( _pParent, _nId )
+
+ {
+ SetWindow( VclPtr<DataNavigator>::Create( _pBindings, this, _pParent ) );
+ SetAlignment(SfxChildAlignment::RIGHT);
+ GetWindow()->SetSizePixel( Size( 250, 400 ) );
+ static_cast<SfxDockingWindow*>(GetWindow())->Initialize( _pInfo );
+ }
+
+ AddDataItemDialog::AddDataItemDialog(weld::Window* pParent, ItemNode* _pNode,
+ const Reference< css::xforms::XFormsUIHelper1 >& _rUIHelper)
+ : GenericDialogController(pParent, "svx/ui/adddataitemdialog.ui", "AddDataItemDialog")
+ , m_xUIHelper(_rUIHelper)
+ , m_pItemNode(_pNode)
+ , m_eItemType(DITNone)
+ , m_sFL_Element(SvxResId(RID_STR_ELEMENT))
+ , m_sFL_Attribute(SvxResId(RID_STR_ATTRIBUTE))
+ , m_sFL_Binding(SvxResId(RID_STR_BINDING))
+ , m_sFT_BindingExp(SvxResId(RID_STR_BINDING_EXPR))
+ , m_xItemFrame(m_xBuilder->weld_frame("itemframe"))
+ , m_xNameFT(m_xBuilder->weld_label("nameft"))
+ , m_xNameED(m_xBuilder->weld_entry("name"))
+ , m_xDefaultFT(m_xBuilder->weld_label("valueft"))
+ , m_xDefaultED(m_xBuilder->weld_entry("value"))
+ , m_xDefaultBtn(m_xBuilder->weld_button("browse"))
+ , m_xSettingsFrame(m_xBuilder->weld_widget("settingsframe"))
+ , m_xDataTypeLB(m_xBuilder->weld_combo_box("datatype"))
+ , m_xRequiredCB(m_xBuilder->weld_check_button("required"))
+ , m_xRequiredBtn(m_xBuilder->weld_button("requiredcond"))
+ , m_xRelevantCB(m_xBuilder->weld_check_button("relevant"))
+ , m_xRelevantBtn(m_xBuilder->weld_button("relevantcond"))
+ , m_xConstraintCB(m_xBuilder->weld_check_button("constraint"))
+ , m_xConstraintBtn(m_xBuilder->weld_button("constraintcond"))
+ , m_xReadonlyCB(m_xBuilder->weld_check_button("readonly"))
+ , m_xReadonlyBtn(m_xBuilder->weld_button("readonlycond"))
+ , m_xCalculateCB(m_xBuilder->weld_check_button("calculate"))
+ , m_xCalculateBtn(m_xBuilder->weld_button("calculatecond"))
+ , m_xOKBtn(m_xBuilder->weld_button("ok"))
+ {
+ InitDialog();
+ InitFromNode();
+ InitDataTypeBox();
+ Check(nullptr);
+ }
+
+ AddDataItemDialog::~AddDataItemDialog()
+ {
+ if ( m_xTempBinding.is() )
+ {
+ Reference< css::xforms::XModel > xModel( m_xUIHelper, UNO_QUERY );
+ if ( xModel.is() )
+ {
+ try
+ {
+ Reference < XSet > xBindings = xModel->getBindings();
+ if ( xBindings.is() )
+ xBindings->remove( Any( m_xTempBinding ) );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "AddDataItemDialog::Dtor()" );
+ }
+ }
+ }
+ if( m_xUIHelper.is() && m_xBinding.is() )
+ {
+ // remove binding, if it does not convey 'useful' information
+ m_xUIHelper->removeBindingIfUseless( m_xBinding );
+ }
+ }
+
+ IMPL_LINK(AddDataItemDialog, CheckHdl, weld::Toggleable&, rBox, void)
+ {
+ Check(&rBox);
+ }
+
+ void AddDataItemDialog::Check(const weld::Toggleable* pBox)
+ {
+ // Condition buttons are only enable if their check box is checked
+ m_xReadonlyBtn->set_sensitive( m_xReadonlyCB->get_active() );
+ m_xRequiredBtn->set_sensitive( m_xRequiredCB->get_active() );
+ m_xRelevantBtn->set_sensitive( m_xRelevantCB->get_active() );
+ m_xConstraintBtn->set_sensitive( m_xConstraintCB->get_active() );
+ m_xCalculateBtn->set_sensitive( m_xCalculateCB->get_active() );
+
+ if ( !(pBox && m_xTempBinding.is()) )
+ return;
+
+ OUString sTemp, sPropName;
+ if ( m_xRequiredCB.get() == pBox )
+ sPropName = PN_REQUIRED_EXPR;
+ else if ( m_xRelevantCB.get() == pBox )
+ sPropName = PN_RELEVANT_EXPR;
+ else if ( m_xConstraintCB.get() == pBox )
+ sPropName = PN_CONSTRAINT_EXPR;
+ else if ( m_xReadonlyCB.get() == pBox )
+ sPropName = PN_READONLY_EXPR;
+ else if ( m_xCalculateCB.get() == pBox )
+ sPropName = PN_CALCULATE_EXPR;
+ bool bIsChecked = pBox->get_active();
+ m_xTempBinding->getPropertyValue( sPropName ) >>= sTemp;
+ if ( bIsChecked && sTemp.isEmpty() )
+ sTemp = TRUE_VALUE;
+ else if ( !bIsChecked && !sTemp.isEmpty() )
+ sTemp.clear();
+ m_xTempBinding->setPropertyValue( sPropName, Any( sTemp ) );
+ }
+
+ IMPL_LINK(AddDataItemDialog, ConditionHdl, weld::Button&, rBtn, void)
+ {
+ OUString sPropName;
+ if ( m_xDefaultBtn.get() == &rBtn )
+ sPropName = PN_BINDING_EXPR;
+ else if ( m_xRequiredBtn.get() == &rBtn )
+ sPropName = PN_REQUIRED_EXPR;
+ else if ( m_xRelevantBtn.get() == &rBtn )
+ sPropName = PN_RELEVANT_EXPR;
+ else if ( m_xConstraintBtn.get() == &rBtn )
+ sPropName = PN_CONSTRAINT_EXPR;
+ else if (m_xReadonlyBtn.get() == &rBtn)
+ sPropName = PN_READONLY_EXPR;
+ else if (m_xCalculateBtn.get() == &rBtn)
+ sPropName = PN_CALCULATE_EXPR;
+ AddConditionDialog aDlg(m_xDialog.get(), sPropName, m_xTempBinding);
+ bool bIsDefBtn = ( m_xDefaultBtn.get() == &rBtn );
+ OUString sCondition;
+ if ( bIsDefBtn )
+ sCondition = m_xDefaultED->get_text();
+ else
+ {
+ OUString sTemp;
+ m_xTempBinding->getPropertyValue( sPropName ) >>= sTemp;
+ if ( sTemp.isEmpty() )
+ sTemp = TRUE_VALUE;
+ sCondition = sTemp;
+ }
+ aDlg.SetCondition( sCondition );
+
+ if (aDlg.run() == RET_OK)
+ {
+ OUString sNewCondition = aDlg.GetCondition();
+ if ( bIsDefBtn )
+ m_xDefaultED->set_text(sNewCondition);
+ else
+ {
+
+ m_xTempBinding->setPropertyValue(
+ sPropName, Any( sNewCondition ) );
+ }
+ }
+ }
+
+ static void copyPropSet( const Reference< XPropertySet >& xFrom, Reference< XPropertySet > const & xTo )
+ {
+ DBG_ASSERT( xFrom.is(), "copyPropSet(): no source" );
+ DBG_ASSERT( xTo.is(), "copyPropSet(): no target" );
+
+ try
+ {
+ // get property names & infos, and iterate over target properties
+ const Sequence< Property > aProperties = xTo->getPropertySetInfo()->getProperties();
+ Reference< XPropertySetInfo > xFromInfo = xFrom->getPropertySetInfo();
+ for ( const Property& rProperty : aProperties )
+ {
+ const OUString& rName = rProperty.Name;
+
+ // if both set have the property, copy the value
+ // (catch and ignore exceptions, if any)
+ if ( xFromInfo->hasPropertyByName( rName ) )
+ {
+ // don't set readonly properties
+ Property aProperty = xFromInfo->getPropertyByName( rName );
+ if ( ( aProperty.Attributes & PropertyAttribute::READONLY ) == 0 )
+ xTo->setPropertyValue(rName, xFrom->getPropertyValue( rName ));
+ }
+ // else: no property? then ignore.
+ }
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "copyPropSet()" );
+ }
+ }
+
+ IMPL_LINK_NOARG(AddDataItemDialog, OKHdl, weld::Button&, void)
+ {
+ bool bIsHandleBinding = ( DITBinding == m_eItemType );
+ bool bIsHandleText = ( DITText == m_eItemType );
+ OUString sNewName( m_xNameED->get_text() );
+
+ if ( ( !bIsHandleBinding && !bIsHandleText && !m_xUIHelper->isValidXMLName( sNewName ) ) ||
+ ( bIsHandleBinding && sNewName.isEmpty() ) )
+ {
+ // Error and don't close the dialog
+ std::unique_ptr<weld::MessageDialog> xErrBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ SvxResId(RID_STR_INVALID_XMLNAME)));
+ xErrBox->set_primary_text(xErrBox->get_primary_text().replaceFirst(MSG_VARIABLE, sNewName));
+ xErrBox->run();
+ return;
+ }
+
+ OUString sDataType( m_xDataTypeLB->get_active_text() );
+ m_xTempBinding->setPropertyValue( PN_BINDING_TYPE, Any( sDataType ) );
+
+ if ( bIsHandleBinding )
+ {
+ // copy properties from temp binding to original binding
+ copyPropSet( m_xTempBinding, m_pItemNode->m_xPropSet );
+ try
+ {
+ OUString sValue = m_xNameED->get_text();
+ m_pItemNode->m_xPropSet->setPropertyValue( PN_BINDING_ID, Any( sValue ) );
+ sValue = m_xDefaultED->get_text();
+ m_pItemNode->m_xPropSet->setPropertyValue( PN_BINDING_EXPR, Any( sValue ) );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "AddDataDialog::OKHdl()" );
+ }
+ }
+ else
+ {
+ // copy properties from temp binding to original binding
+ copyPropSet( m_xTempBinding, m_xBinding );
+ try
+ {
+ if ( bIsHandleText )
+ m_xUIHelper->setNodeValue( m_pItemNode->m_xNode, m_xDefaultED->get_text() );
+ else
+ {
+ Reference< css::xml::dom::XNode > xNewNode =
+ m_xUIHelper->renameNode( m_pItemNode->m_xNode, m_xNameED->get_text() );
+ m_xUIHelper->setNodeValue( xNewNode, m_xDefaultED->get_text() );
+ m_pItemNode->m_xNode = xNewNode;
+ }
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "AddDataDialog::OKHdl()" );
+ }
+ }
+ // then close the dialog
+ m_xDialog->response(RET_OK);
+ }
+
+ void AddDataItemDialog::InitDialog()
+ {
+ // set handler
+ Link<weld::Toggleable&,void> aLink = LINK( this, AddDataItemDialog, CheckHdl );
+ m_xRequiredCB->connect_toggled( aLink );
+ m_xRelevantCB->connect_toggled( aLink );
+ m_xConstraintCB->connect_toggled( aLink );
+ m_xReadonlyCB->connect_toggled( aLink );
+ m_xCalculateCB->connect_toggled( aLink );
+
+ Link<weld::Button&,void> aLink2 = LINK( this, AddDataItemDialog, ConditionHdl );
+ m_xDefaultBtn->connect_clicked( aLink2 );
+ m_xRequiredBtn->connect_clicked( aLink2 );
+ m_xRelevantBtn->connect_clicked( aLink2 );
+ m_xConstraintBtn->connect_clicked( aLink2 );
+ m_xReadonlyBtn->connect_clicked( aLink2 );
+ m_xCalculateBtn->connect_clicked( aLink2 );
+
+ m_xOKBtn->connect_clicked( LINK( this, AddDataItemDialog, OKHdl ) );
+ }
+
+ void AddDataItemDialog::InitFromNode()
+ {
+ if ( m_pItemNode )
+ {
+ if ( m_pItemNode->m_xNode.is() )
+ {
+ try
+ {
+ // detect type of the node
+ css::xml::dom::NodeType eChildType = m_pItemNode->m_xNode->getNodeType();
+ switch ( eChildType )
+ {
+ case css::xml::dom::NodeType_ATTRIBUTE_NODE:
+ m_eItemType = DITAttribute;
+ break;
+ case css::xml::dom::NodeType_ELEMENT_NODE:
+ m_eItemType = DITElement;
+ break;
+ case css::xml::dom::NodeType_TEXT_NODE:
+ m_eItemType = DITText;
+ break;
+ default:
+ OSL_FAIL( "AddDataItemDialog::InitFronNode: cannot handle this node type!" );
+ break;
+ }
+
+ /** Get binding of the node and clone it
+ Then use this temporary binding in the dialog.
+ When the user click OK the temporary binding will be copied
+ into the original binding.
+ */
+
+ Reference< css::xml::dom::XNode > xNode = m_pItemNode->m_xNode;
+ m_xBinding = m_xUIHelper->getBindingForNode( xNode, true );
+ if ( m_xBinding.is() )
+ {
+ Reference< css::xforms::XModel > xModel( m_xUIHelper, UNO_QUERY );
+ if ( xModel.is() )
+ {
+ m_xTempBinding = m_xUIHelper->cloneBindingAsGhost( m_xBinding );
+ Reference < XSet > xBindings = xModel->getBindings();
+ if ( xBindings.is() )
+ xBindings->insert( Any( m_xTempBinding ) );
+ }
+ }
+
+ if ( m_eItemType != DITText )
+ {
+ OUString sName( m_xUIHelper->getNodeName( m_pItemNode->m_xNode ) );
+ m_xNameED->set_text( sName );
+ }
+ m_xDefaultED->set_text( m_pItemNode->m_xNode->getNodeValue() );
+ }
+ catch( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "AddDataItemDialog::InitFromNode()" );
+ }
+ }
+ else if ( m_pItemNode->m_xPropSet.is() )
+ {
+ m_eItemType = DITBinding;
+ Reference< css::xforms::XModel > xModel( m_xUIHelper, UNO_QUERY );
+ if ( xModel.is() )
+ {
+ try
+ {
+ m_xTempBinding = m_xUIHelper->cloneBindingAsGhost( m_pItemNode->m_xPropSet );
+ Reference < XSet > xBindings = xModel->getBindings();
+ if ( xBindings.is() )
+ xBindings->insert( Any( m_xTempBinding ) );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "AddDataItemDialog::InitFromNode()" );
+ }
+ }
+ try
+ {
+ Reference< XPropertySetInfo > xInfo = m_pItemNode->m_xPropSet->getPropertySetInfo();
+ OUString sTemp;
+ if ( xInfo->hasPropertyByName( PN_BINDING_ID ) )
+ {
+ m_pItemNode->m_xPropSet->getPropertyValue( PN_BINDING_ID ) >>= sTemp;
+ m_xNameED->set_text( sTemp );
+ m_pItemNode->m_xPropSet->getPropertyValue( PN_BINDING_EXPR ) >>= sTemp;
+ m_xDefaultED->set_text( sTemp );
+ }
+ else if ( xInfo->hasPropertyByName( PN_SUBMISSION_BIND ) )
+ {
+ m_pItemNode->m_xPropSet->getPropertyValue( PN_SUBMISSION_ID ) >>= sTemp;
+ m_xNameED->set_text( sTemp );
+ }
+ }
+ catch( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "AddDataItemDialog::InitFromNode()" );
+ }
+
+ m_xDefaultBtn->show();
+ }
+
+ if ( m_xTempBinding.is() )
+ {
+ try
+ {
+ OUString sTemp;
+ if ( ( m_xTempBinding->getPropertyValue( PN_REQUIRED_EXPR ) >>= sTemp )
+ && !sTemp.isEmpty() )
+ m_xRequiredCB->set_active(true);
+ if ( ( m_xTempBinding->getPropertyValue( PN_RELEVANT_EXPR ) >>= sTemp )
+ && !sTemp.isEmpty() )
+ m_xRelevantCB->set_active(true);
+ if ( ( m_xTempBinding->getPropertyValue( PN_CONSTRAINT_EXPR ) >>= sTemp )
+ && !sTemp.isEmpty() )
+ m_xConstraintCB->set_active(true);
+ if ( ( m_xTempBinding->getPropertyValue( PN_READONLY_EXPR ) >>= sTemp )
+ && !sTemp.isEmpty() )
+ m_xReadonlyCB->set_active(true);
+ if ( ( m_xTempBinding->getPropertyValue( PN_CALCULATE_EXPR ) >>= sTemp )
+ && !sTemp.isEmpty() )
+ m_xCalculateCB->set_active(true);
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "AddDataItemDialog::InitFromNode()" );
+ }
+ }
+ }
+
+ if ( DITText == m_eItemType )
+ {
+ m_xSettingsFrame->hide();
+ m_xNameFT->set_sensitive(false);
+ m_xNameED->set_sensitive(false);
+ }
+ }
+
+ void AddDataItemDialog::InitDataTypeBox()
+ {
+ if ( m_eItemType == DITText )
+ return;
+
+ Reference< css::xforms::XModel > xModel( m_xUIHelper, UNO_QUERY );
+ if ( !xModel.is() )
+ return;
+
+ try
+ {
+ Reference< css::xforms::XDataTypeRepository > xDataTypes =
+ xModel->getDataTypeRepository();
+ if ( xDataTypes.is() )
+ {
+ const Sequence< OUString > aNameList = xDataTypes->getElementNames();
+ for ( const OUString& rName : aNameList )
+ m_xDataTypeLB->append_text(rName);
+ }
+
+ if ( m_xTempBinding.is() )
+ {
+ OUString sTemp;
+ if ( m_xTempBinding->getPropertyValue( PN_BINDING_TYPE ) >>= sTemp )
+ {
+ int nPos = m_xDataTypeLB->find_text(sTemp);
+ if (nPos == -1)
+ {
+ m_xDataTypeLB->append_text(sTemp);
+ nPos = m_xDataTypeLB->get_count() - 1;
+ }
+ m_xDataTypeLB->set_active(nPos);
+ }
+ }
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "AddDataItemDialog::InitDataTypeBox()" );
+ }
+ }
+
+ void AddDataItemDialog::InitText( DataItemType _eType )
+ {
+ OUString sText;
+
+ switch ( _eType )
+ {
+ case DITAttribute :
+ {
+ sText = m_sFL_Attribute;
+ break;
+ }
+
+ case DITBinding :
+ {
+ sText = m_sFL_Binding;
+ m_xDefaultFT->set_label(m_sFT_BindingExp);
+ break;
+ }
+
+ default:
+ {
+ sText = m_sFL_Element;
+ }
+ }
+
+ m_xItemFrame->set_label(sText);
+ }
+
+ AddConditionDialog::AddConditionDialog(weld::Window* pParent,
+ OUString _aPropertyName,
+ const Reference< XPropertySet >& _rPropSet)
+ : GenericDialogController(pParent, "svx/ui/addconditiondialog.ui", "AddConditionDialog")
+ , m_aResultIdle("svx AddConditionDialog m_aResultIdle")
+ , m_sPropertyName(std::move(_aPropertyName))
+ , m_xBinding(_rPropSet)
+ , m_xConditionED(m_xBuilder->weld_text_view("condition"))
+ , m_xResultWin(m_xBuilder->weld_text_view("result"))
+ , m_xEditNamespacesBtn(m_xBuilder->weld_button("edit"))
+ , m_xOKBtn(m_xBuilder->weld_button("ok"))
+ {
+ DBG_ASSERT( m_xBinding.is(), "AddConditionDialog::Ctor(): no Binding" );
+
+ m_xConditionED->set_size_request(m_xConditionED->get_approximate_digit_width() * 52,
+ m_xConditionED->get_height_rows(4));
+ m_xResultWin->set_size_request(m_xResultWin->get_approximate_digit_width() * 52,
+ m_xResultWin->get_height_rows(4));
+
+ m_xConditionED->connect_changed( LINK( this, AddConditionDialog, ModifyHdl ) );
+ m_xEditNamespacesBtn->connect_clicked( LINK( this, AddConditionDialog, EditHdl ) );
+ m_xOKBtn->connect_clicked( LINK( this, AddConditionDialog, OKHdl ) );
+ m_aResultIdle.SetPriority( TaskPriority::LOWEST );
+ m_aResultIdle.SetInvokeHandler( LINK( this, AddConditionDialog, ResultHdl ) );
+
+ if ( !m_sPropertyName.isEmpty() )
+ {
+ try
+ {
+ OUString sTemp;
+ if ( ( m_xBinding->getPropertyValue( m_sPropertyName ) >>= sTemp )
+ && !sTemp.isEmpty() )
+ {
+ m_xConditionED->set_text( sTemp );
+ }
+ else
+ {
+//! m_xBinding->setPropertyValue( m_sPropertyName, makeAny( TRUE_VALUE ) );
+ m_xConditionED->set_text( TRUE_VALUE );
+ }
+
+ Reference< css::xforms::XModel > xModel;
+ if ( ( m_xBinding->getPropertyValue( PN_BINDING_MODEL ) >>= xModel ) && xModel.is() )
+ m_xUIHelper.set( xModel, UNO_QUERY );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "AddConditionDialog::Ctor()" );
+ }
+ }
+
+ DBG_ASSERT( m_xUIHelper.is(), "AddConditionDialog::Ctor(): no UIHelper" );
+ ResultHdl( &m_aResultIdle );
+ }
+
+ AddConditionDialog::~AddConditionDialog()
+ {
+ }
+
+ IMPL_LINK_NOARG(AddConditionDialog, EditHdl, weld::Button&, void)
+ {
+ Reference< XNameContainer > xNameContnr;
+ try
+ {
+ m_xBinding->getPropertyValue( PN_BINDING_NAMESPACES ) >>= xNameContnr;
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "AddDataItemDialog::EditHdl()" );
+ }
+ NamespaceItemDialog aDlg(this, xNameContnr);
+ aDlg.run();
+ try
+ {
+ m_xBinding->setPropertyValue( PN_BINDING_NAMESPACES, Any( xNameContnr ) );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "AddDataItemDialog::EditHdl()" );
+ }
+ }
+
+ IMPL_LINK_NOARG(AddConditionDialog, OKHdl, weld::Button&, void)
+ {
+ m_xDialog->response(RET_OK);
+ }
+
+ IMPL_LINK_NOARG(AddConditionDialog, ModifyHdl, weld::TextView&, void)
+ {
+ m_aResultIdle.Start();
+ }
+
+ IMPL_LINK_NOARG(AddConditionDialog, ResultHdl, Timer *, void)
+ {
+ OUString sCondition = comphelper::string::strip(m_xConditionED->get_text(), ' ');
+ OUString sResult;
+ if ( !sCondition.isEmpty() )
+ {
+ try
+ {
+ sResult = m_xUIHelper->getResultForExpression( m_xBinding, ( m_sPropertyName == PN_BINDING_EXPR ), sCondition );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "AddConditionDialog::ResultHdl()" );
+ }
+ }
+ m_xResultWin->set_text(sResult);
+ }
+
+ NamespaceItemDialog::NamespaceItemDialog(AddConditionDialog* pCondDlg, Reference<XNameContainer>& rContainer)
+ : GenericDialogController(pCondDlg->getDialog(), "svx/ui/namespacedialog.ui", "NamespaceDialog")
+ , m_pConditionDlg(pCondDlg)
+ , m_rNamespaces(rContainer)
+ , m_xNamespacesList(m_xBuilder->weld_tree_view("namespaces"))
+ , m_xAddNamespaceBtn(m_xBuilder->weld_button("add"))
+ , m_xEditNamespaceBtn(m_xBuilder->weld_button("edit"))
+ , m_xDeleteNamespaceBtn(m_xBuilder->weld_button("delete"))
+ , m_xOKBtn(m_xBuilder->weld_button("ok"))
+ {
+ m_xNamespacesList->set_size_request(m_xNamespacesList->get_approximate_digit_width() * 80,
+ m_xNamespacesList->get_height_rows(8));
+
+ std::vector<int> aWidths
+ {
+ o3tl::narrowing<int>(m_xNamespacesList->get_approximate_digit_width() * 20)
+ };
+ m_xNamespacesList->set_column_fixed_widths(aWidths);
+
+ m_xNamespacesList->connect_changed( LINK( this, NamespaceItemDialog, SelectHdl ) );
+ Link<weld::Button&,void> aLink = LINK( this, NamespaceItemDialog, ClickHdl );
+ m_xAddNamespaceBtn->connect_clicked( aLink );
+ m_xEditNamespaceBtn->connect_clicked( aLink );
+ m_xDeleteNamespaceBtn->connect_clicked( aLink );
+ m_xOKBtn->connect_clicked( LINK( this, NamespaceItemDialog, OKHdl ) );
+
+ LoadNamespaces();
+ SelectHdl(*m_xNamespacesList);
+ }
+
+ NamespaceItemDialog::~NamespaceItemDialog()
+ {
+ }
+
+ IMPL_LINK_NOARG( NamespaceItemDialog, SelectHdl, weld::TreeView&, void)
+ {
+ bool bEnable = m_xNamespacesList->get_selected_index() != -1;
+ m_xEditNamespaceBtn->set_sensitive( bEnable );
+ m_xDeleteNamespaceBtn->set_sensitive( bEnable );
+ }
+
+ IMPL_LINK( NamespaceItemDialog, ClickHdl, weld::Button&, rButton, void )
+ {
+ if (m_xAddNamespaceBtn.get() == &rButton)
+ {
+ ManageNamespaceDialog aDlg(m_xDialog.get(), m_pConditionDlg, false);
+ if (aDlg.run() == RET_OK)
+ {
+ m_xNamespacesList->append_text(aDlg.GetPrefix());
+ int nRow = m_xNamespacesList->n_children();
+ m_xNamespacesList->set_text(nRow - 1, aDlg.GetURL(), 1);
+ }
+ }
+ else if (m_xEditNamespaceBtn.get() == &rButton)
+ {
+ ManageNamespaceDialog aDlg(m_xDialog.get(), m_pConditionDlg, true);
+ int nEntry = m_xNamespacesList->get_selected_index();
+ DBG_ASSERT( nEntry != -1, "NamespaceItemDialog::ClickHdl(): no entry" );
+ OUString sPrefix(m_xNamespacesList->get_text(nEntry, 0));
+ aDlg.SetNamespace(sPrefix, m_xNamespacesList->get_text(nEntry, 1));
+ if (aDlg.run() == RET_OK)
+ {
+ // if a prefix was changed, mark the old prefix as 'removed'
+ if( sPrefix != aDlg.GetPrefix() )
+ m_aRemovedList.push_back( sPrefix );
+
+ m_xNamespacesList->set_text(nEntry, aDlg.GetPrefix(), 0);
+ m_xNamespacesList->set_text(nEntry, aDlg.GetURL(), 1);
+ }
+ }
+ else if (m_xDeleteNamespaceBtn.get() == &rButton)
+ {
+ int nEntry = m_xNamespacesList->get_selected_index();
+ DBG_ASSERT( nEntry != -1, "NamespaceItemDialog::ClickHdl(): no entry" );
+ OUString sPrefix(m_xNamespacesList->get_text(nEntry, 0));
+ m_aRemovedList.push_back( sPrefix );
+ m_xNamespacesList->remove(nEntry);
+ }
+ else
+ {
+ SAL_WARN( "svx.form", "NamespaceItemDialog::ClickHdl(): invalid button" );
+ }
+
+ SelectHdl(*m_xNamespacesList);
+ }
+
+ IMPL_LINK_NOARG(NamespaceItemDialog, OKHdl, weld::Button&, void)
+ {
+ try
+ {
+ // update namespace container
+ sal_Int32 i, nRemovedCount = m_aRemovedList.size();
+ for( i = 0; i < nRemovedCount; ++i )
+ m_rNamespaces->removeByName( m_aRemovedList[i] );
+
+ sal_Int32 nEntryCount = m_xNamespacesList->n_children();
+ for( i = 0; i < nEntryCount; ++i )
+ {
+ OUString sPrefix(m_xNamespacesList->get_text(i, 0));
+ OUString sURL(m_xNamespacesList->get_text(i, 1));
+
+ if ( m_rNamespaces->hasByName( sPrefix ) )
+ m_rNamespaces->replaceByName( sPrefix, Any( sURL ) );
+ else
+ m_rNamespaces->insertByName( sPrefix, Any( sURL ) );
+ }
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "NamespaceItemDialog::OKHdl()" );
+ }
+ // and close the dialog
+ m_xDialog->response(RET_OK);
+ }
+
+ void NamespaceItemDialog::LoadNamespaces()
+ {
+ try
+ {
+ int nRow = 0;
+ const Sequence< OUString > aAllNames = m_rNamespaces->getElementNames();
+ for ( const OUString& sPrefix : aAllNames )
+ {
+ if ( m_rNamespaces->hasByName( sPrefix ) )
+ {
+ OUString sURL;
+ Any aAny = m_rNamespaces->getByName( sPrefix );
+ if (aAny >>= sURL)
+ {
+ m_xNamespacesList->append_text(sPrefix);
+ m_xNamespacesList->set_text(nRow, sURL, 1);
+ ++nRow;
+ }
+ }
+ }
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "NamespaceItemDialog::LoadNamespaces()" );
+ }
+ }
+
+ ManageNamespaceDialog::ManageNamespaceDialog(weld::Window* pParent, AddConditionDialog* pCondDlg, bool bIsEdit)
+ : GenericDialogController(pParent, "svx/ui/addnamespacedialog.ui", "AddNamespaceDialog")
+ , m_pConditionDlg(pCondDlg)
+ , m_xPrefixED(m_xBuilder->weld_entry("prefix"))
+ , m_xUrlED(m_xBuilder->weld_entry("url"))
+ , m_xOKBtn(m_xBuilder->weld_button("ok"))
+ , m_xAltTitle(m_xBuilder->weld_label("alttitle"))
+ {
+ if (bIsEdit)
+ m_xDialog->set_title(m_xAltTitle->get_label());
+
+ m_xOKBtn->connect_clicked(LINK(this, ManageNamespaceDialog, OKHdl));
+ }
+
+ ManageNamespaceDialog::~ManageNamespaceDialog()
+ {
+ }
+
+ IMPL_LINK_NOARG(ManageNamespaceDialog, OKHdl, weld::Button&, void)
+ {
+ OUString sPrefix = m_xPrefixED->get_text();
+
+ try
+ {
+ if (!m_pConditionDlg->GetUIHelper()->isValidPrefixName(sPrefix))
+ {
+ std::unique_ptr<weld::MessageDialog> xErrBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ SvxResId(RID_STR_INVALID_XMLPREFIX)));
+ xErrBox->set_primary_text(xErrBox->get_primary_text().replaceFirst(MSG_VARIABLE, sPrefix));
+ xErrBox->run();
+ return;
+ }
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "ManageNamespacesDialog::OKHdl()" );
+ }
+
+ // no error so close the dialog
+ m_xDialog->response(RET_OK);
+ }
+
+ AddSubmissionDialog::AddSubmissionDialog(
+ weld::Window* pParent, ItemNode* _pNode,
+ const Reference< css::xforms::XFormsUIHelper1 >& _rUIHelper)
+ : GenericDialogController(pParent, "svx/ui/addsubmissiondialog.ui", "AddSubmissionDialog")
+ , m_pItemNode(_pNode)
+ , m_xUIHelper(_rUIHelper)
+ , m_xNameED(m_xBuilder->weld_entry("name"))
+ , m_xActionED(m_xBuilder->weld_entry("action"))
+ , m_xMethodLB(m_xBuilder->weld_combo_box("method"))
+ , m_xRefED(m_xBuilder->weld_entry("expression"))
+ , m_xRefBtn(m_xBuilder->weld_button("browse"))
+ , m_xBindLB(m_xBuilder->weld_combo_box("binding"))
+ , m_xReplaceLB(m_xBuilder->weld_combo_box("replace"))
+ , m_xOKBtn(m_xBuilder->weld_button("ok"))
+ {
+ FillAllBoxes();
+
+ m_xRefBtn->connect_clicked( LINK( this, AddSubmissionDialog, RefHdl ) );
+ m_xOKBtn->connect_clicked( LINK( this, AddSubmissionDialog, OKHdl ) );
+ }
+
+ AddSubmissionDialog::~AddSubmissionDialog()
+ {
+ // #i38991# if we have added a binding, we need to remove it as well.
+ if( m_xCreatedBinding.is() && m_xUIHelper.is() )
+ m_xUIHelper->removeBindingIfUseless( m_xCreatedBinding );
+ }
+
+ IMPL_LINK_NOARG(AddSubmissionDialog, RefHdl, weld::Button&, void)
+ {
+ AddConditionDialog aDlg(m_xDialog.get(), PN_BINDING_EXPR, m_xTempBinding );
+ aDlg.SetCondition( m_xRefED->get_text() );
+ if ( aDlg.run() == RET_OK )
+ m_xRefED->set_text(aDlg.GetCondition());
+ }
+
+ IMPL_LINK_NOARG(AddSubmissionDialog, OKHdl, weld::Button&, void)
+ {
+ OUString sName(m_xNameED->get_text());
+ if(sName.isEmpty())
+ {
+ std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ SvxResId(RID_STR_EMPTY_SUBMISSIONNAME)));
+ xErrorBox->run();
+ return;
+ }
+
+ if ( !m_xSubmission.is() )
+ {
+ DBG_ASSERT( !m_xNewSubmission.is(),
+ "AddSubmissionDialog::OKHdl(): new submission already exists" );
+
+ // add a new submission
+ Reference< css::xforms::XModel > xModel( m_xUIHelper, UNO_QUERY );
+ if ( xModel.is() )
+ {
+ try
+ {
+ m_xNewSubmission = xModel->createSubmission();
+ m_xSubmission = m_xNewSubmission;
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "AddSubmissionDialog::OKHdl()" );
+ }
+ }
+ }
+
+ if ( m_xSubmission.is() )
+ {
+ OUString sTemp = m_xNameED->get_text();
+ try
+ {
+ m_xSubmission->setPropertyValue( PN_SUBMISSION_ID, Any( sTemp ) );
+ sTemp = m_xActionED->get_text();
+ m_xSubmission->setPropertyValue( PN_SUBMISSION_ACTION, Any( sTemp ) );
+ sTemp = m_aMethodString.toAPI( m_xMethodLB->get_active_text() );
+ m_xSubmission->setPropertyValue( PN_SUBMISSION_METHOD, Any( sTemp ) );
+ sTemp = m_xRefED->get_text();
+ m_xSubmission->setPropertyValue( PN_SUBMISSION_REF, Any( sTemp ) );
+ OUString sEntry = m_xBindLB->get_active_text();
+ sal_Int32 nColonIdx = sEntry.indexOf(':');
+ if (nColonIdx != -1)
+ sEntry = sEntry.copy(0, nColonIdx);
+ sTemp = sEntry;
+ m_xSubmission->setPropertyValue( PN_SUBMISSION_BIND, Any( sTemp ) );
+ sTemp = m_aReplaceString.toAPI( m_xReplaceLB->get_active_text() );
+ m_xSubmission->setPropertyValue( PN_SUBMISSION_REPLACE, Any( sTemp ) );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "AddSubmissionDialog::OKHdl()" );
+ }
+ }
+
+ m_xDialog->response(RET_OK);
+ }
+
+ void AddSubmissionDialog::FillAllBoxes()
+ {
+ // method box
+ m_xMethodLB->append_text(SvxResId(RID_STR_METHOD_POST));
+ m_xMethodLB->append_text(SvxResId(RID_STR_METHOD_PUT));
+ m_xMethodLB->append_text(SvxResId(RID_STR_METHOD_GET));
+ m_xMethodLB->set_active(0);
+
+ // binding box
+ Reference< css::xforms::XModel > xModel( m_xUIHelper, UNO_QUERY );
+ if ( xModel.is() )
+ {
+ try
+ {
+ Reference< XEnumerationAccess > xNumAccess = xModel->getBindings();
+ if ( xNumAccess.is() )
+ {
+ Reference < XEnumeration > xNum = xNumAccess->createEnumeration();
+ if ( xNum.is() && xNum->hasMoreElements() )
+ {
+ while ( xNum->hasMoreElements() )
+ {
+ Reference< XPropertySet > xPropSet;
+ Any aAny = xNum->nextElement();
+ if ( aAny >>= xPropSet )
+ {
+ OUString sEntry;
+ OUString sTemp;
+ xPropSet->getPropertyValue( PN_BINDING_ID ) >>= sTemp;
+ sEntry += sTemp + ": ";
+ xPropSet->getPropertyValue( PN_BINDING_EXPR ) >>= sTemp;
+ sEntry += sTemp;
+ m_xBindLB->append_text(sEntry);
+
+ if ( !m_xTempBinding.is() )
+ m_xTempBinding = xPropSet;
+ }
+ }
+ }
+ }
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "AddSubmissionDialog::FillAllBoxes()" );
+ }
+ }
+
+ // #i36342# we need a temporary binding; create one if no existing binding
+ // is found
+ if( !m_xTempBinding.is() )
+ {
+ m_xCreatedBinding = m_xUIHelper->getBindingForNode(
+ Reference<css::xml::dom::XNode>(
+ xModel->getDefaultInstance()->getDocumentElement(),
+ UNO_QUERY_THROW ),
+ true );
+ m_xTempBinding = m_xCreatedBinding;
+ }
+
+ // replace box
+ m_xReplaceLB->append_text(SvxResId(RID_STR_REPLACE_NONE));
+ m_xReplaceLB->append_text(SvxResId(RID_STR_REPLACE_INST));
+ m_xReplaceLB->append_text(SvxResId(RID_STR_REPLACE_DOC));
+
+
+ // init the controls with the values of the submission
+ if ( m_pItemNode && m_pItemNode->m_xPropSet.is() )
+ {
+ m_xSubmission = m_pItemNode->m_xPropSet;
+ try
+ {
+ OUString sTemp;
+ m_xSubmission->getPropertyValue( PN_SUBMISSION_ID ) >>= sTemp;
+ m_xNameED->set_text( sTemp );
+ m_xSubmission->getPropertyValue( PN_SUBMISSION_ACTION ) >>= sTemp;
+ m_xActionED->set_text( sTemp );
+ m_xSubmission->getPropertyValue( PN_SUBMISSION_REF ) >>= sTemp;
+ m_xRefED->set_text(sTemp);
+
+ m_xSubmission->getPropertyValue( PN_SUBMISSION_METHOD ) >>= sTemp;
+ sTemp = m_aMethodString.toUI( sTemp );
+ sal_Int32 nPos = m_xMethodLB->find_text( sTemp );
+ if (nPos == -1)
+ {
+ m_xMethodLB->append_text( sTemp );
+ nPos = m_xMethodLB->get_count() - 1;
+ }
+ m_xMethodLB->set_active( nPos );
+
+ m_xSubmission->getPropertyValue( PN_SUBMISSION_BIND ) >>= sTemp;
+ nPos = m_xBindLB->find_text(sTemp);
+ if (nPos == -1)
+ {
+ m_xBindLB->append_text(sTemp);
+ nPos = m_xBindLB->get_count() - 1;
+ }
+ m_xBindLB->set_active(nPos);
+
+ m_xSubmission->getPropertyValue( PN_SUBMISSION_REPLACE ) >>= sTemp;
+ sTemp = m_aReplaceString.toUI( sTemp );
+ if ( sTemp.isEmpty() )
+ sTemp = m_xReplaceLB->get_text(0); // first entry == "none"
+ nPos = m_xReplaceLB->find_text(sTemp);
+ if (nPos == -1)
+ {
+ m_xReplaceLB->append_text(sTemp);
+ nPos = m_xReplaceLB->get_count() - 1;
+ }
+ m_xReplaceLB->set_active(nPos);
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "AddSubmissionDialog::FillAllBoxes()" );
+ }
+ }
+
+ m_xRefBtn->set_sensitive(m_xTempBinding.is());
+ }
+
+ AddModelDialog::AddModelDialog(weld::Window* pParent, bool bIsEdit)
+ : GenericDialogController(pParent, "svx/ui/addmodeldialog.ui", "AddModelDialog")
+ , m_xNameED(m_xBuilder->weld_entry("name"))
+ , m_xModifyCB(m_xBuilder->weld_check_button("modify"))
+ , m_xAltTitle(m_xBuilder->weld_label("alttitle"))
+ {
+ if (bIsEdit)
+ m_xDialog->set_title(m_xAltTitle->get_label());
+ }
+
+ AddModelDialog::~AddModelDialog()
+ {
+ }
+
+ AddInstanceDialog::AddInstanceDialog(weld::Window* pParent, bool _bEdit)
+ : GenericDialogController(pParent, "svx/ui/addinstancedialog.ui", "AddInstanceDialog")
+ , m_xNameED(m_xBuilder->weld_entry("name"))
+ , m_xURLED(new SvtURLBox(m_xBuilder->weld_combo_box("url")))
+ , m_xFilePickerBtn(m_xBuilder->weld_button("browse"))
+ , m_xLinkInstanceCB(m_xBuilder->weld_check_button("link"))
+ , m_xAltTitle(m_xBuilder->weld_label("alttitle"))
+ {
+ if (_bEdit)
+ m_xDialog->set_title(m_xAltTitle->get_label());
+
+ m_xURLED->DisableHistory();
+ m_xFilePickerBtn->connect_clicked(LINK(this, AddInstanceDialog, FilePickerHdl));
+
+ // load the filter name from fps resource
+ m_sAllFilterName = Translate::get(STR_FILTERNAME_ALL, Translate::Create("fps"));
+ }
+
+ AddInstanceDialog::~AddInstanceDialog()
+ {
+ }
+
+ IMPL_LINK_NOARG(AddInstanceDialog, FilePickerHdl, weld::Button&, void)
+ {
+ ::sfx2::FileDialogHelper aDlg(
+ css::ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE,
+ FileDialogFlags::NONE, m_xDialog.get());
+ aDlg.SetContext(sfx2::FileDialogHelper::FormsAddInstance);
+
+ aDlg.AddFilter( m_sAllFilterName, FILEDIALOG_FILTER_ALL );
+ OUString sFilterName( "XML" );
+ aDlg.AddFilter( sFilterName, "*.xml" );
+ aDlg.SetCurrentFilter( sFilterName );
+
+ if (aDlg.Execute() == ERRCODE_NONE)
+ m_xURLED->set_entry_text(aDlg.GetPath());
+ }
+
+ LinkedInstanceWarningBox::LinkedInstanceWarningBox(weld::Widget* pParent)
+ : MessageDialogController(pParent, "svx/ui/formlinkwarndialog.ui",
+ "FormLinkWarnDialog")
+ {
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/dbcharsethelper.cxx b/svx/source/form/dbcharsethelper.cxx
new file mode 100644
index 0000000000..a20ffa7c13
--- /dev/null
+++ b/svx/source/form/dbcharsethelper.cxx
@@ -0,0 +1,48 @@
+/* -*- 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 <dbcharsethelper.hxx>
+
+#include <connectivity/dbcharset.hxx>
+
+using namespace ::dbtools;
+
+namespace svxform::charset_helper
+{
+
+ sal_Int32 getSupportedTextEncodings( ::std::vector< rtl_TextEncoding >& _rEncs )
+ {
+ OCharsetMap aCharsetInfo;
+ _rEncs.clear();
+
+ OCharsetMap::const_iterator aLoop = aCharsetInfo.begin();
+ OCharsetMap::const_iterator aLoopEnd = aCharsetInfo.end();
+ while (aLoop != aLoopEnd)
+ {
+ _rEncs.push_back( (*aLoop).getEncoding() );
+ ++aLoop;
+ }
+
+ return _rEncs.size();
+ }
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/delayedevent.cxx b/svx/source/form/delayedevent.cxx
new file mode 100644
index 0000000000..8a9cb218d3
--- /dev/null
+++ b/svx/source/form/delayedevent.cxx
@@ -0,0 +1,48 @@
+/* -*- 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 <delayedevent.hxx>
+#include <vcl/svapp.hxx>
+#include <sal/log.hxx>
+
+namespace svxform
+{
+ void DelayedEvent::Call()
+ {
+ CancelPendingCall();
+ SAL_WARN_IF( m_nEventId != nullptr, "svx.form", "DelayedEvent::Call: CancelPendingCall did not work!" );
+
+ m_nEventId = Application::PostUserEvent( LINK( this, DelayedEvent, OnCall ) );
+ }
+
+ void DelayedEvent::CancelPendingCall()
+ {
+ if ( m_nEventId )
+ Application::RemoveUserEvent( m_nEventId );
+ m_nEventId = nullptr;
+ }
+
+ IMPL_LINK( DelayedEvent, OnCall, void*, _pArg, void )
+ {
+ m_nEventId = nullptr;
+ m_aHandler.Call( _pArg );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/filtnav.cxx b/svx/source/form/filtnav.cxx
new file mode 100644
index 0000000000..c890d84aba
--- /dev/null
+++ b/svx/source/form/filtnav.cxx
@@ -0,0 +1,1849 @@
+/* -*- 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 <filtnav.hxx>
+#include <fmexch.hxx>
+#include <helpids.h>
+#include <fmprop.hxx>
+#include <svx/strings.hrc>
+
+#include <com/sun/star/awt/XControl.hpp>
+#include <com/sun/star/form/runtime/XFormController.hpp>
+#include <com/sun/star/util/NumberFormatter.hpp>
+#include <com/sun/star/sdb/SQLContext.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/string.hxx>
+#include <connectivity/dbtools.hxx>
+#include <connectivity/sqlnode.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <fmshimp.hxx>
+#include <o3tl/safeint.hxx>
+#include <sfx2/objitem.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/fmshell.hxx>
+#include <svx/fmtools.hxx>
+#include <svx/svxids.hrc>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <utility>
+#include <vcl/commandevent.hxx>
+#include <vcl/event.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+
+#include <bitmaps.hlst>
+
+#include <functional>
+
+using namespace ::svxform;
+using namespace ::connectivity;
+using namespace ::dbtools;
+
+namespace svxform
+{
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::container::XIndexAccess;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::beans::XPropertySet;
+ using ::com::sun::star::form::runtime::XFormController;
+ using ::com::sun::star::form::runtime::XFilterController;
+ using ::com::sun::star::form::runtime::XFilterControllerListener;
+ using ::com::sun::star::form::runtime::FilterEvent;
+ using ::com::sun::star::lang::EventObject;
+ using ::com::sun::star::form::XForm;
+ using ::com::sun::star::container::XChild;
+ using ::com::sun::star::awt::XControl;
+ using ::com::sun::star::sdbc::XConnection;
+ using ::com::sun::star::util::XNumberFormatsSupplier;
+ using ::com::sun::star::util::XNumberFormatter;
+ using ::com::sun::star::util::NumberFormatter;
+ using ::com::sun::star::sdbc::XRowSet;
+ using ::com::sun::star::lang::Locale;
+ using ::com::sun::star::sdb::SQLContext;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::uno::UNO_SET_THROW;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::uno::Sequence;
+
+
+OFilterItemExchange::OFilterItemExchange()
+ : m_pFormItem(nullptr)
+{
+}
+
+void OFilterItemExchange::AddSupportedFormats()
+{
+ AddFormat(getFormatId());
+}
+
+SotClipboardFormatId OFilterItemExchange::getFormatId()
+{
+ static SotClipboardFormatId s_nFormat =
+ SotExchange::RegisterFormatName("application/x-openoffice;windows_formatname=\"form.FilterControlExchange\"");
+ DBG_ASSERT(static_cast<SotClipboardFormatId>(-1) != s_nFormat, "OFilterExchangeHelper::getFormatId: bad exchange id!");
+ return s_nFormat;
+}
+
+rtl::Reference<OLocalExchange> OFilterExchangeHelper::createExchange() const
+{
+ return new OFilterItemExchange;
+}
+
+OUString FmFilterData::GetImage() const
+{
+ return OUString();
+}
+
+FmParentData::~FmParentData()
+{
+}
+
+OUString FmFormItem::GetImage() const
+{
+ return RID_SVXBMP_FORM;
+}
+
+FmFilterItem* FmFilterItems::Find( const ::sal_Int32 _nFilterComponentIndex ) const
+{
+ for ( auto & pData : m_aChildren )
+ {
+ FmFilterItem& rCondition = dynamic_cast<FmFilterItem&>(*pData);
+ if ( _nFilterComponentIndex == rCondition.GetComponentIndex() )
+ return &rCondition;
+ }
+ return nullptr;
+}
+
+OUString FmFilterItems::GetImage() const
+{
+ return RID_SVXBMP_FILTER;
+}
+
+FmFilterItem::FmFilterItem( FmFilterItems* pParent,
+ OUString aFieldName,
+ const OUString& aText,
+ const sal_Int32 _nComponentIndex )
+ :FmFilterData(pParent, aText)
+ ,m_aFieldName(std::move(aFieldName))
+ ,m_nComponentIndex( _nComponentIndex )
+{
+}
+
+OUString FmFilterItem::GetImage() const
+{
+ return RID_SVXBMP_FIELD;
+}
+
+// Hints for communication between model and view
+
+namespace {
+
+class FmFilterHint : public SfxHint
+{
+ FmFilterData* m_pData;
+
+public:
+ explicit FmFilterHint(FmFilterData* pData):m_pData(pData){}
+ FmFilterData* GetData() const { return m_pData; }
+};
+
+class FmFilterInsertedHint : public FmFilterHint
+{
+ size_t m_nPos; // Position relative to the parent of the data
+
+public:
+ FmFilterInsertedHint(FmFilterData* pData, size_t nRelPos)
+ :FmFilterHint(pData)
+ ,m_nPos(nRelPos){}
+
+ size_t GetPos() const { return m_nPos; }
+};
+
+class FmFilterRemovedHint : public FmFilterHint
+{
+public:
+ explicit FmFilterRemovedHint(FmFilterData* pData)
+ :FmFilterHint(pData){}
+};
+
+
+class FmFilterTextChangedHint : public FmFilterHint
+{
+public:
+ explicit FmFilterTextChangedHint(FmFilterData* pData)
+ :FmFilterHint(pData){}
+};
+
+class FilterClearingHint : public SfxHint
+{
+public:
+ FilterClearingHint(){}
+};
+
+class FmFilterCurrentChangedHint : public SfxHint
+{
+public:
+ FmFilterCurrentChangedHint(){}
+};
+
+}
+
+// class FmFilterAdapter, listener at the FilterControls
+class FmFilterAdapter : public ::cppu::WeakImplHelper< XFilterControllerListener >
+{
+ FmFilterModel* m_pModel;
+ Reference< XIndexAccess > m_xControllers;
+
+public:
+ FmFilterAdapter(FmFilterModel* pModel, const Reference< XIndexAccess >& xControllers);
+
+// XEventListener
+ virtual void SAL_CALL disposing(const EventObject& Source) override;
+
+// XFilterControllerListener
+ virtual void SAL_CALL predicateExpressionChanged( const FilterEvent& Event ) override;
+ virtual void SAL_CALL disjunctiveTermRemoved( const FilterEvent& Event ) override;
+ virtual void SAL_CALL disjunctiveTermAdded( const FilterEvent& Event ) override;
+
+// helpers
+ /// @throws RuntimeException
+ void dispose();
+
+ void AddOrRemoveListener( const Reference< XIndexAccess >& _rxControllers, const bool _bAdd );
+
+ static void setText(sal_Int32 nPos,
+ const FmFilterItem* pFilterItem,
+ const OUString& rText);
+};
+
+
+FmFilterAdapter::FmFilterAdapter(FmFilterModel* pModel, const Reference< XIndexAccess >& xControllers)
+ :m_pModel( pModel )
+ ,m_xControllers( xControllers )
+{
+ AddOrRemoveListener( m_xControllers, true );
+}
+
+
+void FmFilterAdapter::dispose()
+{
+ AddOrRemoveListener( m_xControllers, false );
+}
+
+
+void FmFilterAdapter::AddOrRemoveListener( const Reference< XIndexAccess >& _rxControllers, const bool _bAdd )
+{
+ for (sal_Int32 i = 0, nLen = _rxControllers->getCount(); i < nLen; ++i)
+ {
+ Reference< XIndexAccess > xElement( _rxControllers->getByIndex(i), UNO_QUERY );
+
+ // step down
+ AddOrRemoveListener( xElement, _bAdd );
+
+ // handle this particular controller
+ Reference< XFilterController > xController( xElement, UNO_QUERY );
+ OSL_ENSURE( xController.is(), "FmFilterAdapter::InsertElements: no XFilterController, cannot sync data!" );
+ if ( xController.is() )
+ {
+ if ( _bAdd )
+ xController->addFilterControllerListener( this );
+ else
+ xController->removeFilterControllerListener( this );
+ }
+ }
+}
+
+
+void FmFilterAdapter::setText(sal_Int32 nRowPos,
+ const FmFilterItem* pFilterItem,
+ const OUString& rText)
+{
+ FmFormItem* pFormItem = dynamic_cast<FmFormItem*>( pFilterItem->GetParent()->GetParent() );
+ assert(pFormItem);
+ try
+ {
+ Reference< XFilterController > xController( pFormItem->GetController(), UNO_QUERY_THROW );
+ xController->setPredicateExpression( pFilterItem->GetComponentIndex(), nRowPos, rText );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+
+// XEventListener
+
+void SAL_CALL FmFilterAdapter::disposing(const EventObject& /*e*/)
+{
+}
+
+
+namespace
+{
+ OUString lcl_getLabelName_nothrow( const Reference< XControl >& _rxControl )
+ {
+ OUString sLabelName;
+ try
+ {
+ Reference< XPropertySet > xModel( _rxControl->getModel(), UNO_QUERY_THROW );
+ sLabelName = getLabelName( xModel );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return sLabelName;
+ }
+
+ Reference< XPropertySet > lcl_getBoundField_nothrow( const Reference< XControl >& _rxControl )
+ {
+ Reference< XPropertySet > xField;
+ try
+ {
+ Reference< XPropertySet > xModelProps( _rxControl->getModel(), UNO_QUERY_THROW );
+ xField.set( xModelProps->getPropertyValue( FM_PROP_BOUNDFIELD ), UNO_QUERY_THROW );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return xField;
+ }
+}
+
+// XFilterControllerListener
+void FmFilterAdapter::predicateExpressionChanged( const FilterEvent& Event )
+{
+ SolarMutexGuard aGuard;
+
+ if ( !m_pModel )
+ return;
+
+ // the controller which sent the event
+ Reference< XFormController > xController( Event.Source, UNO_QUERY_THROW );
+ Reference< XFilterController > xFilterController( Event.Source, UNO_QUERY_THROW );
+ Reference< XForm > xForm( xController->getModel(), UNO_QUERY_THROW );
+
+ FmFormItem* pFormItem = m_pModel->Find( m_pModel->m_aChildren, xForm );
+ OSL_ENSURE( pFormItem, "FmFilterAdapter::predicateExpressionChanged: don't know this form!" );
+ if ( !pFormItem )
+ return;
+
+ const sal_Int32 nActiveTerm( xFilterController->getActiveTerm() );
+
+ FmFilterData* pData = pFormItem->GetChildren()[nActiveTerm].get();
+ FmFilterItems& rFilter = dynamic_cast<FmFilterItems&>(*pData);
+ FmFilterItem* pFilterItem = rFilter.Find( Event.FilterComponent );
+ if ( pFilterItem )
+ {
+ if ( !Event.PredicateExpression.isEmpty())
+ {
+ pFilterItem->SetText( Event.PredicateExpression );
+ // notify the UI
+ FmFilterTextChangedHint aChangeHint(pFilterItem);
+ m_pModel->Broadcast( aChangeHint );
+ }
+ else
+ {
+ // no text anymore so remove the condition
+ m_pModel->Remove(pFilterItem);
+ }
+ }
+ else
+ {
+ // searching the component by field name
+ OUString aFieldName( lcl_getLabelName_nothrow( xFilterController->getFilterComponent( Event.FilterComponent ) ) );
+
+ std::unique_ptr<FmFilterItem> pNewFilterItem(new FmFilterItem(&rFilter, aFieldName, Event.PredicateExpression, Event.FilterComponent));
+ m_pModel->Insert(rFilter.GetChildren().end(), std::move(pNewFilterItem));
+ }
+
+ // ensure there's one empty term in the filter, just in case the active term was previously empty
+ m_pModel->EnsureEmptyFilterRows( *pFormItem );
+}
+
+
+void SAL_CALL FmFilterAdapter::disjunctiveTermRemoved( const FilterEvent& Event )
+{
+ SolarMutexGuard aGuard;
+
+ Reference< XFormController > xController( Event.Source, UNO_QUERY_THROW );
+ Reference< XFilterController > xFilterController( Event.Source, UNO_QUERY_THROW );
+ Reference< XForm > xForm( xController->getModel(), UNO_QUERY_THROW );
+
+ FmFormItem* pFormItem = m_pModel->Find( m_pModel->m_aChildren, xForm );
+ OSL_ENSURE( pFormItem, "FmFilterAdapter::disjunctiveTermRemoved: don't know this form!" );
+ if ( !pFormItem )
+ return;
+
+ auto& rTermItems = pFormItem->GetChildren();
+ const bool bValidIndex = ( Event.DisjunctiveTerm >= 0 ) && ( o3tl::make_unsigned(Event.DisjunctiveTerm) < rTermItems.size() );
+ OSL_ENSURE( bValidIndex, "FmFilterAdapter::disjunctiveTermRemoved: invalid term index!" );
+ if ( !bValidIndex )
+ return;
+
+ // if the first term was removed, then the to-be first term needs its text updated
+ if ( Event.DisjunctiveTerm == 0 )
+ {
+ rTermItems[1]->SetText( SvxResId(RID_STR_FILTER_FILTER_FOR));
+ FmFilterTextChangedHint aChangeHint( rTermItems[1].get() );
+ m_pModel->Broadcast( aChangeHint );
+ }
+
+ // finally remove the entry from the model
+ m_pModel->Remove( rTermItems.begin() + Event.DisjunctiveTerm );
+
+ // ensure there's one empty term in the filter, just in case the currently removed one was the last empty one
+ m_pModel->EnsureEmptyFilterRows( *pFormItem );
+}
+
+
+void SAL_CALL FmFilterAdapter::disjunctiveTermAdded( const FilterEvent& Event )
+{
+ SolarMutexGuard aGuard;
+
+ Reference< XFormController > xController( Event.Source, UNO_QUERY_THROW );
+ Reference< XFilterController > xFilterController( Event.Source, UNO_QUERY_THROW );
+ Reference< XForm > xForm( xController->getModel(), UNO_QUERY_THROW );
+
+ FmFormItem* pFormItem = m_pModel->Find( m_pModel->m_aChildren, xForm );
+ OSL_ENSURE( pFormItem, "FmFilterAdapter::disjunctiveTermAdded: don't know this form!" );
+ if ( !pFormItem )
+ return;
+
+ const sal_Int32 nInsertPos = Event.DisjunctiveTerm;
+ bool bValidIndex = ( nInsertPos >= 0 ) && ( o3tl::make_unsigned(nInsertPos) <= pFormItem->GetChildren().size() );
+ if ( !bValidIndex )
+ {
+ OSL_FAIL( "FmFilterAdapter::disjunctiveTermAdded: invalid index!" );
+ return;
+ }
+
+ auto insertPos = pFormItem->GetChildren().begin() + nInsertPos;
+
+ // "Filter for" for first position, "Or" for the other positions
+ std::unique_ptr<FmFilterItems> pFilterItems(new FmFilterItems(pFormItem, (nInsertPos?SvxResId(RID_STR_FILTER_FILTER_OR):SvxResId(RID_STR_FILTER_FILTER_FOR))));
+ m_pModel->Insert( insertPos, std::move(pFilterItems) );
+}
+
+
+FmFilterModel::FmFilterModel()
+ :FmParentData(nullptr, OUString())
+ ,OSQLParserClient(comphelper::getProcessComponentContext())
+ ,m_pCurrentItems(nullptr)
+{
+}
+
+
+FmFilterModel::~FmFilterModel()
+{
+ Clear();
+}
+
+
+void FmFilterModel::Clear()
+{
+ // notify
+ FilterClearingHint aClearedHint;
+ Broadcast( aClearedHint );
+
+ // lose endings
+ if (m_pAdapter.is())
+ {
+ m_pAdapter->dispose();
+ m_pAdapter.clear();
+ }
+
+ m_pCurrentItems = nullptr;
+ m_xController = nullptr;
+ m_xControllers = nullptr;
+
+ m_aChildren.clear();
+}
+
+
+void FmFilterModel::Update(const Reference< XIndexAccess > & xControllers, const Reference< XFormController > & xCurrent)
+{
+ if ( xCurrent == m_xController )
+ return;
+
+ if (!xControllers.is())
+ {
+ Clear();
+ return;
+ }
+
+ // there is only a new current controller
+ if ( m_xControllers != xControllers )
+ {
+ Clear();
+
+ m_xControllers = xControllers;
+ Update(m_xControllers, this);
+
+ DBG_ASSERT(xCurrent.is(), "FmFilterModel::Update(...) no current controller");
+
+ // Listening for TextChanges
+ m_pAdapter = new FmFilterAdapter(this, xControllers);
+
+ SetCurrentController(xCurrent);
+ EnsureEmptyFilterRows( *this );
+ }
+ else
+ SetCurrentController(xCurrent);
+}
+
+
+void FmFilterModel::Update(const Reference< XIndexAccess > & xControllers, FmParentData* pParent)
+{
+ try
+ {
+ sal_Int32 nCount = xControllers->getCount();
+ for ( sal_Int32 i = 0; i < nCount; ++i )
+ {
+ Reference< XFormController > xController( xControllers->getByIndex(i), UNO_QUERY_THROW );
+
+ Reference< XPropertySet > xFormProperties( xController->getModel(), UNO_QUERY_THROW );
+ OUString aName;
+ OSL_VERIFY( xFormProperties->getPropertyValue( FM_PROP_NAME ) >>= aName );
+
+ // Insert a new item for the form
+ FmFormItem* pFormItem = new FmFormItem( pParent, xController, aName );
+ Insert( pParent->GetChildren().end(), std::unique_ptr<FmFilterData>(pFormItem) );
+
+ Reference< XFilterController > xFilterController( pFormItem->GetFilterController(), UNO_SET_THROW );
+
+ // insert the existing filters for the form
+ OUString aTitle(SvxResId(RID_STR_FILTER_FILTER_FOR));
+
+ const Sequence< Sequence< OUString > > aExpressions = xFilterController->getPredicateExpressions();
+ for ( auto const & conjunctionTerm : aExpressions )
+ {
+ // we always display one row, even if there's no term to be displayed
+ FmFilterItems* pFilterItems = new FmFilterItems( pFormItem, aTitle );
+ Insert( pFormItem->GetChildren().end(), std::unique_ptr<FmFilterData>(pFilterItems) );
+
+ const Sequence< OUString >& rDisjunction( conjunctionTerm );
+ sal_Int32 nComponentIndex = -1;
+ for ( const OUString& rDisjunctiveTerm : rDisjunction )
+ {
+ ++nComponentIndex;
+
+ if ( rDisjunctiveTerm.isEmpty() )
+ // no condition for this particular component in this particular conjunction term
+ continue;
+
+ // determine the display name of the control
+ const Reference< XControl > xFilterControl( xFilterController->getFilterComponent( nComponentIndex ) );
+ const OUString sDisplayName( lcl_getLabelName_nothrow( xFilterControl ) );
+
+ // insert a new entry
+ std::unique_ptr<FmFilterItem> pANDCondition(new FmFilterItem( pFilterItems, sDisplayName, rDisjunctiveTerm, nComponentIndex ));
+ Insert( pFilterItems->GetChildren().end(), std::move(pANDCondition) );
+ }
+
+ // title for the next conditions
+ aTitle = SvxResId( RID_STR_FILTER_FILTER_OR );
+ }
+
+ // now add dependent controllers
+ Update( xController, pFormItem );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+
+FmFormItem* FmFilterModel::Find(const ::std::vector<std::unique_ptr<FmFilterData>>& rItems, const Reference< XFormController > & xController) const
+{
+ for (const auto& rItem : rItems)
+ {
+ FmFormItem* pForm = dynamic_cast<FmFormItem*>( rItem.get() );
+ if (pForm)
+ {
+ if ( xController == pForm->GetController() )
+ return pForm;
+ else
+ {
+ pForm = Find(pForm->GetChildren(), xController);
+ if (pForm)
+ return pForm;
+ }
+ }
+ }
+ return nullptr;
+}
+
+
+FmFormItem* FmFilterModel::Find(const ::std::vector<std::unique_ptr<FmFilterData>>& rItems, const Reference< XForm >& xForm) const
+{
+ for (const auto& rItem : rItems)
+ {
+ FmFormItem* pForm = dynamic_cast<FmFormItem*>( rItem.get() );
+ if (pForm)
+ {
+ if (xForm == pForm->GetController()->getModel())
+ return pForm;
+ else
+ {
+ pForm = Find(pForm->GetChildren(), xForm);
+ if (pForm)
+ return pForm;
+ }
+ }
+ }
+ return nullptr;
+}
+
+void FmFilterModel::SetCurrentController(const Reference< XFormController > & xCurrent)
+{
+ if ( xCurrent == m_xController )
+ return;
+
+ m_xController = xCurrent;
+
+ FmFormItem* pItem = Find( m_aChildren, xCurrent );
+ if ( !pItem )
+ return;
+
+ try
+ {
+ Reference< XFilterController > xFilterController( m_xController, UNO_QUERY_THROW );
+ const sal_Int32 nActiveTerm( xFilterController->getActiveTerm() );
+ if (nActiveTerm != -1 && pItem->GetChildren().size() > o3tl::make_unsigned(nActiveTerm))
+ {
+ SetCurrentItems( static_cast< FmFilterItems* >( pItem->GetChildren()[ nActiveTerm ].get() ) );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+void FmFilterModel::AppendFilterItems( FmFormItem& _rFormItem )
+{
+ // insert the condition behind the last filter items
+ auto iter = std::find_if(_rFormItem.GetChildren().rbegin(), _rFormItem.GetChildren().rend(),
+ [](const std::unique_ptr<FmFilterData>& rChild) { return dynamic_cast<const FmFilterItems*>(rChild.get()) != nullptr; });
+
+ sal_Int32 nInsertPos = iter.base() - _rFormItem.GetChildren().begin();
+ // delegate this to the FilterController, it will notify us, which will let us update our model
+ try
+ {
+ Reference< XFilterController > xFilterController( _rFormItem.GetFilterController(), UNO_SET_THROW );
+ if ( nInsertPos >= xFilterController->getDisjunctiveTerms() )
+ xFilterController->appendEmptyDisjunctiveTerm();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+void FmFilterModel::Insert(const ::std::vector<std::unique_ptr<FmFilterData>>::iterator& rPos, std::unique_ptr<FmFilterData> pData)
+{
+ auto pTemp = pData.get();
+ size_t nPos;
+ ::std::vector<std::unique_ptr<FmFilterData>>& rItems = pData->GetParent()->GetChildren();
+ if (rPos == rItems.end())
+ {
+ nPos = rItems.size();
+ rItems.push_back(std::move(pData));
+ }
+ else
+ {
+ nPos = rPos - rItems.begin();
+ rItems.insert(rPos, std::move(pData));
+ }
+
+ // notify the UI
+ FmFilterInsertedHint aInsertedHint(pTemp, nPos);
+ Broadcast( aInsertedHint );
+}
+
+void FmFilterModel::Remove(FmFilterData* pData)
+{
+ FmParentData* pParent = pData->GetParent();
+ ::std::vector<std::unique_ptr<FmFilterData>>& rItems = pParent->GetChildren();
+
+ // erase the item from the model
+ auto i = ::std::find_if(rItems.begin(), rItems.end(),
+ [&](const std::unique_ptr<FmFilterData>& p) { return p.get() == pData; } );
+ DBG_ASSERT(i != rItems.end(), "FmFilterModel::Remove(): unknown Item");
+ // position within the parent
+ sal_Int32 nPos = i - rItems.begin();
+ if (auto pFilterItems = dynamic_cast<FmFilterItems*>( pData))
+ {
+ FmFormItem* pFormItem = static_cast<FmFormItem*>(pParent);
+
+ try
+ {
+ Reference< XFilterController > xFilterController( pFormItem->GetFilterController(), UNO_SET_THROW );
+
+ bool bEmptyLastTerm = ( ( nPos == 0 ) && xFilterController->getDisjunctiveTerms() == 1 );
+ if ( bEmptyLastTerm )
+ {
+ // remove all children (by setting an empty predicate expression)
+ ::std::vector< std::unique_ptr<FmFilterData> >& rChildren = pFilterItems->GetChildren();
+ while ( !rChildren.empty() )
+ {
+ auto removePos = rChildren.end() - 1;
+ if (FmFilterItem* pFilterItem = dynamic_cast<FmFilterItem*>( removePos->get() ))
+ {
+ FmFilterAdapter::setText( nPos, pFilterItem, OUString() );
+ }
+ Remove( removePos );
+ }
+ }
+ else
+ {
+ xFilterController->removeDisjunctiveTerm( nPos );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ else // FormItems can not be deleted
+ {
+ FmFilterItem& rFilterItem = dynamic_cast<FmFilterItem&>(*pData);
+
+ // if it's the last condition remove the parent
+ if (rItems.size() == 1)
+ Remove(rFilterItem.GetParent());
+ else
+ {
+ // find the position of the father within his father
+ ::std::vector<std::unique_ptr<FmFilterData>>& rParentParentItems = pData->GetParent()->GetParent()->GetChildren();
+ auto j = ::std::find_if(rParentParentItems.begin(), rParentParentItems.end(),
+ [&](const std::unique_ptr<FmFilterData>& p) { return p.get() == rFilterItem.GetParent(); });
+ DBG_ASSERT(j != rParentParentItems.end(), "FmFilterModel::Remove(): unknown Item");
+ sal_Int32 nParentPos = j - rParentParentItems.begin();
+
+ // EmptyText removes the filter
+ FmFilterAdapter::setText(nParentPos, &rFilterItem, OUString());
+ Remove( i );
+ }
+ }
+}
+
+void FmFilterModel::Remove( const ::std::vector<std::unique_ptr<FmFilterData>>::iterator& rPos )
+{
+ // remove from parent's child list
+ std::unique_ptr<FmFilterData> pData = std::move(*rPos);
+ pData->GetParent()->GetChildren().erase( rPos );
+
+ // notify the view, this will remove the actual SvTreeListEntry
+ FmFilterRemovedHint aRemoveHint( pData.get() );
+ Broadcast( aRemoveHint );
+}
+
+
+bool FmFilterModel::ValidateText(FmFilterItem const * pItem, OUString& rText, OUString& rErrorMsg) const
+{
+ FmFormItem* pFormItem = dynamic_cast<FmFormItem*>( pItem->GetParent()->GetParent() );
+ assert(pFormItem);
+ try
+ {
+ Reference< XFormController > xFormController( pFormItem->GetController() );
+ // obtain the connection of the form belonging to the controller
+ Reference< XRowSet > xRowSet( xFormController->getModel(), UNO_QUERY_THROW );
+ Reference< XConnection > xConnection( getConnection( xRowSet ) );
+
+ // obtain a number formatter for this connection
+ // TODO: shouldn't this be cached?
+ Reference< XNumberFormatsSupplier > xFormatSupplier = getNumberFormats( xConnection, true );
+ Reference< XNumberFormatter > xFormatter( NumberFormatter::create( comphelper::getProcessComponentContext() ), UNO_QUERY_THROW );
+ xFormatter->attachNumberFormatsSupplier( xFormatSupplier );
+
+ // get the field (database column) which the item is responsible for
+ Reference< XFilterController > xFilterController( xFormController, UNO_QUERY_THROW );
+ Reference< XPropertySet > xField( lcl_getBoundField_nothrow( xFilterController->getFilterComponent( pItem->GetComponentIndex() ) ), UNO_SET_THROW );
+
+ // parse the given text as filter predicate
+ OUString aErr, aTxt( rText );
+ std::unique_ptr< OSQLParseNode > pParseNode = predicateTree( aErr, aTxt, xFormatter, xField );
+ rErrorMsg = aErr;
+ rText = aTxt;
+ if ( pParseNode != nullptr )
+ {
+ OUString aPreparedText;
+ Locale aAppLocale = Application::GetSettings().GetUILanguageTag().getLocale();
+ pParseNode->parseNodeToPredicateStr(
+ aPreparedText, xConnection, xFormatter, xField, OUString(), aAppLocale, ".", getParseContext() );
+ rText = aPreparedText;
+ return true;
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ return false;
+}
+
+
+void FmFilterModel::Append(FmFilterItems* pItems, std::unique_ptr<FmFilterItem> pFilterItem)
+{
+ Insert(pItems->GetChildren().end(), std::move(pFilterItem));
+}
+
+
+void FmFilterModel::SetTextForItem(FmFilterItem* pItem, const OUString& rText)
+{
+ ::std::vector<std::unique_ptr<FmFilterData>>& rItems = pItem->GetParent()->GetParent()->GetChildren();
+ auto i = ::std::find_if(rItems.begin(), rItems.end(),
+ [&](const std::unique_ptr<FmFilterData>& p) { return p.get() == pItem->GetParent(); });
+ sal_Int32 nParentPos = i - rItems.begin();
+
+ FmFilterAdapter::setText(nParentPos, pItem, rText);
+
+ if (rText.isEmpty())
+ Remove(pItem);
+ else
+ {
+ // Change the text
+ pItem->SetText(rText);
+ FmFilterTextChangedHint aChangeHint(pItem);
+ Broadcast( aChangeHint );
+ }
+}
+
+
+void FmFilterModel::SetCurrentItems(FmFilterItems* pCurrent)
+{
+ if (m_pCurrentItems == pCurrent)
+ return;
+
+ // search for the condition
+ if (pCurrent)
+ {
+ FmFormItem* pFormItem = static_cast<FmFormItem*>(pCurrent->GetParent());
+ ::std::vector<std::unique_ptr<FmFilterData>>& rItems = pFormItem->GetChildren();
+ auto i = ::std::find_if(rItems.begin(), rItems.end(),
+ [&](const std::unique_ptr<FmFilterData>& p) { return p.get() == pCurrent; });
+
+ if (i != rItems.end())
+ {
+ // determine the filter position
+ sal_Int32 nPos = i - rItems.begin();
+ try
+ {
+ Reference< XFilterController > xFilterController( pFormItem->GetFilterController(), UNO_SET_THROW );
+ xFilterController->setActiveTerm( nPos );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ if ( m_xController != pFormItem->GetController() )
+ // calls SetCurrentItems again
+ SetCurrentController( pFormItem->GetController() );
+ else
+ m_pCurrentItems = pCurrent;
+ }
+ else
+ m_pCurrentItems = nullptr;
+ }
+ else
+ m_pCurrentItems = nullptr;
+
+
+ // notify the UI
+ FmFilterCurrentChangedHint aHint;
+ Broadcast( aHint );
+}
+
+
+void FmFilterModel::EnsureEmptyFilterRows( FmParentData& _rItem )
+{
+ // checks whether for each form there's one free level for input
+ ::std::vector< std::unique_ptr<FmFilterData> >& rChildren = _rItem.GetChildren();
+ bool bAppendLevel = dynamic_cast<const FmFormItem*>(&_rItem) != nullptr;
+
+ for ( const auto& rpChild : rChildren )
+ {
+ FmFilterItems* pItems = dynamic_cast<FmFilterItems*>( rpChild.get() );
+ if ( pItems && pItems->GetChildren().empty() )
+ {
+ bAppendLevel = false;
+ break;
+ }
+
+ FmFormItem* pFormItem = dynamic_cast<FmFormItem*>( rpChild.get() );
+ if (pFormItem)
+ {
+ EnsureEmptyFilterRows( *pFormItem );
+ continue;
+ }
+ }
+
+ if ( bAppendLevel )
+ {
+ FmFormItem* pFormItem = dynamic_cast<FmFormItem*>( &_rItem );
+ OSL_ENSURE( pFormItem, "FmFilterModel::EnsureEmptyFilterRows: no FmFormItem, but a FmFilterItems child?" );
+ if ( pFormItem )
+ AppendFilterItems( *pFormItem );
+ }
+}
+
+const int nxD = 4;
+const int nxDBmp = 12;
+
+IMPL_STATIC_LINK(FmFilterNavigator, CustomGetSizeHdl, weld::TreeView::get_size_args, aPayload, Size)
+{
+ vcl::RenderContext& rRenderContext = aPayload.first;
+ const OUString& rId = aPayload.second;
+
+ Size aSize;
+
+ FmFilterData* pData = weld::fromId<FmFilterData*>(rId);
+ OUString sText = pData->GetText();
+
+ if (FmFilterItem* pItem = dynamic_cast<FmFilterItem*>(pData))
+ {
+ rRenderContext.Push(vcl::PushFlags::FONT);
+ vcl::Font aFont(rRenderContext.GetFont());
+ aFont.SetWeight(WEIGHT_BOLD);
+ rRenderContext.SetFont(aFont);
+
+ OUString sName = pItem->GetFieldName() + ": ";
+ aSize = Size(rRenderContext.GetTextWidth(sName), rRenderContext.GetTextHeight());
+
+ rRenderContext.Pop();
+
+ aSize.AdjustWidth(rRenderContext.GetTextWidth(sText) + nxD);
+ }
+ else
+ {
+ aSize = Size(rRenderContext.GetTextWidth(sText), rRenderContext.GetTextHeight());
+ if (dynamic_cast<FmFilterItems*>(pData))
+ aSize.AdjustWidth(nxDBmp);
+ }
+
+ return aSize;
+}
+
+IMPL_STATIC_LINK(FmFilterNavigator, CustomRenderHdl, weld::TreeView::render_args, aPayload, void)
+{
+ vcl::RenderContext& rRenderContext = std::get<0>(aPayload);
+ const ::tools::Rectangle& rRect = std::get<1>(aPayload);
+ ::tools::Rectangle aRect(rRect.TopLeft(), Size(rRenderContext.GetOutputSize().Width() - rRect.Left(), rRect.GetHeight()));
+ bool bSelected = std::get<2>(aPayload);
+ const OUString& rId = std::get<3>(aPayload);
+
+ rRenderContext.Push(vcl::PushFlags::TEXTCOLOR);
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ if (bSelected)
+ rRenderContext.SetTextColor(rStyleSettings.GetHighlightTextColor());
+ else
+ rRenderContext.SetTextColor(rStyleSettings.GetDialogTextColor());
+
+ FmFilterData* pData = weld::fromId<FmFilterData*>(rId);
+ OUString sText = pData->GetText();
+ Point aPos(aRect.TopLeft());
+
+ if (FmFilterItem* pFilter = dynamic_cast<FmFilterItem*>(pData))
+ {
+ vcl::Font aFont(rRenderContext.GetFont());
+ aFont.SetWeight(WEIGHT_BOLD);
+
+ rRenderContext.Push(vcl::PushFlags::FONT);
+ rRenderContext.SetFont(aFont);
+
+ OUString sName = pFilter->GetFieldName() + ": ";
+ rRenderContext.DrawText(aPos, sName);
+
+ // position for the second text
+ aPos.AdjustX(rRenderContext.GetTextWidth(sName) + nxD);
+ rRenderContext.Pop();
+
+ rRenderContext.DrawText(aPos, sText);
+ }
+ else if (FmFilterItems* pRow = dynamic_cast<FmFilterItems*>(pData))
+ {
+ FmFormItem* pForm = static_cast<FmFormItem*>(pRow->GetParent());
+
+ // current filter is significant painted
+ const bool bIsCurrentFilter = pForm->GetChildren()[ pForm->GetFilterController()->getActiveTerm() ].get() == pRow;
+ if (bIsCurrentFilter)
+ {
+ rRenderContext.Push(vcl::PushFlags::LINECOLOR);
+ rRenderContext.SetLineColor(rRenderContext.GetTextColor());
+
+ Point aFirst(aPos.X(), aRect.Bottom() - 6);
+ Point aSecond(aFirst .X() + 2, aFirst.Y() + 3);
+
+ rRenderContext.DrawLine(aFirst, aSecond);
+
+ aFirst = aSecond;
+ aFirst.AdjustX(1);
+ aSecond.AdjustX(6);
+ aSecond.AdjustY(-5);
+
+ rRenderContext.DrawLine(aFirst, aSecond);
+ rRenderContext.Pop();
+ }
+
+ rRenderContext.DrawText(Point(aPos.X() + nxDBmp, aPos.Y()), sText);
+ }
+ else
+ rRenderContext.DrawText(aPos, sText);
+
+ rRenderContext.Pop();
+}
+
+FmFilterNavigatorDropTarget::FmFilterNavigatorDropTarget(FmFilterNavigator& rTreeView)
+ : DropTargetHelper(rTreeView.get_widget().get_drop_target())
+ , m_rTreeView(rTreeView)
+{
+}
+
+sal_Int8 FmFilterNavigatorDropTarget::AcceptDrop(const AcceptDropEvent& rEvt)
+{
+ sal_Int8 nAccept = m_rTreeView.AcceptDrop(rEvt);
+
+ if (nAccept != DND_ACTION_NONE)
+ {
+ // to enable the autoscroll when we're close to the edges
+ weld::TreeView& rWidget = m_rTreeView.get_widget();
+ rWidget.get_dest_row_at_pos(rEvt.maPosPixel, nullptr, true);
+ }
+
+ return nAccept;
+}
+
+sal_Int8 FmFilterNavigatorDropTarget::ExecuteDrop(const ExecuteDropEvent& rEvt)
+{
+ return m_rTreeView.ExecuteDrop(rEvt);
+}
+
+FmFilterNavigator::FmFilterNavigator(vcl::Window* pTopLevel, std::unique_ptr<weld::TreeView> xTreeView)
+ : m_xTopLevel(pTopLevel)
+ , m_xTreeView(std::move(xTreeView))
+ , m_aDropTargetHelper(*this)
+ , m_nAsyncRemoveEvent(nullptr)
+{
+ m_xTreeView->set_help_id(HID_FILTER_NAVIGATOR);
+
+ m_xTreeView->set_selection_mode(SelectionMode::Multiple);
+
+ m_pModel.reset( new FmFilterModel() );
+ StartListening( *m_pModel );
+
+ m_xTreeView->connect_custom_get_size(LINK(this, FmFilterNavigator, CustomGetSizeHdl));
+ m_xTreeView->connect_custom_render(LINK(this, FmFilterNavigator, CustomRenderHdl));
+ m_xTreeView->set_column_custom_renderer(0, true);
+
+ m_xTreeView->connect_changed(LINK(this, FmFilterNavigator, SelectHdl));
+ m_xTreeView->connect_key_press(LINK(this, FmFilterNavigator, KeyInputHdl));
+ m_xTreeView->connect_popup_menu(LINK(this, FmFilterNavigator, PopupMenuHdl));
+ m_xTreeView->connect_editing(LINK(this, FmFilterNavigator, EditingEntryHdl),
+ LINK(this, FmFilterNavigator, EditedEntryHdl));
+ m_xTreeView->connect_drag_begin(LINK(this, FmFilterNavigator, DragBeginHdl));
+}
+
+FmFilterNavigator::~FmFilterNavigator()
+{
+ if (m_nAsyncRemoveEvent)
+ Application::RemoveUserEvent(m_nAsyncRemoveEvent);
+ EndListening(*m_pModel);
+ m_pModel.reset();
+}
+
+void FmFilterNavigator::UpdateContent(const Reference< XIndexAccess > & xControllers, const Reference< XFormController > & xCurrent)
+{
+ if (xCurrent == m_pModel->GetCurrentController())
+ return;
+
+ m_pModel->Update(xControllers, xCurrent);
+
+ // expand the filters for the current controller
+ std::unique_ptr<weld::TreeIter> xEntry = FindEntry(m_pModel->GetCurrentForm());
+ if (!xEntry || m_xTreeView->get_row_expanded(*xEntry))
+ return;
+
+ m_xTreeView->unselect_all();
+
+ m_xTreeView->expand_row(*xEntry);
+
+ xEntry = FindEntry(m_pModel->GetCurrentItems());
+ if (xEntry)
+ {
+ if (!m_xTreeView->get_row_expanded(*xEntry))
+ m_xTreeView->expand_row(*xEntry);
+ m_xTreeView->select(*xEntry);
+ SelectHdl(*m_xTreeView);
+ }
+}
+
+IMPL_LINK(FmFilterNavigator, EditingEntryHdl, const weld::TreeIter&, rIter, bool)
+{
+ // returns true to allow editing
+ if (dynamic_cast<const FmFilterItem*>(weld::fromId<FmFilterData*>(m_xTreeView->get_id(rIter))))
+ {
+ m_xEditingCurrently = m_xTreeView->make_iterator(&rIter);
+ return true;
+ }
+ m_xEditingCurrently.reset();
+ return false;
+}
+
+IMPL_LINK(FmFilterNavigator, EditedEntryHdl, const IterString&, rIterString, bool)
+{
+ const weld::TreeIter& rIter = rIterString.first;
+ const OUString& rNewText = rIterString.second;
+
+ assert(m_xEditingCurrently && m_xTreeView->iter_compare(rIter, *m_xEditingCurrently) == 0 &&
+ "FmFilterNavigator::EditedEntry: suspicious entry!");
+ m_xEditingCurrently.reset();
+
+ FmFilterData* pData = weld::fromId<FmFilterData*>(m_xTreeView->get_id(rIter));
+
+ DBG_ASSERT(dynamic_cast<const FmFilterItem*>(pData) != nullptr,
+ "FmFilterNavigator::EditedEntry() wrong entry");
+
+ OUString aText(comphelper::string::strip(rNewText, ' '));
+ if (aText.isEmpty())
+ {
+ // deleting the entry asynchron
+ m_nAsyncRemoveEvent = Application::PostUserEvent(LINK(this, FmFilterNavigator, OnRemove), pData);
+ }
+ else
+ {
+ OUString aErrorMsg;
+
+ if (m_pModel->ValidateText(static_cast<FmFilterItem*>(pData), aText, aErrorMsg))
+ {
+ // this will set the text at the FmFilterItem, as well as update any filter controls
+ // which are connected to this particular entry
+ m_pModel->SetTextForItem(static_cast<FmFilterItem*>(pData), aText);
+ m_xTreeView->set_text(rIter, aText);
+ }
+ else
+ {
+ // display the error and return sal_False
+ SQLContext aError(SvxResId(RID_STR_SYNTAXERROR), {}, {}, 0, {}, aErrorMsg);
+ displayException(aError, VCLUnoHelper::GetInterface(m_xTopLevel));
+
+ return false;
+ }
+ }
+ return true;
+}
+
+IMPL_LINK( FmFilterNavigator, OnRemove, void*, p, void )
+{
+ m_nAsyncRemoveEvent = nullptr;
+ // now remove the entry
+ m_pModel->Remove(static_cast<FmFilterData*>(p));
+}
+
+sal_Int8 FmFilterNavigator::AcceptDrop( const AcceptDropEvent& rEvt )
+{
+ if (!m_aControlExchange.isDragSource())
+ return DND_ACTION_NONE;
+
+ if (!OFilterItemExchange::hasFormat(m_aDropTargetHelper.GetDataFlavorExVector()))
+ return DND_ACTION_NONE;
+
+ // do we contain the formitem?
+ if (!FindEntry(m_aControlExchange->getFormItem()))
+ return DND_ACTION_NONE;
+
+ Point aDropPos = rEvt.maPosPixel;
+ std::unique_ptr<weld::TreeIter> xDropTarget(m_xTreeView->make_iterator());
+ // get_dest_row_at_pos with false cause we must drop exactly "on" a node to paste a condition into it
+ if (!m_xTreeView->get_dest_row_at_pos(aDropPos, xDropTarget.get(), false))
+ xDropTarget.reset();
+
+ if (!xDropTarget)
+ return DND_ACTION_NONE;
+
+ FmFilterData* pData = weld::fromId<FmFilterData*>(m_xTreeView->get_id(*xDropTarget));
+ FmFormItem* pForm = nullptr;
+ if (dynamic_cast<const FmFilterItem*>(pData) != nullptr)
+ {
+ pForm = dynamic_cast<FmFormItem*>( pData->GetParent()->GetParent() );
+ if (pForm != m_aControlExchange->getFormItem())
+ return DND_ACTION_NONE;
+ }
+ else if (dynamic_cast<const FmFilterItems*>( pData) != nullptr)
+ {
+ pForm = dynamic_cast<FmFormItem*>( pData->GetParent() );
+ if (pForm != m_aControlExchange->getFormItem())
+ return DND_ACTION_NONE;
+ }
+ else
+ return DND_ACTION_NONE;
+
+ return rEvt.mnAction;
+}
+
+namespace
+{
+ FmFilterItems* getTargetItems(const weld::TreeView& rTreeView, const weld::TreeIter& rTarget)
+ {
+ FmFilterData* pData = weld::fromId<FmFilterData*>(rTreeView.get_id(rTarget));
+ FmFilterItems* pTargetItems = dynamic_cast<FmFilterItems*>(pData);
+ if (!pTargetItems)
+ pTargetItems = dynamic_cast<FmFilterItems*>(pData->GetParent());
+ return pTargetItems;
+ }
+}
+
+sal_Int8 FmFilterNavigator::ExecuteDrop( const ExecuteDropEvent& rEvt )
+{
+ if (!m_aControlExchange.isDragSource())
+ return DND_ACTION_NONE;
+
+ Point aDropPos = rEvt.maPosPixel;
+ std::unique_ptr<weld::TreeIter> xDropTarget(m_xTreeView->make_iterator());
+ // get_dest_row_at_pos with false cause we must drop exactly "on" a node to paste a condition into it
+ if (!m_xTreeView->get_dest_row_at_pos(aDropPos, xDropTarget.get(), false))
+ xDropTarget.reset();
+ if (!xDropTarget)
+ return DND_ACTION_NONE;
+
+ // search the container where to add the items
+ FmFilterItems* pTargetItems = getTargetItems(*m_xTreeView, *xDropTarget);
+ m_xTreeView->unselect_all();
+ std::unique_ptr<weld::TreeIter> xEntry = FindEntry(pTargetItems);
+ if (xEntry)
+ {
+ m_xTreeView->select(*xEntry);
+ m_xTreeView->set_cursor(*xEntry);
+ }
+
+ insertFilterItem(m_aControlExchange->getDraggedEntries(),pTargetItems,DND_ACTION_COPY == rEvt.mnAction);
+
+ return DND_ACTION_COPY;
+}
+
+IMPL_LINK_NOARG(FmFilterNavigator, SelectHdl, weld::TreeView&, void)
+{
+ std::unique_ptr<weld::TreeIter> xIter(m_xTreeView->make_iterator());
+ if (!m_xTreeView->get_selected(xIter.get()))
+ return;
+
+ FmFilterData* pData = weld::fromId<FmFilterData*>(m_xTreeView->get_id(*xIter));
+
+ FmFormItem* pFormItem = nullptr;
+ if (FmFilterItem* pItem = dynamic_cast<FmFilterItem*>(pData))
+ pFormItem = static_cast<FmFormItem*>(pItem->GetParent()->GetParent());
+ else if (FmFilterItems* pItems = dynamic_cast<FmFilterItems*>(pData))
+ pFormItem = static_cast<FmFormItem*>(pItems->GetParent()->GetParent());
+ else
+ pFormItem = dynamic_cast<FmFormItem*>(pData);
+
+ if (pFormItem)
+ {
+ // will the controller be exchanged?
+ if (FmFilterItem* pItem = dynamic_cast<FmFilterItem*>(pData))
+ m_pModel->SetCurrentItems(static_cast<FmFilterItems*>(pItem->GetParent()));
+ else if (FmFilterItems* pItems = dynamic_cast<FmFilterItems*>(pData))
+ m_pModel->SetCurrentItems(pItems);
+ else
+ m_pModel->SetCurrentController(pFormItem->GetController());
+ }
+}
+
+void FmFilterNavigator::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
+{
+ if (const FmFilterInsertedHint* pInsertHint = dynamic_cast<const FmFilterInsertedHint*>(&rHint))
+ {
+ Insert(pInsertHint->GetData(), pInsertHint->GetPos());
+ }
+ else if( dynamic_cast<const FilterClearingHint*>(&rHint) )
+ {
+ m_xTreeView->clear();
+ }
+ else if (const FmFilterRemovedHint* pRemoveHint = dynamic_cast<const FmFilterRemovedHint*>(&rHint))
+ {
+ Remove(pRemoveHint->GetData());
+ }
+ else if (const FmFilterTextChangedHint *pChangeHint = dynamic_cast<const FmFilterTextChangedHint*>(&rHint))
+ {
+ std::unique_ptr<weld::TreeIter> xEntry = FindEntry(pChangeHint->GetData());
+ if (xEntry)
+ m_xTreeView->set_text(*xEntry, pChangeHint->GetData()->GetText());
+ }
+ else if( dynamic_cast<const FmFilterCurrentChangedHint*>(&rHint) )
+ {
+ m_xTreeView->queue_draw();
+ }
+}
+
+std::unique_ptr<weld::TreeIter> FmFilterNavigator::FindEntry(const FmFilterData* pItem) const
+{
+ if (!pItem)
+ return nullptr;
+ std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator();
+ if (!m_xTreeView->get_iter_first(*xEntry))
+ return nullptr;
+ do
+ {
+ FmFilterData* pEntryItem = weld::fromId<FmFilterData*>(m_xTreeView->get_id(*xEntry));
+ if (pEntryItem == pItem)
+ return xEntry;
+ }
+ while (m_xTreeView->iter_next(*xEntry));
+
+ return nullptr;
+}
+
+void FmFilterNavigator::Insert(const FmFilterData* pItem, int nPos)
+{
+ const FmParentData* pParent = pItem->GetParent() ? pItem->GetParent() : m_pModel.get();
+
+ // insert the item
+ std::unique_ptr<weld::TreeIter> xParentEntry = FindEntry(pParent);
+
+ OUString sId(weld::toId(pItem));
+ std::unique_ptr<weld::TreeIter> xRet(m_xTreeView->make_iterator());
+ m_xTreeView->insert(xParentEntry.get(), nPos, &pItem->GetText(), &sId,
+ nullptr, nullptr, false, xRet.get());
+ m_xTreeView->set_image(*xRet, pItem->GetImage());
+
+ if (!xParentEntry)
+ return;
+ m_xTreeView->expand_row(*xParentEntry);
+}
+
+void FmFilterNavigator::EndEditing()
+{
+ if (m_xEditingCurrently)
+ {
+ // end editing
+ m_xTreeView->end_editing();
+ m_xEditingCurrently.reset();
+ }
+}
+
+void FmFilterNavigator::Remove(FmFilterData const * pItem)
+{
+ // the entry for the data
+ std::unique_ptr<weld::TreeIter> xEntry = FindEntry(pItem);
+ if (!xEntry)
+ return;
+
+ if (m_xEditingCurrently && m_xTreeView->iter_compare(*xEntry, *m_xEditingCurrently) == 0)
+ EndEditing();
+
+ m_xTreeView->remove(*xEntry);
+}
+
+FmFormItem* FmFilterNavigator::getSelectedFilterItems(::std::vector<FmFilterItem*>& _rItemList)
+{
+ // be sure that the data is only used within only one form!
+ FmFormItem* pFirstItem = nullptr;
+
+ bool bHandled = true;
+ bool bFoundSomething = false;
+
+ m_xTreeView->selected_foreach([this, &bHandled, &bFoundSomething, &pFirstItem, &_rItemList](weld::TreeIter& rEntry) {
+ FmFilterData* pFilterEntry = weld::fromId<FmFilterData*>(m_xTreeView->get_id(rEntry));
+ FmFilterItem* pFilter = dynamic_cast<FmFilterItem*>(pFilterEntry);
+ if (pFilter)
+ {
+ FmFormItem* pForm = dynamic_cast<FmFormItem*>( pFilter->GetParent()->GetParent() );
+ if (!pForm)
+ bHandled = false;
+ else if (!pFirstItem)
+ pFirstItem = pForm;
+ else if (pFirstItem != pForm)
+ bHandled = false;
+
+ if (bHandled)
+ {
+ _rItemList.push_back(pFilter);
+ bFoundSomething = true;
+ }
+ }
+ return !bHandled;
+ });
+
+ if ( !bHandled || !bFoundSomething )
+ pFirstItem = nullptr;
+ return pFirstItem;
+}
+
+void FmFilterNavigator::insertFilterItem(const ::std::vector<FmFilterItem*>& _rFilterList,FmFilterItems* _pTargetItems,bool _bCopy)
+{
+ for (FmFilterItem* pLookupItem : _rFilterList)
+ {
+ if ( pLookupItem->GetParent() == _pTargetItems )
+ continue;
+
+ FmFilterItem* pFilterItem = _pTargetItems->Find( pLookupItem->GetComponentIndex() );
+ OUString aText = pLookupItem->GetText();
+ if ( !pFilterItem )
+ {
+ pFilterItem = new FmFilterItem( _pTargetItems, pLookupItem->GetFieldName(), aText, pLookupItem->GetComponentIndex() );
+ m_pModel->Append( _pTargetItems, std::unique_ptr<FmFilterItem>(pFilterItem) );
+ }
+
+ if ( !_bCopy )
+ m_pModel->Remove( pLookupItem );
+
+ // now set the text for the new dragged item
+ m_pModel->SetTextForItem( pFilterItem, aText );
+ }
+
+ m_pModel->EnsureEmptyFilterRows( *_pTargetItems->GetParent() );
+}
+
+IMPL_LINK(FmFilterNavigator, DragBeginHdl, bool&, rUnsetDragIcon, bool)
+{
+ rUnsetDragIcon = false;
+
+ // be sure that the data is only used within an only one form!
+ m_aControlExchange.prepareDrag();
+
+ ::std::vector<FmFilterItem*> aItemList;
+ if (FmFormItem* pFirstItem = getSelectedFilterItems(aItemList))
+ {
+ m_aControlExchange->setDraggedEntries(std::move(aItemList));
+ m_aControlExchange->setFormItem(pFirstItem);
+
+ OFilterItemExchange& rExchange = *m_aControlExchange;
+ rtl::Reference<TransferDataContainer> xHelper(&rExchange);
+ m_xTreeView->enable_drag_source(xHelper, DND_ACTION_COPYMOVE);
+ rExchange.setDragging(true);
+
+ return false;
+ }
+ return true;
+}
+
+IMPL_LINK(FmFilterNavigator, PopupMenuHdl, const CommandEvent&, rEvt, bool)
+{
+ bool bHandled = false;
+ switch (rEvt.GetCommand())
+ {
+ case CommandEventId::ContextMenu:
+ {
+ // the place where it was clicked
+ Point aWhere;
+ std::unique_ptr<weld::TreeIter> xClicked(m_xTreeView->make_iterator());
+ if (rEvt.IsMouseEvent())
+ {
+ aWhere = rEvt.GetMousePosPixel();
+ if (!m_xTreeView->get_dest_row_at_pos(aWhere, xClicked.get(), false))
+ break;
+
+ if (!m_xTreeView->is_selected(*xClicked))
+ {
+ m_xTreeView->unselect_all();
+ m_xTreeView->select(*xClicked);
+ m_xTreeView->set_cursor(*xClicked);
+ }
+ }
+ else
+ {
+ if (!m_xTreeView->get_cursor(xClicked.get()))
+ break;
+ aWhere = m_xTreeView->get_row_area(*xClicked).Center();
+ }
+
+ ::std::vector<FmFilterData*> aSelectList;
+ m_xTreeView->selected_foreach([this, &aSelectList](weld::TreeIter& rEntry) {
+ FmFilterData* pFilterEntry = weld::fromId<FmFilterData*>(m_xTreeView->get_id(rEntry));
+
+ // don't delete forms
+ FmFormItem* pForm = dynamic_cast<FmFormItem*>(pFilterEntry);
+ if (!pForm)
+ aSelectList.push_back(pFilterEntry);
+
+ return false;
+ });
+
+ if (aSelectList.size() == 1)
+ {
+ // don't delete the only empty row of a form
+ FmFilterItems* pFilterItems = dynamic_cast<FmFilterItems*>( aSelectList[0] );
+ if (pFilterItems && pFilterItems->GetChildren().empty()
+ && pFilterItems->GetParent()->GetChildren().size() == 1)
+ aSelectList.clear();
+ }
+
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xTreeView.get(), "svx/ui/filtermenu.ui"));
+ std::unique_ptr<weld::Menu> xContextMenu(xBuilder->weld_menu("menu"));
+
+ // every condition could be deleted except the first one if it's the only one
+ bool bNoDelete = false;
+ if (aSelectList.empty())
+ {
+ bNoDelete = true;
+ xContextMenu->remove("delete");
+ }
+
+ FmFilterData* pFilterEntry = weld::fromId<FmFilterData*>(m_xTreeView->get_id(*xClicked));
+ auto pFilterItem = dynamic_cast<FmFilterItem*>(pFilterEntry);
+ bool bEdit = pFilterItem &&
+ m_xTreeView->is_selected(*xClicked) && m_xTreeView->count_selected_rows() == 1;
+
+ if (bNoDelete && !bEdit)
+ {
+ // nothing is in the menu, don't bother
+ return true;
+ }
+
+ if (!bEdit)
+ {
+ xContextMenu->remove("edit");
+ xContextMenu->remove("isnull");
+ xContextMenu->remove("isnotnull");
+ }
+
+ OUString sIdent = xContextMenu->popup_at_rect(m_xTreeView.get(), tools::Rectangle(aWhere, ::Size(1, 1)));
+ if (sIdent == "edit")
+ {
+ m_xTreeView->start_editing(*xClicked);
+ }
+ else if (sIdent == "isnull")
+ {
+ OUString aErrorMsg;
+ OUString aText = "IS NULL";
+ assert(pFilterItem && "if item is null this menu entry was removed and unavailable");
+ m_pModel->ValidateText(pFilterItem, aText, aErrorMsg);
+ m_pModel->SetTextForItem(pFilterItem, aText);
+ }
+ else if (sIdent == "isnotnull")
+ {
+ OUString aErrorMsg;
+ OUString aText = "IS NOT NULL";
+
+ assert(pFilterItem && "if item is null this menu entry was removed and unavailable");
+ m_pModel->ValidateText(pFilterItem, aText, aErrorMsg);
+ m_pModel->SetTextForItem(pFilterItem, aText);
+ }
+ else if (sIdent == "delete")
+ {
+ DeleteSelection();
+ }
+ bHandled = true;
+ }
+ break;
+ default: break;
+ }
+
+ return bHandled;
+}
+
+bool FmFilterNavigator::getNextEntry(weld::TreeIter& rEntry)
+{
+ bool bEntry = m_xTreeView->iter_next(rEntry);
+ // we need the next filter entry
+ if (bEntry)
+ {
+ while (!m_xTreeView->iter_has_child(rEntry))
+ {
+ std::unique_ptr<weld::TreeIter> xNext = m_xTreeView->make_iterator(&rEntry);
+ if (!m_xTreeView->iter_next(*xNext))
+ break;
+ m_xTreeView->copy_iterator(*xNext, rEntry);
+ }
+ }
+ return bEntry;
+}
+
+bool FmFilterNavigator::getPrevEntry(weld::TreeIter& rEntry)
+{
+ bool bEntry = m_xTreeView->iter_previous(rEntry);
+ // check if the previous entry is a filter, if so get the next prev
+ if (bEntry && m_xTreeView->iter_has_child(rEntry))
+ {
+ bEntry = m_xTreeView->iter_previous(rEntry);
+ // if the entry is still no leaf return
+ if (bEntry && m_xTreeView->iter_has_child(rEntry))
+ bEntry = false;
+ }
+ return bEntry;
+}
+IMPL_LINK(FmFilterNavigator, KeyInputHdl, const ::KeyEvent&, rKEvt, bool)
+{
+ const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
+
+ switch ( rKeyCode.GetCode() )
+ {
+ case KEY_UP:
+ case KEY_DOWN:
+ {
+ if ( !rKeyCode.IsMod1() || !rKeyCode.IsMod2() || rKeyCode.IsShift() )
+ break;
+
+ ::std::vector<FmFilterItem*> aItemList;
+ if ( !getSelectedFilterItems( aItemList ) )
+ break;
+
+
+ std::vector<std::unique_ptr<weld::TreeIter>> aSelected;
+ m_xTreeView->selected_foreach([this, &aSelected](weld::TreeIter& rEntry){
+ aSelected.emplace_back(m_xTreeView->make_iterator(&rEntry));
+ return false;
+ });
+
+ std::unique_ptr<weld::TreeIter> xTarget;
+ ::std::function<bool(FmFilterNavigator*, weld::TreeIter&)> getter;
+
+ if (rKeyCode.GetCode() == KEY_UP)
+ {
+ xTarget = m_xTreeView->make_iterator(aSelected.front().get());
+ getter = ::std::mem_fn(&FmFilterNavigator::getPrevEntry);
+ }
+ else
+ {
+ xTarget = m_xTreeView->make_iterator(aSelected.back().get());
+ getter = ::std::mem_fn(&FmFilterNavigator::getNextEntry);
+ }
+
+ bool bTarget = getter(this, *xTarget);
+ if (!bTarget)
+ break;
+
+ FmFilterItems* pTargetItems = getTargetItems(*m_xTreeView, *xTarget);
+ if (!pTargetItems)
+ break;
+
+ ::std::vector<FmFilterItem*>::const_iterator aEnd = aItemList.end();
+ bool bNextTargetItem = true;
+ while ( bNextTargetItem )
+ {
+ ::std::vector<FmFilterItem*>::const_iterator i = aItemList.begin();
+ for (; i != aEnd; ++i)
+ {
+ if ( (*i)->GetParent() == pTargetItems )
+ {
+ bTarget = getter(this, *xTarget);
+ if (!bTarget)
+ return true;
+ pTargetItems = getTargetItems(*m_xTreeView, *xTarget);
+ break;
+ }
+ else
+ {
+ FmFilterItem* pFilterItem = pTargetItems->Find( (*i)->GetComponentIndex() );
+ // we found the text component so jump above
+ if ( pFilterItem )
+ {
+ bTarget = getter(this, *xTarget);
+ if (!bTarget)
+ return true;
+
+ pTargetItems = getTargetItems(*m_xTreeView, *xTarget);
+ break;
+ }
+ }
+ }
+ bNextTargetItem = i != aEnd && pTargetItems;
+ }
+
+ if ( pTargetItems )
+ {
+ insertFilterItem( aItemList, pTargetItems, false );
+ return true;
+ }
+ }
+ break;
+
+ case KEY_DELETE:
+ {
+ if ( rKeyCode.GetModifier() )
+ break;
+
+ std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator();
+ if (m_xTreeView->get_iter_first(*xEntry) && !m_xTreeView->is_selected(*xEntry))
+ DeleteSelection();
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void FmFilterNavigator::DeleteSelection()
+{
+ // to avoid the deletion of an entry twice (e.g. deletion of a parent and afterward
+ // the deletion of its child, I have to shrink the selection list
+ std::vector<FmFilterData*> aEntryList;
+
+ m_xTreeView->selected_foreach([this, &aEntryList](weld::TreeIter& rEntry) {
+ FmFilterData* pFilterEntry = weld::fromId<FmFilterData*>(m_xTreeView->get_id(rEntry));
+
+ if (dynamic_cast<FmFilterItem*>(pFilterEntry))
+ {
+ std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(&rEntry));
+ if (m_xTreeView->iter_parent(*xParent) && m_xTreeView->is_selected(*xParent))
+ return false;
+ }
+
+ FmFormItem* pForm = dynamic_cast<FmFormItem*>(pFilterEntry);
+ if (!pForm)
+ aEntryList.emplace_back(pFilterEntry);
+
+ return false;
+ });
+
+ // Remove the selection
+ m_xTreeView->unselect_all();
+
+ for (auto i = aEntryList.rbegin(); i != aEntryList.rend(); ++i)
+ m_pModel->Remove(*i);
+}
+
+FmFilterNavigatorWin::FmFilterNavigatorWin(SfxBindings* _pBindings, SfxChildWindow* _pMgr,
+ vcl::Window* _pParent)
+ : SfxDockingWindow(_pBindings, _pMgr, _pParent, "FilterNavigator", "svx/ui/filternavigator.ui")
+ , SfxControllerItem( SID_FM_FILTER_NAVIGATOR_CONTROL, *_pBindings )
+ , m_xNavigatorTree(new FmFilterNavigator(this, m_xBuilder->weld_tree_view("treeview")))
+{
+ SetHelpId( HID_FILTER_NAVIGATOR_WIN );
+
+ SetText( SvxResId(RID_STR_FILTER_NAVIGATOR) );
+ SfxDockingWindow::SetFloatingSize( Size(200,200) );
+}
+
+FmFilterNavigatorWin::~FmFilterNavigatorWin()
+{
+ disposeOnce();
+}
+
+void FmFilterNavigatorWin::dispose()
+{
+ m_xNavigatorTree.reset();
+ ::SfxControllerItem::dispose();
+ SfxDockingWindow::dispose();
+}
+
+void FmFilterNavigatorWin::UpdateContent(FmFormShell const * pFormShell)
+{
+ if (!m_xNavigatorTree)
+ return;
+
+ if (!pFormShell)
+ m_xNavigatorTree->UpdateContent( nullptr, nullptr );
+ else
+ {
+ Reference<XFormController> const xController(pFormShell->GetImpl()->getActiveInternalController_Lock());
+ Reference< XIndexAccess > xContainer;
+ if (xController.is())
+ {
+ Reference< XChild > xChild = xController;
+ for (Reference< XInterface > xParent(xChild->getParent());
+ xParent.is();
+ xParent = xChild.is() ? xChild->getParent() : Reference< XInterface > ())
+ {
+ xContainer.set(xParent, UNO_QUERY);
+ xChild.set(xParent, UNO_QUERY);
+ }
+ }
+ m_xNavigatorTree->UpdateContent(xContainer, xController);
+ }
+}
+
+void FmFilterNavigatorWin::StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState )
+{
+ if( !pState || SID_FM_FILTER_NAVIGATOR_CONTROL != nSID )
+ return;
+
+ if( eState >= SfxItemState::DEFAULT )
+ {
+ FmFormShell* pShell = dynamic_cast<FmFormShell*>( static_cast<const SfxObjectItem*>(pState)->GetShell() );
+ UpdateContent( pShell );
+ }
+ else
+ UpdateContent( nullptr );
+}
+
+bool FmFilterNavigatorWin::Close()
+{
+ if (m_xNavigatorTree)
+ m_xNavigatorTree->EndEditing();
+
+ UpdateContent( nullptr );
+ return SfxDockingWindow::Close();
+}
+
+void FmFilterNavigatorWin::FillInfo( SfxChildWinInfo& rInfo ) const
+{
+ SfxDockingWindow::FillInfo( rInfo );
+ rInfo.bVisible = false;
+}
+
+Size FmFilterNavigatorWin::CalcDockingSize( SfxChildAlignment eAlign )
+{
+ if ( ( eAlign == SfxChildAlignment::TOP ) || ( eAlign == SfxChildAlignment::BOTTOM ) )
+ return Size();
+
+ return SfxDockingWindow::CalcDockingSize( eAlign );
+}
+
+SfxChildAlignment FmFilterNavigatorWin::CheckAlignment( SfxChildAlignment eActAlign, SfxChildAlignment eAlign )
+{
+ switch (eAlign)
+ {
+ case SfxChildAlignment::LEFT:
+ case SfxChildAlignment::RIGHT:
+ case SfxChildAlignment::NOALIGNMENT:
+ return eAlign;
+ default:
+ break;
+ }
+
+ return eActAlign;
+}
+
+void FmFilterNavigatorWin::GetFocus()
+{
+ // oj #97405#
+ if (m_xNavigatorTree)
+ m_xNavigatorTree->GrabFocus();
+ else
+ SfxDockingWindow::GetFocus();
+}
+
+SFX_IMPL_DOCKINGWINDOW( FmFilterNavigatorWinMgr, SID_FM_FILTER_NAVIGATOR )
+
+
+FmFilterNavigatorWinMgr::FmFilterNavigatorWinMgr( vcl::Window *_pParent, sal_uInt16 _nId,
+ SfxBindings *_pBindings, SfxChildWinInfo* _pInfo )
+ :SfxChildWindow( _pParent, _nId )
+{
+ SetWindow( VclPtr<FmFilterNavigatorWin>::Create( _pBindings, this, _pParent ) );
+ static_cast<SfxDockingWindow*>(GetWindow())->Initialize( _pInfo );
+}
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmPropBrw.cxx b/svx/source/form/fmPropBrw.cxx
new file mode 100644
index 0000000000..e30f90cf5e
--- /dev/null
+++ b/svx/source/form/fmPropBrw.cxx
@@ -0,0 +1,583 @@
+/* -*- 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 <sal/macros.h>
+
+#include <fmprop.hxx>
+#include <fmPropBrw.hxx>
+#include <svx/strings.hrc>
+#include <fmservs.hxx>
+#include <fmshimp.hxx>
+#include <fmpgeimp.hxx>
+
+#include <svx/dialmgr.hxx>
+#include <svx/fmpage.hxx>
+#include <svx/fmshell.hxx>
+#include <svx/fmview.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svxids.hrc>
+
+#include <com/sun/star/awt/XControlContainer.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/form/XForm.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/form/inspection/DefaultFormComponentInspectorModel.hpp>
+#include <com/sun/star/frame/Frame.hpp>
+#include <com/sun/star/inspection/ObjectInspector.hpp>
+#include <com/sun/star/inspection/XObjectInspectorUI.hpp>
+#include <com/sun/star/inspection/DefaultHelpProvider.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/util/VetoException.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/types.hxx>
+#include <cppuhelper/component_context.hxx>
+#include <o3tl/deleter.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/childwin.hxx>
+#include <sfx2/objitem.hxx>
+#include <sfx2/objsh.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <unotools/confignode.hxx>
+#include <vcl/stdtext.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/weldutils.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::form::inspection;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::inspection;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+using namespace ::svxform;
+using ::com::sun::star::awt::XWindow;
+
+//= FmPropBrwMgr
+SFX_IMPL_MODELESSDIALOGCONTOLLER(FmPropBrwMgr, SID_FM_SHOW_PROPERTIES)
+
+FmPropBrwMgr::FmPropBrwMgr( vcl::Window* _pParent, sal_uInt16 _nId,
+ SfxBindings* _pBindings, const SfxChildWinInfo* _pInfo)
+ :SfxChildWindow(_pParent, _nId)
+{
+ std::shared_ptr<FmPropBrw> xControl(new FmPropBrw(::comphelper::getProcessComponentContext(), _pBindings,
+ this, _pParent->GetFrameWeld(), _pInfo), o3tl::default_delete<FmPropBrw>());
+ SetController(std::move(xControl));
+ static_cast<FmPropBrw*>(GetController().get())->Initialize( _pInfo );
+}
+
+static OUString GetUIHeadlineName(sal_Int16 nClassId, const Any& aUnoObj)
+{
+ TranslateId pClassNameResourceId;
+
+ switch ( nClassId )
+ {
+ case FormComponentType::TEXTFIELD:
+ {
+ Reference< XInterface > xIFace;
+ aUnoObj >>= xIFace;
+ pClassNameResourceId = RID_STR_PROPTITLE_EDIT;
+ if (xIFace.is())
+ { // we have a chance to check if it's a formatted field model
+ Reference< XServiceInfo > xInfo(xIFace, UNO_QUERY);
+ if (xInfo.is() && (xInfo->supportsService(FM_SUN_COMPONENT_FORMATTEDFIELD)))
+ pClassNameResourceId = RID_STR_PROPTITLE_FORMATTED;
+ else if (!xInfo.is())
+ {
+ // couldn't distinguish between formatted and edit with the service name, so try with the properties
+ Reference< XPropertySet > xProps(xIFace, UNO_QUERY);
+ if (xProps.is())
+ {
+ Reference< XPropertySetInfo > xPropsInfo = xProps->getPropertySetInfo();
+ if (xPropsInfo.is() && xPropsInfo->hasPropertyByName(FM_PROP_FORMATSSUPPLIER))
+ pClassNameResourceId = RID_STR_PROPTITLE_FORMATTED;
+ }
+ }
+ }
+ }
+ break;
+
+ case FormComponentType::COMMANDBUTTON:
+ pClassNameResourceId = RID_STR_PROPTITLE_PUSHBUTTON; break;
+ case FormComponentType::RADIOBUTTON:
+ pClassNameResourceId = RID_STR_PROPTITLE_RADIOBUTTON; break;
+ case FormComponentType::CHECKBOX:
+ pClassNameResourceId = RID_STR_PROPTITLE_CHECKBOX; break;
+ case FormComponentType::LISTBOX:
+ pClassNameResourceId = RID_STR_PROPTITLE_LISTBOX; break;
+ case FormComponentType::COMBOBOX:
+ pClassNameResourceId = RID_STR_PROPTITLE_COMBOBOX; break;
+ case FormComponentType::GROUPBOX:
+ pClassNameResourceId = RID_STR_PROPTITLE_GROUPBOX; break;
+ case FormComponentType::IMAGEBUTTON:
+ pClassNameResourceId = RID_STR_PROPTITLE_IMAGEBUTTON; break;
+ case FormComponentType::FIXEDTEXT:
+ pClassNameResourceId = RID_STR_PROPTITLE_FIXEDTEXT; break;
+ case FormComponentType::GRIDCONTROL:
+ pClassNameResourceId = RID_STR_PROPTITLE_DBGRID; break;
+ case FormComponentType::FILECONTROL:
+ pClassNameResourceId = RID_STR_PROPTITLE_FILECONTROL; break;
+ case FormComponentType::DATEFIELD:
+ pClassNameResourceId = RID_STR_PROPTITLE_DATEFIELD; break;
+ case FormComponentType::TIMEFIELD:
+ pClassNameResourceId = RID_STR_PROPTITLE_TIMEFIELD; break;
+ case FormComponentType::NUMERICFIELD:
+ pClassNameResourceId = RID_STR_PROPTITLE_NUMERICFIELD; break;
+ case FormComponentType::CURRENCYFIELD:
+ pClassNameResourceId = RID_STR_PROPTITLE_CURRENCYFIELD; break;
+ case FormComponentType::PATTERNFIELD:
+ pClassNameResourceId = RID_STR_PROPTITLE_PATTERNFIELD; break;
+ case FormComponentType::IMAGECONTROL:
+ pClassNameResourceId = RID_STR_PROPTITLE_IMAGECONTROL; break;
+ case FormComponentType::HIDDENCONTROL:
+ pClassNameResourceId = RID_STR_PROPTITLE_HIDDEN; break;
+ case FormComponentType::SCROLLBAR:
+ pClassNameResourceId = RID_STR_PROPTITLE_SCROLLBAR; break;
+ case FormComponentType::SPINBUTTON:
+ pClassNameResourceId = RID_STR_PROPTITLE_SPINBUTTON; break;
+ case FormComponentType::NAVIGATIONBAR:
+ pClassNameResourceId = RID_STR_PROPTITLE_NAVBAR; break;
+ case FormComponentType::CONTROL:
+ default:
+ pClassNameResourceId = RID_STR_CONTROL; break;
+ }
+
+ return SvxResId(pClassNameResourceId);
+}
+
+FmPropBrw::FmPropBrw(const Reference< XComponentContext >& _xORB, SfxBindings* _pBindings,
+ SfxChildWindow* _pMgr, weld::Window* _pParent, const SfxChildWinInfo* _pInfo)
+ : SfxModelessDialogController(_pBindings, _pMgr, _pParent, "svx/ui/formpropertydialog.ui", "FormPropertyDialog")
+ , SfxControllerItem(SID_FM_PROPERTY_CONTROL, *_pBindings)
+ , m_bInitialStateChange(true)
+ , m_pParent(_pParent)
+ , m_nAsyncGetFocusId(nullptr)
+ , m_xDialogBox(m_xBuilder->weld_box("dialog-vbox1"))
+ , m_xContainer(m_xBuilder->weld_container("container"))
+ , m_xORB(_xORB)
+{
+ m_xContainer->set_size_request(m_xContainer->get_approximate_digit_width() * 72, m_xContainer->get_text_height() * 20);
+
+ try
+ {
+ // create a frame wrapper for myself
+ m_xMeAsFrame = Frame::create(m_xORB);
+
+ // transport the container area of this dialog to be the container window of the frame
+ css::uno::Reference<css::awt::XWindow> xFrameContainerWindow(new weld::TransportAsXWindow(m_xContainer.get()));
+ m_xMeAsFrame->initialize(xFrameContainerWindow);
+ m_xMeAsFrame->setName("form property browser");
+ }
+ catch (const Exception&)
+ {
+ OSL_FAIL("FmPropBrw::FmPropBrw: could not create/initialize my frame!");
+ m_xMeAsFrame.clear();
+ }
+
+ if ( _pInfo )
+ m_sLastActivePage = _pInfo->aExtraString;
+}
+
+FmPropBrw::~FmPropBrw()
+{
+ if (m_nAsyncGetFocusId)
+ {
+ Application::RemoveUserEvent(m_nAsyncGetFocusId);
+ m_nAsyncGetFocusId = nullptr;
+ }
+
+ if (m_xBrowserController.is())
+ implDetachController();
+ try
+ {
+ // remove our own properties from the component context. We cannot ensure that the component context
+ // is freed (there might be refcount problems :-\), so at least ensure the context itself
+ // does hold the objects anymore
+ Reference<XNameContainer> xName(m_xInspectorContext,uno::UNO_QUERY);
+ if ( xName.is() )
+ {
+ const OUString pProps[] = { OUString( "ContextDocument" )
+ , OUString( "DialogParentWindow" )
+ , OUString( "ControlContext" )
+ , OUString( "ControlShapeAccess" ) };
+ for (const auto & i : pProps)
+ xName->removeByName( i );
+ }
+ }
+ catch (const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ ::SfxControllerItem::dispose();
+}
+
+OUString FmPropBrw::getCurrentPage() const
+{
+ OUString sCurrentPage;
+ try
+ {
+ if ( m_xBrowserController.is() )
+ {
+ OSL_VERIFY( m_xBrowserController->getViewData() >>= sCurrentPage );
+ }
+
+ if ( sCurrentPage.isEmpty() )
+ sCurrentPage = m_sLastActivePage;
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.form", "caught an exception while retrieving the current page!");
+ }
+ return sCurrentPage;
+}
+
+void FmPropBrw::implDetachController()
+{
+ m_sLastActivePage = getCurrentPage();
+
+ implSetNewSelection( InterfaceBag() );
+
+ if ( m_xMeAsFrame.is() )
+ {
+ try
+ {
+ m_xMeAsFrame->setComponent(nullptr, nullptr);
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.form", "caught an exception while resetting the component!");
+ }
+ }
+
+ // we attached a frame to the controller manually, so we need to manually tell it that it's detached, too
+ if ( m_xBrowserController.is() )
+ {
+ m_xBrowserController->attachFrame( nullptr );
+ }
+
+ m_xBrowserController.clear();
+ m_xInspectorModel.clear();
+ m_xMeAsFrame.clear();
+}
+
+void FmPropBrw::Close()
+{
+ // suspend the controller (it is allowed to veto)
+ if ( m_xMeAsFrame.is() )
+ {
+ try
+ {
+ Reference< XController > xController( m_xMeAsFrame->getController() );
+ if ( xController.is() && !xController->suspend( true ) )
+ return;
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.form", "caught an exception while asking the controller!");
+ }
+ }
+
+ implDetachController();
+
+ // remember our bindings: while we're closed, we're deleted, too, so accessing the bindings after this
+ // would be deadly
+ // 10/19/00 - 79321 - FS
+ SfxBindings& rBindings = SfxControllerItem::GetBindings();
+
+ SfxModelessDialogController::Close();
+
+ rBindings.Invalidate(SID_FM_CTL_PROPERTIES);
+ rBindings.Invalidate(SID_FM_PROPERTIES);
+}
+
+bool FmPropBrw::implIsReadOnlyModel() const
+{
+ try
+ {
+ if ( m_xInspectorModel.is() )
+ return m_xInspectorModel->getIsReadOnly();
+ return false;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return true;
+}
+
+
+void FmPropBrw::implSetNewSelection( const InterfaceBag& _rSelection )
+{
+ if ( !m_xBrowserController.is() )
+ return;
+
+ try
+ {
+ Reference< XObjectInspector > xInspector( m_xBrowserController, UNO_QUERY_THROW );
+
+ // tell it the objects to inspect
+ xInspector->inspect( comphelper::containerToSequence(_rSelection) );
+ }
+ catch( const VetoException& )
+ {
+ return;
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.form", "");
+ return;
+ }
+
+ // set the new title according to the selected object
+ OUString sTitle;
+
+ if ( _rSelection.empty() )
+ {
+ sTitle = SvxResId(RID_STR_NO_PROPERTIES);
+ }
+ else if ( _rSelection.size() > 1 )
+ {
+ // no form component and (no form or no name) -> Multiselection
+ sTitle = SvxResId(RID_STR_PROPERTIES_CONTROL) +
+ SvxResId(RID_STR_PROPTITLE_MULTISELECT);
+ }
+ else
+ {
+ Reference< XPropertySet > xSingleSelection( *_rSelection.begin(), UNO_QUERY);
+ if ( ::comphelper::hasProperty( FM_PROP_CLASSID, xSingleSelection ) )
+ {
+ sal_Int16 nClassID = FormComponentType::CONTROL;
+ xSingleSelection->getPropertyValue( FM_PROP_CLASSID ) >>= nClassID;
+
+ sTitle = SvxResId(RID_STR_PROPERTIES_CONTROL) +
+ GetUIHeadlineName(nClassID, Any(xSingleSelection));
+ }
+ else if ( Reference< XForm >( xSingleSelection, UNO_QUERY ).is() )
+ sTitle = SvxResId(RID_STR_PROPERTIES_FORM);
+ }
+
+ if ( implIsReadOnlyModel() )
+ sTitle += SvxResId(RID_STR_READONLY_VIEW);
+
+ m_xDialog->set_title(sTitle);
+}
+
+void FmPropBrw::FillInfo( SfxChildWinInfo& rInfo ) const
+{
+ rInfo.bVisible = false;
+ rInfo.aExtraString = getCurrentPage();
+}
+
+IMPL_LINK_NOARG( FmPropBrw, OnAsyncGetFocus, void*, void )
+{
+ m_xDialogBox->child_grab_focus();
+ m_nAsyncGetFocusId = nullptr;
+}
+
+namespace
+{
+ bool lcl_shouldEnableHelpSection( const Reference< XComponentContext >& _rxContext )
+ {
+ ::utl::OConfigurationTreeRoot aConfiguration(
+ ::utl::OConfigurationTreeRoot::createWithComponentContext(
+ _rxContext,
+ "/org.openoffice.Office.Common/Forms/PropertyBrowser/" ) );
+
+ bool bEnabled = false;
+ OSL_VERIFY( aConfiguration.getNodeValue( "DirectHelp" ) >>= bEnabled );
+ return bEnabled;
+ }
+}
+
+void FmPropBrw::impl_createPropertyBrowser_throw( FmFormShell* _pFormShell )
+{
+ // the document in which we live
+ Reference< XInterface > xDocument;
+ if ( _pFormShell && _pFormShell->GetObjectShell() )
+ xDocument = _pFormShell->GetObjectShell()->GetModel();
+
+ // the context of the controls in our document
+ Reference< awt::XControlContainer > xControlContext;
+ if ( _pFormShell && _pFormShell->GetFormView() )
+ {
+ SdrPageView* pPageView = _pFormShell->GetFormView()->GetSdrPageView();
+
+ if(pPageView)
+ {
+ SdrPageWindow* pPageWindow = pPageView->GetPageWindow(0);
+
+ if(pPageWindow)
+ {
+ xControlContext = pPageWindow->GetControlContainer();
+ }
+ }
+ }
+
+ // the default parent window for message boxes
+ Reference< XWindow > xParentWindow(m_xDialog->GetXWindow());
+
+ // the mapping from control models to control shapes
+ Reference< XMap > xControlMap;
+ FmFormPage* pFormPage = _pFormShell ? _pFormShell->GetCurPage() : nullptr;
+ if ( pFormPage )
+ xControlMap = pFormPage->GetImpl().getControlToShapeMap();
+
+ // our own component context
+
+ // a ComponentContext for the
+ ::cppu::ContextEntry_Init aHandlerContextInfo[] =
+ {
+ ::cppu::ContextEntry_Init( "ContextDocument", Any( xDocument ) ),
+ ::cppu::ContextEntry_Init( "DialogParentWindow", Any( xParentWindow ) ),
+ ::cppu::ContextEntry_Init( "ControlContext", Any( xControlContext ) ),
+ ::cppu::ContextEntry_Init( "ControlShapeAccess", Any( xControlMap ) )
+ };
+ m_xInspectorContext.set(
+ ::cppu::createComponentContext( aHandlerContextInfo, SAL_N_ELEMENTS( aHandlerContextInfo ),
+ m_xORB ) );
+
+ bool bEnableHelpSection = lcl_shouldEnableHelpSection( m_xORB );
+
+ // an object inspector model
+ m_xInspectorModel =
+ bEnableHelpSection
+ ? DefaultFormComponentInspectorModel::createWithHelpSection( m_xInspectorContext, 3, 5 )
+ : DefaultFormComponentInspectorModel::createDefault( m_xInspectorContext );
+
+ // an object inspector
+ m_xBrowserController =
+ ObjectInspector::createWithModel(
+ m_xInspectorContext, m_xInspectorModel
+ );
+
+ if ( !m_xBrowserController.is() )
+ {
+ ShowServiceNotAvailableError(m_pParent, u"com.sun.star.inspection.ObjectInspector", true);
+ }
+ else
+ {
+ m_xBrowserController->attachFrame( Reference<XFrame>(m_xMeAsFrame,UNO_QUERY_THROW) );
+ }
+
+ if ( bEnableHelpSection )
+ {
+ Reference< XObjectInspector > xInspector( m_xBrowserController, UNO_QUERY_THROW );
+ Reference< XObjectInspectorUI > xInspectorUI( xInspector->getInspectorUI() );
+ DefaultHelpProvider::create( m_xInspectorContext, xInspectorUI );
+ }
+}
+
+
+void FmPropBrw::impl_ensurePropertyBrowser_nothrow( FmFormShell* _pFormShell )
+{
+ // the document in which we live
+ Reference< XInterface > xDocument;
+ SfxObjectShell* pObjectShell = _pFormShell ? _pFormShell->GetObjectShell() : nullptr;
+ if ( pObjectShell )
+ xDocument = pObjectShell->GetModel();
+ if ( ( xDocument == m_xLastKnownDocument ) && m_xBrowserController.is() )
+ // nothing to do
+ return;
+
+ try
+ {
+ // clean up any previous instances of the object inspector
+ if ( m_xMeAsFrame.is() )
+ m_xMeAsFrame->setComponent( nullptr, nullptr );
+ else
+ ::comphelper::disposeComponent( m_xBrowserController );
+ m_xBrowserController.clear();
+ m_xInspectorModel.clear();
+
+ // and create a new one
+ impl_createPropertyBrowser_throw( _pFormShell );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ m_xLastKnownDocument = xDocument;
+}
+
+
+void FmPropBrw::StateChangedAtToolBoxControl(sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState)
+{
+ if (!pState || SID_FM_PROPERTY_CONTROL != nSID)
+ return;
+
+ try
+ {
+ if (eState >= SfxItemState::DEFAULT)
+ {
+ FmFormShell* pShell = dynamic_cast<FmFormShell*>( static_cast<const SfxObjectItem*>(pState)->GetShell() );
+ InterfaceBag aSelection;
+ if ( pShell )
+ pShell->GetImpl()->getCurrentSelection_Lock(aSelection);
+
+ impl_ensurePropertyBrowser_nothrow( pShell );
+
+ // set the new object to inspect
+ implSetNewSelection( aSelection );
+
+ // if this is the first time we're here, some additional things need to be done ...
+ if ( m_bInitialStateChange )
+ {
+ // if we're just newly created, we want to have the focus
+ m_nAsyncGetFocusId = Application::PostUserEvent(LINK(this, FmPropBrw, OnAsyncGetFocus));
+
+ // and additionally, we want to show the page which was active during
+ // our previous incarnation
+ if ( !m_sLastActivePage.isEmpty() )
+ {
+ try
+ {
+ if ( m_xBrowserController.is() )
+ m_xBrowserController->restoreViewData( Any( m_sLastActivePage ) );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.form",
+ "caught an exception while setting the initial page!");
+ }
+ }
+
+ m_bInitialStateChange = false;
+ }
+
+ }
+ else
+ {
+ implSetNewSelection( InterfaceBag() );
+ }
+ }
+ catch (Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx.form", "");
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmcontrolbordermanager.cxx b/svx/source/form/fmcontrolbordermanager.cxx
new file mode 100644
index 0000000000..ae33791e2c
--- /dev/null
+++ b/svx/source/form/fmcontrolbordermanager.cxx
@@ -0,0 +1,425 @@
+/* -*- 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 <fmcontrolbordermanager.hxx>
+
+#include <fmprop.hxx>
+
+#include <com/sun/star/form/validation/XValidatableFormComponent.hpp>
+#include <com/sun/star/awt/XTextComponent.hpp>
+#include <com/sun/star/awt/XListBox.hpp>
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <osl/diagnose.h>
+
+
+namespace svxform
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::awt;
+ using namespace ::com::sun::star::form::validation;
+
+
+ //= helper
+
+
+ static void setUnderline( const Reference< XVclWindowPeer >& _rxPeer, const UnderlineDescriptor& _rUnderline )
+ {
+ OSL_ENSURE( _rxPeer.is(), "setUnderline: invalid peer!" );
+
+ // the underline type is an aspect of the font
+ FontDescriptor aFont;
+ OSL_VERIFY( _rxPeer->getProperty( FM_PROP_FONT ) >>= aFont );
+ aFont.Underline = _rUnderline.nUnderlineType;
+ _rxPeer->setProperty( FM_PROP_FONT, Any( aFont ) );
+ // the underline color is a separate property
+ _rxPeer->setProperty( FM_PROP_TEXTLINECOLOR, Any( _rUnderline.nUnderlineColor ) );
+ }
+
+
+ static void getUnderline( const Reference< XVclWindowPeer >& _rxPeer, UnderlineDescriptor& _rUnderline )
+ {
+ OSL_ENSURE( _rxPeer.is(), "getUnderline: invalid peer!" );
+
+ FontDescriptor aFont;
+ OSL_VERIFY( _rxPeer->getProperty( FM_PROP_FONT ) >>= aFont );
+ _rUnderline.nUnderlineType = aFont.Underline;
+
+ OSL_VERIFY( _rxPeer->getProperty( FM_PROP_TEXTLINECOLOR ) >>= _rUnderline.nUnderlineColor );
+ }
+
+
+ static void getBorder( const Reference< XVclWindowPeer >& _rxPeer, BorderDescriptor& _rBorder )
+ {
+ OSL_ENSURE( _rxPeer.is(), "getBorder: invalid peer!" );
+
+ OSL_VERIFY( _rxPeer->getProperty( FM_PROP_BORDER ) >>= _rBorder.nBorderType );
+ OSL_VERIFY( _rxPeer->getProperty( FM_PROP_BORDERCOLOR ) >>= _rBorder.nBorderColor );
+ }
+
+
+ static void setBorder( const Reference< XVclWindowPeer >& _rxPeer, const BorderDescriptor& _rBorder )
+ {
+ OSL_ENSURE( _rxPeer.is(), "setBorder: invalid peer!" );
+
+ _rxPeer->setProperty( FM_PROP_BORDER, Any( _rBorder.nBorderType ) );
+ _rxPeer->setProperty( FM_PROP_BORDERCOLOR, Any( _rBorder.nBorderColor ) );
+ }
+
+ ControlBorderManager::ControlBorderManager()
+ :m_nFocusColor ( 0x000000FF )
+ ,m_nMouseHoveColor( 0x007098BE )
+ ,m_nInvalidColor ( 0x00FF0000 )
+ ,m_bDynamicBorderColors( false )
+ {
+ }
+
+
+ ControlBorderManager::~ControlBorderManager()
+ {
+ }
+
+
+ bool ControlBorderManager::canColorBorder( const Reference< XVclWindowPeer >& _rxPeer )
+ {
+ OSL_PRECOND( _rxPeer.is(), "ControlBorderManager::canColorBorder: invalid peer!" );
+
+ PeerBag::const_iterator aPos = m_aColorableControls.find( _rxPeer );
+ if ( aPos != m_aColorableControls.end() )
+ return true;
+
+ aPos = m_aNonColorableControls.find( _rxPeer );
+ if ( aPos != m_aNonColorableControls.end() )
+ return false;
+
+ // this peer is not yet known
+
+ // no border coloring for controls which are not for text input
+ // #i37434# / 2004-11-19 / frank.schoenheit@sun.com
+ Reference< XTextComponent > xText( _rxPeer, UNO_QUERY );
+ Reference< XListBox > xListBox( _rxPeer, UNO_QUERY );
+ if ( xText.is() || xListBox.is() )
+ {
+ sal_Int16 nBorderStyle = VisualEffect::NONE;
+ OSL_VERIFY( _rxPeer->getProperty( FM_PROP_BORDER ) >>= nBorderStyle );
+ if ( nBorderStyle == VisualEffect::FLAT )
+ // if you change this to also accept LOOK3D, then this would also work, but look ugly
+ {
+ m_aColorableControls.insert( _rxPeer );
+ return true;
+ }
+ }
+
+ m_aNonColorableControls.insert( _rxPeer );
+ return false;
+ }
+
+
+ ControlStatus ControlBorderManager::getControlStatus( const Reference< XControl >& _rxControl )
+ {
+ ControlStatus nStatus = ControlStatus::NONE;
+
+ if ( _rxControl.get() == m_aFocusControl.xControl.get() )
+ nStatus |= ControlStatus::Focused;
+
+ if ( _rxControl.get() == m_aMouseHoverControl.xControl.get() )
+ nStatus |= ControlStatus::MouseHover;
+
+ if ( m_aInvalidControls.find( ControlData( _rxControl ) ) != m_aInvalidControls.end() )
+ nStatus |= ControlStatus::Invalid;
+
+ return nStatus;
+ }
+
+
+ Color ControlBorderManager::getControlColorByStatus( ControlStatus _nStatus ) const
+ {
+ // "invalid" is ranked highest
+ if ( _nStatus & ControlStatus::Invalid )
+ return m_nInvalidColor;
+
+ // then, "focused" is more important than ...
+ if ( _nStatus & ControlStatus::Focused )
+ return m_nFocusColor;
+
+ // ... "mouse over"
+ if ( _nStatus & ControlStatus::MouseHover )
+ return m_nMouseHoveColor;
+
+ OSL_FAIL( "ControlBorderManager::getControlColorByStatus: invalid status!" );
+ return Color(0);
+ }
+
+
+ void ControlBorderManager::updateBorderStyle( const Reference< XControl >& _rxControl, const Reference< XVclWindowPeer >& _rxPeer, const BorderDescriptor& _rFallback )
+ {
+ OSL_PRECOND( _rxControl.is() && _rxPeer.is(), "ControlBorderManager::updateBorderStyle: invalid parameters!" );
+
+ ControlStatus nStatus = getControlStatus( _rxControl );
+ BorderDescriptor aBorder;
+ aBorder.nBorderType = ( nStatus == ControlStatus::NONE )
+ ? _rFallback.nBorderType
+ : VisualEffect::FLAT;
+ aBorder.nBorderColor = ( nStatus == ControlStatus::NONE )
+ ? _rFallback.nBorderColor
+ : getControlColorByStatus( nStatus );
+ setBorder( _rxPeer, aBorder );
+ }
+
+
+ void ControlBorderManager::determineOriginalBorderStyle( const Reference< XControl >& _rxControl, BorderDescriptor& _rData ) const
+ {
+ _rData = ControlData();
+ if ( m_aFocusControl.xControl.get() == _rxControl.get() )
+ {
+ _rData = m_aFocusControl;
+ }
+ else if ( m_aMouseHoverControl.xControl.get() == _rxControl.get() )
+ {
+ _rData = m_aMouseHoverControl;
+ }
+ else
+ {
+ ControlBag::const_iterator aPos = m_aInvalidControls.find( _rxControl );
+ if ( aPos != m_aInvalidControls.end() )
+ {
+ _rData = *aPos;
+ }
+ else
+ {
+ Reference< XVclWindowPeer > xPeer( _rxControl->getPeer(), UNO_QUERY );
+ getBorder( xPeer, _rData );
+ }
+ }
+ }
+
+
+ void ControlBorderManager::controlStatusGained( const Reference< XInterface >& _rxControl, ControlData& _rControlData )
+ {
+ if ( _rxControl == _rControlData.xControl )
+ // nothing to do - though suspicious
+ return;
+
+ Reference< XControl > xAsControl( _rxControl, UNO_QUERY );
+ DBG_ASSERT( xAsControl.is(), "ControlBorderManager::controlStatusGained: invalid control!" );
+ if ( !xAsControl.is() )
+ return;
+
+ try
+ {
+ Reference< XVclWindowPeer > xPeer( xAsControl->getPeer(), UNO_QUERY );
+ if ( xPeer.is() && canColorBorder( xPeer ) )
+ {
+ // remember the control and its current border color
+ _rControlData.xControl.clear(); // so determineOriginalBorderStyle doesn't get confused
+
+ determineOriginalBorderStyle( xAsControl, _rControlData );
+
+ _rControlData.xControl = xAsControl;
+
+ updateBorderStyle( xAsControl, xPeer, _rControlData );
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "ControlBorderManager::controlStatusGained" );
+ }
+ }
+
+
+ void ControlBorderManager::controlStatusLost( const Reference< XInterface >& _rxControl, ControlData& _rControlData )
+ {
+ if ( _rxControl != _rControlData.xControl )
+ // nothing to do
+ return;
+
+ OSL_PRECOND( _rControlData.xControl.is(), "ControlBorderManager::controlStatusLost: invalid control data - this will crash!" );
+ try
+ {
+ Reference< XVclWindowPeer > xPeer( _rControlData.xControl->getPeer(), UNO_QUERY );
+ if ( xPeer.is() && canColorBorder( xPeer ) )
+ {
+ ControlData aPreviousStatus( _rControlData );
+ _rControlData = ControlData();
+ updateBorderStyle( aPreviousStatus.xControl, xPeer, aPreviousStatus );
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "ControlBorderManager::controlStatusLost" );
+ }
+ }
+
+
+ void ControlBorderManager::enableDynamicBorderColor( )
+ {
+ m_bDynamicBorderColors = true;
+ }
+
+
+ void ControlBorderManager::disableDynamicBorderColor( )
+ {
+ m_bDynamicBorderColors = false;
+ restoreAll();
+ }
+
+
+ void ControlBorderManager::setStatusColor( ControlStatus _nStatus, Color _nColor )
+ {
+ switch ( _nStatus )
+ {
+ case ControlStatus::Focused:
+ m_nFocusColor = _nColor;
+ break;
+ case ControlStatus::MouseHover:
+ m_nMouseHoveColor = _nColor;
+ break;
+ case ControlStatus::Invalid:
+ m_nInvalidColor = _nColor;
+ break;
+ default:
+ OSL_FAIL( "ControlBorderManager::setStatusColor: invalid status!" );
+ }
+ }
+
+
+ void ControlBorderManager::restoreAll()
+ {
+ if ( m_aFocusControl.xControl.is() )
+ controlStatusLost( m_aFocusControl.xControl, m_aFocusControl );
+ if ( m_aMouseHoverControl.xControl.is() )
+ controlStatusLost( m_aMouseHoverControl.xControl, m_aMouseHoverControl );
+
+ ControlBag aInvalidControls;
+ m_aInvalidControls.swap( aInvalidControls );
+
+ for (const auto& rControl : aInvalidControls)
+ {
+ Reference< XVclWindowPeer > xPeer( rControl.xControl->getPeer(), UNO_QUERY );
+ if ( xPeer.is() )
+ {
+ updateBorderStyle( rControl.xControl, xPeer, rControl );
+ xPeer->setProperty( FM_PROP_HELPTEXT, Any( rControl.sOriginalHelpText ) );
+ setUnderline( xPeer, rControl );
+ }
+ }
+ }
+
+
+ void ControlBorderManager::focusGained( const Reference< XInterface >& _rxControl )
+ {
+ if ( m_bDynamicBorderColors )
+ controlStatusGained( _rxControl, m_aFocusControl );
+ }
+
+
+ void ControlBorderManager::focusLost( const Reference< XInterface >& _rxControl )
+ {
+ if ( m_bDynamicBorderColors )
+ controlStatusLost( _rxControl, m_aFocusControl );
+ }
+
+
+ void ControlBorderManager::mouseEntered( const Reference< XInterface >& _rxControl )
+ {
+ if ( m_bDynamicBorderColors )
+ controlStatusGained( _rxControl, m_aMouseHoverControl );
+ }
+
+
+ void ControlBorderManager::mouseExited( const Reference< XInterface >& _rxControl )
+ {
+ if ( m_bDynamicBorderColors )
+ controlStatusLost( _rxControl, m_aMouseHoverControl );
+ }
+
+
+ void ControlBorderManager::validityChanged( const Reference< XControl >& _rxControl, const Reference< XValidatableFormComponent >& _rxValidatable )
+ {
+ try
+ {
+ OSL_ENSURE( _rxControl.is(), "ControlBorderManager::validityChanged: invalid control!" );
+ OSL_ENSURE( _rxValidatable.is(), "ControlBorderManager::validityChanged: invalid validatable!" );
+
+ Reference< XVclWindowPeer > xPeer( _rxControl.is() ? _rxControl->getPeer() : Reference< XWindowPeer >(), UNO_QUERY );
+ if ( !xPeer.is() || !_rxValidatable.is() )
+ return;
+
+ ControlData aData( _rxControl );
+
+ if ( _rxValidatable->isValid() )
+ {
+ ControlBag::iterator aPos = m_aInvalidControls.find( aData );
+ if ( aPos != m_aInvalidControls.end() )
+ { // invalid before, valid now
+ ControlData aOriginalLayout( *aPos );
+ m_aInvalidControls.erase( aPos );
+
+ // restore all the things we used to indicate invalidity
+ if ( m_bDynamicBorderColors )
+ updateBorderStyle( _rxControl, xPeer, aOriginalLayout );
+ xPeer->setProperty( FM_PROP_HELPTEXT, Any( aOriginalLayout.sOriginalHelpText ) );
+ setUnderline( xPeer, aOriginalLayout );
+ }
+ return;
+ }
+
+ // we're here in the INVALID case
+ if ( m_aInvalidControls.find( _rxControl ) == m_aInvalidControls.end() )
+ { // valid before, invalid now
+
+ // remember the current border
+ determineOriginalBorderStyle( _rxControl, aData );
+ // and tool tip
+ xPeer->getProperty( FM_PROP_HELPTEXT ) >>= aData.sOriginalHelpText;
+ // and font
+ getUnderline( xPeer, aData );
+
+ m_aInvalidControls.insert( aData );
+
+ // update the border to the new invalidity
+ if ( m_bDynamicBorderColors && canColorBorder( xPeer ) )
+ updateBorderStyle( _rxControl, xPeer, aData );
+ else
+ {
+ // and also the new font
+ setUnderline( xPeer, UnderlineDescriptor( css::awt::FontUnderline::WAVE, m_nInvalidColor ) );
+ }
+ }
+
+ // update the explanation for invalidity (this is always done, even if the validity did not change)
+ Reference< XValidator > xValidator = _rxValidatable->getValidator();
+ OSL_ENSURE( xValidator.is(), "ControlBorderManager::validityChanged: invalid, but no validator?" );
+ OUString sExplainInvalidity = xValidator.is() ? xValidator->explainInvalid( _rxValidatable->getCurrentValue() ) : OUString();
+ xPeer->setProperty( FM_PROP_HELPTEXT, Any( sExplainInvalidity ) );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "ControlBorderManager::validityChanged" );
+ }
+ }
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmcontrollayout.cxx b/svx/source/form/fmcontrollayout.cxx
new file mode 100644
index 0000000000..bdc3539320
--- /dev/null
+++ b/svx/source/form/fmcontrollayout.cxx
@@ -0,0 +1,312 @@
+/* -*- 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 <fmcontrollayout.hxx>
+#include <fmprop.hxx>
+
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/awt/VisualEffect.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/lang/Locale.hpp>
+#include <com/sun/star/awt/FontDescriptor.hpp>
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/container/XChild.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <i18nlangtag/mslangid.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <unotools/confignode.hxx>
+#include <unotools/syslocale.hxx>
+#include <unotools/localedatawrapper.hxx>
+
+#include <toolkit/helper/vclunohelper.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <vcl/outdev.hxx>
+
+
+namespace svxform
+{
+
+
+ using namespace ::utl;
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::uno::UNO_SET_THROW;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::uno::RuntimeException;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::beans::XPropertySet;
+ using ::com::sun::star::beans::XPropertySetInfo;
+ using ::com::sun::star::lang::Locale;
+ using ::com::sun::star::awt::FontDescriptor;
+ using ::com::sun::star::style::XStyleFamiliesSupplier;
+ using ::com::sun::star::lang::XServiceInfo;
+ using ::com::sun::star::container::XNameAccess;
+ using ::com::sun::star::container::XChild;
+
+ namespace FormComponentType = ::com::sun::star::form::FormComponentType;
+ namespace VisualEffect = ::com::sun::star::awt::VisualEffect;
+ namespace ScriptType = ::com::sun::star::i18n::ScriptType;
+
+
+ namespace
+ {
+ ::utl::OConfigurationNode getLayoutSettings( DocumentType _eDocType )
+ {
+ OUString sConfigName = "/org.openoffice.Office.Common/Forms/ControlLayout/" +
+ DocumentClassification::getModuleIdentifierForDocumentType( _eDocType );
+ return OConfigurationTreeRoot::createWithComponentContext(
+ ::comphelper::getProcessComponentContext(), // TODO
+ sConfigName );
+ }
+
+ template< class INTERFACE_TYPE >
+ Reference< INTERFACE_TYPE > getTypedModelNode( const Reference< XInterface >& _rxModelNode )
+ {
+ Reference< INTERFACE_TYPE > xTypedNode( _rxModelNode, UNO_QUERY );
+ if ( xTypedNode.is() )
+ return xTypedNode;
+ else
+ {
+ Reference< XChild > xChild( _rxModelNode, UNO_QUERY );
+ if ( xChild.is() )
+ return getTypedModelNode< INTERFACE_TYPE >( xChild->getParent() );
+ else
+ return nullptr;
+ }
+ }
+
+
+ bool lcl_getDocumentDefaultStyleAndFamily( const Reference< XInterface >& _rxDocument, OUString& _rFamilyName, OUString& _rStyleName )
+ {
+ bool bSuccess = true;
+ Reference< XServiceInfo > xDocumentSI( _rxDocument, UNO_QUERY );
+ if ( xDocumentSI.is() )
+ {
+ if ( xDocumentSI->supportsService("com.sun.star.text.TextDocument")
+ || xDocumentSI->supportsService("com.sun.star.text.WebDocument")
+ )
+ {
+ _rFamilyName = "ParagraphStyles";
+ _rStyleName = "Standard";
+ }
+ else if ( xDocumentSI->supportsService("com.sun.star.sheet.SpreadsheetDocument") )
+ {
+ _rFamilyName = "CellStyles";
+ _rStyleName = "Default";
+ }
+ else if ( xDocumentSI->supportsService("com.sun.star.drawing.DrawingDocument")
+ || xDocumentSI->supportsService("com.sun.star.presentation.PresentationDocument")
+ )
+ {
+ _rFamilyName = "graphics";
+ _rStyleName = "standard";
+ }
+ else
+ bSuccess = false;
+ }
+ return bSuccess;
+ }
+
+
+ void lcl_initializeControlFont( const Reference< XPropertySet >& _rxModel )
+ {
+ try
+ {
+ Reference< XPropertySet > xStyle( ControlLayouter::getDefaultDocumentTextStyle( _rxModel ), UNO_SET_THROW );
+ Reference< XPropertySetInfo > xStylePSI( xStyle->getPropertySetInfo(), UNO_SET_THROW );
+
+ // determine the script type associated with the system locale
+ const SvtSysLocale aSysLocale;
+ const LocaleDataWrapper& rSysLocaleData = aSysLocale.GetLocaleData();
+ const sal_Int16 eSysLocaleScriptType = MsLangId::getScriptType( rSysLocaleData.getLanguageTag().getLanguageType() );
+
+ // depending on this script type, use the right property from the document's style which controls the
+ // default locale for document content
+ const char* pCharLocalePropertyName = "CharLocale";
+ switch ( eSysLocaleScriptType )
+ {
+ case ScriptType::LATIN:
+ // already defaulted above
+ break;
+ case ScriptType::ASIAN:
+ pCharLocalePropertyName = "CharLocaleAsian";
+ break;
+ case ScriptType::COMPLEX:
+ pCharLocalePropertyName = "CharLocaleComplex";
+ break;
+ default:
+ OSL_FAIL( "lcl_initializeControlFont: unexpected script type for system locale!" );
+ break;
+ }
+
+ OUString sCharLocalePropertyName = OUString::createFromAscii( pCharLocalePropertyName );
+ Locale aDocumentCharLocale;
+ if ( xStylePSI->hasPropertyByName( sCharLocalePropertyName ) )
+ {
+ OSL_VERIFY( xStyle->getPropertyValue( sCharLocalePropertyName ) >>= aDocumentCharLocale );
+ }
+ // fall back to CharLocale property at the style
+ if ( aDocumentCharLocale.Language.isEmpty() )
+ {
+ sCharLocalePropertyName = "CharLocale";
+ if ( xStylePSI->hasPropertyByName( sCharLocalePropertyName ) )
+ {
+ OSL_VERIFY( xStyle->getPropertyValue( sCharLocalePropertyName ) >>= aDocumentCharLocale );
+ }
+ }
+ // fall back to the system locale
+ if ( aDocumentCharLocale.Language.isEmpty() )
+ {
+ aDocumentCharLocale = rSysLocaleData.getLanguageTag().getLocale();
+ }
+
+ // retrieve a default font for this locale, and set it at the control
+ vcl::Font aFont = OutputDevice::GetDefaultFont( DefaultFontType::SANS, LanguageTag::convertToLanguageType( aDocumentCharLocale ), GetDefaultFontFlags::OnlyOne );
+ FontDescriptor aFontDesc = VCLUnoHelper::CreateFontDescriptor( aFont );
+ _rxModel->setPropertyValue("FontDescriptor", Any( aFontDesc )
+ );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ }
+
+
+ //= ControlLayouter
+
+
+ Reference< XPropertySet > ControlLayouter::getDefaultDocumentTextStyle( const Reference< XPropertySet >& _rxModel )
+ {
+ // the style family collection
+ Reference< XStyleFamiliesSupplier > xSuppStyleFamilies( getTypedModelNode< XStyleFamiliesSupplier >( _rxModel ), UNO_SET_THROW );
+ Reference< XNameAccess > xStyleFamilies( xSuppStyleFamilies->getStyleFamilies(), UNO_SET_THROW );
+
+ // the names of the family, and the style - depends on the document type we live in
+ OUString sFamilyName, sStyleName;
+ if ( !lcl_getDocumentDefaultStyleAndFamily( xSuppStyleFamilies, sFamilyName, sStyleName ) )
+ throw RuntimeException("unknown document type!");
+
+ // the concrete style
+ Reference< XNameAccess > xStyleFamily( xStyleFamilies->getByName( sFamilyName ), UNO_QUERY_THROW );
+ return Reference< XPropertySet >( xStyleFamily->getByName( sStyleName ), UNO_QUERY_THROW );
+ }
+
+
+ void ControlLayouter::initializeControlLayout( const Reference< XPropertySet >& _rxControlModel, DocumentType _eDocType )
+ {
+ DBG_ASSERT( _rxControlModel.is(), "ControlLayouter::initializeControlLayout: invalid model!" );
+ if ( !_rxControlModel.is() )
+ return;
+
+ try
+ {
+ Reference< XPropertySetInfo > xPSI( _rxControlModel->getPropertySetInfo(), UNO_SET_THROW );
+
+ // the control type
+ sal_Int16 nClassId = FormComponentType::CONTROL;
+ _rxControlModel->getPropertyValue( FM_PROP_CLASSID ) >>= nClassId;
+
+ // the document type
+ if ( _eDocType == eUnknownDocumentType )
+ _eDocType = DocumentClassification::classifyHostDocument( _rxControlModel );
+
+ // let's see what the configuration says about the visual effect
+ OConfigurationNode aConfig = getLayoutSettings( _eDocType );
+ Any aVisualEffect = aConfig.getNodeValue( OUString( "VisualEffect" ) );
+ if ( aVisualEffect.hasValue() )
+ {
+ OUString sVisualEffect;
+ OSL_VERIFY( aVisualEffect >>= sVisualEffect );
+
+ sal_Int16 nVisualEffect = VisualEffect::NONE;
+ if ( sVisualEffect == "flat" )
+ nVisualEffect = VisualEffect::FLAT;
+ else if ( sVisualEffect == "3D" )
+ nVisualEffect = VisualEffect::LOOK3D;
+
+ if ( xPSI->hasPropertyByName( FM_PROP_BORDER ) )
+ {
+ if ( ( nClassId != FormComponentType::COMMANDBUTTON )
+ && ( nClassId != FormComponentType::RADIOBUTTON )
+ && ( nClassId != FormComponentType::CHECKBOX )
+ && ( nClassId != FormComponentType::GROUPBOX )
+ && ( nClassId != FormComponentType::FIXEDTEXT )
+ && ( nClassId != FormComponentType::SCROLLBAR )
+ && ( nClassId != FormComponentType::SPINBUTTON )
+ )
+ {
+ _rxControlModel->setPropertyValue( FM_PROP_BORDER, Any( nVisualEffect ) );
+ if ( ( nVisualEffect == VisualEffect::FLAT )
+ && ( xPSI->hasPropertyByName( FM_PROP_BORDERCOLOR ) )
+ )
+ // light gray flat border
+ _rxControlModel->setPropertyValue( FM_PROP_BORDERCOLOR, Any( sal_Int32(0x00C0C0C0) ) );
+ }
+ }
+ if ( xPSI->hasPropertyByName( FM_PROP_VISUALEFFECT ) )
+ _rxControlModel->setPropertyValue( FM_PROP_VISUALEFFECT, Any( nVisualEffect ) );
+ }
+
+ // the font (only if we use the document's ref devices for rendering control text, otherwise, the
+ // default font of VCL controls is assumed to be fine)
+ if ( useDocumentReferenceDevice( _eDocType )
+ && xPSI->hasPropertyByName( FM_PROP_FONT )
+ )
+ lcl_initializeControlFont( _rxControlModel );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "ControlLayouter::initializeControlLayout" );
+ }
+ }
+
+ bool ControlLayouter::useDynamicBorderColor( DocumentType _eDocType )
+ {
+ OConfigurationNode aConfig = getLayoutSettings( _eDocType );
+ Any aDynamicBorderColor = aConfig.getNodeValue( OUString( "DynamicBorderColors" ) );
+ bool bDynamicBorderColor = false;
+ OSL_VERIFY( aDynamicBorderColor >>= bDynamicBorderColor );
+ return bDynamicBorderColor;
+ }
+
+
+ bool ControlLayouter::useDocumentReferenceDevice( DocumentType _eDocType )
+ {
+ if ( _eDocType == eUnknownDocumentType )
+ return false;
+ OConfigurationNode aConfig = getLayoutSettings( _eDocType );
+ Any aUseRefDevice = aConfig.getNodeValue( OUString( "UseDocumentTextMetrics" ) );
+ bool bUseRefDevice = false;
+ OSL_VERIFY( aUseRefDevice >>= bUseRefDevice );
+ return bUseRefDevice;
+ }
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmdmod.cxx b/svx/source/form/fmdmod.cxx
new file mode 100644
index 0000000000..87c13e44e6
--- /dev/null
+++ b/svx/source/form/fmdmod.cxx
@@ -0,0 +1,92 @@
+/* -*- 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 <sal/config.h>
+
+#include <sal/macros.h>
+#include <svx/fmdmod.hxx>
+#include <fmservs.hxx>
+#include <fmobj.hxx>
+#include <svx/unoshape.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/sequence.hxx>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+using namespace ::svxform;
+
+
+::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > SAL_CALL SvxFmMSFactory::createInstance(const OUString& rServiceSpecifier)
+{
+ ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > xRet;
+
+ if ( rServiceSpecifier.startsWith( "com.sun.star.form.component." ) )
+ {
+ css::uno::Reference<css::uno::XComponentContext> xContext = comphelper::getProcessComponentContext();
+ xRet = xContext->getServiceManager()->createInstanceWithContext(rServiceSpecifier, xContext);
+ }
+ else if ( rServiceSpecifier == "com.sun.star.drawing.ControlShape" )
+ {
+ SdrModel& rTargetModel(getSdrModelFromUnoModel());
+ rtl::Reference<SdrObject> pObj = new FmFormObj(rTargetModel);
+ xRet = getXWeak(new SvxShapeControl(pObj.get()));
+ }
+
+ if (!xRet.is())
+ {
+ xRet = SvxUnoDrawMSFactory::createInstance(rServiceSpecifier);
+ }
+
+ return xRet;
+}
+
+
+::com::sun::star::uno::Sequence< OUString > SAL_CALL SvxFmMSFactory::getAvailableServiceNames()
+{
+ static constexpr OUString aSvxComponentServiceNameList[] =
+ {
+ FM_SUN_COMPONENT_TEXTFIELD,
+ FM_SUN_COMPONENT_FORM,
+ FM_SUN_COMPONENT_LISTBOX,
+ FM_SUN_COMPONENT_COMBOBOX,
+ FM_SUN_COMPONENT_RADIOBUTTON,
+ FM_SUN_COMPONENT_GROUPBOX,
+ FM_SUN_COMPONENT_FIXEDTEXT,
+ FM_SUN_COMPONENT_COMMANDBUTTON,
+ FM_SUN_COMPONENT_CHECKBOX,
+ FM_SUN_COMPONENT_GRIDCONTROL,
+ FM_SUN_COMPONENT_IMAGEBUTTON,
+ FM_SUN_COMPONENT_FILECONTROL,
+ FM_SUN_COMPONENT_TIMEFIELD,
+ FM_SUN_COMPONENT_DATEFIELD,
+ FM_SUN_COMPONENT_NUMERICFIELD,
+ FM_SUN_COMPONENT_CURRENCYFIELD,
+ FM_SUN_COMPONENT_PATTERNFIELD,
+ FM_SUN_COMPONENT_HIDDENCONTROL,
+ FM_SUN_COMPONENT_IMAGECONTROL
+ };
+
+ static const sal_uInt16 nSvxComponentServiceNameListCount = SAL_N_ELEMENTS(aSvxComponentServiceNameList);
+
+ auto aSeq( comphelper::arrayToSequence< OUString >(aSvxComponentServiceNameList, nSvxComponentServiceNameListCount) );
+
+ ::com::sun::star::uno::Sequence< OUString > aParentSeq( SvxUnoDrawMSFactory::getAvailableServiceNames() );
+ return comphelper::concatSequences( aParentSeq, aSeq );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmdocumentclassification.cxx b/svx/source/form/fmdocumentclassification.cxx
new file mode 100644
index 0000000000..4878403fd2
--- /dev/null
+++ b/svx/source/form/fmdocumentclassification.cxx
@@ -0,0 +1,197 @@
+/* -*- 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 <fmdocumentclassification.hxx>
+
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/xforms/XFormsSupplier.hpp>
+#include <com/sun/star/frame/XModule.hpp>
+
+#include <o3tl/string_view.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+
+namespace svxform
+{
+
+
+ namespace
+ {
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::container::XChild;
+ using ::com::sun::star::frame::XModel;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::frame::XModule;
+
+
+ template< class TYPE >
+ Reference< TYPE > getTypedModelNode( const Reference< XInterface >& _rxModelNode )
+ {
+ Reference< TYPE > xTypedNode( _rxModelNode, UNO_QUERY );
+ if ( xTypedNode.is() )
+ return xTypedNode;
+ else
+ {
+ Reference< XChild > xChild( _rxModelNode, UNO_QUERY );
+ if ( xChild.is() )
+ return getTypedModelNode< TYPE >( xChild->getParent() );
+ else
+ return Reference< TYPE >();
+ }
+ }
+
+
+ Reference< XModel > getDocument( const Reference< XInterface >& _rxModelNode )
+ {
+ return getTypedModelNode< XModel >( _rxModelNode );
+ }
+ }
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::frame;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::xforms;
+ using namespace ::com::sun::star::container;
+
+
+ namespace
+ {
+
+ struct ModuleInfo
+ {
+ const char* pAsciiModuleOrServiceName;
+ DocumentType eType;
+ };
+
+
+ const ModuleInfo* lcl_getModuleInfo()
+ {
+ static const ModuleInfo aModuleInfo[] =
+ {
+ { "com.sun.star.text.TextDocument", eTextDocument },
+ { "com.sun.star.text.WebDocument", eWebDocument },
+ { "com.sun.star.sheet.SpreadsheetDocument", eSpreadsheetDocument },
+ { "com.sun.star.drawing.DrawingDocument", eDrawingDocument },
+ { "com.sun.star.presentation.PresentationDocument", ePresentationDocument },
+ { "com.sun.star.xforms.XMLFormDocument", eEnhancedForm },
+ { "com.sun.star.sdb.FormDesign", eDatabaseForm },
+ { "com.sun.star.sdb.TextReportDesign", eDatabaseReport },
+ { "com.sun.star.text.GlobalDocument", eTextDocument },
+ { nullptr, eUnknownDocumentType }
+ };
+ return aModuleInfo;
+ }
+ }
+
+
+ //= DocumentClassification
+
+
+ DocumentType DocumentClassification::classifyDocument( const Reference< XModel >& _rxDocumentModel )
+ {
+ DocumentType eType( eUnknownDocumentType );
+
+ OSL_ENSURE( _rxDocumentModel.is(), "DocumentClassification::classifyDocument: invalid document!" );
+ if ( !_rxDocumentModel.is() )
+ return eType;
+
+ try
+ {
+ // first, check whether the document has a ModuleIdentifier which we know
+ Reference< XModule > xModule( _rxDocumentModel, UNO_QUERY );
+ if ( xModule.is() )
+ eType = getDocumentTypeForModuleIdentifier( xModule->getIdentifier() );
+ if ( eType != eUnknownDocumentType )
+ return eType;
+
+ // second, check whether it supports one of the services we know
+ Reference< XServiceInfo > xSI( _rxDocumentModel, UNO_QUERY_THROW );
+ const ModuleInfo* pModuleInfo = lcl_getModuleInfo();
+ while ( pModuleInfo->pAsciiModuleOrServiceName )
+ {
+ if ( xSI->supportsService( OUString::createFromAscii( pModuleInfo->pAsciiModuleOrServiceName ) ) )
+ return pModuleInfo->eType;
+ ++pModuleInfo;
+ }
+
+ // last: uhm, there is no last resort
+ OSL_FAIL( "DocumentClassification::classifyDocument: unknown document!" );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ return eType;
+ }
+
+
+ DocumentType DocumentClassification::classifyHostDocument( const Reference< XInterface >& _rxFormComponent )
+ {
+ DocumentType eType( eUnknownDocumentType );
+
+ try
+ {
+ Reference< XModel > xDocument( getDocument( _rxFormComponent ) );
+ if ( !xDocument.is() )
+ return eUnknownDocumentType;
+ eType = classifyDocument( xDocument );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "DocumentClassification::classifyHostDocument" );
+ }
+
+ return eType;
+ }
+
+
+ DocumentType DocumentClassification::getDocumentTypeForModuleIdentifier( std::u16string_view _rModuleIdentifier )
+ {
+ const ModuleInfo* pModuleInfo = lcl_getModuleInfo();
+ while ( pModuleInfo->pAsciiModuleOrServiceName )
+ {
+ if ( o3tl::equalsAscii(_rModuleIdentifier, pModuleInfo->pAsciiModuleOrServiceName ) )
+ return pModuleInfo->eType;
+ ++pModuleInfo;
+ }
+ return eUnknownDocumentType;
+ }
+
+
+ OUString DocumentClassification::getModuleIdentifierForDocumentType( DocumentType _eType )
+ {
+ const ModuleInfo* pModuleInfo = lcl_getModuleInfo();
+ while ( pModuleInfo->pAsciiModuleOrServiceName )
+ {
+ if ( pModuleInfo->eType == _eType )
+ return OUString::createFromAscii( pModuleInfo->pAsciiModuleOrServiceName );
+ ++pModuleInfo;
+ }
+ return OUString();
+ }
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmexch.cxx b/svx/source/form/fmexch.cxx
new file mode 100644
index 0000000000..fcc814c92c
--- /dev/null
+++ b/svx/source/form/fmexch.cxx
@@ -0,0 +1,347 @@
+/* -*- 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 <fmexch.hxx>
+
+#include <sot/formats.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+namespace svxform
+{
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::datatransfer;
+
+ OLocalExchange::OLocalExchange( )
+ :m_bDragging( false )
+ ,m_bClipboardOwner( false )
+ {
+ }
+
+ void OLocalExchange::copyToClipboard(const weld::Widget& rWidget, const GrantAccess&)
+ {
+ if ( m_bClipboardOwner )
+ { // simulate a lostOwnership to notify parties interested in
+ m_aClipboardListener.Call( *this );
+ }
+
+ m_bClipboardOwner = true;
+ CopyToClipboard(rWidget.get_clipboard());
+ }
+
+ void OLocalExchange::clear()
+ {
+ if ( !isClipboardOwner() )
+ return;
+
+ try
+ {
+ Reference< clipboard::XClipboard > xClipBoard( getOwnClipboard() );
+ if ( xClipBoard.is() )
+ xClipBoard->setContents( nullptr, nullptr );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ m_bClipboardOwner = false;
+ }
+
+ void SAL_CALL OLocalExchange::lostOwnership( const Reference< clipboard::XClipboard >& _rxClipboard, const Reference< XTransferable >& _rxTrans )
+ {
+ TransferDataContainer::implCallOwnLostOwnership( _rxClipboard, _rxTrans );
+ m_bClipboardOwner = false;
+
+ m_aClipboardListener.Call( *this );
+ }
+
+ void OLocalExchange::setDragging(bool bDragging)
+ {
+ m_bDragging = bDragging;
+ }
+
+ void OLocalExchange::DragFinished( sal_Int8 nDropAction )
+ {
+ TransferDataContainer::DragFinished( nDropAction );
+ setDragging(false);
+ }
+
+ bool OLocalExchange::hasFormat( const DataFlavorExVector& _rFormats, SotClipboardFormatId _nFormatId )
+ {
+ return std::any_of(_rFormats.begin(), _rFormats.end(),
+ [&_nFormatId](const DataFlavorEx& rFormat) { return rFormat.mnSotId == _nFormatId; });
+ }
+
+ bool OLocalExchange::GetData( const css::datatransfer::DataFlavor& /*_rFlavor*/, const OUString& /*rDestDoc*/ )
+ {
+ return false; // do not have any formats by default
+ }
+
+ OControlTransferData::OControlTransferData( )
+ : m_bFocusEntry(false)
+ {
+ }
+
+ OControlTransferData::OControlTransferData( const Reference< XTransferable >& _rxTransferable )
+ : m_bFocusEntry(false)
+ {
+ TransferableDataHelper aExchangedData( _rxTransferable );
+
+ // try the formats we know
+ if ( OControlExchange::hasControlPathFormat( aExchangedData.GetDataFlavorExVector() ) )
+ { // paths to the controls, relative to a root
+ Sequence< Any > aControlPathData;
+ if ( aExchangedData.GetAny(OControlExchange::getControlPathFormatId(), OUString()) >>= aControlPathData )
+ {
+ DBG_ASSERT( aControlPathData.getLength() >= 2, "OControlTransferData::OControlTransferData: invalid data for the control path format!" );
+ if ( aControlPathData.getLength() >= 2 )
+ {
+ aControlPathData[0] >>= m_xFormsRoot;
+ aControlPathData[1] >>= m_aControlPaths;
+ }
+ }
+ else
+ {
+ OSL_FAIL( "OControlTransferData::OControlTransferData: invalid data for the control path format (2)!" );
+ }
+ }
+ if ( OControlExchange::hasHiddenControlModelsFormat( aExchangedData.GetDataFlavorExVector() ) )
+ { // sequence of models of hidden controls
+ aExchangedData.GetAny(OControlExchange::getHiddenControlModelsFormatId(), OUString()) >>= m_aHiddenControlModels;
+ }
+
+ updateFormats( );
+ }
+
+
+ static bool lcl_fillDataFlavorEx( SotClipboardFormatId nId, DataFlavorEx& _rFlavor )
+ {
+ _rFlavor.mnSotId = nId;
+ return SotExchange::GetFormatDataFlavor( _rFlavor.mnSotId, _rFlavor );
+ }
+
+
+ void OControlTransferData::updateFormats( )
+ {
+ m_aCurrentFormats.clear();
+ m_aCurrentFormats.reserve( 3 );
+
+ DataFlavorEx aFlavor;
+
+ if ( m_aHiddenControlModels.hasElements() )
+ {
+ if ( lcl_fillDataFlavorEx( OControlExchange::getHiddenControlModelsFormatId(), aFlavor ) )
+ m_aCurrentFormats.push_back( aFlavor );
+ }
+
+ if ( m_xFormsRoot.is() && m_aControlPaths.hasElements() )
+ {
+ if ( lcl_fillDataFlavorEx( OControlExchange::getControlPathFormatId(), aFlavor ) )
+ m_aCurrentFormats.push_back( aFlavor );
+ }
+
+ if ( !m_aSelectedEntries.empty() )
+ {
+ if ( lcl_fillDataFlavorEx( OControlExchange::getFieldExchangeFormatId(), aFlavor ) )
+ m_aCurrentFormats.push_back( aFlavor );
+ }
+ }
+
+ size_t OControlTransferData::onEntryRemoved(const weld::TreeView* pView, const weld::TreeIter* _pEntry)
+ {
+ auto aIter = std::find_if(m_aSelectedEntries.begin(), m_aSelectedEntries.end(),
+ [pView, _pEntry](const auto& rElem) {
+ return pView->iter_compare(*rElem, *_pEntry) == 0;
+ });
+ if (aIter != m_aSelectedEntries.end())
+ m_aSelectedEntries.erase(aIter);
+
+ return m_aSelectedEntries.size();
+ }
+
+ void OControlTransferData::addSelectedEntry(std::unique_ptr<weld::TreeIter> xEntry)
+ {
+ m_aSelectedEntries.emplace(std::move(xEntry));
+ }
+
+ void OControlTransferData::setFocusEntry(bool _bFocusEntry)
+ {
+ m_bFocusEntry = _bFocusEntry;
+ }
+
+ void OControlTransferData::addHiddenControlsFormat(const css::uno::Sequence< css::uno::Reference< css::uno::XInterface > >& seqInterfaces)
+ {
+ m_aHiddenControlModels = seqInterfaces;
+ }
+
+ void OControlTransferData::buildPathFormat(const weld::TreeView* pTreeBox, const weld::TreeIter* pRoot)
+ {
+ m_aControlPaths.realloc(0);
+
+ sal_Int32 nEntryCount = m_aSelectedEntries.size();
+ if (nEntryCount == 0)
+ return;
+
+ m_aControlPaths.realloc(nEntryCount);
+ css::uno::Sequence<sal_uInt32>* pAllPaths = m_aControlPaths.getArray();
+ for (const auto& rCurrentEntry : m_aSelectedEntries)
+ {
+ // first we collect the path in an array
+ ::std::vector< sal_uInt32 > aCurrentPath;
+
+ std::unique_ptr<weld::TreeIter> xLoop(pTreeBox->make_iterator(rCurrentEntry.get()));
+ while (pTreeBox->iter_compare(*xLoop, *pRoot) != 0)
+ {
+ aCurrentPath.push_back(pTreeBox->get_iter_index_in_parent(*xLoop));
+ bool bLoop = pTreeBox->iter_parent(*xLoop);
+ assert(bLoop && "OControlTransferData::buildPathFormat: invalid root or entry !"); (void)bLoop;
+ }
+
+ // then we can transfer it into css::uno::Sequence
+ Sequence<sal_uInt32>& rCurrentPath = *pAllPaths;
+ sal_Int32 nDepth = aCurrentPath.size();
+
+ rCurrentPath.realloc(nDepth);
+ sal_uInt32* pSeq = rCurrentPath.getArray();
+ sal_Int32 j,k;
+ for (j = nDepth - 1, k = 0; k<nDepth; --j, ++k)
+ pSeq[j] = aCurrentPath[k];
+ ++pAllPaths;
+ }
+ }
+
+ void OControlTransferData::buildListFromPath(const weld::TreeView* pTreeBox, const weld::TreeIter* pRoot)
+ {
+ ListBoxEntrySet().swap(m_aSelectedEntries);
+
+ for (const css::uno::Sequence<sal_uInt32>& rPaths : std::as_const(m_aControlPaths))
+ {
+ std::unique_ptr<weld::TreeIter> xSearch(pTreeBox->make_iterator(pRoot));
+ for (const sal_uInt32 nThisPath : rPaths)
+ pTreeBox->iter_nth_child(*xSearch, nThisPath);
+ m_aSelectedEntries.emplace(std::move(xSearch));
+ }
+ }
+
+ OControlExchange::OControlExchange( )
+ {
+ }
+
+ bool OControlExchange::GetData( const DataFlavor& _rFlavor, const OUString& rDestDoc )
+ {
+ const SotClipboardFormatId nFormatId = SotExchange::GetFormat( _rFlavor );
+
+ if ( getControlPathFormatId( ) == nFormatId )
+ {
+ // ugly. We have to pack all the info into one object
+ Sequence< Any > aCompleteInfo( 2 );
+ OSL_ENSURE( m_xFormsRoot.is(), "OLocalExchange::GetData: invalid forms root for this format!" );
+ aCompleteInfo.getArray()[ 0 ] <<= m_xFormsRoot;
+ aCompleteInfo.getArray()[ 1 ] <<= m_aControlPaths;
+
+ SetAny( Any( aCompleteInfo ) );
+ }
+ else if ( getHiddenControlModelsFormatId() == nFormatId )
+ {
+ // just need to transfer the models
+ SetAny( Any( m_aHiddenControlModels ) );
+ }
+ else
+ return OLocalExchange::GetData(_rFlavor, rDestDoc);
+
+ return true;
+ }
+
+ void OControlExchange::AddSupportedFormats()
+ {
+ if (m_bFocusEntry && !m_aSelectedEntries.empty())
+ AddFormat(getFieldExchangeFormatId());
+
+ if (m_aControlPaths.hasElements())
+ AddFormat(getControlPathFormatId());
+
+ if (m_aHiddenControlModels.hasElements())
+ AddFormat(getHiddenControlModelsFormatId());
+ }
+
+ SotClipboardFormatId OControlExchange::getControlPathFormatId()
+ {
+ static SotClipboardFormatId s_nFormat =
+ SotExchange::RegisterFormatName("application/x-openoffice;windows_formatname=\"svxform.ControlPathExchange\"");
+ DBG_ASSERT(static_cast<SotClipboardFormatId>(-1) != s_nFormat, "OControlExchange::getControlPathFormatId: bad exchange id!");
+ return s_nFormat;
+ }
+
+ SotClipboardFormatId OControlExchange::getHiddenControlModelsFormatId()
+ {
+ static SotClipboardFormatId s_nFormat =
+ SotExchange::RegisterFormatName("application/x-openoffice;windows_formatname=\"svxform.HiddenControlModelsExchange\"");
+ DBG_ASSERT(static_cast<SotClipboardFormatId>(-1) != s_nFormat, "OControlExchange::getHiddenControlModelsFormatId: bad exchange id!");
+ return s_nFormat;
+ }
+
+
+ SotClipboardFormatId OControlExchange::getFieldExchangeFormatId()
+ {
+ static SotClipboardFormatId s_nFormat =
+ SotExchange::RegisterFormatName("application/x-openoffice;windows_formatname=\"svxform.FieldNameExchange\"");
+ DBG_ASSERT(static_cast<SotClipboardFormatId>(-1) != s_nFormat, "OControlExchange::getFieldExchangeFormatId: bad exchange id!");
+ return s_nFormat;
+ }
+
+ //= OControlExchangeHelper
+ rtl::Reference<OLocalExchange> OControlExchangeHelper::createExchange() const
+ {
+ return new OControlExchange;
+ }
+
+ OLocalExchangeHelper::OLocalExchangeHelper()
+ {
+ }
+
+ OLocalExchangeHelper::~OLocalExchangeHelper()
+ {
+ implReset();
+ }
+
+ void OLocalExchangeHelper::copyToClipboard(const weld::Widget& rWidget) const
+ {
+ DBG_ASSERT( m_xTransferable.is(), "OLocalExchangeHelper::copyToClipboard: not prepared!" );
+ m_xTransferable->copyToClipboard(rWidget, OLocalExchange::GrantAccess());
+ }
+
+ void OLocalExchangeHelper::implReset()
+ {
+ if (m_xTransferable.is())
+ {
+ m_xTransferable->setClipboardListener( Link<OLocalExchange&,void>() );
+ m_xTransferable.clear();
+ }
+ }
+
+ void OLocalExchangeHelper::prepareDrag( )
+ {
+ DBG_ASSERT(!m_xTransferable.is() || !m_xTransferable->isDragging(), "OLocalExchangeHelper::prepareDrag: recursive DnD?");
+
+ implReset();
+ m_xTransferable = createExchange();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmexpl.cxx b/svx/source/form/fmexpl.cxx
new file mode 100644
index 0000000000..53827917d9
--- /dev/null
+++ b/svx/source/form/fmexpl.cxx
@@ -0,0 +1,529 @@
+/* -*- 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 <svx/strings.hrc>
+#include <fmexpl.hxx>
+
+#include <helpids.h>
+#include <svx/svdobjkind.hxx>
+#include <svx/fmtools.hxx>
+#include <fmexch.hxx>
+
+#include <svx/svxids.hrc>
+
+#include <fmprop.hxx>
+#include <bitmaps.hlst>
+#include <svx/dialmgr.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <sfx2/objitem.hxx>
+
+#include <svx/fmshell.hxx>
+#include <comphelper/types.hxx>
+#include <utility>
+
+using namespace ::svxform;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::container;
+
+FmNavInsertedHint::FmNavInsertedHint( FmEntryData* pInsertedEntryData, sal_uInt32 nRelPos )
+ :pEntryData( pInsertedEntryData )
+ ,nPos( nRelPos )
+
+{
+}
+
+
+FmNavInsertedHint::~FmNavInsertedHint()
+{
+}
+
+
+
+FmNavModelReplacedHint::FmNavModelReplacedHint( FmEntryData* pAffectedEntryData )
+ :pEntryData( pAffectedEntryData )
+{
+}
+
+
+FmNavModelReplacedHint::~FmNavModelReplacedHint()
+{
+}
+
+FmNavRemovedHint::FmNavRemovedHint( FmEntryData* pRemovedEntryData )
+ :pEntryData( pRemovedEntryData )
+{
+}
+
+
+FmNavRemovedHint::~FmNavRemovedHint()
+{
+}
+
+FmNavNameChangedHint::FmNavNameChangedHint( FmEntryData* pData, OUString _aNewName )
+ :pEntryData( pData )
+ ,aNewName(std::move( _aNewName ))
+{
+}
+
+
+FmNavNameChangedHint::~FmNavNameChangedHint()
+{
+}
+
+FmNavClearedHint::FmNavClearedHint()
+{
+}
+
+
+FmNavClearedHint::~FmNavClearedHint()
+{
+}
+
+
+FmEntryDataList::FmEntryDataList()
+{
+}
+
+
+FmEntryDataList::~FmEntryDataList()
+{
+}
+
+
+void FmEntryDataList::removeNoDelete( FmEntryData* pItem )
+{
+ auto it = std::find_if(maEntryDataList.begin(), maEntryDataList.end(),
+ [&pItem](const std::unique_ptr<FmEntryData>& rEntryData) { return rEntryData.get() == pItem; });
+ if (it != maEntryDataList.end())
+ {
+ // coverity[leaked_storage] - deliberately not deleted, ownership transferred
+ it->release();
+ maEntryDataList.erase( it );
+ return;
+ }
+ assert(false);
+}
+
+
+void FmEntryDataList::insert( std::unique_ptr<FmEntryData> pItem, size_t Index )
+{
+ if ( Index < maEntryDataList.size() )
+ {
+ maEntryDataList.insert( maEntryDataList.begin() + Index, std::move(pItem) );
+ }
+ else
+ maEntryDataList.push_back( std::move(pItem) );
+}
+
+
+void FmEntryDataList::clear()
+{
+ maEntryDataList.clear();
+}
+
+
+FmEntryData::FmEntryData( FmEntryData* pParentData, const Reference< XInterface >& _rxIFace )
+ :pParent( pParentData )
+{
+ pChildList.reset( new FmEntryDataList() );
+
+ newObject( _rxIFace );
+}
+
+
+FmEntryData::~FmEntryData()
+{
+ pChildList->clear();
+}
+
+
+void FmEntryData::newObject( const css::uno::Reference< css::uno::XInterface >& _rxIFace )
+{
+ // do not just copy, normalize it
+ m_xNormalizedIFace.set( _rxIFace, UNO_QUERY );
+ m_xProperties.set(m_xNormalizedIFace, css::uno::UNO_QUERY);
+ m_xChild.set(m_xNormalizedIFace, css::uno::UNO_QUERY);
+}
+
+
+FmEntryData::FmEntryData( const FmEntryData& rEntryData )
+{
+ pChildList.reset( new FmEntryDataList() );
+ aText = rEntryData.GetText();
+ m_aNormalImage = rEntryData.GetNormalImage();
+ pParent = rEntryData.GetParent();
+
+ FmEntryData* pChildData;
+ size_t nEntryCount = rEntryData.GetChildList()->size();
+ for( size_t i = 0; i < nEntryCount; i++ )
+ {
+ pChildData = rEntryData.GetChildList()->at( i );
+ std::unique_ptr<FmEntryData> pNewChildData = pChildData->Clone();
+ pChildList->insert( std::move(pNewChildData), size_t(-1) );
+ }
+
+ m_xNormalizedIFace = rEntryData.m_xNormalizedIFace;
+ m_xProperties = rEntryData.m_xProperties;
+ m_xChild = rEntryData.m_xChild;
+}
+
+
+
+bool FmEntryData::IsEqualWithoutChildren( FmEntryData* pEntryData )
+{
+ if(this == pEntryData)
+ return true;
+
+ if( !pEntryData )
+ return false;
+
+ if( aText != pEntryData->GetText() )
+ return false;
+
+ if( !pEntryData->GetParent() && pParent )
+ return false;
+
+ if( pEntryData->GetParent() && !pParent )
+ return false;
+
+ if( !pEntryData->GetParent() && !pParent )
+ return true;
+
+ if( !pParent->IsEqualWithoutChildren(pEntryData->GetParent()) )
+ return false;
+
+ return true;
+}
+
+FmFormData::FmFormData(const Reference< XForm >& _rxForm, FmFormData* _pParent)
+ : FmEntryData(_pParent, _rxForm)
+ , m_xForm(_rxForm)
+{
+ // set images
+ m_aNormalImage = RID_SVXBMP_FORM;
+
+ // set title
+ if (m_xForm.is())
+ {
+ Reference< XPropertySet > xSet(m_xForm, UNO_QUERY);
+ if (xSet.is())
+ {
+ OUString aEntryName(::comphelper::getString(xSet->getPropertyValue( FM_PROP_NAME )));
+ SetText(aEntryName);
+ }
+ }
+ else
+ SetText( OUString() );
+}
+
+FmFormData::~FmFormData()
+{
+}
+
+FmFormData::FmFormData( const FmFormData& rFormData )
+ :FmEntryData( rFormData )
+{
+ m_xForm = rFormData.GetFormIface();
+}
+
+
+std::unique_ptr<FmEntryData> FmFormData::Clone()
+{
+ return std::unique_ptr<FmEntryData>(new FmFormData( *this ));
+}
+
+
+bool FmFormData::IsEqualWithoutChildren( FmEntryData* pEntryData )
+{
+ if(this == pEntryData)
+ return true;
+ FmFormData* pFormData = dynamic_cast<FmFormData*>(pEntryData);
+ if( !pFormData )
+ return false;
+ if( m_xForm.get() != pFormData->GetFormIface().get() )
+ return false;
+
+ return FmEntryData::IsEqualWithoutChildren( pFormData );
+}
+
+FmControlData::FmControlData(const Reference< XFormComponent >& _rxComponent, FmFormData* _pParent)
+: FmEntryData( _pParent, _rxComponent ),
+ m_xFormComponent( _rxComponent )
+{
+
+ // set images
+ m_aNormalImage = GetImage();
+
+
+ // set title
+ Reference< XPropertySet > xSet(m_xFormComponent, UNO_QUERY);
+ if( xSet.is() )
+ {
+ SetText( ::comphelper::getString(xSet->getPropertyValue( FM_PROP_NAME )));
+ }
+}
+
+
+FmControlData::~FmControlData()
+{
+}
+
+
+FmControlData::FmControlData( const FmControlData& rControlData )
+ :FmEntryData( rControlData )
+{
+ m_xFormComponent = rControlData.GetFormComponent();
+}
+
+
+std::unique_ptr<FmEntryData> FmControlData::Clone()
+{
+ return std::unique_ptr<FmEntryData>(new FmControlData( *this ));
+}
+
+
+OUString FmControlData::GetImage() const
+{
+ // Default-Image
+ OUString aImage(RID_SVXBMP_CONTROL);
+
+ Reference< XServiceInfo > xInfo( m_xFormComponent, UNO_QUERY );
+ if (!m_xFormComponent.is())
+ return aImage;
+
+
+ // Spezielle Control-Images
+ SdrObjKind nObjectType = getControlTypeByObject(xInfo);
+ switch (nObjectType)
+ {
+ case SdrObjKind::FormButton:
+ aImage = RID_SVXBMP_BUTTON;
+ break;
+
+ case SdrObjKind::FormFixedText:
+ aImage = RID_SVXBMP_FIXEDTEXT;
+ break;
+
+ case SdrObjKind::FormEdit:
+ aImage = RID_SVXBMP_EDITBOX;
+ break;
+
+ case SdrObjKind::FormRadioButton:
+ aImage = RID_SVXBMP_RADIOBUTTON;
+ break;
+
+ case SdrObjKind::FormCheckbox:
+ aImage = RID_SVXBMP_CHECKBOX;
+ break;
+
+ case SdrObjKind::FormListbox:
+ aImage = RID_SVXBMP_LISTBOX;
+ break;
+
+ case SdrObjKind::FormCombobox:
+ aImage = RID_SVXBMP_COMBOBOX;
+ break;
+
+ case SdrObjKind::FormNavigationBar:
+ aImage = RID_SVXBMP_NAVIGATIONBAR;
+ break;
+
+ case SdrObjKind::FormGroupBox:
+ aImage = RID_SVXBMP_GROUPBOX;
+ break;
+
+ case SdrObjKind::FormImageButton:
+ aImage = RID_SVXBMP_IMAGEBUTTON;
+ break;
+
+ case SdrObjKind::FormFileControl:
+ aImage = RID_SVXBMP_FILECONTROL;
+ break;
+
+ case SdrObjKind::FormHidden:
+ aImage = RID_SVXBMP_HIDDEN;
+ break;
+
+ case SdrObjKind::FormDateField:
+ aImage = RID_SVXBMP_DATEFIELD;
+ break;
+
+ case SdrObjKind::FormTimeField:
+ aImage = RID_SVXBMP_TIMEFIELD;
+ break;
+
+ case SdrObjKind::FormNumericField:
+ aImage = RID_SVXBMP_NUMERICFIELD;
+ break;
+
+ case SdrObjKind::FormCurrencyField:
+ aImage = RID_SVXBMP_CURRENCYFIELD;
+ break;
+
+ case SdrObjKind::FormPatternField:
+ aImage = RID_SVXBMP_PATTERNFIELD;
+ break;
+
+ case SdrObjKind::FormImageControl:
+ aImage = RID_SVXBMP_IMAGECONTROL;
+ break;
+
+ case SdrObjKind::FormFormattedField:
+ aImage = RID_SVXBMP_FORMATTEDFIELD;
+ break;
+
+ case SdrObjKind::FormGrid:
+ aImage = RID_SVXBMP_GRID;
+ break;
+
+ case SdrObjKind::FormScrollbar:
+ aImage = RID_SVXBMP_SCROLLBAR;
+ break;
+
+ case SdrObjKind::FormSpinButton:
+ aImage = RID_SVXBMP_SPINBUTTON;
+ break;
+
+ default:;
+ }
+
+ return aImage;
+}
+
+bool FmControlData::IsEqualWithoutChildren( FmEntryData* pEntryData )
+{
+ if(this == pEntryData)
+ return true;
+
+ FmControlData* pControlData = dynamic_cast<FmControlData*>(pEntryData);
+ if( !pControlData )
+ return false;
+
+ if( m_xFormComponent.get() != pControlData->GetFormComponent().get() )
+ return false;
+
+ return FmEntryData::IsEqualWithoutChildren( pControlData );
+}
+
+void FmControlData::ModelReplaced(const Reference< XFormComponent >& _rxNew)
+{
+ m_xFormComponent = _rxNew;
+ newObject( m_xFormComponent );
+ // set images anew
+ m_aNormalImage = GetImage();
+}
+
+namespace svxform
+{
+
+ NavigatorFrame::NavigatorFrame( SfxBindings* _pBindings, SfxChildWindow* _pMgr,
+ vcl::Window* _pParent )
+ : SfxDockingWindow(_pBindings, _pMgr, _pParent, "FormNavigator", "svx/ui/formnavigator.ui")
+ , SfxControllerItem( SID_FM_FMEXPLORER_CONTROL, *_pBindings )
+ , m_xNavigatorTree(new NavigatorTree(m_xBuilder->weld_tree_view("treeview")))
+ {
+ SetHelpId( HID_FORM_NAVIGATOR_WIN );
+
+ SetText( SvxResId(RID_STR_FMEXPLORER) );
+ SfxDockingWindow::SetFloatingSize( Size(200,200) );
+ }
+
+ NavigatorFrame::~NavigatorFrame()
+ {
+ disposeOnce();
+ }
+
+ void NavigatorFrame::dispose()
+ {
+ m_xNavigatorTree.reset();
+ ::SfxControllerItem::dispose();
+ SfxDockingWindow::dispose();
+ }
+
+ void NavigatorFrame::UpdateContent( FmFormShell* pFormShell )
+ {
+ m_xNavigatorTree->UpdateContent(pFormShell);
+ }
+
+ void NavigatorFrame::StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState )
+ {
+ if( !pState || SID_FM_FMEXPLORER_CONTROL != nSID )
+ return;
+
+ if( eState >= SfxItemState::DEFAULT )
+ {
+ FmFormShell* pShell = dynamic_cast<FmFormShell*>( static_cast<const SfxObjectItem*>(pState)->GetShell() );
+ UpdateContent( pShell );
+ }
+ else
+ UpdateContent( nullptr );
+ }
+
+ void NavigatorFrame::GetFocus()
+ {
+ if (m_xNavigatorTree )
+ m_xNavigatorTree->GrabFocus();
+ else
+ SfxDockingWindow::GetFocus();
+ }
+
+ bool NavigatorFrame::Close()
+ {
+ UpdateContent( nullptr );
+ return SfxDockingWindow::Close();
+ }
+
+ void NavigatorFrame::FillInfo( SfxChildWinInfo& rInfo ) const
+ {
+ SfxDockingWindow::FillInfo( rInfo );
+ rInfo.bVisible = false;
+ }
+
+ Size NavigatorFrame::CalcDockingSize( SfxChildAlignment eAlign )
+ {
+ if ( ( eAlign == SfxChildAlignment::TOP ) || ( eAlign == SfxChildAlignment::BOTTOM ) )
+ return Size();
+
+ return SfxDockingWindow::CalcDockingSize( eAlign );
+ }
+
+ SfxChildAlignment NavigatorFrame::CheckAlignment( SfxChildAlignment _eActAlign, SfxChildAlignment _eAlign )
+ {
+ if ( ( _eAlign == SfxChildAlignment::LEFT ) || ( _eAlign == SfxChildAlignment::RIGHT ) || ( _eAlign == SfxChildAlignment::NOALIGNMENT ) )
+ return _eAlign;
+ return _eActAlign;
+ }
+
+ SFX_IMPL_DOCKINGWINDOW( NavigatorFrameManager, SID_FM_SHOW_FMEXPLORER )
+
+ NavigatorFrameManager::NavigatorFrameManager( vcl::Window* _pParent, sal_uInt16 _nId,
+ SfxBindings* _pBindings, SfxChildWinInfo* _pInfo )
+ :SfxChildWindow( _pParent, _nId )
+ {
+ SetWindow( VclPtr<NavigatorFrame>::Create( _pBindings, this, _pParent ) );
+ static_cast<SfxDockingWindow*>(GetWindow())->Initialize( _pInfo );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmmodel.cxx b/svx/source/form/fmmodel.cxx
new file mode 100644
index 0000000000..71fa374c04
--- /dev/null
+++ b/svx/source/form/fmmodel.cxx
@@ -0,0 +1,205 @@
+/* -*- 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 <fmundo.hxx>
+#include <fmdocumentclassification.hxx>
+#include <fmcontrollayout.hxx>
+
+#include <com/sun/star/form/XForms.hpp>
+#include <svx/fmmodel.hxx>
+#include <svx/fmpage.hxx>
+
+#include <sfx2/objsh.hxx>
+#include <osl/diagnose.h>
+
+#include <optional>
+
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::container::XNameContainer;
+using namespace svxform;
+
+
+struct FmFormModelImplData
+{
+ rtl::Reference<FmXUndoEnvironment> mxUndoEnv;
+ bool bOpenInDesignIsDefaulted;
+ std::optional<bool> aControlsUseRefDevice;
+
+ FmFormModelImplData()
+ :bOpenInDesignIsDefaulted( true )
+ {
+ }
+};
+
+FmFormModel::FmFormModel(SfxItemPool* pPool, SfxObjectShell* pPers)
+ : SdrModel(pPool, pPers)
+ , m_pObjShell(nullptr)
+ , m_bOpenInDesignMode(false)
+ , m_bAutoControlFocus(false)
+{
+ m_pImpl.reset( new FmFormModelImplData );
+ m_pImpl->mxUndoEnv = new FmXUndoEnvironment(*this);
+}
+
+FmFormModel::~FmFormModel()
+{
+ if (m_pObjShell && m_pImpl->mxUndoEnv->IsListening(*m_pObjShell))
+ SetObjectShell(nullptr);
+
+ ClearUndoBuffer();
+ // minimum limit for undos
+ SetMaxUndoActionCount(1);
+}
+
+rtl::Reference<SdrPage> FmFormModel::AllocPage(bool bMasterPage)
+{
+ return new FmFormPage(*this, bMasterPage);
+}
+
+void FmFormModel::InsertPage(SdrPage* pPage, sal_uInt16 nPos)
+{
+ // hack for as long as the method is internal
+ if (m_pObjShell && !m_pImpl->mxUndoEnv->IsListening( *m_pObjShell ))
+ SetObjectShell(m_pObjShell);
+
+ SdrModel::InsertPage( pPage, nPos );
+}
+
+rtl::Reference<SdrPage> FmFormModel::RemovePage(sal_uInt16 nPgNum)
+{
+ FmFormPage* pToBeRemovedPage = dynamic_cast< FmFormPage* >( GetPage( nPgNum ) );
+ OSL_ENSURE( pToBeRemovedPage, "FmFormModel::RemovePage: *which page*?" );
+
+ if ( pToBeRemovedPage )
+ {
+ Reference< XNameContainer > xForms( pToBeRemovedPage->GetForms( false ) );
+ if ( xForms.is() )
+ m_pImpl->mxUndoEnv->RemoveForms( xForms );
+ }
+
+ rtl::Reference<FmFormPage> pRemovedPage = static_cast<FmFormPage*>(SdrModel::RemovePage(nPgNum).get());
+ OSL_ENSURE( pRemovedPage == pToBeRemovedPage, "FmFormModel::RemovePage: inconsistency!" );
+ return pRemovedPage;
+}
+
+void FmFormModel::InsertMasterPage(SdrPage* pPage, sal_uInt16 nPos)
+{
+ // hack for as long as the method is internal
+ if (m_pObjShell && !m_pImpl->mxUndoEnv->IsListening( *m_pObjShell ))
+ SetObjectShell(m_pObjShell);
+
+ SdrModel::InsertMasterPage(pPage, nPos);
+}
+
+rtl::Reference<SdrPage> FmFormModel::RemoveMasterPage(sal_uInt16 nPgNum)
+{
+ rtl::Reference<FmFormPage> pPage = static_cast<FmFormPage*>(SdrModel::RemoveMasterPage(nPgNum).get());
+
+ if ( pPage )
+ {
+ Reference< XNameContainer > xForms( pPage->GetForms( false ) );
+ if ( xForms.is() )
+ m_pImpl->mxUndoEnv->RemoveForms( xForms );
+ }
+
+ return pPage;
+}
+
+
+void FmFormModel::implSetOpenInDesignMode( bool _bOpenDesignMode )
+{
+ if( _bOpenDesignMode != m_bOpenInDesignMode )
+ {
+ m_bOpenInDesignMode = _bOpenDesignMode;
+
+ if ( m_pObjShell )
+ m_pObjShell->SetModified();
+ }
+ // no matter if we really did it or not - from now on, it does not count as defaulted anymore
+ m_pImpl->bOpenInDesignIsDefaulted = false;
+}
+
+
+void FmFormModel::SetOpenInDesignMode( bool bOpenDesignMode )
+{
+ implSetOpenInDesignMode( bOpenDesignMode );
+}
+
+
+bool FmFormModel::OpenInDesignModeIsDefaulted( )
+{
+ return m_pImpl->bOpenInDesignIsDefaulted;
+}
+
+
+bool FmFormModel::ControlsUseRefDevice() const
+{
+ if ( !m_pImpl->aControlsUseRefDevice.has_value() )
+ {
+ DocumentType eDocType = eUnknownDocumentType;
+ if ( m_pObjShell )
+ eDocType = DocumentClassification::classifyHostDocument( m_pObjShell->GetModel() );
+ m_pImpl->aControlsUseRefDevice = ControlLayouter::useDocumentReferenceDevice(eDocType);
+ }
+ return *m_pImpl->aControlsUseRefDevice;
+}
+
+
+void FmFormModel::SetAutoControlFocus( bool _bAutoControlFocus )
+{
+ if( _bAutoControlFocus != m_bAutoControlFocus )
+ {
+ m_bAutoControlFocus = _bAutoControlFocus;
+ m_pObjShell->SetModified();
+ }
+}
+
+
+void FmFormModel::SetObjectShell( SfxObjectShell* pShell )
+{
+ if (pShell == m_pObjShell)
+ return;
+
+ if (m_pObjShell)
+ {
+ m_pImpl->mxUndoEnv->EndListening( *this );
+ m_pImpl->mxUndoEnv->EndListening( *m_pObjShell );
+ }
+
+ m_pObjShell = pShell;
+
+ if (m_pObjShell)
+ {
+ m_pImpl->mxUndoEnv->SetReadOnly( m_pObjShell->IsReadOnly() || m_pObjShell->IsReadOnlyUI(), FmXUndoEnvironment::Accessor() );
+
+ if (!m_pImpl->mxUndoEnv->IsReadOnly())
+ m_pImpl->mxUndoEnv->StartListening(*this);
+
+ m_pImpl->mxUndoEnv->StartListening( *m_pObjShell );
+ }
+}
+
+
+FmXUndoEnvironment& FmFormModel::GetUndoEnv()
+{
+ return *m_pImpl->mxUndoEnv;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmobj.cxx b/svx/source/form/fmobj.cxx
new file mode 100644
index 0000000000..029b0f37e1
--- /dev/null
+++ b/svx/source/form/fmobj.cxx
@@ -0,0 +1,653 @@
+/* -*- 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 <fmobj.hxx>
+#include <fmprop.hxx>
+#include <fmvwimp.hxx>
+#include <fmpgeimp.hxx>
+#include <o3tl/string_view.hxx>
+#include <svx/fmview.hxx>
+#include <svx/fmpage.hxx>
+#include <svx/svdovirt.hxx>
+#include <svx/fmmodel.hxx>
+
+#include <com/sun/star/awt/XDevice.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/form/Forms.hpp>
+#include <com/sun/star/script/XEventAttacherManager.hpp>
+#include <svx/fmtools.hxx>
+
+#include <comphelper/property.hxx>
+#include <comphelper/processfactory.hxx>
+#include <toolkit/awt/vclxdevice.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::script;
+using namespace ::com::sun::star::container;
+using namespace ::svxform;
+
+
+FmFormObj::FmFormObj(
+ SdrModel& rSdrModel,
+ const OUString& rModelName)
+: SdrUnoObj(rSdrModel, rModelName)
+ ,m_nPos(-1)
+ ,m_pLastKnownRefDevice(nullptr)
+{
+ // normally, this is done in SetUnoControlModel, but if the call happened in the base class ctor,
+ // then our incarnation of it was not called (since we were not constructed at this time).
+ impl_checkRefDevice_nothrow( true );
+}
+
+FmFormObj::FmFormObj(SdrModel& rSdrModel)
+: SdrUnoObj(rSdrModel, "")
+ ,m_nPos(-1)
+ ,m_pLastKnownRefDevice(nullptr)
+{
+ // Stuff that old SetModel also did:
+ impl_checkRefDevice_nothrow();
+}
+
+FmFormObj::FmFormObj(SdrModel& rSdrModel, FmFormObj const & rSource)
+: SdrUnoObj(rSdrModel, rSource)
+ ,m_nPos(-1)
+ ,m_pLastKnownRefDevice(nullptr)
+{
+ // Stuff that old SetModel also did:
+ impl_checkRefDevice_nothrow();
+
+ // If UnoControlModel is part of an event environment,
+ // events may assigned to it.
+ Reference< XFormComponent > xContent(rSource.xUnoControlModel, UNO_QUERY);
+ if (xContent.is())
+ {
+ Reference< XEventAttacherManager > xManager(xContent->getParent(), UNO_QUERY);
+ Reference< XIndexAccess > xManagerAsIndex(xManager, UNO_QUERY);
+ if (xManagerAsIndex.is())
+ {
+ sal_Int32 nPos = getElementPos( xManagerAsIndex, xContent );
+ if ( nPos >= 0 )
+ aEvts = xManager->getScriptEvents( nPos );
+ }
+ }
+ else
+ aEvts = rSource.aEvts;
+
+ Reference< XChild > xSourceAsChild(rSource.GetUnoControlModel(), UNO_QUERY);
+ if (!xSourceAsChild.is())
+ return;
+
+ Reference< XInterface > xSourceContainer = xSourceAsChild->getParent();
+
+ m_xEnvironmentHistory = css::form::Forms::create( comphelper::getProcessComponentContext() );
+
+ ensureModelEnv(xSourceContainer, m_xEnvironmentHistory);
+ m_aEventsHistory = aEvts;
+ // if we were clone there was a call to operator=, so aEvts are exactly the events we need here...
+}
+
+FmFormObj::~FmFormObj()
+{
+
+ if (m_xEnvironmentHistory.is())
+ m_xEnvironmentHistory->dispose();
+
+ m_xEnvironmentHistory = nullptr;
+ m_aEventsHistory.realloc(0);
+}
+
+
+void FmFormObj::SetObjEnv(const Reference< XIndexContainer > & xForm, const sal_Int32 nIdx,
+ const Sequence< ScriptEventDescriptor >& rEvts)
+{
+ m_xParent = xForm;
+ aEvts = rEvts;
+ m_nPos = nIdx;
+}
+
+
+void FmFormObj::ClearObjEnv()
+{
+ m_xParent.clear();
+ aEvts.realloc( 0 );
+ m_nPos = -1;
+}
+
+
+void FmFormObj::impl_checkRefDevice_nothrow( bool _force )
+{
+ const FmFormModel* pFormModel = dynamic_cast<FmFormModel*>(&getSdrModelFromSdrObject());
+ if ( !pFormModel || !pFormModel->ControlsUseRefDevice() )
+ return;
+
+ OutputDevice* pCurrentRefDevice = pFormModel->GetRefDevice();
+ if ( ( m_pLastKnownRefDevice.get() == pCurrentRefDevice ) && !_force )
+ return;
+
+ Reference< XControlModel > xControlModel( GetUnoControlModel() );
+ if ( !xControlModel.is() )
+ return;
+
+ m_pLastKnownRefDevice = pCurrentRefDevice;
+ if ( !m_pLastKnownRefDevice )
+ return;
+
+ try
+ {
+ Reference< XPropertySet > xModelProps( GetUnoControlModel(), UNO_QUERY_THROW );
+ Reference< XPropertySetInfo > xPropertyInfo( xModelProps->getPropertySetInfo(), UNO_SET_THROW );
+
+ static constexpr OUString sRefDevicePropName = u"ReferenceDevice"_ustr;
+ if ( xPropertyInfo->hasPropertyByName( sRefDevicePropName ) )
+ {
+ rtl::Reference<VCLXDevice> pUnoRefDevice = new VCLXDevice;
+ pUnoRefDevice->SetOutputDevice( m_pLastKnownRefDevice );
+ Reference< XDevice > xRefDevice( pUnoRefDevice );
+ xModelProps->setPropertyValue( sRefDevicePropName, Any( xRefDevice ) );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+
+void FmFormObj::impl_isolateControlModel_nothrow()
+{
+ try
+ {
+ Reference< XChild > xControlModel( GetUnoControlModel(), UNO_QUERY );
+ if ( xControlModel.is() )
+ {
+ Reference< XIndexContainer> xParent( xControlModel->getParent(), UNO_QUERY );
+ if ( xParent.is() )
+ {
+ sal_Int32 nPos = getElementPos( xParent, xControlModel );
+ xParent->removeByIndex( nPos );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+
+void FmFormObj::handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage)
+{
+ FmFormPage* pOldFormPage(dynamic_cast< FmFormPage* >(getSdrPageFromSdrObject()));
+ if ( pOldFormPage )
+ pOldFormPage->GetImpl().formObjectRemoved( *this );
+
+ FmFormPage* pNewFormPage = dynamic_cast<FmFormPage*>( pNewPage );
+ if ( !pNewFormPage )
+ {
+ // Maybe it makes sense to create an environment history here : if somebody set's our page to NULL, and we have a valid page before,
+ // me may want to remember our place within the old page. For this we could create a new m_xEnvironmentHistory to store it.
+ // So the next SetPage with a valid new page would restore that environment within the new page.
+ // But for the original Bug (#57300#) we don't need that, so I omit it here. Maybe this will be implemented later.
+ impl_isolateControlModel_nothrow();
+ SdrUnoObj::handlePageChange(pOldPage, pNewPage);
+ return;
+ }
+
+ Reference< css::form::XForms > xNewPageForms = pNewFormPage->GetForms();
+ Reference< XIndexContainer > xNewParent;
+ Sequence< ScriptEventDescriptor> aNewEvents;
+
+ // calc the new parent for my model (within the new page's forms hierarchy)
+ // do we have a history ? (from :Clone)
+ if ( m_xEnvironmentHistory.is() )
+ {
+ // the element in m_xEnvironmentHistory which is equivalent to my new parent (which (perhaps) has to be created within pNewPage->GetForms)
+ // is the right-most element in the tree.
+ Reference< XIndexContainer > xRightMostLeaf( m_xEnvironmentHistory, UNO_QUERY_THROW );
+ try
+ {
+ while ( xRightMostLeaf->getCount() )
+ {
+ xRightMostLeaf.set(
+ xRightMostLeaf->getByIndex( xRightMostLeaf->getCount() - 1 ),
+ UNO_QUERY_THROW
+ );
+ }
+
+ xNewParent.set( ensureModelEnv( xRightMostLeaf, xNewPageForms ), UNO_QUERY_THROW );
+
+ // we successfully cloned the environment in m_xEnvironmentHistory, so we can use m_aEventsHistory
+ // (which describes the events of our model at the moment m_xEnvironmentHistory was created)
+ aNewEvents = m_aEventsHistory;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+ if ( !xNewParent.is() )
+ {
+ // are we a valid part of our current page forms ?
+ Reference< XIndexContainer > xOldForms;
+ if ( pOldFormPage )
+ xOldForms.set( pOldFormPage->GetForms(), UNO_QUERY_THROW );
+
+ if ( xOldForms.is() )
+ {
+ // search (upward from our model) for xOldForms
+ Reference< XChild > xSearch( GetUnoControlModel(), UNO_QUERY );
+ while (xSearch.is())
+ {
+ if ( xSearch == xOldForms )
+ break;
+ xSearch.set( xSearch->getParent(), UNO_QUERY );
+ }
+ if ( xSearch.is() ) // implies xSearch == xOldForms, which means we're a valid part of our current page forms hierarchy
+ {
+ Reference< XChild > xMeAsChild( GetUnoControlModel(), UNO_QUERY );
+ xNewParent.set( ensureModelEnv( xMeAsChild->getParent(), xNewPageForms ), UNO_QUERY );
+
+ if ( xNewParent.is() )
+ {
+ try
+ {
+ // transfer the events from our (model's) parent to the new (model's) parent, too
+ Reference< XEventAttacherManager > xEventManager(xMeAsChild->getParent(), UNO_QUERY);
+ Reference< XIndexAccess > xManagerAsIndex(xEventManager, UNO_QUERY);
+ if (xManagerAsIndex.is())
+ {
+ sal_Int32 nPos = getElementPos(xManagerAsIndex, xMeAsChild);
+ if (nPos >= 0)
+ aNewEvents = xEventManager->getScriptEvents(nPos);
+ }
+ else
+ aNewEvents = aEvts;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ }
+ }
+ }
+
+ // now set the page
+ SdrUnoObj::handlePageChange(pOldPage, pNewPage);
+
+ // place my model within the new parent container
+ if (xNewParent.is())
+ {
+ Reference< XFormComponent > xMeAsFormComp(GetUnoControlModel(), UNO_QUERY);
+ if (xMeAsFormComp.is())
+ {
+ // check if I have another parent (and remove me, if necessary)
+ Reference< XIndexContainer > xOldParent(xMeAsFormComp->getParent(), UNO_QUERY);
+ if (xOldParent.is())
+ {
+ sal_Int32 nPos = getElementPos(xOldParent, xMeAsFormComp);
+ if (nPos > -1)
+ xOldParent->removeByIndex(nPos);
+ }
+
+ // and insert into the new container
+ xNewParent->insertByIndex(xNewParent->getCount(), Any(xMeAsFormComp));
+
+ // transfer the events
+ if (aNewEvents.hasElements())
+ {
+ try
+ {
+ Reference< XEventAttacherManager > xEventManager(xNewParent, UNO_QUERY);
+ Reference< XIndexAccess > xManagerAsIndex(xEventManager, UNO_QUERY);
+ if (xManagerAsIndex.is())
+ {
+ sal_Int32 nPos = getElementPos(xManagerAsIndex, xMeAsFormComp);
+ DBG_ASSERT(nPos >= 0, "FmFormObj::SetPage : inserted but not present ?");
+ xEventManager->registerScriptEvents(nPos, aNewEvents);
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ }
+ }
+ }
+
+ // delete my history
+ if (m_xEnvironmentHistory.is())
+ m_xEnvironmentHistory->dispose();
+
+ m_xEnvironmentHistory = nullptr;
+ m_aEventsHistory.realloc(0);
+
+ pNewFormPage->GetImpl().formObjectInserted( *this );
+}
+
+SdrInventor FmFormObj::GetObjInventor() const
+{
+ return SdrInventor::FmForm;
+}
+
+SdrObjKind FmFormObj::GetObjIdentifier() const
+{
+ return SdrObjKind::UNO;
+}
+
+rtl::Reference<SdrObject> FmFormObj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new FmFormObj(rTargetModel, *this);
+}
+
+void FmFormObj::NbcReformatText()
+{
+ impl_checkRefDevice_nothrow();
+ SdrUnoObj::NbcReformatText();
+}
+
+
+namespace
+{
+ OUString lcl_getFormComponentAccessPath(const Reference< XInterface >& _xElement, Reference< XInterface >& _rTopLevelElement)
+ {
+ Reference< css::form::XFormComponent> xChild(_xElement, UNO_QUERY);
+ Reference< css::container::XIndexAccess> xParent;
+ if (xChild.is())
+ xParent.set(xChild->getParent(), UNO_QUERY);
+
+ // while the current content is a form
+ OUString sReturn;
+ while (xChild.is())
+ {
+ // get the content's relative pos within its parent container
+ sal_Int32 nPos = getElementPos(xParent, xChild);
+
+ // prepend this current relative pos
+ OUString sCurrentIndex = OUString::number(nPos);
+ if (!sReturn.isEmpty())
+ {
+ sCurrentIndex += "\\" + sReturn;
+ }
+
+ sReturn = sCurrentIndex;
+
+ // travel up
+ xChild.set(xParent, css::uno::UNO_QUERY);
+ if (xChild.is())
+ xParent.set(xChild->getParent(), UNO_QUERY);
+ }
+
+ _rTopLevelElement = xParent;
+ return sReturn;
+ }
+}
+
+
+Reference< XInterface > FmFormObj::ensureModelEnv(const Reference< XInterface > & _rSourceContainer, const Reference<css::form::XForms>& _rTopLevelDestContainer)
+{
+ Reference< XInterface > xTopLevelSource;
+ OUString sAccessPath = lcl_getFormComponentAccessPath(_rSourceContainer, xTopLevelSource);
+ if (!xTopLevelSource.is())
+ // something went wrong, maybe _rSourceContainer isn't part of a valid forms hierarchy
+ return Reference< XInterface > ();
+
+ Reference< XIndexContainer > xDestContainer(_rTopLevelDestContainer, UNO_QUERY_THROW);
+ Reference< XIndexContainer > xSourceContainer(xTopLevelSource, UNO_QUERY);
+ DBG_ASSERT(xSourceContainer.is(), "FmFormObj::ensureModelEnv : the top level source is invalid !");
+
+ sal_Int32 nTokIndex = 0;
+ do
+ {
+ std::u16string_view aToken = o3tl::getToken(sAccessPath, 0, '\\', nTokIndex );
+ sal_uInt16 nIndex = static_cast<sal_uInt16>(o3tl::toInt32(aToken));
+
+ // get the DSS of the source form (we have to find an equivalent for)
+ DBG_ASSERT(nIndex<xSourceContainer->getCount(), "FmFormObj::ensureModelEnv : invalid access path !");
+ Reference< XPropertySet > xSourceForm;
+ xSourceContainer->getByIndex(nIndex) >>= xSourceForm;
+ DBG_ASSERT(xSourceForm.is(), "FmFormObj::ensureModelEnv : invalid source form !");
+
+ Any aSrcCursorSource, aSrcCursorSourceType, aSrcDataSource;
+ DBG_ASSERT(::comphelper::hasProperty(FM_PROP_COMMAND, xSourceForm) && ::comphelper::hasProperty(FM_PROP_COMMANDTYPE, xSourceForm)
+ && ::comphelper::hasProperty(FM_PROP_DATASOURCE, xSourceForm), "FmFormObj::ensureModelEnv : invalid access path or invalid form (missing props) !");
+ // the parent access path should refer to a row set
+ try
+ {
+ aSrcCursorSource = xSourceForm->getPropertyValue(FM_PROP_COMMAND);
+ aSrcCursorSourceType = xSourceForm->getPropertyValue(FM_PROP_COMMANDTYPE);
+ aSrcDataSource = xSourceForm->getPropertyValue(FM_PROP_DATASOURCE);
+ }
+ catch(Exception&)
+ {
+ OSL_FAIL("FmFormObj::ensureModelEnv : could not retrieve a source DSS !");
+ }
+
+
+ // calc the number of (source) form siblings with the same DSS
+ Reference< XPropertySet > xCurrentSourceForm, xCurrentDestForm;
+ sal_Int16 nCurrentSourceIndex = 0;
+ sal_Int32 nCurrentDestIndex = 0;
+ while (nCurrentSourceIndex <= nIndex)
+ {
+ bool bEqualDSS = false;
+ while (!bEqualDSS) // (we don't have to check nCurrentSourceIndex here : it's bound by nIndex)
+ {
+ xSourceContainer->getByIndex(nCurrentSourceIndex) >>= xCurrentSourceForm;
+ DBG_ASSERT(xCurrentSourceForm.is(), "FmFormObj::ensureModelEnv : invalid form ancestor (2) !");
+ bEqualDSS = false;
+ if (::comphelper::hasProperty(FM_PROP_DATASOURCE, xCurrentSourceForm))
+ { // it is a form
+ try
+ {
+ if ( xCurrentSourceForm->getPropertyValue(FM_PROP_COMMAND) == aSrcCursorSource
+ && xCurrentSourceForm->getPropertyValue(FM_PROP_COMMANDTYPE) == aSrcCursorSourceType
+ && xCurrentSourceForm->getPropertyValue(FM_PROP_DATASOURCE) == aSrcDataSource
+ )
+ {
+ bEqualDSS = true;
+ }
+ }
+ catch(Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx.form",
+ "exception while getting a sibling's DSS !");
+ }
+
+ }
+ ++nCurrentSourceIndex;
+ }
+
+ DBG_ASSERT(bEqualDSS, "FmFormObj::ensureModelEnv : found no source form !");
+ // ??? at least the nIndex-th one should have been found ???
+
+ // now search the next one with the given DSS (within the destination container)
+ bEqualDSS = false;
+ while (!bEqualDSS && (nCurrentDestIndex < xDestContainer->getCount()))
+ {
+ xDestContainer->getByIndex(nCurrentDestIndex) >>= xCurrentDestForm;
+ DBG_ASSERT(xCurrentDestForm.is(), "FmFormObj::ensureModelEnv : invalid destination form !");
+ bEqualDSS = false;
+ if (::comphelper::hasProperty(FM_PROP_DATASOURCE, xCurrentDestForm))
+ { // it is a form
+ try
+ {
+ if ( xCurrentDestForm->getPropertyValue(FM_PROP_COMMAND) == aSrcCursorSource
+ && xCurrentDestForm->getPropertyValue(FM_PROP_COMMANDTYPE) == aSrcCursorSourceType
+ && xCurrentDestForm->getPropertyValue(FM_PROP_DATASOURCE) == aSrcDataSource
+ )
+ {
+ bEqualDSS = true;
+ }
+ }
+ catch(Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx.form",
+ "exception while getting a destination DSS !");
+ }
+
+ }
+ ++nCurrentDestIndex;
+ }
+
+ if (!bEqualDSS)
+ { // There is at least one more source form with the given DSS than destination forms are.
+ // correct this ...
+ try
+ {
+ // create and insert (into the destination) a copy of the form
+ xCurrentDestForm.set(
+ ::comphelper::getProcessServiceFactory()->createInstance("com.sun.star.form.component.DataForm"),
+ UNO_QUERY_THROW );
+ ::comphelper::copyProperties( xCurrentSourceForm, xCurrentDestForm );
+
+ DBG_ASSERT(nCurrentDestIndex == xDestContainer->getCount(), "FmFormObj::ensureModelEnv : something went wrong with the numbers !");
+ xDestContainer->insertByIndex(nCurrentDestIndex, Any(xCurrentDestForm));
+
+ ++nCurrentDestIndex;
+ // like nCurrentSourceIndex, nCurrentDestIndex now points 'behind' the form it actually means
+ }
+ catch(Exception&)
+ {
+ OSL_FAIL("FmFormObj::ensureModelEnv : something went seriously wrong while creating a new form !");
+ // no more options anymore ...
+ return Reference< XInterface > ();
+ }
+
+ }
+ }
+
+ // now xCurrentDestForm is a form equivalent to xSourceForm (which means they have the same DSS and the same number
+ // of left siblings with the same DSS, which counts for all their ancestors, too)
+
+ // go down
+ xDestContainer.set(xCurrentDestForm, UNO_QUERY);
+ xSourceContainer.set(xSourceForm, UNO_QUERY);
+ DBG_ASSERT(xDestContainer.is() && xSourceContainer.is(), "FmFormObj::ensureModelEnv : invalid container !");
+ }
+ while ( nTokIndex >= 0 );
+
+ return Reference<XInterface>( xDestContainer, UNO_QUERY );
+}
+
+FmFormObj* FmFormObj::GetFormObject( SdrObject* _pSdrObject )
+{
+ FmFormObj* pFormObject = dynamic_cast< FmFormObj* >( _pSdrObject );
+ if ( !pFormObject )
+ {
+ SdrVirtObj* pVirtualObject = dynamic_cast< SdrVirtObj* >( _pSdrObject );
+ if ( pVirtualObject )
+ pFormObject = dynamic_cast< FmFormObj* >( &pVirtualObject->ReferencedObj() );
+ }
+ return pFormObject;
+}
+
+
+const FmFormObj* FmFormObj::GetFormObject( const SdrObject* _pSdrObject )
+{
+ const FmFormObj* pFormObject = dynamic_cast< const FmFormObj* >( _pSdrObject );
+ if ( !pFormObject )
+ {
+ const SdrVirtObj* pVirtualObject = dynamic_cast< const SdrVirtObj* >( _pSdrObject );
+ if ( pVirtualObject )
+ pFormObject = dynamic_cast< const FmFormObj* >( &pVirtualObject->GetReferencedObj() );
+ }
+ return pFormObject;
+}
+
+
+void FmFormObj::SetUnoControlModel( const Reference< css::awt::XControlModel >& _rxModel )
+{
+ SdrUnoObj::SetUnoControlModel( _rxModel );
+
+ FmFormPage* pFormPage(dynamic_cast< FmFormPage* >(getSdrPageFromSdrObject()));
+ if ( pFormPage )
+ pFormPage->GetImpl().formModelAssigned( *this );
+
+ impl_checkRefDevice_nothrow( true );
+}
+
+
+bool FmFormObj::EndCreate( SdrDragStat& rStat, SdrCreateCmd eCmd )
+{
+ bool bResult = SdrUnoObj::EndCreate(rStat, eCmd);
+ if ( bResult && SdrCreateCmd::ForceEnd == eCmd && rStat.GetView() )
+ {
+ FmFormPage* pFormPage(dynamic_cast< FmFormPage* >(getSdrPageFromSdrObject()));
+
+ if (nullptr != pFormPage)
+ {
+ try
+ {
+ Reference< XFormComponent > xContent( xUnoControlModel, UNO_QUERY_THROW );
+ Reference< XForm > xParentForm( xContent->getParent(), UNO_QUERY );
+
+ Reference< XIndexContainer > xFormToInsertInto;
+
+ if ( !xParentForm.is() )
+ { // model is not yet part of a form component hierarchy
+ xParentForm.set( pFormPage->GetImpl().findPlaceInFormComponentHierarchy( xContent ), UNO_SET_THROW );
+ xFormToInsertInto.set( xParentForm, UNO_QUERY_THROW );
+ }
+
+ FmFormPageImpl::setUniqueName( xContent, xParentForm );
+
+ if ( xFormToInsertInto.is() )
+ xFormToInsertInto->insertByIndex( xFormToInsertInto->getCount(), Any( xContent ) );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+ FmFormView* pView( dynamic_cast< FmFormView* >( rStat.GetView() ) );
+ FmXFormView* pViewImpl = pView ? pView->GetImpl() : nullptr;
+ OSL_ENSURE( pViewImpl, "FmFormObj::EndCreate: no view!?" );
+ if ( pViewImpl )
+ pViewImpl->onCreatedFormObject( *this );
+ }
+ return bResult;
+}
+
+
+void FmFormObj::BrkCreate( SdrDragStat& rStat )
+{
+ SdrUnoObj::BrkCreate( rStat );
+ impl_isolateControlModel_nothrow();
+
+ FmFormView* pView( dynamic_cast< FmFormView* >( rStat.GetView() ) );
+ FmXFormView* pViewImpl = pView ? pView->GetImpl() : nullptr;
+ OSL_ENSURE( pViewImpl, "FmFormObj::EndCreate: no view!?" );
+ if ( pViewImpl )
+ pViewImpl->breakCreateFormObject();
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmobjfac.cxx b/svx/source/form/fmobjfac.cxx
new file mode 100644
index 0000000000..459350663a
--- /dev/null
+++ b/svx/source/form/fmobjfac.cxx
@@ -0,0 +1,231 @@
+/* -*- 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 <config_fuzzers.h>
+
+#include <comphelper/diagnose_ex.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/fmtools.hxx>
+#include <fmservs.hxx>
+
+#include <svx/fmobjfac.hxx>
+
+#include <svx/svdobjkind.hxx>
+
+#include <fmobj.hxx>
+
+#include <svx/fmshell.hxx>
+
+#include <svx/svxids.hrc>
+#include <tbxform.hxx>
+
+#include <tabwin.hxx>
+#include <fmexpl.hxx>
+#include <filtnav.hxx>
+
+#include <fmprop.hxx>
+#include <fmPropBrw.hxx>
+#include <datanavi.hxx>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::svxform;
+
+static bool bInit = false;
+
+FmFormObjFactory::FmFormObjFactory()
+{
+ if ( bInit )
+ return;
+
+ SdrObjFactory::InsertMakeObjectHdl(LINK(this, FmFormObjFactory, MakeObject));
+
+
+ // register the configuration css::frame::Controller and the NavigationBar
+ SvxFmTbxCtlAbsRec::RegisterControl( SID_FM_RECORD_ABSOLUTE );
+ SvxFmTbxCtlRecText::RegisterControl( SID_FM_RECORD_TEXT );
+ SvxFmTbxCtlRecFromText::RegisterControl( SID_FM_RECORD_FROM_TEXT );
+ SvxFmTbxCtlRecTotal::RegisterControl( SID_FM_RECORD_TOTAL );
+ SvxFmTbxPrevRec::RegisterControl( SID_FM_RECORD_PREV );
+ SvxFmTbxNextRec::RegisterControl( SID_FM_RECORD_NEXT );
+
+ // registering global windows
+ FmFieldWinMgr::RegisterChildWindow();
+ FmPropBrwMgr::RegisterChildWindow();
+ NavigatorFrameManager::RegisterChildWindow();
+ DataNavigatorManager::RegisterChildWindow();
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ FmFilterNavigatorWinMgr::RegisterChildWindow();
+#endif
+
+ // register the interface for the Formshell
+ FmFormShell::RegisterInterface();
+
+ ImplSmartRegisterUnoServices();
+ bInit = true;
+}
+
+// create css::form::Form objects
+namespace
+{
+ void lcl_initProperty( FmFormObj const * _pObject, const OUString& _rPropName, const Any& _rValue )
+ {
+ try
+ {
+ Reference< XPropertySet > xModelSet( _pObject->GetUnoControlModel(), UNO_QUERY );
+ if ( xModelSet.is() )
+ xModelSet->setPropertyValue( _rPropName, _rValue );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "lcl_initProperty" );
+ }
+ }
+}
+
+IMPL_STATIC_LINK(FmFormObjFactory, MakeObject, SdrObjCreatorParams, aParams, rtl::Reference<SdrObject>)
+{
+ rtl::Reference<SdrObject> pNewObj;
+
+ if (aParams.nInventor == SdrInventor::FmForm)
+ {
+ OUString sServiceSpecifier;
+
+ typedef ::std::vector< ::std::pair< OUString, Any > > PropertyValueArray;
+ PropertyValueArray aInitialProperties;
+
+ switch ( aParams.nObjIdentifier )
+ {
+ case SdrObjKind::FormEdit:
+ sServiceSpecifier = FM_COMPONENT_EDIT;
+ break;
+
+ case SdrObjKind::FormButton:
+ sServiceSpecifier = FM_COMPONENT_COMMANDBUTTON;
+ break;
+
+ case SdrObjKind::FormFixedText:
+ sServiceSpecifier = FM_COMPONENT_FIXEDTEXT;
+ break;
+
+ case SdrObjKind::FormListbox:
+ sServiceSpecifier = FM_COMPONENT_LISTBOX;
+ break;
+
+ case SdrObjKind::FormCheckbox:
+ sServiceSpecifier = FM_COMPONENT_CHECKBOX;
+ break;
+
+ case SdrObjKind::FormRadioButton:
+ sServiceSpecifier = FM_COMPONENT_RADIOBUTTON;
+ break;
+
+ case SdrObjKind::FormGroupBox:
+ sServiceSpecifier = FM_COMPONENT_GROUPBOX;
+ break;
+
+ case SdrObjKind::FormCombobox:
+ sServiceSpecifier = FM_COMPONENT_COMBOBOX;
+ break;
+
+ case SdrObjKind::FormGrid:
+ sServiceSpecifier = FM_COMPONENT_GRID;
+ break;
+
+ case SdrObjKind::FormImageButton:
+ sServiceSpecifier = FM_COMPONENT_IMAGEBUTTON;
+ break;
+
+ case SdrObjKind::FormFileControl:
+ sServiceSpecifier = FM_COMPONENT_FILECONTROL;
+ break;
+
+ case SdrObjKind::FormDateField:
+ sServiceSpecifier = FM_COMPONENT_DATEFIELD;
+ break;
+
+ case SdrObjKind::FormTimeField:
+ sServiceSpecifier = FM_COMPONENT_TIMEFIELD;
+ aInitialProperties.emplace_back( FM_PROP_TIMEMAX, Any( tools::Time( 23, 59, 59, 999999999 ).GetUNOTime() ) );
+ break;
+
+ case SdrObjKind::FormNumericField:
+ sServiceSpecifier = FM_COMPONENT_NUMERICFIELD;
+ break;
+
+ case SdrObjKind::FormCurrencyField:
+ sServiceSpecifier = FM_COMPONENT_CURRENCYFIELD;
+ break;
+
+ case SdrObjKind::FormPatternField:
+ sServiceSpecifier = FM_COMPONENT_PATTERNFIELD;
+ break;
+
+ case SdrObjKind::FormHidden:
+ sServiceSpecifier = FM_COMPONENT_HIDDEN;
+ break;
+
+ case SdrObjKind::FormImageControl:
+ sServiceSpecifier = FM_COMPONENT_IMAGECONTROL;
+ break;
+
+ case SdrObjKind::FormFormattedField:
+ sServiceSpecifier = FM_COMPONENT_FORMATTEDFIELD;
+ break;
+
+ case SdrObjKind::FormNavigationBar:
+ sServiceSpecifier = FM_SUN_COMPONENT_NAVIGATIONBAR;
+ break;
+
+ case SdrObjKind::FormScrollbar:
+ sServiceSpecifier = FM_SUN_COMPONENT_SCROLLBAR;
+ aInitialProperties.emplace_back( FM_PROP_BORDER, Any( sal_Int16(0) ) );
+ break;
+
+ case SdrObjKind::FormSpinButton:
+ sServiceSpecifier = FM_SUN_COMPONENT_SPINBUTTON;
+ aInitialProperties.emplace_back( FM_PROP_BORDER, Any( sal_Int16(0) ) );
+ break;
+
+ default:
+ break;
+ }
+
+ // create the actual object
+ if ( !sServiceSpecifier.isEmpty() )
+ pNewObj = new FmFormObj(aParams.rSdrModel, sServiceSpecifier);
+ else
+ pNewObj = new FmFormObj(aParams.rSdrModel);
+
+ // initialize some properties which we want to differ from the defaults
+ for (const auto& rInitProp : aInitialProperties)
+ {
+ lcl_initProperty(
+ static_cast< FmFormObj* >( pNewObj.get() ),
+ rInitProp.first,
+ rInitProp.second
+ );
+ }
+ }
+ return pNewObj;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmpage.cxx b/svx/source/form/fmpage.cxx
new file mode 100644
index 0000000000..8c24f3cc22
--- /dev/null
+++ b/svx/source/form/fmpage.cxx
@@ -0,0 +1,170 @@
+/* -*- 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 <svx/fmpage.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+#include <svx/fmmodel.hxx>
+
+#include <fmobj.hxx>
+
+#include <fmpgeimp.hxx>
+
+#include <svx/svdview.hxx>
+#include <tools/urlobj.hxx>
+#include <vcl/help.hxx>
+#include <vcl/window.hxx>
+#include <osl/diagnose.h>
+#include <fmprop.hxx>
+#include <fmundo.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/types.hxx>
+
+using namespace ::svxform;
+using com::sun::star::uno::Reference;
+using com::sun::star::uno::UNO_QUERY;
+
+
+FmFormPage::FmFormPage(FmFormModel& rModel, bool bMasterPage)
+: SdrPage(rModel, bMasterPage)
+ ,m_pImpl( new FmFormPageImpl( *this ) )
+{
+}
+
+void FmFormPage::lateInit(const FmFormPage& rPage)
+{
+ // call parent
+ SdrPage::lateInit( rPage );
+
+ // copy local variables (former stuff from copy constructor)
+ m_pImpl->initFrom( rPage.GetImpl() );
+ m_sPageName = rPage.m_sPageName;
+}
+
+
+FmFormPage::~FmFormPage()
+{
+}
+
+rtl::Reference<SdrPage> FmFormPage::CloneSdrPage(SdrModel& rTargetModel) const
+{
+ FmFormModel& rFmFormModel(static_cast< FmFormModel& >(rTargetModel));
+ rtl::Reference<FmFormPage> pClonedFmFormPage =
+ new FmFormPage(
+ rFmFormModel,
+ IsMasterPage());
+ pClonedFmFormPage->lateInit(*this);
+ return pClonedFmFormPage;
+}
+
+
+void FmFormPage::InsertObject(SdrObject* pObj, size_t nPos)
+{
+ SdrPage::InsertObject( pObj, nPos );
+ static_cast< FmFormModel& >(getSdrModelFromSdrPage()).GetUndoEnv().Inserted(pObj);
+}
+
+
+const Reference< css::form::XForms > & FmFormPage::GetForms( bool _bForceCreate ) const
+{
+ const SdrPage& rMasterPage( *this );
+ const FmFormPage* pFormPage = dynamic_cast< const FmFormPage* >( &rMasterPage );
+ OSL_ENSURE( pFormPage, "FmFormPage::GetForms: referenced page is no FmFormPage - is this allowed?!" );
+ if ( !pFormPage )
+ pFormPage = this;
+
+ return pFormPage->m_pImpl->getForms( _bForceCreate );
+}
+
+
+bool FmFormPage::RequestHelp( vcl::Window* pWindow, SdrView const * pView,
+ const HelpEvent& rEvt )
+{
+ if( pView->IsAction() )
+ return false;
+
+ Point aPos = rEvt.GetMousePosPixel();
+ aPos = pWindow->ScreenToOutputPixel( aPos );
+ aPos = pWindow->PixelToLogic( aPos );
+
+ SdrPageView* pPV = nullptr;
+ SdrObject* pObj = pView->PickObj(aPos, 0, pPV, SdrSearchOptions::DEEP);
+ if (!pObj)
+ return false;
+
+ FmFormObj* pFormObject = FmFormObj::GetFormObject( pObj );
+ if ( !pFormObject )
+ return false;
+
+ OUString aHelpText;
+ css::uno::Reference< css::beans::XPropertySet > xSet( pFormObject->GetUnoControlModel(), css::uno::UNO_QUERY );
+ if (xSet.is())
+ {
+ if (::comphelper::hasProperty(FM_PROP_HELPTEXT, xSet))
+ aHelpText = ::comphelper::getString(xSet->getPropertyValue(FM_PROP_HELPTEXT));
+
+ if (aHelpText.isEmpty() && ::comphelper::hasProperty(FM_PROP_TARGET_URL, xSet))
+ {
+ OUString aText = ::comphelper::getString(xSet->getPropertyValue(FM_PROP_TARGET_URL));
+ INetURLObject aUrl(aText);
+
+ // test if it is a protocol type that I want to display
+ INetProtocol aProtocol = aUrl.GetProtocol();
+ static const INetProtocol s_aQuickHelpSupported[] =
+ { INetProtocol::Ftp, INetProtocol::Http, INetProtocol::File, INetProtocol::Mailto,
+ INetProtocol::Https, INetProtocol::Javascript,
+ INetProtocol::Ldap
+ };
+ for (const INetProtocol& i : s_aQuickHelpSupported)
+ if (i == aProtocol)
+ {
+ aHelpText = aUrl.GetURLNoPass(INetURLObject::DecodeMechanism::Unambiguous);
+ break;
+ }
+ }
+ }
+ if ( !aHelpText.isEmpty() )
+ {
+ // display the help
+ tools::Rectangle aItemRect = pObj->GetCurrentBoundRect();
+ aItemRect = pWindow->LogicToPixel( aItemRect );
+ Point aPt = pWindow->OutputToScreenPixel( aItemRect.TopLeft() );
+ aItemRect.SetLeft( aPt.X() );
+ aItemRect.SetTop( aPt.Y() );
+ aPt = pWindow->OutputToScreenPixel( aItemRect.BottomRight() );
+ aItemRect.SetRight( aPt.X() );
+ aItemRect.SetBottom( aPt.Y() );
+ if( rEvt.GetMode() == HelpEventMode::BALLOON )
+ Help::ShowBalloon( pWindow, aItemRect.Center(), aItemRect, aHelpText);
+ else
+ Help::ShowQuickHelp( pWindow, aItemRect, aHelpText );
+ }
+ return true;
+}
+
+
+rtl::Reference<SdrObject> FmFormPage::RemoveObject(size_t nObjNum)
+{
+ rtl::Reference<SdrObject> pObj = SdrPage::RemoveObject(nObjNum);
+ if (pObj)
+ static_cast< FmFormModel& >(getSdrModelFromSdrPage()).GetUndoEnv().Removed(pObj.get());
+ return pObj;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmpgeimp.cxx b/svx/source/form/fmpgeimp.cxx
new file mode 100644
index 0000000000..3275841b7f
--- /dev/null
+++ b/svx/source/form/fmpgeimp.cxx
@@ -0,0 +1,713 @@
+/* -*- 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 <fmpgeimp.hxx>
+#include <fmundo.hxx>
+#include <svx/fmtools.hxx>
+#include <fmprop.hxx>
+#include <fmservs.hxx>
+#include <fmobj.hxx>
+#include <formcontrolfactory.hxx>
+#include <svx/svditer.hxx>
+#include <svx/strings.hrc>
+#include <treevisitor.hxx>
+
+#include <com/sun/star/sdb/CommandType.hpp>
+#include <com/sun/star/sdbc/XRowSet.hpp>
+#include <com/sun/star/container/EnumerableMap.hpp>
+#include <com/sun/star/drawing/XControlShape.hpp>
+#include <com/sun/star/form/Forms.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+
+#include <sal/log.hxx>
+#include <sfx2/objsh.hxx>
+#include <svx/fmpage.hxx>
+#include <svx/fmmodel.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <svx/dialmgr.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/types.hxx>
+#include <connectivity/dbtools.hxx>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::form;
+using ::com::sun::star::awt::XControlModel;
+using ::com::sun::star::container::XMap;
+using ::com::sun::star::container::EnumerableMap;
+using ::com::sun::star::drawing::XControlShape;
+using namespace ::svxform;
+using namespace ::dbtools;
+
+
+FmFormPageImpl::FmFormPageImpl( FmFormPage& _rPage )
+ :m_rPage( _rPage )
+ ,m_bFirstActivation( true )
+ ,m_bAttemptedFormCreation( false )
+{
+}
+
+
+namespace
+{
+ class FormComponentInfo
+ {
+ public:
+ static size_t childCount( const Reference< XInterface >& _component )
+ {
+ Reference< XIndexAccess > xContainer( _component, UNO_QUERY );
+ if ( xContainer.is() )
+ return xContainer->getCount();
+ return 0;
+ }
+
+ static Reference< XInterface > getChild( const Reference< XInterface >& _component, size_t _index )
+ {
+ Reference< XIndexAccess > xContainer( _component, UNO_QUERY_THROW );
+ return Reference< XInterface >( xContainer->getByIndex( _index ), UNO_QUERY );
+ }
+ };
+
+ typedef ::std::pair< Reference< XInterface >, Reference< XInterface > > FormComponentPair;
+
+ class FormHierarchyComparator
+ {
+ public:
+ FormHierarchyComparator()
+ {
+ }
+
+ static size_t childCount( const FormComponentPair& _components )
+ {
+ size_t lhsCount = FormComponentInfo::childCount( _components.first );
+ size_t rhsCount = FormComponentInfo::childCount( _components.second );
+ if ( lhsCount != rhsCount )
+ throw RuntimeException( "Found inconsistent form component hierarchies (1)!" );
+ return lhsCount;
+ }
+
+ static FormComponentPair getChild( const FormComponentPair& _components, size_t _index )
+ {
+ return FormComponentPair(
+ FormComponentInfo::getChild( _components.first, _index ),
+ FormComponentInfo::getChild( _components.second, _index )
+ );
+ }
+ };
+
+ typedef ::std::map< Reference< XControlModel >, Reference< XControlModel > > MapControlModels;
+
+ class FormComponentAssignment
+ {
+ public:
+ explicit FormComponentAssignment( MapControlModels& _out_controlModelMap )
+ :m_rControlModelMap( _out_controlModelMap )
+ {
+ }
+
+ void process( const FormComponentPair& _component )
+ {
+ Reference< XControlModel > lhsControlModel( _component.first, UNO_QUERY );
+ Reference< XControlModel > rhsControlModel( _component.second, UNO_QUERY );
+ if ( lhsControlModel.is() != rhsControlModel.is() )
+ throw RuntimeException( "Found inconsistent form component hierarchies (2)!" );
+
+ if ( lhsControlModel.is() )
+ m_rControlModelMap[ lhsControlModel ] = rhsControlModel;
+ }
+
+ private:
+ MapControlModels& m_rControlModelMap;
+ };
+}
+
+
+void FmFormPageImpl::initFrom( FmFormPageImpl& i_foreignImpl )
+{
+ // clone the Forms collection
+ const Reference< css::form::XForms > xForeignForms( i_foreignImpl.getForms( false ) );
+
+ if ( !xForeignForms.is() )
+ return;
+
+ try
+ {
+ m_xForms.set( xForeignForms->createClone(), UNO_QUERY_THROW );
+
+ // create a mapping between the original control models and their clones
+ MapControlModels aModelAssignment;
+
+ typedef TreeVisitor< FormComponentPair, FormHierarchyComparator, FormComponentAssignment > FormComponentVisitor;
+ FormComponentVisitor aVisitor{ FormHierarchyComparator() };
+
+ FormComponentAssignment aAssignmentProcessor( aModelAssignment );
+ aVisitor.process( FormComponentPair( xForeignForms, m_xForms ), aAssignmentProcessor );
+
+ // assign the cloned models to their SdrObjects
+ SdrObjListIter aForeignIter( &i_foreignImpl.m_rPage );
+ SdrObjListIter aOwnIter( &m_rPage );
+
+ OSL_ENSURE( aForeignIter.IsMore() == aOwnIter.IsMore(), "FmFormPageImpl::FmFormPageImpl: inconsistent number of objects (1)!" );
+ while ( aForeignIter.IsMore() && aOwnIter.IsMore() )
+ {
+ FmFormObj* pForeignObj = dynamic_cast< FmFormObj* >( aForeignIter.Next() );
+ FmFormObj* pOwnObj = dynamic_cast< FmFormObj* >( aOwnIter.Next() );
+
+ bool bForeignIsForm = pForeignObj && ( pForeignObj->GetObjInventor() == SdrInventor::FmForm );
+ bool bOwnIsForm = pOwnObj && ( pOwnObj->GetObjInventor() == SdrInventor::FmForm );
+
+ if ( bForeignIsForm != bOwnIsForm )
+ {
+ // if this fires, don't attempt to do further assignments, something's completely messed up
+ SAL_WARN( "svx.form", "FmFormPageImpl::FmFormPageImpl: inconsistent ordering of objects!" );
+ break;
+ }
+
+ if ( !bForeignIsForm )
+ // no form control -> next round
+ continue;
+
+ Reference< XControlModel > xForeignModel( pForeignObj->GetUnoControlModel() );
+ if ( !xForeignModel.is() )
+ {
+ // if this fires, the SdrObject does not have a UNO Control Model. This is pathological, but well ...
+ // So the cloned SdrObject will also not have a UNO Control Model.
+ SAL_WARN( "svx.form", "FmFormPageImpl::FmFormPageImpl: control shape without control!" );
+ continue;
+ }
+
+ MapControlModels::const_iterator assignment = aModelAssignment.find( xForeignModel );
+ if ( assignment == aModelAssignment.end() )
+ {
+ // if this fires, the source SdrObject has a model, but it is not part of the model hierarchy in
+ // i_foreignImpl.getForms().
+ // Pathological, too ...
+ SAL_WARN( "svx.form", "FmFormPageImpl::FmFormPageImpl: no clone found for this model!" );
+ continue;
+ }
+
+ pOwnObj->SetUnoControlModel( assignment->second );
+ }
+ OSL_ENSURE( aForeignIter.IsMore() == aOwnIter.IsMore(), "FmFormPageImpl::FmFormPageImpl: inconsistent number of objects (2)!" );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+
+Reference< XMap > FmFormPageImpl::getControlToShapeMap()
+{
+ Reference< XMap > xControlShapeMap( m_aControlShapeMap.get(), UNO_QUERY );
+ if ( xControlShapeMap.is() )
+ return xControlShapeMap;
+
+ xControlShapeMap = impl_createControlShapeMap_nothrow();
+ m_aControlShapeMap = xControlShapeMap;
+ return xControlShapeMap;
+}
+
+
+namespace
+{
+ void lcl_insertFormObject_throw( const FmFormObj& _object, const Reference< XMap >& _map )
+ {
+ // the control model
+ Reference< XControlModel > xControlModel = _object.GetUnoControlModel();
+ OSL_ENSURE( xControlModel.is(), "lcl_insertFormObject_throw: suspicious: no control model!" );
+ if ( !xControlModel.is() )
+ return;
+
+ Reference< XControlShape > xControlShape( const_cast< FmFormObj& >( _object ).getUnoShape(), UNO_QUERY );
+ OSL_ENSURE( xControlShape.is(), "lcl_insertFormObject_throw: suspicious: no control shape!" );
+ if ( !xControlShape.is() )
+ return;
+
+ _map->put( Any( xControlModel ), Any( xControlShape ) );
+ }
+
+ void lcl_removeFormObject_throw( const FmFormObj& _object, const Reference< XMap >& _map )
+ {
+ // the control model
+ Reference< XControlModel > xControlModel = _object.GetUnoControlModel();
+ OSL_ENSURE( xControlModel.is(), "lcl_removeFormObject: suspicious: no control model!" );
+ if ( !xControlModel.is() )
+ {
+ return;
+ }
+
+ Any aOldAssignment = _map->remove( Any( xControlModel ) );
+ OSL_ENSURE(
+ aOldAssignment == Any( Reference< XControlShape >( const_cast< FmFormObj& >( _object ).getUnoShape(), UNO_QUERY ) ),
+ "lcl_removeFormObject: map was inconsistent!" );
+ }
+}
+
+
+Reference< XMap > FmFormPageImpl::impl_createControlShapeMap_nothrow()
+{
+ Reference< XMap > xMap;
+
+ try
+ {
+ xMap = EnumerableMap::create( comphelper::getProcessComponentContext(),
+ ::cppu::UnoType< XControlModel >::get(),
+ ::cppu::UnoType< XControlShape >::get()
+ );
+
+ SdrObjListIter aPageIter( &m_rPage );
+ while ( aPageIter.IsMore() )
+ {
+ // only FmFormObjs are what we're interested in
+ FmFormObj* pCurrent = FmFormObj::GetFormObject( aPageIter.Next() );
+ if ( !pCurrent )
+ continue;
+
+ lcl_insertFormObject_throw( *pCurrent, xMap );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return xMap;
+}
+
+
+const Reference< css::form::XForms >& FmFormPageImpl::getForms( bool _bForceCreate )
+{
+ if ( m_xForms.is() || !_bForceCreate )
+ return m_xForms;
+
+ if ( !m_bAttemptedFormCreation )
+ {
+ m_bAttemptedFormCreation = true;
+
+ Reference<XComponentContext> xContext = comphelper::getProcessComponentContext();
+ m_xForms = css::form::Forms::create( xContext );
+
+ if ( m_aFormsCreationHdl.IsSet() )
+ {
+ m_aFormsCreationHdl.Call( *this );
+ }
+
+ FmFormModel& rFmFormModel(dynamic_cast< FmFormModel& >(m_rPage.getSdrModelFromSdrPage()));
+
+ // give the newly created collection a place in the universe
+ SfxObjectShell* pObjShell(rFmFormModel.GetObjectShell());
+ if ( pObjShell )
+ m_xForms->setParent( pObjShell->GetModel() );
+
+ // tell the UNDO environment that we have a new forms collection
+ rFmFormModel.GetUndoEnv().AddForms( Reference<XNameContainer>(m_xForms,UNO_QUERY_THROW) );
+ }
+ return m_xForms;
+}
+
+
+FmFormPageImpl::~FmFormPageImpl()
+{
+ xCurrentForm = nullptr;
+
+ ::comphelper::disposeComponent( m_xForms );
+}
+
+
+bool FmFormPageImpl::validateCurForm()
+{
+ if ( !xCurrentForm.is() )
+ return false;
+
+ if ( !xCurrentForm->getParent().is() )
+ xCurrentForm.clear();
+
+ return xCurrentForm.is();
+}
+
+
+void FmFormPageImpl::setCurForm(const Reference< css::form::XForm >& xForm)
+{
+ xCurrentForm = xForm;
+}
+
+
+Reference< XForm > FmFormPageImpl::getDefaultForm()
+{
+ Reference< XForm > xForm;
+
+ Reference< XForms > xForms( getForms() );
+
+ // by default, we use our "current form"
+ if ( !validateCurForm() )
+ {
+ // check whether there is a "standard" form
+ if ( Reference<XNameAccess>(xForms,UNO_QUERY_THROW)->hasElements() )
+ {
+ // find the standard form
+ OUString sStandardFormname = SvxResId(RID_STR_STDFORMNAME);
+
+ try
+ {
+ if ( xForms->hasByName( sStandardFormname ) )
+ xForm.set( xForms->getByName( sStandardFormname ), UNO_QUERY_THROW );
+ else
+ {
+ xForm.set( xForms->getByIndex(0), UNO_QUERY_THROW );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ }
+ else
+ {
+ xForm = xCurrentForm;
+ }
+
+ // did not find an existing suitable form -> create a new one
+ if ( !xForm.is() )
+ {
+ SdrModel& rModel(m_rPage.getSdrModelFromSdrPage());
+
+ if( rModel.IsUndoEnabled() )
+ {
+ OUString aStr(SvxResId(RID_STR_FORM));
+ OUString aUndoStr(SvxResId(RID_STR_UNDO_CONTAINER_INSERT));
+ rModel.BegUndo(aUndoStr.replaceFirst("'#'", aStr));
+ }
+
+ try
+ {
+ xForm.set( ::comphelper::getProcessServiceFactory()->createInstance( FM_SUN_COMPONENT_FORM ), UNO_QUERY );
+
+ // a form should always have the command type table as default
+ Reference< XPropertySet > xFormProps( xForm, UNO_QUERY_THROW );
+ xFormProps->setPropertyValue( FM_PROP_COMMANDTYPE, Any( sal_Int32( CommandType::TABLE ) ) );
+
+ // and the "Standard" name
+ OUString sName = SvxResId(RID_STR_STDFORMNAME);
+ xFormProps->setPropertyValue( FM_PROP_NAME, Any( sName ) );
+
+ if( rModel.IsUndoEnabled() )
+ {
+ rModel.AddUndo(
+ std::make_unique<FmUndoContainerAction>(
+ static_cast< FmFormModel& >(rModel),
+ FmUndoContainerAction::Inserted,
+ xForms,
+ xForm,
+ xForms->getCount()));
+ }
+ xForms->insertByName( sName, Any( xForm ) );
+ xCurrentForm = xForm;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ xForm.clear();
+ }
+
+ if( rModel.IsUndoEnabled() )
+ rModel.EndUndo();
+ }
+
+ return xForm;
+}
+
+
+Reference< css::form::XForm > FmFormPageImpl::findPlaceInFormComponentHierarchy(
+ const Reference< XFormComponent > & rContent, const Reference< XDataSource > & rDatabase,
+ const OUString& rDBTitle, const OUString& rCursorSource, sal_Int32 nCommandType )
+{
+ // if the control already is child of a form, don't do anything
+ if (!rContent.is() || rContent->getParent().is())
+ return nullptr;
+
+ Reference< XForm > xForm;
+
+ // If database and CursorSource are set, the form is searched for using
+ // these criteria, otherwise only current and the DefaultForm.
+ if (rDatabase.is() && !rCursorSource.isEmpty())
+ {
+ validateCurForm();
+
+ // first search in the current form
+ xForm = findFormForDataSource( xCurrentForm, rDatabase, rCursorSource, nCommandType );
+
+ Reference< css::container::XIndexAccess > xFormsByIndex = getForms();
+ DBG_ASSERT(xFormsByIndex.is(), "FmFormPageImpl::findPlaceInFormComponentHierarchy : no index access for my forms collection !");
+ sal_Int32 nCount = xFormsByIndex->getCount();
+ for (sal_Int32 i = 0; !xForm.is() && i < nCount; i++)
+ {
+ Reference< css::form::XForm > xToSearch;
+ xFormsByIndex->getByIndex(i) >>= xToSearch;
+ xForm = findFormForDataSource( xToSearch, rDatabase, rCursorSource, nCommandType );
+ }
+
+ // If no css::form found, then create a new one
+ if (!xForm.is())
+ {
+ SdrModel& rModel(m_rPage.getSdrModelFromSdrPage());
+ const bool bUndo(rModel.IsUndoEnabled());
+
+ if( bUndo )
+ {
+ OUString aStr(SvxResId(RID_STR_FORM));
+ OUString aUndoStr(SvxResId(RID_STR_UNDO_CONTAINER_INSERT));
+ aUndoStr = aUndoStr.replaceFirst("#", aStr);
+ rModel.BegUndo(aUndoStr);
+ }
+
+ xForm.set(::comphelper::getProcessServiceFactory()->createInstance(FM_SUN_COMPONENT_FORM), UNO_QUERY);
+ // a form should always have the command type table as default
+ Reference< css::beans::XPropertySet > xFormProps(xForm, UNO_QUERY);
+ try { xFormProps->setPropertyValue(FM_PROP_COMMANDTYPE, Any(sal_Int32(CommandType::TABLE))); }
+ catch(Exception&) { }
+
+ if (!rDBTitle.isEmpty())
+ xFormProps->setPropertyValue(FM_PROP_DATASOURCE,Any(rDBTitle));
+ else
+ {
+ Reference< css::beans::XPropertySet > xDatabaseProps(rDatabase, UNO_QUERY);
+ Any aDatabaseUrl = xDatabaseProps->getPropertyValue(FM_PROP_URL);
+ xFormProps->setPropertyValue(FM_PROP_URL, aDatabaseUrl);
+ }
+
+ xFormProps->setPropertyValue(FM_PROP_COMMAND,Any(rCursorSource));
+ xFormProps->setPropertyValue(FM_PROP_COMMANDTYPE, Any(nCommandType));
+
+ Reference< css::container::XNameAccess > xNamedSet = getForms();
+
+ const bool bTableOrQuery = ( CommandType::TABLE == nCommandType ) || ( CommandType::QUERY == nCommandType );
+ OUString sName = FormControlFactory::getUniqueName( xNamedSet,
+ bTableOrQuery ? rCursorSource : SvxResId(RID_STR_STDFORMNAME) );
+
+ xFormProps->setPropertyValue( FM_PROP_NAME, Any( sName ) );
+
+ if( bUndo )
+ {
+ Reference< css::container::XIndexContainer > xContainer = getForms();
+ rModel.AddUndo(
+ std::make_unique<FmUndoContainerAction>(
+ static_cast< FmFormModel& >(rModel),
+ FmUndoContainerAction::Inserted,
+ xContainer,
+ xForm,
+ xContainer->getCount()));
+ }
+
+ getForms()->insertByName( sName, Any( xForm ) );
+
+ if( bUndo )
+ rModel.EndUndo();
+ }
+ xCurrentForm = xForm;
+ }
+
+ xForm = getDefaultForm();
+ return xForm;
+}
+
+
+Reference< XForm > FmFormPageImpl::findFormForDataSource(
+ const Reference< XForm > & rForm, const Reference< XDataSource > & _rxDatabase,
+ const OUString& _rCursorSource, sal_Int32 nCommandType)
+{
+ Reference< XForm > xResultForm;
+ Reference< XRowSet > xDBForm(rForm, UNO_QUERY);
+ Reference< XPropertySet > xFormProps(rForm, UNO_QUERY);
+ if (!xDBForm.is() || !xFormProps.is())
+ return xResultForm;
+
+ OSL_ENSURE(_rxDatabase.is(), "FmFormPageImpl::findFormForDataSource: invalid data source!");
+ OUString sLookupName; // the name of the data source we're looking for
+ OUString sFormDataSourceName; // the name of the data source the current connection in the form is based on
+ try
+ {
+ Reference< XPropertySet > xDSProps(_rxDatabase, UNO_QUERY);
+ if (xDSProps.is())
+ xDSProps->getPropertyValue(FM_PROP_NAME) >>= sLookupName;
+
+ xFormProps->getPropertyValue(FM_PROP_DATASOURCE) >>= sFormDataSourceName;
+ // if there's no DataSourceName set at the form, check whether we can deduce one from its
+ // ActiveConnection
+ if (sFormDataSourceName.isEmpty())
+ {
+ Reference< XConnection > xFormConnection;
+ xFormProps->getPropertyValue( FM_PROP_ACTIVE_CONNECTION ) >>= xFormConnection;
+ if ( !xFormConnection.is() )
+ isEmbeddedInDatabase( xFormProps, xFormConnection );
+ if (xFormConnection.is())
+ {
+ Reference< XChild > xConnAsChild(xFormConnection, UNO_QUERY);
+ if (xConnAsChild.is())
+ {
+ Reference< XDataSource > xFormDS(xConnAsChild->getParent(), UNO_QUERY);
+ if (xFormDS.is())
+ {
+ xDSProps.set(xFormDS, css::uno::UNO_QUERY);
+ if (xDSProps.is())
+ xDSProps->getPropertyValue(FM_PROP_NAME) >>= sFormDataSourceName;
+ }
+ }
+ }
+ }
+ }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx", "FmFormPageImpl::findFormForDataSource");
+ }
+
+ if (sLookupName == sFormDataSourceName)
+ {
+ // now check whether CursorSource and type match
+ OUString aCursorSource = ::comphelper::getString(xFormProps->getPropertyValue(FM_PROP_COMMAND));
+ sal_Int32 nType = ::comphelper::getINT32(xFormProps->getPropertyValue(FM_PROP_COMMANDTYPE));
+ if (aCursorSource.isEmpty() || ((nType == nCommandType) && (aCursorSource == _rCursorSource))) // found the form
+ {
+ xResultForm = rForm;
+ // if no data source is set yet, it is done here
+ if (aCursorSource.isEmpty())
+ {
+ xFormProps->setPropertyValue(FM_PROP_COMMAND, Any(_rCursorSource));
+ xFormProps->setPropertyValue(FM_PROP_COMMANDTYPE, Any(nCommandType));
+ }
+ }
+ }
+
+ // as long as xResultForm is NULL, search the child forms of rForm
+ Reference< XIndexAccess > xComponents(rForm, UNO_QUERY);
+ sal_Int32 nCount = xComponents->getCount();
+ for (sal_Int32 i = 0; !xResultForm.is() && i < nCount; ++i)
+ {
+ Reference< css::form::XForm > xSearchForm;
+ xComponents->getByIndex(i) >>= xSearchForm;
+ // continue searching in the sub form
+ if (xSearchForm.is())
+ xResultForm = findFormForDataSource( xSearchForm, _rxDatabase, _rCursorSource, nCommandType );
+ }
+ return xResultForm;
+}
+
+
+OUString FmFormPageImpl::setUniqueName(const Reference< XFormComponent > & xFormComponent, const Reference< XForm > & xControls)
+{
+#if OSL_DEBUG_LEVEL > 0
+ try
+ {
+ OSL_ENSURE( !xFormComponent->getParent().is(), "FmFormPageImpl::setUniqueName: to be called before insertion!" );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+#endif
+ OUString sName;
+ Reference< css::beans::XPropertySet > xSet(xFormComponent, UNO_QUERY);
+ if (xSet.is())
+ {
+ sName = ::comphelper::getString( xSet->getPropertyValue( FM_PROP_NAME ) );
+ Reference< css::container::XNameAccess > xNameAcc(xControls, UNO_QUERY);
+
+ if (sName.isEmpty() || xNameAcc->hasByName(sName))
+ {
+ // set a default name via the ClassId
+ sal_Int16 nClassId( FormComponentType::CONTROL );
+ xSet->getPropertyValue( FM_PROP_CLASSID ) >>= nClassId;
+
+ OUString sDefaultName = FormControlFactory::getDefaultUniqueName_ByComponentType(
+ Reference< XNameAccess >( xControls, UNO_QUERY ), xSet );
+
+ // do not overwrite the name of radio buttons that have it!
+ if (sName.isEmpty() || nClassId != css::form::FormComponentType::RADIOBUTTON)
+ {
+ xSet->setPropertyValue(FM_PROP_NAME, Any(sDefaultName));
+ }
+
+ sName = sDefaultName;
+ }
+ }
+ return sName;
+}
+
+
+void FmFormPageImpl::formModelAssigned( const FmFormObj& _object )
+{
+ Reference< XMap > xControlShapeMap( m_aControlShapeMap.get(), UNO_QUERY );
+ if ( !xControlShapeMap.is() )
+ // our map does not exist -> not interested in this event
+ return;
+
+ try
+ {
+ lcl_removeFormObject_throw( _object, xControlShapeMap );
+ lcl_insertFormObject_throw( _object, xControlShapeMap );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+
+void FmFormPageImpl::formObjectInserted( const FmFormObj& _object )
+{
+ Reference< XMap > xControlShapeMap( m_aControlShapeMap.get(), UNO_QUERY );
+ if ( !xControlShapeMap.is() )
+ // our map does not exist -> not interested in this event
+ return;
+
+ try
+ {
+ lcl_insertFormObject_throw( _object, xControlShapeMap );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+
+void FmFormPageImpl::formObjectRemoved( const FmFormObj& _object )
+{
+ Reference< XMap > xControlShapeMap( m_aControlShapeMap.get(), UNO_QUERY );
+ if ( !xControlShapeMap.is() )
+ // our map does not exist -> not interested in this event
+ return;
+
+ try
+ {
+ lcl_removeFormObject_throw( _object, xControlShapeMap );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmscriptingenv.cxx b/svx/source/form/fmscriptingenv.cxx
new file mode 100644
index 0000000000..db7904174b
--- /dev/null
+++ b/svx/source/form/fmscriptingenv.cxx
@@ -0,0 +1,928 @@
+/* -*- 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 <fmscriptingenv.hxx>
+#include <svx/fmmodel.hxx>
+
+#include <com/sun/star/awt/XControl.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/EventObject.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/script/XScriptListener.hpp>
+
+#include <comphelper/diagnose_ex.hxx>
+#include <tools/debug.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <mutex>
+#include <o3tl/sorted_vector.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/app.hxx>
+#include <basic/basmgr.hxx>
+
+#include <memory>
+
+using std::pair;
+
+namespace svxform
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::script::XEventAttacherManager;
+ using ::com::sun::star::lang::IllegalArgumentException;
+ using ::com::sun::star::script::XScriptListener;
+ using ::com::sun::star::script::ScriptEvent;
+ using ::com::sun::star::lang::EventObject;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::lang::DisposedException;
+ using ::com::sun::star::uno::RuntimeException;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::awt::XControl;
+ using ::com::sun::star::beans::XPropertySet;
+
+ //= FormScriptListener
+
+ typedef ::cppu::WeakImplHelper < XScriptListener
+ > FormScriptListener_Base;
+
+ /** implements the XScriptListener interface, is used by FormScriptingEnvironment
+ */
+ class FormScriptListener :public FormScriptListener_Base
+ {
+ private:
+ std::mutex m_aMutex;
+ FormScriptingEnvironment *m_pScriptExecutor;
+
+ public:
+ explicit FormScriptListener( FormScriptingEnvironment * pScriptExecutor );
+
+ // XScriptListener
+ virtual void SAL_CALL firing( const ScriptEvent& aEvent ) override;
+ virtual Any SAL_CALL approveFiring( const ScriptEvent& aEvent ) override;
+ // XEventListener
+ virtual void SAL_CALL disposing( const EventObject& Source ) override;
+
+ // lifetime control
+ void dispose();
+
+ protected:
+ virtual ~FormScriptListener() override;
+
+ private:
+ /** determines whether calling a given method at a given listener interface can be done asynchronously
+
+ @param _rListenerType
+ the name of the UNO type whose method is to be checked
+ @param _rMethodName
+ the name of the method at the interface determined by _rListenerType
+
+ @return
+ <TRUE/> if and only if the method is declared <code>oneway</code>, i.e. can be called asynchronously
+ */
+ static bool impl_allowAsynchronousCall_nothrow( const OUString& _rListenerType, const OUString& _rMethodName );
+
+ /** determines whether the instance is already disposed
+ */
+ bool impl_isDisposed_nothrow() const { return !m_pScriptExecutor; }
+
+ /** fires the given script event in a thread-safe manner
+
+ This methods calls our script executor's doFireScriptEvent, with previously releasing the given mutex guard,
+ but ensuring that our script executor is not deleted between this release and the actual call.
+
+ @param _rGuard
+ a clearable guard to our mutex. Must be the only active guard to our mutex.
+ @param _rEvent
+ the event to fire
+ @param _pSynchronousResult
+ a place to take a possible result of the script call.
+
+ @precond
+ m_pScriptExecutor is not <NULL/>.
+ */
+ void impl_doFireScriptEvent_nothrow( std::unique_lock<std::mutex>& _rGuard, const ScriptEvent& _rEvent, Any* _pSynchronousResult );
+
+ private:
+ DECL_LINK( OnAsyncScriptEvent, void*, void );
+ };
+
+ FormScriptListener::FormScriptListener( FormScriptingEnvironment* pScriptExecutor )
+ :m_pScriptExecutor( pScriptExecutor )
+ {
+ }
+
+
+ FormScriptListener::~FormScriptListener()
+ {
+ }
+
+
+ bool FormScriptListener::impl_allowAsynchronousCall_nothrow( const OUString& _rListenerType, const OUString& _rMethodName )
+ {
+ // This used to be implemented as:
+ // is (_rListenerType + "::" + _rMethodName) a oneway function?
+ // since we got rid of the notion of oneway, this is the list
+ // of oneway methods, autogenerated by postprocessing of
+ // commitdiff 90eac3e69749a9227c4b6902b1f3cef1e338c6d1
+ static const o3tl::sorted_vector<pair<OUString, OUString>> delayed_event_listeners{
+ pair<OUString,OUString>("com.sun.star.accessibility.XAccessibleComponent","grabFocus"),
+ pair<OUString,OUString>("com.sun.star.accessibility.XAccessibleEventBroadcaster","addAccessibleEventListener"),
+ pair<OUString,OUString>("com.sun.star.accessibility.XAccessibleEventBroadcaster","removeAccessibleEventListener"),
+ pair<OUString,OUString>("com.sun.star.accessibility.XAccessibleSelection","clearAccessibleSelection"),
+ pair<OUString,OUString>("com.sun.star.accessibility.XAccessibleSelection","selectAllAccessibleChildren"),
+ pair<OUString,OUString>("com.sun.star.awt.XActionListener","actionPerformed"),
+ pair<OUString,OUString>("com.sun.star.awt.XActivateListener","windowActivated"),
+ pair<OUString,OUString>("com.sun.star.awt.XActivateListener","windowDeactivated"),
+ pair<OUString,OUString>("com.sun.star.awt.XAdjustmentListener","adjustmentValueChanged"),
+ pair<OUString,OUString>("com.sun.star.awt.XButton","addActionListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XButton","removeActionListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XButton","setLabel"),
+ pair<OUString,OUString>("com.sun.star.awt.XButton","setActionCommand"),
+ pair<OUString,OUString>("com.sun.star.awt.XCheckBox","addItemListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XCheckBox","removeItemListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XCheckBox","setState"),
+ pair<OUString,OUString>("com.sun.star.awt.XCheckBox","setLabel"),
+ pair<OUString,OUString>("com.sun.star.awt.XCheckBox","enableTriState"),
+ pair<OUString,OUString>("com.sun.star.awt.XComboBox","addItemListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XComboBox","removeItemListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XComboBox","addActionListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XComboBox","removeActionListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XComboBox","addItem"),
+ pair<OUString,OUString>("com.sun.star.awt.XComboBox","addItems"),
+ pair<OUString,OUString>("com.sun.star.awt.XComboBox","removeItems"),
+ pair<OUString,OUString>("com.sun.star.awt.XComboBox","setDropDownLineCount"),
+ pair<OUString,OUString>("com.sun.star.awt.XControl","setContext"),
+ pair<OUString,OUString>("com.sun.star.awt.XControl","createPeer"),
+ pair<OUString,OUString>("com.sun.star.awt.XControl","setDesignMode"),
+ pair<OUString,OUString>("com.sun.star.awt.XControlContainer","setStatusText"),
+ pair<OUString,OUString>("com.sun.star.awt.XControlContainer","addControl"),
+ pair<OUString,OUString>("com.sun.star.awt.XControlContainer","removeControl"),
+ pair<OUString,OUString>("com.sun.star.awt.XCurrencyField","setValue"),
+ pair<OUString,OUString>("com.sun.star.awt.XCurrencyField","setMin"),
+ pair<OUString,OUString>("com.sun.star.awt.XCurrencyField","setMax"),
+ pair<OUString,OUString>("com.sun.star.awt.XCurrencyField","setFirst"),
+ pair<OUString,OUString>("com.sun.star.awt.XCurrencyField","setLast"),
+ pair<OUString,OUString>("com.sun.star.awt.XCurrencyField","setSpinSize"),
+ pair<OUString,OUString>("com.sun.star.awt.XCurrencyField","setDecimalDigits"),
+ pair<OUString,OUString>("com.sun.star.awt.XCurrencyField","setStrictFormat"),
+ pair<OUString,OUString>("com.sun.star.awt.XDateField","setDate"),
+ pair<OUString,OUString>("com.sun.star.awt.XDateField","setMin"),
+ pair<OUString,OUString>("com.sun.star.awt.XDateField","setMax"),
+ pair<OUString,OUString>("com.sun.star.awt.XDateField","setFirst"),
+ pair<OUString,OUString>("com.sun.star.awt.XDateField","setLast"),
+ pair<OUString,OUString>("com.sun.star.awt.XDateField","setLongFormat"),
+ pair<OUString,OUString>("com.sun.star.awt.XDateField","setStrictFormat"),
+ pair<OUString,OUString>("com.sun.star.awt.XDialog","setTitle"),
+ pair<OUString,OUString>("com.sun.star.awt.XDisplayConnection","addEventHandler"),
+ pair<OUString,OUString>("com.sun.star.awt.XDisplayConnection","removeEventHandler"),
+ pair<OUString,OUString>("com.sun.star.awt.XDisplayConnection","addErrorHandler"),
+ pair<OUString,OUString>("com.sun.star.awt.XDisplayConnection","removeErrorHandler"),
+ pair<OUString,OUString>("com.sun.star.awt.XExtendedToolkit","addTopWindowListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XExtendedToolkit","removeTopWindowListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XExtendedToolkit","addKeyHandler"),
+ pair<OUString,OUString>("com.sun.star.awt.XExtendedToolkit","removeKeyHandler"),
+ pair<OUString,OUString>("com.sun.star.awt.XExtendedToolkit","addFocusListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XExtendedToolkit","removeFocusListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XExtendedToolkit","fireFocusGained"),
+ pair<OUString,OUString>("com.sun.star.awt.XExtendedToolkit","fireFocusLost"),
+ pair<OUString,OUString>("com.sun.star.awt.XFileDialog","setPath"),
+ pair<OUString,OUString>("com.sun.star.awt.XFileDialog","setFilters"),
+ pair<OUString,OUString>("com.sun.star.awt.XFileDialog","setCurrentFilter"),
+ pair<OUString,OUString>("com.sun.star.awt.XFixedHyperlink","setText"),
+ pair<OUString,OUString>("com.sun.star.awt.XFixedHyperlink","setURL"),
+ pair<OUString,OUString>("com.sun.star.awt.XFixedHyperlink","setAlignment"),
+ pair<OUString,OUString>("com.sun.star.awt.XFixedHyperlink","addActionListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XFixedHyperlink","removeActionListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XFixedText","setText"),
+ pair<OUString,OUString>("com.sun.star.awt.XFixedText","setAlignment"),
+ pair<OUString,OUString>("com.sun.star.awt.XFocusListener","focusGained"),
+ pair<OUString,OUString>("com.sun.star.awt.XFocusListener","focusLost"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","setFont"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","selectFont"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","setTextColor"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","setTextFillColor"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","setLineColor"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","setFillColor"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","setRasterOp"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","setClipRegion"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","intersectClipRegion"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","push"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","pop"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","copy"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","draw"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","drawPixel"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","drawLine"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","drawRect"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","drawRoundedRect"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","drawPolyLine"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","drawPolygon"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","drawPolyPolygon"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","drawEllipse"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","drawArc"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","drawPie"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","drawChord"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","drawGradient"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","drawText"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","drawTextArray"),
+ pair<OUString,OUString>("com.sun.star.awt.XImageButton","addActionListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XImageButton","removeActionListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XImageButton","setActionCommand"),
+ pair<OUString,OUString>("com.sun.star.awt.XImageConsumer","init"),
+ pair<OUString,OUString>("com.sun.star.awt.XImageConsumer","setColorModel"),
+ pair<OUString,OUString>("com.sun.star.awt.XImageConsumer","setPixelsByBytes"),
+ pair<OUString,OUString>("com.sun.star.awt.XImageConsumer","setPixelsByLongs"),
+ pair<OUString,OUString>("com.sun.star.awt.XImageConsumer","complete"),
+ pair<OUString,OUString>("com.sun.star.awt.XImageProducer","addConsumer"),
+ pair<OUString,OUString>("com.sun.star.awt.XImageProducer","removeConsumer"),
+ pair<OUString,OUString>("com.sun.star.awt.XImageProducer","startProduction"),
+ pair<OUString,OUString>("com.sun.star.awt.XItemEventBroadcaster","addItemListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XItemEventBroadcaster","removeItemListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XItemListener","itemStateChanged"),
+ pair<OUString,OUString>("com.sun.star.awt.XKeyListener","keyPressed"),
+ pair<OUString,OUString>("com.sun.star.awt.XKeyListener","keyReleased"),
+ pair<OUString,OUString>("com.sun.star.awt.XListBox","addItemListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XListBox","removeItemListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XListBox","addActionListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XListBox","removeActionListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XListBox","addItem"),
+ pair<OUString,OUString>("com.sun.star.awt.XListBox","addItems"),
+ pair<OUString,OUString>("com.sun.star.awt.XListBox","removeItems"),
+ pair<OUString,OUString>("com.sun.star.awt.XListBox","selectItemPos"),
+ pair<OUString,OUString>("com.sun.star.awt.XListBox","selectItemsPos"),
+ pair<OUString,OUString>("com.sun.star.awt.XListBox","selectItem"),
+ pair<OUString,OUString>("com.sun.star.awt.XListBox","setMultipleMode"),
+ pair<OUString,OUString>("com.sun.star.awt.XListBox","setDropDownLineCount"),
+ pair<OUString,OUString>("com.sun.star.awt.XListBox","makeVisible"),
+ pair<OUString,OUString>("com.sun.star.awt.XMenu","addMenuListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XMenu","removeMenuListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XMenu","insertItem"),
+ pair<OUString,OUString>("com.sun.star.awt.XMenu","removeItem"),
+ pair<OUString,OUString>("com.sun.star.awt.XMenu","enableItem"),
+ pair<OUString,OUString>("com.sun.star.awt.XMenu","setItemText"),
+ pair<OUString,OUString>("com.sun.star.awt.XMenu","setPopupMenu"),
+ pair<OUString,OUString>("com.sun.star.awt.XMenuListener","highlight"),
+ pair<OUString,OUString>("com.sun.star.awt.XMenuListener","select"),
+ pair<OUString,OUString>("com.sun.star.awt.XMenuListener","activate"),
+ pair<OUString,OUString>("com.sun.star.awt.XMenuListener","deactivate"),
+ pair<OUString,OUString>("com.sun.star.awt.XMessageBox","setCaptionText"),
+ pair<OUString,OUString>("com.sun.star.awt.XMessageBox","setMessageText"),
+ pair<OUString,OUString>("com.sun.star.awt.XMouseListener","mousePressed"),
+ pair<OUString,OUString>("com.sun.star.awt.XMouseListener","mouseReleased"),
+ pair<OUString,OUString>("com.sun.star.awt.XMouseListener","mouseEntered"),
+ pair<OUString,OUString>("com.sun.star.awt.XMouseListener","mouseExited"),
+ pair<OUString,OUString>("com.sun.star.awt.XNumericField","setValue"),
+ pair<OUString,OUString>("com.sun.star.awt.XNumericField","setMin"),
+ pair<OUString,OUString>("com.sun.star.awt.XNumericField","setMax"),
+ pair<OUString,OUString>("com.sun.star.awt.XNumericField","setFirst"),
+ pair<OUString,OUString>("com.sun.star.awt.XNumericField","setLast"),
+ pair<OUString,OUString>("com.sun.star.awt.XNumericField","setSpinSize"),
+ pair<OUString,OUString>("com.sun.star.awt.XNumericField","setDecimalDigits"),
+ pair<OUString,OUString>("com.sun.star.awt.XNumericField","setStrictFormat"),
+ pair<OUString,OUString>("com.sun.star.awt.XPaintListener","windowPaint"),
+ pair<OUString,OUString>("com.sun.star.awt.XPatternField","setMasks"),
+ pair<OUString,OUString>("com.sun.star.awt.XPatternField","setString"),
+ pair<OUString,OUString>("com.sun.star.awt.XPatternField","setStrictFormat"),
+ pair<OUString,OUString>("com.sun.star.awt.XPointer","setType"),
+ pair<OUString,OUString>("com.sun.star.awt.XPopupMenu","insertSeparator"),
+ pair<OUString,OUString>("com.sun.star.awt.XPopupMenu","setDefaultItem"),
+ pair<OUString,OUString>("com.sun.star.awt.XPopupMenu","checkItem"),
+ pair<OUString,OUString>("com.sun.star.awt.XProgressBar","setForegroundColor"),
+ pair<OUString,OUString>("com.sun.star.awt.XProgressBar","setBackgroundColor"),
+ pair<OUString,OUString>("com.sun.star.awt.XProgressBar","setRange"),
+ pair<OUString,OUString>("com.sun.star.awt.XProgressBar","setValue"),
+ pair<OUString,OUString>("com.sun.star.awt.XProgressMonitor","addText"),
+ pair<OUString,OUString>("com.sun.star.awt.XProgressMonitor","removeText"),
+ pair<OUString,OUString>("com.sun.star.awt.XProgressMonitor","updateText"),
+ pair<OUString,OUString>("com.sun.star.awt.XRadioButton","addItemListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XRadioButton","removeItemListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XRadioButton","setState"),
+ pair<OUString,OUString>("com.sun.star.awt.XRadioButton","setLabel"),
+ pair<OUString,OUString>("com.sun.star.awt.XRegion","clear"),
+ pair<OUString,OUString>("com.sun.star.awt.XRegion","move"),
+ pair<OUString,OUString>("com.sun.star.awt.XRegion","unionRectangle"),
+ pair<OUString,OUString>("com.sun.star.awt.XRegion","intersectRectangle"),
+ pair<OUString,OUString>("com.sun.star.awt.XRegion","excludeRectangle"),
+ pair<OUString,OUString>("com.sun.star.awt.XRegion","xOrRectangle"),
+ pair<OUString,OUString>("com.sun.star.awt.XRegion","unionRegion"),
+ pair<OUString,OUString>("com.sun.star.awt.XRegion","intersectRegion"),
+ pair<OUString,OUString>("com.sun.star.awt.XRegion","excludeRegion"),
+ pair<OUString,OUString>("com.sun.star.awt.XRegion","xOrRegion"),
+ pair<OUString,OUString>("com.sun.star.awt.XScrollBar","addAdjustmentListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XScrollBar","removeAdjustmentListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XScrollBar","setValue"),
+ pair<OUString,OUString>("com.sun.star.awt.XScrollBar","setValues"),
+ pair<OUString,OUString>("com.sun.star.awt.XScrollBar","setMaximum"),
+ pair<OUString,OUString>("com.sun.star.awt.XScrollBar","setLineIncrement"),
+ pair<OUString,OUString>("com.sun.star.awt.XScrollBar","setBlockIncrement"),
+ pair<OUString,OUString>("com.sun.star.awt.XScrollBar","setVisibleSize"),
+ pair<OUString,OUString>("com.sun.star.awt.XScrollBar","setOrientation"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinField","addSpinListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinField","removeSpinListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinField","up"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinField","down"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinField","first"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinField","last"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinField","enableRepeat"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinListener","up"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinListener","down"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinListener","first"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinListener","last"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinValue","addAdjustmentListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinValue","removeAdjustmentListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinValue","setValue"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinValue","setValues"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinValue","setMinimum"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinValue","setMaximum"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinValue","setSpinIncrement"),
+ pair<OUString,OUString>("com.sun.star.awt.XTabController","setModel"),
+ pair<OUString,OUString>("com.sun.star.awt.XTabController","setContainer"),
+ pair<OUString,OUString>("com.sun.star.awt.XTabController","autoTabOrder"),
+ pair<OUString,OUString>("com.sun.star.awt.XTabController","activateTabOrder"),
+ pair<OUString,OUString>("com.sun.star.awt.XTabController","activateFirst"),
+ pair<OUString,OUString>("com.sun.star.awt.XTabController","activateLast"),
+ pair<OUString,OUString>("com.sun.star.awt.XTabControllerModel","setGroupControl"),
+ pair<OUString,OUString>("com.sun.star.awt.XTabControllerModel","setControlModels"),
+ pair<OUString,OUString>("com.sun.star.awt.XTabControllerModel","setGroup"),
+ pair<OUString,OUString>("com.sun.star.awt.XTextComponent","addTextListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XTextComponent","removeTextListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XTextComponent","setText"),
+ pair<OUString,OUString>("com.sun.star.awt.XTextComponent","insertText"),
+ pair<OUString,OUString>("com.sun.star.awt.XTextComponent","setSelection"),
+ pair<OUString,OUString>("com.sun.star.awt.XTextComponent","setEditable"),
+ pair<OUString,OUString>("com.sun.star.awt.XTextComponent","setMaxTextLen"),
+ pair<OUString,OUString>("com.sun.star.awt.XTextEditField","setEchoChar"),
+ pair<OUString,OUString>("com.sun.star.awt.XTextListener","textChanged"),
+ pair<OUString,OUString>("com.sun.star.awt.XTimeField","setTime"),
+ pair<OUString,OUString>("com.sun.star.awt.XTimeField","setMin"),
+ pair<OUString,OUString>("com.sun.star.awt.XTimeField","setMax"),
+ pair<OUString,OUString>("com.sun.star.awt.XTimeField","setFirst"),
+ pair<OUString,OUString>("com.sun.star.awt.XTimeField","setLast"),
+ pair<OUString,OUString>("com.sun.star.awt.XTimeField","setStrictFormat"),
+ pair<OUString,OUString>("com.sun.star.awt.XTopWindow","addTopWindowListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XTopWindow","removeTopWindowListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XTopWindow","toFront"),
+ pair<OUString,OUString>("com.sun.star.awt.XTopWindow","toBack"),
+ pair<OUString,OUString>("com.sun.star.awt.XTopWindow","setMenuBar"),
+ pair<OUString,OUString>("com.sun.star.awt.XTopWindowListener","windowOpened"),
+ pair<OUString,OUString>("com.sun.star.awt.XTopWindowListener","windowClosing"),
+ pair<OUString,OUString>("com.sun.star.awt.XTopWindowListener","windowClosed"),
+ pair<OUString,OUString>("com.sun.star.awt.XTopWindowListener","windowMinimized"),
+ pair<OUString,OUString>("com.sun.star.awt.XTopWindowListener","windowNormalized"),
+ pair<OUString,OUString>("com.sun.star.awt.XTopWindowListener","windowActivated"),
+ pair<OUString,OUString>("com.sun.star.awt.XTopWindowListener","windowDeactivated"),
+ pair<OUString,OUString>("com.sun.star.awt.XUnoControlContainer","setTabControllers"),
+ pair<OUString,OUString>("com.sun.star.awt.XUnoControlContainer","addTabController"),
+ pair<OUString,OUString>("com.sun.star.awt.XUnoControlContainer","removeTabController"),
+ pair<OUString,OUString>("com.sun.star.awt.XUserInputInterception","addKeyHandler"),
+ pair<OUString,OUString>("com.sun.star.awt.XUserInputInterception","removeKeyHandler"),
+ pair<OUString,OUString>("com.sun.star.awt.XUserInputInterception","addMouseClickHandler"),
+ pair<OUString,OUString>("com.sun.star.awt.XUserInputInterception","removeMouseClickHandler"),
+ pair<OUString,OUString>("com.sun.star.awt.XVclContainer","addVclContainerListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XVclContainer","removeVclContainerListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XVclContainerListener","windowAdded"),
+ pair<OUString,OUString>("com.sun.star.awt.XVclContainerListener","windowRemoved"),
+ pair<OUString,OUString>("com.sun.star.awt.XVclContainerPeer","enableDialogControl"),
+ pair<OUString,OUString>("com.sun.star.awt.XVclContainerPeer","setTabOrder"),
+ pair<OUString,OUString>("com.sun.star.awt.XVclContainerPeer","setGroup"),
+ pair<OUString,OUString>("com.sun.star.awt.XVclWindowPeer","setDesignMode"),
+ pair<OUString,OUString>("com.sun.star.awt.XVclWindowPeer","enableClipSiblings"),
+ pair<OUString,OUString>("com.sun.star.awt.XVclWindowPeer","setForeground"),
+ pair<OUString,OUString>("com.sun.star.awt.XVclWindowPeer","setControlFont"),
+ pair<OUString,OUString>("com.sun.star.awt.XView","draw"),
+ pair<OUString,OUString>("com.sun.star.awt.XView","setZoom"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindow","setPosSize"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindow","setVisible"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindow","setEnable"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindow","setFocus"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindow","addWindowListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindow","removeWindowListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindow","addFocusListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindow","removeFocusListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindow","addKeyListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindow","removeKeyListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindow","addMouseListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindow","removeMouseListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindow","addMouseMotionListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindow","removeMouseMotionListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindow","addPaintListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindow","removePaintListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindowListener","windowResized"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindowListener","windowMoved"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindowListener","windowShown"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindowListener","windowHidden"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindowListener2","windowEnabled"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindowListener2","windowDisabled"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindowPeer","setPointer"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindowPeer","setBackground"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindowPeer","invalidate"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindowPeer","invalidateRect"),
+ pair<OUString,OUString>("com.sun.star.awt.grid.XGridSelectionListener","selectionChanged"),
+ pair<OUString,OUString>("com.sun.star.awt.tab.XTabPageContainer","addTabPageContainerListener"),
+ pair<OUString,OUString>("com.sun.star.awt.tab.XTabPageContainer","removeTabPageContainerListener"),
+ pair<OUString,OUString>("com.sun.star.awt.tab.XTabPageContainerListener","tabPageActivated"),
+ pair<OUString,OUString>("com.sun.star.configuration.backend.XBackendChangesNotifier","addChangesListener"),
+ pair<OUString,OUString>("com.sun.star.configuration.backend.XBackendChangesNotifier","removeChangesListener"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.clipboard.XClipboard","setContents"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.clipboard.XClipboardListener","changedContents"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.clipboard.XClipboardNotifier","addClipboardListener"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.clipboard.XClipboardNotifier","removeClipboardListener"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.clipboard.XClipboardOwner","lostOwnership"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XAutoscroll","autoscroll"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDragGestureListener","dragGestureRecognized"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDragGestureRecognizer","addDragGestureListener"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDragGestureRecognizer","removeDragGestureListener"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDragSource","startDrag"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDragSourceContext","setCursor"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDragSourceContext","setImage"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDragSourceContext","transferablesFlavorsChanged"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDragSourceListener","dragDropEnd"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDragSourceListener","dragEnter"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDragSourceListener","dragExit"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDragSourceListener","dragOver"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDragSourceListener","dropActionChanged"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDropTarget","addDropTargetListener"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDropTarget","removeDropTargetListener"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDropTarget","setDefaultActions"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDropTargetDragContext","acceptDrag"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDropTargetDragContext","rejectDrag"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDropTargetDropContext","acceptDrop"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDropTargetDropContext","rejectDrop"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDropTargetDropContext","dropComplete"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDropTargetListener","dragEnter"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDropTargetListener","dragExit"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDropTargetListener","dragOver"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDropTargetListener","dropActionChanged"),
+ pair<OUString,OUString>("com.sun.star.document.XEventBroadcaster","addEventListener"),
+ pair<OUString,OUString>("com.sun.star.document.XEventBroadcaster","removeEventListener"),
+ pair<OUString,OUString>("com.sun.star.document.XEventListener","notifyEvent"),
+ pair<OUString,OUString>("com.sun.star.document.XStorageChangeListener","notifyStorageChange"),
+ pair<OUString,OUString>("com.sun.star.drawing.XControlShape","setControl"),
+ pair<OUString,OUString>("com.sun.star.form.XApproveActionBroadcaster","addApproveActionListener"),
+ pair<OUString,OUString>("com.sun.star.form.XApproveActionBroadcaster","removeApproveActionListener"),
+ pair<OUString,OUString>("com.sun.star.form.XBoundControl","setLock"),
+ pair<OUString,OUString>("com.sun.star.form.XChangeBroadcaster","addChangeListener"),
+ pair<OUString,OUString>("com.sun.star.form.XChangeBroadcaster","removeChangeListener"),
+ pair<OUString,OUString>("com.sun.star.form.XChangeListener","changed"),
+ pair<OUString,OUString>("com.sun.star.form.XConfirmDeleteBroadcaster","addConfirmDeleteListener"),
+ pair<OUString,OUString>("com.sun.star.form.XConfirmDeleteBroadcaster","removeConfirmDeleteListener"),
+ pair<OUString,OUString>("com.sun.star.form.XDatabaseParameterBroadcaster","addParameterListener"),
+ pair<OUString,OUString>("com.sun.star.form.XDatabaseParameterBroadcaster","removeParameterListener"),
+ pair<OUString,OUString>("com.sun.star.form.XDatabaseParameterBroadcaster2","addDatabaseParameterListener"),
+ pair<OUString,OUString>("com.sun.star.form.XDatabaseParameterBroadcaster2","removeDatabaseParameterListener"),
+ pair<OUString,OUString>("com.sun.star.form.XErrorBroadcaster","addErrorListener"),
+ pair<OUString,OUString>("com.sun.star.form.XErrorBroadcaster","removeErrorListener"),
+ pair<OUString,OUString>("com.sun.star.form.XFormController","addActivateListener"),
+ pair<OUString,OUString>("com.sun.star.form.XFormController","removeActivateListener"),
+ pair<OUString,OUString>("com.sun.star.form.XFormControllerListener","formActivated"),
+ pair<OUString,OUString>("com.sun.star.form.XFormControllerListener","formDeactivated"),
+ pair<OUString,OUString>("com.sun.star.form.XGrid","setCurrentColumnPosition"),
+ pair<OUString,OUString>("com.sun.star.form.XGridPeer","setColumns"),
+ pair<OUString,OUString>("com.sun.star.form.XLoadListener","loaded"),
+ pair<OUString,OUString>("com.sun.star.form.XLoadListener","unloading"),
+ pair<OUString,OUString>("com.sun.star.form.XLoadListener","unloaded"),
+ pair<OUString,OUString>("com.sun.star.form.XLoadListener","reloading"),
+ pair<OUString,OUString>("com.sun.star.form.XLoadListener","reloaded"),
+ pair<OUString,OUString>("com.sun.star.form.XLoadable","load"),
+ pair<OUString,OUString>("com.sun.star.form.XLoadable","unload"),
+ pair<OUString,OUString>("com.sun.star.form.XLoadable","reload"),
+ pair<OUString,OUString>("com.sun.star.form.XLoadable","addLoadListener"),
+ pair<OUString,OUString>("com.sun.star.form.XLoadable","removeLoadListener"),
+ pair<OUString,OUString>("com.sun.star.form.XPositioningListener","positioned"),
+ pair<OUString,OUString>("com.sun.star.form.XReset","reset"),
+ pair<OUString,OUString>("com.sun.star.form.XReset","addResetListener"),
+ pair<OUString,OUString>("com.sun.star.form.XReset","removeResetListener"),
+ pair<OUString,OUString>("com.sun.star.form.XResetListener","resetted"),
+ pair<OUString,OUString>("com.sun.star.form.XSubmit","submit"),
+ pair<OUString,OUString>("com.sun.star.form.XSubmit","addSubmitListener"),
+ pair<OUString,OUString>("com.sun.star.form.XSubmit","removeSubmitListener"),
+ pair<OUString,OUString>("com.sun.star.form.XUpdateBroadcaster","addUpdateListener"),
+ pair<OUString,OUString>("com.sun.star.form.XUpdateBroadcaster","removeUpdateListener"),
+ pair<OUString,OUString>("com.sun.star.frame.XBrowseHistoryRegistry","updateViewData"),
+ pair<OUString,OUString>("com.sun.star.frame.XBrowseHistoryRegistry","createNewEntry"),
+ pair<OUString,OUString>("com.sun.star.frame.XConfigManager","addPropertyChangeListener"),
+ pair<OUString,OUString>("com.sun.star.frame.XConfigManager","removePropertyChangeListener"),
+ pair<OUString,OUString>("com.sun.star.frame.XConfigManager","flush"),
+ pair<OUString,OUString>("com.sun.star.frame.XDesktop","addTerminateListener"),
+ pair<OUString,OUString>("com.sun.star.frame.XDesktop","removeTerminateListener"),
+ pair<OUString,OUString>("com.sun.star.frame.XDispatch","dispatch"),
+ pair<OUString,OUString>("com.sun.star.frame.XDispatch","addStatusListener"),
+ pair<OUString,OUString>("com.sun.star.frame.XDispatch","removeStatusListener"),
+ pair<OUString,OUString>("com.sun.star.frame.XDocumentTemplates","update"),
+ pair<OUString,OUString>("com.sun.star.frame.XFrame","setCreator"),
+ pair<OUString,OUString>("com.sun.star.frame.XFrame","setName"),
+ pair<OUString,OUString>("com.sun.star.frame.XFrame","activate"),
+ pair<OUString,OUString>("com.sun.star.frame.XFrame","deactivate"),
+ pair<OUString,OUString>("com.sun.star.frame.XFrame","addFrameActionListener"),
+ pair<OUString,OUString>("com.sun.star.frame.XFrame","removeFrameActionListener"),
+ pair<OUString,OUString>("com.sun.star.frame.XFrameActionListener","frameAction"),
+ pair<OUString,OUString>("com.sun.star.frame.XFrameLoader","load"),
+ pair<OUString,OUString>("com.sun.star.frame.XFrameLoader","cancel"),
+ pair<OUString,OUString>("com.sun.star.frame.XLoadEventListener","loadFinished"),
+ pair<OUString,OUString>("com.sun.star.frame.XLoadEventListener","loadCancelled"),
+ pair<OUString,OUString>("com.sun.star.frame.XModel","connectController"),
+ pair<OUString,OUString>("com.sun.star.frame.XModel","disconnectController"),
+ pair<OUString,OUString>("com.sun.star.frame.XModel","lockControllers"),
+ pair<OUString,OUString>("com.sun.star.frame.XModel","unlockControllers"),
+ pair<OUString,OUString>("com.sun.star.frame.XNotifyingDispatch","dispatchWithNotification"),
+ pair<OUString,OUString>("com.sun.star.frame.XRecordableDispatch","dispatchAndRecord"),
+ pair<OUString,OUString>("com.sun.star.frame.XSessionManagerClient","addSessionManagerListener"),
+ pair<OUString,OUString>("com.sun.star.frame.XSessionManagerClient","removeSessionManagerListener"),
+ pair<OUString,OUString>("com.sun.star.frame.XSessionManagerClient","queryInteraction"),
+ pair<OUString,OUString>("com.sun.star.frame.XSessionManagerClient","interactionDone"),
+ pair<OUString,OUString>("com.sun.star.frame.XSessionManagerClient","saveDone"),
+ pair<OUString,OUString>("com.sun.star.frame.XSessionManagerListener","doSave"),
+ pair<OUString,OUString>("com.sun.star.frame.XSessionManagerListener","approveInteraction"),
+ pair<OUString,OUString>("com.sun.star.frame.XSessionManagerListener","shutdownCanceled"),
+ pair<OUString,OUString>("com.sun.star.frame.XSessionManagerListener2","doQuit"),
+ pair<OUString,OUString>("com.sun.star.frame.XStatusListener","statusChanged"),
+ pair<OUString,OUString>("com.sun.star.frame.XTask","tileWindows"),
+ pair<OUString,OUString>("com.sun.star.frame.XTask","arrangeWindowsVertical"),
+ pair<OUString,OUString>("com.sun.star.frame.XTask","arrangeWindowsHorizontal"),
+ pair<OUString,OUString>("com.sun.star.frame.XWindowArranger","arrange"),
+ pair<OUString,OUString>("com.sun.star.inspection.XPropertyControlContext","activateNextControl"),
+ pair<OUString,OUString>("com.sun.star.inspection.XPropertyControlObserver","focusGained"),
+ pair<OUString,OUString>("com.sun.star.inspection.XPropertyControlObserver","valueChanged"),
+ pair<OUString,OUString>("com.sun.star.mozilla.XCloseSessionListener","sessionClosed"),
+ pair<OUString,OUString>("com.sun.star.mozilla.XMenuProxy","addMenuProxyListener"),
+ pair<OUString,OUString>("com.sun.star.mozilla.XMenuProxy","removeMenuProxyListener"),
+ pair<OUString,OUString>("com.sun.star.mozilla.XPluginInstance","start"),
+ pair<OUString,OUString>("com.sun.star.mozilla.XPluginInstance","stop"),
+ pair<OUString,OUString>("com.sun.star.mozilla.XPluginInstance","destroy"),
+ pair<OUString,OUString>("com.sun.star.mozilla.XPluginInstance","createWindow"),
+ pair<OUString,OUString>("com.sun.star.mozilla.XPluginInstance","newStream"),
+ pair<OUString,OUString>("com.sun.star.mozilla.XPluginInstance","newURL"),
+ pair<OUString,OUString>("com.sun.star.mozilla.XPluginInstanceNotifySink","notifyURL"),
+ pair<OUString,OUString>("com.sun.star.mozilla.XPluginInstancePeer","showStatusMessage"),
+ pair<OUString,OUString>("com.sun.star.mozilla.XPluginInstancePeer","enableScripting"),
+ pair<OUString,OUString>("com.sun.star.mozilla.XPluginInstancePeer","newStream"),
+ pair<OUString,OUString>("com.sun.star.mozilla.XPluginInstancePeer","getURL"),
+ pair<OUString,OUString>("com.sun.star.mozilla.XPluginInstancePeer","postURL"),
+ pair<OUString,OUString>("com.sun.star.mozilla.XPluginWindowPeer","setChildWindow"),
+ pair<OUString,OUString>("com.sun.star.script.vba.XVBACompatibility","addVBAScriptListener"),
+ pair<OUString,OUString>("com.sun.star.script.vba.XVBACompatibility","removeVBAScriptListener"),
+ pair<OUString,OUString>("com.sun.star.sdb.XDatabaseAccess","addDatabaseAccessListener"),
+ pair<OUString,OUString>("com.sun.star.sdb.XDatabaseAccess","removeDatabaseAccessListener"),
+ pair<OUString,OUString>("com.sun.star.sdb.XDatabaseAccessListener","connectionChanged"),
+ pair<OUString,OUString>("com.sun.star.sdb.XDatabaseAccessListener","connectionClosing"),
+ pair<OUString,OUString>("com.sun.star.sdb.XRowSetApproveBroadcaster","addRowSetApproveListener"),
+ pair<OUString,OUString>("com.sun.star.sdb.XRowSetApproveBroadcaster","removeRowSetApproveListener"),
+ pair<OUString,OUString>("com.sun.star.sdb.XRowSetChangeListener","onRowSetChanged"),
+ pair<OUString,OUString>("com.sun.star.sdb.XRowSetSupplier","setRowSet"),
+ pair<OUString,OUString>("com.sun.star.sdb.XRowsChangeListener","rowsChanged"),
+ pair<OUString,OUString>("com.sun.star.sdb.XSQLErrorBroadcaster","addSQLErrorListener"),
+ pair<OUString,OUString>("com.sun.star.sdb.XSQLErrorBroadcaster","removeSQLErrorListener"),
+ pair<OUString,OUString>("com.sun.star.sdbc.XRowSet","addRowSetListener"),
+ pair<OUString,OUString>("com.sun.star.sdbc.XRowSet","removeRowSetListener"),
+ pair<OUString,OUString>("com.sun.star.sdbc.XRowSetListener","cursorMoved"),
+ pair<OUString,OUString>("com.sun.star.sdbc.XRowSetListener","rowChanged"),
+ pair<OUString,OUString>("com.sun.star.sdbc.XRowSetListener","rowSetChanged"),
+ pair<OUString,OUString>("com.sun.star.sheet.XCalculatable","enableAutomaticCalculation"),
+ pair<OUString,OUString>("com.sun.star.sheet.XVolatileResult","addResultListener"),
+ pair<OUString,OUString>("com.sun.star.sheet.XVolatileResult","removeResultListener"),
+ pair<OUString,OUString>("com.sun.star.task.XJobExecutor","trigger"),
+ pair<OUString,OUString>("com.sun.star.task.XStatusIndicator","start"),
+ pair<OUString,OUString>("com.sun.star.task.XStatusIndicator","end"),
+ pair<OUString,OUString>("com.sun.star.task.XStatusIndicator","setText"),
+ pair<OUString,OUString>("com.sun.star.task.XStatusIndicator","setValue"),
+ pair<OUString,OUString>("com.sun.star.task.XStatusIndicator","reset"),
+ pair<OUString,OUString>("com.sun.star.text.XSimpleText","insertString"),
+ pair<OUString,OUString>("com.sun.star.text.XTextCursor","collapseToStart"),
+ pair<OUString,OUString>("com.sun.star.text.XTextCursor","collapseToEnd"),
+ pair<OUString,OUString>("com.sun.star.text.XTextRange","setString"),
+ pair<OUString,OUString>("com.sun.star.text.XTextViewCursor","setVisible"),
+ pair<OUString,OUString>("com.sun.star.ucb.XCommandProcessor","abort"),
+ pair<OUString,OUString>("com.sun.star.ucb.XCommandProcessor2","releaseCommandIdentifier"),
+ pair<OUString,OUString>("com.sun.star.ucb.XContent","addContentEventListener"),
+ pair<OUString,OUString>("com.sun.star.ucb.XContent","removeContentEventListener"),
+ pair<OUString,OUString>("com.sun.star.ucb.XContentProviderManager","deregisterContentProvider"),
+ pair<OUString,OUString>("com.sun.star.ucb.XContentTransmitter","transmit"),
+ pair<OUString,OUString>("com.sun.star.ucb.XPropertySetRegistry","removePropertySet"),
+ pair<OUString,OUString>("com.sun.star.ui.XUIConfigurationListener","elementInserted"),
+ pair<OUString,OUString>("com.sun.star.ui.XUIConfigurationListener","elementRemoved"),
+ pair<OUString,OUString>("com.sun.star.ui.XUIConfigurationListener","elementReplaced"),
+ pair<OUString,OUString>("com.sun.star.ui.dialogs.XFilePickerNotifier","addFilePickerListener"),
+ pair<OUString,OUString>("com.sun.star.ui.dialogs.XFilePickerNotifier","removeFilePickerListener"),
+ pair<OUString,OUString>("com.sun.star.util.XBroadcaster","lockBroadcasts"),
+ pair<OUString,OUString>("com.sun.star.util.XBroadcaster","unlockBroadcasts"),
+ pair<OUString,OUString>("com.sun.star.util.XChangesListener","changesOccurred"),
+ pair<OUString,OUString>("com.sun.star.util.XChangesNotifier","addChangesListener"),
+ pair<OUString,OUString>("com.sun.star.util.XChangesNotifier","removeChangesListener"),
+ pair<OUString,OUString>("com.sun.star.util.XCloseBroadcaster","addCloseListener"),
+ pair<OUString,OUString>("com.sun.star.util.XCloseBroadcaster","removeCloseListener"),
+ pair<OUString,OUString>("com.sun.star.util.XFlushable","addFlushListener"),
+ pair<OUString,OUString>("com.sun.star.util.XFlushable","removeFlushListener"),
+ pair<OUString,OUString>("com.sun.star.util.XModeChangeListener","modeChanged"),
+ pair<OUString,OUString>("com.sun.star.util.XModifyBroadcaster","addModifyListener"),
+ pair<OUString,OUString>("com.sun.star.util.XModifyBroadcaster","removeModifyListener"),
+ pair<OUString,OUString>("com.sun.star.util.XRefreshable","addRefreshListener"),
+ pair<OUString,OUString>("com.sun.star.util.XRefreshable","removeRefreshListener"),
+ pair<OUString,OUString>("com.sun.star.util.XSearchDescriptor","setSearchString"),
+ pair<OUString,OUString>("com.sun.star.view.XPrintJobBroadcaster","addPrintJobListener"),
+ pair<OUString,OUString>("com.sun.star.view.XPrintJobBroadcaster","removePrintJobListener"),
+ pair<OUString,OUString>("com.sun.star.view.XPrintJobListener","printJobEvent"),
+ pair<OUString,OUString>("com.sun.star.view.XPrintableBroadcaster","addPrintableListener"),
+ pair<OUString,OUString>("com.sun.star.view.XPrintableBroadcaster","removePrintableListener"),
+ pair<OUString,OUString>("com.sun.star.view.XPrintableListener","stateChanged"),
+ pair<OUString,OUString>("com.sun.star.view.XSelectionChangeListener","selectionChanged"),
+ pair<OUString,OUString>("com.sun.star.beans.XMultiPropertySet","addPropertiesChangeListener"),
+ pair<OUString,OUString>("com.sun.star.beans.XMultiPropertySet","removePropertiesChangeListener"),
+ pair<OUString,OUString>("com.sun.star.beans.XMultiPropertySet","firePropertiesChangeEvent"),
+ pair<OUString,OUString>("com.sun.star.beans.XPropertiesChangeNotifier","addPropertiesChangeListener"),
+ pair<OUString,OUString>("com.sun.star.beans.XPropertiesChangeNotifier","removePropertiesChangeListener"),
+ pair<OUString,OUString>("com.sun.star.container.XContainer","addContainerListener"),
+ pair<OUString,OUString>("com.sun.star.container.XContainer","removeContainerListener"),
+ pair<OUString,OUString>("com.sun.star.container.XContainerListener","elementInserted"),
+ pair<OUString,OUString>("com.sun.star.container.XContainerListener","elementRemoved"),
+ pair<OUString,OUString>("com.sun.star.container.XContainerListener","elementReplaced"),
+ pair<OUString,OUString>("com.sun.star.container.XNamed","setName"),
+ pair<OUString,OUString>("com.sun.star.io.XDataExporter","exportData"),
+ pair<OUString,OUString>("com.sun.star.io.XDataExporter","cancel"),
+ pair<OUString,OUString>("com.sun.star.io.XDataImporter","importData"),
+ pair<OUString,OUString>("com.sun.star.io.XDataImporter","cancel"),
+ pair<OUString,OUString>("com.sun.star.io.XDataTransferEventListener","finished"),
+ pair<OUString,OUString>("com.sun.star.io.XDataTransferEventListener","cancelled"),
+ pair<OUString,OUString>("com.sun.star.lang.XConnectionPointContainer","advise"),
+ pair<OUString,OUString>("com.sun.star.lang.XConnectionPointContainer","unadvise"),
+ pair<OUString,OUString>("com.sun.star.script.XAllListener","firing"),
+ pair<OUString,OUString>("com.sun.star.uno.XInterface","acquire"),
+ pair<OUString,OUString>("com.sun.star.uno.XInterface","release"),
+ pair<OUString,OUString>("com.sun.star.uno.XReference","dispose")};
+
+ pair<OUString,OUString> k(_rListenerType, _rMethodName);
+ return delayed_event_listeners.find(k) != delayed_event_listeners.end();
+ }
+
+
+ void FormScriptListener::impl_doFireScriptEvent_nothrow( std::unique_lock<std::mutex>& _rGuard, const ScriptEvent& _rEvent, Any* _pSynchronousResult )
+ {
+ OSL_PRECOND( m_pScriptExecutor, "FormScriptListener::impl_doFireScriptEvent_nothrow: this will crash!" );
+
+ _rGuard.unlock();
+ m_pScriptExecutor->doFireScriptEvent( _rEvent, _pSynchronousResult );
+ }
+
+
+ void SAL_CALL FormScriptListener::firing( const ScriptEvent& _rEvent )
+ {
+ if ( _rEvent.ScriptType == "VBAInterop" )
+ return; // not handled here
+
+ std::unique_lock aGuard( m_aMutex );
+
+ if ( impl_isDisposed_nothrow() )
+ return;
+
+ if ( !impl_allowAsynchronousCall_nothrow( _rEvent.ListenerType.getTypeName(), _rEvent.MethodName ) )
+ {
+ impl_doFireScriptEvent_nothrow( aGuard, _rEvent, nullptr );
+ return;
+ }
+
+ acquire();
+ Application::PostUserEvent( LINK( this, FormScriptListener, OnAsyncScriptEvent ), new ScriptEvent( _rEvent ) );
+ }
+
+
+ Any SAL_CALL FormScriptListener::approveFiring( const ScriptEvent& _rEvent )
+ {
+ Any aResult;
+
+ std::unique_lock aGuard( m_aMutex );
+ if ( !impl_isDisposed_nothrow() )
+ impl_doFireScriptEvent_nothrow( aGuard, _rEvent, &aResult );
+
+ return aResult;
+ }
+
+
+ void SAL_CALL FormScriptListener::disposing( const EventObject& /*Source*/ )
+ {
+ // not interested in
+ }
+
+
+ void FormScriptListener::dispose()
+ {
+ std::unique_lock aGuard( m_aMutex );
+ m_pScriptExecutor = nullptr;
+ }
+
+ IMPL_LINK( FormScriptListener, OnAsyncScriptEvent, void*, p, void )
+ {
+ ScriptEvent* _pEvent = static_cast<ScriptEvent*>(p);
+ OSL_PRECOND( _pEvent != nullptr, "FormScriptListener::OnAsyncScriptEvent: invalid event!" );
+ if ( !_pEvent )
+ return;
+
+ {
+ std::unique_lock aGuard( m_aMutex );
+
+ if ( !impl_isDisposed_nothrow() )
+ impl_doFireScriptEvent_nothrow( aGuard, *_pEvent, nullptr );
+ }
+
+ delete _pEvent;
+ // we acquired ourself immediately before posting the event
+ release();
+ }
+
+ FormScriptingEnvironment::FormScriptingEnvironment( FmFormModel& _rModel )
+ :m_rFormModel( _rModel )
+ ,m_bDisposed( false )
+ {
+ m_pScriptListener = new FormScriptListener( this );
+ // note that this is a cyclic reference between the FormScriptListener and the FormScriptingEnvironment
+ // This cycle is broken up when our instance is disposed.
+ }
+
+ void FormScriptingEnvironment::impl_registerOrRevoke_throw( const Reference< XEventAttacherManager >& _rxManager, bool _bRegister )
+ {
+ std::unique_lock aGuard( m_aMutex );
+
+ if ( !_rxManager.is() )
+ throw IllegalArgumentException();
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ try
+ {
+ if ( _bRegister )
+ _rxManager->addScriptListener( m_pScriptListener );
+ else
+ _rxManager->removeScriptListener( m_pScriptListener );
+ }
+ catch( const RuntimeException& ) { throw; }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+
+ void FormScriptingEnvironment::registerEventAttacherManager( const Reference< XEventAttacherManager >& _rxManager )
+ {
+ impl_registerOrRevoke_throw( _rxManager, true );
+ }
+
+
+ void FormScriptingEnvironment::revokeEventAttacherManager( const Reference< XEventAttacherManager >& _rxManager )
+ {
+ impl_registerOrRevoke_throw( _rxManager, false );
+ }
+
+#if HAVE_FEATURE_SCRIPTING
+ namespace
+ {
+ class NewStyleUNOScript
+ {
+ SfxObjectShell& m_rObjectShell;
+ const OUString m_sScriptCode;
+
+ public:
+ NewStyleUNOScript( SfxObjectShell& _rObjectShell, OUString _aScriptCode )
+ :m_rObjectShell( _rObjectShell )
+ ,m_sScriptCode(std::move( _aScriptCode ))
+ {
+ }
+
+ void invoke( const Sequence< Any >& _rArguments, Any& _rSynchronousResult );
+ };
+
+
+ void NewStyleUNOScript::invoke( const Sequence< Any >& _rArguments, Any& _rSynchronousResult )
+ {
+ Sequence< sal_Int16 > aOutArgsIndex;
+ Sequence< Any > aOutArgs;
+ EventObject aEvent;
+ Any aCaller;
+ if ( _rArguments.hasElements() && ( _rArguments[ 0 ] >>= aEvent ) )
+ {
+ try
+ {
+ Reference< XControl > xControl( aEvent.Source, UNO_QUERY_THROW );
+ Reference< XPropertySet > xProps( xControl->getModel(), UNO_QUERY_THROW );
+ aCaller = xProps->getPropertyValue("Name");
+ }
+ catch( Exception& ) {}
+ }
+ m_rObjectShell.CallXScript( m_sScriptCode, _rArguments, _rSynchronousResult, aOutArgsIndex, aOutArgs, true, aCaller.hasValue() ? &aCaller : nullptr );
+ }
+ }
+#endif
+
+ void FormScriptingEnvironment::doFireScriptEvent( const ScriptEvent& _rEvent, Any* _pSynchronousResult )
+ {
+#if !HAVE_FEATURE_SCRIPTING
+ (void) _rEvent;
+ (void) _pSynchronousResult;
+ (void) m_rFormModel;
+#else
+ SolarMutexClearableGuard aSolarGuard;
+ std::unique_lock aGuard( m_aMutex );
+
+ if ( m_bDisposed )
+ return;
+
+ // SfxObjectShellRef is good here since the model controls the lifetime of the object
+ SfxObjectShellRef xObjectShell = m_rFormModel.GetObjectShell();
+ if( !xObjectShell.is() )
+ return;
+
+ // the script to execute
+ std::shared_ptr< NewStyleUNOScript > pScript;
+
+ if ( _rEvent.ScriptType != "StarBasic" )
+ {
+ pScript = std::make_shared<NewStyleUNOScript>( *xObjectShell, _rEvent.ScriptCode );
+ }
+ else
+ {
+ OUString sScriptCode = _rEvent.ScriptCode;
+ OUString sMacroLocation;
+
+ // is there a location in the script name ("application" or "document")?
+ sal_Int32 nPrefixLen = sScriptCode.indexOf( ':' );
+ DBG_ASSERT( 0 <= nPrefixLen, "FormScriptingEnvironment::doFireScriptEvent: Basic script name in old format encountered!" );
+
+ if ( 0 <= nPrefixLen )
+ {
+ // and it has such a prefix
+ sMacroLocation = sScriptCode.copy( 0, nPrefixLen );
+ DBG_ASSERT( sMacroLocation == "document"
+ || sMacroLocation == "application",
+ "FormScriptingEnvironment::doFireScriptEvent: invalid (unknown) prefix!" );
+
+ // strip the prefix: the SfxObjectShell::CallScript knows nothing about such prefixes
+ sScriptCode = sScriptCode.copy( nPrefixLen + 1 );
+ }
+
+ if ( sMacroLocation.isEmpty() )
+ {
+ // legacy format: use the app-wide Basic, if it has a respective method, otherwise fall back to the doc's Basic
+ if ( SfxApplication::GetBasicManager()->HasMacro( sScriptCode ) )
+ sMacroLocation = "application";
+ else
+ sMacroLocation = "document";
+ }
+
+ OUString sScriptURI = "vnd.sun.star.script:" +
+ sScriptCode +
+ "?language=Basic&location=" +
+ sMacroLocation;
+
+ pScript = std::make_shared<NewStyleUNOScript>( *xObjectShell, sScriptURI );
+ }
+
+ assert(pScript && "FormScriptingEnvironment::doFireScriptEvent: no script to execute!");
+
+ aGuard.unlock();
+ aSolarGuard.clear();
+
+ Any aIgnoreResult;
+ pScript->invoke( _rEvent.Arguments, _pSynchronousResult ? *_pSynchronousResult : aIgnoreResult );
+ pScript.reset();
+
+ {
+ // object shells are not thread safe, so guard the destruction
+ SolarMutexGuard aSolarGuarsReset;
+ xObjectShell = nullptr;
+ }
+#endif
+ }
+
+
+ void FormScriptingEnvironment::dispose()
+ {
+ std::unique_lock aGuard( m_aMutex );
+ m_bDisposed = true;
+ m_pScriptListener->dispose();
+ m_pScriptListener.clear();
+ }
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmservs.cxx b/svx/source/form/fmservs.cxx
new file mode 100644
index 0000000000..a6c0daf5d1
--- /dev/null
+++ b/svx/source/form/fmservs.cxx
@@ -0,0 +1,74 @@
+/* -*- 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/container/XSet.hpp>
+#include <cppuhelper/factory.hxx>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <fmservs.hxx>
+
+using namespace com::sun::star;
+
+#define REGISTER_SERVICE(ImplName, ServiceName) \
+ sString = (ServiceName); \
+ xSingleFactory = ::cppu::createSingleFactory(xServiceFactory, \
+ OUString(), ImplName##_NewInstance_Impl, \
+ uno::Sequence< OUString>(&sString, 1)); \
+ if (xSingleFactory.is()) \
+ xSet->insert(uno::Any(xSingleFactory));
+
+namespace svxform
+{
+ void ImplSmartRegisterUnoServices()
+ {
+ uno::Reference< lang::XMultiServiceFactory > xServiceFactory = ::comphelper::getProcessServiceFactory();
+ uno::Reference< container::XSet > xSet(xServiceFactory, uno::UNO_QUERY);
+ if (!xSet.is())
+ return;
+
+ uno::Reference< lang::XSingleServiceFactory > xSingleFactory;
+
+ OUString sString;
+
+
+ // FormController
+ REGISTER_SERVICE( FormController, "com.sun.star.form.runtime.FormController" );
+ REGISTER_SERVICE( LegacyFormController, "com.sun.star.form.FormController" );
+
+
+ // FormController - register selfaware service
+ xSingleFactory = ::cppu::createSingleFactory( xServiceFactory,
+ OAddConditionDialog_GetImplementationName(),
+ OAddConditionDialog_Create,
+ OAddConditionDialog_GetSupportedServiceNames()
+ );
+ if ( xSingleFactory.is() )
+ xSet->insert( uno::Any( xSingleFactory ) );
+
+
+ // DBGridControl
+ REGISTER_SERVICE(FmXGridControl, FM_CONTROL_GRID); // compatibility
+ REGISTER_SERVICE(FmXGridControl, FM_CONTROL_GRIDCONTROL);
+ REGISTER_SERVICE(FmXGridControl, FM_SUN_CONTROL_GRIDCONTROL);
+ };
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmshell.cxx b/svx/source/form/fmshell.cxx
new file mode 100644
index 0000000000..423bbb7c25
--- /dev/null
+++ b/svx/source/form/fmshell.cxx
@@ -0,0 +1,1416 @@
+/* -*- 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 <fmvwimp.hxx>
+#include <svx/fmshell.hxx>
+#include <svx/fmtools.hxx>
+#include <fmprop.hxx>
+#include <fmundo.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/awt/XTabControllerModel.hpp>
+#include <sfx2/viewfrm.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <svl/whiter.hxx>
+#include <sfx2/app.hxx>
+#include <svl/intitem.hxx>
+#include <svl/stritem.hxx>
+#include <svl/visitem.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/dispatch.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/fmpage.hxx>
+#include <svx/svditer.hxx>
+
+#include <svx/svxids.hrc>
+
+#include <svx/svdobjkind.hxx>
+#include <svl/eitem.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/fmmodel.hxx>
+#include <fmshimp.hxx>
+#include <svx/svdpagv.hxx>
+#include <sfx2/objitem.hxx>
+#include <sfx2/viewsh.hxx>
+#include <fmexpl.hxx>
+#include <formcontrolling.hxx>
+#include <comphelper/types.hxx>
+#include <fmdocumentclassification.hxx>
+#include <formtoolbars.hxx>
+
+#include <svx/svxdlg.hxx>
+
+#include <svx/sdrobjectfilter.hxx>
+
+#define ShellClass_FmFormShell
+#include <svxslots.hxx>
+
+#include <memory>
+
+// is used for Invalidate -> maintain it as well
+// sort ascending !!!!!!
+sal_uInt16 const ControllerSlotMap[] = // slots of the controller
+{
+ SID_FM_CONFIG,
+ SID_FM_PUSHBUTTON,
+ SID_FM_RADIOBUTTON,
+ SID_FM_CHECKBOX,
+ SID_FM_FIXEDTEXT,
+ SID_FM_GROUPBOX,
+ SID_FM_EDIT,
+ SID_FM_LISTBOX,
+ SID_FM_COMBOBOX,
+ SID_FM_DBGRID,
+ SID_FM_IMAGEBUTTON,
+ SID_FM_FILECONTROL,
+ SID_FM_NAVIGATIONBAR,
+ SID_FM_CTL_PROPERTIES,
+ SID_FM_PROPERTIES,
+ SID_FM_TAB_DIALOG,
+ SID_FM_ADD_FIELD,
+ SID_FM_DESIGN_MODE,
+ SID_FM_SHOW_FMEXPLORER,
+ SID_FM_SHOW_PROPERTIES,
+ SID_FM_FMEXPLORER_CONTROL,
+ SID_FM_DATEFIELD,
+ SID_FM_TIMEFIELD,
+ SID_FM_NUMERICFIELD,
+ SID_FM_CURRENCYFIELD,
+ SID_FM_PATTERNFIELD,
+ SID_FM_OPEN_READONLY,
+ SID_FM_IMAGECONTROL,
+ SID_FM_USE_WIZARDS,
+ SID_FM_FORMATTEDFIELD,
+ SID_FM_FILTER_NAVIGATOR,
+ SID_FM_AUTOCONTROLFOCUS,
+ SID_FM_SCROLLBAR,
+ SID_FM_SPINBUTTON,
+ SID_FM_SHOW_DATANAVIGATOR,
+ SID_FM_DATANAVIGATOR_CONTROL,
+
+ 0
+};
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::sdbcx;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::form::runtime;
+using namespace ::svxform;
+
+FmDesignModeChangedHint::FmDesignModeChangedHint( bool bDesMode )
+ :m_bDesignMode( bDesMode )
+{
+}
+
+
+FmDesignModeChangedHint::~FmDesignModeChangedHint()
+{
+}
+
+SFX_IMPL_INTERFACE(FmFormShell, SfxShell)
+
+void FmFormShell::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_NAVIGATION, SfxVisibilityFlags::Standard|SfxVisibilityFlags::ReadonlyDoc,
+ ToolbarId::SvxTbx_Form_Navigation,
+ SfxShellFeature::FormShowDatabaseBar);
+
+ GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_NAVIGATION, SfxVisibilityFlags::Standard|SfxVisibilityFlags::ReadonlyDoc,
+ ToolbarId::SvxTbx_Form_Filter,
+ SfxShellFeature::FormShowFilterBar);
+
+ GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT, SfxVisibilityFlags::Standard | SfxVisibilityFlags::ReadonlyDoc,
+ ToolbarId::SvxTbx_Text_Control_Attributes,
+ SfxShellFeature::FormShowTextControlBar);
+
+ GetStaticInterface()->RegisterChildWindow(SID_FM_ADD_FIELD, false, SfxShellFeature::FormShowField);
+ GetStaticInterface()->RegisterChildWindow(SID_FM_SHOW_PROPERTIES, false, SfxShellFeature::FormShowProperties);
+ GetStaticInterface()->RegisterChildWindow(SID_FM_SHOW_FMEXPLORER, false, SfxShellFeature::FormShowExplorer);
+ GetStaticInterface()->RegisterChildWindow(SID_FM_FILTER_NAVIGATOR, false, SfxShellFeature::FormShowFilterNavigator);
+ GetStaticInterface()->RegisterChildWindow(SID_FM_SHOW_DATANAVIGATOR, false, SfxShellFeature::FormShowDataNavigator);
+
+ GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT, SfxVisibilityFlags::Standard,
+ ToolbarId::SvxTbx_Controls,
+ SfxShellFeature::FormTBControls);
+
+ GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT, SfxVisibilityFlags::Standard,
+ ToolbarId::SvxTbx_FormDesign,
+ SfxShellFeature::FormTBDesign);
+}
+
+
+FmFormShell::FmFormShell( SfxViewShell* _pParent, FmFormView* pView )
+ :SfxShell(_pParent)
+ ,m_pImpl(new FmXFormShell(*this, &_pParent->GetViewFrame()))
+ ,m_pFormView( pView )
+ ,m_pFormModel( nullptr )
+ ,m_nLastSlot( 0 )
+ ,m_bDesignMode( true )
+ ,m_bHasForms(false)
+{
+ SetPool( &SfxGetpApp()->GetPool() );
+ SetName( "Form" );
+
+ SetView(m_pFormView);
+}
+
+
+FmFormShell::~FmFormShell()
+{
+ if ( m_pFormView )
+ SetView( nullptr );
+
+ m_pImpl->dispose();
+}
+
+
+void FmFormShell::NotifyMarkListChanged(FmFormView* pWhichView)
+{
+ FmNavViewMarksChanged aChangeNotification(pWhichView);
+ Broadcast(aChangeNotification);
+}
+
+
+bool FmFormShell::PrepareClose(bool bUI)
+{
+ if (GetImpl()->didPrepareClose_Lock())
+ // we already made a PrepareClose for the current modifications of the current form
+ return true;
+
+ bool bResult = true;
+ // Save the data records, not in DesignMode and FilterMode
+ if (!m_bDesignMode && !GetImpl()->isInFilterMode_Lock() &&
+ m_pFormView && m_pFormView->GetActualOutDev() &&
+ m_pFormView->GetActualOutDev()->GetOutDevType() == OUTDEV_WINDOW)
+ {
+ SdrPageView* pCurPageView = m_pFormView->GetSdrPageView();
+
+ // sal_uInt16 nPos = pCurPageView ? pCurPageView->GetWinList().Find((OutputDevice*)m_pFormView->GetActualOutDev()) : SDRPAGEVIEWWIN_NOTFOUND;
+ SdrPageWindow* pWindow = pCurPageView ? pCurPageView->FindPageWindow(*const_cast<OutputDevice*>(m_pFormView->GetActualOutDev())) : nullptr;
+
+ if(pWindow)
+ {
+ // First, the current contents of the controls are stored.
+ // If everything has gone smoothly, the modified records are stored.
+ if (GetImpl()->getActiveController_Lock().is())
+ {
+ const svx::ControllerFeatures& rController = GetImpl()->getActiveControllerFeatures_Lock();
+ if ( rController->commitCurrentControl() )
+ {
+ const bool bModified = rController->isModifiedRow();
+
+ if ( bModified && bUI )
+ {
+ SfxViewShell* pShell = GetViewShell();
+ vcl::Window* pShellWnd = pShell ? pShell->GetWindow() : nullptr;
+ weld::Widget* pFrameWeld = pShellWnd ? pShellWnd->GetFrameWeld() : nullptr;
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(pFrameWeld, "svx/ui/savemodifieddialog.ui"));
+ std::unique_ptr<weld::MessageDialog> xQry(xBuilder->weld_message_dialog("SaveModifiedDialog"));
+ switch (xQry->run())
+ {
+ case RET_YES:
+ bResult = rController->commitCurrentRecord( );
+ [[fallthrough]];
+ case RET_NO:
+ GetImpl()->didPrepareClose_Lock(true);
+ break;
+
+ case RET_CANCEL:
+ return false;
+ }
+ }
+ }
+ }
+ }
+ }
+ return bResult;
+}
+
+
+void FmFormShell::impl_setDesignMode(bool bDesign)
+{
+ if (m_pFormView)
+ {
+ if (!bDesign)
+ m_nLastSlot = SID_FM_DESIGN_MODE;
+
+ GetImpl()->SetDesignMode_Lock(bDesign);
+ // my m_bDesignMode is also set by the Impl ...
+ }
+ else
+ {
+ m_bHasForms = false;
+ m_bDesignMode = bDesign;
+ UIFeatureChanged();
+ }
+
+ GetViewShell()->GetViewFrame().GetBindings().Invalidate(ControllerSlotMap);
+}
+
+bool FmFormShell::HasUIFeature(SfxShellFeature nFeature) const
+{
+ assert((nFeature & ~SfxShellFeature::FormMask) == SfxShellFeature::NONE);
+ bool bResult = false;
+ if (nFeature & SfxShellFeature::FormShowDatabaseBar)
+ {
+ // only if forms are also available
+ bResult = !m_bDesignMode && GetImpl()->hasDatabaseBar_Lock() && !GetImpl()->isInFilterMode_Lock();
+ }
+ else if (nFeature & SfxShellFeature::FormShowFilterBar)
+ {
+ // only if forms are also available
+ bResult = !m_bDesignMode && GetImpl()->hasDatabaseBar_Lock() && GetImpl()->isInFilterMode_Lock();
+ }
+ else if (nFeature & SfxShellFeature::FormShowFilterNavigator)
+ {
+ bResult = !m_bDesignMode && GetImpl()->hasDatabaseBar_Lock() && GetImpl()->isInFilterMode_Lock();
+ }
+ else if (nFeature & SfxShellFeature::FormShowField)
+ {
+ bResult = m_bDesignMode && m_pFormView && m_bHasForms;
+ }
+ else if (nFeature & SfxShellFeature::FormShowProperties)
+ {
+ bResult = m_bDesignMode && m_pFormView && m_bHasForms;
+ }
+ else if (nFeature & SfxShellFeature::FormShowExplorer)
+ {
+ bResult = m_bDesignMode; // OJ #101593# && m_pFormView && m_bHasForms;
+ }
+ else if (nFeature & SfxShellFeature::FormShowTextControlBar)
+ {
+ bResult = !GetImpl()->IsReadonlyDoc_Lock() && m_pImpl->IsActiveControl_Lock(true);
+ }
+ else if (nFeature & SfxShellFeature::FormShowDataNavigator)
+ {
+ bResult = GetImpl()->isEnhancedForm_Lock();
+ }
+ else if ( (nFeature & SfxShellFeature::FormTBControls)
+ || (nFeature & SfxShellFeature::FormTBDesign)
+ )
+ {
+ bResult = true;
+ }
+
+ return bResult;
+}
+
+
+void FmFormShell::Execute(SfxRequest &rReq)
+{
+ sal_uInt16 nSlot = rReq.GetSlot();
+
+
+ // set MasterSlot
+ switch( nSlot )
+ {
+ case SID_FM_PUSHBUTTON:
+ case SID_FM_RADIOBUTTON:
+ case SID_FM_CHECKBOX:
+ case SID_FM_FIXEDTEXT:
+ case SID_FM_GROUPBOX:
+ case SID_FM_LISTBOX:
+ case SID_FM_COMBOBOX:
+ case SID_FM_NAVIGATIONBAR:
+ case SID_FM_EDIT:
+ case SID_FM_DBGRID:
+ case SID_FM_IMAGEBUTTON:
+ case SID_FM_IMAGECONTROL:
+ case SID_FM_FILECONTROL:
+ case SID_FM_DATEFIELD:
+ case SID_FM_TIMEFIELD:
+ case SID_FM_NUMERICFIELD:
+ case SID_FM_CURRENCYFIELD:
+ case SID_FM_PATTERNFIELD:
+ case SID_FM_FORMATTEDFIELD:
+ case SID_FM_SCROLLBAR:
+ case SID_FM_SPINBUTTON:
+ m_nLastSlot = nSlot;
+ break;
+ }
+
+
+ // set the Identifier and Inventor of the Uno control
+ SdrObjKind nIdentifier = SdrObjKind::NONE;
+ switch( nSlot )
+ {
+ case SID_FM_CHECKBOX:
+ nIdentifier = SdrObjKind::FormCheckbox;
+ break;
+ case SID_FM_PUSHBUTTON:
+ nIdentifier = SdrObjKind::FormButton;
+ break;
+ case SID_FM_FIXEDTEXT:
+ nIdentifier = SdrObjKind::FormFixedText;
+ break;
+ case SID_FM_LISTBOX:
+ nIdentifier = SdrObjKind::FormListbox;
+ break;
+ case SID_FM_EDIT:
+ nIdentifier = SdrObjKind::FormEdit;
+ break;
+ case SID_FM_RADIOBUTTON:
+ nIdentifier = SdrObjKind::FormRadioButton;
+ break;
+ case SID_FM_GROUPBOX:
+ nIdentifier = SdrObjKind::FormGroupBox;
+ break;
+ case SID_FM_COMBOBOX:
+ nIdentifier = SdrObjKind::FormCombobox;
+ break;
+ case SID_FM_NAVIGATIONBAR:
+ nIdentifier = SdrObjKind::FormNavigationBar;
+ break;
+ case SID_FM_DBGRID:
+ nIdentifier = SdrObjKind::FormGrid;
+ break;
+ case SID_FM_IMAGEBUTTON:
+ nIdentifier = SdrObjKind::FormImageButton;
+ break;
+ case SID_FM_IMAGECONTROL:
+ nIdentifier = SdrObjKind::FormImageControl;
+ break;
+ case SID_FM_FILECONTROL:
+ nIdentifier = SdrObjKind::FormFileControl;
+ break;
+ case SID_FM_DATEFIELD:
+ nIdentifier = SdrObjKind::FormDateField;
+ break;
+ case SID_FM_TIMEFIELD:
+ nIdentifier = SdrObjKind::FormTimeField;
+ break;
+ case SID_FM_NUMERICFIELD:
+ nIdentifier = SdrObjKind::FormNumericField;
+ break;
+ case SID_FM_CURRENCYFIELD:
+ nIdentifier = SdrObjKind::FormCurrencyField;
+ break;
+ case SID_FM_PATTERNFIELD:
+ nIdentifier = SdrObjKind::FormPatternField;
+ break;
+ case SID_FM_FORMATTEDFIELD:
+ nIdentifier = SdrObjKind::FormFormattedField;
+ break;
+ case SID_FM_SCROLLBAR:
+ nIdentifier = SdrObjKind::FormScrollbar;
+ break;
+ case SID_FM_SPINBUTTON:
+ nIdentifier = SdrObjKind::FormSpinButton;
+ break;
+ }
+
+ switch ( nSlot )
+ {
+ case SID_FM_CHECKBOX:
+ case SID_FM_PUSHBUTTON:
+ case SID_FM_FIXEDTEXT:
+ case SID_FM_LISTBOX:
+ case SID_FM_EDIT:
+ case SID_FM_RADIOBUTTON:
+ case SID_FM_COMBOBOX:
+ case SID_FM_NAVIGATIONBAR:
+ case SID_FM_GROUPBOX:
+ case SID_FM_DBGRID:
+ case SID_FM_IMAGEBUTTON:
+ case SID_FM_IMAGECONTROL:
+ case SID_FM_FILECONTROL:
+ case SID_FM_DATEFIELD:
+ case SID_FM_TIMEFIELD:
+ case SID_FM_NUMERICFIELD:
+ case SID_FM_CURRENCYFIELD:
+ case SID_FM_PATTERNFIELD:
+ case SID_FM_FORMATTEDFIELD:
+ case SID_FM_SCROLLBAR:
+ case SID_FM_SPINBUTTON:
+ {
+ const SfxBoolItem* pGrabFocusItem = rReq.GetArg<SfxBoolItem>(SID_FM_TOGGLECONTROLFOCUS);
+ if ( pGrabFocusItem && pGrabFocusItem->GetValue() )
+ { // see below
+ SfxViewShell* pShell = GetViewShell();
+ vcl::Window* pShellWnd = pShell ? pShell->GetWindow() : nullptr;
+ if ( pShellWnd )
+ pShellWnd->GrabFocus();
+ break;
+ }
+
+ SfxUInt16Item aIdentifierItem( SID_FM_CONTROL_IDENTIFIER, static_cast<sal_uInt16>(nIdentifier) );
+ SfxUInt32Item aInventorItem( SID_FM_CONTROL_INVENTOR, sal_uInt32(SdrInventor::FmForm) );
+ const SfxPoolItem* pArgs[] =
+ {
+ &aIdentifierItem, &aInventorItem, nullptr
+ };
+ const SfxPoolItem* pInternalArgs[] =
+ {
+ nullptr
+ };
+
+ GetViewShell()->GetViewFrame().GetDispatcher()->Execute( SID_FM_CREATE_CONTROL, SfxCallMode::ASYNCHRON,
+ pArgs, rReq.GetModifier(), pInternalArgs );
+
+ if ( rReq.GetModifier() & KEY_MOD1 )
+ {
+ // #99013# if selected with control key, return focus to current view
+ // do this asynchron, so that the creation can be finished first
+ // reusing the SID_FM_TOGGLECONTROLFOCUS is somewhat hacky... which it wouldn't if it would have another
+ // name, so I do not really have a big problem with this...
+ SfxBoolItem aGrabFocusIndicatorItem( SID_FM_TOGGLECONTROLFOCUS, true );
+ GetViewShell()->GetViewFrame().GetDispatcher()->ExecuteList(
+ nSlot, SfxCallMode::ASYNCHRON,
+ { &aGrabFocusIndicatorItem });
+ }
+
+ rReq.Done();
+ } break;
+ }
+
+ // individual actions
+ switch( nSlot )
+ {
+ case SID_FM_FORM_DESIGN_TOOLS:
+ {
+ FormToolboxes aToolboxAccess(GetImpl()->getHostFrame_Lock());
+ aToolboxAccess.toggleToolbox( nSlot );
+ rReq.Done();
+ }
+ break;
+
+ case SID_FM_TOGGLECONTROLFOCUS:
+ {
+ FmFormView* pFormView = GetFormView();
+ if ( !pFormView )
+ break;
+
+ // if we execute this ourself, then either the application does not implement an own handling for this,
+ // of we're on the top of the dispatcher stack, which means a control has the focus.
+ // In the latter case, we put the focus to the document window, otherwise, we focus the first control
+ const bool bHasControlFocus = GetImpl()->HasControlFocus_Lock();
+ if ( bHasControlFocus )
+ {
+ if (m_pFormView)
+ {
+ const OutputDevice* pDevice = m_pFormView->GetActualOutDev();
+ vcl::Window* pWindow = pDevice->GetOwnerWindow();
+ if ( pWindow )
+ pWindow->GrabFocus();
+ }
+ }
+ else
+ {
+ pFormView->GrabFirstControlFocus( );
+ }
+ }
+ break;
+
+ case SID_FM_VIEW_AS_GRID:
+ GetImpl()->CreateExternalView_Lock();
+ break;
+ case SID_FM_CONVERTTO_EDIT :
+ case SID_FM_CONVERTTO_BUTTON :
+ case SID_FM_CONVERTTO_FIXEDTEXT :
+ case SID_FM_CONVERTTO_LISTBOX :
+ case SID_FM_CONVERTTO_CHECKBOX :
+ case SID_FM_CONVERTTO_RADIOBUTTON :
+ case SID_FM_CONVERTTO_GROUPBOX :
+ case SID_FM_CONVERTTO_COMBOBOX :
+ case SID_FM_CONVERTTO_IMAGEBUTTON :
+ case SID_FM_CONVERTTO_FILECONTROL :
+ case SID_FM_CONVERTTO_DATE :
+ case SID_FM_CONVERTTO_TIME :
+ case SID_FM_CONVERTTO_NUMERIC :
+ case SID_FM_CONVERTTO_CURRENCY :
+ case SID_FM_CONVERTTO_PATTERN :
+ case SID_FM_CONVERTTO_IMAGECONTROL :
+ case SID_FM_CONVERTTO_FORMATTED :
+ case SID_FM_CONVERTTO_SCROLLBAR :
+ case SID_FM_CONVERTTO_SPINBUTTON :
+ case SID_FM_CONVERTTO_NAVIGATIONBAR :
+ GetImpl()->executeControlConversionSlot_Lock(FmXFormShell::SlotToIdent(nSlot));
+ // after the conversion, re-determine the selection, since the
+ // selected object has changed
+ GetImpl()->SetSelection_Lock(GetFormView()->GetMarkedObjectList());
+ break;
+ case SID_FM_LEAVE_CREATE:
+ m_nLastSlot = 0;
+ rReq.Done();
+ break;
+ case SID_FM_SHOW_PROPERTY_BROWSER:
+ {
+ const SfxBoolItem* pShowItem = rReq.GetArg<SfxBoolItem>(SID_FM_SHOW_PROPERTIES);
+ bool bShow = true;
+ if ( pShowItem )
+ bShow = pShowItem->GetValue();
+ GetImpl()->ShowSelectionProperties_Lock(bShow);
+
+ rReq.Done();
+ } break;
+
+ case SID_FM_PROPERTIES:
+ {
+ // display the PropertyBrowser
+ const SfxBoolItem* pShowItem = rReq.GetArg<SfxBoolItem>(nSlot);
+ bool bShow = pShowItem == nullptr || pShowItem->GetValue();
+
+ InterfaceBag aOnlyTheForm;
+ aOnlyTheForm.insert(Reference<XInterface>(GetImpl()->getCurrentForm_Lock(), UNO_QUERY));
+ GetImpl()->setCurrentSelection_Lock(std::move(aOnlyTheForm));
+
+ GetImpl()->ShowSelectionProperties_Lock(bShow);
+
+ rReq.Done();
+ } break;
+
+ case SID_FM_CTL_PROPERTIES:
+ {
+ const SfxBoolItem* pShowItem = rReq.GetArg<SfxBoolItem>(nSlot);
+ bool bShow = pShowItem == nullptr || pShowItem->GetValue();
+
+ OSL_ENSURE( GetImpl()->onlyControlsAreMarked_Lock(), "FmFormShell::Execute: ControlProperties should be disabled!" );
+ if ( bShow )
+ GetImpl()->selectLastMarkedControls_Lock();
+ GetImpl()->ShowSelectionProperties_Lock(bShow);
+
+ rReq.Done();
+ } break;
+ case SID_FM_SHOW_PROPERTIES:
+ case SID_FM_ADD_FIELD:
+ case SID_FM_FILTER_NAVIGATOR:
+ case SID_FM_SHOW_DATANAVIGATOR :
+ {
+ GetViewShell()->GetViewFrame().ToggleChildWindow(nSlot);
+ rReq.Done();
+ } break;
+ case SID_FM_SHOW_FMEXPLORER:
+ {
+ if (!m_pFormView) // force setting the view
+ GetViewShell()->GetViewFrame().GetDispatcher()->Execute(SID_CREATE_SW_DRAWVIEW);
+
+ GetViewShell()->GetViewFrame().ChildWindowExecute(rReq);
+ rReq.Done();
+ }
+ break;
+
+ case SID_FM_TAB_DIALOG:
+ {
+ GetImpl()->ExecuteTabOrderDialog_Lock(
+ Reference<XTabControllerModel>(GetImpl()->getCurrentForm_Lock(), UNO_QUERY));
+ rReq.Done();
+ }
+ break;
+
+ case SID_FM_DESIGN_MODE:
+ {
+ const SfxBoolItem* pDesignItem = rReq.GetArg<SfxBoolItem>(nSlot);
+ bool bDesignMode = pDesignItem ? pDesignItem->GetValue() : !m_bDesignMode;
+ SetDesignMode( bDesignMode );
+ if ( m_bDesignMode == bDesignMode )
+ rReq.Done();
+
+ m_nLastSlot = SID_FM_DESIGN_MODE;
+ }
+ break;
+
+ case SID_FM_AUTOCONTROLFOCUS:
+ {
+ FmFormModel* pModel = GetFormModel();
+ DBG_ASSERT(pModel, "FmFormShell::Execute : invalid call !");
+ // should have been disabled in GetState if we don't have a FormModel
+ pModel->SetAutoControlFocus( !pModel->GetAutoControlFocus() );
+ GetViewShell()->GetViewFrame().GetBindings().Invalidate(SID_FM_AUTOCONTROLFOCUS);
+ }
+ break;
+ case SID_FM_OPEN_READONLY:
+ {
+ FmFormModel* pModel = GetFormModel();
+ DBG_ASSERT(pModel, "FmFormShell::Execute : invalid call !");
+ // should have been disabled in GetState if we don't have a FormModel
+ pModel->SetOpenInDesignMode( !pModel->GetOpenInDesignMode() );
+ GetViewShell()->GetViewFrame().GetBindings().Invalidate(SID_FM_OPEN_READONLY);
+ }
+ break;
+ case SID_FM_USE_WIZARDS:
+ {
+ GetImpl()->SetWizardUsing_Lock(!GetImpl()->GetWizardUsing_Lock());
+ GetViewShell()->GetViewFrame().GetBindings().Invalidate(SID_FM_USE_WIZARDS);
+ }
+ break;
+ case SID_FM_SEARCH:
+ {
+ const svx::ControllerFeatures& rController = GetImpl()->getActiveControllerFeatures_Lock();
+ if ( rController->commitCurrentControl() && rController->commitCurrentRecord() )
+ GetImpl()->ExecuteSearch_Lock();
+ rReq.Done();
+ }
+ break;
+
+ case SID_FM_RECORD_FIRST:
+ case SID_FM_RECORD_PREV:
+ case SID_FM_RECORD_NEXT:
+ case SID_FM_RECORD_LAST:
+ case SID_FM_RECORD_NEW:
+ case SID_FM_REFRESH:
+ case SID_FM_REFRESH_FORM_CONTROL:
+ case SID_FM_RECORD_DELETE:
+ case SID_FM_RECORD_UNDO:
+ case SID_FM_RECORD_SAVE:
+ case SID_FM_REMOVE_FILTER_SORT:
+ case SID_FM_SORTDOWN:
+ case SID_FM_SORTUP:
+ case SID_FM_AUTOFILTER:
+ case SID_FM_ORDERCRIT:
+ case SID_FM_FORM_FILTERED:
+ {
+ GetImpl()->ExecuteFormSlot_Lock(nSlot);
+ rReq.Done();
+ }
+ break;
+
+ case SID_FM_RECORD_ABSOLUTE:
+ {
+ const svx::ControllerFeatures& rController = GetImpl()->getNavControllerFeatures_Lock();
+ sal_Int32 nRecord = -1;
+
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ if ( pArgs )
+ {
+ const SfxPoolItem* pItem;
+ if ( ( pArgs->GetItemState( FN_PARAM_1, true, &pItem ) ) == SfxItemState::SET )
+ {
+ const SfxInt32Item* pTypedItem = dynamic_cast<const SfxInt32Item* >( pItem );
+ if ( pTypedItem )
+ nRecord = std::max( pTypedItem->GetValue(), sal_Int32(0) );
+ }
+ }
+ else
+ {
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractFmInputRecordNoDialog> dlg(pFact->CreateFmInputRecordNoDialog(rReq.GetFrameWeld()));
+ dlg->SetValue( rController->getCursor()->getRow() );
+ if ( dlg->Execute() == RET_OK )
+ nRecord = dlg->GetValue();
+
+ rReq.AppendItem( SfxInt32Item( TypedWhichId<SfxInt32Item>(FN_PARAM_1), nRecord ) );
+ }
+
+ if ( nRecord != -1 )
+ rController->execute( nSlot, "Position", Any( nRecord ) );
+
+ rReq.Done();
+ } break;
+ case SID_FM_FILTER_EXECUTE:
+ case SID_FM_FILTER_EXIT:
+ {
+ bool bCancelled = ( SID_FM_FILTER_EXIT == nSlot );
+ bool bReopenNavigator = false;
+
+ if ( !bCancelled )
+ {
+ // if the filter navigator is still open, we need to close it, so it can possibly
+ // commit it's most recent changes
+ if (GetViewShell())
+ if ( GetViewShell()->GetViewFrame().HasChildWindow( SID_FM_FILTER_NAVIGATOR ) )
+ {
+ GetViewShell()->GetViewFrame().ToggleChildWindow( SID_FM_FILTER_NAVIGATOR );
+ bReopenNavigator = true;
+ }
+
+ Reference<runtime::XFormController> const xController(GetImpl()->getActiveController_Lock());
+
+ if ( GetViewShell()->GetViewFrame().HasChildWindow( SID_FM_FILTER_NAVIGATOR )
+ // closing the window was denied, for instance because of an invalid criterion
+
+ || ( xController.is()
+ && !GetImpl()->getActiveControllerFeatures_Lock()->commitCurrentControl()
+ )
+ // committing the controller was denied
+ )
+ {
+ rReq.Done();
+ break;
+ }
+ }
+
+ GetImpl()->stopFiltering_Lock(!bCancelled);
+ rReq.Done();
+
+ if ( bReopenNavigator )
+ // we closed the navigator only to implicitly commit it (as we do not have another
+ // direct wire to it), but to the user, it should look as it was always open
+ GetViewShell()->GetViewFrame().ToggleChildWindow( SID_FM_FILTER_NAVIGATOR );
+ }
+ break;
+
+ case SID_FM_FILTER_START:
+ {
+ GetImpl()->startFiltering_Lock();
+ rReq.Done();
+
+ // initially open the filter navigator, the whole form based filter is pretty useless without it
+ SfxBoolItem aIdentifierItem( SID_FM_FILTER_NAVIGATOR, true );
+ GetViewShell()->GetViewFrame().GetDispatcher()->ExecuteList(
+ SID_FM_FILTER_NAVIGATOR, SfxCallMode::ASYNCHRON,
+ { &aIdentifierItem });
+ } break;
+ }
+}
+
+
+void FmFormShell::GetState(SfxItemSet &rSet)
+{
+ SfxWhichIter aIter( rSet );
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while ( nWhich )
+ {
+ switch( nWhich )
+ {
+ case SID_FM_FORM_DESIGN_TOOLS:
+ {
+ FormToolboxes aToolboxAccess(GetImpl()->getHostFrame_Lock());
+ rSet.Put( SfxBoolItem( nWhich, aToolboxAccess.isToolboxVisible( nWhich ) ) );
+ }
+ break;
+
+ case SID_FM_FILTER_EXECUTE:
+ case SID_FM_FILTER_EXIT:
+ if (!GetImpl()->isInFilterMode_Lock())
+ rSet.DisableItem( nWhich );
+ break;
+
+ case SID_FM_USE_WIZARDS:
+ if ( !SvtModuleOptions().IsModuleInstalled( SvtModuleOptions::EModule::DATABASE ) )
+ rSet.Put( SfxVisibilityItem( nWhich, false ) );
+ else if (!GetFormModel())
+ rSet.DisableItem( nWhich );
+ else
+ rSet.Put(SfxBoolItem(nWhich, GetImpl()->GetWizardUsing_Lock()));
+ break;
+ case SID_FM_AUTOCONTROLFOCUS:
+ if (!GetFormModel())
+ rSet.DisableItem( nWhich );
+ else
+ rSet.Put( SfxBoolItem(nWhich, GetFormModel()->GetAutoControlFocus() ) );
+ break;
+ case SID_FM_OPEN_READONLY:
+ if (!GetFormModel())
+ rSet.DisableItem( nWhich );
+ else
+ rSet.Put( SfxBoolItem(nWhich, GetFormModel()->GetOpenInDesignMode() ) );
+ break;
+
+ case SID_FM_NAVIGATIONBAR:
+ case SID_FM_DBGRID:
+ if ( !SvtModuleOptions().IsModuleInstalled( SvtModuleOptions::EModule::DATABASE ) )
+ {
+ rSet.Put( SfxVisibilityItem( nWhich, false ) );
+ break;
+ }
+ [[fallthrough]];
+
+ case SID_FM_SCROLLBAR:
+ case SID_FM_IMAGECONTROL:
+ case SID_FM_FILECONTROL:
+ case SID_FM_CURRENCYFIELD:
+ case SID_FM_PATTERNFIELD:
+ case SID_FM_IMAGEBUTTON:
+ case SID_FM_RADIOBUTTON:
+ case SID_FM_COMBOBOX:
+ case SID_FM_GROUPBOX:
+ case SID_FM_CHECKBOX:
+ case SID_FM_PUSHBUTTON:
+ case SID_FM_FIXEDTEXT:
+ case SID_FM_LISTBOX:
+ case SID_FM_EDIT:
+ case SID_FM_DATEFIELD:
+ case SID_FM_TIMEFIELD:
+ case SID_FM_NUMERICFIELD:
+ case SID_FM_FORMATTEDFIELD:
+ case SID_FM_SPINBUTTON:
+ if (!m_bDesignMode)
+ rSet.DisableItem( nWhich );
+ else
+ {
+ bool bLayerLocked = false;
+ if (m_pFormView)
+ {
+ // If the css::drawing::Layer is locked, the slots must be disabled. #36897
+ SdrPageView* pPV = m_pFormView->GetSdrPageView();
+ if (pPV != nullptr)
+ bLayerLocked = pPV->IsLayerLocked(m_pFormView->GetActiveLayer());
+ }
+ if (bLayerLocked)
+ rSet.DisableItem( nWhich );
+ else
+ rSet.Put( SfxBoolItem(nWhich, (nWhich==m_nLastSlot)) );
+ }
+ break;
+ case SID_FM_FILTER_NAVIGATOR_CONTROL:
+ {
+ if (GetImpl()->isInFilterMode_Lock())
+ rSet.Put(SfxObjectItem(nWhich, this));
+ else
+ rSet.Put(SfxObjectItem(nWhich));
+ } break;
+ case SID_FM_FIELDS_CONTROL:
+ case SID_FM_PROPERTY_CONTROL:
+ {
+ if (!m_bDesignMode || !m_pFormView || !m_bHasForms)
+ rSet.Put(SfxObjectItem(nWhich));
+ else
+ rSet.Put(SfxObjectItem(nWhich, this));
+
+ } break;
+ case SID_FM_FMEXPLORER_CONTROL:
+ case SID_FM_DATANAVIGATOR_CONTROL :
+ {
+ if (!m_bDesignMode || !m_pFormView)
+ rSet.Put(SfxObjectItem(nWhich));
+ else
+ rSet.Put(SfxObjectItem(nWhich, this));
+
+ } break;
+ case SID_FM_ADD_FIELD:
+ case SID_FM_SHOW_FMEXPLORER:
+ case SID_FM_SHOW_PROPERTIES:
+ case SID_FM_FILTER_NAVIGATOR:
+ case SID_FM_SHOW_DATANAVIGATOR:
+ {
+ if ( GetViewShell()->GetViewFrame().KnowsChildWindow(nWhich) )
+ rSet.Put( SfxBoolItem( nWhich, GetViewShell()->GetViewFrame().HasChildWindow(nWhich)) );
+ else
+ rSet.DisableItem(nWhich);
+ } break;
+
+ case SID_FM_SHOW_PROPERTY_BROWSER:
+ {
+ rSet.Put(SfxBoolItem(nWhich, GetImpl()->IsPropBrwOpen_Lock()));
+ }
+ break;
+
+ case SID_FM_CTL_PROPERTIES:
+ {
+ // potentially, give the Impl the opportunity to update its
+ // current objects which are aligned with the current MarkList
+ if (GetImpl()->IsSelectionUpdatePending_Lock())
+ GetImpl()->ForceUpdateSelection_Lock();
+
+ if (!m_pFormView || !m_bDesignMode || !GetImpl()->onlyControlsAreMarked_Lock())
+ rSet.DisableItem( nWhich );
+ else
+ {
+ bool const bChecked = GetImpl()->IsPropBrwOpen_Lock() && !GetImpl()->isSolelySelected_Lock(GetImpl()->getCurrentForm_Lock());
+ // if the property browser is open, and only controls are marked, and the current selection
+ // does not consist of only the current form, then the current selection is the (composition of)
+ // the currently marked controls
+ rSet.Put( SfxBoolItem( nWhich, bChecked ) );
+ }
+ } break;
+
+ case SID_FM_PROPERTIES:
+ {
+ // potentially, give the Impl the opportunity to update its
+ // current objects which are aligned with the current MarkList
+ if (GetImpl()->IsSelectionUpdatePending_Lock())
+ GetImpl()->ForceUpdateSelection_Lock();
+
+ if (!m_pFormView || !m_bDesignMode || !GetImpl()->getCurrentForm_Lock().is())
+ rSet.DisableItem( nWhich );
+ else
+ {
+ bool const bChecked = GetImpl()->IsPropBrwOpen_Lock() && GetImpl()->isSolelySelected_Lock(GetImpl()->getCurrentForm_Lock());
+ rSet.Put(SfxBoolItem(nWhich, bChecked));
+ }
+ } break;
+ case SID_FM_TAB_DIALOG:
+ // potentially, give the Impl the opportunity to update its
+ // current objects which are aligned with the current MarkList
+ if (GetImpl()->IsSelectionUpdatePending_Lock())
+ GetImpl()->ForceUpdateSelection_Lock();
+
+ if (!m_pFormView || !m_bDesignMode || !GetImpl()->getCurrentForm_Lock().is() )
+ rSet.DisableItem( nWhich );
+ break;
+ case SID_FM_DESIGN_MODE:
+ if (!m_pFormView || GetImpl()->IsReadonlyDoc_Lock())
+ rSet.DisableItem( nWhich );
+ else
+ rSet.Put( SfxBoolItem(nWhich, m_bDesignMode) );
+ break;
+ case SID_FM_SEARCH:
+ case SID_FM_RECORD_FIRST:
+ case SID_FM_RECORD_NEXT:
+ case SID_FM_RECORD_PREV:
+ case SID_FM_RECORD_LAST:
+ case SID_FM_RECORD_NEW:
+ case SID_FM_RECORD_DELETE:
+ case SID_FM_RECORD_ABSOLUTE:
+ case SID_FM_RECORD_TOTAL:
+ case SID_FM_RECORD_SAVE:
+ case SID_FM_RECORD_UNDO:
+ case SID_FM_FORM_FILTERED:
+ case SID_FM_REMOVE_FILTER_SORT:
+ case SID_FM_SORTUP:
+ case SID_FM_SORTDOWN:
+ case SID_FM_ORDERCRIT:
+ case SID_FM_FILTER_START:
+ case SID_FM_AUTOFILTER:
+ case SID_FM_REFRESH:
+ case SID_FM_REFRESH_FORM_CONTROL:
+ case SID_FM_VIEW_AS_GRID:
+ GetFormState(rSet,nWhich);
+ break;
+
+ case SID_FM_CHANGECONTROLTYPE:
+ {
+ if ( !m_pFormView || !m_bDesignMode )
+ rSet.DisableItem( nWhich );
+ else
+ {
+ if (!GetImpl()->canConvertCurrentSelectionToControl_Lock(u"ConvertToFixed"))
+ // if it cannot be converted to a fixed text, it is no single control
+ rSet.DisableItem( nWhich );
+ }
+ } break;
+
+ case SID_FM_CONVERTTO_FILECONTROL :
+ case SID_FM_CONVERTTO_CURRENCY :
+ case SID_FM_CONVERTTO_PATTERN :
+ case SID_FM_CONVERTTO_IMAGECONTROL :
+ case SID_FM_CONVERTTO_SCROLLBAR :
+ case SID_FM_CONVERTTO_NAVIGATIONBAR :
+ case SID_FM_CONVERTTO_IMAGEBUTTON :
+ case SID_FM_CONVERTTO_EDIT :
+ case SID_FM_CONVERTTO_BUTTON :
+ case SID_FM_CONVERTTO_FIXEDTEXT :
+ case SID_FM_CONVERTTO_LISTBOX :
+ case SID_FM_CONVERTTO_CHECKBOX :
+ case SID_FM_CONVERTTO_RADIOBUTTON :
+ case SID_FM_CONVERTTO_GROUPBOX :
+ case SID_FM_CONVERTTO_COMBOBOX :
+ case SID_FM_CONVERTTO_DATE :
+ case SID_FM_CONVERTTO_TIME :
+ case SID_FM_CONVERTTO_NUMERIC :
+ case SID_FM_CONVERTTO_FORMATTED :
+ case SID_FM_CONVERTTO_SPINBUTTON :
+ {
+ if (!m_pFormView || !m_bDesignMode || !GetImpl()->canConvertCurrentSelectionToControl_Lock(FmXFormShell::SlotToIdent(nWhich)))
+ rSet.DisableItem( nWhich );
+ else
+ {
+ rSet.Put( SfxBoolItem( nWhich, false ) );
+ // just to have a defined state (available and not checked)
+ }
+ }
+ break;
+ }
+ nWhich = aIter.NextWhich();
+ }
+}
+
+
+void FmFormShell::GetFormState(SfxItemSet &rSet, sal_uInt16 nWhich)
+{
+ if ( !GetImpl()->getNavController_Lock().is()
+ || !isRowSetAlive(GetImpl()->getNavController_Lock()->getModel())
+ || !m_pFormView
+ || m_bDesignMode
+ || !GetImpl()->getActiveForm_Lock().is()
+ || GetImpl()->isInFilterMode_Lock()
+ )
+ rSet.DisableItem(nWhich);
+ else
+ {
+ bool bEnable = false;
+ try
+ {
+ switch (nWhich)
+ {
+ case SID_FM_VIEW_AS_GRID:
+ if (GetImpl()->getHostFrame_Lock().is() && GetImpl()->getNavController_Lock().is())
+ {
+ bEnable = true;
+ bool bDisplayingCurrent =
+ GetImpl()->getInternalForm_Lock(
+ Reference<XForm>(GetImpl()->getNavController_Lock()->getModel(), UNO_QUERY)
+ ) == GetImpl()->getExternallyDisplayedForm_Lock();
+ rSet.Put(SfxBoolItem(nWhich, bDisplayingCurrent));
+ }
+ break;
+
+ case SID_FM_SEARCH:
+ {
+ Reference<css::beans::XPropertySet> const xNavSet(GetImpl()->getActiveForm_Lock(), UNO_QUERY);
+ sal_Int32 nCount = ::comphelper::getINT32(xNavSet->getPropertyValue(FM_PROP_ROWCOUNT));
+ bEnable = nCount != 0;
+ } break;
+ case SID_FM_RECORD_ABSOLUTE:
+ case SID_FM_RECORD_TOTAL:
+ {
+ FeatureState aState;
+ GetImpl()->getNavControllerFeatures_Lock()->getState( nWhich, aState );
+ if ( SID_FM_RECORD_ABSOLUTE == nWhich )
+ {
+ sal_Int32 nPosition = 0;
+ aState.State >>= nPosition;
+ rSet.Put( SfxInt32Item( SID_FM_RECORD_ABSOLUTE, nPosition ) );
+ }
+ else if ( SID_FM_RECORD_TOTAL == nWhich )
+ {
+ OUString sTotalCount;
+ aState.State >>= sTotalCount;
+ rSet.Put( SfxStringItem( nWhich, sTotalCount ) );
+ }
+ bEnable = aState.Enabled;
+ }
+ break;
+
+ // first, prev, next, last, and absolute affect the nav controller, not the
+ // active controller
+ case SID_FM_RECORD_FIRST:
+ case SID_FM_RECORD_PREV:
+ case SID_FM_RECORD_NEXT:
+ case SID_FM_RECORD_LAST:
+ case SID_FM_RECORD_NEW:
+ case SID_FM_RECORD_SAVE:
+ case SID_FM_RECORD_UNDO:
+ case SID_FM_RECORD_DELETE:
+ case SID_FM_REFRESH:
+ case SID_FM_REFRESH_FORM_CONTROL:
+ case SID_FM_REMOVE_FILTER_SORT:
+ case SID_FM_SORTUP:
+ case SID_FM_SORTDOWN:
+ case SID_FM_AUTOFILTER:
+ case SID_FM_ORDERCRIT:
+ bEnable = GetImpl()->IsFormSlotEnabled( nWhich, nullptr );
+ break;
+
+ case SID_FM_FORM_FILTERED:
+ {
+ FeatureState aState;
+ bEnable = GetImpl()->IsFormSlotEnabled( nWhich, &aState );
+
+ rSet.Put( SfxBoolItem( nWhich, ::comphelper::getBOOL( aState.State ) ) );
+ }
+ break;
+
+ case SID_FM_FILTER_START:
+ bEnable = GetImpl()->getActiveControllerFeatures_Lock()->canDoFormFilter();
+ break;
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.form", "caught an exception while determining the state!");
+ }
+ if (!bEnable)
+ rSet.DisableItem(nWhich);
+ }
+}
+
+
+FmFormPage* FmFormShell::GetCurPage() const
+{
+ FmFormPage* pP = nullptr;
+ if (m_pFormView && m_pFormView->GetSdrPageView())
+ pP = dynamic_cast<FmFormPage*>( m_pFormView->GetSdrPageView()->GetPage() );
+ return pP;
+}
+
+
+void FmFormShell::SetView( FmFormView* _pView )
+{
+ if ( m_pFormView )
+ {
+ if ( IsActive() )
+ GetImpl()->viewDeactivated_Lock(*m_pFormView);
+
+ m_pFormView->SetFormShell( nullptr, FmFormView::FormShellAccess() );
+ m_pFormView = nullptr;
+ m_pFormModel = nullptr;
+ }
+
+ if ( !_pView )
+ return;
+
+ m_pFormView = _pView;
+ m_pFormView->SetFormShell( this, FmFormView::FormShellAccess() );
+ m_pFormModel = static_cast<FmFormModel*>(&m_pFormView->GetModel());
+
+ impl_setDesignMode( m_pFormView->IsDesignMode() );
+
+ // We activate our view if we are activated ourself, but sometimes the Activate precedes the SetView.
+ // But here we know both the view and our activation state so we at least are able to pass the latter
+ // to the former.
+ // FS - 30.06.99 - 67308
+ if ( IsActive() )
+ GetImpl()->viewActivated_Lock(*m_pFormView);
+}
+
+
+void FmFormShell::DetermineForms(bool bInvalidate)
+{
+ // are there forms on the current page
+ bool bForms = GetImpl()->hasForms_Lock();
+ if (bForms != m_bHasForms)
+ {
+ m_bHasForms = bForms;
+ if (bInvalidate)
+ UIFeatureChanged();
+ }
+}
+
+
+bool FmFormShell::GetY2KState(sal_uInt16& nReturn)
+{
+ return GetImpl()->GetY2KState_Lock(nReturn);
+}
+
+
+void FmFormShell::SetY2KState(sal_uInt16 n)
+{
+ GetImpl()->SetY2KState_Lock(n);
+}
+
+
+void FmFormShell::Activate(bool bMDI)
+{
+ SfxShell::Activate(bMDI);
+
+ if ( m_pFormView )
+ GetImpl()->viewActivated_Lock(*m_pFormView, true);
+}
+
+
+void FmFormShell::Deactivate(bool bMDI)
+{
+ SfxShell::Deactivate(bMDI);
+
+ if ( m_pFormView )
+ GetImpl()->viewDeactivated_Lock(*m_pFormView, false);
+}
+
+
+void FmFormShell::ExecuteTextAttribute( SfxRequest& _rReq )
+{
+ m_pImpl->ExecuteTextAttribute_Lock(_rReq);
+}
+
+
+void FmFormShell::GetTextAttributeState( SfxItemSet& _rSet )
+{
+ m_pImpl->GetTextAttributeState_Lock(_rSet);
+}
+
+
+bool FmFormShell::IsActiveControl() const
+{
+ return m_pImpl->IsActiveControl_Lock(false);
+}
+
+
+void FmFormShell::ForgetActiveControl()
+{
+ m_pImpl->ForgetActiveControl_Lock();
+}
+
+
+void FmFormShell::SetControlActivationHandler( const Link<LinkParamNone*,void>& _rHdl )
+{
+ m_pImpl->SetControlActivationHandler_Lock(_rHdl);
+}
+
+
+namespace
+{
+ SdrUnoObj* lcl_findUnoObject( const SdrObjList& _rObjList, const Reference< XControlModel >& _rxModel )
+ {
+ SdrObjListIter aIter( &_rObjList );
+ while ( aIter.IsMore() )
+ {
+ SdrObject* pObject = aIter.Next();
+ SdrUnoObj* pUnoObject = dynamic_cast<SdrUnoObj*>( pObject );
+ if ( !pUnoObject )
+ continue;
+
+ Reference< XControlModel > xControlModel = pUnoObject->GetUnoControlModel();
+ if ( !xControlModel.is() )
+ continue;
+
+ if ( _rxModel == xControlModel )
+ return pUnoObject;
+ }
+ return nullptr;
+ }
+}
+
+
+void FmFormShell::ToggleControlFocus( const SdrUnoObj& i_rUnoObject, const SdrView& i_rView, const OutputDevice& i_rDevice ) const
+{
+ try
+ {
+ // check if the focus currently is in a control
+ // Well, okay, do it the other way 'round: Check whether the current control of the active controller
+ // actually has the focus. This should be equivalent.
+ const bool bHasControlFocus = GetImpl()->HasControlFocus_Lock();
+
+ if ( bHasControlFocus )
+ {
+ vcl::Window* pWindow = i_rDevice.GetOwnerWindow();
+ OSL_ENSURE( pWindow, "FmFormShell::ToggleControlFocus: I need a Window, really!" );
+ if ( pWindow )
+ pWindow->GrabFocus();
+ }
+ else
+ {
+ Reference< XControl > xControl;
+ GetFormControl( i_rUnoObject.GetUnoControlModel(), i_rView, i_rDevice, xControl );
+ Reference< XWindow > xControlWindow( xControl, UNO_QUERY );
+ if ( xControlWindow.is() )
+ xControlWindow->setFocus();
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+
+namespace
+{
+ class FocusableControlsFilter : public svx::ISdrObjectFilter
+ {
+ public:
+ FocusableControlsFilter( const SdrView& i_rView, const OutputDevice& i_rDevice )
+ :m_rView( i_rView )
+ ,m_rDevice( i_rDevice )
+ {
+ }
+
+ public:
+ virtual bool includeObject( const SdrObject& i_rObject ) const override
+ {
+ const SdrUnoObj* pUnoObj = dynamic_cast< const SdrUnoObj* >( &i_rObject );
+ if ( !pUnoObj )
+ return false;
+
+ Reference< XControl > xControl = pUnoObj->GetUnoControl( m_rView, m_rDevice );
+ return FmXFormView::isFocusable( xControl );
+ }
+
+ private:
+ const SdrView& m_rView;
+ const OutputDevice& m_rDevice;
+ };
+}
+
+
+::std::unique_ptr< svx::ISdrObjectFilter > FmFormShell::CreateFocusableControlFilter( const SdrView& i_rView, const OutputDevice& i_rDevice )
+{
+ ::std::unique_ptr< svx::ISdrObjectFilter > pFilter;
+
+ if ( !i_rView.IsDesignMode() )
+ pFilter.reset( new FocusableControlsFilter( i_rView, i_rDevice ) );
+
+ return pFilter;
+}
+
+
+SdrUnoObj* FmFormShell::GetFormControl( const Reference< XControlModel >& _rxModel, const SdrView& _rView, const OutputDevice& _rDevice, Reference< XControl >& _out_rxControl ) const
+{
+ if ( !_rxModel.is() )
+ return nullptr;
+
+ // we can only retrieve controls for SdrObjects which belong to page which is actually displayed in the given view
+ SdrPageView* pPageView = _rView.GetSdrPageView();
+ SdrPage* pPage = pPageView ? pPageView->GetPage() : nullptr;
+ OSL_ENSURE( pPage, "FmFormShell::GetFormControl: no page displayed in the given view!" );
+ if ( !pPage )
+ return nullptr;
+
+ SdrUnoObj* pUnoObject = lcl_findUnoObject( *pPage, _rxModel );
+ if ( pUnoObject )
+ {
+ _out_rxControl = pUnoObject->GetUnoControl( _rView, _rDevice );
+ return pUnoObject;
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ // perhaps we are fed with a control model which lives on a page other than the one displayed
+ // in the given view. This is worth being reported as error, in non-product builds.
+ FmFormModel* pModel = GetFormModel();
+ if ( pModel )
+ {
+ sal_uInt16 pageCount = pModel->GetPageCount();
+ for ( sal_uInt16 page = 0; page < pageCount; ++page )
+ {
+ pPage = pModel->GetPage( page );
+ OSL_ENSURE( pPage, "FmFormShell::GetFormControl: NULL page encountered!" );
+ if ( !pPage )
+ continue;
+
+ pUnoObject = lcl_findUnoObject( *pPage, _rxModel );
+ OSL_ENSURE( !pUnoObject, "FmFormShell::GetFormControl: the given control model belongs to a wrong page (displayed elsewhere)!" );
+ }
+ }
+#else
+ (void) this; // avoid loplugin:staticmethods
+#endif
+
+ return nullptr;
+}
+
+
+Reference< runtime::XFormController > FmFormShell::GetFormController( const Reference< XForm >& _rxForm, const SdrView& _rView, const OutputDevice& _rDevice )
+{
+ const FmFormView* pFormView = dynamic_cast< const FmFormView* >( &_rView );
+ if ( !pFormView )
+ return nullptr;
+
+ return pFormView->GetFormController( _rxForm, _rDevice );
+}
+
+
+void FmFormShell::SetDesignMode( bool _bDesignMode )
+{
+ if ( _bDesignMode == m_bDesignMode )
+ return;
+
+ FmFormModel* pModel = GetFormModel();
+ if (pModel)
+ // Switch off the undo environment for the time of the transition. This ensures that
+ // one can also change non-transient properties there. (It should be done with
+ // caution, however, and it should always be reversed when one switches the mode back.
+ // An example is the setting of the maximum text length by the OEditModel on its control.)
+ pModel->GetUndoEnv().Lock();
+
+ // then the actual switch
+ if ( m_bDesignMode || PrepareClose() )
+ impl_setDesignMode(!m_bDesignMode );
+
+ // and my undo environment back on
+ if ( pModel )
+ pModel->GetUndoEnv().UnLock();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmshimp.cxx b/svx/source/form/fmshimp.cxx
new file mode 100644
index 0000000000..d54c5acaeb
--- /dev/null
+++ b/svx/source/form/fmshimp.cxx
@@ -0,0 +1,3946 @@
+/* -*- 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 <sal/config.h>
+
+#include <o3tl/safeint.hxx>
+#include <sal/macros.h>
+#include <sal/log.hxx>
+#include <fmobj.hxx>
+#include <fmpgeimp.hxx>
+#include <svx/fmtools.hxx>
+#include <fmprop.hxx>
+#include <fmservs.hxx>
+#include <fmshimp.hxx>
+#include <fmtextcontrolshell.hxx>
+#include <fmundo.hxx>
+#include <fmurl.hxx>
+#include <fmvwimp.hxx>
+#include <gridcols.hxx>
+#include <svx/svditer.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <svx/svdobjkind.hxx>
+#include <svx/fmmodel.hxx>
+#include <svx/fmpage.hxx>
+#include <svx/fmshell.hxx>
+#include <svx/fmview.hxx>
+#include <svx/obj3d.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svxdlg.hxx>
+#include <svx/svxids.hrc>
+#include <bitmaps.hlst>
+#include <formnavi.hrc>
+
+#include <com/sun/star/awt/XWindow2.hpp>
+#include <com/sun/star/awt/XCheckBox.hpp>
+#include <com/sun/star/awt/XListBox.hpp>
+#include <com/sun/star/awt/XTextComponent.hpp>
+#include <com/sun/star/beans/theIntrospection.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/XPropertyState.hpp>
+#include <com/sun/star/container/XContainer.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/form/ListSourceType.hpp>
+#include <com/sun/star/form/TabOrderDialog.hpp>
+#include <com/sun/star/form/XGrid.hpp>
+#include <com/sun/star/form/XGridPeer.hpp>
+#include <com/sun/star/form/XLoadable.hpp>
+#include <com/sun/star/form/XReset.hpp>
+#include <com/sun/star/form/binding/XBindableValue.hpp>
+#include <com/sun/star/form/binding/XListEntrySink.hpp>
+#include <com/sun/star/frame/FrameSearchFlag.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/script/XEventAttacherManager.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
+#include <com/sun/star/util/XModeSelector.hpp>
+#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+
+#include <comphelper/evtmethodhelper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/solarmutex.hxx>
+#include <comphelper/string.hxx>
+#include <comphelper/types.hxx>
+#include <connectivity/dbtools.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/viewsh.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <unotools/configmgr.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/window.hxx>
+
+#include <algorithm>
+#include <map>
+#include <memory>
+#include <string_view>
+#include <vector>
+
+// is used for Invalidate -> maintain it as well
+const sal_uInt16 DatabaseSlotMap[] =
+{
+ SID_FM_RECORD_FIRST,
+ SID_FM_RECORD_NEXT,
+ SID_FM_RECORD_PREV,
+ SID_FM_RECORD_LAST,
+ SID_FM_RECORD_NEW,
+ SID_FM_RECORD_DELETE,
+ SID_FM_RECORD_ABSOLUTE,
+ SID_FM_RECORD_TOTAL,
+ SID_FM_RECORD_SAVE,
+ SID_FM_RECORD_UNDO,
+ SID_FM_REMOVE_FILTER_SORT,
+ SID_FM_SORTUP,
+ SID_FM_SORTDOWN,
+ SID_FM_ORDERCRIT,
+ SID_FM_AUTOFILTER,
+ SID_FM_FORM_FILTERED,
+ SID_FM_REFRESH,
+ SID_FM_REFRESH_FORM_CONTROL,
+ SID_FM_SEARCH,
+ SID_FM_FILTER_START,
+ SID_FM_VIEW_AS_GRID,
+ 0
+};
+
+// is used for Invalidate -> maintain it as well
+// sort ascending !!!!!!
+const sal_Int16 DlgSlotMap[] = // slots of the controller
+{
+ SID_FM_CTL_PROPERTIES,
+ SID_FM_PROPERTIES,
+ SID_FM_TAB_DIALOG,
+ SID_FM_ADD_FIELD,
+ SID_FM_SHOW_FMEXPLORER,
+ SID_FM_FIELDS_CONTROL,
+ SID_FM_SHOW_PROPERTIES,
+ SID_FM_PROPERTY_CONTROL,
+ SID_FM_FMEXPLORER_CONTROL,
+ SID_FM_SHOW_DATANAVIGATOR,
+ SID_FM_DATANAVIGATOR_CONTROL,
+ 0
+};
+
+const sal_Int16 SelObjectSlotMap[] = // slots depending on the SelObject
+{
+ SID_FM_CONVERTTO_EDIT,
+ SID_FM_CONVERTTO_BUTTON,
+ SID_FM_CONVERTTO_FIXEDTEXT,
+ SID_FM_CONVERTTO_LISTBOX,
+ SID_FM_CONVERTTO_CHECKBOX,
+ SID_FM_CONVERTTO_RADIOBUTTON,
+ SID_FM_CONVERTTO_GROUPBOX,
+ SID_FM_CONVERTTO_COMBOBOX,
+ SID_FM_CONVERTTO_IMAGEBUTTON,
+ SID_FM_CONVERTTO_FILECONTROL,
+ SID_FM_CONVERTTO_DATE,
+ SID_FM_CONVERTTO_TIME,
+ SID_FM_CONVERTTO_NUMERIC,
+ SID_FM_CONVERTTO_CURRENCY,
+ SID_FM_CONVERTTO_PATTERN,
+ SID_FM_CONVERTTO_IMAGECONTROL,
+ SID_FM_CONVERTTO_FORMATTED,
+ SID_FM_CONVERTTO_SCROLLBAR,
+ SID_FM_CONVERTTO_SPINBUTTON,
+ SID_FM_CONVERTTO_NAVIGATIONBAR,
+
+ SID_FM_FMEXPLORER_CONTROL,
+ SID_FM_DATANAVIGATOR_CONTROL,
+
+ 0
+};
+
+// the following arrays must be consistent, i.e., corresponding entries should
+// be at the same relative position within their respective arrays
+static std::u16string_view aConvertSlots[] =
+{
+ u"ConvertToEdit",
+ u"ConvertToButton",
+ u"ConvertToFixed",
+ u"ConvertToList",
+ u"ConvertToCheckBox",
+ u"ConvertToRadio",
+ u"ConvertToGroup",
+ u"ConvertToCombo",
+ u"ConvertToImageBtn",
+ u"ConvertToFileControl",
+ u"ConvertToDate",
+ u"ConvertToTime",
+ u"ConvertToNumeric",
+ u"ConvertToCurrency",
+ u"ConvertToPattern",
+ u"ConvertToImageControl",
+ u"ConvertToFormatted",
+ u"ConvertToScrollBar",
+ u"ConvertToSpinButton",
+ u"ConvertToNavigationBar"
+};
+
+constexpr OUString aImgIds[] =
+{
+ RID_SVXBMP_EDITBOX,
+ RID_SVXBMP_BUTTON,
+ RID_SVXBMP_FIXEDTEXT,
+ RID_SVXBMP_LISTBOX,
+ RID_SVXBMP_CHECKBOX,
+ RID_SVXBMP_RADIOBUTTON,
+ RID_SVXBMP_GROUPBOX,
+ RID_SVXBMP_COMBOBOX,
+ RID_SVXBMP_IMAGEBUTTON,
+ RID_SVXBMP_FILECONTROL,
+ RID_SVXBMP_DATEFIELD,
+ RID_SVXBMP_TIMEFIELD,
+ RID_SVXBMP_NUMERICFIELD,
+ RID_SVXBMP_CURRENCYFIELD,
+ RID_SVXBMP_PATTERNFIELD,
+ RID_SVXBMP_IMAGECONTROL,
+ RID_SVXBMP_FORMATTEDFIELD,
+ RID_SVXBMP_SCROLLBAR,
+ RID_SVXBMP_SPINBUTTON,
+ RID_SVXBMP_NAVIGATIONBAR
+};
+
+const SdrObjKind nObjectTypes[] =
+{
+ SdrObjKind::FormEdit,
+ SdrObjKind::FormButton,
+ SdrObjKind::FormFixedText,
+ SdrObjKind::FormListbox,
+ SdrObjKind::FormCheckbox,
+ SdrObjKind::FormRadioButton,
+ SdrObjKind::FormGroupBox,
+ SdrObjKind::FormCombobox,
+ SdrObjKind::FormImageButton,
+ SdrObjKind::FormFileControl,
+ SdrObjKind::FormDateField,
+ SdrObjKind::FormTimeField,
+ SdrObjKind::FormNumericField,
+ SdrObjKind::FormCurrencyField,
+ SdrObjKind::FormPatternField,
+ SdrObjKind::FormImageControl,
+ SdrObjKind::FormFormattedField,
+ SdrObjKind::FormScrollbar,
+ SdrObjKind::FormSpinButton,
+ SdrObjKind::FormNavigationBar
+};
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::ui;
+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::form;
+using namespace ::com::sun::star::form::binding;
+using namespace ::com::sun::star::form::runtime;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::view;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::script;
+using namespace ::svxform;
+using namespace ::svx;
+using namespace ::dbtools;
+
+
+//= helper
+
+namespace
+{
+
+ void collectInterfacesFromMarkList( const SdrMarkList& _rMarkList, InterfaceBag& /* [out] */ _rInterfaces )
+ {
+ _rInterfaces.clear();
+
+ const size_t nMarkCount = _rMarkList.GetMarkCount();
+ for ( size_t i = 0; i < nMarkCount; ++i)
+ {
+ SdrObject* pCurrent = _rMarkList.GetMark( i )->GetMarkedSdrObj();
+
+ std::optional<SdrObjListIter> oGroupIterator;
+ if ( pCurrent->IsGroupObject() )
+ {
+ oGroupIterator.emplace( pCurrent->GetSubList() );
+ pCurrent = oGroupIterator->IsMore() ? oGroupIterator->Next() : nullptr;
+ }
+
+ while ( pCurrent )
+ {
+ FmFormObj* pAsFormObject = FmFormObj::GetFormObject( pCurrent );
+ // note this will de-reference virtual objects, if necessary/possible
+ if ( pAsFormObject )
+ {
+ Reference< XInterface > xControlModel( pAsFormObject->GetUnoControlModel(), UNO_QUERY );
+ // the UNO_QUERY is important for normalization
+ if ( xControlModel.is() )
+ _rInterfaces.insert( xControlModel );
+ }
+
+ // next element
+ pCurrent = oGroupIterator && oGroupIterator->IsMore() ? oGroupIterator->Next() : nullptr;
+ }
+ }
+ }
+
+
+ sal_Int32 GridView2ModelPos(const Reference< XIndexAccess>& rColumns, sal_Int16 nViewPos)
+ {
+ try
+ {
+ if (rColumns.is())
+ {
+ // loop through all columns
+ sal_Int32 i;
+ Reference< XPropertySet> xCur;
+ for (i=0; i<rColumns->getCount(); ++i)
+ {
+ rColumns->getByIndex(i) >>= xCur;
+ if (!::comphelper::getBOOL(xCur->getPropertyValue(FM_PROP_HIDDEN)))
+ {
+ // for every visible col : if nViewPos is greater zero, decrement it, else we
+ // have found the model position
+ if (!nViewPos)
+ break;
+ else
+ --nViewPos;
+ }
+ }
+ if (i<rColumns->getCount())
+ return i;
+ }
+ }
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return -1;
+ }
+
+
+ void TransferEventScripts(const Reference< XControlModel>& xModel, const Reference< XControl>& xControl,
+ const Sequence< ScriptEventDescriptor>& rTransferIfAvailable)
+ {
+ // first check if we have a XEventAttacherManager for the model
+ Reference< XChild> xModelChild(xModel, UNO_QUERY);
+ if (!xModelChild.is())
+ return; // nothing to do
+
+ Reference< XEventAttacherManager> xEventManager(xModelChild->getParent(), UNO_QUERY);
+ if (!xEventManager.is())
+ return; // nothing to do
+
+ if (!rTransferIfAvailable.hasElements())
+ return; // nothing to do
+
+ // check for the index of the model within its parent
+ Reference< XIndexAccess> xParentIndex(xModelChild->getParent(), UNO_QUERY);
+ if (!xParentIndex.is())
+ return; // nothing to do
+ sal_Int32 nIndex = getElementPos(xParentIndex, xModel);
+ if (nIndex<0 || nIndex>=xParentIndex->getCount())
+ return; // nothing to do
+
+ // then we need information about the listeners supported by the control and the model
+ Sequence< Type> aModelListeners;
+ Sequence< Type> aControlListeners;
+
+ Reference< XIntrospection> xIntrospection = theIntrospection::get(::comphelper::getProcessComponentContext());
+
+ if (xModel.is())
+ {
+ Any aModel(xModel);
+ aModelListeners = xIntrospection->inspect(aModel)->getSupportedListeners();
+ }
+
+ if (xControl.is())
+ {
+ Any aControl(xControl);
+ aControlListeners = xIntrospection->inspect(aControl)->getSupportedListeners();
+ }
+
+ sal_Int32 nMaxNewLen = aModelListeners.getLength() + aControlListeners.getLength();
+ if (!nMaxNewLen)
+ return; // the model and the listener don't support any listeners (or we were unable to retrieve these infos)
+
+ Sequence< ScriptEventDescriptor> aTransferable(nMaxNewLen);
+ ScriptEventDescriptor* pTransferable = aTransferable.getArray();
+
+ for (const ScriptEventDescriptor& rCurrent : rTransferIfAvailable)
+ {
+ // search the model/control idl classes for the event described by pCurrent
+ for (const Sequence< Type>* pCurrentArray : { &aModelListeners, &aControlListeners })
+ {
+ for (const Type& rCurrentListener : *pCurrentArray)
+ {
+ OUString aListener = rCurrentListener.getTypeName();
+ if (!aListener.isEmpty())
+ aListener = aListener.copy(aListener.lastIndexOf('.')+1);
+
+ if (aListener == rCurrent.ListenerType)
+ // the current ScriptEventDescriptor doesn't match the current listeners class
+ continue;
+
+ // now check the methods
+ Sequence< OUString> aMethodsNames = ::comphelper::getEventMethodsForType(rCurrentListener);
+
+ if (comphelper::findValue(aMethodsNames, rCurrent.EventMethod) != -1)
+ {
+ // we can transfer the script event : the model (control) supports it
+ *pTransferable = rCurrent;
+ ++pTransferable;
+ break;
+ }
+ }
+ }
+ }
+
+ sal_Int32 nRealNewLen = pTransferable - aTransferable.getArray();
+ aTransferable.realloc(nRealNewLen);
+
+ xEventManager->registerScriptEvents(nIndex, aTransferable);
+ }
+
+
+ OUString getServiceNameByControlType(SdrObjKind nType)
+ {
+ switch (nType)
+ {
+ case SdrObjKind::FormEdit : return FM_COMPONENT_TEXTFIELD;
+ case SdrObjKind::FormButton : return FM_COMPONENT_COMMANDBUTTON;
+ case SdrObjKind::FormFixedText : return FM_COMPONENT_FIXEDTEXT;
+ case SdrObjKind::FormListbox : return FM_COMPONENT_LISTBOX;
+ case SdrObjKind::FormCheckbox : return FM_COMPONENT_CHECKBOX;
+ case SdrObjKind::FormRadioButton : return FM_COMPONENT_RADIOBUTTON;
+ case SdrObjKind::FormGroupBox : return FM_COMPONENT_GROUPBOX;
+ case SdrObjKind::FormCombobox : return FM_COMPONENT_COMBOBOX;
+ case SdrObjKind::FormGrid : return FM_COMPONENT_GRIDCONTROL;
+ case SdrObjKind::FormImageButton : return FM_COMPONENT_IMAGEBUTTON;
+ case SdrObjKind::FormFileControl : return FM_COMPONENT_FILECONTROL;
+ case SdrObjKind::FormDateField : return FM_COMPONENT_DATEFIELD;
+ case SdrObjKind::FormTimeField : return FM_COMPONENT_TIMEFIELD;
+ case SdrObjKind::FormNumericField : return FM_COMPONENT_NUMERICFIELD;
+ case SdrObjKind::FormCurrencyField : return FM_COMPONENT_CURRENCYFIELD;
+ case SdrObjKind::FormPatternField : return FM_COMPONENT_PATTERNFIELD;
+ case SdrObjKind::FormHidden : return FM_COMPONENT_HIDDENCONTROL;
+ case SdrObjKind::FormImageControl : return FM_COMPONENT_IMAGECONTROL;
+ case SdrObjKind::FormFormattedField : return FM_COMPONENT_FORMATTEDFIELD;
+ case SdrObjKind::FormScrollbar : return FM_SUN_COMPONENT_SCROLLBAR;
+ case SdrObjKind::FormSpinButton : return FM_SUN_COMPONENT_SPINBUTTON;
+ case SdrObjKind::FormNavigationBar : return FM_SUN_COMPONENT_NAVIGATIONBAR;
+ default:;
+ }
+ return OUString();
+ }
+
+}
+
+
+// check if the control has one of the interfaces we can use for searching
+// *_pCurrentText will be filled with the current text of the control (as used when searching this control)
+bool IsSearchableControl( const css::uno::Reference< css::uno::XInterface>& _rxControl,
+ OUString* _pCurrentText )
+{
+ if ( !_rxControl.is() )
+ return false;
+
+ Reference< XTextComponent > xAsText( _rxControl, UNO_QUERY );
+ if ( xAsText.is() )
+ {
+ if ( _pCurrentText )
+ *_pCurrentText = xAsText->getText();
+ return true;
+ }
+
+ Reference< XListBox > xListBox( _rxControl, UNO_QUERY );
+ if ( xListBox.is() )
+ {
+ if ( _pCurrentText )
+ *_pCurrentText = xListBox->getSelectedItem();
+ return true;
+ }
+
+ Reference< XCheckBox > xCheckBox( _rxControl, UNO_QUERY );
+ if ( xCheckBox.is() )
+ {
+ if ( _pCurrentText )
+ {
+ switch ( static_cast<::TriState>(xCheckBox->getState()) )
+ {
+ case TRISTATE_FALSE: *_pCurrentText = "0"; break;
+ case TRISTATE_TRUE: *_pCurrentText = "1"; break;
+ default: _pCurrentText->clear(); break;
+ }
+ }
+ return true;
+ }
+
+ return false;
+}
+
+
+bool FmXBoundFormFieldIterator::ShouldStepInto(const Reference< XInterface>& _rContainer) const
+{
+ if (_rContainer == m_xStartingPoint)
+ // would be quite stupid to step over the root...
+ return true;
+
+ return Reference< XControlModel>(_rContainer, UNO_QUERY).is();
+}
+
+
+bool FmXBoundFormFieldIterator::ShouldHandleElement(const Reference< XInterface>& _rElement)
+{
+ if (!_rElement.is())
+ // NULL element
+ return false;
+
+ if (Reference< XForm>(_rElement, UNO_QUERY).is() || Reference< XGrid>(_rElement, UNO_QUERY).is())
+ // a forms or a grid
+ return false;
+
+ Reference< XPropertySet> xSet(_rElement, UNO_QUERY);
+ if (!xSet.is() || !::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
+ // no "BoundField" property
+ return false;
+
+ Any aVal( xSet->getPropertyValue(FM_PROP_BOUNDFIELD) );
+ if (aVal.getValueTypeClass() != TypeClass_INTERFACE)
+ // void or invalid property value
+ return false;
+
+ return aVal.hasValue();
+}
+
+
+static bool isControlList(const SdrMarkList& rMarkList)
+{
+ // the list contains only controls and at least one control
+ const size_t nMarkCount = rMarkList.GetMarkCount();
+ bool bControlList = nMarkCount != 0;
+
+ bool bHadAnyLeafs = false;
+
+ for (size_t i = 0; i < nMarkCount && bControlList; ++i)
+ {
+ SdrObject *pObj = rMarkList.GetMark(i)->GetMarkedSdrObj();
+ E3dObject* pAs3DObject = DynCastE3dObject( pObj);
+ // E3dObject's do not contain any 2D-objects (by definition)
+ // we need this extra check here : an E3dObject->IsGroupObject says "YES", but an SdrObjListIter working
+ // with an E3dObject doesn't give me any Nodes (E3dObject has a sub list, but no members in that list,
+ // cause there implementation differs from the one of "normal" SdrObject's. Unfortunally SdrObject::IsGroupObject
+ // doesn't check the element count of the sub list, which is simply a bug in IsGroupObject we can't fix at the moment).
+ // So at the end of this function bControlList would have the same value it was initialized with above : sal_True
+ // And this would be wrong :)
+ // 03.02.00 - 72529 - FS
+ if (!pAs3DObject)
+ {
+ if (pObj->IsGroupObject())
+ {
+ SdrObjListIter aIter(pObj->GetSubList());
+ while (aIter.IsMore() && bControlList)
+ {
+ bControlList = SdrInventor::FmForm == aIter.Next()->GetObjInventor();
+ bHadAnyLeafs = true;
+ }
+ }
+ else
+ {
+ bHadAnyLeafs = true;
+ bControlList = SdrInventor::FmForm == pObj->GetObjInventor();
+ }
+ }
+ }
+
+ return bControlList && bHadAnyLeafs;
+}
+
+
+static Reference< XForm > GetForm(const Reference< XInterface>& _rxElement)
+{
+ Reference< XForm > xForm( _rxElement, UNO_QUERY );
+ if ( xForm.is() )
+ return xForm;
+
+ Reference< XChild > xChild( _rxElement, UNO_QUERY );
+ if ( xChild.is() )
+ return GetForm( xChild->getParent() );
+
+ return Reference< XForm >();
+}
+
+FmXFormShell_Base_Disambiguation::FmXFormShell_Base_Disambiguation( ::osl::Mutex& _rMutex )
+ :FmXFormShell_BD_BASE( _rMutex )
+{
+}
+
+FmXFormShell::FmXFormShell( FmFormShell& _rShell, SfxViewFrame* _pViewFrame )
+ :FmXFormShell_BASE(m_aMutex)
+ ,FmXFormShell_CFGBASE("Office.Common/Misc", ConfigItemMode::NONE)
+ ,m_aMarkTimer("svx::FmXFormShell m_aMarkTimer")
+ ,m_eNavigate( NavigationBarMode_NONE )
+ ,m_nInvalidationEvent( nullptr )
+ ,m_nActivationEvent( nullptr )
+ ,m_pShell( &_rShell )
+ ,m_pTextShell( new svx::FmTextControlShell( _pViewFrame ) )
+ ,m_aActiveControllerFeatures( this )
+ ,m_aNavControllerFeatures( this )
+ ,m_eDocumentType( eUnknownDocumentType )
+ ,m_nLockSlotInvalidation( 0 )
+ ,m_bHadPropertyBrowserInDesignMode( false )
+ ,m_bTrackProperties( true )
+ ,m_bUseWizards( true )
+ ,m_bDatabaseBar( false )
+ ,m_bInActivate( false )
+ ,m_bSetFocus( false )
+ ,m_bFilterMode( false )
+ ,m_bChangingDesignMode( false )
+ ,m_bPreparedClose( false )
+ ,m_bFirstActivation( true )
+{
+ m_aMarkTimer.SetTimeout(100);
+ m_aMarkTimer.SetInvokeHandler(LINK(this, FmXFormShell, OnTimeOut_Lock));
+
+ m_xAttachedFrame = _pViewFrame->GetFrame().GetFrameInterface();
+
+ // to prevent deletion of this we acquire our refcounter once
+ osl_atomic_increment(&m_refCount);
+
+ // correct the refcounter
+ osl_atomic_decrement(&m_refCount);
+
+ // cache the current configuration settings we're interested in
+ implAdjustConfigCache_Lock();
+ // and register for changes on this settings
+ Sequence< OUString > aNames { "FormControlPilotsEnabled" };
+ EnableNotification(aNames);
+}
+
+
+FmXFormShell::~FmXFormShell()
+{
+}
+
+
+Reference< css::frame::XModel > FmXFormShell::getContextDocument_Lock() const
+{
+ Reference< css::frame::XModel > xModel;
+
+ // determine the type of document we live in
+ try
+ {
+ Reference< css::frame::XController > xController;
+ if ( m_xAttachedFrame.is() )
+ xController = m_xAttachedFrame->getController();
+ if ( xController.is() )
+ xModel = xController->getModel();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return xModel;
+}
+
+
+bool FmXFormShell::isEnhancedForm_Lock() const
+{
+ return getDocumentType_Lock() == eEnhancedForm;
+}
+
+
+bool FmXFormShell::impl_checkDisposed_Lock() const
+{
+ DBG_TESTSOLARMUTEX();
+ if ( !m_pShell )
+ {
+ OSL_FAIL( "FmXFormShell::impl_checkDisposed: already disposed!" );
+ return true;
+ }
+ return false;
+}
+
+
+::svxform::DocumentType FmXFormShell::getDocumentType_Lock() const
+{
+ if ( m_eDocumentType != eUnknownDocumentType )
+ return m_eDocumentType;
+
+ // determine the type of document we live in
+ Reference<css::frame::XModel> xModel = getContextDocument_Lock();
+ if ( xModel.is() )
+ m_eDocumentType = DocumentClassification::classifyDocument( xModel );
+ else
+ {
+ OSL_FAIL( "FmXFormShell::getDocumentType: can't determine the document type!" );
+ m_eDocumentType = eTextDocument;
+ // fallback, just to have a defined state
+ }
+
+ return m_eDocumentType;
+}
+
+
+bool FmXFormShell::IsReadonlyDoc_Lock() const
+{
+ if (impl_checkDisposed_Lock())
+ return true;
+
+ FmFormModel* pModel = m_pShell->GetFormModel();
+ if ( pModel && pModel->GetObjectShell() )
+ return pModel->GetObjectShell()->IsReadOnly() || pModel->GetObjectShell()->IsReadOnlyUI();
+ return true;
+}
+
+// EventListener
+
+void SAL_CALL FmXFormShell::disposing(const lang::EventObject& e)
+{
+ SolarMutexGuard g;
+
+ if (m_xActiveController == e.Source)
+ {
+ // the controller will release, then release everything
+ stopListening_Lock();
+ m_xActiveForm = nullptr;
+ m_xActiveController = nullptr;
+ m_xNavigationController = nullptr;
+
+ m_aActiveControllerFeatures.dispose();
+ m_aNavControllerFeatures.dispose();
+
+ if ( m_pShell )
+ m_pShell->GetViewShell()->GetViewFrame().GetBindings().InvalidateShell(*m_pShell);
+ }
+
+ if (e.Source != m_xExternalViewController)
+ return;
+
+ Reference< runtime::XFormController > xFormController( m_xExternalViewController, UNO_QUERY );
+ OSL_ENSURE( xFormController.is(), "FmXFormShell::disposing: invalid external view controller!" );
+ if (xFormController.is())
+ xFormController->removeActivateListener(static_cast<XFormControllerListener*>(this));
+
+ if (m_xExternalViewController.is())
+ m_xExternalViewController->removeEventListener(static_cast<XEventListener*>(static_cast<XPropertyChangeListener*>(this)));
+
+ m_xExternalViewController = nullptr;
+ m_xExternalDisplayedForm = nullptr;
+ m_xExtViewTriggerController = nullptr;
+
+ InvalidateSlot_Lock( SID_FM_VIEW_AS_GRID, false );
+}
+
+
+void SAL_CALL FmXFormShell::propertyChange(const PropertyChangeEvent& evt)
+{
+ SolarMutexGuard g;
+
+ if (impl_checkDisposed_Lock())
+ return;
+
+ if (evt.PropertyName == FM_PROP_ROWCOUNT)
+ {
+ // The update following this forces a re-painting of the corresponding
+ // slots. But if I am not in the MainThread of the application (because,
+ // for example, a cursor is counting data sets at the moment and always
+ // gives me this PropertyChanges), this can clash with normal paints in
+ // the MainThread of the application. (Such paints happen, for example,
+ // if one simply places another application over the office and switches
+ // back again).
+ // Therefore the use of the SolarMutex, which safeguards that.
+ comphelper::SolarMutex& rSolarSafety = Application::GetSolarMutex();
+ if (rSolarSafety.tryToAcquire())
+ {
+ m_pShell->GetViewShell()->GetViewFrame().GetBindings().Invalidate(SID_FM_RECORD_TOTAL, true);
+ m_pShell->GetViewShell()->GetViewFrame().GetBindings().Update(SID_FM_RECORD_TOTAL);
+ rSolarSafety.release();
+ }
+ else
+ {
+ // with the following the slot is invalidated asynchron
+ LockSlotInvalidation_Lock(true);
+ InvalidateSlot_Lock(SID_FM_RECORD_TOTAL, false);
+ LockSlotInvalidation_Lock(false);
+ }
+ }
+
+ // this may be called from a non-main-thread so invalidate the shell asynchronously
+ LockSlotInvalidation_Lock(true);
+ InvalidateSlot_Lock(0, false); // special meaning : invalidate m_pShell
+ LockSlotInvalidation_Lock(false);
+}
+
+
+void FmXFormShell::invalidateFeatures( const ::std::vector< sal_Int32 >& _rFeatures )
+{
+ SolarMutexGuard g;
+
+ if (impl_checkDisposed_Lock())
+ return;
+
+ OSL_ENSURE( !_rFeatures.empty(), "FmXFormShell::invalidateFeatures: invalid arguments!" );
+
+ if (!m_pShell->GetViewShell())
+ return;
+
+ // unfortunately, SFX requires sal_uInt16
+ ::std::vector< sal_uInt16 > aSlotIds( _rFeatures.begin(), _rFeatures.end() );
+
+ // furthermore, SFX wants a terminating 0
+ aSlotIds.push_back( 0 );
+
+ // and, last but not least, SFX wants the ids to be sorted
+ ::std::sort( aSlotIds.begin(), aSlotIds.end() - 1 );
+
+ sal_uInt16 *pSlotIds = aSlotIds.data();
+ m_pShell->GetViewShell()->GetViewFrame().GetBindings().Invalidate( pSlotIds );
+}
+
+
+void SAL_CALL FmXFormShell::formActivated(const lang::EventObject& rEvent)
+{
+ SolarMutexGuard g;
+
+ if (impl_checkDisposed_Lock())
+ return;
+
+ Reference< runtime::XFormController > xController( rEvent.Source, UNO_QUERY_THROW );
+ m_pTextShell->formActivated( xController );
+ setActiveController_Lock(xController);
+}
+
+
+void SAL_CALL FmXFormShell::formDeactivated(const lang::EventObject& rEvent)
+{
+ SolarMutexGuard g;
+
+ if (impl_checkDisposed_Lock())
+ return;
+
+ Reference< runtime::XFormController > xController( rEvent.Source, UNO_QUERY_THROW );
+ m_pTextShell->formDeactivated( xController );
+}
+
+
+void FmXFormShell::disposing()
+{
+ SolarMutexGuard g;
+
+ FmXFormShell_BASE::disposing();
+
+ if ( m_pShell && !m_pShell->IsDesignMode() )
+ setActiveController_Lock(nullptr, true);
+ // do NOT save the content of the old form (the second parameter tells this)
+ // if we're here, then we expect that PrepareClose has been called, and thus the user
+ // got a chance to commit or reject any changes. So in case we're here and there
+ // are still uncommitted changes, the user explicitly wanted this.
+
+ m_pTextShell->dispose();
+
+ m_xAttachedFrame = nullptr;
+
+ CloseExternalFormViewer_Lock();
+
+ while ( !m_aLoadingPages.empty() )
+ {
+ Application::RemoveUserEvent( m_aLoadingPages.front().nEventId );
+ m_aLoadingPages.pop();
+ }
+
+ {
+ if (m_nInvalidationEvent)
+ {
+ Application::RemoveUserEvent(m_nInvalidationEvent);
+ m_nInvalidationEvent = nullptr;
+ }
+ if ( m_nActivationEvent )
+ {
+ Application::RemoveUserEvent( m_nActivationEvent );
+ m_nActivationEvent = nullptr;
+ }
+ }
+
+ {
+ DBG_ASSERT(!m_nInvalidationEvent, "FmXFormShell::~FmXFormShell : still have an invalidation event !");
+ // should have been deleted while being disposed
+
+ m_aMarkTimer.Stop();
+ }
+
+ DisableNotification();
+
+ RemoveElement_Lock(m_xForms);
+ m_xForms.clear();
+
+ impl_switchActiveControllerListening_Lock(false);
+ m_xActiveController = nullptr;
+ m_xActiveForm = nullptr;
+
+ m_pShell = nullptr;
+ m_xNavigationController = nullptr;
+ m_xCurrentForm = nullptr;
+ m_xLastGridFound = nullptr;
+ m_xAttachedFrame = nullptr;
+ m_xExternalViewController = nullptr;
+ m_xExtViewTriggerController = nullptr;
+ m_xExternalDisplayedForm = nullptr;
+
+ InterfaceBag().swap(m_aCurrentSelection);
+
+ m_aActiveControllerFeatures.dispose();
+ m_aNavControllerFeatures.dispose();
+}
+
+
+void FmXFormShell::UpdateSlot_Lock(sal_Int16 _nId)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ if ( m_nLockSlotInvalidation )
+ {
+ OSL_FAIL( "FmXFormShell::UpdateSlot: cannot update if invalidation is currently locked!" );
+ InvalidateSlot_Lock(_nId, false);
+ }
+ else
+ {
+ OSL_ENSURE( _nId, "FmXFormShell::UpdateSlot: can't update the complete shell!" );
+ m_pShell->GetViewShell()->GetViewFrame().GetBindings().Invalidate( _nId, true, true );
+ m_pShell->GetViewShell()->GetViewFrame().GetBindings().Update( _nId );
+ }
+}
+
+void FmXFormShell::InvalidateSlot_Lock(sal_Int16 nId, bool bWithId)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ if (m_nLockSlotInvalidation)
+ {
+ sal_uInt8 nFlags = ( bWithId ? 0x01 : 0 );
+ m_arrInvalidSlots.emplace_back(nId, nFlags );
+ }
+ else
+ if (nId)
+ m_pShell->GetViewShell()->GetViewFrame().GetBindings().Invalidate(nId, true, bWithId);
+ else
+ m_pShell->GetViewShell()->GetViewFrame().GetBindings().InvalidateShell(*m_pShell);
+}
+
+void FmXFormShell::LockSlotInvalidation_Lock(bool bLock)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ DBG_ASSERT(bLock || m_nLockSlotInvalidation>0, "FmXFormShell::LockSlotInvalidation : invalid call !");
+
+ if (bLock)
+ ++m_nLockSlotInvalidation;
+ else if (!--m_nLockSlotInvalidation)
+ {
+ // (asynchronously) invalidate everything accumulated during the locked phase
+ if (!m_nInvalidationEvent)
+ m_nInvalidationEvent = Application::PostUserEvent(LINK(this, FmXFormShell, OnInvalidateSlots_Lock));
+ }
+}
+
+IMPL_LINK_NOARG(FmXFormShell, OnInvalidateSlots_Lock, void*,void)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ m_nInvalidationEvent = nullptr;
+
+ for (const auto& rInvalidSlot : m_arrInvalidSlots)
+ {
+ if (rInvalidSlot.id)
+ m_pShell->GetViewShell()->GetViewFrame().GetBindings().Invalidate(rInvalidSlot.id, true, (rInvalidSlot.flags & 0x01));
+ else
+ m_pShell->GetViewShell()->GetViewFrame().GetBindings().InvalidateShell(*m_pShell);
+ }
+ m_arrInvalidSlots.clear();
+}
+
+void FmXFormShell::ForceUpdateSelection_Lock()
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ if (IsSelectionUpdatePending_Lock())
+ {
+ m_aMarkTimer.Stop();
+
+ // optionally turn off the invalidation of slots which is implicitly done by SetSelection
+ LockSlotInvalidation_Lock(true);
+
+ SetSelection_Lock(m_pShell->GetFormView()->GetMarkedObjectList());
+
+ LockSlotInvalidation_Lock(false);
+ }
+}
+
+void FmXFormShell::GetConversionMenu_Lock(weld::Menu& rNewMenu)
+{
+ for (size_t i = 0; i < SAL_N_ELEMENTS(aConvertSlots); ++i)
+ {
+ // the corresponding image at it
+ rNewMenu.append(OUString(aConvertSlots[i]), SvxResId(RID_SVXSW_CONVERTMENU[i]), aImgIds[i]);
+ }
+}
+
+OUString FmXFormShell::SlotToIdent(sal_uInt16 nSlot)
+{
+ static_assert(SAL_N_ELEMENTS(SelObjectSlotMap) >= SAL_N_ELEMENTS(aConvertSlots));
+
+ for (size_t i = 0; i < SAL_N_ELEMENTS(aConvertSlots); ++i)
+ {
+ if (nSlot == SelObjectSlotMap[i])
+ return OUString(aConvertSlots[i]);
+ }
+
+ return {};
+}
+
+bool FmXFormShell::isControlConversionSlot(std::u16string_view rIdent)
+{
+ for (const auto& rConvertSlot : aConvertSlots)
+ if (rIdent == rConvertSlot)
+ return true;
+ return false;
+}
+
+void FmXFormShell::executeControlConversionSlot_Lock(std::u16string_view rIdent)
+{
+ OSL_PRECOND( canConvertCurrentSelectionToControl_Lock(rIdent), "FmXFormShell::executeControlConversionSlot: illegal call!" );
+ InterfaceBag::const_iterator aSelectedElement = m_aCurrentSelection.begin();
+ if ( aSelectedElement == m_aCurrentSelection.end() )
+ return;
+
+ executeControlConversionSlot_Lock(Reference<XFormComponent>(*aSelectedElement, UNO_QUERY), rIdent);
+}
+
+bool FmXFormShell::executeControlConversionSlot_Lock(const Reference<XFormComponent>& _rxObject, std::u16string_view rIdent)
+{
+ if (impl_checkDisposed_Lock())
+ return false;
+
+ OSL_ENSURE( _rxObject.is(), "FmXFormShell::executeControlConversionSlot: invalid object!" );
+ if ( !_rxObject.is() )
+ return false;
+
+ SdrPage* pPage = m_pShell->GetCurPage();
+ FmFormPage* pFormPage = dynamic_cast< FmFormPage* >( pPage );
+ OSL_ENSURE( pFormPage, "FmXFormShell::executeControlConversionSlot: no current (form) page!" );
+ if ( !pFormPage )
+ return false;
+
+ OSL_ENSURE( isSolelySelected_Lock(_rxObject),
+ "FmXFormShell::executeControlConversionSlot: hmm ... shouldn't this parameter be redundant?" );
+
+ for (size_t lookupSlot = 0; lookupSlot < SAL_N_ELEMENTS(aConvertSlots); ++lookupSlot)
+ {
+ if (rIdent == aConvertSlots[lookupSlot])
+ {
+ Reference< XInterface > xNormalizedObject( _rxObject, UNO_QUERY );
+
+ FmFormObj* pFormObject = nullptr;
+ SdrObjListIter aPageIter( pFormPage );
+ while ( aPageIter.IsMore() )
+ {
+ SdrObject* pCurrent = aPageIter.Next();
+ pFormObject = FmFormObj::GetFormObject( pCurrent );
+ if ( !pFormObject )
+ continue;
+
+ Reference< XInterface > xCurrentNormalized( pFormObject->GetUnoControlModel(), UNO_QUERY );
+ if ( xCurrentNormalized.get() == xNormalizedObject.get() )
+ break;
+
+ pFormObject = nullptr;
+ }
+
+ if ( !pFormObject )
+ return false;
+
+ OUString sNewName( getServiceNameByControlType( nObjectTypes[ lookupSlot ] ) );
+ Reference<XComponentContext> xContext = comphelper::getProcessComponentContext();
+ Reference< XControlModel> xNewModel( xContext->getServiceManager()->createInstanceWithContext(sNewName, xContext), UNO_QUERY );
+ if (!xNewModel.is())
+ return false;
+
+ Reference< XControlModel> xOldModel( pFormObject->GetUnoControlModel() );
+
+ // transfer properties
+ Reference< XPropertySet> xOldSet(xOldModel, UNO_QUERY);
+ Reference< XPropertySet> xNewSet(xNewModel, UNO_QUERY);
+
+
+ lang::Locale aNewLanguage = Application::GetSettings().GetUILanguageTag().getLocale();
+ TransferFormComponentProperties(xOldSet, xNewSet, aNewLanguage);
+
+ Sequence< css::script::ScriptEventDescriptor> aOldScripts;
+ Reference< XChild> xChild(xOldModel, UNO_QUERY);
+ if (xChild.is())
+ {
+ Reference< XIndexAccess> xParent(xChild->getParent(), UNO_QUERY);
+
+ // remember old script events
+ Reference< css::script::XEventAttacherManager> xEvManager(xChild->getParent(), UNO_QUERY);
+ if (xParent.is() && xEvManager.is())
+ {
+ sal_Int32 nIndex = getElementPos(xParent, xOldModel);
+ if (nIndex>=0 && nIndex<xParent->getCount())
+ aOldScripts = xEvManager->getScriptEvents(nIndex);
+ }
+
+ // replace the model within the parent container
+ Reference< XIndexContainer> xIndexParent(xChild->getParent(), UNO_QUERY);
+ if (xIndexParent.is())
+ {
+ // the form container works with FormComponents
+ Reference< XFormComponent> xComponent(xNewModel, UNO_QUERY);
+ DBG_ASSERT(xComponent.is(), "FmXFormShell::executeControlConversionSlot: the new model is no form component !");
+ Any aNewModel(xComponent);
+ try
+ {
+
+ sal_Int32 nIndex = getElementPos(xParent, xOldModel);
+ if (nIndex>=0 && nIndex<xParent->getCount())
+ xIndexParent->replaceByIndex(nIndex, aNewModel);
+ else
+ {
+ OSL_FAIL("FmXFormShell::executeControlConversionSlot: could not replace the model !");
+ Reference< css::lang::XComponent> xNewComponent(xNewModel, UNO_QUERY);
+ if (xNewComponent.is())
+ xNewComponent->dispose();
+ return false;
+ }
+ }
+ catch(Exception&)
+ {
+ OSL_FAIL("FmXFormShell::executeControlConversionSlot: could not replace the model !");
+ Reference< css::lang::XComponent> xNewComponent(xNewModel, UNO_QUERY);
+ if (xNewComponent.is())
+ xNewComponent->dispose();
+ return false;
+ }
+
+ }
+ }
+
+ // special handling for the LabelControl-property : can only be set when the model is placed
+ // within the forms hierarchy
+ if (::comphelper::hasProperty(FM_PROP_CONTROLLABEL, xOldSet) && ::comphelper::hasProperty(FM_PROP_CONTROLLABEL, xNewSet))
+ {
+ try
+ {
+ xNewSet->setPropertyValue(FM_PROP_CONTROLLABEL, xOldSet->getPropertyValue(FM_PROP_CONTROLLABEL));
+ }
+ catch(Exception&)
+ {
+ }
+
+ }
+
+ // set new model
+ pFormObject->SetChanged();
+ pFormObject->SetUnoControlModel(xNewModel);
+
+ // transfer script events
+ // (do this _after_ SetUnoControlModel as we need the new (implicitly created) control)
+ if (aOldScripts.hasElements())
+ {
+ // find the control for the model
+ Reference<XControlContainer> xControlContainer(getControlContainerForView_Lock());
+
+ const Sequence< Reference< XControl> > aControls( xControlContainer->getControls() );
+
+ Reference< XControl> xControl;
+ auto pControl = std::find_if(aControls.begin(), aControls.end(),
+ [&xNewModel](const Reference< XControl>& rControl) { return rControl->getModel() == xNewModel; });
+ if (pControl != aControls.end())
+ xControl = *pControl;
+ TransferEventScripts(xNewModel, xControl, aOldScripts);
+ }
+
+ // transfer value bindings, if possible
+ {
+ Reference< XBindableValue > xOldBindable( xOldModel, UNO_QUERY );
+ Reference< XBindableValue > xNewBindable( xNewModel, UNO_QUERY );
+ if ( xOldBindable.is() )
+ {
+ try
+ {
+ if ( xNewBindable.is() )
+ xNewBindable->setValueBinding( xOldBindable->getValueBinding() );
+ xOldBindable->setValueBinding( nullptr );
+ }
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ }
+ // same for list entry sources
+ {
+ Reference< XListEntrySink > xOldSink( xOldModel, UNO_QUERY );
+ Reference< XListEntrySink > xNewSink( xNewModel, UNO_QUERY );
+ if ( xOldSink.is() )
+ {
+ try
+ {
+ if ( xNewSink.is() )
+ xNewSink->setListEntrySource( xOldSink->getListEntrySource() );
+ xOldSink->setListEntrySource( nullptr );
+ }
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ }
+
+ // create an undo action
+ FmFormModel* pModel = m_pShell->GetFormModel();
+ DBG_ASSERT(pModel != nullptr, "FmXFormShell::executeControlConversionSlot: my shell has no model !");
+ if (pModel && pModel->IsUndoEnabled() )
+ {
+ pModel->AddUndo(std::make_unique<FmUndoModelReplaceAction>(*pModel, pFormObject, xOldModel));
+ }
+ else
+ {
+ FmUndoModelReplaceAction::DisposeElement( xOldModel );
+ }
+
+ return true;
+ }
+ }
+ return false;
+}
+
+bool FmXFormShell::canConvertCurrentSelectionToControl_Lock(std::u16string_view rIdent)
+{
+ if ( m_aCurrentSelection.empty() )
+ return false;
+
+ InterfaceBag::const_iterator aCheck = m_aCurrentSelection.begin();
+ Reference< lang::XServiceInfo > xElementInfo( *aCheck, UNO_QUERY );
+ if ( !xElementInfo.is() )
+ // no service info -> cannot determine this
+ return false;
+
+ if ( ++aCheck != m_aCurrentSelection.end() )
+ // more than one element
+ return false;
+
+ if ( Reference< XForm >::query( xElementInfo ).is() )
+ // it's a form
+ return false;
+
+ SdrObjKind nObjectType = getControlTypeByObject( xElementInfo );
+
+ if ( ( SdrObjKind::FormHidden == nObjectType )
+ || ( SdrObjKind::FormControl == nObjectType )
+ || ( SdrObjKind::FormGrid == nObjectType )
+ )
+ return false; // those types cannot be converted
+
+ DBG_ASSERT(SAL_N_ELEMENTS(aConvertSlots) == SAL_N_ELEMENTS(nObjectTypes),
+ "FmXFormShell::canConvertCurrentSelectionToControl: aConvertSlots & nObjectTypes must have the same size !");
+
+ for (size_t i = 0; i < SAL_N_ELEMENTS(aConvertSlots); ++i)
+ if (rIdent == aConvertSlots[i])
+ return nObjectTypes[i] != nObjectType;
+
+ return true; // all other slots: assume "yes"
+}
+
+void FmXFormShell::checkControlConversionSlotsForCurrentSelection_Lock(weld::Menu& rMenu)
+{
+ for (int i = 0, nCount = rMenu.n_children(); i < nCount; ++i)
+ {
+ // the context is already of a type that corresponds to the entry -> disable
+ OUString sIdent(aConvertSlots[i]);
+ rMenu.set_sensitive(sIdent, canConvertCurrentSelectionToControl_Lock(sIdent));
+ }
+}
+
+void FmXFormShell::LoopGrids_Lock(LoopGridsSync nSync, LoopGridsFlags nFlags)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ Reference< XIndexContainer> xControlModels(m_xActiveForm, UNO_QUERY);
+ if (!xControlModels.is())
+ return;
+
+ for (sal_Int32 i=0; i<xControlModels->getCount(); ++i)
+ {
+ Reference< XPropertySet> xModelSet;
+ xControlModels->getByIndex(i) >>= xModelSet;
+ if (!xModelSet.is())
+ continue;
+
+ if (!::comphelper::hasProperty(FM_PROP_CLASSID, xModelSet))
+ continue;
+ sal_Int16 nClassId = ::comphelper::getINT16(xModelSet->getPropertyValue(FM_PROP_CLASSID));
+ if (FormComponentType::GRIDCONTROL != nClassId)
+ continue;
+
+ if (!::comphelper::hasProperty(FM_PROP_CURSORCOLOR, xModelSet) || !::comphelper::hasProperty(FM_PROP_ALWAYSSHOWCURSOR, xModelSet) || !::comphelper::hasProperty(FM_PROP_DISPLAYSYNCHRON, xModelSet))
+ continue;
+
+ switch (nSync)
+ {
+ case LoopGridsSync::DISABLE_SYNC:
+ {
+ xModelSet->setPropertyValue(FM_PROP_DISPLAYSYNCHRON, Any(false));
+ }
+ break;
+ case LoopGridsSync::FORCE_SYNC:
+ {
+ Any aOldVal( xModelSet->getPropertyValue(FM_PROP_DISPLAYSYNCHRON) );
+ xModelSet->setPropertyValue(FM_PROP_DISPLAYSYNCHRON, Any(true));
+ xModelSet->setPropertyValue(FM_PROP_DISPLAYSYNCHRON, aOldVal);
+ }
+ break;
+ case LoopGridsSync::ENABLE_SYNC:
+ {
+ xModelSet->setPropertyValue(FM_PROP_DISPLAYSYNCHRON, Any(true));
+ }
+ break;
+ }
+
+ if (nFlags & LoopGridsFlags::DISABLE_ROCTRLR)
+ {
+ xModelSet->setPropertyValue(FM_PROP_ALWAYSSHOWCURSOR, Any(false));
+ Reference< XPropertyState> xModelPropState(xModelSet, UNO_QUERY);
+ if (xModelPropState.is())
+ xModelPropState->setPropertyToDefault(FM_PROP_CURSORCOLOR);
+ else
+ xModelSet->setPropertyValue(FM_PROP_CURSORCOLOR, Any()); // this should be the default
+ }
+ }
+}
+
+
+Reference< XControlContainer > FmXFormShell::getControlContainerForView_Lock() const
+{
+ if (impl_checkDisposed_Lock())
+ return nullptr;
+
+ SdrPageView* pPageView = nullptr;
+ if ( m_pShell && m_pShell->GetFormView() )
+ pPageView = m_pShell->GetFormView()->GetSdrPageView();
+
+ Reference< XControlContainer> xControlContainer;
+ if ( pPageView )
+ xControlContainer = pPageView->GetPageWindow(0)->GetControlContainer();
+
+ return xControlContainer;
+}
+
+
+void FmXFormShell::ExecuteTabOrderDialog_Lock(const Reference<XTabControllerModel>& _rxForForm)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ OSL_PRECOND( _rxForForm.is(), "FmXFormShell::ExecuteTabOrderDialog: invalid tabbing model!" );
+ if ( !_rxForForm.is() )
+ return;
+
+ try
+ {
+ Reference< XWindow > xParentWindow;
+ if (m_pShell->GetViewShell())
+ xParentWindow = VCLUnoHelper::GetInterface ( &m_pShell->GetViewShell()->GetViewFrame().GetWindow() );
+
+ Reference< dialogs::XExecutableDialog > xDialog = form::TabOrderDialog::createWithModel(
+ comphelper::getProcessComponentContext(),
+ _rxForForm, getControlContainerForView_Lock(), xParentWindow
+ );
+
+ xDialog->execute();
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "FmXFormShell::ExecuteTabOrderDialog" );
+ }
+}
+
+
+void FmXFormShell::ExecuteSearch_Lock()
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ // a collection of all (logical) forms
+ FmFormArray().swap(m_aSearchForms);
+ ::std::vector< OUString > aContextNames;
+ impl_collectFormSearchContexts_nothrow_Lock(
+ m_pShell->GetCurPage()->GetForms(), u"",
+ m_aSearchForms, aContextNames);
+
+ if ( m_aSearchForms.size() != aContextNames.size() )
+ {
+ SAL_WARN ( "svx.form", "FmXFormShell::ExecuteSearch: nonsense!" );
+ return;
+ }
+
+ // filter out the forms which do not contain valid controls at all
+ {
+ FmFormArray aValidForms;
+ ::std::vector< OUString > aValidContexts;
+ FmFormArray::const_iterator form = m_aSearchForms.begin();
+ ::std::vector< OUString >::const_iterator contextName = aContextNames.begin();
+ for ( ; form != m_aSearchForms.end(); ++form, ++contextName )
+ {
+ FmSearchContext aTestContext;
+ aTestContext.nContext = static_cast< sal_Int16 >( form - m_aSearchForms.begin() );
+ sal_uInt32 nValidControls = OnSearchContextRequest_Lock(aTestContext);
+ if ( nValidControls > 0 )
+ {
+ aValidForms.push_back( *form );
+ aValidContexts.push_back( *contextName );
+ }
+ }
+
+ m_aSearchForms.swap( aValidForms );
+ aContextNames.swap( aValidContexts );
+ }
+
+ if (m_aSearchForms.empty() )
+ {
+ // there are no controls that meet all the conditions for a search
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Warning, VclButtonsType::Ok,
+ SvxResId(RID_STR_NODATACONTROLS)));
+ xBox->run();
+ return;
+ }
+
+ // now I need another 'initial context'
+ sal_Int16 nInitialContext = 0;
+ Reference<XForm> xActiveForm(getActiveForm_Lock());
+ for ( size_t i=0; i<m_aSearchForms.size(); ++i )
+ {
+ if (m_aSearchForms.at(i) == xActiveForm)
+ {
+ nInitialContext = static_cast<sal_Int16>(i);
+ break;
+ }
+ }
+
+ // If the dialog should initially offer the text of the active control,
+ // this must have an XTextComponent interface. An addition, this makes
+ // sense only if the current field is also bound to a table (or whatever) field.
+ OUString strActiveField;
+ OUString strInitialText;
+ // ... this I get from my FormController
+ DBG_ASSERT(m_xActiveController.is(), "FmXFormShell::ExecuteSearch : no active controller !");
+ Reference< XControl> xActiveControl( m_xActiveController->getCurrentControl());
+ if (xActiveControl.is())
+ {
+ // the control can tell me its model ...
+ Reference< XControlModel> xActiveModel( xActiveControl->getModel());
+ DBG_ASSERT(xActiveModel.is(), "FmXFormShell::ExecuteSearch : active control has no model !");
+
+ // I ask the model for the ControlSource property ...
+ Reference< XPropertySet> xProperties(xActiveControl->getModel(), UNO_QUERY);
+ if (::comphelper::hasProperty(FM_PROP_CONTROLSOURCE, xProperties) && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xProperties))
+ {
+ Reference< XPropertySet> xField;
+ xProperties->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
+ if (xField.is()) // (only when the thing is really bound)
+ {
+ // and the control itself for a TextComponent interface (so that I can pick up the text there)
+ Reference< XTextComponent> xText(xActiveControl, UNO_QUERY);
+ if (xText.is())
+ {
+ strActiveField = getLabelName(xProperties);
+ strInitialText = xText->getText();
+ }
+ }
+ }
+ else
+ {
+ // the control itself has no ControlSource, but maybe it is a GridControl
+ Reference< XGrid> xGrid(xActiveControl, UNO_QUERY);
+ if (xGrid.is())
+ {
+ // for strActiveField I need the ControlSource of the column,
+ // for that the columns container, for that the GridPeer
+ Reference< XGridPeer> xGridPeer(xActiveControl->getPeer(), UNO_QUERY);
+ Reference< XIndexAccess> xColumns;
+ if (xGridPeer.is())
+ xColumns = xGridPeer->getColumns();
+
+ sal_Int16 nViewCol = xGrid->getCurrentColumnPosition();
+ sal_Int32 nModelCol = GridView2ModelPos(xColumns, nViewCol);
+ Reference< XPropertySet> xCurrentCol;
+ if(xColumns.is())
+ xColumns->getByIndex(nModelCol) >>= xCurrentCol;
+ if (xCurrentCol.is())
+ strActiveField = ::comphelper::getString(xCurrentCol->getPropertyValue(FM_PROP_LABEL));
+
+ // the text of the current column
+ Reference< XIndexAccess> xColControls(xGridPeer, UNO_QUERY);
+ Reference< XInterface> xCurControl;
+ xColControls->getByIndex(nViewCol) >>= xCurControl;
+ OUString sInitialText;
+ if (IsSearchableControl(xCurControl, &sInitialText))
+ strInitialText = sInitialText;
+ }
+ }
+ }
+
+ // taking care of possible GridControls that I know
+ LoopGrids_Lock(LoopGridsSync::DISABLE_SYNC);
+
+ // Now I am ready for the dialogue.
+ // When the potential deadlocks caused by the use of the solar mutex in
+ // MTs VCLX... classes are eventually cleared, an SM_USETHREAD should be
+ // placed here, because the search in a separate thread is nevertheless
+ // somewhat more fluid. Should be, however, somehow made dependent of the
+ // underlying cursor. DAO for example is not thread-safe.
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractFmSearchDialog> pDialog(
+ pFact->CreateFmSearchDialog(
+ m_pShell->GetViewShell()->GetViewFrame().GetFrameWeld(),
+ strInitialText, aContextNames, nInitialContext,
+ LINK(this, FmXFormShell, OnSearchContextRequest_Lock) ));
+ pDialog->SetActiveField( strActiveField );
+ pDialog->SetFoundHandler(LINK(this, FmXFormShell, OnFoundData_Lock));
+ pDialog->SetCanceledNotFoundHdl(LINK(this, FmXFormShell, OnCanceledNotFound_Lock));
+ pDialog->Execute();
+ pDialog.disposeAndClear();
+
+ // restore GridControls again
+ LoopGrids_Lock(LoopGridsSync::ENABLE_SYNC, LoopGridsFlags::DISABLE_ROCTRLR);
+
+ m_pShell->GetFormView()->UnMarkAll(m_pShell->GetFormView()->GetSdrPageView());
+ // because I marked controls in OnFoundData (if I was there)
+}
+
+
+bool FmXFormShell::GetY2KState_Lock(sal_uInt16& n)
+{
+ if (impl_checkDisposed_Lock())
+ return false;
+
+ if (m_pShell->IsDesignMode())
+ // in the design mode (without active controls) the main document is to take care of it
+ return false;
+
+ Reference<XForm> xForm(getActiveForm_Lock());
+ if (!xForm.is())
+ // no current form (in particular no current control) -> the main document is to take care
+ return false;
+
+ Reference< XRowSet> xDB(xForm, UNO_QUERY);
+ DBG_ASSERT(xDB.is(), "FmXFormShell::GetY2KState : current form has no dbform-interface !");
+
+ Reference< XNumberFormatsSupplier> xSupplier( getNumberFormats(getConnection(xDB)));
+ if (xSupplier.is())
+ {
+ Reference< XPropertySet> xSet(xSupplier->getNumberFormatSettings());
+ if (xSet.is())
+ {
+ try
+ {
+ Any aVal( xSet->getPropertyValue("TwoDigitDateStart") );
+ aVal >>= n;
+ return true;
+ }
+ catch(Exception&)
+ {
+ }
+
+ }
+ }
+ return false;
+}
+
+
+void FmXFormShell::SetY2KState_Lock(sal_uInt16 n)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ Reference<XForm> xActiveForm(getActiveForm_Lock());
+ Reference< XRowSet > xActiveRowSet( xActiveForm, UNO_QUERY );
+ if ( xActiveRowSet.is() )
+ {
+ Reference< XNumberFormatsSupplier > xSupplier( getNumberFormats( getConnection( xActiveRowSet ) ) );
+ if (xSupplier.is())
+ {
+ Reference< XPropertySet> xSet(xSupplier->getNumberFormatSettings());
+ if (xSet.is())
+ {
+ try
+ {
+ xSet->setPropertyValue("TwoDigitDateStart", Any(sal_uInt16(n)));
+ }
+ catch(Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx.form", "");
+ }
+
+ }
+ return;
+ }
+ }
+
+ // no active form found -> iterate through all current forms
+ Reference< XIndexAccess> xCurrentForms( m_xForms);
+ if (!xCurrentForms.is())
+ { // in the alive mode, my forms are not set, but the ones on the page are
+ if (m_pShell->GetCurPage())
+ xCurrentForms = m_pShell->GetCurPage()->GetForms( false );
+ }
+ if (!xCurrentForms.is())
+ return;
+
+ ::comphelper::IndexAccessIterator aIter(xCurrentForms);
+ Reference< XInterface> xCurrentElement( aIter.Next());
+ while (xCurrentElement.is())
+ {
+ // is the current element a DatabaseForm?
+ Reference< XRowSet> xElementAsRowSet( xCurrentElement, UNO_QUERY );
+ if ( xElementAsRowSet.is() )
+ {
+ Reference< XNumberFormatsSupplier > xSupplier( getNumberFormats( getConnection( xElementAsRowSet ) ) );
+ if (!xSupplier.is())
+ continue;
+
+ Reference< XPropertySet> xSet(xSupplier->getNumberFormatSettings());
+ if (xSet.is())
+ {
+ try
+ {
+ xSet->setPropertyValue("TwoDigitDateStart", Any(sal_uInt16(n)));
+ }
+ catch(Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx.form", "");
+ }
+
+ }
+ }
+ xCurrentElement = aIter.Next();
+ }
+}
+
+
+void FmXFormShell::CloseExternalFormViewer_Lock()
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ if (!m_xExternalViewController.is())
+ return;
+
+ Reference< css::frame::XFrame> xExternalViewFrame( m_xExternalViewController->getFrame());
+ Reference< css::frame::XDispatchProvider> xCommLink(xExternalViewFrame, UNO_QUERY);
+ if (!xCommLink.is())
+ return;
+
+ xExternalViewFrame->setComponent(nullptr,nullptr);
+ ::comphelper::disposeComponent(xExternalViewFrame);
+ m_xExternalViewController = nullptr;
+ m_xExtViewTriggerController = nullptr;
+ m_xExternalDisplayedForm = nullptr;
+}
+
+
+Reference<XResultSet> FmXFormShell::getInternalForm_Lock(const Reference<XResultSet>& _xForm) const
+{
+ if (impl_checkDisposed_Lock())
+ return nullptr;
+
+ Reference< runtime::XFormController> xExternalCtrlr(m_xExternalViewController, UNO_QUERY);
+ if (xExternalCtrlr.is() && (_xForm == xExternalCtrlr->getModel()))
+ {
+ DBG_ASSERT(m_xExternalDisplayedForm.is(), "FmXFormShell::getInternalForm : invalid external form !");
+ return m_xExternalDisplayedForm;
+ }
+ return _xForm;
+}
+
+
+Reference<XForm> FmXFormShell::getInternalForm_Lock(const Reference<XForm>& _xForm) const
+{
+ if (impl_checkDisposed_Lock())
+ return nullptr;
+
+ Reference< runtime::XFormController > xExternalCtrlr(m_xExternalViewController, UNO_QUERY);
+ if (xExternalCtrlr.is() && (_xForm == xExternalCtrlr->getModel()))
+ {
+ DBG_ASSERT(m_xExternalDisplayedForm.is(), "FmXFormShell::getInternalForm : invalid external form !");
+ return Reference< XForm>(m_xExternalDisplayedForm, UNO_QUERY);
+ }
+ return _xForm;
+}
+
+
+namespace
+{
+ bool lcl_isNavigationRelevant( sal_Int32 _nWhich )
+ {
+ return ( _nWhich == SID_FM_RECORD_FIRST )
+ || ( _nWhich == SID_FM_RECORD_PREV )
+ || ( _nWhich == SID_FM_RECORD_NEXT )
+ || ( _nWhich == SID_FM_RECORD_LAST )
+ || ( _nWhich == SID_FM_RECORD_NEW );
+ }
+}
+
+
+bool FmXFormShell::IsFormSlotEnabled( sal_Int32 _nSlot, FeatureState* _pCompleteState ) const
+{
+ const svx::ControllerFeatures& rController =
+ lcl_isNavigationRelevant( _nSlot )
+ ? getNavControllerFeatures_Lock()
+ : getActiveControllerFeatures_Lock();
+
+ if ( !_pCompleteState )
+ return rController->isEnabled( _nSlot );
+
+ rController->getState( _nSlot, *_pCompleteState );
+ return _pCompleteState->Enabled;
+}
+
+
+void FmXFormShell::ExecuteFormSlot_Lock( sal_Int32 _nSlot )
+{
+ const svx::ControllerFeatures& rController =
+ lcl_isNavigationRelevant( _nSlot )
+ ? getNavControllerFeatures_Lock()
+ : getActiveControllerFeatures_Lock();
+
+ rController->execute( _nSlot );
+
+ if ( _nSlot != SID_FM_RECORD_UNDO )
+ return;
+
+ // if we're doing an UNDO, *and* if the affected form is the form which we also display
+ // as external view, then we need to reset the controls of the external form, too
+ if (getInternalForm_Lock(getActiveForm_Lock()) != m_xExternalDisplayedForm)
+ return;
+
+ Reference< XIndexAccess > xContainer( m_xExternalDisplayedForm, UNO_QUERY );
+ if ( !xContainer.is() )
+ return;
+
+ Reference< XReset > xReset;
+ for ( sal_Int32 i = 0; i < xContainer->getCount(); ++i )
+ {
+ if ( ( xContainer->getByIndex( i ) >>= xReset ) && xReset.is() )
+ {
+ // no resets on sub forms
+ Reference< XForm > xAsForm( xReset, UNO_QUERY );
+ if ( !xAsForm.is() )
+ xReset->reset();
+ }
+ }
+}
+
+void FmXFormShell::impl_switchActiveControllerListening_Lock(const bool _bListen)
+{
+ if ( !m_xActiveController.is() )
+ return;
+
+ if ( _bListen )
+ m_xActiveController->addEventListener( static_cast<XFormControllerListener*>(this) );
+ else
+ m_xActiveController->removeEventListener( static_cast<XFormControllerListener*>(this) );
+}
+
+void FmXFormShell::setActiveController_Lock(const Reference<runtime::XFormController>& xController, bool _bNoSaveOldContent)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ if (m_bChangingDesignMode)
+ return;
+ DBG_ASSERT(!m_pShell->IsDesignMode(), "only to be used in alive mode");
+
+ // if the routine has been called a second time,
+ // the focus should no longer be transferred
+ if (m_bInActivate)
+ {
+ m_bSetFocus = xController != m_xActiveController;
+ return;
+ }
+
+ if (xController == m_xActiveController)
+ return;
+
+ // switch all nav dispatchers belonging to the form of the current nav controller to 'non active'
+ Reference< XResultSet> xNavigationForm;
+ if (m_xNavigationController.is())
+ xNavigationForm.set(m_xNavigationController->getModel(), UNO_QUERY);
+
+ m_bInActivate = true;
+
+ // check if the 2 controllers serve different forms
+ Reference< XResultSet> xOldForm;
+ if (m_xActiveController.is())
+ xOldForm.set(m_xActiveController->getModel(), UNO_QUERY);
+ Reference< XResultSet> xNewForm;
+ if (xController.is())
+ xNewForm = Reference< XResultSet>(xController->getModel(), UNO_QUERY);
+ xOldForm = getInternalForm_Lock(xOldForm);
+ xNewForm = getInternalForm_Lock(xNewForm);
+
+ bool bDifferentForm = ( xOldForm.get() != xNewForm.get() );
+ bool bNeedSave = bDifferentForm && !_bNoSaveOldContent;
+ // we save the content of the old form if we move to a new form, and saving old content is allowed
+
+ if ( m_xActiveController.is() && bNeedSave )
+ {
+ // save content on change of the controller; a commit has already been executed
+ if ( m_aActiveControllerFeatures->commitCurrentControl() )
+ {
+ m_bSetFocus = true;
+ if ( m_aActiveControllerFeatures->isModifiedRow() )
+ {
+ bool bIsNew = m_aActiveControllerFeatures->isInsertionRow();
+ bool bResult = m_aActiveControllerFeatures->commitCurrentRecord();
+ if ( !bResult && m_bSetFocus )
+ {
+ // if we couldn't save the current record, set the focus back to the
+ // current control
+ Reference< XWindow > xWindow( m_xActiveController->getCurrentControl(), UNO_QUERY );
+ if ( xWindow.is() )
+ xWindow->setFocus();
+ m_bInActivate = false;
+ return;
+ }
+ else if ( bResult && bIsNew )
+ {
+ Reference< XResultSet > xCursor( m_aActiveControllerFeatures->getCursor() );
+ if ( xCursor.is() )
+ {
+ DO_SAFE( xCursor->last(); );
+ }
+ }
+ }
+ }
+ }
+
+ stopListening_Lock();
+
+ impl_switchActiveControllerListening_Lock(false);
+
+ m_aActiveControllerFeatures.dispose();
+ m_xActiveController = xController;
+ if ( m_xActiveController.is() )
+ m_aActiveControllerFeatures.assign( m_xActiveController );
+
+ impl_switchActiveControllerListening_Lock(true);
+
+ if ( m_xActiveController.is() )
+ m_xActiveForm = getInternalForm_Lock(Reference<XForm>(m_xActiveController->getModel(), UNO_QUERY));
+ else
+ m_xActiveForm = nullptr;
+
+ startListening_Lock();
+
+ // activate all dispatchers belonging to form of the new navigation controller
+ xNavigationForm = nullptr;
+ if (m_xNavigationController.is())
+ xNavigationForm.set(m_xNavigationController->getModel(), UNO_QUERY);
+
+ m_bInActivate = false;
+
+ m_pShell->UIFeatureChanged();
+ m_pShell->GetViewShell()->GetViewFrame().GetBindings().InvalidateShell(*m_pShell);
+
+ InvalidateSlot_Lock(SID_FM_FILTER_NAVIGATOR_CONTROL, true);
+}
+
+void FmXFormShell::getCurrentSelection_Lock(InterfaceBag& /* [out] */ _rSelection) const
+{
+ _rSelection = m_aCurrentSelection;
+}
+
+bool FmXFormShell::setCurrentSelectionFromMark_Lock(const SdrMarkList& _rMarkList)
+{
+ m_aLastKnownMarkedControls.clear();
+
+ if ( ( _rMarkList.GetMarkCount() > 0 ) && isControlList( _rMarkList ) )
+ collectInterfacesFromMarkList( _rMarkList, m_aLastKnownMarkedControls );
+
+ return setCurrentSelection_Lock(o3tl::sorted_vector(m_aLastKnownMarkedControls));
+}
+
+
+bool FmXFormShell::selectLastMarkedControls_Lock()
+{
+ return setCurrentSelection_Lock(o3tl::sorted_vector(m_aLastKnownMarkedControls));
+}
+
+
+bool FmXFormShell::setCurrentSelection_Lock( InterfaceBag&& _rSelection )
+{
+ if (impl_checkDisposed_Lock())
+ return false;
+
+ DBG_ASSERT( m_pShell->IsDesignMode(), "FmXFormShell::setCurrentSelection: only to be used in design mode!" );
+
+ if ( _rSelection.empty() && m_aCurrentSelection.empty() )
+ // nothing to do
+ return false;
+
+ if ( _rSelection.size() == m_aCurrentSelection.size() )
+ {
+ InterfaceBag::const_iterator aNew = _rSelection.begin();
+ InterfaceBag::const_iterator aOld = m_aCurrentSelection.begin();
+ for ( ; aNew != _rSelection.end(); ++aNew, ++aOld )
+ {
+ OSL_ENSURE( Reference< XInterface >( *aNew, UNO_QUERY ).get() == aNew->get(), "FmXFormShell::setCurrentSelection: new interface not normalized!" );
+ OSL_ENSURE( Reference< XInterface >( *aOld, UNO_QUERY ).get() == aOld->get(), "FmXFormShell::setCurrentSelection: old interface not normalized!" );
+
+ if ( aNew->get() != aOld->get() )
+ break;
+ }
+
+ if ( aNew == _rSelection.end() )
+ // both bags equal
+ return false;
+ }
+
+ // the following is some strange code to ensure that when you have two grid controls in a document,
+ // only one of them can have a selected column.
+ // TODO: this should happen elsewhere, but not here - shouldn't it?
+ if ( !m_aCurrentSelection.empty() )
+ {
+ Reference< XChild > xCur; if ( m_aCurrentSelection.size() == 1 ) xCur.set(*m_aCurrentSelection.begin(), css::uno::UNO_QUERY);
+ Reference< XChild > xNew; if ( _rSelection.size() == 1 ) xNew.set(*_rSelection.begin(), css::uno::UNO_QUERY);
+
+ // is there nothing to be selected, or the parents differ, and the parent of the current object
+ // is a selection supplier, then deselect
+ if ( xCur.is() && ( !xNew.is() || ( xCur->getParent() != xNew->getParent() ) ) )
+ {
+ Reference< XSelectionSupplier > xSel( xCur->getParent(), UNO_QUERY );
+ if ( xSel.is() )
+ xSel->select( Any() );
+ }
+ }
+
+ m_aCurrentSelection = _rSelection;
+
+ // determine the form which all the selected objects belong to, if any
+ Reference< XForm > xNewCurrentForm;
+ for (const auto& rpSelection : m_aCurrentSelection)
+ {
+ Reference< XForm > xThisRoundsForm( GetForm( rpSelection ) );
+ OSL_ENSURE( xThisRoundsForm.is(), "FmXFormShell::setCurrentSelection: *everything* should belong to a form!" );
+
+ if ( !xNewCurrentForm.is() )
+ { // the first form we encountered
+ xNewCurrentForm = xThisRoundsForm;
+ }
+ else if ( xNewCurrentForm != xThisRoundsForm )
+ { // different forms -> no "current form" at all
+ xNewCurrentForm.clear();
+ break;
+ }
+ }
+
+ if ( !m_aCurrentSelection.empty() )
+ impl_updateCurrentForm_Lock(xNewCurrentForm);
+
+ // ensure some slots are updated
+ for (sal_Int16 i : SelObjectSlotMap)
+ InvalidateSlot_Lock(i, false);
+
+ return true;
+}
+
+
+bool FmXFormShell::isSolelySelected_Lock(const Reference<XInterface>& _rxObject)
+{
+ return ( m_aCurrentSelection.size() == 1 ) && ( *m_aCurrentSelection.begin() == _rxObject );
+}
+
+
+void FmXFormShell::forgetCurrentForm_Lock()
+{
+ if ( !m_xCurrentForm.is() )
+ return;
+
+ // reset ...
+ impl_updateCurrentForm_Lock(nullptr);
+
+ // ... and try finding a new current form
+ // #i88186# / 2008-04-12 / frank.schoenheit@sun.com
+ impl_defaultCurrentForm_nothrow_Lock();
+}
+
+
+void FmXFormShell::impl_updateCurrentForm_Lock(const Reference<XForm>& _rxNewCurForm)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ m_xCurrentForm = _rxNewCurForm;
+
+ // propagate to the FormPage(Impl)
+ FmFormPage* pPage = m_pShell->GetCurPage();
+ if ( pPage )
+ pPage->GetImpl().setCurForm( m_xCurrentForm );
+
+ // ensure the UI which depends on the current form is up-to-date
+ for (sal_Int16 i : DlgSlotMap)
+ InvalidateSlot_Lock(i, false);
+}
+
+
+void FmXFormShell::startListening_Lock()
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ Reference< XRowSet> xDatabaseForm(m_xActiveForm, UNO_QUERY);
+ if (xDatabaseForm.is() && getConnection(xDatabaseForm).is())
+ {
+ Reference< XPropertySet> xActiveFormSet(m_xActiveForm, UNO_QUERY);
+ if (xActiveFormSet.is())
+ {
+ // if there is a data source, then build the listener
+ // TODO: this is strange - shouldn't this depend on a isLoaded instead of
+ // a "has command value"? Finally, the command value only means that it was
+ // intended to be loaded, not that it actually *is* loaded
+ OUString aSource = ::comphelper::getString(xActiveFormSet->getPropertyValue(FM_PROP_COMMAND));
+ if (!aSource.isEmpty())
+ {
+ m_bDatabaseBar = true;
+
+ xActiveFormSet->getPropertyValue(FM_PROP_NAVIGATION) >>= m_eNavigate;
+
+ switch (m_eNavigate)
+ {
+ case NavigationBarMode_PARENT:
+ {
+ // search for the controller via which navigation is possible
+ Reference< XChild> xChild = m_xActiveController;
+ Reference< runtime::XFormController > xParent;
+ while (xChild.is())
+ {
+ xChild.set(xChild->getParent(), UNO_QUERY);
+ xParent.set(xChild, UNO_QUERY);
+ Reference< XPropertySet> xParentSet;
+ if (xParent.is())
+ xParentSet.set(xParent->getModel(), UNO_QUERY);
+ if (xParentSet.is())
+ {
+ xParentSet->getPropertyValue(FM_PROP_NAVIGATION) >>= m_eNavigate;
+ if (m_eNavigate == NavigationBarMode_CURRENT)
+ break;
+ }
+ }
+ m_xNavigationController = xParent;
+ }
+ break;
+
+ case NavigationBarMode_CURRENT:
+ m_xNavigationController = m_xActiveController;
+ break;
+
+ default:
+ m_xNavigationController = nullptr;
+ m_bDatabaseBar = false;
+ }
+
+ m_aNavControllerFeatures.dispose();
+ if ( m_xNavigationController.is() && ( m_xNavigationController != m_xActiveController ) )
+ m_aNavControllerFeatures.assign( m_xNavigationController );
+
+ // because of RecordCount, listen at the controller which controls the navigation
+ Reference< XPropertySet> xNavigationSet;
+ if (m_xNavigationController.is())
+ {
+ xNavigationSet.set(m_xNavigationController->getModel(), UNO_QUERY);
+ if (xNavigationSet.is())
+ xNavigationSet->addPropertyChangeListener(FM_PROP_ROWCOUNT,this);
+ }
+ return;
+ }
+ }
+ }
+
+ m_eNavigate = NavigationBarMode_NONE;
+ m_bDatabaseBar = false;
+ m_xNavigationController = nullptr;
+}
+
+
+void FmXFormShell::stopListening_Lock()
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ Reference< XRowSet> xDatabaseForm(m_xActiveForm, UNO_QUERY);
+ if ( xDatabaseForm.is() )
+ {
+ if (m_xNavigationController.is())
+ {
+ Reference< XPropertySet> xSet(m_xNavigationController->getModel(), UNO_QUERY);
+ if (xSet.is())
+ xSet->removePropertyChangeListener(FM_PROP_ROWCOUNT, this);
+
+ }
+ }
+
+ m_bDatabaseBar = false;
+ m_eNavigate = NavigationBarMode_NONE;
+ m_xNavigationController = nullptr;
+}
+
+void FmXFormShell::ShowSelectionProperties_Lock(bool bShow)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ // if the window is already visible, only update the state
+ bool bHasChild = m_pShell->GetViewShell()->GetViewFrame().HasChildWindow( SID_FM_SHOW_PROPERTIES );
+ if ( bHasChild && bShow )
+ UpdateSlot_Lock(SID_FM_PROPERTY_CONTROL);
+
+ // else toggle state
+ else
+ m_pShell->GetViewShell()->GetViewFrame().ToggleChildWindow(SID_FM_SHOW_PROPERTIES);
+
+ InvalidateSlot_Lock(SID_FM_PROPERTIES, false);
+ InvalidateSlot_Lock(SID_FM_CTL_PROPERTIES, false);
+}
+
+IMPL_LINK(FmXFormShell, OnFoundData_Lock, FmFoundRecordInformation&, rfriWhere, void)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ DBG_ASSERT((rfriWhere.nContext >= 0) && (o3tl::make_unsigned(rfriWhere.nContext) < m_aSearchForms.size()),
+ "FmXFormShell::OnFoundData : invalid context!");
+ Reference< XForm> xForm( m_aSearchForms.at(rfriWhere.nContext));
+ DBG_ASSERT(xForm.is(), "FmXFormShell::OnFoundData : invalid form!");
+
+ Reference< XRowLocate> xCursor(xForm, UNO_QUERY);
+ if (!xCursor.is())
+ return; // what should I do there?
+
+ // to the record
+ try
+ {
+ xCursor->moveToBookmark(rfriWhere.aPosition);
+ }
+ catch(const SQLException&)
+ {
+ OSL_FAIL("Can position on bookmark!");
+ }
+
+ LoopGrids_Lock(LoopGridsSync::FORCE_SYNC);
+
+ // and to the field (for that, I collected the XVclComponent interfaces before the start of the search)
+ SAL_WARN_IF(o3tl::make_unsigned(rfriWhere.nFieldPos) >=
+ m_arrSearchedControls.size(),
+ "svx.form", "FmXFormShell::OnFoundData : invalid index!");
+ SdrObject* pObject = m_arrSearchedControls.at(rfriWhere.nFieldPos);
+
+ m_pShell->GetFormView()->UnMarkAll(m_pShell->GetFormView()->GetSdrPageView());
+ m_pShell->GetFormView()->MarkObj(pObject, m_pShell->GetFormView()->GetSdrPageView());
+
+ FmFormObj* pFormObject = FmFormObj::GetFormObject( pObject );
+ Reference< XControlModel > xControlModel( pFormObject ? pFormObject->GetUnoControlModel() : Reference< XControlModel >() );
+ DBG_ASSERT( xControlModel.is(), "FmXFormShell::OnFoundData: invalid control!" );
+ if ( !xControlModel.is() )
+ return;
+
+ // disable the permanent cursor for the last grid we found a record
+ if (m_xLastGridFound.is() && (m_xLastGridFound != xControlModel))
+ {
+ Reference< XPropertySet> xOldSet(m_xLastGridFound, UNO_QUERY);
+ xOldSet->setPropertyValue(FM_PROP_ALWAYSSHOWCURSOR, Any( false ) );
+ Reference< XPropertyState> xOldSetState(xOldSet, UNO_QUERY);
+ if (xOldSetState.is())
+ xOldSetState->setPropertyToDefault(FM_PROP_CURSORCOLOR);
+ else
+ xOldSet->setPropertyValue(FM_PROP_CURSORCOLOR, Any());
+ }
+
+ // if the field is in a GridControl, I have to additionally go into the corresponding column there
+ sal_Int32 nGridColumn = m_arrRelativeGridColumn[rfriWhere.nFieldPos];
+ if (nGridColumn != -1)
+ { // unfortunately, I have to first get the control again
+ Reference<XControl> xControl(pFormObject ? impl_getControl_Lock(xControlModel, *pFormObject) : Reference<XControl>());
+ Reference< XGrid> xGrid(xControl, UNO_QUERY);
+ DBG_ASSERT(xGrid.is(), "FmXFormShell::OnFoundData : invalid control!");
+ // if one of the asserts fires, I probably did something wrong on building of m_arrSearchedControls
+
+ // enable a permanent cursor for the grid so we can see the found text
+ Reference< XPropertySet> xModelSet(xControlModel, UNO_QUERY);
+ DBG_ASSERT(xModelSet.is(), "FmXFormShell::OnFoundData : invalid control model (no property set) !");
+ xModelSet->setPropertyValue( FM_PROP_ALWAYSSHOWCURSOR, Any( true ) );
+ xModelSet->setPropertyValue( FM_PROP_CURSORCOLOR, Any( COL_LIGHTRED ) );
+ m_xLastGridFound = xControlModel;
+
+ if ( xGrid.is() )
+ xGrid->setCurrentColumnPosition(static_cast<sal_Int16>(nGridColumn));
+ }
+
+ // As the cursor has been repositioned, I have (in positioned) invalidated
+ // my form bar slots. But that does not take effect here unfortunately, as
+ // generally the (modal) search dialog is of course at the top ... So, force ...
+ sal_uInt16 nPos = 0;
+ while (DatabaseSlotMap[nPos])
+ m_pShell->GetViewShell()->GetViewFrame().GetBindings().Update(DatabaseSlotMap[nPos++]);
+ // unfortunately the update goes against the invalidate with only individual slots
+}
+
+IMPL_LINK(FmXFormShell, OnCanceledNotFound_Lock, FmFoundRecordInformation&, rfriWhere, void)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ DBG_ASSERT((rfriWhere.nContext >= 0) && (o3tl::make_unsigned(rfriWhere.nContext) < m_aSearchForms.size()),
+ "FmXFormShell::OnCanceledNotFound : invalid context!");
+ Reference< XForm> xForm( m_aSearchForms.at(rfriWhere.nContext));
+ DBG_ASSERT(xForm.is(), "FmXFormShell::OnCanceledNotFound : invalid form!");
+
+ Reference< XRowLocate> xCursor(xForm, UNO_QUERY);
+ if (!xCursor.is())
+ return; // what should I do there?
+
+ // to the record
+ try
+ {
+ xCursor->moveToBookmark(rfriWhere.aPosition);
+ }
+ catch(const SQLException&)
+ {
+ OSL_FAIL("Can position on bookmark!");
+ }
+
+
+ m_pShell->GetFormView()->UnMarkAll(m_pShell->GetFormView()->GetSdrPageView());
+}
+
+
+IMPL_LINK(FmXFormShell, OnSearchContextRequest_Lock, FmSearchContext&, rfmscContextInfo, sal_uInt32)
+{
+ if (impl_checkDisposed_Lock())
+ return 0;
+
+ DBG_ASSERT(rfmscContextInfo.nContext < static_cast<sal_Int16>(m_aSearchForms.size()), "FmXFormShell::OnSearchContextRequest : invalid parameter !");
+ Reference< XForm> xForm( m_aSearchForms.at(rfmscContextInfo.nContext));
+ DBG_ASSERT(xForm.is(), "FmXFormShell::OnSearchContextRequest : unexpected : invalid context !");
+
+ Reference< XResultSet> xIter(xForm, UNO_QUERY);
+ DBG_ASSERT(xIter.is(), "FmXFormShell::OnSearchContextRequest : unexpected : context has no iterator !");
+
+
+ // assemble the list of fields to involve (that is, the ControlSources of all fields that have such a property)
+ OUString strFieldList, sFieldDisplayNames;
+ m_arrSearchedControls.clear();
+ m_arrRelativeGridColumn.clear();
+
+ // small problem: To mark found fields, I need SdrObjects. To determine which controls
+ // to include in the search, I need Controls (that is, XControl interfaces). So I have
+ // to iterate over one of them and get the other in some way. Unfortunately, there is
+ // no direct connection between the two worlds (except from a GetUnoControl to a
+ // SdrUnoObject, but this requires an OutputDevice I can not do anything with.
+ // However I can get to the Model from the Control and also from the SdrObject, and in
+ // this way the assignment SdrObject<->Control is possible with a double loop.
+ // The alternative to this (ugly but certainly not entirely fixable) solution would be
+ // to renounce the caching of the SdrObjects, which would lead to significant extra
+ // work in OnFoundData (since there I'd have to get the SdrObject first thing every
+ // time). But since OnFoundData is usually called more often than ExecuteSearch, I'll
+ // do that here.
+
+ Reference< XNameAccess> xValidFormFields;
+ Reference< XColumnsSupplier> xSupplyCols(xIter, UNO_QUERY);
+ DBG_ASSERT(xSupplyCols.is(), "FmXFormShell::OnSearchContextRequest : invalid cursor : no columns supplier !");
+ if (xSupplyCols.is())
+ xValidFormFields = xSupplyCols->getColumns();
+ DBG_ASSERT(xValidFormFields.is(), "FmXFormShell::OnSearchContextRequest : form has no fields !");
+
+ // current Page/Controller
+ FmFormPage* pCurrentPage = m_pShell->GetCurPage();
+ assert(pCurrentPage && "FmXFormShell::OnSearchContextRequest : no page !");
+ // Search all SdrControls of this page...
+ OUString sControlSource, aName;
+
+ SdrObjListIter aPageIter( pCurrentPage );
+ while ( aPageIter.IsMore() )
+ {
+ SdrObject* pCurrent = aPageIter.Next();
+ FmFormObj* pFormObject = FmFormObj::GetFormObject( pCurrent );
+ // note that in case pCurrent is a virtual object, pFormObject points to the referenced object
+
+ if ( !pFormObject )
+ continue;
+
+ // the current object's model, in different tastes
+ Reference< XControlModel> xControlModel( pFormObject->GetUnoControlModel() );
+ Reference< XFormComponent > xCurrentFormComponent( xControlModel, UNO_QUERY );
+ DBG_ASSERT( xCurrentFormComponent.is(), "FmXFormShell::OnSearchContextRequest: invalid objects!" );
+ if ( !xCurrentFormComponent.is() )
+ continue;
+
+ // does the component belong to the form which we're interested in?
+ if ( xCurrentFormComponent->getParent() != xForm )
+ continue;
+
+ // ... ask for the ControlSource property
+ SearchableControlIterator iter( xCurrentFormComponent );
+ Reference< XControl> xControl;
+ // the control that has model xControlModel
+ // (the following while can be passed through several times, without the Control
+ // being modified, so I don't have to search every time from scratch)
+
+ Reference< XInterface > xSearchable( iter.Next() );
+ while ( xSearchable.is() )
+ {
+ sControlSource = iter.getCurrentValue();
+ if ( sControlSource.isEmpty() )
+ {
+ // the current element has no ControlSource, so it is a GridControl (that
+ // is the only thing that still permits the SearchableControlIteratore)
+ xControl = impl_getControl_Lock(xControlModel, *pFormObject);
+ DBG_ASSERT(xControl.is(), "FmXFormShell::OnSearchContextRequest : didn't ::std::find a control with requested model !");
+
+ Reference< XGridPeer> xGridPeer;
+ if ( xControl.is() )
+ xGridPeer.set( xControl->getPeer(), UNO_QUERY );
+ do
+ {
+ if (!xGridPeer.is())
+ break;
+
+ Reference< XIndexAccess> xPeerContainer(xGridPeer, UNO_QUERY);
+ if (!xPeerContainer.is())
+ break;
+
+ Reference< XIndexAccess> xModelColumns = xGridPeer->getColumns();
+ DBG_ASSERT(xModelColumns.is(), "FmXFormShell::OnSearchContextRequest : there is a grid control without columns !");
+ // the case 'no columns' should be indicated with an empty container, I think ...
+ DBG_ASSERT(xModelColumns->getCount() >= xPeerContainer->getCount(), "FmXFormShell::OnSearchContextRequest : impossible : have more view than model columns !");
+
+ Reference< XInterface> xCurrentColumn;
+ for (sal_Int32 nViewPos=0; nViewPos<xPeerContainer->getCount(); ++nViewPos)
+ {
+ xPeerContainer->getByIndex(nViewPos) >>= xCurrentColumn;
+ if (!xCurrentColumn.is())
+ continue;
+
+ // can we use this column control for searching ?
+ if (!IsSearchableControl(xCurrentColumn))
+ continue;
+
+ sal_Int32 nModelPos = GridView2ModelPos(xModelColumns, nViewPos);
+ Reference< XPropertySet> xCurrentColModel;
+ xModelColumns->getByIndex(nModelPos) >>= xCurrentColModel;
+ aName = ::comphelper::getString(xCurrentColModel->getPropertyValue(FM_PROP_CONTROLSOURCE));
+ // the cursor has a field matching the control source ?
+ if (xValidFormFields->hasByName(aName))
+ {
+ strFieldList += aName + ";";
+
+ sFieldDisplayNames +=
+ ::comphelper::getString(xCurrentColModel->getPropertyValue(FM_PROP_LABEL)) +
+ ";";
+
+ rfmscContextInfo.arrFields.push_back(xCurrentColumn);
+
+ // and the SdrOject to the Field
+ m_arrSearchedControls.push_back(pCurrent);
+ // the number of the column
+ m_arrRelativeGridColumn.push_back(nViewPos);
+ }
+ }
+ } while (false);
+ }
+ else
+ {
+ if (!sControlSource.isEmpty() && xValidFormFields->hasByName(sControlSource))
+ {
+ // now I need the Control to SdrObject
+ if (!xControl.is())
+ {
+ xControl = impl_getControl_Lock(xControlModel, *pFormObject);
+ DBG_ASSERT(xControl.is(), "FmXFormShell::OnSearchContextRequest : didn't ::std::find a control with requested model !");
+ }
+
+ if (IsSearchableControl(xControl))
+ {
+ // all tests passed -> take along in the list
+ strFieldList += sControlSource + ";";
+
+ // the label which should appear for the control :
+ sFieldDisplayNames +=
+ getLabelName(Reference< XPropertySet>(xControlModel, UNO_QUERY)) +
+ ";";
+
+ // mark the SdrObject (accelerates the treatment in OnFoundData)
+ m_arrSearchedControls.push_back(pCurrent);
+
+ // the number of the column (here a dummy, since it is only interesting for GridControls)
+ m_arrRelativeGridColumn.push_back(-1);
+
+ // and for the formatted search...
+ rfmscContextInfo.arrFields.emplace_back( xControl, UNO_QUERY );
+ }
+ }
+ }
+
+ xSearchable = iter.Next();
+ }
+ }
+
+ strFieldList = comphelper::string::stripEnd(strFieldList, ';');
+ sFieldDisplayNames = comphelper::string::stripEnd(sFieldDisplayNames, ';');
+
+ if (rfmscContextInfo.arrFields.empty())
+ {
+ rfmscContextInfo.arrFields.clear();
+ rfmscContextInfo.xCursor = nullptr;
+ rfmscContextInfo.strUsedFields.clear();
+ return 0;
+ }
+
+ rfmscContextInfo.xCursor = xIter;
+ rfmscContextInfo.strUsedFields = strFieldList;
+ rfmscContextInfo.sFieldDisplayNames = sFieldDisplayNames;
+
+ // 66463 - 31.05.99 - FS
+ // when the cursor is a non-STANDARD RecordMode, set it back
+ Reference< XPropertySet> xCursorSet(rfmscContextInfo.xCursor, UNO_QUERY);
+ Reference< XResultSetUpdate> xUpdateCursor(rfmscContextInfo.xCursor, UNO_QUERY);
+ if (xUpdateCursor.is() && xCursorSet.is())
+ {
+ if (::comphelper::getBOOL(xCursorSet->getPropertyValue(FM_PROP_ISNEW)))
+ xUpdateCursor->moveToCurrentRow();
+ else if (::comphelper::getBOOL(xCursorSet->getPropertyValue(FM_PROP_ISMODIFIED)))
+ xUpdateCursor->cancelRowUpdates();
+ }
+
+ return rfmscContextInfo.arrFields.size();
+}
+
+ // XContainerListener
+
+void SAL_CALL FmXFormShell::elementInserted(const ContainerEvent& evt)
+{
+ SolarMutexGuard g;
+
+ if (impl_checkDisposed_Lock())
+ return;
+
+ // new object to listen to
+ Reference< XInterface> xTemp;
+ evt.Element >>= xTemp;
+ AddElement_Lock(xTemp);
+
+ m_pShell->DetermineForms(true);
+}
+
+
+void SAL_CALL FmXFormShell::elementReplaced(const ContainerEvent& evt)
+{
+ SolarMutexGuard g;
+
+ if (impl_checkDisposed_Lock() )
+ return;
+
+ Reference< XInterface> xTemp;
+ evt.ReplacedElement >>= xTemp;
+ RemoveElement_Lock(xTemp);
+ evt.Element >>= xTemp;
+ AddElement_Lock(xTemp);
+}
+
+
+void SAL_CALL FmXFormShell::elementRemoved(const ContainerEvent& evt)
+{
+ SolarMutexGuard g;
+
+ if (impl_checkDisposed_Lock())
+ return;
+
+ Reference< XInterface> xTemp;
+ evt.Element >>= xTemp;
+ RemoveElement_Lock(xTemp);
+
+ m_pShell->DetermineForms(true);
+}
+
+
+void FmXFormShell::UpdateForms_Lock(bool _bInvalidate)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ Reference< XIndexAccess > xForms;
+
+ FmFormPage* pPage = m_pShell->GetCurPage();
+ if ( pPage && m_pShell->m_bDesignMode )
+ xForms = pPage->GetForms( false );
+
+ if ( m_xForms != xForms )
+ {
+ RemoveElement_Lock( m_xForms );
+ m_xForms = xForms;
+ AddElement_Lock(m_xForms);
+ }
+
+ SolarMutexGuard g;
+ m_pShell->DetermineForms( _bInvalidate );
+}
+
+
+void FmXFormShell::AddElement_Lock(const Reference<XInterface>& _xElement)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+ impl_AddElement_nothrow(_xElement);
+}
+
+void FmXFormShell::impl_AddElement_nothrow(const Reference< XInterface>& Element)
+{
+ // listen at the container
+ const Reference< XIndexContainer> xContainer(Element, UNO_QUERY);
+ if (xContainer.is())
+ {
+ const sal_uInt32 nCount = xContainer->getCount();
+ Reference< XInterface> xElement;
+ for (sal_uInt32 i = 0; i < nCount; ++i)
+ {
+ xElement.set(xContainer->getByIndex(i),UNO_QUERY);
+ impl_AddElement_nothrow(xElement);
+ }
+
+ const Reference< XContainer> xCont(Element, UNO_QUERY);
+ if (xCont.is())
+ xCont->addContainerListener(this);
+ }
+
+ const Reference< css::view::XSelectionSupplier> xSelSupplier(Element, UNO_QUERY);
+ if (xSelSupplier.is())
+ xSelSupplier->addSelectionChangeListener(this);
+}
+
+
+void FmXFormShell::RemoveElement_Lock(const Reference<XInterface>& Element)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+ impl_RemoveElement_nothrow_Lock(Element);
+}
+
+void FmXFormShell::impl_RemoveElement_nothrow_Lock(const Reference<XInterface>& Element)
+{
+ const Reference< css::view::XSelectionSupplier> xSelSupplier(Element, UNO_QUERY);
+ if (xSelSupplier.is())
+ xSelSupplier->removeSelectionChangeListener(this);
+
+ // remove connection to children
+ const Reference< XIndexContainer> xContainer(Element, UNO_QUERY);
+ if (xContainer.is())
+ {
+ const Reference< XContainer> xCont(Element, UNO_QUERY);
+ if (xCont.is())
+ xCont->removeContainerListener(this);
+
+ const sal_uInt32 nCount = xContainer->getCount();
+ Reference< XInterface> xElement;
+ for (sal_uInt32 i = 0; i < nCount; i++)
+ {
+ xElement.set(xContainer->getByIndex(i),UNO_QUERY);
+ impl_RemoveElement_nothrow_Lock(xElement);
+ }
+ }
+
+ auto wasSelectedPos = m_aCurrentSelection.find( Element );
+ if ( wasSelectedPos != m_aCurrentSelection.end() )
+ m_aCurrentSelection.erase( wasSelectedPos );
+}
+
+
+void SAL_CALL FmXFormShell::selectionChanged(const lang::EventObject& rEvent)
+{
+ SolarMutexGuard g;
+
+ if (impl_checkDisposed_Lock())
+ return;
+
+ Reference< XSelectionSupplier > xSupplier( rEvent.Source, UNO_QUERY );
+ Reference< XInterface > xSelObj( xSupplier->getSelection(), UNO_QUERY );
+ // a selection was removed, this can only be done by the shell
+ if ( !xSelObj.is() )
+ return;
+
+ EnableTrackProperties_Lock(false);
+
+ bool bMarkChanged = m_pShell->GetFormView()->checkUnMarkAll(rEvent.Source);
+
+ InterfaceBag aNewSelection;
+ aNewSelection.insert( Reference<XInterface>( xSelObj, UNO_QUERY ) );
+
+ if (setCurrentSelection_Lock(std::move(aNewSelection)) && IsPropBrwOpen_Lock())
+ ShowSelectionProperties_Lock(true);
+
+ EnableTrackProperties_Lock(true);
+
+ if ( bMarkChanged )
+ m_pShell->NotifyMarkListChanged( m_pShell->GetFormView() );
+}
+
+
+IMPL_LINK_NOARG(FmXFormShell, OnTimeOut_Lock, Timer*, void)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ if (m_pShell->IsDesignMode() && m_pShell->GetFormView())
+ SetSelection_Lock(m_pShell->GetFormView()->GetMarkedObjectList());
+}
+
+
+void FmXFormShell::SetSelectionDelayed_Lock()
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ if (m_pShell->IsDesignMode() && IsTrackPropertiesEnabled_Lock() && !m_aMarkTimer.IsActive())
+ m_aMarkTimer.Start();
+}
+
+
+void FmXFormShell::SetSelection_Lock(const SdrMarkList& rMarkList)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ DetermineSelection_Lock(rMarkList);
+ m_pShell->NotifyMarkListChanged(m_pShell->GetFormView());
+}
+
+void FmXFormShell::DetermineSelection_Lock(const SdrMarkList& rMarkList)
+{
+ if (setCurrentSelectionFromMark_Lock(rMarkList) && IsPropBrwOpen_Lock())
+ ShowSelectionProperties_Lock(true);
+}
+
+bool FmXFormShell::IsPropBrwOpen_Lock() const
+{
+ if (impl_checkDisposed_Lock())
+ return false;
+
+ return m_pShell->GetViewShell() &&
+ m_pShell->GetViewShell()->GetViewFrame().HasChildWindow(SID_FM_SHOW_PROPERTIES);
+}
+
+class FmXFormShell::SuspendPropertyTracking
+{
+private:
+ FmXFormShell& m_rShell;
+ bool m_bEnabled;
+
+public:
+ explicit SuspendPropertyTracking( FmXFormShell& _rShell )
+ :m_rShell( _rShell )
+ ,m_bEnabled( false )
+ {
+ if (m_rShell.IsTrackPropertiesEnabled_Lock())
+ {
+ m_rShell.EnableTrackProperties_Lock(false);
+ m_bEnabled = true;
+ }
+ }
+
+ ~SuspendPropertyTracking( )
+ {
+ if ( m_bEnabled ) // note that ( false != m_bEnabled ) implies ( NULL != m_pShell )
+ m_rShell.EnableTrackProperties_Lock(true);
+ }
+};
+
+void FmXFormShell::SetDesignMode_Lock(bool bDesign)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ DBG_ASSERT(m_pShell->GetFormView(), "FmXFormShell::SetDesignMode : invalid call (have no shell or no view) !");
+ m_bChangingDesignMode = true;
+
+ // 67506 - 15.07.99 - FS
+ // if we're switching off the design mode we have to force the property browser to be closed
+ // so it can commit it's changes _before_ we load the forms
+ if (!bDesign)
+ {
+ m_bHadPropertyBrowserInDesignMode = m_pShell->GetViewShell()->GetViewFrame().HasChildWindow(SID_FM_SHOW_PROPERTIES);
+ if (m_bHadPropertyBrowserInDesignMode)
+ m_pShell->GetViewShell()->GetViewFrame().ToggleChildWindow(SID_FM_SHOW_PROPERTIES);
+ }
+
+ FmFormView* pFormView = m_pShell->GetFormView();
+ if (bDesign)
+ {
+ // we are currently filtering, so stop filtering
+ if (m_bFilterMode)
+ stopFiltering_Lock(false);
+
+ // unsubscribe from the objects of my MarkList
+ pFormView->GetImpl()->stopMarkListWatching();
+ }
+ else
+ {
+ m_aMarkTimer.Stop();
+
+ SuspendPropertyTracking aSuspend( *this );
+ pFormView->GetImpl()->saveMarkList();
+ }
+
+ if (bDesign && m_xExternalViewController.is())
+ CloseExternalFormViewer_Lock();
+
+ pFormView->ChangeDesignMode(bDesign);
+
+ // notify listeners
+ FmDesignModeChangedHint aChangedHint( bDesign );
+ m_pShell->Broadcast(aChangedHint);
+
+ m_pShell->m_bDesignMode = bDesign;
+ UpdateForms_Lock(false);
+
+ m_pTextShell->designModeChanged();
+
+ if (bDesign)
+ {
+ SdrMarkList aList;
+ {
+ // during changing the mark list, don't track the selected objects in the property browser
+ SuspendPropertyTracking aSuspend( *this );
+ // restore the marks
+ pFormView->GetImpl()->restoreMarkList( aList );
+ }
+
+ // synchronize with the restored mark list
+ if ( aList.GetMarkCount() )
+ SetSelection_Lock(aList);
+ }
+ else
+ {
+ // subscribe to the model of the view (so that I'm informed when someone deletes
+ // during the alive mode controls that I had saved in the saveMarklist (60343)
+ pFormView->GetImpl()->startMarkListWatching();
+ }
+
+ m_pShell->UIFeatureChanged();
+
+ // 67506 - 15.07.99 - FS
+ if (bDesign && m_bHadPropertyBrowserInDesignMode)
+ {
+ // The UIFeatureChanged performs an update (a check of the available features) asynchronously.
+ // So we can't call ShowSelectionProperties directly as the according feature isn't enabled yet.
+ // That's why we use an asynchron execution on the dispatcher.
+ // (And that's why this has to be done AFTER the UIFeatureChanged.)
+ m_pShell->GetViewShell()->GetViewFrame().GetDispatcher()->Execute( SID_FM_SHOW_PROPERTY_BROWSER, SfxCallMode::ASYNCHRON );
+ }
+ m_bChangingDesignMode = false;
+}
+
+Reference< XControl> FmXFormShell::impl_getControl_Lock(const Reference<XControlModel>& i_rxModel, const FmFormObj& i_rKnownFormObj)
+{
+ if (impl_checkDisposed_Lock())
+ return nullptr;
+
+ Reference< XControl > xControl;
+ try
+ {
+ Reference< XControlContainer> xControlContainer(getControlContainerForView_Lock(), UNO_SET_THROW);
+
+ const Sequence< Reference< XControl > > seqControls( xControlContainer->getControls() );
+ // ... that I can then search
+ for (Reference< XControl > const & control : seqControls)
+ {
+ xControl.set( control, UNO_SET_THROW );
+ Reference< XControlModel > xCurrentModel( xControl->getModel() );
+ if ( xCurrentModel == i_rxModel )
+ break;
+ xControl.clear();
+ }
+
+ if ( !xControl.is() )
+ {
+ // fallback (some controls might not have been created, yet, since they were never visible so far)
+ Reference< XControl > xContainerControl( xControlContainer, UNO_QUERY_THROW );
+ const vcl::Window* pContainerWindow = VCLUnoHelper::GetWindow( xContainerControl->getPeer() );
+ ENSURE_OR_THROW( pContainerWindow, "unexpected control container implementation" );
+
+ const SdrView* pSdrView = m_pShell ? m_pShell->GetFormView() : nullptr;
+ ENSURE_OR_THROW( pSdrView, "no current view" );
+
+ xControl.set( i_rKnownFormObj.GetUnoControl( *pSdrView, *pContainerWindow->GetOutDev() ), UNO_SET_THROW );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ OSL_ENSURE( xControl.is(), "FmXFormShell::impl_getControl: no control found!" );
+ return xControl;
+}
+
+// note: _out_rForms is a member so needs lock
+void FmXFormShell::impl_collectFormSearchContexts_nothrow_Lock( const Reference<XInterface>& _rxStartingPoint,
+ std::u16string_view _rCurrentLevelPrefix, FmFormArray& _out_rForms, ::std::vector< OUString >& _out_rNames )
+{
+ try
+ {
+ Reference< XIndexAccess> xContainer( _rxStartingPoint, UNO_QUERY );
+ if ( !xContainer.is() )
+ return;
+
+ sal_Int32 nCount( xContainer->getCount() );
+ if ( nCount == 0 )
+ return;
+
+ OUString sCurrentFormName;
+ OUStringBuffer aNextLevelPrefix;
+ for ( sal_Int32 i=0; i<nCount; ++i )
+ {
+ // is the current child a form?
+ Reference< XForm > xCurrentAsForm( xContainer->getByIndex(i), UNO_QUERY );
+ if ( !xCurrentAsForm.is() )
+ continue;
+
+ Reference< XNamed > xNamed( xCurrentAsForm, UNO_QUERY_THROW );
+ sCurrentFormName = xNamed->getName();
+
+ // the name of the current form
+ OUString sCompleteCurrentName( sCurrentFormName );
+ if ( !_rCurrentLevelPrefix.empty() )
+ {
+ sCompleteCurrentName += OUString::Concat(" (") + _rCurrentLevelPrefix + ")";
+ }
+
+ // the prefix for the next level
+ aNextLevelPrefix = _rCurrentLevelPrefix;
+ if ( !_rCurrentLevelPrefix.empty() )
+ aNextLevelPrefix.append( '/' );
+ aNextLevelPrefix.append( sCurrentFormName );
+
+ // remember both the form and its "display name"
+ _out_rForms.push_back( xCurrentAsForm );
+ _out_rNames.push_back( sCompleteCurrentName );
+
+ // and descend
+ impl_collectFormSearchContexts_nothrow_Lock(
+ xCurrentAsForm, aNextLevelPrefix,
+ _out_rForms, _out_rNames);
+ aNextLevelPrefix.setLength(0);
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+void FmXFormShell::startFiltering_Lock()
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ // setting all forms in filter mode
+ FmXFormView* pXView = m_pShell->GetFormView()->GetImpl();
+
+ // if the active controller is our external one we have to use the trigger controller
+ Reference< XControlContainer> xContainer;
+ if (getActiveController_Lock() == m_xExternalViewController)
+ {
+ DBG_ASSERT(m_xExtViewTriggerController.is(), "FmXFormShell::startFiltering : inconsistent : active external controller, but no one triggered this !");
+ xContainer = m_xExtViewTriggerController->getContainer();
+ }
+ else
+ xContainer = getActiveController_Lock()->getContainer();
+
+ rtl::Reference< FormViewPageWindowAdapter > pAdapter = pXView->findWindow( xContainer );
+ if ( pAdapter.is() )
+ {
+ const ::std::vector< Reference< runtime::XFormController> >& rControllerList = pAdapter->GetList();
+ for (const auto& rpController : rControllerList)
+ {
+ Reference< XModeSelector> xModeSelector(rpController, UNO_QUERY);
+ if (xModeSelector.is())
+ xModeSelector->setMode( "FilterMode" );
+ }
+ }
+
+ m_bFilterMode = true;
+
+ m_pShell->UIFeatureChanged();
+ SfxViewFrame& rViewFrame = m_pShell->GetViewShell()->GetViewFrame();
+ rViewFrame.GetBindings().InvalidateShell( *m_pShell );
+
+ if ( rViewFrame.KnowsChildWindow( SID_FM_FILTER_NAVIGATOR )
+ && !rViewFrame.HasChildWindow( SID_FM_FILTER_NAVIGATOR )
+ )
+ {
+ rViewFrame.ToggleChildWindow( SID_FM_FILTER_NAVIGATOR );
+ }
+}
+
+static void saveFilter(const Reference< runtime::XFormController >& _rxController)
+{
+ Reference< XPropertySet> xFormAsSet(_rxController->getModel(), UNO_QUERY);
+ Reference< XPropertySet> xControllerAsSet(_rxController, UNO_QUERY);
+
+ // call the subcontroller
+ Reference< runtime::XFormController > xController;
+ for (sal_Int32 i = 0, nCount = _rxController->getCount(); i < nCount; ++i)
+ {
+ _rxController->getByIndex(i) >>= xController;
+ saveFilter(xController);
+ }
+
+ try
+ {
+
+ xFormAsSet->setPropertyValue(FM_PROP_FILTER, xControllerAsSet->getPropertyValue(FM_PROP_FILTER));
+ xFormAsSet->setPropertyValue(FM_PROP_APPLYFILTER, Any( true ) );
+ }
+ catch (const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+}
+
+
+void FmXFormShell::stopFiltering_Lock(bool bSave)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ m_bFilterMode = false;
+
+ FmXFormView* pXView = m_pShell->GetFormView()->GetImpl();
+
+ // if the active controller is our external one we have to use the trigger controller
+ Reference< XControlContainer> xContainer;
+ if (getActiveController_Lock() == m_xExternalViewController)
+ {
+ DBG_ASSERT(m_xExtViewTriggerController.is(), "FmXFormShell::stopFiltering : inconsistent : active external controller, but no one triggered this !");
+ xContainer = m_xExtViewTriggerController->getContainer();
+ }
+ else
+ xContainer = getActiveController_Lock()->getContainer();
+
+ rtl::Reference< FormViewPageWindowAdapter > pAdapter = pXView->findWindow(xContainer);
+ if ( pAdapter.is() )
+ {
+ const ::std::vector< Reference< runtime::XFormController > >& rControllerList = pAdapter->GetList();
+ ::std::vector < OUString > aOriginalFilters;
+ ::std::vector < bool > aOriginalApplyFlags;
+
+ if (bSave)
+ {
+ for (const auto& rpController : rControllerList)
+ {
+ // remember the current filter settings in case we're going to reload the forms below (which may fail)
+ try
+ {
+ Reference< XPropertySet > xFormAsSet(rpController->getModel(), UNO_QUERY);
+ aOriginalFilters.push_back(::comphelper::getString(xFormAsSet->getPropertyValue(FM_PROP_FILTER)));
+ aOriginalApplyFlags.push_back(::comphelper::getBOOL(xFormAsSet->getPropertyValue(FM_PROP_APPLYFILTER)));
+ }
+ catch(Exception&)
+ {
+ OSL_FAIL("FmXFormShell::stopFiltering : could not get the original filter !");
+ // put dummies into the arrays so the they have the right size
+
+ if (aOriginalFilters.size() == aOriginalApplyFlags.size())
+ // the first getPropertyValue failed -> use two dummies
+ aOriginalFilters.emplace_back( );
+ aOriginalApplyFlags.push_back( false );
+ }
+ saveFilter(rpController);
+ }
+ }
+ for (const auto& rController : rControllerList)
+ {
+
+ Reference< XModeSelector> xModeSelector(rController, UNO_QUERY);
+ if (xModeSelector.is())
+ xModeSelector->setMode( "DataMode" );
+ }
+ if (bSave) // execute the filter
+ {
+ const ::std::vector< Reference< runtime::XFormController > > & rControllers = pAdapter->GetList();
+ for (::std::vector< Reference< runtime::XFormController > > ::const_iterator j = rControllers.begin();
+ j != rControllers.end(); ++j)
+ {
+ Reference< XLoadable> xReload((*j)->getModel(), UNO_QUERY);
+ if (!xReload.is())
+ continue;
+ Reference< XPropertySet > xFormSet(xReload, UNO_QUERY);
+
+ try
+ {
+ xReload->reload();
+ }
+ catch(Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx.form", "");
+ }
+
+ if (!isRowSetAlive(xFormSet))
+ { // something went wrong -> restore the original state
+ OUString sOriginalFilter = aOriginalFilters[ j - rControllers.begin() ];
+ bool bOriginalApplyFlag = aOriginalApplyFlags[ j - rControllers.begin() ];
+ try
+ {
+ xFormSet->setPropertyValue(FM_PROP_FILTER, Any(sOriginalFilter));
+ xFormSet->setPropertyValue(FM_PROP_APPLYFILTER, Any(bOriginalApplyFlag));
+ xReload->reload();
+ }
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ }
+ }
+ }
+
+ m_pShell->UIFeatureChanged();
+ m_pShell->GetViewShell()->GetViewFrame().GetBindings().InvalidateShell(*m_pShell);
+}
+
+
+void FmXFormShell::CreateExternalView_Lock()
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ DBG_ASSERT(m_xAttachedFrame.is(), "FmXFormShell::CreateExternalView : no frame !");
+
+ // the frame the external view is displayed in
+ bool bAlreadyExistent = m_xExternalViewController.is();
+ Reference< css::frame::XFrame> xExternalViewFrame;
+
+ Reference<runtime::XFormController> xCurrentNavController(getNavController_Lock());
+ // the creation of the "partwindow" may cause a deactivate of the document which will result in our nav controller to be set to NULL
+
+ // _first_ check if we have any valid fields we can use for the grid view
+ // FS - 21.10.99 - 69219
+ {
+ FmXBoundFormFieldIterator aModelIterator(xCurrentNavController->getModel());
+ bool bHaveUsableControls = false;
+ for (;;)
+ {
+ Reference< XPropertySet> xCurrentModelSet(aModelIterator.Next(), UNO_QUERY);
+ if (!xCurrentModelSet.is())
+ break;
+ // the FmXBoundFormFieldIterator only supplies controls with a valid control source
+ // so we just have to check the field type
+ sal_Int16 nClassId = ::comphelper::getINT16(xCurrentModelSet->getPropertyValue(FM_PROP_CLASSID));
+ switch (nClassId)
+ {
+ case FormComponentType::IMAGECONTROL:
+ case FormComponentType::CONTROL:
+ continue;
+ }
+ bHaveUsableControls = true;
+ break;
+ }
+
+ if (!bHaveUsableControls)
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Warning, VclButtonsType::Ok,
+ SvxResId(RID_STR_NOCONTROLS_FOR_EXTERNALDISPLAY)));
+ xBox->run();
+ return;
+ }
+ }
+
+ // load the component for external form views
+ if (!bAlreadyExistent)
+ {
+ OUString sFrameName("_beamer");
+ URL aWantToDispatch;
+ aWantToDispatch.Complete = FMURL_COMPONENT_FORMGRIDVIEW;
+
+ Reference< css::frame::XDispatchProvider> xProv(m_xAttachedFrame, UNO_QUERY);
+ Reference< css::frame::XDispatch> xDisp;
+ if (xProv.is())
+ xDisp = xProv->queryDispatch(aWantToDispatch, sFrameName,
+ css::frame::FrameSearchFlag::CHILDREN | css::frame::FrameSearchFlag::CREATE);
+ if (xDisp.is())
+ {
+ xDisp->dispatch(aWantToDispatch, Sequence< PropertyValue>());
+ }
+
+ // with this the component should be loaded, now search the frame where it resides in
+ xExternalViewFrame = m_xAttachedFrame->findFrame(sFrameName, css::frame::FrameSearchFlag::CHILDREN);
+ if (xExternalViewFrame.is())
+ {
+ m_xExternalViewController = xExternalViewFrame->getController();
+ if (m_xExternalViewController.is())
+ m_xExternalViewController->addEventListener(static_cast<XEventListener*>(static_cast<XPropertyChangeListener*>(this)));
+ }
+ }
+ else
+ {
+ xExternalViewFrame = m_xExternalViewController->getFrame();
+ Reference< css::frame::XDispatchProvider> xCommLink(xExternalViewFrame, UNO_QUERY);
+
+ // if we display the active form we interpret the slot as "remove it"
+ Reference< XForm> xCurrentModel(xCurrentNavController->getModel(), UNO_QUERY);
+ if ((xCurrentModel == m_xExternalDisplayedForm) || (getInternalForm_Lock(xCurrentModel) == m_xExternalDisplayedForm))
+ {
+ if (m_xExternalViewController == getActiveController_Lock())
+ {
+ Reference< runtime::XFormController > xAsFormController( m_xExternalViewController, UNO_QUERY );
+ ControllerFeatures aHelper( xAsFormController );
+ (void)aHelper->commitCurrentControl();
+ }
+
+ Reference< runtime::XFormController > xNewController(m_xExtViewTriggerController);
+ CloseExternalFormViewer_Lock();
+ setActiveController_Lock(xNewController);
+ return;
+ }
+
+ URL aClearURL;
+ aClearURL.Complete = FMURL_GRIDVIEW_CLEARVIEW;
+
+ Reference< css::frame::XDispatch> xClear( xCommLink->queryDispatch(aClearURL, OUString(), 0));
+ if (xClear.is())
+ xClear->dispatch(aClearURL, Sequence< PropertyValue>());
+ }
+
+ // TODO: We need an interceptor at the xSupplier, which forwards all queryDispatch requests to the FormController
+ // instance for which this "external view" was triggered
+
+ // get the dispatch interface of the frame so we can communicate (interceptable) with the controller
+ Reference< css::frame::XDispatchProvider> xCommLink(xExternalViewFrame, UNO_QUERY);
+
+ if (m_xExternalViewController.is())
+ {
+ DBG_ASSERT(xCommLink.is(), "FmXFormShell::CreateExternalView : the component doesn't have the necessary interfaces !");
+ // collect the dispatchers we will need
+ URL aAddColumnURL;
+ aAddColumnURL.Complete = FMURL_GRIDVIEW_ADDCOLUMN;
+ Reference< css::frame::XDispatch> xAddColumnDispatch( xCommLink->queryDispatch(aAddColumnURL, OUString(), 0));
+ URL aAttachURL;
+ aAttachURL.Complete = FMURL_GRIDVIEW_ATTACHTOFORM;
+ Reference< css::frame::XDispatch> xAttachDispatch( xCommLink->queryDispatch(aAttachURL, OUString(), 0));
+
+ if (xAddColumnDispatch.is() && xAttachDispatch.is())
+ {
+ DBG_ASSERT(xCurrentNavController.is(), "FmXFormShell::CreateExternalView : invalid call : have no nav controller !");
+ // first : dispatch the descriptions for the columns to add
+ sal_Int16 nAddedColumns = 0;
+
+ // for radio buttons we need some special structures
+ typedef std::map< OUString, Sequence< OUString> > MapUString2UstringSeq;
+ typedef std::map< OUString, OUString > FmMapUString2UString;
+ typedef std::map< OUString, sal_Int16 > FmMapUString2Int16;
+
+ MapUString2UstringSeq aRadioValueLists;
+ MapUString2UstringSeq aRadioListSources;
+ FmMapUString2UString aRadioControlSources;
+ FmMapUString2Int16 aRadioPositions;
+
+ FmXBoundFormFieldIterator aModelIterator(xCurrentNavController->getModel());
+ OUString sColumnType,aGroupName,sControlSource;
+ Sequence< Property> aProps;
+ for (;;)
+ {
+ Reference< XPropertySet> xCurrentModelSet(aModelIterator.Next(), UNO_QUERY);
+ if (!xCurrentModelSet.is())
+ break;
+ OSL_ENSURE(xCurrentModelSet.is(),"xCurrentModelSet is null!");
+ // create a description of the column to be created
+ // first : determine it's type
+
+ sal_Int16 nClassId = ::comphelper::getINT16(xCurrentModelSet->getPropertyValue(FM_PROP_CLASSID));
+ switch (nClassId)
+ {
+ case FormComponentType::RADIOBUTTON:
+ {
+ // get the label of the button (this is the access key for our structures)
+ aGroupName = getLabelName(xCurrentModelSet);
+
+ // add the reference value of the radio button to the list source sequence
+ Sequence< OUString>& aThisGroupLabels = aRadioListSources[aGroupName];
+ sal_Int32 nNewSizeL = aThisGroupLabels.getLength() + 1;
+ aThisGroupLabels.realloc(nNewSizeL);
+ aThisGroupLabels.getArray()[nNewSizeL - 1] = ::comphelper::getString(xCurrentModelSet->getPropertyValue(FM_PROP_REFVALUE));
+
+ // add the label to the value list sequence
+ Sequence< OUString>& aThisGroupControlSources = aRadioValueLists[aGroupName];
+ sal_Int32 nNewSizeC = aThisGroupControlSources.getLength() + 1;
+ aThisGroupControlSources.realloc(nNewSizeC);
+ aThisGroupControlSources.getArray()[nNewSizeC - 1] = ::comphelper::getString(xCurrentModelSet->getPropertyValue(FM_PROP_LABEL));
+
+ // remember the controls source of the radio group
+ sControlSource = ::comphelper::getString(xCurrentModelSet->getPropertyValue(FM_PROP_CONTROLSOURCE));
+ if (aRadioControlSources.find(aGroupName) == aRadioControlSources.end())
+ aRadioControlSources[aGroupName] = sControlSource;
+#ifdef DBG_UTIL
+ else
+ DBG_ASSERT(aRadioControlSources[aGroupName] == sControlSource,
+ "FmXFormShell::CreateExternalView : inconsistent radio buttons detected !");
+ // (radio buttons with the same name should have the same control source)
+#endif
+ // remember the position within the columns
+ if (aRadioPositions.find(aGroupName) == aRadioPositions.end())
+ aRadioPositions[aGroupName] = nAddedColumns;
+
+ // any further handling is done below
+ }
+ continue;
+
+ case FormComponentType::IMAGECONTROL:
+ case FormComponentType::CONTROL:
+ // no grid columns for these types (though they have a control source)
+ continue;
+ case FormComponentType::CHECKBOX:
+ sColumnType = FM_COL_CHECKBOX; break;
+ case FormComponentType::LISTBOX:
+ sColumnType = FM_COL_LISTBOX; break;
+ case FormComponentType::COMBOBOX:
+ sColumnType = FM_COL_COMBOBOX; break;
+ case FormComponentType::DATEFIELD:
+ sColumnType = FM_COL_DATEFIELD; break;
+ case FormComponentType::TIMEFIELD:
+ sColumnType = FM_COL_TIMEFIELD; break;
+ case FormComponentType::NUMERICFIELD:
+ sColumnType = FM_COL_NUMERICFIELD; break;
+ case FormComponentType::CURRENCYFIELD:
+ sColumnType = FM_COL_CURRENCYFIELD; break;
+ case FormComponentType::PATTERNFIELD:
+ sColumnType = FM_COL_PATTERNFIELD; break;
+
+ case FormComponentType::TEXTFIELD:
+ {
+ sColumnType = FM_COL_TEXTFIELD;
+ // we know at least two different controls which are TextFields : the basic edit field and the formatted
+ // field. we distinguish them by their service name
+ Reference< lang::XServiceInfo> xInfo(xCurrentModelSet, UNO_QUERY);
+ if (xInfo.is())
+ {
+ SdrObjKind nObjectType = getControlTypeByObject(xInfo);
+ if (SdrObjKind::FormFormattedField == nObjectType)
+ sColumnType = FM_COL_FORMATTEDFIELD;
+ }
+ }
+ break;
+ default:
+ sColumnType = FM_COL_TEXTFIELD; break;
+ }
+
+ const sal_Int16 nDispatchArgs = 3;
+ Sequence< PropertyValue> aDispatchArgs(nDispatchArgs);
+ PropertyValue* pDispatchArgs = aDispatchArgs.getArray();
+
+ // properties describing "meta data" about the column
+ // the type
+ pDispatchArgs->Name = FMARG_ADDCOL_COLUMNTYPE;
+ pDispatchArgs->Value <<= sColumnType;
+ ++pDispatchArgs;
+
+ // the pos : append the col
+ pDispatchArgs->Name = FMARG_ADDCOL_COLUMNPOS;
+ pDispatchArgs->Value <<= nAddedColumns;
+ ++pDispatchArgs;
+
+ // the properties to forward to the new column
+ Sequence< PropertyValue> aColumnProps(1);
+ PropertyValue* pColumnProps = aColumnProps.getArray();
+
+ // the label
+ pColumnProps->Name = FM_PROP_LABEL;
+ pColumnProps->Value <<= getLabelName(xCurrentModelSet);
+ ++pColumnProps;
+
+ // for all other props : transfer them
+ Reference< XPropertySetInfo> xControlModelInfo( xCurrentModelSet->getPropertySetInfo());
+ DBG_ASSERT(xControlModelInfo.is(), "FmXFormShell::CreateExternalView : the control model has no property info ! This will crash !");
+ aProps = xControlModelInfo->getProperties();
+
+ // realloc the control description sequence
+ sal_Int32 nExistentDescs = pColumnProps - aColumnProps.getArray();
+ aColumnProps.realloc(nExistentDescs + aProps.getLength());
+ pColumnProps = aColumnProps.getArray() + nExistentDescs;
+
+ for (const Property& rProp : std::as_const(aProps))
+ {
+ if (rProp.Name == FM_PROP_LABEL)
+ // already set
+ continue;
+ if (rProp.Name == FM_PROP_DEFAULTCONTROL)
+ // allow the column's own "default control"
+ continue;
+ if (rProp.Attributes & PropertyAttribute::READONLY)
+ // assume that properties which are readonly for the control are ro for the column to be created, too
+ continue;
+
+ pColumnProps->Name = rProp.Name;
+ pColumnProps->Value = xCurrentModelSet->getPropertyValue(rProp.Name);
+ ++pColumnProps;
+ }
+ aColumnProps.realloc(pColumnProps - aColumnProps.getArray());
+
+ // columns props are a dispatch argument
+ pDispatchArgs->Name = "ColumnProperties"; // TODO : fmurl.*
+ pDispatchArgs->Value <<= aColumnProps;
+ ++pDispatchArgs;
+ DBG_ASSERT(nDispatchArgs == (pDispatchArgs - aDispatchArgs.getConstArray()),
+ "FmXFormShell::CreateExternalView : forgot to adjust nDispatchArgs ?");
+
+ // dispatch the "add column"
+ xAddColumnDispatch->dispatch(aAddColumnURL, aDispatchArgs);
+ ++nAddedColumns;
+ }
+
+ // now for the radio button handling
+ sal_Int16 nOffset(0);
+ // properties describing the "direct" column properties
+ const sal_Int16 nListBoxDescription = 6;
+ Sequence< PropertyValue> aListBoxDescription(nListBoxDescription);
+ for (const auto& rCtrlSource : aRadioControlSources)
+ {
+ PropertyValue* pListBoxDescription = aListBoxDescription.getArray();
+ // label
+ pListBoxDescription->Name = FM_PROP_LABEL;
+ pListBoxDescription->Value <<= rCtrlSource.first;
+ ++pListBoxDescription;
+
+ // control source
+ pListBoxDescription->Name = FM_PROP_CONTROLSOURCE;
+ pListBoxDescription->Value <<= rCtrlSource.second;
+ ++pListBoxDescription;
+
+ // bound column
+ pListBoxDescription->Name = FM_PROP_BOUNDCOLUMN;
+ pListBoxDescription->Value <<= sal_Int16(1);
+ ++pListBoxDescription;
+
+ // content type
+ pListBoxDescription->Name = FM_PROP_LISTSOURCETYPE;
+ pListBoxDescription->Value <<= ListSourceType_VALUELIST;
+ ++pListBoxDescription;
+
+ // list source
+ MapUString2UstringSeq::const_iterator aCurrentListSource = aRadioListSources.find(rCtrlSource.first);
+ DBG_ASSERT(aCurrentListSource != aRadioListSources.end(),
+ "FmXFormShell::CreateExternalView : inconsistent radio descriptions !");
+ pListBoxDescription->Name = FM_PROP_LISTSOURCE;
+ pListBoxDescription->Value <<= (*aCurrentListSource).second;
+ ++pListBoxDescription;
+
+ // value list
+ MapUString2UstringSeq::const_iterator aCurrentValueList = aRadioValueLists.find(rCtrlSource.first);
+ assert(aCurrentValueList != aRadioValueLists.end() && "FmXFormShell::CreateExternalView : inconsistent radio descriptions !");
+ pListBoxDescription->Name = FM_PROP_STRINGITEMLIST;
+ pListBoxDescription->Value <<= (*aCurrentValueList).second;
+ ++pListBoxDescription;
+
+ DBG_ASSERT(nListBoxDescription == (pListBoxDescription - aListBoxDescription.getConstArray()),
+ "FmXFormShell::CreateExternalView : forgot to adjust nListBoxDescription ?");
+
+ // properties describing the column "meta data"
+ const sal_Int16 nDispatchArgs = 3;
+ Sequence< PropertyValue> aDispatchArgs(nDispatchArgs);
+ PropertyValue* pDispatchArgs = aDispatchArgs.getArray();
+
+ // column type : listbox
+ pDispatchArgs->Name = FMARG_ADDCOL_COLUMNTYPE;
+ pDispatchArgs->Value <<= OUString(FM_COL_LISTBOX);
+// pDispatchArgs->Value <<= (OUString)FM_COL_LISTBOX;
+ ++pDispatchArgs;
+
+ // column position
+ pDispatchArgs->Name = FMARG_ADDCOL_COLUMNPOS;
+ FmMapUString2Int16::const_iterator aOffset = aRadioPositions.find(rCtrlSource.first);
+ assert(aOffset != aRadioPositions.end() && "FmXFormShell::CreateExternalView : inconsistent radio descriptions !");
+ sal_Int16 nPosition = (*aOffset).second;
+ nPosition = nPosition + nOffset;
+ // we already inserted nOffset additional columns...
+ pDispatchArgs->Value <<= nPosition;
+ ++pDispatchArgs;
+
+ // the
+ pDispatchArgs->Name = "ColumnProperties"; // TODO : fmurl.*
+ pDispatchArgs->Value <<= aListBoxDescription;
+ ++pDispatchArgs;
+ DBG_ASSERT(nDispatchArgs == (pDispatchArgs - aDispatchArgs.getConstArray()),
+ "FmXFormShell::CreateExternalView : forgot to adjust nDispatchArgs ?");
+
+ // dispatch the "add column"
+ xAddColumnDispatch->dispatch(aAddColumnURL, aDispatchArgs);
+ ++nAddedColumns;
+ ++nOffset;
+ }
+
+
+ DBG_ASSERT(nAddedColumns > 0, "FmXFormShell::CreateExternalView : no controls (inconsistent) !");
+ // we should have checked if we have any usable controls (see above).
+
+ // "load" the "form" of the external view
+ PropertyValue aArg;
+ aArg.Name = FMARG_ATTACHTO_MASTERFORM;
+ Reference< XResultSet> xForm(xCurrentNavController->getModel(), UNO_QUERY);
+ aArg.Value <<= xForm;
+
+ m_xExternalDisplayedForm = xForm;
+ // do this before dispatching the "attach" command, as the attach may result in a call to our queryDispatch (for the FormSlots)
+ // which needs the m_xExternalDisplayedForm
+
+ xAttachDispatch->dispatch(aAttachURL, Sequence< PropertyValue>(&aArg, 1));
+
+ m_xExtViewTriggerController = xCurrentNavController;
+
+ // we want to know modifications done in the external view
+ // if the external controller is a XFormController we can use all our default handlings for it
+ Reference< runtime::XFormController > xFormController( m_xExternalViewController, UNO_QUERY );
+ OSL_ENSURE( xFormController.is(), "FmXFormShell::CreateExternalView:: invalid external view controller!" );
+ if (xFormController.is())
+ xFormController->addActivateListener(static_cast<XFormControllerListener*>(this));
+ }
+ }
+#ifdef DBG_UTIL
+ else
+ {
+ OSL_FAIL("FmXFormShell::CreateExternalView : could not create the external form view !");
+ }
+#endif
+ InvalidateSlot_Lock(SID_FM_VIEW_AS_GRID, false);
+}
+
+
+void FmXFormShell::implAdjustConfigCache_Lock()
+{
+ const bool bFuzzing(utl::ConfigManager::IsFuzzing());
+ if (bFuzzing)
+ return;
+
+ // get (cache) the wizard usage flag
+ Sequence< OUString > aNames { "FormControlPilotsEnabled" };
+ Sequence< Any > aFlags = GetProperties(aNames);
+ if (1 == aFlags.getLength())
+ m_bUseWizards = ::cppu::any2bool(aFlags[0]);
+}
+
+
+void FmXFormShell::Notify( const css::uno::Sequence< OUString >& _rPropertyNames)
+{
+ DBG_TESTSOLARMUTEX();
+ if (impl_checkDisposed_Lock())
+ return;
+
+ for (const OUString& rName : _rPropertyNames)
+ if (rName == "FormControlPilotsEnabled")
+ {
+ implAdjustConfigCache_Lock();
+ InvalidateSlot_Lock(SID_FM_USE_WIZARDS, true);
+ }
+}
+
+void FmXFormShell::ImplCommit()
+{
+}
+
+
+void FmXFormShell::SetWizardUsing_Lock(bool _bUseThem)
+{
+ m_bUseWizards = _bUseThem;
+
+ Sequence< OUString > aNames { "FormControlPilotsEnabled" };
+ Sequence< Any > aValues{ Any(m_bUseWizards) };
+ PutProperties(aNames, aValues);
+}
+
+
+void FmXFormShell::viewDeactivated_Lock(FmFormView& _rCurrentView, bool _bDeactivateController)
+{
+
+ if ( _rCurrentView.GetImpl() && !_rCurrentView.IsDesignMode() )
+ {
+ _rCurrentView.GetImpl()->Deactivate( _bDeactivateController );
+ }
+
+ // if we have an async load operation pending for the 0-th page for this view,
+ // we need to cancel this
+ if (FmFormPage* pPage = _rCurrentView.GetCurPage())
+ {
+ // move all events from our queue to a new one, omit the events for the deactivated
+ // page
+ ::std::queue< FmLoadAction > aNewEvents;
+ while ( !m_aLoadingPages.empty() )
+ {
+ FmLoadAction aAction = m_aLoadingPages.front();
+ m_aLoadingPages.pop();
+ if ( pPage != aAction.pPage )
+ {
+ aNewEvents.push( aAction );
+ }
+ else
+ {
+ Application::RemoveUserEvent( aAction.nEventId );
+ }
+ }
+ m_aLoadingPages = aNewEvents;
+
+ // remove callbacks at the page
+ pPage->GetImpl().SetFormsCreationHdl( Link<FmFormPageImpl&,void>() );
+ }
+ UpdateForms_Lock(true);
+}
+
+
+IMPL_LINK_NOARG( FmXFormShell, OnFirstTimeActivation_Lock, void*, void )
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ m_nActivationEvent = nullptr;
+ SfxObjectShell* pDocument = m_pShell->GetObjectShell();
+
+ if ( pDocument && !pDocument->HasName() )
+ {
+ if (isEnhancedForm_Lock())
+ {
+ // show the data navigator
+ if ( !m_pShell->GetViewShell()->GetViewFrame().HasChildWindow( SID_FM_SHOW_DATANAVIGATOR ) )
+ m_pShell->GetViewShell()->GetViewFrame().ToggleChildWindow( SID_FM_SHOW_DATANAVIGATOR );
+ }
+ }
+}
+
+
+IMPL_LINK_NOARG( FmXFormShell, OnFormsCreated_Lock, FmFormPageImpl&, void )
+{
+ UpdateForms_Lock(true);
+}
+
+
+void FmXFormShell::viewActivated_Lock(FmFormView& _rCurrentView, bool _bSyncAction)
+{
+ FmFormPage* pPage = _rCurrentView.GetCurPage();
+
+ // activate our view if we are activated ourself
+ // FS - 30.06.99 - 67308
+ if ( _rCurrentView.GetImpl() && !_rCurrentView.IsDesignMode() )
+ {
+ // load forms for the page the current view belongs to
+ if ( pPage )
+ {
+ if ( !pPage->GetImpl().hasEverBeenActivated() )
+ loadForms_Lock(pPage, LoadFormsFlags::Load
+ | (_bSyncAction ? LoadFormsFlags::Sync
+ : LoadFormsFlags::Async));
+ pPage->GetImpl().setHasBeenActivated( );
+ }
+
+ // first-time initializations for the views
+ if ( !_rCurrentView.GetImpl()->hasEverBeenActivated( ) )
+ {
+ auto* pFormModel = dynamic_cast<FmFormModel*>(&_rCurrentView.GetModel());
+ _rCurrentView.GetImpl()->onFirstViewActivation(pFormModel);
+ _rCurrentView.GetImpl()->setHasBeenActivated();
+ }
+
+ // activate the current view
+ _rCurrentView.GetImpl()->Activate( _bSyncAction );
+ }
+
+ // set callbacks at the page
+ if ( pPage )
+ {
+ pPage->GetImpl().SetFormsCreationHdl(LINK(this, FmXFormShell, OnFormsCreated_Lock));
+ }
+
+ UpdateForms_Lock(true);
+
+ if ( m_bFirstActivation )
+ {
+ m_nActivationEvent = Application::PostUserEvent(LINK(this, FmXFormShell, OnFirstTimeActivation_Lock));
+ m_bFirstActivation = false;
+ }
+
+ // find a default "current form", if there is none, yet
+ // #i88186# / 2008-04-12 / frank.schoenheit@sun.com
+ impl_defaultCurrentForm_nothrow_Lock();
+}
+
+
+void FmXFormShell::impl_defaultCurrentForm_nothrow_Lock()
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ if ( m_xCurrentForm.is() )
+ // no action required
+ return;
+
+ FmFormView* pFormView = m_pShell->GetFormView();
+ FmFormPage* pPage = pFormView ? pFormView->GetCurPage() : nullptr;
+ if ( !pPage )
+ return;
+
+ try
+ {
+ Reference< XIndexAccess > xForms = pPage->GetForms( false );
+ if ( !xForms.is() || !xForms->hasElements() )
+ return;
+
+ Reference< XForm > xNewCurrentForm( xForms->getByIndex(0), UNO_QUERY_THROW );
+ impl_updateCurrentForm_Lock(xNewCurrentForm);
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+
+void FmXFormShell::smartControlReset( const Reference< XIndexAccess >& _rxModels )
+{
+ if (!_rxModels.is())
+ {
+ OSL_FAIL("FmXFormShell::smartControlReset: invalid container!");
+ return;
+ }
+
+ sal_Int32 nCount = _rxModels->getCount();
+ Reference< XPropertySet > xCurrent;
+ Reference< XPropertySetInfo > xCurrentInfo;
+ Reference< XPropertySet > xBoundField;
+
+ for (sal_Int32 i=0; i<nCount; ++i)
+ {
+ _rxModels->getByIndex(i) >>= xCurrent;
+ if (xCurrent.is())
+ xCurrentInfo = xCurrent->getPropertySetInfo();
+ else
+ xCurrentInfo.clear();
+ if (!xCurrentInfo.is())
+ continue;
+
+ if (xCurrentInfo->hasPropertyByName(FM_PROP_CLASSID))
+ { // it's a control model
+
+ // check if this control is bound to a living database field
+ if (xCurrentInfo->hasPropertyByName(FM_PROP_BOUNDFIELD))
+ xCurrent->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xBoundField;
+ else
+ xBoundField.clear();
+
+ // reset only if it's *not* bound
+ bool bReset = !xBoundField.is();
+
+ // and additionally, check if it has an external value binding
+ Reference< XBindableValue > xBindable( xCurrent, UNO_QUERY );
+ if ( xBindable.is() && xBindable->getValueBinding().is() )
+ bReset = false;
+
+ if ( bReset )
+ {
+ Reference< XReset > xControlReset( xCurrent, UNO_QUERY );
+ if ( xControlReset.is() )
+ xControlReset->reset();
+ }
+ }
+ else
+ {
+ Reference< XIndexAccess > xContainer(xCurrent, UNO_QUERY);
+ if (xContainer.is())
+ smartControlReset(xContainer);
+ }
+ }
+}
+
+
+IMPL_LINK_NOARG( FmXFormShell, OnLoadForms_Lock, void*, void )
+{
+ FmLoadAction aAction = m_aLoadingPages.front();
+ m_aLoadingPages.pop();
+
+ loadForms_Lock(aAction.pPage, aAction.nFlags & ~LoadFormsFlags::Async);
+}
+
+
+namespace
+{
+ bool lcl_isLoadable( const Reference< XInterface >& _rxLoadable )
+ {
+ // determines whether a form should be loaded or not
+ // if there is no datasource or connection there is no reason to load a form
+ Reference< XPropertySet > xSet( _rxLoadable, UNO_QUERY );
+ if ( !xSet.is() )
+ return false;
+ try
+ {
+ Reference< XConnection > xConn;
+ if ( isEmbeddedInDatabase( _rxLoadable, xConn ) )
+ return true;
+
+ // is there already an active connection
+ xSet->getPropertyValue(FM_PROP_ACTIVE_CONNECTION) >>= xConn;
+ if ( xConn.is() )
+ return true;
+
+ OUString sPropertyValue;
+ OSL_VERIFY( xSet->getPropertyValue( FM_PROP_DATASOURCE ) >>= sPropertyValue );
+ if ( !sPropertyValue.isEmpty() )
+ return true;
+
+ OSL_VERIFY( xSet->getPropertyValue( FM_PROP_URL ) >>= sPropertyValue );
+ if ( !sPropertyValue.isEmpty() )
+ return true;
+ }
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return false;
+ }
+}
+
+
+void FmXFormShell::loadForms_Lock(FmFormPage* _pPage, const LoadFormsFlags _nBehaviour /* LoadFormsFlags::Load | LoadFormsFlags::Sync */)
+{
+ DBG_ASSERT( ( _nBehaviour & ( LoadFormsFlags::Async | LoadFormsFlags::Unload ) ) != ( LoadFormsFlags::Async | LoadFormsFlags::Unload ),
+ "FmXFormShell::loadForms: async loading not supported - this will heavily fail!" );
+
+ if ( _nBehaviour & LoadFormsFlags::Async )
+ {
+ m_aLoadingPages.push( FmLoadAction(
+ _pPage,
+ _nBehaviour,
+ Application::PostUserEvent(LINK(this, FmXFormShell, OnLoadForms_Lock), _pPage)
+ ) );
+ return;
+ }
+
+ DBG_ASSERT( _pPage, "FmXFormShell::loadForms: invalid page!" );
+ if ( !_pPage )
+ return;
+
+ // lock the undo env so the forms can change non-transient properties while loading
+ // (without this my doc's modified flag would be set)
+ FmFormModel& rFmFormModel(dynamic_cast< FmFormModel& >(_pPage->getSdrModelFromSdrPage()));
+ rFmFormModel.GetUndoEnv().Lock();
+
+ // load all forms
+ Reference< XIndexAccess > xForms = _pPage->GetForms( false );
+
+ if ( xForms.is() )
+ {
+ Reference< XLoadable > xForm;
+ for ( sal_Int32 j = 0, nCount = xForms->getCount(); j < nCount; ++j )
+ {
+ xForms->getByIndex( j ) >>= xForm;
+ bool bFormWasLoaded = false;
+ // a database form must be loaded for
+ try
+ {
+ if ( !( _nBehaviour & LoadFormsFlags::Unload ) )
+ {
+ if ( lcl_isLoadable( xForm ) && !xForm->isLoaded() )
+ xForm->load();
+ }
+ else
+ {
+ if ( xForm->isLoaded() )
+ {
+ bFormWasLoaded = true;
+ xForm->unload();
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ // reset the form if it was loaded
+ if ( bFormWasLoaded )
+ {
+ Reference< XIndexAccess > xContainer( xForm, UNO_QUERY );
+ DBG_ASSERT( xContainer.is(), "FmXFormShell::loadForms: the form is no container!" );
+ if ( xContainer.is() )
+ smartControlReset( xContainer );
+ }
+ }
+ }
+
+ // unlock the environment
+ rFmFormModel.GetUndoEnv().UnLock();
+}
+
+
+void FmXFormShell::ExecuteTextAttribute_Lock(SfxRequest& _rReq)
+{
+ DBG_TESTSOLARMUTEX();
+ m_pTextShell->ExecuteTextAttribute( _rReq );
+}
+
+
+void FmXFormShell::GetTextAttributeState_Lock(SfxItemSet& _rSet)
+{
+ DBG_TESTSOLARMUTEX();
+ m_pTextShell->GetTextAttributeState( _rSet );
+}
+
+
+bool FmXFormShell::IsActiveControl_Lock(bool _bCountRichTextOnly ) const
+{
+ DBG_TESTSOLARMUTEX();
+ return m_pTextShell->IsActiveControl( _bCountRichTextOnly );
+}
+
+
+void FmXFormShell::ForgetActiveControl_Lock()
+{
+ DBG_TESTSOLARMUTEX();
+ m_pTextShell->ForgetActiveControl();
+}
+
+
+void FmXFormShell::SetControlActivationHandler_Lock(const Link<LinkParamNone*,void>& _rHdl)
+{
+ DBG_TESTSOLARMUTEX();
+ m_pTextShell->SetControlActivationHandler( _rHdl );
+}
+
+void FmXFormShell::handleShowPropertiesRequest_Lock()
+{
+ if (onlyControlsAreMarked_Lock())
+ ShowSelectionProperties_Lock( true );
+}
+
+
+void FmXFormShell::handleMouseButtonDown_Lock(const SdrViewEvent& _rViewEvent)
+{
+ // catch simple double clicks
+ if (_rViewEvent.mnMouseClicks == 2 && _rViewEvent.mnMouseCode == MOUSE_LEFT)
+ {
+ if ( _rViewEvent.meHit == SdrHitKind::MarkedObject )
+ {
+ if (onlyControlsAreMarked_Lock())
+ ShowSelectionProperties_Lock( true );
+ }
+ }
+}
+
+
+bool FmXFormShell::HasControlFocus_Lock() const
+{
+ bool bHasControlFocus = false;
+
+ try
+ {
+ Reference<runtime::XFormController> xController(getActiveController_Lock());
+ Reference< XControl > xCurrentControl;
+ if ( xController.is() )
+ xCurrentControl.set( xController->getCurrentControl() );
+ if ( xCurrentControl.is() )
+ {
+ Reference< XWindow2 > xPeerWindow( xCurrentControl->getPeer(), UNO_QUERY_THROW );
+ bHasControlFocus = xPeerWindow->hasFocus();
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ return bHasControlFocus;
+}
+
+
+SearchableControlIterator::SearchableControlIterator(Reference< XInterface> const & xStartingPoint)
+ :IndexAccessIterator(xStartingPoint)
+{
+}
+
+
+bool SearchableControlIterator::ShouldHandleElement(const Reference< XInterface>& xElement)
+{
+ // if the thing has a ControlSource and a BoundField property
+ Reference< XPropertySet> xProperties(xElement, UNO_QUERY);
+ if (::comphelper::hasProperty(FM_PROP_CONTROLSOURCE, xProperties) && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xProperties))
+ {
+ // and the BoundField is valid
+ Reference< XPropertySet> xField;
+ xProperties->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
+ if (xField.is())
+ {
+ // we take it
+ m_sCurrentValue = ::comphelper::getString(xProperties->getPropertyValue(FM_PROP_CONTROLSOURCE));
+ return true;
+ }
+ }
+
+ // if it is a grid control
+ if (::comphelper::hasProperty(FM_PROP_CLASSID, xProperties))
+ {
+ Any aClassId( xProperties->getPropertyValue(FM_PROP_CLASSID) );
+ if (::comphelper::getINT16(aClassId) == FormComponentType::GRIDCONTROL)
+ {
+ m_sCurrentValue.clear();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+bool SearchableControlIterator::ShouldStepInto(const Reference< XInterface>& /*xContainer*/) const
+{
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmsrccfg.cxx b/svx/source/form/fmsrccfg.cxx
new file mode 100644
index 0000000000..499d2cb167
--- /dev/null
+++ b/svx/source/form/fmsrccfg.cxx
@@ -0,0 +1,286 @@
+/* -*- 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 <svx/fmsrccfg.hxx>
+
+#include <sal/log.hxx>
+#include <comphelper/processfactory.hxx>
+#include <i18nutil/transliteration.hxx>
+
+using namespace ::com::sun::star::uno;
+
+namespace svxform
+{
+ // search parameters
+
+ FmSearchParams::FmSearchParams()
+ :nTransliterationFlags( TransliterationFlags::NONE )
+ ,nSearchForType ( 0 )
+ ,nPosition ( MATCHING_ANYWHERE )
+ ,nLevOther ( 2 )
+ ,nLevShorter ( 2 )
+ ,nLevLonger ( 2 )
+ ,bLevRelaxed ( true )
+ ,bAllFields ( false )
+ ,bUseFormatter ( true )
+ ,bBackwards ( false )
+ ,bWildcard ( false )
+ ,bRegular ( false )
+ ,bApproxSearch ( false )
+ ,bSoundsLikeCJK ( false )
+ {
+ nTransliterationFlags =
+ TransliterationFlags::ignoreSpace_ja_JP
+ | TransliterationFlags::ignoreMiddleDot_ja_JP
+ | TransliterationFlags::ignoreProlongedSoundMark_ja_JP
+ | TransliterationFlags::ignoreSeparator_ja_JP
+ | TransliterationFlags::IGNORE_CASE;
+ }
+
+ bool FmSearchParams::isIgnoreWidthCJK( ) const
+ {
+ return bool(nTransliterationFlags & TransliterationFlags::IGNORE_WIDTH);
+ }
+
+ bool FmSearchParams::isCaseSensitive( ) const
+ {
+ return !(nTransliterationFlags & TransliterationFlags::IGNORE_CASE);
+ }
+
+ void FmSearchParams::setCaseSensitive( bool _bCase )
+ {
+ if ( _bCase )
+ nTransliterationFlags &= ~TransliterationFlags::IGNORE_CASE;
+ else
+ nTransliterationFlags |= TransliterationFlags::IGNORE_CASE;
+ }
+
+ // maps from ascii values to int values
+
+ namespace {
+
+ struct Ascii2Int16
+ {
+ const char* pAscii;
+ sal_Int16 nValue;
+ };
+
+ }
+
+ static const Ascii2Int16* lcl_getSearchForTypeValueMap()
+ {
+ static const Ascii2Int16 s_aSearchForTypeMap[] =
+ {
+ { "text", 0 },
+ { "null", 1 },
+ { "non-null", 2 },
+ { nullptr, -1 }
+ };
+ return s_aSearchForTypeMap;
+ }
+
+ static const Ascii2Int16* lcl_getSearchPositionValueMap()
+ {
+ static const Ascii2Int16 s_aSearchPositionMap[] =
+ {
+ { "anywhere-in-field", MATCHING_ANYWHERE },
+ { "beginning-of-field", MATCHING_BEGINNING },
+ { "end-of-field", MATCHING_END },
+ { "complete-field", MATCHING_WHOLETEXT },
+ { nullptr, -1 }
+ };
+ return s_aSearchPositionMap;
+ }
+
+ static sal_Int16 lcl_implMapAsciiValue( const OUString& _rAsciiValue, const Ascii2Int16* _pMap )
+ {
+ // search the map for the given ascii value
+ const Ascii2Int16* pSearch = _pMap;
+ while ( pSearch && pSearch->pAscii )
+ {
+ if ( _rAsciiValue.equalsAscii( pSearch->pAscii ) )
+ // found
+ return pSearch->nValue;
+ ++pSearch;
+ }
+
+ SAL_WARN(
+ "svx.form", "could not convert the ascii value " << _rAsciiValue);
+ return -1;
+ }
+
+ static const char* lcl_implMapIntValue( const sal_Int16 _nValue, const Ascii2Int16* _pMap )
+ {
+ // search the map for the given integer value
+ const Ascii2Int16* pSearch = _pMap;
+ while ( pSearch && pSearch->pAscii )
+ {
+ if ( _nValue == pSearch->nValue )
+ // found
+ return pSearch->pAscii;
+ ++pSearch;
+ }
+
+ SAL_WARN( "svx", "lcl_implMapIntValue: could not convert the integer value "
+ << _nValue << " !");
+ static const char* const s_pDummy = "";
+ // just as a fallback...
+ return s_pDummy;
+ }
+
+ // class FmSearchConfigItem - a config item that stores search parameters
+
+#define TA( c ) &c, cppu::UnoType<decltype(c)>::get()
+
+ FmSearchConfigItem::FmSearchConfigItem()
+ :OConfigurationValueContainer( ::comphelper::getProcessComponentContext(), m_aMutex, "/org.openoffice.Office.DataAccess/FormSearchOptions", 2 )
+ {
+ // register our members so the data exchange with the node values is done automatically
+
+ registerExchangeLocation( "SearchHistory", TA( aHistory ) );
+ registerExchangeLocation( "LevenshteinOther", TA( nLevOther ) );
+ registerExchangeLocation( "LevenshteinShorter", TA( nLevShorter ) );
+ registerExchangeLocation( "LevenshteinLonger", TA( nLevLonger ) );
+ registerExchangeLocation( "IsLevenshteinRelaxed", TA( bLevRelaxed ) );
+ registerExchangeLocation( "IsSearchAllFields", TA( bAllFields ) );
+ registerExchangeLocation( "IsUseFormatter", TA( bUseFormatter ) );
+ registerExchangeLocation( "IsBackwards", TA( bBackwards ) );
+ registerExchangeLocation( "IsWildcardSearch", TA( bWildcard ) );
+ registerExchangeLocation( "IsUseRegularExpression", TA( bRegular ) );
+ registerExchangeLocation( "IsSimilaritySearch", TA( bApproxSearch ) );
+ registerExchangeLocation( "IsUseAsianOptions", TA( bSoundsLikeCJK ) );
+
+ // the properties which need to be translated
+ registerExchangeLocation( "SearchType", TA( m_sSearchForType ) );
+ registerExchangeLocation( "SearchPosition", TA( m_sSearchPosition ) );
+
+ registerExchangeLocation( "IsMatchCase", TA( m_bIsMatchCase ) );
+ registerExchangeLocation( "Japanese/IsMatchFullHalfWidthForms", TA( m_bIsMatchFullHalfWidthForms ) );
+ registerExchangeLocation( "Japanese/IsMatchHiraganaKatakana", TA( m_bIsMatchHiraganaKatakana ) );
+ registerExchangeLocation( "Japanese/IsMatchContractions", TA( m_bIsMatchContractions ) );
+ registerExchangeLocation( "Japanese/IsMatchMinusDashCho-on", TA( m_bIsMatchMinusDashCho_on ) );
+ registerExchangeLocation( "Japanese/IsMatchRepeatCharMarks", TA( m_bIsMatchRepeatCharMarks ) );
+ registerExchangeLocation( "Japanese/IsMatchVariantFormKanji", TA( m_bIsMatchVariantFormKanji ) );
+ registerExchangeLocation( "Japanese/IsMatchOldKanaForms", TA( m_bIsMatchOldKanaForms ) );
+ registerExchangeLocation( "Japanese/IsMatch_DiZi_DuZu", TA( m_bIsMatch_DiZi_DuZu ) );
+ registerExchangeLocation( "Japanese/IsMatch_BaVa_HaFa", TA( m_bIsMatch_BaVa_HaFa ) );
+ registerExchangeLocation( "Japanese/IsMatch_TsiThiChi_DhiZi", TA( m_bIsMatch_TsiThiChi_DhiZi ) );
+ registerExchangeLocation( "Japanese/IsMatch_HyuIyu_ByuVyu", TA( m_bIsMatch_HyuIyu_ByuVyu ) );
+ registerExchangeLocation( "Japanese/IsMatch_SeShe_ZeJe", TA( m_bIsMatch_SeShe_ZeJe ) );
+ registerExchangeLocation( "Japanese/IsMatch_IaIya", TA( m_bIsMatch_IaIya ) );
+ registerExchangeLocation( "Japanese/IsMatch_KiKu", TA( m_bIsMatch_KiKu ) );
+ registerExchangeLocation( "Japanese/IsIgnorePunctuation", TA( m_bIsIgnorePunctuation ) );
+ registerExchangeLocation( "Japanese/IsIgnoreWhitespace", TA( m_bIsIgnoreWhitespace ) );
+ registerExchangeLocation( "Japanese/IsIgnoreProlongedSoundMark",TA( m_bIsIgnoreProlongedSoundMark ) );
+ registerExchangeLocation( "Japanese/IsIgnoreMiddleDot", TA( m_bIsIgnoreMiddleDot ) );
+
+ read( );
+ }
+
+ FmSearchConfigItem::~FmSearchConfigItem()
+ {
+ commit( );
+ }
+
+ void FmSearchConfigItem::implTranslateFromConfig( )
+ {
+ // the search-for string
+ nSearchForType = lcl_implMapAsciiValue( m_sSearchForType, lcl_getSearchForTypeValueMap() );
+
+ // the search position
+ nPosition = lcl_implMapAsciiValue( m_sSearchPosition, lcl_getSearchPositionValueMap() );
+
+ // the transliteration flags
+ nTransliterationFlags = TransliterationFlags::NONE;
+
+ if ( !m_bIsMatchCase ) nTransliterationFlags |= TransliterationFlags::IGNORE_CASE;
+ if ( m_bIsMatchFullHalfWidthForms ) nTransliterationFlags |= TransliterationFlags::IGNORE_WIDTH;
+ if ( m_bIsMatchHiraganaKatakana ) nTransliterationFlags |= TransliterationFlags::IGNORE_KANA;
+ if ( m_bIsMatchContractions ) nTransliterationFlags |= TransliterationFlags::ignoreSize_ja_JP;
+ if ( m_bIsMatchMinusDashCho_on ) nTransliterationFlags |= TransliterationFlags::ignoreMinusSign_ja_JP;
+ if ( m_bIsMatchRepeatCharMarks ) nTransliterationFlags |= TransliterationFlags::ignoreIterationMark_ja_JP;
+ if ( m_bIsMatchVariantFormKanji ) nTransliterationFlags |= TransliterationFlags::ignoreTraditionalKanji_ja_JP;
+ if ( m_bIsMatchOldKanaForms ) nTransliterationFlags |= TransliterationFlags::ignoreTraditionalKana_ja_JP;
+ if ( m_bIsMatch_DiZi_DuZu ) nTransliterationFlags |= TransliterationFlags::ignoreZiZu_ja_JP;
+ if ( m_bIsMatch_BaVa_HaFa ) nTransliterationFlags |= TransliterationFlags::ignoreBaFa_ja_JP;
+ if ( m_bIsMatch_TsiThiChi_DhiZi ) nTransliterationFlags |= TransliterationFlags::ignoreTiJi_ja_JP;
+ if ( m_bIsMatch_HyuIyu_ByuVyu ) nTransliterationFlags |= TransliterationFlags::ignoreHyuByu_ja_JP;
+ if ( m_bIsMatch_SeShe_ZeJe ) nTransliterationFlags |= TransliterationFlags::ignoreSeZe_ja_JP;
+ if ( m_bIsMatch_IaIya ) nTransliterationFlags |= TransliterationFlags::ignoreIandEfollowedByYa_ja_JP;
+ if ( m_bIsMatch_KiKu ) nTransliterationFlags |= TransliterationFlags::ignoreKiKuFollowedBySa_ja_JP;
+
+ if ( m_bIsIgnorePunctuation ) nTransliterationFlags |= TransliterationFlags::ignoreSeparator_ja_JP;
+ if ( m_bIsIgnoreWhitespace ) nTransliterationFlags |= TransliterationFlags::ignoreSpace_ja_JP;
+ if ( m_bIsIgnoreProlongedSoundMark ) nTransliterationFlags |= TransliterationFlags::ignoreProlongedSoundMark_ja_JP;
+ if ( m_bIsIgnoreMiddleDot ) nTransliterationFlags |= TransliterationFlags::ignoreMiddleDot_ja_JP;
+ }
+
+ void FmSearchConfigItem::implTranslateToConfig( )
+ {
+ // the search-for string
+ m_sSearchForType = OUString::createFromAscii( lcl_implMapIntValue( nSearchForType, lcl_getSearchForTypeValueMap() ) );
+
+ // the search position
+ m_sSearchPosition = OUString::createFromAscii( lcl_implMapIntValue( nPosition, lcl_getSearchPositionValueMap() ) );
+
+ // the transliteration flags
+
+ m_bIsMatchCase = !( nTransliterationFlags & TransliterationFlags::IGNORE_CASE );
+ m_bIsMatchFullHalfWidthForms = bool( nTransliterationFlags & TransliterationFlags::IGNORE_WIDTH );
+ m_bIsMatchHiraganaKatakana = bool( nTransliterationFlags & TransliterationFlags::IGNORE_KANA );
+ m_bIsMatchContractions = bool( nTransliterationFlags & TransliterationFlags::ignoreSize_ja_JP );
+ m_bIsMatchMinusDashCho_on = bool( nTransliterationFlags & TransliterationFlags::ignoreMinusSign_ja_JP );
+ m_bIsMatchRepeatCharMarks = bool( nTransliterationFlags & TransliterationFlags::ignoreIterationMark_ja_JP );
+ m_bIsMatchVariantFormKanji = bool( nTransliterationFlags & TransliterationFlags::ignoreTraditionalKanji_ja_JP );
+ m_bIsMatchOldKanaForms = bool( nTransliterationFlags & TransliterationFlags::ignoreTraditionalKana_ja_JP );
+ m_bIsMatch_DiZi_DuZu = bool( nTransliterationFlags & TransliterationFlags::ignoreZiZu_ja_JP );
+ m_bIsMatch_BaVa_HaFa = bool( nTransliterationFlags & TransliterationFlags::ignoreBaFa_ja_JP );
+ m_bIsMatch_TsiThiChi_DhiZi = bool( nTransliterationFlags & TransliterationFlags::ignoreTiJi_ja_JP );
+ m_bIsMatch_HyuIyu_ByuVyu = bool( nTransliterationFlags & TransliterationFlags::ignoreHyuByu_ja_JP );
+ m_bIsMatch_SeShe_ZeJe = bool( nTransliterationFlags & TransliterationFlags::ignoreSeZe_ja_JP );
+ m_bIsMatch_IaIya = bool( nTransliterationFlags & TransliterationFlags::ignoreIandEfollowedByYa_ja_JP );
+ m_bIsMatch_KiKu = bool( nTransliterationFlags & TransliterationFlags::ignoreKiKuFollowedBySa_ja_JP );
+
+ m_bIsIgnorePunctuation = bool( nTransliterationFlags & TransliterationFlags::ignoreSeparator_ja_JP );
+ m_bIsIgnoreWhitespace = bool( nTransliterationFlags & TransliterationFlags::ignoreSpace_ja_JP );
+ m_bIsIgnoreProlongedSoundMark = bool( nTransliterationFlags & TransliterationFlags::ignoreProlongedSoundMark_ja_JP );
+ m_bIsIgnoreMiddleDot = bool( nTransliterationFlags & TransliterationFlags::ignoreMiddleDot_ja_JP );
+ }
+
+ const FmSearchParams& FmSearchConfigItem::getParams() const
+ {
+ // ensure that the properties which are not stored directly are up-to-date
+ const_cast< FmSearchConfigItem* >( this )->implTranslateFromConfig( );
+
+ // and return our FmSearchParams part
+ return *this;
+ }
+
+ void FmSearchConfigItem::setParams( const FmSearchParams& _rParams )
+ {
+ // copy the FmSearchParams part
+ *static_cast< FmSearchParams* >( this ) = _rParams;
+
+ // translate the settings not represented by a direct config value
+ implTranslateToConfig();
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmsrcimp.cxx b/svx/source/form/fmsrcimp.cxx
new file mode 100644
index 0000000000..6bdd7a6c8f
--- /dev/null
+++ b/svx/source/form/fmsrcimp.cxx
@@ -0,0 +1,1058 @@
+/* -*- 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 <sal/config.h>
+
+#include <o3tl/safeint.hxx>
+#include <o3tl/string_view.hxx>
+#include <svx/fmtools.hxx>
+#include <svx/fmsrccfg.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <tools/wldcrd.hxx>
+#include <vcl/svapp.hxx>
+#include <unotools/textsearch.hxx>
+#include <com/sun/star/awt/XTextComponent.hpp>
+#include <com/sun/star/awt/XListBox.hpp>
+#include <com/sun/star/awt/XCheckBox.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/util/SearchAlgorithms2.hpp>
+#include <com/sun/star/util/SearchFlags.hpp>
+#include <com/sun/star/lang/Locale.hpp>
+#include <com/sun/star/i18n/CollatorOptions.hpp>
+
+#include <com/sun/star/sdb/XColumn.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+#include <com/sun/star/sdbc/XDatabaseMetaData.hpp>
+#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
+
+#include <fmprop.hxx>
+#include <svx/fmsrcimp.hxx>
+
+#include <comphelper/types.hxx>
+#include <unotools/syslocale.hxx>
+#include <i18nutil/searchopt.hxx>
+
+#define EQUAL_BOOKMARKS(a, b) a == b
+
+#define IFACECAST(c) static_cast<const Reference< XInterface >&>(c)
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::i18n;
+using namespace ::com::sun::star::beans;
+using namespace ::svxform;
+
+
+// = FmRecordCountListener
+
+// SMART_UNO_IMPLEMENTATION(FmRecordCountListener, UsrObject);
+
+
+FmRecordCountListener::FmRecordCountListener(const Reference< css::sdbc::XResultSet > & dbcCursor)
+{
+
+ m_xListening.set(dbcCursor, UNO_QUERY);
+ if (!m_xListening.is())
+ return;
+
+ if (::comphelper::getBOOL(m_xListening->getPropertyValue(FM_PROP_ROWCOUNTFINAL)))
+ {
+ m_xListening = nullptr;
+ // there's nothing to do as the record count is already known
+ return;
+ }
+
+ m_xListening->addPropertyChangeListener(FM_PROP_ROWCOUNT, static_cast<css::beans::XPropertyChangeListener*>(this));
+}
+
+
+void FmRecordCountListener::SetPropChangeHandler(const Link<sal_Int32,void>& lnk)
+{
+ m_lnkWhoWantsToKnow = lnk;
+
+ if (m_xListening.is())
+ NotifyCurrentCount();
+}
+
+
+FmRecordCountListener::~FmRecordCountListener()
+{
+
+}
+
+
+void FmRecordCountListener::DisConnect()
+{
+ if(m_xListening.is())
+ m_xListening->removePropertyChangeListener(FM_PROP_ROWCOUNT, static_cast<css::beans::XPropertyChangeListener*>(this));
+ m_xListening = nullptr;
+}
+
+
+void SAL_CALL FmRecordCountListener::disposing(const css::lang::EventObject& /*Source*/)
+{
+ DBG_ASSERT(m_xListening.is(), "FmRecordCountListener::disposing should never have been called without a propset !");
+ DisConnect();
+}
+
+
+void FmRecordCountListener::NotifyCurrentCount()
+{
+ if (m_lnkWhoWantsToKnow.IsSet())
+ {
+ DBG_ASSERT(m_xListening.is(), "FmRecordCountListener::NotifyCurrentCount : I have no propset ... !?");
+ sal_Int32 theCount = ::comphelper::getINT32(m_xListening->getPropertyValue(FM_PROP_ROWCOUNT));
+ m_lnkWhoWantsToKnow.Call(theCount);
+ }
+}
+
+
+void FmRecordCountListener::propertyChange(const css::beans::PropertyChangeEvent& /*evt*/)
+{
+ NotifyCurrentCount();
+}
+
+
+// FmSearchEngine - local classes
+
+SimpleTextWrapper::SimpleTextWrapper(const Reference< css::awt::XTextComponent > & _xText)
+ :ControlTextWrapper(_xText)
+ ,m_xText(_xText)
+{
+ DBG_ASSERT(m_xText.is(), "FmSearchEngine::SimpleTextWrapper::SimpleTextWrapper : invalid argument !");
+}
+
+
+OUString SimpleTextWrapper::getCurrentText() const
+{
+ return m_xText->getText();
+}
+
+
+ListBoxWrapper::ListBoxWrapper(const Reference< css::awt::XListBox > & _xBox)
+ :ControlTextWrapper(_xBox)
+ ,m_xBox(_xBox)
+{
+ DBG_ASSERT(m_xBox.is(), "FmSearchEngine::ListBoxWrapper::ListBoxWrapper : invalid argument !");
+}
+
+
+OUString ListBoxWrapper::getCurrentText() const
+{
+ return m_xBox->getSelectedItem();
+}
+
+
+CheckBoxWrapper::CheckBoxWrapper(const Reference< css::awt::XCheckBox > & _xBox)
+ :ControlTextWrapper(_xBox)
+ ,m_xBox(_xBox)
+{
+ DBG_ASSERT(m_xBox.is(), "FmSearchEngine::CheckBoxWrapper::CheckBoxWrapper : invalid argument !");
+}
+
+
+OUString CheckBoxWrapper::getCurrentText() const
+{
+ switch (static_cast<TriState>(m_xBox->getState()))
+ {
+ case TRISTATE_FALSE: return "0";
+ case TRISTATE_TRUE: return "1";
+ default: break;
+ }
+ return OUString();
+}
+
+
+// = FmSearchEngine
+
+bool FmSearchEngine::MoveCursor()
+{
+ bool bSuccess = true;
+ try
+ {
+ if (m_bForward)
+ if (m_xSearchCursor.isLast())
+ m_xSearchCursor.first();
+ else
+ m_xSearchCursor.next();
+ else
+ if (m_xSearchCursor.isFirst())
+ {
+ rtl::Reference<FmRecordCountListener> prclListener = new FmRecordCountListener(m_xSearchCursor);
+ prclListener->SetPropChangeHandler(LINK(this, FmSearchEngine, OnNewRecordCount));
+
+ m_xSearchCursor.last();
+
+ prclListener->DisConnect();
+ }
+ else
+ m_xSearchCursor.previous();
+ }
+ catch(Exception const&)
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "FmSearchEngine::MoveCursor");
+ bSuccess = false;
+ }
+ catch(...)
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "FmSearchEngine::MoveCursor : caught an unknown Exception !");
+ bSuccess = false;
+ }
+
+ return bSuccess;
+}
+
+
+bool FmSearchEngine::MoveField(sal_Int32& nPos, FieldCollection::iterator& iter, const FieldCollection::iterator& iterBegin, const FieldCollection::iterator& iterEnd)
+{
+ bool bSuccess(true);
+ if (m_bForward)
+ {
+ ++iter;
+ ++nPos;
+ if (iter == iterEnd)
+ {
+ bSuccess = MoveCursor();
+ iter = iterBegin;
+ nPos = 0;
+ }
+ } else
+ {
+ if (iter == iterBegin)
+ {
+ bSuccess = MoveCursor();
+ iter = iterEnd;
+ nPos = iter-iterBegin;
+ }
+ --iter;
+ --nPos;
+ }
+ return bSuccess;
+}
+
+
+void FmSearchEngine::BuildAndInsertFieldInfo(const Reference< css::container::XIndexAccess > & xAllFields, sal_Int32 nField)
+{
+ DBG_ASSERT( xAllFields.is() && ( nField >= 0 ) && ( nField < xAllFields->getCount() ),
+ "FmSearchEngine::BuildAndInsertFieldInfo: invalid field descriptor!" );
+
+ // the field itself
+ Reference< XInterface > xCurrentField;
+ xAllFields->getByIndex(nField) >>= xCurrentField;
+
+ // From this I now know that it supports the DatabaseRecord service (I hope).
+ // For the FormatKey and the type I need the PropertySet.
+ Reference< css::beans::XPropertySet > xProperties(xCurrentField, UNO_QUERY_THROW);
+
+ // build the FieldInfo for that
+ FieldInfo fiCurrent;
+ fiCurrent.xContents.set(xCurrentField, UNO_QUERY);
+
+ // and memorize
+ m_arrUsedFields.insert(m_arrUsedFields.end(), fiCurrent);
+
+}
+
+OUString FmSearchEngine::FormatField(sal_Int32 nWhich)
+{
+ DBG_ASSERT(o3tl::make_unsigned(nWhich) < m_aControlTexts.size(), "FmSearchEngine::FormatField(sal_Int32) : invalid position !");
+ DBG_ASSERT(m_aControlTexts[nWhich], "FmSearchEngine::FormatField(sal_Int32) : invalid object in array !");
+ DBG_ASSERT(m_aControlTexts[nWhich]->getControl().is(), "FmSearchEngine::FormatField : invalid control !");
+
+ if (m_nCurrentFieldIndex != -1)
+ {
+ DBG_ASSERT((nWhich == 0) || (nWhich == m_nCurrentFieldIndex), "FmSearchEngine::FormatField : parameter nWhich is invalid");
+ // analogous situation as below
+ nWhich = m_nCurrentFieldIndex;
+ }
+
+ DBG_ASSERT((nWhich >= 0) && (o3tl::make_unsigned(nWhich) < m_aControlTexts.size()),
+ "FmSearchEngine::FormatField : invalid argument nWhich !");
+ return m_aControlTexts[m_nCurrentFieldIndex == -1 ? nWhich : m_nCurrentFieldIndex]->getCurrentText();
+}
+
+
+FmSearchEngine::SearchResult FmSearchEngine::SearchSpecial(bool _bSearchForNull, sal_Int32& nFieldPos,
+ FieldCollection::iterator& iterFieldLoop, const FieldCollection::iterator& iterBegin, const FieldCollection::iterator& iterEnd)
+{
+ // memorize the start position
+ Any aStartMark;
+ try { aStartMark = m_xSearchCursor.getBookmark(); }
+ catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); return SearchResult::Error; }
+ FieldCollection::const_iterator iterInitialField = iterFieldLoop;
+
+
+ bool bFound(false);
+ bool bMovedAround(false);
+ do
+ {
+ Application::Reschedule( true );
+
+ // the content to be compared currently
+ iterFieldLoop->xContents->getString(); // needed for wasNull
+ bFound = _bSearchForNull == bool(iterFieldLoop->xContents->wasNull());
+ if (bFound)
+ break;
+
+ // next field (implicitly next record, if necessary)
+ if (!MoveField(nFieldPos, iterFieldLoop, iterBegin, iterEnd))
+ { // When moving to the next field, something went wrong...
+ // Continuing is not possible, since the next time exactly the same
+ // will definitely go wrong again, thus abort.
+ // Before, however, so that the search continues at the current position:
+ try { m_aPreviousLocBookmark = m_xSearchCursor.getBookmark(); }
+ catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); }
+ m_iterPreviousLocField = iterFieldLoop;
+ // and leave
+ return SearchResult::Error;
+ }
+
+ Any aCurrentBookmark;
+ try { aCurrentBookmark = m_xSearchCursor.getBookmark(); }
+ catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); return SearchResult::Error; }
+
+ bMovedAround = EQUAL_BOOKMARKS(aStartMark, aCurrentBookmark) && (iterFieldLoop == iterInitialField);
+
+ if (nFieldPos == 0)
+ // that is, I've moved to a new record
+ PropagateProgress(bMovedAround);
+ // if we moved to the starting position we don't have to propagate an 'overflow' message
+ // FS - 07.12.99 - 68530
+
+ // cancel requested?
+ if (CancelRequested())
+ return SearchResult::Cancelled;
+
+ } while (!bMovedAround);
+
+ return bFound ? SearchResult::Found : SearchResult::NotFound;
+}
+
+
+FmSearchEngine::SearchResult FmSearchEngine::SearchWildcard(std::u16string_view strExpression, sal_Int32& nFieldPos,
+ FieldCollection::iterator& iterFieldLoop, const FieldCollection::iterator& iterBegin, const FieldCollection::iterator& iterEnd)
+{
+ // memorize the start position
+ Any aStartMark;
+ try { aStartMark = m_xSearchCursor.getBookmark(); }
+ catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); return SearchResult::Error; }
+ FieldCollection::const_iterator iterInitialField = iterFieldLoop;
+
+ WildCard aSearchExpression(strExpression);
+
+
+ bool bFound(false);
+ bool bMovedAround(false);
+ do
+ {
+ Application::Reschedule( true );
+
+ // the content to be compared currently
+ OUString sCurrentCheck;
+ if (m_bFormatter)
+ sCurrentCheck = FormatField(nFieldPos);
+ else
+ sCurrentCheck = iterFieldLoop->xContents->getString();
+
+ if (!GetCaseSensitive())
+ // norm the string
+ sCurrentCheck = m_aCharacterClassficator.lowercase(sCurrentCheck);
+
+ // now the test is easy...
+ bFound = aSearchExpression.Matches(sCurrentCheck);
+
+ if (bFound)
+ break;
+
+ // next field (implicitly next record, if necessary)
+ if (!MoveField(nFieldPos, iterFieldLoop, iterBegin, iterEnd))
+ { // When moving to the next field, something went wrong...
+ // Continuing is not possible, since the next time exactly the same
+ // will definitely go wrong again, thus abort.
+ // Before, however, so that the search continues at the current position:
+ try { m_aPreviousLocBookmark = m_xSearchCursor.getBookmark(); }
+ catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); }
+ m_iterPreviousLocField = iterFieldLoop;
+ // and leave
+ return SearchResult::Error;
+ }
+
+ Any aCurrentBookmark;
+ try { aCurrentBookmark = m_xSearchCursor.getBookmark(); }
+ catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); return SearchResult::Error; }
+
+ bMovedAround = EQUAL_BOOKMARKS(aStartMark, aCurrentBookmark) && (iterFieldLoop == iterInitialField);
+
+ if (nFieldPos == 0)
+ // that is, I've moved to a new record
+ PropagateProgress(bMovedAround);
+ // if we moved to the starting position we don't have to propagate an 'overflow' message
+ // FS - 07.12.99 - 68530
+
+ // cancel requested?
+ if (CancelRequested())
+ return SearchResult::Cancelled;
+
+ } while (!bMovedAround);
+
+ return bFound ? SearchResult::Found : SearchResult::NotFound;
+}
+
+
+FmSearchEngine::SearchResult FmSearchEngine::SearchRegularApprox(const OUString& strExpression, sal_Int32& nFieldPos,
+ FieldCollection::iterator& iterFieldLoop, const FieldCollection::iterator& iterBegin, const FieldCollection::iterator& iterEnd)
+{
+ DBG_ASSERT(m_bLevenshtein || m_bRegular,
+ "FmSearchEngine::SearchRegularApprox : invalid search mode!");
+ DBG_ASSERT(!m_bLevenshtein || !m_bRegular,
+ "FmSearchEngine::SearchRegularApprox : cannot search for regular expressions and similarities at the same time!");
+
+ // memorize start position
+ Any aStartMark;
+ try { aStartMark = m_xSearchCursor.getBookmark(); }
+ catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); return SearchResult::Error; }
+ FieldCollection::const_iterator iterInitialField = iterFieldLoop;
+
+ // collect parameters
+ i18nutil::SearchOptions2 aParam;
+ aParam.AlgorithmType2 = m_bRegular ? SearchAlgorithms2::REGEXP : SearchAlgorithms2::APPROXIMATE;
+ aParam.searchFlag = 0;
+ aParam.transliterateFlags = GetTransliterationFlags();
+ if ( !GetTransliteration() )
+ { // if transliteration is not enabled, the only flags which matter are IGNORE_CASE and IGNORE_WIDTH
+ aParam.transliterateFlags &= TransliterationFlags::IGNORE_CASE | TransliterationFlags::IGNORE_WIDTH;
+ }
+ if (m_bLevenshtein)
+ {
+ if (m_bLevRelaxed)
+ aParam.searchFlag |= SearchFlags::LEV_RELAXED;
+ aParam.changedChars = m_nLevOther;
+ aParam.deletedChars = m_nLevShorter;
+ aParam.insertedChars = m_nLevLonger;
+ }
+ aParam.searchString = strExpression;
+ aParam.Locale = SvtSysLocale().GetLanguageTag().getLocale();
+ ::utl::TextSearch aLocalEngine( aParam);
+
+
+ bool bFound = false;
+ bool bMovedAround(false);
+ do
+ {
+ Application::Reschedule( true );
+
+ // the content to be compared currently
+ OUString sCurrentCheck;
+ if (m_bFormatter)
+ sCurrentCheck = FormatField(nFieldPos);
+ else
+ sCurrentCheck = iterFieldLoop->xContents->getString();
+
+ // (don't care about case here, this is done by the TextSearch object, 'cause we passed our case parameter to it)
+
+ sal_Int32 nStart = 0, nEnd = sCurrentCheck.getLength();
+ bFound = aLocalEngine.SearchForward(sCurrentCheck, &nStart, &nEnd);
+ // it says 'forward' here, but that only refers to the search within
+ // sCurrentCheck, so it has nothing to do with the direction of my
+ // record migration (MoveField takes care of that)
+
+ // check if the position is correct
+ if (bFound)
+ {
+ switch (m_nPosition)
+ {
+ case MATCHING_WHOLETEXT :
+ if (nEnd != sCurrentCheck.getLength())
+ {
+ bFound = false;
+ break;
+ }
+ [[fallthrough]];
+ case MATCHING_BEGINNING :
+ if (nStart != 0)
+ bFound = false;
+ break;
+ case MATCHING_END :
+ if (nEnd != sCurrentCheck.getLength())
+ bFound = false;
+ break;
+ }
+ }
+
+ if (bFound) // still?
+ break;
+
+ // next field (implicitly next record, if necessary)
+ if (!MoveField(nFieldPos, iterFieldLoop, iterBegin, iterEnd))
+ { // When moving to the next field, something went wrong...
+ // Continuing is not possible, since the next time exactly the same
+ // will definitely go wrong again, thus abort (without error
+ // notification, I expect it to be displayed in the Move).
+ // Before, however, so that the search continues at the current position:
+ try { m_aPreviousLocBookmark = m_xSearchCursor.getBookmark(); }
+ catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); }
+ m_iterPreviousLocField = iterFieldLoop;
+ // and leave
+ return SearchResult::Error;
+ }
+
+ Any aCurrentBookmark;
+ try { aCurrentBookmark = m_xSearchCursor.getBookmark(); }
+ catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); return SearchResult::Error; }
+ bMovedAround = EQUAL_BOOKMARKS(aStartMark, aCurrentBookmark) && (iterFieldLoop == iterInitialField);
+
+ if (nFieldPos == 0)
+ // that is, I've moved to a new record
+ PropagateProgress(bMovedAround);
+ // if we moved to the starting position we don't have to propagate an 'overflow' message
+ // FS - 07.12.99 - 68530
+
+ // cancel requested?
+ if (CancelRequested())
+ return SearchResult::Cancelled;
+
+ } while (!bMovedAround);
+
+ return bFound ? SearchResult::Found : SearchResult::NotFound;
+}
+
+
+FmSearchEngine::FmSearchEngine(const Reference< XComponentContext >& _rxContext,
+ const Reference< XResultSet > & xCursor, std::u16string_view sVisibleFields,
+ const InterfaceArray& arrFields)
+ :m_xSearchCursor(xCursor)
+ ,m_aCharacterClassficator( _rxContext, SvtSysLocale().GetLanguageTag() )
+ ,m_aStringCompare( _rxContext )
+ ,m_nCurrentFieldIndex(-2) // -1 already has a meaning, so I take -2 for 'invalid'
+ ,m_xOriginalIterator(xCursor)
+ ,m_xClonedIterator(m_xOriginalIterator, true)
+ ,m_eSearchForType(SearchFor::String)
+ ,m_srResult(SearchResult::Found)
+ ,m_bCancelAsynchRequest(false)
+ ,m_bSearchingCurrently(false)
+ ,m_bFormatter(true) // this must be consistent with m_xSearchCursor, which is generally == m_xOriginalIterator
+ ,m_bForward(false)
+ ,m_bWildcard(false)
+ ,m_bRegular(false)
+ ,m_bLevenshtein(false)
+ ,m_bTransliteration(false)
+ ,m_bLevRelaxed(false)
+ ,m_nLevOther(0)
+ ,m_nLevShorter(0)
+ ,m_nLevLonger(0)
+ ,m_nPosition(MATCHING_ANYWHERE)
+ ,m_nTransliterationFlags(TransliterationFlags::NONE)
+{
+
+ fillControlTexts(arrFields);
+ Init(sVisibleFields);
+}
+
+
+void FmSearchEngine::SetIgnoreWidthCJK(bool bSet)
+{
+ if (bSet)
+ m_nTransliterationFlags |= TransliterationFlags::IGNORE_WIDTH;
+ else
+ m_nTransliterationFlags &= ~TransliterationFlags::IGNORE_WIDTH;
+}
+
+
+bool FmSearchEngine::GetIgnoreWidthCJK() const
+{
+ return bool(m_nTransliterationFlags & TransliterationFlags::IGNORE_WIDTH);
+}
+
+
+void FmSearchEngine::SetCaseSensitive(bool bSet)
+{
+ if (bSet)
+ m_nTransliterationFlags &= ~TransliterationFlags::IGNORE_CASE;
+ else
+ m_nTransliterationFlags |= TransliterationFlags::IGNORE_CASE;
+}
+
+
+bool FmSearchEngine::GetCaseSensitive() const
+{
+ return !(m_nTransliterationFlags & TransliterationFlags::IGNORE_CASE);
+}
+
+
+void FmSearchEngine::fillControlTexts(const InterfaceArray& arrFields)
+{
+ m_aControlTexts.clear();
+ Reference< XInterface > xCurrent;
+ for (const auto & rField : arrFields)
+ {
+ xCurrent = rField;
+ DBG_ASSERT(xCurrent.is(), "FmSearchEngine::fillControlTexts : invalid field interface !");
+ // check which type of control this is
+ Reference< css::awt::XTextComponent > xAsText(xCurrent, UNO_QUERY);
+ if (xAsText.is())
+ {
+ m_aControlTexts.emplace_back(new SimpleTextWrapper(xAsText));
+ continue;
+ }
+
+ Reference< css::awt::XListBox > xAsListBox(xCurrent, UNO_QUERY);
+ if (xAsListBox.is())
+ {
+ m_aControlTexts.emplace_back(new ListBoxWrapper(xAsListBox));
+ continue;
+ }
+
+ Reference< css::awt::XCheckBox > xAsCheckBox(xCurrent, UNO_QUERY);
+ DBG_ASSERT(xAsCheckBox.is(), "FmSearchEngine::fillControlTexts : invalid field interface (no supported type) !");
+ // we don't have any more options ...
+ m_aControlTexts.emplace_back(new CheckBoxWrapper(xAsCheckBox));
+ }
+}
+
+
+void FmSearchEngine::Init(std::u16string_view sVisibleFields)
+{
+ // analyze the fields
+ // additionally, create the mapping: because the list of used columns can be shorter than the list
+ // of columns of the cursor, we need a mapping: "used column number n" -> "cursor column m"
+ m_arrFieldMapping.clear();
+
+ // important: The case of the columns does not need to be exact - for instance:
+ // - a user created a form which works on a table, for which the driver returns a column name "COLUMN"
+ // - the driver itself works case-insensitive with column names
+ // - a control in the form is bound to "column" - not the different case
+ // In such a scenario, the form and the field would work okay, but we here need to case for the different case
+ // explicitly
+ // #i8755#
+
+ // so first of all, check if the database handles identifiers case sensitive
+ Reference< XConnection > xConn;
+ Reference< XDatabaseMetaData > xMeta;
+ Reference< XPropertySet > xCursorProps( IFACECAST( m_xSearchCursor ), UNO_QUERY );
+ if ( xCursorProps.is() )
+ {
+ try
+ {
+ xCursorProps->getPropertyValue( FM_PROP_ACTIVE_CONNECTION ) >>= xConn;
+ }
+ catch( const Exception& ) { /* silent this - will be asserted below */ }
+ }
+ if ( xConn.is() )
+ xMeta = xConn->getMetaData();
+ OSL_ENSURE( xMeta.is(), "FmSearchEngine::Init: very strange cursor (could not derive connection meta data from it)!" );
+
+ bool bCaseSensitiveIdentifiers = true; // assume case sensitivity
+ if ( xMeta.is() )
+ bCaseSensitiveIdentifiers = xMeta->supportsMixedCaseQuotedIdentifiers();
+
+ // now that we have this information, we need a collator which is able to case (in)sensitivity compare strings
+ m_aStringCompare.loadDefaultCollator( SvtSysLocale().GetLanguageTag().getLocale(),
+ bCaseSensitiveIdentifiers ? 0 : css::i18n::CollatorOptions::CollatorOptions_IGNORE_CASE );
+
+ try
+ {
+ // the cursor can give me a record (as PropertySet), which supports the DatabaseRecord service
+ Reference< css::sdbcx::XColumnsSupplier > xSupplyCols(IFACECAST(m_xSearchCursor), UNO_QUERY);
+ DBG_ASSERT(xSupplyCols.is(), "FmSearchEngine::Init : invalid cursor (no columns supplier) !");
+ Reference< css::container::XNameAccess > xAllFieldNames = xSupplyCols->getColumns();
+ const Sequence< OUString > seqFieldNames = xAllFieldNames->getElementNames();
+
+ OUString sCurrentField;
+ sal_Int32 nIndex = 0;
+ do
+ {
+ sCurrentField = o3tl::getToken(sVisibleFields, 0, ';' , nIndex);
+
+ // search in the field collection
+ sal_Int32 nFoundIndex = -1;
+ auto pFieldName = std::find_if(seqFieldNames.begin(), seqFieldNames.end(),
+ [this, &sCurrentField](const OUString& rFieldName) {
+ return 0 == m_aStringCompare.compareString( rFieldName, sCurrentField ); });
+ if (pFieldName != seqFieldNames.end())
+ nFoundIndex = static_cast<sal_Int32>(std::distance(seqFieldNames.begin(), pFieldName));
+ DBG_ASSERT(nFoundIndex != -1, "FmSearchEngine::Init : Invalid field name were given !");
+ m_arrFieldMapping.push_back(nFoundIndex);
+ }
+ while ( nIndex >= 0 );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx.form", "");
+ }
+
+}
+
+
+void FmSearchEngine::SetFormatterUsing(bool bSet)
+{
+ if (m_bFormatter == bSet)
+ return;
+ m_bFormatter = bSet;
+
+ // I did not use a formatter, but TextComponents -> the SearchIterator needs to be adjusted
+ try
+ {
+ if (m_bFormatter)
+ {
+ DBG_ASSERT(m_xSearchCursor == m_xClonedIterator, "FmSearchEngine::SetFormatterUsing : inconsistent state !");
+ m_xSearchCursor = m_xOriginalIterator;
+ m_xSearchCursor.moveToBookmark(m_xClonedIterator.getBookmark());
+ // so that I continue with the new iterator at the actual place where I previously stopped
+ }
+ else
+ {
+ DBG_ASSERT(m_xSearchCursor == m_xOriginalIterator, "FmSearchEngine::SetFormatterUsing : inconsistent state !");
+ m_xSearchCursor = m_xClonedIterator;
+ m_xSearchCursor.moveToBookmark(m_xOriginalIterator.getBookmark());
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ // I have to re-bind the fields, because the text exchange might take
+ // place over these fields and the underlying cursor has changed
+ RebuildUsedFields(m_nCurrentFieldIndex, true);
+}
+
+
+void FmSearchEngine::PropagateProgress(bool _bDontPropagateOverflow)
+{
+ if (!m_aProgressHandler.IsSet())
+ return;
+
+ FmSearchProgress aProgress;
+ try
+ {
+ aProgress.aSearchState = FmSearchProgress::State::Progress;
+ aProgress.nCurrentRecord = m_xSearchCursor.getRow() - 1;
+ if (m_bForward)
+ aProgress.bOverflow = !_bDontPropagateOverflow && m_xSearchCursor.isFirst();
+ else
+ aProgress.bOverflow = !_bDontPropagateOverflow && m_xSearchCursor.isLast();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ m_aProgressHandler.Call(&aProgress);
+}
+
+
+void FmSearchEngine::SearchNextImpl()
+{
+ DBG_ASSERT(!(m_bWildcard && m_bRegular) && !(m_bRegular && m_bLevenshtein) && !(m_bLevenshtein && m_bWildcard),
+ "FmSearchEngine::SearchNextImpl : search parameters are mutually exclusive!");
+
+ DBG_ASSERT(m_xSearchCursor.is(), "FmSearchEngine::SearchNextImpl : have invalid iterator!");
+
+ // the parameters of the search
+ OUString strSearchExpression(m_strSearchExpression); // I need non-const
+ if (!GetCaseSensitive())
+ // norm the string
+ strSearchExpression = m_aCharacterClassficator.lowercase(strSearchExpression);
+
+ if (!m_bRegular && !m_bLevenshtein)
+ { // 'normal' search I run through WildCards in any case, but must before adjust the OUString depending on the mode
+
+ if (!m_bWildcard)
+ { // since in all other cases * and ? in the search string are of course
+ // also allowed, but should not count as WildCards, I need to normalize
+ OUString aTmp(strSearchExpression);
+ aTmp = aTmp.replaceAll("*", "\\*");
+ aTmp = aTmp.replaceAll("?", "\\?");
+ strSearchExpression = aTmp;
+
+ switch (m_nPosition)
+ {
+ case MATCHING_ANYWHERE :
+ strSearchExpression = "*" + strSearchExpression + "*";
+ break;
+ case MATCHING_BEGINNING :
+ strSearchExpression += "*";
+ break;
+ case MATCHING_END :
+ strSearchExpression = "*" + strSearchExpression;
+ break;
+ case MATCHING_WHOLETEXT :
+ break;
+ default :
+ OSL_FAIL("FmSearchEngine::SearchNextImpl() : the methods listbox may contain only 4 entries ...");
+ }
+ }
+ }
+
+ // for work on field list
+ FieldCollection::iterator iterBegin = m_arrUsedFields.begin();
+ FieldCollection::iterator iterEnd = m_arrUsedFields.end();
+ FieldCollection::iterator iterFieldCheck;
+
+ sal_Int32 nFieldPos;
+
+ if (m_aPreviousLocBookmark.hasValue())
+ {
+ DBG_ASSERT(EQUAL_BOOKMARKS(m_aPreviousLocBookmark, m_xSearchCursor.getBookmark()),
+ "FmSearchEngine::SearchNextImpl : invalid position!");
+ iterFieldCheck = m_iterPreviousLocField;
+ // continue in the field after (or before) the last discovery
+ nFieldPos = iterFieldCheck - iterBegin;
+ MoveField(nFieldPos, iterFieldCheck, iterBegin, iterEnd);
+ }
+ else
+ {
+ if (m_bForward)
+ iterFieldCheck = iterBegin;
+ else
+ {
+ iterFieldCheck = iterEnd;
+ --iterFieldCheck;
+ }
+ nFieldPos = iterFieldCheck - iterBegin;
+ }
+
+ PropagateProgress(true);
+ SearchResult srResult;
+ if (m_eSearchForType != SearchFor::String)
+ srResult = SearchSpecial(m_eSearchForType == SearchFor::Null, nFieldPos, iterFieldCheck, iterBegin, iterEnd);
+ else if (!m_bRegular && !m_bLevenshtein)
+ srResult = SearchWildcard(strSearchExpression, nFieldPos, iterFieldCheck, iterBegin, iterEnd);
+ else
+ srResult = SearchRegularApprox(strSearchExpression, nFieldPos, iterFieldCheck, iterBegin, iterEnd);
+
+ m_srResult = srResult;
+
+ if (SearchResult::Error == m_srResult)
+ return;
+
+ // found?
+ if (SearchResult::Found == m_srResult)
+ {
+ // memorize the position
+ try { m_aPreviousLocBookmark = m_xSearchCursor.getBookmark(); }
+ catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); }
+ m_iterPreviousLocField = iterFieldCheck;
+ }
+ else
+ // invalidate the "last discovery"
+ InvalidatePreviousLoc();
+}
+
+
+void FmSearchEngine::OnSearchTerminated()
+{
+ if (!m_aProgressHandler.IsSet())
+ return;
+
+ FmSearchProgress aProgress;
+ try
+ {
+ switch (m_srResult)
+ {
+ case SearchResult::Error :
+ aProgress.aSearchState = FmSearchProgress::State::Error;
+ break;
+ case SearchResult::Found :
+ aProgress.aSearchState = FmSearchProgress::State::Successful;
+ aProgress.aBookmark = m_aPreviousLocBookmark;
+ aProgress.nFieldIndex = m_iterPreviousLocField - m_arrUsedFields.begin();
+ break;
+ case SearchResult::NotFound :
+ aProgress.aSearchState = FmSearchProgress::State::NothingFound;
+ aProgress.aBookmark = m_xSearchCursor.getBookmark();
+ break;
+ case SearchResult::Cancelled :
+ aProgress.aSearchState = FmSearchProgress::State::Canceled;
+ aProgress.aBookmark = m_xSearchCursor.getBookmark();
+ break;
+ }
+ aProgress.nCurrentRecord = m_xSearchCursor.getRow() - 1;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ // by definition, the link must be thread-safe (I just require that),
+ // so that I do not have to worry about such things here
+ m_aProgressHandler.Call(&aProgress);
+
+ m_bSearchingCurrently = false;
+}
+
+
+IMPL_LINK(FmSearchEngine, OnNewRecordCount, sal_Int32, theCounter, void)
+{
+ if (!m_aProgressHandler.IsSet())
+ return;
+
+ FmSearchProgress aProgress;
+ aProgress.nCurrentRecord = theCounter;
+ aProgress.aSearchState = FmSearchProgress::State::ProgressCounting;
+ m_aProgressHandler.Call(&aProgress);
+}
+
+
+bool FmSearchEngine::CancelRequested()
+{
+ bool bReturn = m_bCancelAsynchRequest;
+ return bReturn;
+}
+
+
+void FmSearchEngine::CancelSearch()
+{
+ m_bCancelAsynchRequest = true;
+}
+
+
+void FmSearchEngine::SwitchToContext(const Reference< css::sdbc::XResultSet > & xCursor, std::u16string_view sVisibleFields, const InterfaceArray& arrFields,
+ sal_Int32 nFieldIndex)
+{
+ DBG_ASSERT(!m_bSearchingCurrently, "FmSearchEngine::SwitchToContext : please do not call while I'm searching !");
+ if (m_bSearchingCurrently)
+ return;
+
+ m_xSearchCursor = xCursor;
+ m_xOriginalIterator = xCursor;
+ m_xClonedIterator = CursorWrapper(m_xOriginalIterator, true);
+
+ fillControlTexts(arrFields);
+
+ Init(sVisibleFields);
+ RebuildUsedFields(nFieldIndex, true);
+}
+
+
+void FmSearchEngine::ImplStartNextSearch()
+{
+ m_bCancelAsynchRequest = false;
+ m_bSearchingCurrently = true;
+
+ SearchNextImpl();
+ OnSearchTerminated();
+}
+
+
+void FmSearchEngine::SearchNext(const OUString& strExpression)
+{
+ m_strSearchExpression = strExpression;
+ m_eSearchForType = SearchFor::String;
+ ImplStartNextSearch();
+}
+
+
+void FmSearchEngine::SearchNextSpecial(bool _bSearchForNull)
+{
+ m_eSearchForType = _bSearchForNull ? SearchFor::Null : SearchFor::NotNull;
+ ImplStartNextSearch();
+}
+
+
+void FmSearchEngine::StartOver(const OUString& strExpression)
+{
+ try
+ {
+ if (m_bForward)
+ m_xSearchCursor.first();
+ else
+ m_xSearchCursor.last();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ return;
+ }
+
+ InvalidatePreviousLoc();
+ SearchNext(strExpression);
+}
+
+
+void FmSearchEngine::StartOverSpecial(bool _bSearchForNull)
+{
+ try
+ {
+ if (m_bForward)
+ m_xSearchCursor.first();
+ else
+ m_xSearchCursor.last();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ return;
+ }
+
+ InvalidatePreviousLoc();
+ SearchNextSpecial(_bSearchForNull);
+}
+
+
+void FmSearchEngine::InvalidatePreviousLoc()
+{
+ m_aPreviousLocBookmark.clear();
+ m_iterPreviousLocField = m_arrUsedFields.end();
+}
+
+
+void FmSearchEngine::RebuildUsedFields(sal_Int32 nFieldIndex, bool bForce)
+{
+ if (!bForce && (nFieldIndex == m_nCurrentFieldIndex))
+ return;
+ // (since I allow no change of the iterator from the outside, the same css::sdbcx::Index
+ // also always means the same column, so I have nothing to do)
+
+ DBG_ASSERT((nFieldIndex == -1) ||
+ ((nFieldIndex >= 0) &&
+ (o3tl::make_unsigned(nFieldIndex) < m_arrFieldMapping.size())),
+ "FmSearchEngine::RebuildUsedFields : nFieldIndex is invalid!");
+ // collect all fields I need to search through
+ m_arrUsedFields.clear();
+ if (nFieldIndex == -1)
+ {
+ Reference< css::container::XIndexAccess > xFields;
+ for (sal_Int32 i : m_arrFieldMapping)
+ {
+ Reference< css::sdbcx::XColumnsSupplier > xSupplyCols(IFACECAST(m_xSearchCursor), UNO_QUERY);
+ DBG_ASSERT(xSupplyCols.is(), "FmSearchEngine::RebuildUsedFields : invalid cursor (no columns supplier) !");
+ xFields.set(xSupplyCols->getColumns(), UNO_QUERY);
+ BuildAndInsertFieldInfo(xFields, i);
+ }
+ }
+ else
+ {
+ Reference< css::container::XIndexAccess > xFields;
+ Reference< css::sdbcx::XColumnsSupplier > xSupplyCols(IFACECAST(m_xSearchCursor), UNO_QUERY);
+ DBG_ASSERT(xSupplyCols.is(), "FmSearchEngine::RebuildUsedFields : invalid cursor (no columns supplier) !");
+ xFields.set (xSupplyCols->getColumns(), UNO_QUERY);
+ BuildAndInsertFieldInfo(xFields, m_arrFieldMapping[static_cast< size_t >(nFieldIndex)]);
+ }
+
+ m_nCurrentFieldIndex = nFieldIndex;
+ // and of course I start the next search in a virgin state again
+ InvalidatePreviousLoc();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmtextcontroldialogs.cxx b/svx/source/form/fmtextcontroldialogs.cxx
new file mode 100644
index 0000000000..cc1d511aa6
--- /dev/null
+++ b/svx/source/form/fmtextcontroldialogs.cxx
@@ -0,0 +1,79 @@
+/* -*- 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 <fmtextcontroldialogs.hxx>
+#include <svx/dialogs.hrc>
+#include <svx/svxids.hrc>
+
+#include <svx/flagsdef.hxx>
+#include <svl/intitem.hxx>
+
+#include <svl/cjkoptions.hxx>
+#include <utility>
+
+
+namespace svx
+{
+
+ TextControlCharAttribDialog::TextControlCharAttribDialog(weld::Window* pParent, const SfxItemSet& rCoreSet, SvxFontListItem aFontList)
+ : SfxTabDialogController(pParent, "svx/ui/textcontrolchardialog.ui", "TextControlCharacterPropertiesDialog", &rCoreSet)
+ , m_aFontList(std::move(aFontList))
+ {
+ AddTabPage("font", RID_SVXPAGE_CHAR_NAME);
+ AddTabPage("fonteffects", RID_SVXPAGE_CHAR_EFFECTS);
+ AddTabPage("position", RID_SVXPAGE_CHAR_POSITION);
+ }
+
+ void TextControlCharAttribDialog::PageCreated(const OUString& rId, SfxTabPage& rPage)
+ {
+ SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool()));
+
+ if (rId == "font")
+ {
+ aSet.Put (m_aFontList);
+ rPage.PageCreated(aSet);
+ }
+ else if (rId == "fonteffects")
+ {
+ aSet.Put (SfxUInt16Item(SID_DISABLE_CTL,DISABLE_CASEMAP));
+ rPage.PageCreated(aSet);
+ }
+ else if (rId == "position")
+ {
+ aSet.Put( SfxUInt32Item(SID_FLAG_TYPE, SVX_PREVIEW_CHARACTER) );
+ rPage.PageCreated(aSet);
+ }
+ }
+
+ TextControlParaAttribDialog::TextControlParaAttribDialog(weld::Window* pParent, const SfxItemSet& rCoreSet)
+ : SfxTabDialogController(pParent, "svx/ui/textcontrolparadialog.ui", "TextControlParagraphPropertiesDialog", &rCoreSet)
+ {
+ AddTabPage("labelTP_PARA_STD", RID_SVXPAGE_STD_PARAGRAPH);
+ AddTabPage("labelTP_PARA_ALIGN", RID_SVXPAGE_ALIGN_PARAGRAPH);
+
+ if( SvtCJKOptions::IsAsianTypographyEnabled() )
+ AddTabPage("labelTP_PARA_ASIAN", RID_SVXPAGE_PARA_ASIAN);
+ else
+ RemoveTabPage("labelTP_PARA_ASIAN");
+
+ AddTabPage("labelTP_TABULATOR", RID_SVXPAGE_TABULATOR);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmtextcontrolfeature.cxx b/svx/source/form/fmtextcontrolfeature.cxx
new file mode 100644
index 0000000000..ac25a9483f
--- /dev/null
+++ b/svx/source/form/fmtextcontrolfeature.cxx
@@ -0,0 +1,119 @@
+/* -*- 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 <fmtextcontrolfeature.hxx>
+#include <fmtextcontrolshell.hxx>
+#include <utility>
+
+#include <osl/diagnose.h>
+#include <comphelper/diagnose_ex.hxx>
+
+namespace svx
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::frame;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::util;
+
+ FmTextControlFeature::FmTextControlFeature( const Reference< XDispatch >& _rxDispatcher, URL _aFeatureURL, SfxSlotId _nSlotId, FmTextControlShell* _pInvalidator )
+ :m_xDispatcher ( _rxDispatcher )
+ ,m_aFeatureURL (std::move( _aFeatureURL ))
+ ,m_nSlotId ( _nSlotId )
+ ,m_pInvalidator ( _pInvalidator )
+ ,m_bFeatureEnabled( false )
+ {
+ OSL_ENSURE( _rxDispatcher.is(), "FmTextControlFeature::FmTextControlFeature: invalid dispatcher!" );
+ OSL_ENSURE( m_nSlotId, "FmTextControlFeature::FmTextControlFeature: invalid slot id!" );
+ OSL_ENSURE( m_pInvalidator, "FmTextControlFeature::FmTextControlFeature: invalid invalidator!" );
+
+ osl_atomic_increment( &m_refCount );
+ try
+ {
+ m_xDispatcher->addStatusListener( this, m_aFeatureURL );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "FmTextControlFeature::FmTextControlFeature" );
+ }
+ osl_atomic_decrement( &m_refCount );
+ }
+
+
+ FmTextControlFeature::~FmTextControlFeature( )
+ {
+ }
+
+
+ void FmTextControlFeature::dispatch() const
+ {
+ dispatch( Sequence< PropertyValue >( ) );
+ }
+
+
+ void FmTextControlFeature::dispatch( const Sequence< PropertyValue >& _rArgs ) const
+ {
+ try
+ {
+ if ( m_xDispatcher.is() )
+ m_xDispatcher->dispatch( m_aFeatureURL, _rArgs );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "FmTextControlFeature::dispatch" );
+ }
+ }
+
+
+ void SAL_CALL FmTextControlFeature::statusChanged( const FeatureStateEvent& _rState )
+ {
+ m_aFeatureState = _rState.State;
+ m_bFeatureEnabled = _rState.IsEnabled;
+
+ if ( m_pInvalidator )
+ m_pInvalidator->Invalidate( m_nSlotId );
+ }
+
+
+ void SAL_CALL FmTextControlFeature::disposing( const EventObject& /*Source*/ )
+ {
+ // nothing to do
+ }
+
+
+ void FmTextControlFeature::dispose()
+ {
+ try
+ {
+ m_xDispatcher->removeStatusListener( this, m_aFeatureURL );
+ m_xDispatcher.clear();
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "FmTextControlFeature::dispose" );
+ }
+ }
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmtextcontrolshell.cxx b/svx/source/form/fmtextcontrolshell.cxx
new file mode 100644
index 0000000000..e938a0ad14
--- /dev/null
+++ b/svx/source/form/fmtextcontrolshell.cxx
@@ -0,0 +1,1309 @@
+/* -*- 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 <fmprop.hxx>
+#include <fmtextcontroldialogs.hxx>
+#include <fmtextcontrolfeature.hxx>
+#include <fmtextcontrolshell.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/scriptspaceitem.hxx>
+#include <svx/svxids.hrc>
+#include <editeng/udlnitem.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/awt/XFocusListener.hpp>
+#include <com/sun/star/awt/XMouseListener.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/msgpool.hxx>
+#include <sfx2/msg.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/sfxuno.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svl/eitem.hxx>
+#include <svl/itempool.hxx>
+#include <svl/ctloptions.hxx>
+#include <svtools/stringtransfer.hxx>
+#include <svl/whiter.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <sal/log.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+
+#include <memory>
+
+
+namespace svx
+{
+
+
+ using namespace ::com::sun::star;
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::form;
+ using namespace ::com::sun::star::form::runtime;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::frame;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::container;
+
+
+ typedef sal_uInt16 WhichId;
+
+
+ static SfxSlotId pTextControlSlots[] =
+ {
+ SID_CLIPBOARD_FORMAT_ITEMS,
+ SID_CUT,
+ SID_COPY,
+ SID_PASTE,
+ SID_SELECTALL,
+// SID_ATTR_TABSTOP, /* 2 */
+ SID_ATTR_CHAR_FONT,
+ SID_ATTR_CHAR_POSTURE,
+ SID_ATTR_CHAR_WEIGHT,
+ SID_ATTR_CHAR_SHADOWED,
+ SID_ATTR_CHAR_WORDLINEMODE,
+ SID_ATTR_CHAR_CONTOUR,
+ SID_ATTR_CHAR_STRIKEOUT,
+ SID_ATTR_CHAR_UNDERLINE,
+ SID_ATTR_CHAR_FONTHEIGHT,
+ SID_ATTR_CHAR_COLOR,
+ SID_ATTR_CHAR_KERNING,
+ SID_ATTR_CHAR_LANGUAGE, /* 20 */
+ SID_ATTR_CHAR_ESCAPEMENT,
+ SID_ATTR_PARA_ADJUST, /* 28 */
+ SID_ATTR_PARA_ADJUST_LEFT,
+ SID_ATTR_PARA_ADJUST_RIGHT,
+ SID_ATTR_PARA_ADJUST_CENTER,
+ SID_ATTR_PARA_ADJUST_BLOCK,
+ SID_ATTR_PARA_LINESPACE, /* 33 */
+ SID_ATTR_PARA_LINESPACE_10,
+ SID_ATTR_PARA_LINESPACE_15,
+ SID_ATTR_PARA_LINESPACE_20,
+ SID_ATTR_LRSPACE, /* 48 */
+ SID_ATTR_ULSPACE, /* 49 */
+ SID_ATTR_CHAR_AUTOKERN,
+ SID_SET_SUPER_SCRIPT,
+ SID_SET_SUB_SCRIPT,
+ SID_CHAR_DLG,
+ SID_PARA_DLG,
+// SID_TEXTDIRECTION_LEFT_TO_RIGHT, /* 907 */
+// SID_TEXTDIRECTION_TOP_TO_BOTTOM,
+ SID_ATTR_CHAR_SCALEWIDTH, /* 911 */
+ SID_ATTR_CHAR_RELIEF,
+ SID_ATTR_PARA_LEFT_TO_RIGHT, /* 950 */
+ SID_ATTR_PARA_RIGHT_TO_LEFT,
+ SID_ATTR_CHAR_OVERLINE,
+ 0
+ };
+
+ // slots which we are not responsible for on the SfxShell level, but
+ // need to handle during the "paragraph attributes" and/or "character
+ // attributes" dialogs
+ static SfxSlotId pDialogSlots[] =
+ {
+ SID_ATTR_TABSTOP,
+ SID_ATTR_PARA_HANGPUNCTUATION,
+ SID_ATTR_PARA_FORBIDDEN_RULES,
+ SID_ATTR_PARA_SCRIPTSPACE,
+ SID_ATTR_CHAR_LATIN_LANGUAGE,
+ SID_ATTR_CHAR_CJK_LANGUAGE,
+ SID_ATTR_CHAR_CTL_LANGUAGE,
+ SID_ATTR_CHAR_LATIN_FONT,
+ SID_ATTR_CHAR_CJK_FONT,
+ SID_ATTR_CHAR_CTL_FONT,
+ SID_ATTR_CHAR_LATIN_FONTHEIGHT,
+ SID_ATTR_CHAR_CJK_FONTHEIGHT,
+ SID_ATTR_CHAR_CTL_FONTHEIGHT,
+ SID_ATTR_CHAR_LATIN_WEIGHT,
+ SID_ATTR_CHAR_CJK_WEIGHT,
+ SID_ATTR_CHAR_CTL_WEIGHT,
+ SID_ATTR_CHAR_LATIN_POSTURE,
+ SID_ATTR_CHAR_CJK_POSTURE,
+ SID_ATTR_CHAR_CTL_POSTURE,
+ SID_ATTR_CHAR_EMPHASISMARK,
+ 0
+ };
+
+ typedef ::cppu::WeakImplHelper < css::awt::XFocusListener
+ > FmFocusListenerAdapter_Base;
+ class FmFocusListenerAdapter : public FmFocusListenerAdapter_Base
+ {
+ private:
+ IFocusObserver* m_pObserver;
+ Reference< css::awt::XWindow > m_xWindow;
+
+ public:
+ FmFocusListenerAdapter( const Reference< css::awt::XControl >& _rxControl, IFocusObserver* _pObserver );
+
+ // clean up the instance
+ void dispose();
+
+ protected:
+ virtual ~FmFocusListenerAdapter() override;
+
+ protected:
+ virtual void SAL_CALL focusGained( const css::awt::FocusEvent& e ) override;
+ virtual void SAL_CALL focusLost( const css::awt::FocusEvent& e ) override;
+ virtual void SAL_CALL disposing( const EventObject& Source ) override;
+ };
+
+
+ FmFocusListenerAdapter::FmFocusListenerAdapter( const Reference< css::awt::XControl >& _rxControl, IFocusObserver* _pObserver )
+ :m_pObserver( _pObserver )
+ ,m_xWindow( _rxControl, UNO_QUERY )
+ {
+
+ DBG_ASSERT( m_xWindow.is(), "FmFocusListenerAdapter::FmFocusListenerAdapter: invalid control!" );
+ osl_atomic_increment( &m_refCount );
+ {
+ try
+ {
+ if ( m_xWindow.is() )
+ m_xWindow->addFocusListener( this );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ osl_atomic_decrement( &m_refCount );
+ }
+
+
+ FmFocusListenerAdapter::~FmFocusListenerAdapter()
+ {
+ acquire();
+ dispose();
+
+ }
+
+
+ void FmFocusListenerAdapter::dispose()
+ {
+ if ( m_xWindow.is() )
+ {
+ m_xWindow->removeFocusListener( this );
+ m_xWindow.clear();
+ }
+ }
+
+
+ void SAL_CALL FmFocusListenerAdapter::focusGained( const css::awt::FocusEvent& e )
+ {
+ if ( m_pObserver )
+ m_pObserver->focusGained( e );
+ }
+
+
+ void SAL_CALL FmFocusListenerAdapter::focusLost( const css::awt::FocusEvent& e )
+ {
+ if ( m_pObserver )
+ m_pObserver->focusLost( e );
+ }
+
+
+ void SAL_CALL FmFocusListenerAdapter::disposing( const EventObject& Source )
+ {
+ DBG_ASSERT( Source.Source == m_xWindow, "FmFocusListenerAdapter::disposing: where did this come from?" );
+ m_xWindow.clear();
+ }
+
+ typedef ::cppu::WeakImplHelper < css::awt::XMouseListener
+ > FmMouseListenerAdapter_Base;
+ class FmMouseListenerAdapter : public FmMouseListenerAdapter_Base
+ {
+ private:
+ IContextRequestObserver* m_pObserver;
+ Reference< css::awt::XWindow > m_xWindow;
+
+ public:
+ FmMouseListenerAdapter( const Reference< css::awt::XControl >& _rxControl, IContextRequestObserver* _pObserver );
+
+ // clean up the instance
+ void dispose();
+
+ protected:
+ virtual ~FmMouseListenerAdapter() override;
+
+ protected:
+ virtual void SAL_CALL mousePressed( const css::awt::MouseEvent& e ) override;
+ virtual void SAL_CALL mouseReleased( const css::awt::MouseEvent& e ) override;
+ virtual void SAL_CALL mouseEntered( const css::awt::MouseEvent& e ) override;
+ virtual void SAL_CALL mouseExited( const css::awt::MouseEvent& e ) override;
+ virtual void SAL_CALL disposing( const EventObject& Source ) override;
+ };
+
+ FmMouseListenerAdapter::FmMouseListenerAdapter( const Reference< css::awt::XControl >& _rxControl, IContextRequestObserver* _pObserver )
+ :m_pObserver( _pObserver )
+ ,m_xWindow( _rxControl, UNO_QUERY )
+ {
+
+ DBG_ASSERT( m_xWindow.is(), "FmMouseListenerAdapter::FmMouseListenerAdapter: invalid control!" );
+ osl_atomic_increment( &m_refCount );
+ {
+ try
+ {
+ if ( m_xWindow.is() )
+ m_xWindow->addMouseListener( this );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ osl_atomic_decrement( &m_refCount );
+ }
+
+
+ FmMouseListenerAdapter::~FmMouseListenerAdapter()
+ {
+ acquire();
+ dispose();
+
+ }
+
+
+ void FmMouseListenerAdapter::dispose()
+ {
+ if ( m_xWindow.is() )
+ {
+ m_xWindow->removeMouseListener( this );
+ m_xWindow.clear();
+ }
+ }
+
+
+ void SAL_CALL FmMouseListenerAdapter::mousePressed( const css::awt::MouseEvent& _rEvent )
+ {
+ SolarMutexGuard aGuard;
+ // is this a request for a context menu?
+ if ( _rEvent.PopupTrigger )
+ {
+ if ( m_pObserver )
+ m_pObserver->contextMenuRequested();
+ }
+ }
+
+
+ void SAL_CALL FmMouseListenerAdapter::mouseReleased( const css::awt::MouseEvent& /*e*/ )
+ {
+ // not interested in
+ }
+
+
+ void SAL_CALL FmMouseListenerAdapter::mouseEntered( const css::awt::MouseEvent& /*e*/ )
+ {
+ // not interested in
+ }
+
+
+ void SAL_CALL FmMouseListenerAdapter::mouseExited( const css::awt::MouseEvent& /*e*/ )
+ {
+ // not interested in
+ }
+
+
+ void SAL_CALL FmMouseListenerAdapter::disposing( const EventObject& Source )
+ {
+ DBG_ASSERT( Source.Source == m_xWindow, "FmMouseListenerAdapter::disposing: where did this come from?" );
+ m_xWindow.clear();
+ }
+
+
+ //= FmTextControlShell
+
+
+ namespace
+ {
+
+ void lcl_translateUnoStateToItem( SfxSlotId _nSlot, const Any& _rUnoState, SfxItemSet& _rSet )
+ {
+ WhichId nWhich = _rSet.GetPool()->GetWhich( _nSlot );
+ if ( !_rUnoState.hasValue() )
+ {
+ if ( ( _nSlot != SID_CUT )
+ && ( _nSlot != SID_COPY )
+ && ( _nSlot != SID_PASTE )
+ )
+ {
+ _rSet.InvalidateItem( nWhich );
+ }
+ }
+ else
+ {
+ switch ( _rUnoState.getValueType().getTypeClass() )
+ {
+ case TypeClass_BOOLEAN:
+ {
+ bool bState = false;
+ _rUnoState >>= bState;
+ if ( _nSlot == SID_ATTR_PARA_SCRIPTSPACE )
+ _rSet.Put( SvxScriptSpaceItem( bState, nWhich ) );
+ else
+ _rSet.Put( SfxBoolItem( nWhich, bState ) );
+ }
+ break;
+
+ default:
+ {
+ Sequence< PropertyValue > aComplexState;
+ if ( _rUnoState >>= aComplexState )
+ {
+ if ( !aComplexState.hasElements() )
+ _rSet.InvalidateItem( nWhich );
+ else
+ {
+ SfxAllItemSet aAllItems( _rSet );
+ TransformParameters( _nSlot, aComplexState, aAllItems );
+ const SfxPoolItem* pTransformed = aAllItems.GetItem( nWhich );
+ OSL_ENSURE( pTransformed, "lcl_translateUnoStateToItem: non-empty parameter sequence leading to empty item?" );
+ if ( pTransformed )
+ _rSet.Put( *pTransformed );
+ else
+ _rSet.InvalidateItem( nWhich );
+ }
+ }
+ else
+ {
+ OSL_FAIL( "lcl_translateUnoStateToItem: invalid state!" );
+ }
+ }
+ }
+ }
+ }
+
+
+ OUString lcl_getUnoSlotName( SfxSlotId _nSlotId )
+ {
+ SfxSlotPool& rSlotPool = SfxSlotPool::GetSlotPool();
+ const SfxSlot* pSlot = rSlotPool.GetSlot( _nSlotId );
+
+ OUString sUnoName;
+ if ( pSlot )
+ {
+ sUnoName = pSlot->GetCommand();
+ }
+ else
+ {
+ // some hard-coded slots, which do not have a UNO name at SFX level, but which
+ // we nevertheless need to transport via UNO mechanisms, so we need a name
+ switch ( _nSlotId )
+ {
+ case SID_ATTR_PARA_HANGPUNCTUATION: sUnoName = ".uno:AllowHangingPunctuation"; break;
+ case SID_ATTR_PARA_FORBIDDEN_RULES: sUnoName = ".uno:ApplyForbiddenCharacterRules"; break;
+ case SID_ATTR_PARA_SCRIPTSPACE: sUnoName = ".uno:UseScriptSpacing"; break;
+ }
+ }
+
+ if (sUnoName.isEmpty())
+ {
+ SAL_WARN( "svx", "lcl_getUnoSlotName: invalid slot id, or invalid slot, or no UNO name! "
+ "(slot id: " << _nSlotId << ")");
+ }
+ return sUnoName;
+ }
+
+
+ bool lcl_determineReadOnly( const Reference< css::awt::XControl >& _rxControl )
+ {
+ bool bIsReadOnlyModel = true;
+ try
+ {
+ Reference< XPropertySet > xModelProps;
+ if ( _rxControl.is() )
+ xModelProps.set(_rxControl->getModel(), css::uno::UNO_QUERY);
+ Reference< XPropertySetInfo > xModelPropInfo;
+ if ( xModelProps.is() )
+ xModelPropInfo = xModelProps->getPropertySetInfo();
+
+ if ( !xModelPropInfo.is() || !xModelPropInfo->hasPropertyByName( FM_PROP_READONLY ) )
+ bIsReadOnlyModel = true;
+ else
+ {
+ bool bReadOnly = true;
+ xModelProps->getPropertyValue( FM_PROP_READONLY ) >>= bReadOnly;
+ bIsReadOnlyModel = bReadOnly;
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return bIsReadOnlyModel;
+ }
+
+
+ vcl::Window* lcl_getWindow( const Reference< css::awt::XControl >& _rxControl )
+ {
+ vcl::Window* pWindow = nullptr;
+ try
+ {
+ Reference< css::awt::XWindowPeer > xControlPeer;
+ if ( _rxControl.is() )
+ xControlPeer = _rxControl->getPeer();
+ if ( xControlPeer.is() )
+ pWindow = VCLUnoHelper::GetWindow( xControlPeer );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ return pWindow;
+ }
+
+
+ bool lcl_isRichText( const Reference< css::awt::XControl >& _rxControl )
+ {
+ if ( !_rxControl.is() )
+ return false;
+
+ bool bIsRichText = false;
+ try
+ {
+ Reference< XPropertySet > xModelProps( _rxControl->getModel(), UNO_QUERY );
+ Reference< XPropertySetInfo > xPSI;
+ if ( xModelProps.is() )
+ xPSI = xModelProps->getPropertySetInfo();
+ OUString sRichTextPropertyName = "RichText";
+ if ( xPSI.is() && xPSI->hasPropertyByName( sRichTextPropertyName ) )
+ {
+ OSL_VERIFY( xModelProps->getPropertyValue( sRichTextPropertyName ) >>= bIsRichText );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return bIsRichText;
+ }
+ }
+
+
+ FmTextControlShell::FmTextControlShell( SfxViewFrame* _pFrame )
+ :m_bActiveControl( false )
+ ,m_bActiveControlIsReadOnly( true )
+ ,m_bActiveControlIsRichText( false )
+ ,m_pViewFrame( _pFrame )
+ ,m_rBindings( _pFrame->GetBindings() )
+ ,m_aClipboardInvalidation("svx FmTextControlShell m_aClipboardInvalidation")
+ ,m_bNeedClipboardInvalidation( true )
+ {
+ m_aClipboardInvalidation.SetInvokeHandler( LINK( this, FmTextControlShell, OnInvalidateClipboard ) );
+ m_aClipboardInvalidation.SetTimeout( 200 );
+ }
+
+
+ FmTextControlShell::~FmTextControlShell()
+ {
+ dispose();
+ }
+
+
+ IMPL_LINK_NOARG( FmTextControlShell, OnInvalidateClipboard, Timer*, void )
+ {
+ if ( m_bNeedClipboardInvalidation )
+ {
+ SAL_INFO("svx.form", "invalidating clipboard slots" );
+ m_rBindings.Invalidate( SID_CUT );
+ m_rBindings.Invalidate( SID_COPY );
+ m_rBindings.Invalidate( SID_PASTE );
+ m_bNeedClipboardInvalidation = false;
+ }
+ }
+
+
+ void FmTextControlShell::transferFeatureStatesToItemSet( ControlFeatures& _rDispatchers, SfxAllItemSet& _rSet, bool _bTranslateLatin )
+ {
+ SfxItemPool& rPool = *_rSet.GetPool();
+
+ for (const auto& rFeature : _rDispatchers)
+ {
+ SfxSlotId nSlotId( rFeature.first );
+#if OSL_DEBUG_LEVEL > 0
+ OUString sUnoSlotName;
+ if ( SfxGetpApp() )
+ sUnoSlotName = lcl_getUnoSlotName( nSlotId );
+ else
+ sUnoSlotName = "<unknown>";
+ OString sUnoSlotNameAscii = "\"" +
+ OUStringToOString( sUnoSlotName, RTL_TEXTENCODING_ASCII_US ) +
+ "\"";
+#endif
+
+ if ( _bTranslateLatin )
+ {
+ // A rich text control offers a dispatcher for the "Font" slot/feature.
+ // Sadly, the semantics of the dispatches is that the feature "Font" depends
+ // on the current cursor position: If it's on latin text, it's the "latin font"
+ // which is set up at the control. If it's on CJK text, it's the "CJK font", and
+ // equivalent for "CTL font".
+ // The same holds for some other font related features/slots.
+ // Thus, we have separate dispatches for "Latin Font", "Latin Font Size", etc,
+ // which are only "virtual", in a sense that there exist no item with this id.
+ // So when we encounter such a dispatcher for, say, "Latin Font", we need to
+ // put an item into the set which has the "Font" id.
+
+ switch ( nSlotId )
+ {
+ case SID_ATTR_CHAR_LATIN_FONT: nSlotId = SID_ATTR_CHAR_FONT; break;
+ case SID_ATTR_CHAR_LATIN_FONTHEIGHT:nSlotId = SID_ATTR_CHAR_FONTHEIGHT; break;
+ case SID_ATTR_CHAR_LATIN_LANGUAGE: nSlotId = SID_ATTR_CHAR_LANGUAGE; break;
+ case SID_ATTR_CHAR_LATIN_POSTURE: nSlotId = SID_ATTR_CHAR_POSTURE; break;
+ case SID_ATTR_CHAR_LATIN_WEIGHT: nSlotId = SID_ATTR_CHAR_WEIGHT; break;
+ }
+ }
+
+ WhichId nWhich = rPool.GetWhich( nSlotId );
+ bool bIsInPool = rPool.IsInRange( nWhich );
+ if ( bIsInPool )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ bool bFeatureIsEnabled = rFeature.second->isFeatureEnabled();
+ OString sMessage = "found a feature state for " + sUnoSlotNameAscii;
+ if ( !bFeatureIsEnabled )
+ sMessage += " (disabled)";
+ SAL_INFO("svx.form", sMessage );
+#endif
+
+ lcl_translateUnoStateToItem( nSlotId, rFeature.second->getFeatureState(), _rSet );
+ }
+#if OSL_DEBUG_LEVEL > 0
+ else
+ {
+ SAL_WARN("svx.form", "found a feature state for " << sUnoSlotNameAscii << ", but could not translate it into an item!" );
+ }
+#endif
+ }
+ }
+
+
+ void FmTextControlShell::executeAttributeDialog( AttributeSet _eSet, SfxRequest& rReq )
+ {
+ const SvxFontListItem* pFontList = dynamic_cast<const SvxFontListItem*>( m_pViewFrame->GetObjectShell()->GetItem( SID_ATTR_CHAR_FONTLIST ) );
+ DBG_ASSERT( pFontList, "FmTextControlShell::executeAttributeDialog: no font list item!" );
+ if ( !pFontList )
+ return;
+
+ rtl::Reference<SfxItemPool> pPool(EditEngine::CreatePool());
+ pPool->FreezeIdRanges();
+ std::optional< SfxItemSet > xPureItems(( SfxItemSet( *pPool ) ));
+
+ // put the current states of the items into the set
+ std::optional<SfxAllItemSet> xCurrentItems(( SfxAllItemSet( *xPureItems ) ));
+ transferFeatureStatesToItemSet( m_aControlFeatures, *xCurrentItems, false );
+
+ // additional items, which we are not responsible for at the SfxShell level,
+ // but which need to be forwarded to the dialog, anyway
+ ControlFeatures aAdditionalFestures;
+ fillFeatureDispatchers( m_xActiveControl, pDialogSlots, aAdditionalFestures );
+ transferFeatureStatesToItemSet( aAdditionalFestures, *xCurrentItems, true );
+
+ std::unique_ptr<SfxTabDialogController> xDialog;
+ if (_eSet == eCharAttribs)
+ xDialog = std::make_unique<TextControlCharAttribDialog>(rReq.GetFrameWeld(), *xCurrentItems, *pFontList);
+ else
+ xDialog = std::make_unique<TextControlParaAttribDialog>(rReq.GetFrameWeld(), *xCurrentItems);
+ if ( RET_OK == xDialog->run() )
+ {
+ const SfxItemSet& rModifiedItems = *xDialog->GetOutputItemSet();
+ for ( WhichId nWhich = pPool->GetFirstWhich(); nWhich <= pPool->GetLastWhich(); ++nWhich )
+ {
+ if ( rModifiedItems.GetItemState( nWhich ) == SfxItemState::SET )
+ {
+ SfxSlotId nSlotForItemSet = pPool->GetSlotId( nWhich );
+ const SfxPoolItem* pModifiedItem = rModifiedItems.GetItem( nWhich );
+
+
+ SfxSlotId nSlotForDispatcher = nSlotForItemSet;
+ switch ( nSlotForDispatcher )
+ {
+ case SID_ATTR_CHAR_FONT: nSlotForDispatcher = SID_ATTR_CHAR_LATIN_FONT; break;
+ case SID_ATTR_CHAR_FONTHEIGHT:nSlotForDispatcher = SID_ATTR_CHAR_LATIN_FONTHEIGHT; break;
+ case SID_ATTR_CHAR_LANGUAGE: nSlotForDispatcher = SID_ATTR_CHAR_LATIN_LANGUAGE; break;
+ case SID_ATTR_CHAR_POSTURE: nSlotForDispatcher = SID_ATTR_CHAR_LATIN_POSTURE; break;
+ case SID_ATTR_CHAR_WEIGHT: nSlotForDispatcher = SID_ATTR_CHAR_LATIN_WEIGHT; break;
+ }
+
+ // do we already have a dispatcher for this slot/feature?
+ ControlFeatures::const_iterator aFeaturePos = m_aControlFeatures.find( nSlotForDispatcher );
+ bool bFound = aFeaturePos != m_aControlFeatures.end( );
+
+ if ( !bFound )
+ {
+ aFeaturePos = aAdditionalFestures.find( nSlotForDispatcher );
+ bFound = aFeaturePos != aAdditionalFestures.end( );
+ }
+
+ if ( bFound )
+ {
+ Sequence< PropertyValue > aArgs;
+ // temporarily put the modified item into a "clean" set,
+ // and let TransformItems calc the respective UNO parameters
+ xPureItems->Put( *pModifiedItem );
+ TransformItems( nSlotForItemSet, *xPureItems, aArgs );
+ xPureItems->ClearItem( nWhich );
+
+ if ( ( nSlotForItemSet == SID_ATTR_PARA_HANGPUNCTUATION )
+ || ( nSlotForItemSet == SID_ATTR_PARA_FORBIDDEN_RULES )
+ || ( nSlotForItemSet == SID_ATTR_PARA_SCRIPTSPACE )
+ )
+ {
+ // these are no UNO slots, they need special handling since TransformItems cannot
+ // handle them
+ DBG_ASSERT( !aArgs.hasElements(), "FmTextControlShell::executeAttributeDialog: these are no UNO slots - are they?" );
+
+ const SfxBoolItem* pBoolItem = dynamic_cast<const SfxBoolItem*>( pModifiedItem );
+ DBG_ASSERT( pBoolItem, "FmTextControlShell::executeAttributeDialog: no bool item?!" );
+ if ( pBoolItem )
+ {
+ aArgs = { comphelper::makePropertyValue("Enable",
+ pBoolItem->GetValue()) };
+ }
+ }
+
+ // dispatch this
+ aFeaturePos->second->dispatch( aArgs );
+ }
+ #if OSL_DEBUG_LEVEL > 0
+ else
+ {
+ OUString sUnoSlotName = lcl_getUnoSlotName( nSlotForItemSet );
+ if ( sUnoSlotName.isEmpty() )
+ sUnoSlotName = "unknown (no SfxSlot)";
+ SAL_WARN( "svx", "FmTextControShell::executeAttributeDialog: Could not handle the following item:"
+ "\n SlotID: " << nSlotForItemSet
+ << "\n WhichID: " << nWhich
+ << "\n UNO name: " << sUnoSlotName );
+ }
+ #endif
+ }
+ }
+ rReq.Done( rModifiedItems );
+ }
+
+ xDialog.reset();
+ xCurrentItems.reset();
+ xPureItems.reset();
+ pPool.clear();
+ }
+
+
+ void FmTextControlShell::executeSelectAll( )
+ {
+ try
+ {
+ if ( m_xActiveTextComponent.is() )
+ {
+ sal_Int32 nTextLen = m_xActiveTextComponent->getText().getLength();
+ m_xActiveTextComponent->setSelection( css::awt::Selection( 0, nTextLen ) );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+
+ void FmTextControlShell::executeClipboardSlot( SfxSlotId _nSlot )
+ {
+ try
+ {
+ if ( m_xActiveTextComponent.is() )
+ {
+ switch ( _nSlot )
+ {
+ case SID_COPY:
+ case SID_CUT:
+ {
+ OUString sSelectedText( m_xActiveTextComponent->getSelectedText() );
+ ::svt::OStringTransfer::CopyString( sSelectedText, lcl_getWindow( m_xActiveControl ) );
+ if ( SID_CUT == _nSlot )
+ {
+ css::awt::Selection aSelection( m_xActiveTextComponent->getSelection() );
+ m_xActiveTextComponent->insertText( aSelection, OUString() );
+ }
+ }
+ break;
+ case SID_PASTE:
+ {
+ OUString sClipboardContent;
+ OSL_VERIFY( ::svt::OStringTransfer::PasteString( sClipboardContent, lcl_getWindow( m_xActiveControl ) ) );
+ css::awt::Selection aSelection( m_xActiveTextComponent->getSelection() );
+ m_xActiveTextComponent->insertText( aSelection, sClipboardContent );
+ }
+ break;
+ default:
+ OSL_FAIL( "FmTextControlShell::executeClipboardSlot: invalid slot!" );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+
+ void FmTextControlShell::ExecuteTextAttribute( SfxRequest& _rReq )
+ {
+ SfxSlotId nSlot = _rReq.GetSlot();
+
+ ControlFeatures::const_iterator aFeaturePos = m_aControlFeatures.find( nSlot );
+ if ( aFeaturePos == m_aControlFeatures.end() )
+ {
+ // special slots
+ switch ( nSlot )
+ {
+ case SID_CHAR_DLG:
+ executeAttributeDialog( eCharAttribs, _rReq );
+ break;
+
+ case SID_PARA_DLG:
+ executeAttributeDialog( eParaAttribs, _rReq );
+ break;
+
+ case SID_SELECTALL:
+ executeSelectAll();
+ break;
+
+ case SID_CUT:
+ case SID_COPY:
+ case SID_PASTE:
+ executeClipboardSlot( nSlot );
+ break;
+
+ default:
+ DBG_ASSERT( aFeaturePos != m_aControlFeatures.end(), "FmTextControShell::ExecuteTextAttribute: I have no such dispatcher, and cannot handle it at all!" );
+ return;
+ }
+ }
+ else
+ {
+ // slots which are dispatched to the control
+
+ switch ( nSlot )
+ {
+ case SID_ATTR_CHAR_STRIKEOUT:
+ case SID_ATTR_CHAR_UNDERLINE:
+ case SID_ATTR_CHAR_OVERLINE:
+ {
+ SfxItemSet aToggled( *_rReq.GetArgs() );
+
+ lcl_translateUnoStateToItem( nSlot, aFeaturePos->second->getFeatureState(), aToggled );
+ WhichId nWhich = aToggled.GetPool()->GetWhich( nSlot );
+ const SfxPoolItem* pItem = aToggled.GetItem( nWhich );
+ if ( ( SID_ATTR_CHAR_UNDERLINE == nSlot ) || ( SID_ATTR_CHAR_OVERLINE == nSlot ) )
+ {
+ const SvxTextLineItem* pTextLine = dynamic_cast<const SvxTextLineItem*>( pItem );
+ DBG_ASSERT( pTextLine, "FmTextControlShell::ExecuteTextAttribute: ooops - no underline/overline item!" );
+ if ( pTextLine )
+ {
+ FontLineStyle eTL = pTextLine->GetLineStyle();
+ if ( SID_ATTR_CHAR_UNDERLINE == nSlot ) {
+ aToggled.Put( SvxUnderlineItem( eTL == LINESTYLE_SINGLE ? LINESTYLE_NONE : LINESTYLE_SINGLE, nWhich ) );
+ } else {
+ aToggled.Put( SvxOverlineItem( eTL == LINESTYLE_SINGLE ? LINESTYLE_NONE : LINESTYLE_SINGLE, nWhich ) );
+ }
+ }
+ }
+ else
+ {
+ const SvxCrossedOutItem* pCrossedOut = dynamic_cast<const SvxCrossedOutItem*>( pItem );
+ DBG_ASSERT( pCrossedOut, "FmTextControlShell::ExecuteTextAttribute: ooops - no CrossedOut item!" );
+ if ( pCrossedOut )
+ {
+ FontStrikeout eFS = pCrossedOut->GetStrikeout();
+ aToggled.Put( SvxCrossedOutItem( eFS == STRIKEOUT_SINGLE ? STRIKEOUT_NONE : STRIKEOUT_SINGLE, nWhich ) );
+ }
+ }
+
+ Sequence< PropertyValue > aArguments;
+ TransformItems( nSlot, aToggled, aArguments );
+ aFeaturePos->second->dispatch( aArguments );
+ }
+ break;
+
+ case SID_ATTR_CHAR_FONTHEIGHT:
+ case SID_ATTR_CHAR_FONT:
+ case SID_ATTR_CHAR_POSTURE:
+ case SID_ATTR_CHAR_WEIGHT:
+ case SID_ATTR_CHAR_SHADOWED:
+ case SID_ATTR_CHAR_CONTOUR:
+ case SID_SET_SUPER_SCRIPT:
+ case SID_SET_SUB_SCRIPT:
+ {
+ const SfxItemSet* pArgs = _rReq.GetArgs();
+ Sequence< PropertyValue > aArgs;
+ if ( pArgs )
+ TransformItems( nSlot, *pArgs, aArgs );
+ aFeaturePos->second->dispatch( aArgs );
+ }
+ break;
+
+ default:
+ if ( aFeaturePos->second->isFeatureEnabled() )
+ aFeaturePos->second->dispatch();
+ break;
+ }
+ }
+ _rReq.Done();
+ }
+
+
+ void FmTextControlShell::GetTextAttributeState( SfxItemSet& _rSet )
+ {
+ SfxWhichIter aIter( _rSet );
+ sal_uInt16 nSlot = aIter.FirstWhich();
+ while ( nSlot )
+ {
+ if ( ( nSlot == SID_ATTR_PARA_LEFT_TO_RIGHT )
+ || ( nSlot == SID_ATTR_PARA_RIGHT_TO_LEFT )
+ )
+ {
+ if ( !SvtCTLOptions::IsCTLFontEnabled() )
+ {
+ _rSet.DisableItem( nSlot );
+ nSlot = aIter.NextWhich();
+ continue;
+ }
+ }
+
+ ControlFeatures::const_iterator aFeaturePos = m_aControlFeatures.find( nSlot );
+ if ( aFeaturePos != m_aControlFeatures.end() )
+ {
+ if ( aFeaturePos->second->isFeatureEnabled() )
+ lcl_translateUnoStateToItem( nSlot, aFeaturePos->second->getFeatureState(), _rSet );
+ else
+ _rSet.DisableItem( nSlot );
+ }
+ else
+ {
+ bool bDisable = false;
+
+ bool bNeedWriteableControl = false;
+ bool bNeedTextComponent = false;
+ bool bNeedSelection = false;
+
+ switch ( nSlot )
+ {
+ case SID_CHAR_DLG:
+ case SID_PARA_DLG:
+ bDisable |= m_aControlFeatures.empty();
+ bNeedWriteableControl = true;
+ break;
+
+ case SID_CUT:
+ bNeedSelection = true;
+ bNeedTextComponent = true;
+ bNeedWriteableControl = true;
+ SAL_INFO("svx.form", "need to invalidate again" );
+ m_bNeedClipboardInvalidation = true;
+ break;
+
+ case SID_PASTE:
+ {
+ vcl::Window* pActiveControlVCLWindow = lcl_getWindow( m_xActiveControl );
+ if ( pActiveControlVCLWindow )
+ {
+ TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( pActiveControlVCLWindow) );
+ bDisable |= !aDataHelper.HasFormat( SotClipboardFormatId::STRING );
+ }
+ else
+ bDisable = true;
+
+ bNeedTextComponent = true;
+ bNeedWriteableControl = true;
+ }
+ break;
+
+ case SID_COPY:
+ bNeedTextComponent = true;
+ bNeedSelection = true;
+ break;
+
+ case SID_SELECTALL:
+ bNeedTextComponent = true;
+ break;
+
+ default:
+ // slot is unknown at all
+ bDisable = true;
+ break;
+ }
+ SAL_WARN_IF( bNeedSelection && !bNeedTextComponent, "svx.form", "FmTextControlShell::GetTextAttributeState: bNeedSelection should imply bNeedTextComponent!" );
+
+ if ( !bDisable && bNeedWriteableControl )
+ bDisable |= !IsActiveControl( ) || m_bActiveControlIsReadOnly;
+
+ if ( !bDisable && bNeedTextComponent )
+ bDisable |= !m_xActiveTextComponent.is();
+
+ if ( !bDisable && bNeedSelection )
+ {
+ css::awt::Selection aSelection = m_xActiveTextComponent->getSelection();
+ bDisable |= aSelection.Min == aSelection.Max;
+ }
+
+ if ( bDisable )
+ _rSet.DisableItem( nSlot );
+ }
+
+ nSlot = aIter.NextWhich();
+ }
+ }
+
+
+ bool FmTextControlShell::IsActiveControl( bool _bCountRichTextOnly ) const
+ {
+ if ( _bCountRichTextOnly && !m_bActiveControlIsRichText )
+ return false;
+
+ return m_bActiveControl;
+ }
+
+
+ void FmTextControlShell::dispose()
+ {
+ if ( IsActiveControl() )
+ controlDeactivated();
+ if ( isControllerListening() )
+ stopControllerListening();
+ }
+
+
+ void FmTextControlShell::designModeChanged()
+ {
+ m_rBindings.Invalidate( pTextControlSlots );
+ }
+
+
+ void FmTextControlShell::formActivated( const Reference< runtime::XFormController >& _rxController )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ SAL_INFO("svx.form", "0x" << OUString::number( reinterpret_cast<sal_IntPtr>(_rxController.get()), 16 ));
+#endif
+
+ DBG_ASSERT( _rxController.is(), "FmTextControlShell::formActivated: invalid controller!" );
+ if ( !_rxController.is() )
+ return;
+
+ // sometimes, a form controller notifies activations, even if it's already activated
+ if ( m_xActiveController == _rxController )
+ return;
+
+ try
+ {
+ startControllerListening( _rxController );
+ controlActivated( _rxController->getCurrentControl() );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+
+ void FmTextControlShell::formDeactivated( const Reference< runtime::XFormController >& _rxController )
+ {
+ SAL_INFO("svx.form", "0x" << OUString::number( reinterpret_cast<sal_IntPtr>(_rxController.get()), 16 ));
+
+ if ( IsActiveControl() )
+ controlDeactivated();
+ if ( isControllerListening() )
+ stopControllerListening();
+ }
+
+
+ void FmTextControlShell::startControllerListening( const Reference< runtime::XFormController >& _rxController )
+ {
+ OSL_PRECOND( _rxController.is(), "FmTextControlShell::startControllerListening: invalid controller!" );
+ if ( !_rxController.is() )
+ return;
+
+ OSL_PRECOND( !isControllerListening(), "FmTextControlShell::startControllerListening: already listening!" );
+ if ( isControllerListening() )
+ stopControllerListening( );
+ DBG_ASSERT( !isControllerListening(), "FmTextControlShell::startControllerListening: inconsistence!" );
+
+ try
+ {
+ const Sequence< Reference< css::awt::XControl > > aControls( _rxController->getControls() );
+ m_aControlObservers.resize( 0 );
+ m_aControlObservers.reserve( aControls.getLength() );
+
+ std::transform(aControls.begin(), aControls.end(), std::back_inserter(m_aControlObservers),
+ [this](const Reference< css::awt::XControl >& rControl) -> FocusListenerAdapter {
+ return FocusListenerAdapter( new FmFocusListenerAdapter( rControl, this ) ); });
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ m_xActiveController = _rxController;
+ }
+
+
+ void FmTextControlShell::stopControllerListening( )
+ {
+ OSL_PRECOND( isControllerListening(), "FmTextControlShell::stopControllerListening: inconsistence!" );
+
+ // dispose all listeners associated with the controls of the active controller
+ for (auto& rpObserver : m_aControlObservers)
+ {
+ rpObserver->dispose();
+ }
+
+ FocusListenerAdapters().swap(m_aControlObservers);
+
+ m_xActiveController.clear();
+ }
+
+
+ void FmTextControlShell::implClearActiveControlRef()
+ {
+ // no more features for this control
+ for (auto& rFeature : m_aControlFeatures)
+ {
+ rFeature.second->dispose();
+ }
+
+ ControlFeatures().swap(m_aControlFeatures);
+
+ if ( m_aContextMenuObserver )
+ {
+ m_aContextMenuObserver->dispose();
+ m_aContextMenuObserver = MouseListenerAdapter();
+ }
+
+ if ( m_xActiveTextComponent.is() )
+ {
+ SAL_INFO("svx.form", "stopping timer for clipboard invalidation" );
+ m_aClipboardInvalidation.Stop();
+ }
+ // no more active control
+ m_xActiveControl.clear();
+ m_xActiveTextComponent.clear();
+ m_bActiveControlIsReadOnly = true;
+ m_bActiveControlIsRichText = false;
+ m_bActiveControl = false;
+ }
+
+
+ void FmTextControlShell::controlDeactivated( )
+ {
+ DBG_ASSERT( IsActiveControl(), "FmTextControlShell::controlDeactivated: no active control!" );
+
+ m_bActiveControl = false;
+
+ m_rBindings.Invalidate( pTextControlSlots );
+ }
+
+
+ void FmTextControlShell::controlActivated( const Reference< css::awt::XControl >& _rxControl )
+ {
+ // ensure that all knittings with the previously active control are lost
+ if ( m_xActiveControl.is() )
+ implClearActiveControlRef();
+ DBG_ASSERT( m_aControlFeatures.empty(), "FmTextControlShell::controlActivated: should have no dispatchers when I'm here!" );
+
+#if OSL_DEBUG_LEVEL > 0
+ {
+ Sequence< Reference< css::awt::XControl > > aActiveControls;
+ if ( m_xActiveController.is() )
+ aActiveControls = m_xActiveController->getControls();
+
+ bool bFoundThisControl = false;
+
+ const Reference< css::awt::XControl >* pControls = aActiveControls.getConstArray();
+ const Reference< css::awt::XControl >* pControlsEnd = pControls + aActiveControls.getLength();
+ for ( ; ( pControls != pControlsEnd ) && !bFoundThisControl; ++pControls )
+ {
+ if ( *pControls == _rxControl )
+ bFoundThisControl = true;
+ }
+ DBG_ASSERT( bFoundThisControl, "FmTextControlShell::controlActivated: only controls which belong to the active controller can be activated!" );
+ }
+#endif
+ // ask the control for dispatchers for our text-related slots
+ fillFeatureDispatchers( _rxControl, pTextControlSlots, m_aControlFeatures );
+
+ // remember this control
+ m_xActiveControl = _rxControl;
+ m_xActiveTextComponent.set(_rxControl, css::uno::UNO_QUERY);
+ m_bActiveControlIsReadOnly = lcl_determineReadOnly( m_xActiveControl );
+ m_bActiveControlIsRichText = lcl_isRichText( m_xActiveControl );
+
+ // if we found a rich text control, we need context menu support
+ if ( m_bActiveControlIsRichText )
+ {
+ DBG_ASSERT( !m_aContextMenuObserver, "FmTextControlShell::controlActivated: already have an observer!" );
+ m_aContextMenuObserver = MouseListenerAdapter( new FmMouseListenerAdapter( _rxControl, this ) );
+ }
+
+ if ( m_xActiveTextComponent.is() )
+ {
+ SAL_INFO("svx.form", "starting timer for clipboard invalidation" );
+ m_aClipboardInvalidation.Start();
+ }
+
+ m_bActiveControl = true;
+
+ m_rBindings.Invalidate( pTextControlSlots );
+
+ if ( m_pViewFrame )
+ m_pViewFrame->UIFeatureChanged();
+
+ // don't call the activation handler if we don't have any slots we can serve
+ // The activation handler is used to put the shell on the top of the dispatcher stack,
+ // so it's preferred when slots are distributed.
+ // Note that this is a slight hack, to prevent that we grab slots from the SfxDispatcher
+ // which should be served by other shells (e.g. Cut/Copy/Paste).
+ // A real solution would be a forwarding-mechanism for slots: We should be on the top
+ // if we're active, but if we cannot handle the slot, then we need to tell the dispatcher
+ // to skip our shell, and pass the slot to the next one. However, this mechanism is not
+ // not in place in SFX.
+ // Another possibility would be to have dedicated shells for the slots which we might
+ // or might not be able to serve. However, this could probably increase the number of
+ // shells too much (In theory, nearly every slot could have an own shell then).
+
+ // #i51621# / 2005-08-19 / frank.schoenheit@sun.com
+ // bool bHaveAnyServeableSlots = m_xActiveTextComponent.is() || !m_aControlFeatures.empty();
+ // LEM: not calling m_aControlActivatonHandler causes fdo#63695, so disable this hack for now.
+ m_aControlActivationHandler.Call( nullptr );
+
+ m_bNeedClipboardInvalidation = true;
+ }
+
+
+ void FmTextControlShell::fillFeatureDispatchers(const Reference< css::awt::XControl >& _rxControl, SfxSlotId* _pZeroTerminatedSlots,
+ ControlFeatures& _rDispatchers)
+ {
+ Reference< XDispatchProvider > xProvider( _rxControl, UNO_QUERY );
+ SfxApplication* pApplication = SfxGetpApp();
+ DBG_ASSERT( pApplication, "FmTextControlShell::fillFeatureDispatchers: no SfxApplication!" );
+ if ( xProvider.is() && pApplication )
+ {
+ SfxSlotId* pSlots = _pZeroTerminatedSlots;
+ while ( *pSlots )
+ {
+ rtl::Reference<FmTextControlFeature> pDispatcher = implGetFeatureDispatcher( xProvider, pApplication, *pSlots );
+ if ( pDispatcher )
+ _rDispatchers.emplace( *pSlots, pDispatcher );
+
+ ++pSlots;
+ }
+ }
+ }
+
+
+ rtl::Reference<FmTextControlFeature> FmTextControlShell::implGetFeatureDispatcher( const Reference< XDispatchProvider >& _rxProvider, SfxApplication const * _pApplication, SfxSlotId _nSlot )
+ {
+ OSL_PRECOND( _rxProvider.is() && _pApplication, "FmTextControlShell::implGetFeatureDispatcher: invalid arg(s)!" );
+ URL aFeatureURL;
+ aFeatureURL.Complete = lcl_getUnoSlotName( _nSlot );
+ try
+ {
+ if ( !m_xURLTransformer.is() )
+ {
+ m_xURLTransformer = util::URLTransformer::create( ::comphelper::getProcessComponentContext() );
+ }
+ if ( m_xURLTransformer.is() )
+ m_xURLTransformer->parseStrict( aFeatureURL );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ Reference< XDispatch > xDispatcher = _rxProvider->queryDispatch( aFeatureURL, OUString(), 0xFF );
+ if ( xDispatcher.is() )
+ return new FmTextControlFeature( xDispatcher, std::move(aFeatureURL), _nSlot, this );
+ return nullptr;
+ }
+
+
+ void FmTextControlShell::Invalidate( SfxSlotId _nSlot )
+ {
+ m_rBindings.Invalidate( _nSlot );
+ // despite this method being called "Invalidate", we also update here - this gives more immediate
+ // feedback in the UI
+ m_rBindings.Update( _nSlot );
+ }
+
+
+ void FmTextControlShell::focusGained( const css::awt::FocusEvent& _rEvent )
+ {
+ Reference< css::awt::XControl > xControl( _rEvent.Source, UNO_QUERY );
+
+#if OSL_DEBUG_LEVEL > 0
+ SAL_INFO("svx.form", "0x" << OUString::number( reinterpret_cast<sal_IntPtr>(xControl.get()), 16 ));
+#endif
+
+ DBG_ASSERT( xControl.is(), "FmTextControlShell::focusGained: suspicious focus event!" );
+ if ( xControl.is() )
+ controlActivated( xControl );
+ }
+
+
+ void FmTextControlShell::focusLost( const css::awt::FocusEvent& _rEvent )
+ {
+ Reference< css::awt::XControl > xControl( _rEvent.Source, UNO_QUERY );
+
+#if OSL_DEBUG_LEVEL > 0
+ SAL_INFO("svx.form", "0x" << OUString::number( reinterpret_cast<sal_IntPtr>(xControl.get()), 16 ));
+#endif
+
+ m_bActiveControl = false;
+ }
+
+
+ void FmTextControlShell::ForgetActiveControl()
+ {
+ implClearActiveControlRef();
+ }
+
+
+ void FmTextControlShell::contextMenuRequested()
+ {
+ m_rBindings.GetDispatcher()->ExecutePopup( "formrichtext" );
+ }
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmtools.cxx b/svx/source/form/fmtools.cxx
new file mode 100644
index 0000000000..cefb5ce515
--- /dev/null
+++ b/svx/source/form/fmtools.cxx
@@ -0,0 +1,371 @@
+/* -*- 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 <fmprop.hxx>
+#include <fmservs.hxx>
+#include <svx/fmtools.hxx>
+#include <svx/svdobjkind.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/io/XPersistObject.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/sdb/ErrorCondition.hpp>
+#include <com/sun/star/sdb/ErrorMessageDialog.hpp>
+#include <com/sun/star/sdb/SQLContext.hpp>
+#include <com/sun/star/sdb/SQLErrorEvent.hpp>
+#include <com/sun/star/sdb/XCompletedConnection.hpp>
+#include <com/sun/star/sdb/XResultSetAccess.hpp>
+#include <com/sun/star/sdbc/XRowSet.hpp>
+#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
+#include <com/sun/star/util/Language.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/types.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::ui::dialogs;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::sdbcx;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::task;
+using namespace ::com::sun::star::form;
+using namespace ::svxform;
+
+
+namespace
+{
+ bool lcl_shouldDisplayError( const Any& _rError )
+ {
+ SQLException aError;
+ if ( !( _rError >>= aError ) )
+ return true;
+
+ if ( ! aError.Message.startsWith( "[OOoBase]" ) )
+ // it is an exception *not* thrown by an OOo Base core component
+ return true;
+
+ // the only exception we do not display ATM is a RowSetVetoException, which
+ // has been raised because an XRowSetApprovalListener vetoed a change
+ if ( aError.ErrorCode + ErrorCondition::ROW_SET_OPERATION_VETOED == 0 )
+ return false;
+
+ // everything else is to be displayed
+ return true;
+ }
+}
+
+void displayException(const Any& _rExcept, const css::uno::Reference<css::awt::XWindow>& rParent)
+{
+ // check whether we need to display it
+ if ( !lcl_shouldDisplayError( _rExcept ) )
+ return;
+
+ try
+ {
+ Reference< XExecutableDialog > xErrorDialog = ErrorMessageDialog::create(::comphelper::getProcessComponentContext(), "", rParent, _rExcept);
+ xErrorDialog->execute();
+ }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx.form", "could not display the error message!");
+ }
+}
+
+void displayException(const css::sdbc::SQLException& _rExcept, const css::uno::Reference<css::awt::XWindow>& rParent)
+{
+ displayException(Any(_rExcept), rParent);
+}
+
+void displayException(const css::sdb::SQLContext& _rExcept, const css::uno::Reference<css::awt::XWindow>& rParent)
+{
+ displayException(Any(_rExcept), rParent);
+}
+
+void displayException(const css::sdb::SQLErrorEvent& _rEvent, const css::uno::Reference<css::awt::XWindow>& rParent)
+{
+ displayException(_rEvent.Reason, rParent);
+}
+
+sal_Int32 getElementPos(const Reference< css::container::XIndexAccess>& xCont, const Reference< XInterface >& xElement)
+{
+ sal_Int32 nIndex = -1;
+ if (!xCont.is())
+ return nIndex;
+
+
+ Reference< XInterface > xNormalized( xElement, UNO_QUERY );
+ DBG_ASSERT( xNormalized.is(), "getElementPos: invalid element!" );
+ if ( xNormalized.is() )
+ {
+ // find child position
+ nIndex = xCont->getCount();
+ while (nIndex--)
+ {
+ try
+ {
+ Reference< XInterface > xCurrent(xCont->getByIndex( nIndex ),UNO_QUERY);
+ DBG_ASSERT( xCurrent.get() == Reference< XInterface >( xCurrent, UNO_QUERY ).get(),
+ "getElementPos: container element not normalized!" );
+ if ( xNormalized.get() == xCurrent.get() )
+ break;
+ }
+ catch(Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "getElementPos" );
+ }
+
+ }
+ }
+ return nIndex;
+}
+
+
+OUString getLabelName(const Reference< css::beans::XPropertySet>& xControlModel)
+{
+ if (!xControlModel.is())
+ return OUString();
+
+ if (::comphelper::hasProperty(FM_PROP_CONTROLLABEL, xControlModel))
+ {
+ Reference< css::beans::XPropertySet> xLabelSet;
+ xControlModel->getPropertyValue(FM_PROP_CONTROLLABEL) >>= xLabelSet;
+ if (xLabelSet.is() && ::comphelper::hasProperty(FM_PROP_LABEL, xLabelSet))
+ {
+ Any aLabel( xLabelSet->getPropertyValue(FM_PROP_LABEL) );
+ if ((aLabel.getValueTypeClass() == TypeClass_STRING) && !::comphelper::getString(aLabel).isEmpty())
+ return ::comphelper::getString(aLabel);
+ }
+ }
+
+ return ::comphelper::getString(xControlModel->getPropertyValue(FM_PROP_CONTROLSOURCE));
+}
+
+
+// = CursorWrapper
+
+CursorWrapper::CursorWrapper(const Reference< css::sdbc::XRowSet>& _rxCursor, bool bUseCloned)
+{
+ ImplConstruct(Reference< css::sdbc::XResultSet>(_rxCursor), bUseCloned);
+}
+
+
+CursorWrapper::CursorWrapper(const Reference< css::sdbc::XResultSet>& _rxCursor, bool bUseCloned)
+{
+ ImplConstruct(_rxCursor, bUseCloned);
+}
+
+
+void CursorWrapper::ImplConstruct(const Reference< css::sdbc::XResultSet>& _rxCursor, bool bUseCloned)
+{
+ if (bUseCloned)
+ {
+ Reference< css::sdb::XResultSetAccess> xAccess(_rxCursor, UNO_QUERY);
+ try
+ {
+ m_xMoveOperations = xAccess.is() ? xAccess->createResultSet() : Reference< css::sdbc::XResultSet>();
+ }
+ catch(Exception&)
+ {
+ }
+ }
+ else
+ m_xMoveOperations = _rxCursor;
+
+ m_xBookmarkOperations.set(m_xMoveOperations, css::uno::UNO_QUERY);
+ m_xColumnsSupplier.set(m_xMoveOperations, css::uno::UNO_QUERY);
+ m_xPropertyAccess.set(m_xMoveOperations, css::uno::UNO_QUERY);
+
+ if ( !m_xMoveOperations.is() || !m_xBookmarkOperations.is() || !m_xColumnsSupplier.is() || !m_xPropertyAccess.is() )
+ { // all or nothing !!
+ m_xMoveOperations = nullptr;
+ m_xBookmarkOperations = nullptr;
+ m_xColumnsSupplier = nullptr;
+ }
+ else
+ m_xGeneric = m_xMoveOperations.get();
+}
+
+CursorWrapper& CursorWrapper::operator=(const Reference< css::sdbc::XRowSet>& _rxCursor)
+{
+ m_xMoveOperations.set(_rxCursor);
+ m_xBookmarkOperations.set(_rxCursor, UNO_QUERY);
+ m_xColumnsSupplier.set(_rxCursor, UNO_QUERY);
+ if (!m_xMoveOperations.is() || !m_xBookmarkOperations.is() || !m_xColumnsSupplier.is())
+ { // all or nothing !!
+ m_xMoveOperations = nullptr;
+ m_xBookmarkOperations = nullptr;
+ m_xColumnsSupplier = nullptr;
+ }
+ return *this;
+}
+
+FmXDisposeListener::~FmXDisposeListener()
+{
+ setAdapter(nullptr);
+}
+
+void FmXDisposeListener::setAdapter(FmXDisposeMultiplexer* pAdapter)
+{
+ std::scoped_lock aGuard(m_aMutex);
+ m_pAdapter = pAdapter;
+}
+
+FmXDisposeMultiplexer::FmXDisposeMultiplexer(FmXDisposeListener* _pListener, const Reference< css::lang::XComponent>& _rxObject)
+ :m_xObject(_rxObject)
+ ,m_pListener(_pListener)
+{
+ m_pListener->setAdapter(this);
+
+ if (m_xObject.is())
+ m_xObject->addEventListener(this);
+}
+
+FmXDisposeMultiplexer::~FmXDisposeMultiplexer()
+{
+}
+
+// css::lang::XEventListener
+
+void FmXDisposeMultiplexer::disposing(const css::lang::EventObject& /*Source*/)
+{
+ Reference< css::lang::XEventListener> xPreventDelete(this);
+
+ if (m_pListener)
+ {
+ m_pListener->disposing(0);
+ m_pListener->setAdapter(nullptr);
+ m_pListener = nullptr;
+ }
+ m_xObject = nullptr;
+}
+
+
+void FmXDisposeMultiplexer::dispose()
+{
+ if (m_xObject.is())
+ {
+ Reference< css::lang::XEventListener> xPreventDelete(this);
+
+ m_xObject->removeEventListener(this);
+ m_xObject = nullptr;
+
+ m_pListener->setAdapter(nullptr);
+ m_pListener = nullptr;
+ }
+}
+
+
+SdrObjKind getControlTypeByObject(const Reference< css::lang::XServiceInfo>& _rxObject)
+{
+ // ask for the persistent service name
+ Reference< css::io::XPersistObject> xPersistence(_rxObject, UNO_QUERY);
+ DBG_ASSERT(xPersistence.is(), "::getControlTypeByObject : argument should be a css::io::XPersistObject !");
+ if (!xPersistence.is())
+ return SdrObjKind::FormControl;
+
+ OUString sPersistentServiceName = xPersistence->getServiceName();
+ if (sPersistentServiceName == FM_COMPONENT_EDIT) // 5.0-Name
+ {
+ // may be a simple edit field or a formatted field, dependent of the supported services
+ if (_rxObject->supportsService(FM_SUN_COMPONENT_FORMATTEDFIELD))
+ return SdrObjKind::FormFormattedField;
+ return SdrObjKind::FormEdit;
+ }
+ if (sPersistentServiceName == FM_COMPONENT_TEXTFIELD)
+ return SdrObjKind::FormEdit;
+ if (sPersistentServiceName == FM_COMPONENT_COMMANDBUTTON)
+ return SdrObjKind::FormButton;
+ if (sPersistentServiceName == FM_COMPONENT_FIXEDTEXT)
+ return SdrObjKind::FormFixedText;
+ if (sPersistentServiceName == FM_COMPONENT_LISTBOX)
+ return SdrObjKind::FormListbox;
+ if (sPersistentServiceName == FM_COMPONENT_CHECKBOX)
+ return SdrObjKind::FormCheckbox;
+ if (sPersistentServiceName == FM_COMPONENT_RADIOBUTTON)
+ return SdrObjKind::FormRadioButton;
+ if (sPersistentServiceName == FM_COMPONENT_GROUPBOX)
+ return SdrObjKind::FormGroupBox;
+ if (sPersistentServiceName == FM_COMPONENT_COMBOBOX)
+ return SdrObjKind::FormCombobox;
+ if (sPersistentServiceName == FM_COMPONENT_GRID) // 5.0-Name
+ return SdrObjKind::FormGrid;
+ if (sPersistentServiceName == FM_COMPONENT_GRIDCONTROL)
+ return SdrObjKind::FormGrid;
+ if (sPersistentServiceName == FM_COMPONENT_IMAGEBUTTON)
+ return SdrObjKind::FormImageButton;
+ if (sPersistentServiceName == FM_COMPONENT_FILECONTROL)
+ return SdrObjKind::FormFileControl;
+ if (sPersistentServiceName == FM_COMPONENT_DATEFIELD)
+ return SdrObjKind::FormDateField;
+ if (sPersistentServiceName == FM_COMPONENT_TIMEFIELD)
+ return SdrObjKind::FormTimeField;
+ if (sPersistentServiceName == FM_COMPONENT_NUMERICFIELD)
+ return SdrObjKind::FormNumericField;
+ if (sPersistentServiceName == FM_COMPONENT_CURRENCYFIELD)
+ return SdrObjKind::FormCurrencyField;
+ if (sPersistentServiceName == FM_COMPONENT_PATTERNFIELD)
+ return SdrObjKind::FormPatternField;
+ if (sPersistentServiceName == FM_COMPONENT_HIDDEN) // 5.0-Name
+ return SdrObjKind::FormHidden;
+ if (sPersistentServiceName == FM_COMPONENT_HIDDENCONTROL)
+ return SdrObjKind::FormHidden;
+ if (sPersistentServiceName == FM_COMPONENT_IMAGECONTROL)
+ return SdrObjKind::FormImageControl;
+ if (sPersistentServiceName == FM_COMPONENT_FORMATTEDFIELD)
+ {
+ OSL_FAIL("::getControlTypeByObject : suspicious persistent service name (formatted field) !");
+ // objects with that service name should exist as they aren't compatible with older versions
+ return SdrObjKind::FormFormattedField;
+ }
+ if ( sPersistentServiceName == FM_SUN_COMPONENT_SCROLLBAR )
+ return SdrObjKind::FormScrollbar;
+ if ( sPersistentServiceName == FM_SUN_COMPONENT_SPINBUTTON )
+ return SdrObjKind::FormSpinButton;
+ if ( sPersistentServiceName == FM_SUN_COMPONENT_NAVIGATIONBAR )
+ return SdrObjKind::FormNavigationBar;
+
+ OSL_FAIL("::getControlTypeByObject : unknown object type !");
+ return SdrObjKind::FormControl;
+}
+
+
+bool isRowSetAlive(const Reference< XInterface >& _rxRowSet)
+{
+ bool bIsAlive = false;
+ Reference< css::sdbcx::XColumnsSupplier> xSupplyCols(_rxRowSet, UNO_QUERY);
+ Reference< css::container::XIndexAccess> xCols;
+ if (xSupplyCols.is())
+ xCols.set(xSupplyCols->getColumns(), UNO_QUERY);
+ if (xCols.is() && (xCols->getCount() > 0))
+ bIsAlive = true;
+
+ return bIsAlive;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmundo.cxx b/svx/source/form/fmundo.cxx
new file mode 100644
index 0000000000..9bbe99a81d
--- /dev/null
+++ b/svx/source/form/fmundo.cxx
@@ -0,0 +1,1254 @@
+/* -*- 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 <sal/config.h>
+
+#include <map>
+
+#include <sal/macros.h>
+#include <fmundo.hxx>
+#include <fmpgeimp.hxx>
+#include <svx/svditer.hxx>
+#include <fmobj.hxx>
+#include <fmprop.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <svx/fmmodel.hxx>
+#include <svx/fmpage.hxx>
+
+#include <com/sun/star/util/XModifyBroadcaster.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/container/XContainer.hpp>
+#include <com/sun/star/container/XContainerListener.hpp>
+#include <com/sun/star/script/XEventAttacherManager.hpp>
+#include <com/sun/star/form/binding/XBindableValue.hpp>
+#include <com/sun/star/form/binding/XListEntrySink.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/script/XScriptListener.hpp>
+
+#include <svx/fmtools.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/event.hxx>
+#include <osl/mutex.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/types.hxx>
+#include <connectivity/dbtools.hxx>
+#include <vcl/svapp.hxx>
+#include <comphelper/processfactory.hxx>
+#include <cppuhelper/implbase.hxx>
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::script;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::form::binding;
+using namespace ::com::sun::star::sdbc;
+using namespace ::svxform;
+using namespace ::dbtools;
+
+
+namespace {
+
+class ScriptEventListenerWrapper : public cppu::WeakImplHelper< XScriptListener >
+{
+public:
+ /// @throws css::uno::RuntimeException
+ explicit ScriptEventListenerWrapper( FmFormModel& _rModel)
+ :m_rModel( _rModel )
+ ,m_attemptedListenerCreation( false )
+ {
+
+ }
+ // XEventListener
+ virtual void SAL_CALL disposing(const EventObject& ) override {}
+
+ // XScriptListener
+ virtual void SAL_CALL firing(const ScriptEvent& evt) override
+ {
+ attemptListenerCreation();
+ if ( m_vbaListener.is() )
+ {
+ m_vbaListener->firing( evt );
+ }
+ }
+
+ virtual Any SAL_CALL approveFiring(const ScriptEvent& evt) override
+ {
+ attemptListenerCreation();
+ if ( m_vbaListener.is() )
+ {
+ return m_vbaListener->approveFiring( evt );
+ }
+ return Any();
+ }
+
+private:
+ void attemptListenerCreation()
+ {
+ if ( m_attemptedListenerCreation )
+ return;
+ m_attemptedListenerCreation = true;
+
+ try
+ {
+ css::uno::Reference<css::uno::XComponentContext> context(
+ comphelper::getProcessComponentContext());
+ Reference< XScriptListener > const xScriptListener(
+ context->getServiceManager()->createInstanceWithContext(
+ "ooo.vba.EventListener", context),
+ UNO_QUERY_THROW);
+ Reference< XPropertySet > const xListenerProps( xScriptListener, UNO_QUERY_THROW );
+ // SfxObjectShellRef is good here since the model controls the lifetime of the shell
+ SfxObjectShellRef const xObjectShell = m_rModel.GetObjectShell();
+ ENSURE_OR_THROW( xObjectShell.is(), "no object shell!" );
+ xListenerProps->setPropertyValue("Model", Any( xObjectShell->GetModel() ) );
+
+ m_vbaListener = xScriptListener;
+ }
+ catch( Exception const & )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ FmFormModel& m_rModel;
+ Reference< XScriptListener > m_vbaListener;
+ bool m_attemptedListenerCreation;
+
+
+};
+
+
+// some helper structs for caching property infos
+
+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
+};
+
+}
+
+
+struct PropertySetInfo
+{
+ typedef std::map<OUString, PropertyInfo> AllProperties;
+
+ 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
+};
+
+static OUString static_STR_UNDO_PROPERTY;
+
+
+FmXUndoEnvironment::FmXUndoEnvironment(FmFormModel& _rModel)
+ :rModel( _rModel )
+ ,m_pScriptingEnv( new svxform::FormScriptingEnvironment( _rModel ) )
+ ,m_Locks( 0 )
+ ,bReadOnly( false )
+ ,m_bDisposed( false )
+{
+ try
+ {
+ m_vbaListener = new ScriptEventListenerWrapper( _rModel );
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+FmXUndoEnvironment::~FmXUndoEnvironment()
+{
+ if ( !m_bDisposed ) // i120746, call FormScriptingEnvironment::dispose to avoid memory leak
+ m_pScriptingEnv->dispose();
+}
+
+void FmXUndoEnvironment::dispose()
+{
+ OSL_ENSURE( !m_bDisposed, "FmXUndoEnvironment::dispose: disposed twice?" );
+ if ( !m_bDisposed )
+ return;
+
+ Lock();
+
+ sal_uInt16 nCount = rModel.GetPageCount();
+ sal_uInt16 i;
+ for (i = 0; i < nCount; i++)
+ {
+ FmFormPage* pPage = dynamic_cast<FmFormPage*>( rModel.GetPage(i) );
+ if ( pPage )
+ {
+ Reference< css::form::XForms > xForms = pPage->GetForms( false );
+ if ( xForms.is() )
+ RemoveElement( xForms );
+ }
+ }
+
+ nCount = rModel.GetMasterPageCount();
+ for (i = 0; i < nCount; i++)
+ {
+ FmFormPage* pPage = dynamic_cast<FmFormPage*>( rModel.GetMasterPage(i) );
+ if ( pPage )
+ {
+ Reference< css::form::XForms > xForms = pPage->GetForms( false );
+ if ( xForms.is() )
+ RemoveElement( xForms );
+ }
+ }
+
+ UnLock();
+
+ OSL_PRECOND( rModel.GetObjectShell(), "FmXUndoEnvironment::dispose: no object shell anymore!" );
+ if ( rModel.GetObjectShell() )
+ EndListening( *rModel.GetObjectShell() );
+
+ if ( IsListening( rModel ) )
+ EndListening( rModel );
+
+ m_pScriptingEnv->dispose();
+
+ m_bDisposed = true;
+}
+
+
+void FmXUndoEnvironment::ModeChanged()
+{
+ OSL_PRECOND( rModel.GetObjectShell(), "FmXUndoEnvironment::ModeChanged: no object shell anymore!" );
+ if ( !rModel.GetObjectShell() )
+ return;
+
+ if (bReadOnly == (rModel.GetObjectShell()->IsReadOnly() || rModel.GetObjectShell()->IsReadOnlyUI()))
+ return;
+
+ bReadOnly = !bReadOnly;
+
+ sal_uInt16 nCount = rModel.GetPageCount();
+ sal_uInt16 i;
+ for (i = 0; i < nCount; i++)
+ {
+ FmFormPage* pPage = dynamic_cast<FmFormPage*>( rModel.GetPage(i) );
+ if ( pPage )
+ {
+ Reference< css::form::XForms > xForms = pPage->GetForms( false );
+ if ( xForms.is() )
+ TogglePropertyListening( xForms );
+ }
+ }
+
+ nCount = rModel.GetMasterPageCount();
+ for (i = 0; i < nCount; i++)
+ {
+ FmFormPage* pPage = dynamic_cast<FmFormPage*>( rModel.GetMasterPage(i) );
+ if ( pPage )
+ {
+ Reference< css::form::XForms > xForms = pPage->GetForms( false );
+ if ( xForms.is() )
+ TogglePropertyListening( xForms );
+ }
+ }
+
+ if (!bReadOnly)
+ StartListening(rModel);
+ else
+ EndListening(rModel);
+}
+
+
+void FmXUndoEnvironment::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
+{
+ if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
+ {
+ const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
+ switch (pSdrHint->GetKind())
+ {
+ case SdrHintKind::ObjectInserted:
+ {
+ SdrObject* pSdrObj = const_cast<SdrObject*>(pSdrHint->GetObject());
+ Inserted( pSdrObj );
+ } break;
+ case SdrHintKind::ObjectRemoved:
+ {
+ SdrObject* pSdrObj = const_cast<SdrObject*>(pSdrHint->GetObject());
+ Removed( pSdrObj );
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ else if (rHint.GetId() == SfxHintId::ThisIsAnSfxEventHint)
+ {
+ switch (static_cast<const SfxEventHint&>(rHint).GetEventId())
+ {
+ case SfxEventHintId::CreateDoc:
+ case SfxEventHintId::OpenDoc:
+ ModeChanged();
+ break;
+ default: break;
+ }
+ }
+ else if (rHint.GetId() != SfxHintId::NONE)
+ {
+ switch (rHint.GetId())
+ {
+ case SfxHintId::Dying:
+ dispose();
+ rModel.SetObjectShell( nullptr );
+ break;
+ case SfxHintId::ModeChanged:
+ ModeChanged();
+ break;
+ default: break;
+ }
+ }
+}
+
+void FmXUndoEnvironment::Inserted(SdrObject* pObj)
+{
+ if (pObj->GetObjInventor() == SdrInventor::FmForm)
+ {
+ FmFormObj* pFormObj = dynamic_cast<FmFormObj*>( pObj );
+ Inserted( pFormObj );
+ }
+ else if (pObj->IsGroupObject())
+ {
+ SdrObjListIter aIter(pObj->GetSubList());
+ while ( aIter.IsMore() )
+ Inserted( aIter.Next() );
+ }
+}
+
+
+namespace
+{
+ bool lcl_searchElement(const Reference< XIndexAccess>& xCont, const Reference< XInterface >& xElement)
+ {
+ if (!xCont.is() || !xElement.is())
+ return false;
+
+ sal_Int32 nCount = xCont->getCount();
+ Reference< XInterface > xComp;
+ for (sal_Int32 i = 0; i < nCount; i++)
+ {
+ try
+ {
+ xCont->getByIndex(i) >>= xComp;
+ if (xComp.is())
+ {
+ if ( xElement == xComp )
+ return true;
+ else
+ {
+ Reference< XIndexAccess> xCont2(xComp, UNO_QUERY);
+ if (xCont2.is() && lcl_searchElement(xCont2, xElement))
+ return true;
+ }
+ }
+ }
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ return false;
+ }
+}
+
+
+void FmXUndoEnvironment::Inserted(FmFormObj* pObj)
+{
+ DBG_ASSERT( pObj, "FmXUndoEnvironment::Inserted: invalid object!" );
+ if ( !pObj )
+ return;
+
+ // 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();
+
+ FmFormPage& rPage(dynamic_cast< FmFormPage& >( *pObj->getSdrPageFromSdrObject()));
+ Reference< XIndexAccess > xForms( rPage.GetForms(), UNO_QUERY_THROW );
+
+ 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();
+ }
+
+ FmFormPageImpl::setUniqueName( xContent, xForm );
+ xNewParent->insertByIndex( nPos, Any( xContent ) );
+
+ Reference< XEventAttacherManager > xManager( xNewParent, UNO_QUERY_THROW );
+ xManager->registerScriptEvents( nPos, pObj->GetOriginalEvents() );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+ // reset FormObject
+ pObj->ClearObjEnv();
+}
+
+
+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;
+
+ if (pObj->GetObjInventor() == SdrInventor::FmForm)
+ {
+ FmFormObj* pFormObj = dynamic_cast<FmFormObj*>( pObj );
+ Removed(pFormObj);
+ }
+ else if (pObj->IsGroupObject())
+ {
+ SdrObjListIter aIter(pObj->GetSubList());
+ while ( aIter.IsMore() )
+ Removed( aIter.Next() );
+ }
+}
+
+
+void FmXUndoEnvironment::Removed(FmFormObj* pObj)
+{
+ DBG_ASSERT( pObj, "FmXUndoEnvironment::Removed: invalid object!" );
+ if ( !pObj )
+ 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;
+
+ Sequence< ScriptEventDescriptor > aEvts;
+ Reference< XEventAttacherManager > xManager(xForm, UNO_QUERY);
+ if (xManager.is())
+ aEvts = xManager->getScriptEvents(nPos);
+
+ try
+ {
+ pObj->SetObjEnv(xForm, nPos, aEvts);
+ xForm->removeByIndex(nPos);
+ }
+ catch(Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+// XEventListener
+
+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);
+ }
+ }
+}
+
+// XPropertyChangeListener
+
+void SAL_CALL FmXUndoEnvironment::propertyChange(const PropertyChangeEvent& evt)
+{
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+
+ 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;
+
+ // the attributes
+ sal_Int32 nAttributes = xSet->getPropertySetInfo()->getPropertyByName(evt.PropertyName).Attributes;
+ aNewEntry.bIsTransientOrReadOnly = ((nAttributes & PropertyAttribute::READONLY) != 0) || ((nAttributes & PropertyAttribute::TRANSIENT) != 0);
+
+ // 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;
+
+ aNewEntry.bIsValueProperty = (sControlSourceProperty == evt.PropertyName);
+ }
+ }
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ // 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
+
+ static constexpr OUString s_sExternalData = u"ExternalData"_ustr;
+ if ( xBindingPropsPSI.is() && xBindingPropsPSI->hasPropertyByName( s_sExternalData ) )
+ {
+ bool bExternalData = true;
+ OSL_VERIFY( xBindingProps->getPropertyValue( s_sExternalData ) >>= bExternalData );
+ bAddUndoAction = !bExternalData;
+ }
+ else
+ bAddUndoAction = !xBinding.is();
+ }
+ }
+
+ if ( bAddUndoAction && ( evt.PropertyName == FM_PROP_STRINGITEMLIST ) )
+ {
+ Reference< XListEntrySink > xSink( evt.Source, UNO_QUERY );
+ if ( xSink.is() && xSink->getListEntrySource().is() )
+ // #i41029# / 2005-01-31 / frank.schoenheit@sun.com
+ bAddUndoAction = false;
+ }
+
+ 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();
+ }
+ }
+}
+
+// XContainerListener
+
+void SAL_CALL FmXUndoEnvironment::elementInserted(const ContainerEvent& evt)
+{
+ SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ // new object for listening
+ Reference< XInterface > xIface;
+ evt.Element >>= xIface;
+ OSL_ENSURE(xIface.is(), "FmXUndoEnvironment::elementInserted: invalid container notification!");
+ AddElement(xIface);
+
+ implSetModified();
+}
+
+
+void FmXUndoEnvironment::implSetModified()
+{
+ if ( !IsLocked() && rModel.GetObjectShell() )
+ {
+ rModel.GetObjectShell()->SetModified();
+ }
+}
+
+
+void SAL_CALL FmXUndoEnvironment::elementReplaced(const ContainerEvent& evt)
+{
+ SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ Reference< XInterface > xIface;
+ evt.ReplacedElement >>= xIface;
+ OSL_ENSURE(xIface.is(), "FmXUndoEnvironment::elementReplaced: invalid container notification!");
+ RemoveElement(xIface);
+
+ evt.Element >>= xIface;
+ AddElement(xIface);
+
+ implSetModified();
+}
+
+
+void SAL_CALL FmXUndoEnvironment::elementRemoved(const ContainerEvent& evt)
+{
+ SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ Reference< XInterface > xIface( evt.Element, UNO_QUERY );
+ OSL_ENSURE(xIface.is(), "FmXUndoEnvironment::elementRemoved: invalid container notification!");
+ RemoveElement(xIface);
+
+ implSetModified();
+}
+
+
+void SAL_CALL FmXUndoEnvironment::modified( const EventObject& /*aEvent*/ )
+{
+ implSetModified();
+}
+
+
+void FmXUndoEnvironment::AddForms(const Reference< XNameContainer > & rForms)
+{
+ Lock();
+ AddElement(Reference<XInterface>( rForms, UNO_QUERY ));
+ UnLock();
+}
+
+
+void FmXUndoEnvironment::RemoveForms(const Reference< XNameContainer > & rForms)
+{
+ Lock();
+ RemoveElement(Reference<XInterface>( rForms, UNO_QUERY ));
+ UnLock();
+}
+
+
+void FmXUndoEnvironment::TogglePropertyListening(const Reference< XInterface > & Element)
+{
+ // listen at the container
+ Reference< XIndexContainer > xContainer(Element, UNO_QUERY);
+ if (xContainer.is())
+ {
+ sal_uInt32 nCount = xContainer->getCount();
+ Reference< XInterface > xIface;
+ for (sal_uInt32 i = 0; i < nCount; i++)
+ {
+ xContainer->getByIndex(i) >>= xIface;
+ TogglePropertyListening(xIface);
+ }
+ }
+
+ Reference< XPropertySet > xSet(Element, UNO_QUERY);
+ if (xSet.is())
+ {
+ if (!bReadOnly)
+ xSet->addPropertyChangeListener( OUString(), this );
+ else
+ xSet->removePropertyChangeListener( OUString(), this );
+ }
+}
+
+
+void FmXUndoEnvironment::switchListening( const Reference< XIndexContainer >& _rxContainer, bool _bStartListening )
+{
+ OSL_PRECOND( _rxContainer.is(), "FmXUndoEnvironment::switchListening: invalid container!" );
+ if ( !_rxContainer.is() )
+ return;
+
+ 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 );
+
+ switchListening( _rxElement, true );
+}
+
+
+void FmXUndoEnvironment::RemoveElement(const Reference< XInterface >& _rxElement)
+{
+ if ( m_bDisposed )
+ return;
+
+ switchListening( _rxElement, false );
+
+ 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() );
+ }
+ }
+
+ Reference< XIndexContainer > xContainer( _rxElement, UNO_QUERY );
+ if ( xContainer.is() )
+ switchListening( xContainer, false );
+}
+
+
+FmUndoPropertyAction::FmUndoPropertyAction(FmFormModel& rNewMod, const PropertyChangeEvent& evt)
+ :SdrUndoAction(rNewMod)
+ ,xObj(evt.Source, UNO_QUERY)
+ ,aPropertyName(evt.PropertyName)
+ ,aNewValue(evt.NewValue)
+ ,aOldValue(evt.OldValue)
+{
+ if (rNewMod.GetObjectShell())
+ rNewMod.GetObjectShell()->SetModified();
+ if(static_STR_UNDO_PROPERTY.isEmpty())
+ static_STR_UNDO_PROPERTY = SvxResId(RID_STR_UNDO_PROPERTY);
+}
+
+
+void FmUndoPropertyAction::Undo()
+{
+ FmXUndoEnvironment& rEnv = static_cast<FmFormModel&>(rMod).GetUndoEnv();
+
+ if (!xObj.is() || rEnv.IsLocked())
+ return;
+
+ rEnv.Lock();
+ try
+ {
+ xObj->setPropertyValue( aPropertyName, aOldValue );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "FmUndoPropertyAction::Undo" );
+ }
+ rEnv.UnLock();
+}
+
+
+void FmUndoPropertyAction::Redo()
+{
+ FmXUndoEnvironment& rEnv = static_cast<FmFormModel&>(rMod).GetUndoEnv();
+
+ if (!xObj.is() || rEnv.IsLocked())
+ return;
+
+ rEnv.Lock();
+ try
+ {
+ xObj->setPropertyValue( aPropertyName, aNewValue );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "FmUndoPropertyAction::Redo" );
+ }
+ rEnv.UnLock();
+}
+
+
+OUString FmUndoPropertyAction::GetComment() const
+{
+ OUString aStr = static_STR_UNDO_PROPERTY.replaceFirst( "#", aPropertyName );
+ return aStr;
+}
+
+
+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 ( !(xCont.is() && xElem.is()) )
+ return;
+
+ // normalize
+ m_xElement = xElem;
+ if ( m_eAction != Removed )
+ return;
+
+ if (m_nIndex >= 0)
+ {
+ Reference< XEventAttacherManager > xManager( xCont, UNO_QUERY );
+ if ( xManager.is() )
+ m_aEvents = xManager->getScriptEvents(m_nIndex);
+ }
+ else
+ m_xElement = nullptr;
+
+ // we now own the element
+ m_xOwnElement = m_xElement;
+}
+
+
+FmUndoContainerAction::~FmUndoContainerAction()
+{
+ // if we own the object...
+ DisposeElement( m_xOwnElement );
+}
+
+
+void FmUndoContainerAction::DisposeElement( const Reference< XInterface > & xElem )
+{
+ Reference< XComponent > xComp( xElem, UNO_QUERY );
+ if ( xComp.is() )
+ {
+ // and the object does not have a parent
+ Reference< XChild > xChild( xElem, UNO_QUERY );
+ if ( xChild.is() && !xChild->getParent().is() )
+ // -> dispose it
+ xComp->dispose();
+ }
+}
+
+
+void FmUndoContainerAction::implReInsert( )
+{
+ if ( m_xContainer->getCount() < m_nIndex )
+ return;
+
+ // insert the element
+ Any aVal;
+ if ( m_xContainer->getElementType() == cppu::UnoType<XFormComponent>::get() )
+ {
+ aVal <<= Reference< XFormComponent >( m_xElement, UNO_QUERY );
+ }
+ else
+ {
+ aVal <<= Reference< XForm >( m_xElement, UNO_QUERY );
+ }
+ m_xContainer->insertByIndex( m_nIndex, aVal );
+
+ OSL_ENSURE( getElementPos( m_xContainer, m_xElement ) == m_nIndex, "FmUndoContainerAction::implReInsert: insertion did not work!" );
+
+ // register the events
+ Reference< XEventAttacherManager > xManager( m_xContainer, UNO_QUERY );
+ if ( xManager.is() )
+ xManager->registerScriptEvents( m_nIndex, m_aEvents );
+
+ // we don't own the object anymore
+ m_xOwnElement = nullptr;
+}
+
+
+void FmUndoContainerAction::implReRemove( )
+{
+ Reference< XInterface > xElement;
+ if ( ( m_nIndex >= 0 ) && ( m_nIndex < m_xContainer->getCount() ) )
+ m_xContainer->getByIndex( m_nIndex ) >>= xElement;
+
+ 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;
+ }
+}
+
+
+void FmUndoContainerAction::Undo()
+{
+ FmXUndoEnvironment& rEnv = static_cast< FmFormModel& >( rMod ).GetUndoEnv();
+
+ if ( !(m_xContainer.is() && !rEnv.IsLocked() && m_xElement.is()) )
+ return;
+
+ rEnv.Lock();
+ try
+ {
+ switch ( m_eAction )
+ {
+ case Inserted:
+ implReRemove();
+ break;
+
+ case Removed:
+ implReInsert();
+ break;
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "FmUndoContainerAction::Undo" );
+ }
+ rEnv.UnLock();
+}
+
+
+void FmUndoContainerAction::Redo()
+{
+ FmXUndoEnvironment& rEnv = static_cast< FmFormModel& >( rMod ).GetUndoEnv();
+ if ( !(m_xContainer.is() && !rEnv.IsLocked() && m_xElement.is()) )
+ return;
+
+ rEnv.Lock();
+ try
+ {
+ switch ( m_eAction )
+ {
+ case Inserted:
+ implReInsert();
+ break;
+
+ case Removed:
+ implReRemove();
+ break;
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "FmUndoContainerAction::Redo" );
+ }
+ rEnv.UnLock();
+}
+
+
+FmUndoModelReplaceAction::FmUndoModelReplaceAction(FmFormModel& _rMod, SdrUnoObj* _pObject, const Reference< XControlModel > & _xReplaced)
+ :SdrUndoAction(_rMod)
+ ,m_xReplaced(_xReplaced)
+ ,m_pObject(_pObject)
+{
+}
+
+
+FmUndoModelReplaceAction::~FmUndoModelReplaceAction()
+{
+ // dispose our element if nobody else is responsible for
+ DisposeElement(m_xReplaced);
+}
+
+
+void FmUndoModelReplaceAction::DisposeElement( const css::uno::Reference< css::awt::XControlModel>& xReplaced )
+{
+ Reference< XComponent > xComp(xReplaced, UNO_QUERY);
+ if (xComp.is())
+ {
+ Reference< XChild > xChild(xReplaced, UNO_QUERY);
+ if (!xChild.is() || !xChild->getParent().is())
+ xComp->dispose();
+ }
+}
+
+
+void FmUndoModelReplaceAction::Undo()
+{
+ try
+ {
+ Reference< XControlModel > xCurrentModel( m_pObject->GetUnoControlModel() );
+
+ // 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 !");
+
+ OUString sName;
+ xCurrentAsSet->getPropertyValue( FM_PROP_NAME ) >>= sName;
+ xCurrentsParent->replaceByName( sName, Any( xComponent ) );
+
+ m_pObject->SetUnoControlModel(m_xReplaced);
+ m_pObject->SetChanged();
+
+ m_xReplaced = xCurrentModel;
+ }
+ }
+ catch(Exception&)
+ {
+ OSL_FAIL("FmUndoModelReplaceAction::Undo : could not replace the model !");
+ }
+}
+
+
+OUString FmUndoModelReplaceAction::GetComment() const
+{
+ return SvxResId(RID_STR_UNDO_MODEL_REPLACE);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmview.cxx b/svx/source/form/fmview.cxx
new file mode 100644
index 0000000000..8267c334bd
--- /dev/null
+++ b/svx/source/form/fmview.cxx
@@ -0,0 +1,595 @@
+/* -*- 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 <sfx2/docfile.hxx>
+#ifdef REFERENCE
+#undef REFERENCE
+#endif
+#include <com/sun/star/sdb/SQLContext.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+#include <com/sun/star/form/XLoadable.hpp>
+#include <fmvwimp.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/bindings.hxx>
+#include <fmobj.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/fmview.hxx>
+#include <svx/fmmodel.hxx>
+#include <svx/fmpage.hxx>
+#include <svx/fmshell.hxx>
+#include <fmshimp.hxx>
+#include <fmservs.hxx>
+#include <fmundo.hxx>
+#include <svx/dataaccessdescriptor.hxx>
+#include <comphelper/namedvaluecollection.hxx>
+#include <o3tl/deleter.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/awt/XControl.hpp>
+#include <tools/debug.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <svx/svxids.hrc>
+#include <vcl/i18nhelp.hxx>
+#include <vcl/window.hxx>
+#include <o3tl/string_view.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::util;
+using namespace ::svxform;
+using namespace ::svx;
+
+FmFormView::FmFormView(
+ SdrModel& rSdrModel,
+ OutputDevice* pOut)
+: E3dView(rSdrModel, pOut)
+{
+ Init();
+}
+
+void FmFormView::Init()
+{
+ pFormShell = nullptr;
+ pImpl = new FmXFormView(this);
+
+ // set model
+ SdrModel* pModel = &GetModel();
+
+ DBG_ASSERT( dynamic_cast<const FmFormModel*>( pModel) != nullptr, "Wrong model" );
+ FmFormModel* pFormModel = dynamic_cast<FmFormModel*>(pModel);
+ if( !pFormModel )
+ return;
+
+ // get DesignMode from model
+ bool bInitDesignMode = pFormModel->GetOpenInDesignMode();
+ if ( pFormModel->OpenInDesignModeIsDefaulted( ) )
+ { // this means that nobody ever explicitly set this on the model, and the model has never
+ // been loaded from a stream.
+ // This means this is a newly created document. This means, we want to have it in design
+ // mode by default (though a newly created model returns true for GetOpenInDesignMode).
+ // We _want_ to have this because it makes a lot of hacks following the original fix
+ DBG_ASSERT( !bInitDesignMode, "FmFormView::Init: doesn't the model default to FALSE anymore?" );
+ // if this asserts, either the on-construction default in the model has changed (then this here
+ // may not be necessary anymore), or we're not dealing with a new document...
+ bInitDesignMode = true;
+ }
+
+ SfxObjectShell* pObjShell = pFormModel->GetObjectShell();
+ if ( pObjShell && pObjShell->GetMedium() )
+ {
+ if ( const SfxUnoAnyItem *pItem = pObjShell->GetMedium()->GetItemSet().GetItemIfSet( SID_COMPONENTDATA, false ) )
+ {
+ ::comphelper::NamedValueCollection aComponentData( pItem->GetValue() );
+ bInitDesignMode = aComponentData.getOrDefault( "ApplyFormDesignMode", bInitDesignMode );
+ }
+ }
+
+ // this will be done in the shell
+ // bDesignMode = !bInitDesignMode; // forces execution of SetDesignMode
+ SetDesignMode( bInitDesignMode );
+}
+
+FmFormView::~FmFormView()
+{
+ if (pFormShell)
+ suppress_fun_call_w_exception(pFormShell->SetView(nullptr));
+
+ pImpl->notifyViewDying();
+}
+
+FmFormPage* FmFormView::GetCurPage()
+{
+ SdrPageView* pPageView = GetSdrPageView();
+ FmFormPage* pCurPage = pPageView ? dynamic_cast<FmFormPage*>( pPageView->GetPage() ) : nullptr;
+ return pCurPage;
+}
+
+void FmFormView::MarkListHasChanged()
+{
+ E3dView::MarkListHasChanged();
+
+ if ( !(pFormShell && IsDesignMode()) )
+ return;
+
+ FmFormObj* pObj = getMarkedGrid();
+ if ( pImpl->m_pMarkedGrid && pImpl->m_pMarkedGrid != pObj )
+ {
+ pImpl->m_pMarkedGrid = nullptr;
+ if ( pImpl->m_xWindow.is() )
+ {
+ pImpl->m_xWindow->removeFocusListener(pImpl);
+ pImpl->m_xWindow = nullptr;
+ }
+ SetMoveOutside(false);
+ //OLMRefreshAllIAOManagers();
+ }
+
+ pFormShell->GetImpl()->SetSelectionDelayed_Lock();
+}
+
+namespace
+{
+ const SdrPageWindow* findPageWindow( const SdrPaintView* _pView, OutputDevice const * _pWindow )
+ {
+ SdrPageView* pPageView = _pView->GetSdrPageView();
+ if(pPageView)
+ {
+ for ( sal_uInt32 window = 0; window < pPageView->PageWindowCount(); ++window )
+ {
+ const SdrPageWindow* pPageWindow = pPageView->GetPageWindow( window );
+ if ( !pPageWindow || &pPageWindow->GetPaintWindow().GetOutputDevice() != _pWindow )
+ continue;
+
+ return pPageWindow;
+ }
+ }
+ return nullptr;
+ }
+}
+
+
+void FmFormView::AddDeviceToPaintView(OutputDevice& rNewDev, vcl::Window* pWindow)
+{
+ E3dView::AddDeviceToPaintView(rNewDev, pWindow);
+
+ // look up the PageViewWindow for the newly inserted window, and care for it
+ // #i39269# / 2004-12-20 / frank.schoenheit@sun.com
+ const SdrPageWindow* pPageWindow = findPageWindow( this, &rNewDev );
+ if ( pPageWindow )
+ pImpl->addWindow( *pPageWindow );
+}
+
+
+void FmFormView::DeleteDeviceFromPaintView(OutputDevice& rNewDev)
+{
+ const SdrPageWindow* pPageWindow = findPageWindow( this, &rNewDev );
+ if ( pPageWindow )
+ pImpl->removeWindow( pPageWindow->GetControlContainer() );
+
+ E3dView::DeleteDeviceFromPaintView(rNewDev);
+}
+
+
+void FmFormView::ChangeDesignMode(bool bDesign)
+{
+ if (bDesign == IsDesignMode())
+ return;
+
+ FmFormModel* pModel = dynamic_cast<FmFormModel*>(&GetModel());
+ if (pModel)
+ { // For the duration of the transition the Undo-Environment is disabled. This ensures that non-transient Properties can
+ // also be changed (this should be done with care and also reversed before switching the mode back. An example is the
+ // setting of the maximal length of the text by FmXEditModel on its control.)
+ pModel->GetUndoEnv().Lock();
+ }
+
+ // --- 1. deactivate all controls if we are switching to design mode
+ if ( bDesign )
+ DeactivateControls( GetSdrPageView() );
+
+ // --- 2. simulate a deactivation (the shell will handle some things there ...?)
+ if ( pFormShell && pFormShell->GetImpl() )
+ pFormShell->GetImpl()->viewDeactivated_Lock(*this);
+ else
+ pImpl->Deactivate();
+
+ // --- 3. activate all controls, if we're switching to alive mode
+ if ( !bDesign )
+ ActivateControls( GetSdrPageView() );
+
+ // --- 4. load resp. unload the forms
+ FmFormPage* pCurPage = GetCurPage();
+ if ( pCurPage )
+ {
+ if ( pFormShell && pFormShell->GetImpl() )
+ pFormShell->GetImpl()->loadForms_Lock(pCurPage, (bDesign ? LoadFormsFlags::Unload : LoadFormsFlags::Load));
+ }
+
+ // --- 5. base class functionality
+ SetDesignMode( bDesign );
+
+ // --- 6. simulate an activation (the shell will handle some things there ...?)
+ OSL_PRECOND( pFormShell && pFormShell->GetImpl(), "FmFormView::ChangeDesignMode: is this really allowed? No shell?" );
+ if ( pFormShell && pFormShell->GetImpl() )
+ pFormShell->GetImpl()->viewActivated_Lock(*this);
+ else
+ pImpl->Activate();
+
+ if ( pCurPage )
+ {
+ if ( bDesign )
+ {
+ if ( GetActualOutDev() && GetActualOutDev()->GetOutDevType() == OUTDEV_WINDOW )
+ {
+ const vcl::Window* pWindow = GetActualOutDev()->GetOwnerWindow();
+ const_cast< vcl::Window* >( pWindow )->GrabFocus();
+ }
+
+ // redraw UNO objects
+ if ( GetSdrPageView() )
+ {
+ SdrObjListIter aIter(pCurPage);
+ while( aIter.IsMore() )
+ {
+ SdrObject* pObj = aIter.Next();
+ if (pObj && pObj->IsUnoObj())
+ {
+ // For redraw just use ActionChanged()
+ // pObj->BroadcastObjectChange();
+ pObj->ActionChanged();
+ }
+ }
+ }
+ }
+ else
+ {
+ // set the auto focus to the first control (if indicated by the model to do so)
+ bool bForceControlFocus = pModel && pModel->GetAutoControlFocus();
+ if (bForceControlFocus)
+ pImpl->AutoFocus();
+ }
+ }
+
+ // Unlock Undo-Environment
+ if (pModel)
+ pModel->GetUndoEnv().UnLock();
+}
+
+
+void FmFormView::GrabFirstControlFocus()
+{
+ if ( !IsDesignMode() )
+ pImpl->AutoFocus();
+}
+
+
+SdrPageView* FmFormView::ShowSdrPage(SdrPage* pPage)
+{
+ SdrPageView* pPV = E3dView::ShowSdrPage(pPage);
+
+ if (pPage)
+ {
+ if (!IsDesignMode())
+ {
+ // creating the controllers
+ ActivateControls(pPV);
+
+ // Deselect all
+ UnmarkAll();
+ }
+ else if ( pFormShell && pFormShell->IsDesignMode() )
+ {
+ FmXFormShell* pFormShellImpl = pFormShell->GetImpl();
+ pFormShellImpl->UpdateForms_Lock(true);
+
+ // so that the form navigator can react to the pagechange
+ pFormShell->GetViewShell()->GetViewFrame().GetBindings().Invalidate(SID_FM_FMEXPLORER_CONTROL, true);
+
+ pFormShellImpl->SetSelection_Lock(GetMarkedObjectList());
+ }
+ }
+
+ // notify our shell that we have been activated
+ if ( pFormShell && pFormShell->GetImpl() )
+ pFormShell->GetImpl()->viewActivated_Lock(*this);
+ else
+ pImpl->Activate();
+
+ return pPV;
+}
+
+
+void FmFormView::HideSdrPage()
+{
+ // --- 1. deactivate controls
+ if ( !IsDesignMode() )
+ DeactivateControls(GetSdrPageView());
+
+ // --- 2. tell the shell the view is (going to be) deactivated
+ if ( pFormShell && pFormShell->GetImpl() )
+ pFormShell->GetImpl()->viewDeactivated_Lock(*this);
+ else
+ pImpl->Deactivate();
+
+ // --- 3. base class behavior
+ E3dView::HideSdrPage();
+}
+
+
+void FmFormView::ActivateControls(SdrPageView const * pPageView)
+{
+ if (!pPageView)
+ return;
+
+ for (sal_uInt32 i = 0; i < pPageView->PageWindowCount(); ++i)
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(i);
+ pImpl->addWindow(rPageWindow);
+ }
+}
+
+
+void FmFormView::DeactivateControls(SdrPageView const * pPageView)
+{
+ if( !pPageView )
+ return;
+
+ for (sal_uInt32 i = 0; i < pPageView->PageWindowCount(); ++i)
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(i);
+ pImpl->removeWindow(rPageWindow.GetControlContainer() );
+ }
+}
+
+
+rtl::Reference<SdrObject> FmFormView::CreateFieldControl( const ODataAccessDescriptor& _rColumnDescriptor )
+{
+ return pImpl->implCreateFieldControl( _rColumnDescriptor );
+}
+
+
+rtl::Reference<SdrObject> FmFormView::CreateXFormsControl( const OXFormsDescriptor &_rDesc )
+{
+ return pImpl->implCreateXFormsControl(_rDesc);
+}
+
+
+rtl::Reference<SdrObject> FmFormView::CreateFieldControl(std::u16string_view rFieldDesc) const
+{
+ sal_Int32 nIdx{ 0 };
+ OUString sDataSource( o3tl::getToken(rFieldDesc, 0, u'\x000B', nIdx));
+ OUString sObjectName( o3tl::getToken(rFieldDesc, 0, u'\x000B', nIdx));
+ sal_uInt16 nObjectType = static_cast<sal_uInt16>(o3tl::toInt32(o3tl::getToken(rFieldDesc, 0, u'\x000B', nIdx)));
+ OUString sFieldName( o3tl::getToken(rFieldDesc, 0, u'\x000B', nIdx));
+
+ if (sFieldName.isEmpty() || sObjectName.isEmpty() || sDataSource.isEmpty())
+ return nullptr;
+
+ ODataAccessDescriptor aColumnDescriptor;
+ aColumnDescriptor.setDataSource(sDataSource);
+ aColumnDescriptor[ DataAccessDescriptorProperty::Command ] <<= sObjectName;
+ aColumnDescriptor[ DataAccessDescriptorProperty::CommandType ] <<= nObjectType;
+ aColumnDescriptor[ DataAccessDescriptorProperty::ColumnName ] <<= sFieldName;
+
+ return pImpl->implCreateFieldControl( aColumnDescriptor );
+}
+
+
+void FmFormView::InsertControlContainer(const Reference< css::awt::XControlContainer > & xCC)
+{
+ if( IsDesignMode() )
+ return;
+
+ SdrPageView* pPageView = GetSdrPageView();
+ if( !pPageView )
+ return;
+
+ for( sal_uInt32 i = 0; i < pPageView->PageWindowCount(); i++ )
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(i);
+
+ if( rPageWindow.GetControlContainer( false ) == xCC )
+ {
+ pImpl->addWindow(rPageWindow);
+ break;
+ }
+ }
+}
+
+
+void FmFormView::RemoveControlContainer(const Reference< css::awt::XControlContainer > & xCC)
+{
+ if( !IsDesignMode() )
+ {
+ pImpl->removeWindow( xCC );
+ }
+}
+
+
+SdrPaintWindow* FmFormView::BeginCompleteRedraw(OutputDevice* pOut)
+{
+ SdrPaintWindow* pPaintWindow = E3dView::BeginCompleteRedraw( pOut );
+ pImpl->suspendTabOrderUpdate();
+ return pPaintWindow;
+}
+
+
+void FmFormView::EndCompleteRedraw( SdrPaintWindow& rPaintWindow, bool bPaintFormLayer )
+{
+ E3dView::EndCompleteRedraw( rPaintWindow, bPaintFormLayer );
+ pImpl->resumeTabOrderUpdate();
+}
+
+
+bool FmFormView::KeyInput(const KeyEvent& rKEvt, vcl::Window* pWin)
+{
+ bool bDone = false;
+ const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
+ if ( IsDesignMode()
+ && rKeyCode.GetCode() == KEY_RETURN
+ )
+ {
+ // RETURN alone enters grid controls, for keyboard accessibility
+ if ( pWin
+ && !rKeyCode.IsShift()
+ && !rKeyCode.IsMod1()
+ && !rKeyCode.IsMod2()
+ )
+ {
+ FmFormObj* pObj = getMarkedGrid();
+ if ( pObj )
+ {
+ Reference< awt::XWindow > xWindow( pObj->GetUnoControl( *this, *pWin->GetOutDev() ), UNO_QUERY );
+ if ( xWindow.is() )
+ {
+ pImpl->m_pMarkedGrid = pObj;
+ pImpl->m_xWindow = xWindow;
+ // add as listener to get notified when ESC will be pressed inside the grid
+ pImpl->m_xWindow->addFocusListener(pImpl);
+ SetMoveOutside(true);
+ //OLMRefreshAllIAOManagers();
+ xWindow->setFocus();
+ bDone = true;
+ }
+ }
+ }
+ // Alt-RETURN alone shows the properties of the selection
+ if ( pFormShell
+ && pFormShell->GetImpl()
+ && !rKeyCode.IsShift()
+ && !rKeyCode.IsMod1()
+ && rKeyCode.IsMod2()
+ )
+ {
+ pFormShell->GetImpl()->handleShowPropertiesRequest_Lock();
+ }
+
+ }
+
+ // tdf#139804 Allow selecting form controls with Alt-<Mnemonic>
+ if (rKeyCode.IsMod2() && rKeyCode.GetCode())
+ {
+ if (FmFormPage* pCurPage = GetCurPage())
+ {
+ for (const rtl::Reference<SdrObject>& pObj : *pCurPage)
+ {
+ FmFormObj* pFormObject = FmFormObj::GetFormObject(pObj.get());
+ if (!pFormObject)
+ continue;
+
+ Reference<awt::XControl> xControl = pFormObject->GetUnoControl(*this, *pWin->GetOutDev());
+ if (!xControl.is())
+ continue;
+ const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper();
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xControl->getPeer());
+ if (rI18nHelper.MatchMnemonic(pWindow->GetText(), rKEvt.GetCharCode()))
+ {
+ pWindow->GrabFocus();
+ pWindow->KeyInput(rKEvt);
+ bDone = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if ( !bDone )
+ bDone = E3dView::KeyInput(rKEvt,pWin);
+ return bDone;
+}
+
+bool FmFormView::checkUnMarkAll(const Reference< XInterface >& _xSource)
+{
+ Reference< css::awt::XControl> xControl(pImpl->m_xWindow,UNO_QUERY);
+ bool bRet = !xControl.is() || !_xSource.is() || _xSource != xControl->getModel();
+ if ( bRet )
+ UnmarkAll();
+
+ return bRet;
+}
+
+
+bool FmFormView::MouseButtonDown( const MouseEvent& _rMEvt, OutputDevice* _pWin )
+{
+ bool bReturn = E3dView::MouseButtonDown( _rMEvt, _pWin );
+
+ if ( pFormShell && pFormShell->GetImpl() )
+ {
+ SdrViewEvent aViewEvent;
+ PickAnything( _rMEvt, SdrMouseEventKind::BUTTONDOWN, aViewEvent );
+ pFormShell->GetImpl()->handleMouseButtonDown_Lock(aViewEvent);
+ }
+
+ return bReturn;
+}
+
+
+FmFormObj* FmFormView::getMarkedGrid() const
+{
+ FmFormObj* pFormObject = nullptr;
+ const SdrMarkList& rMarkList = GetMarkedObjectList();
+ if ( 1 == rMarkList.GetMarkCount() )
+ {
+ SdrMark* pMark = rMarkList.GetMark(0);
+ if ( pMark )
+ {
+ pFormObject = FmFormObj::GetFormObject( pMark->GetMarkedSdrObj() );
+ if ( pFormObject )
+ {
+ Reference< XServiceInfo > xServInfo( pFormObject->GetUnoControlModel(), UNO_QUERY );
+ if ( !xServInfo.is() || !xServInfo->supportsService( FM_SUN_COMPONENT_GRIDCONTROL ) )
+ pFormObject = nullptr;
+ }
+ }
+ }
+ return pFormObject;
+}
+
+void FmFormView::createControlLabelPair( OutputDevice const * _pOutDev, sal_Int32 _nXOffsetMM, sal_Int32 _nYOffsetMM,
+ const Reference< XPropertySet >& _rxField, const Reference< XNumberFormats >& _rxNumberFormats,
+ SdrObjKind _nControlObjectID, SdrInventor _nInventor, SdrObjKind _nLabelObjectID,
+ SdrModel& _rModel,
+ rtl::Reference<SdrUnoObj>& _rpLabel,
+ rtl::Reference<SdrUnoObj>& _rpControl )
+{
+ FmXFormView::createControlLabelPair(
+ *_pOutDev, _nXOffsetMM, _nYOffsetMM,
+ _rxField, _rxNumberFormats,
+ _nControlObjectID, u"", _nInventor, _nLabelObjectID,
+ _rModel,
+ _rpLabel, _rpControl
+ );
+}
+
+Reference< runtime::XFormController > FmFormView::GetFormController( const Reference< XForm >& _rxForm, const OutputDevice& _rDevice ) const
+{
+ return pImpl->getFormController( _rxForm, _rDevice );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmvwimp.cxx b/svx/source/form/fmvwimp.cxx
new file mode 100644
index 0000000000..242f51dcca
--- /dev/null
+++ b/svx/source/form/fmvwimp.cxx
@@ -0,0 +1,1919 @@
+/* -*- 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 <fmdocumentclassification.hxx>
+#include <fmobj.hxx>
+#include <fmpgeimp.hxx>
+#include <fmprop.hxx>
+#include <svx/strings.hrc>
+#include <fmservs.hxx>
+#include <fmshimp.hxx>
+#include <svx/fmtools.hxx>
+#include <fmvwimp.hxx>
+#include <formcontrolfactory.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <svx/svditer.hxx>
+#include <svx/dataaccessdescriptor.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/svdobjkind.hxx>
+#include <svx/fmmodel.hxx>
+#include <svx/fmpage.hxx>
+#include <svx/fmshell.hxx>
+#include <svx/fmview.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/xmlexchg.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
+#include <com/sun/star/sdbc/XRowSet.hpp>
+#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
+#include <com/sun/star/util/XNumberFormats.hpp>
+#include <com/sun/star/sdb/CommandType.hpp>
+#include <com/sun/star/sdbc/DataType.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/form/FormButtonType.hpp>
+#include <com/sun/star/form/binding/XBindableValue.hpp>
+#include <com/sun/star/form/binding/XValueBinding.hpp>
+#include <com/sun/star/form/runtime/FormController.hpp>
+#include <com/sun/star/form/submission/XSubmissionSupplier.hpp>
+#include <com/sun/star/awt/XTabControllerModel.hpp>
+#include <com/sun/star/awt/XControlContainer.hpp>
+#include <com/sun/star/awt/XTabController.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/awt/XControl.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/sdb/XQueriesSupplier.hpp>
+#include <com/sun/star/container/XContainer.hpp>
+
+#include <comphelper/namedvaluecollection.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/types.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <sal/log.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <vcl/stdtext.hxx>
+#include <vcl/window.hxx>
+#include <connectivity/dbtools.hxx>
+
+#include <algorithm>
+
+using namespace ::comphelper;
+using namespace ::svx;
+using namespace ::svxform;
+using namespace ::dbtools;
+
+ using namespace ::com::sun::star;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::uno::UNO_SET_THROW;
+ using ::com::sun::star::uno::Type;
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::form::FormButtonType_SUBMIT;
+ using ::com::sun::star::form::binding::XValueBinding;
+ using ::com::sun::star::form::binding::XBindableValue;
+ using ::com::sun::star::lang::XComponent;
+ using ::com::sun::star::container::XIndexAccess;
+ using ::com::sun::star::form::runtime::FormController;
+ using ::com::sun::star::form::runtime::XFormController;
+ using ::com::sun::star::script::XEventAttacherManager;
+ using ::com::sun::star::awt::XTabControllerModel;
+ using ::com::sun::star::container::XChild;
+ using ::com::sun::star::task::XInteractionHandler;
+ using ::com::sun::star::awt::XTabController;
+ using ::com::sun::star::awt::XControlContainer;
+ using ::com::sun::star::awt::XControl;
+ using ::com::sun::star::form::XFormComponent;
+ using ::com::sun::star::form::XForm;
+ using ::com::sun::star::lang::IndexOutOfBoundsException;
+ using ::com::sun::star::container::XContainer;
+ using ::com::sun::star::container::ContainerEvent;
+ using ::com::sun::star::lang::EventObject;
+ using ::com::sun::star::sdb::SQLErrorEvent;
+ using ::com::sun::star::sdbc::XRowSet;
+ using ::com::sun::star::beans::XPropertySet;
+ using ::com::sun::star::container::XElementAccess;
+ using ::com::sun::star::awt::XWindow;
+ using ::com::sun::star::awt::FocusEvent;
+ using ::com::sun::star::ui::dialogs::XExecutableDialog;
+ using ::com::sun::star::sdbc::XDataSource;
+ using ::com::sun::star::container::XIndexContainer;
+ using ::com::sun::star::sdbc::XConnection;
+ using ::com::sun::star::container::XNameAccess;
+ using ::com::sun::star::sdbc::SQLException;
+ using ::com::sun::star::util::XNumberFormatsSupplier;
+ using ::com::sun::star::util::XNumberFormats;
+ using ::com::sun::star::beans::XPropertySetInfo;
+
+ namespace FormComponentType = ::com::sun::star::form::FormComponentType;
+ namespace CommandType = ::com::sun::star::sdb::CommandType;
+ namespace DataType = ::com::sun::star::sdbc::DataType;
+
+
+class FmXFormView::ObjectRemoveListener : public SfxListener
+{
+ FmXFormView* m_pParent;
+public:
+ explicit ObjectRemoveListener( FmXFormView* pParent );
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+};
+
+FormViewPageWindowAdapter::FormViewPageWindowAdapter( css::uno::Reference<css::uno::XComponentContext> _xContext, const SdrPageWindow& _rWindow, FmXFormView* _pViewImpl )
+: m_xControlContainer( _rWindow.GetControlContainer() ),
+ m_xContext(std::move( _xContext )),
+ m_pViewImpl( _pViewImpl ),
+ m_pWindow( _rWindow.GetPaintWindow().GetOutputDevice().GetOwnerWindow() )
+{
+
+ // create an XFormController for every form
+ FmFormPage* pFormPage = dynamic_cast< FmFormPage* >( _rWindow.GetPageView().GetPage() );
+ DBG_ASSERT( pFormPage, "FormViewPageWindowAdapter::FormViewPageWindowAdapter: no FmFormPage found!" );
+ if ( !pFormPage )
+ return;
+
+ try
+ {
+ Reference< XIndexAccess > xForms( pFormPage->GetForms(), UNO_QUERY_THROW );
+ sal_uInt32 nLength = xForms->getCount();
+ for (sal_uInt32 i = 0; i < nLength; i++)
+ {
+ Reference< XForm > xForm( xForms->getByIndex(i), UNO_QUERY );
+ if ( xForm.is() )
+ setController( xForm, nullptr );
+ }
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+FormViewPageWindowAdapter::~FormViewPageWindowAdapter()
+{
+}
+
+void FormViewPageWindowAdapter::dispose()
+{
+ for ( ::std::vector< Reference< XFormController > >::const_iterator i = m_aControllerList.begin();
+ i != m_aControllerList.end();
+ ++i
+ )
+ {
+ try
+ {
+ Reference< XFormController > xController( *i, UNO_SET_THROW );
+
+ // detaching the events
+ Reference< XChild > xControllerModel( xController->getModel(), UNO_QUERY );
+ if ( xControllerModel.is() )
+ {
+ Reference< XEventAttacherManager > xEventManager( xControllerModel->getParent(), UNO_QUERY_THROW );
+ Reference< XInterface > xControllerNormalized( xController, UNO_QUERY_THROW );
+ xEventManager->detach( i - m_aControllerList.begin(), xControllerNormalized );
+ }
+
+ // dispose the formcontroller
+ xController->dispose();
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+ m_aControllerList.clear();
+}
+
+sal_Bool SAL_CALL FormViewPageWindowAdapter::hasElements()
+{
+ return getCount() != 0;
+}
+
+Type SAL_CALL FormViewPageWindowAdapter::getElementType()
+{
+ return cppu::UnoType<XFormController>::get();
+}
+
+// XIndexAccess
+sal_Int32 SAL_CALL FormViewPageWindowAdapter::getCount()
+{
+ return m_aControllerList.size();
+}
+
+Any SAL_CALL FormViewPageWindowAdapter::getByIndex(sal_Int32 nIndex)
+{
+ if (nIndex < 0 ||
+ nIndex >= getCount())
+ throw IndexOutOfBoundsException();
+
+ Any aElement;
+ aElement <<= m_aControllerList[nIndex];
+ return aElement;
+}
+
+void SAL_CALL FormViewPageWindowAdapter::makeVisible( const Reference< XControl >& Control )
+{
+ SolarMutexGuard aSolarGuard;
+
+ Reference< XWindow > xWindow( Control, UNO_QUERY );
+ if ( xWindow.is() && m_pViewImpl->getView() && m_pWindow )
+ {
+ awt::Rectangle aRect = xWindow->getPosSize();
+ ::tools::Rectangle aNewRect( aRect.X, aRect.Y, aRect.X + aRect.Width, aRect.Y + aRect.Height );
+ aNewRect = m_pWindow->PixelToLogic( aNewRect );
+ m_pViewImpl->getView()->MakeVisible( aNewRect, *m_pWindow );
+ }
+}
+
+static Reference< XFormController > getControllerSearchChildren( const Reference< XIndexAccess > & xIndex, const Reference< XTabControllerModel > & xModel)
+{
+ if (xIndex.is() && xIndex->getCount())
+ {
+ Reference< XFormController > xController;
+
+ for (sal_Int32 n = xIndex->getCount(); n-- && !xController.is(); )
+ {
+ xIndex->getByIndex(n) >>= xController;
+ if (xModel.get() == xController->getModel().get())
+ return xController;
+ else
+ {
+ xController = getControllerSearchChildren(xController, xModel);
+ if ( xController.is() )
+ return xController;
+ }
+ }
+ }
+ return Reference< XFormController > ();
+}
+
+// Search the according controller
+Reference< XFormController > FormViewPageWindowAdapter::getController( const Reference< XForm > & xForm ) const
+{
+ Reference< XTabControllerModel > xModel(xForm, UNO_QUERY);
+ for (const auto& rpController : m_aControllerList)
+ {
+ if (rpController->getModel().get() == xModel.get())
+ return rpController;
+
+ // the current-round controller isn't the right one. perhaps one of its children ?
+ Reference< XFormController > xChildSearch = getControllerSearchChildren(Reference< XIndexAccess > (rpController, UNO_QUERY), xModel);
+ if (xChildSearch.is())
+ return xChildSearch;
+ }
+ return Reference< XFormController > ();
+}
+
+
+void FormViewPageWindowAdapter::setController(const Reference< XForm > & xForm, const Reference< XFormController >& _rxParentController )
+{
+ DBG_ASSERT( xForm.is(), "FormViewPageWindowAdapter::setController: there should be a form!" );
+ Reference< XIndexAccess > xFormCps(xForm, UNO_QUERY);
+ if (!xFormCps.is())
+ return;
+
+ Reference< XTabControllerModel > xTabOrder(xForm, UNO_QUERY);
+
+ // create a form controller
+ Reference< XFormController > xController( FormController::create(m_xContext) );
+
+ Reference< XInteractionHandler > xHandler;
+ if ( _rxParentController.is() )
+ xHandler = _rxParentController->getInteractionHandler();
+ else
+ {
+ // TODO: should we create a default handler? Not really necessary, since the
+ // FormController itself has a default fallback
+ }
+ if ( xHandler.is() )
+ xController->setInteractionHandler( xHandler );
+
+ xController->setContext( this );
+
+ xController->setModel( xTabOrder );
+ xController->setContainer( m_xControlContainer );
+ xController->activateTabOrder();
+ xController->addActivateListener( m_pViewImpl );
+
+ if ( _rxParentController.is() )
+ _rxParentController->addChildController( xController );
+ else
+ {
+ m_aControllerList.push_back(xController);
+
+ xController->setParent( *this );
+
+ // attaching the events
+ Reference< XEventAttacherManager > xEventManager( xForm->getParent(), UNO_QUERY );
+ xEventManager->attach(m_aControllerList.size() - 1, Reference<XInterface>( xController, UNO_QUERY ), Any(xController) );
+ }
+
+ // now go through the subforms
+ sal_uInt32 nLength = xFormCps->getCount();
+ Reference< XForm > xSubForm;
+ for (sal_uInt32 i = 0; i < nLength; i++)
+ {
+ if ( xFormCps->getByIndex(i) >>= xSubForm )
+ setController( xSubForm, xController );
+ }
+}
+
+
+void FormViewPageWindowAdapter::updateTabOrder( const Reference< XForm >& _rxForm )
+{
+ OSL_PRECOND( _rxForm.is(), "FormViewPageWindowAdapter::updateTabOrder: illegal argument!" );
+ if ( !_rxForm.is() )
+ return;
+
+ try
+ {
+ Reference< XTabController > xTabCtrl( getController( _rxForm ) );
+ if ( xTabCtrl.is() )
+ { // if there already is a TabController for this form, then delegate the "updateTabOrder" request
+ xTabCtrl->activateTabOrder();
+ }
+ else
+ { // otherwise, create a TabController
+
+ // if it's a sub form, then we must ensure there exist TabControllers
+ // for all its ancestors, too
+ Reference< XForm > xParentForm( _rxForm->getParent(), UNO_QUERY );
+ // there is a parent form -> look for the respective controller
+ Reference< XFormController > xParentController;
+ if ( xParentForm.is() )
+ xParentController = getController( xParentForm );
+
+ setController( _rxForm, xParentController );
+ }
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+
+FmXFormView::FmXFormView(FmFormView* _pView )
+ :m_pMarkedGrid(nullptr)
+ ,m_pView(_pView)
+ ,m_nActivationEvent(nullptr)
+ ,m_nErrorMessageEvent( nullptr )
+ ,m_nAutoFocusEvent( nullptr )
+ ,m_nControlWizardEvent( nullptr )
+ ,m_bFirstActivation( true )
+ ,m_isTabOrderUpdateSuspended( false )
+{
+}
+
+
+void FmXFormView::cancelEvents()
+{
+ if ( m_nActivationEvent )
+ {
+ Application::RemoveUserEvent( m_nActivationEvent );
+ m_nActivationEvent = nullptr;
+ }
+
+ if ( m_nErrorMessageEvent )
+ {
+ Application::RemoveUserEvent( m_nErrorMessageEvent );
+ m_nErrorMessageEvent = nullptr;
+ }
+
+ if ( m_nAutoFocusEvent )
+ {
+ Application::RemoveUserEvent( m_nAutoFocusEvent );
+ m_nAutoFocusEvent = nullptr;
+ }
+
+ if ( m_nControlWizardEvent )
+ {
+ Application::RemoveUserEvent( m_nControlWizardEvent );
+ m_nControlWizardEvent = nullptr;
+ }
+}
+
+
+void FmXFormView::notifyViewDying( )
+{
+ DBG_ASSERT( m_pView, "FmXFormView::notifyViewDying: my view already died!" );
+ m_pView = nullptr;
+ cancelEvents();
+}
+
+
+FmXFormView::~FmXFormView()
+{
+ DBG_ASSERT( m_aPageWindowAdapters.empty(), "FmXFormView::~FmXFormView: Window list not empty!" );
+ for (const auto& rpAdapter : m_aPageWindowAdapters)
+ {
+ rpAdapter->dispose();
+ }
+
+ cancelEvents();
+}
+
+// EventListener
+
+void SAL_CALL FmXFormView::disposing(const EventObject& Source)
+{
+ if ( m_xWindow.is() && Source.Source == m_xWindow )
+ {
+ m_xWindow->removeFocusListener(this);
+ if ( m_pView )
+ {
+ m_pView->SetMoveOutside( false, FmFormView::ImplAccess() );
+ }
+ m_xWindow = nullptr;
+ }
+}
+
+// XFormControllerListener
+
+void SAL_CALL FmXFormView::formActivated(const EventObject& rEvent)
+{
+ if ( m_pView && m_pView->GetFormShell() && m_pView->GetFormShell()->GetImpl() )
+ m_pView->GetFormShell()->GetImpl()->formActivated( rEvent );
+}
+
+
+void SAL_CALL FmXFormView::formDeactivated(const EventObject& rEvent)
+{
+ if ( m_pView && m_pView->GetFormShell() && m_pView->GetFormShell()->GetImpl() )
+ m_pView->GetFormShell()->GetImpl()->formDeactivated( rEvent );
+}
+
+// XContainerListener
+
+void SAL_CALL FmXFormView::elementInserted(const ContainerEvent& evt)
+{
+ try
+ {
+ Reference< XControlContainer > xControlContainer( evt.Source, UNO_QUERY_THROW );
+ Reference< XControl > xControl( evt.Element, UNO_QUERY_THROW );
+ Reference< XFormComponent > xControlModel( xControl->getModel(), UNO_QUERY_THROW );
+ Reference< XForm > xForm( xControlModel->getParent(), UNO_QUERY_THROW );
+
+ if ( m_isTabOrderUpdateSuspended )
+ {
+ // remember the container and the control, so we can update the tab order on resumeTabOrderUpdate
+ m_aNeedTabOrderUpdate[ xControlContainer ].insert( xForm );
+ }
+ else
+ {
+ rtl::Reference< FormViewPageWindowAdapter > pAdapter = findWindow( xControlContainer );
+ if ( pAdapter.is() )
+ pAdapter->updateTabOrder( xForm );
+ }
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+
+void SAL_CALL FmXFormView::elementReplaced(const ContainerEvent& evt)
+{
+ elementInserted(evt);
+}
+
+
+void SAL_CALL FmXFormView::elementRemoved(const ContainerEvent& /*evt*/)
+{
+}
+
+
+rtl::Reference< FormViewPageWindowAdapter > FmXFormView::findWindow( const Reference< XControlContainer >& _rxCC ) const
+{
+ auto i = std::find_if(m_aPageWindowAdapters.begin(), m_aPageWindowAdapters.end(),
+ [&_rxCC](const rtl::Reference< FormViewPageWindowAdapter >& rpAdapter) { return _rxCC == rpAdapter->getControlContainer(); });
+ if (i != m_aPageWindowAdapters.end())
+ return *i;
+ return nullptr;
+}
+
+
+void FmXFormView::addWindow(const SdrPageWindow& rWindow)
+{
+ FmFormPage* pFormPage = dynamic_cast<FmFormPage*>( rWindow.GetPageView().GetPage() );
+ if ( !pFormPage )
+ return;
+
+ const Reference< XControlContainer >& xCC = rWindow.GetControlContainer();
+ if ( xCC.is()
+ && ( !findWindow( xCC ).is() )
+ )
+ {
+ rtl::Reference< FormViewPageWindowAdapter > pAdapter = new FormViewPageWindowAdapter( comphelper::getProcessComponentContext(), rWindow, this );
+ m_aPageWindowAdapters.push_back( pAdapter );
+
+ // listen at the ControlContainer to notice changes
+ Reference< XContainer > xContainer( xCC, UNO_QUERY );
+ if ( xContainer.is() )
+ xContainer->addContainerListener( this );
+ }
+}
+
+
+void FmXFormView::removeWindow( const Reference< XControlContainer >& _rxCC )
+{
+ // Is called if
+ // - the design mode is being switched to
+ // - a window is deleted while in the design mode
+ // - the control container for a window is removed while the active mode is on
+
+ auto i = std::find_if(m_aPageWindowAdapters.begin(), m_aPageWindowAdapters.end(),
+ [&_rxCC](const rtl::Reference< FormViewPageWindowAdapter >& rpAdapter) { return _rxCC == rpAdapter->getControlContainer(); });
+ if (i != m_aPageWindowAdapters.end())
+ {
+ Reference< XContainer > xContainer( _rxCC, UNO_QUERY );
+ if ( xContainer.is() )
+ xContainer->removeContainerListener( this );
+
+ (*i)->dispose();
+ m_aPageWindowAdapters.erase( i );
+ }
+}
+
+void FmXFormView::displayAsyncErrorMessage( const SQLErrorEvent& _rEvent )
+{
+ DBG_ASSERT( nullptr == m_nErrorMessageEvent, "FmXFormView::displayAsyncErrorMessage: not too fast, please!" );
+ // This should not happen - usually, the PostUserEvent is faster than any possible user
+ // interaction which could trigger a new error. If it happens, we need a queue for the events.
+ m_aAsyncError = _rEvent;
+ m_nErrorMessageEvent = Application::PostUserEvent( LINK( this, FmXFormView, OnDelayedErrorMessage ) );
+}
+
+IMPL_LINK_NOARG(FmXFormView, OnDelayedErrorMessage, void*, void)
+{
+ m_nErrorMessageEvent = nullptr;
+ displayException(m_aAsyncError, GetParentWindow());
+}
+
+void FmXFormView::onFirstViewActivation( const FmFormModel* _pDocModel )
+{
+ if ( _pDocModel && _pDocModel->GetAutoControlFocus() )
+ m_nAutoFocusEvent = Application::PostUserEvent( LINK( this, FmXFormView, OnAutoFocus ) );
+}
+
+void FmXFormView::suspendTabOrderUpdate()
+{
+ OSL_ENSURE( !m_isTabOrderUpdateSuspended, "FmXFormView::suspendTabOrderUpdate: nesting not allowed!" );
+ m_isTabOrderUpdateSuspended = true;
+}
+
+void FmXFormView::resumeTabOrderUpdate()
+{
+ OSL_ENSURE( m_isTabOrderUpdateSuspended, "FmXFormView::resumeTabOrderUpdate: not suspended!" );
+ m_isTabOrderUpdateSuspended = false;
+
+ // update the tab orders for all components which were collected since the suspendTabOrderUpdate call.
+ for (const auto& rContainer : m_aNeedTabOrderUpdate)
+ {
+ rtl::Reference< FormViewPageWindowAdapter > pAdapter = findWindow( rContainer.first );
+ if ( !pAdapter.is() )
+ continue;
+
+ for (const auto& rForm : rContainer.second)
+ {
+ pAdapter->updateTabOrder( rForm );
+ }
+ }
+ m_aNeedTabOrderUpdate.clear();
+}
+
+namespace
+{
+ bool isActivableDatabaseForm(const Reference< XFormController > &xController)
+ {
+ // only database forms are to be activated
+ Reference< XRowSet > xForm(xController->getModel(), UNO_QUERY);
+ if ( !xForm.is() || !getConnection( xForm ).is() )
+ return false;
+
+ Reference< XPropertySet > xFormSet( xForm, UNO_QUERY );
+ if ( !xFormSet.is() )
+ {
+ SAL_WARN( "svx.form", "FmXFormView::OnActivate: a form which does not have properties?" );
+ return false;
+ }
+
+ const OUString aSource = ::comphelper::getString( xFormSet->getPropertyValue( FM_PROP_COMMAND ) );
+
+ return !aSource.isEmpty();
+ }
+
+ class find_active_databaseform
+ {
+ const Reference< XFormController > xActiveController;
+
+ public:
+
+ explicit find_active_databaseform( const Reference< XFormController >& _xActiveController )
+ : xActiveController(_xActiveController )
+ {}
+
+ Reference < XFormController > operator() (const Reference< XFormController > &xController)
+ {
+ if(xController == xActiveController && isActivableDatabaseForm(xController))
+ return xController;
+
+ if ( !xController.is() )
+ {
+ SAL_WARN( "svx.form", "FmXFormView::OnActivate: a form controller which does not have children?" );
+ return nullptr;
+ }
+
+ for(sal_Int32 i = 0; i < xController->getCount(); ++i)
+ {
+ const Any a(xController->getByIndex(i));
+ Reference < XFormController > xI;
+ if ((a >>= xI) && xI.is())
+ {
+ Reference < XFormController > xRes(operator()(xI));
+ if (xRes.is())
+ return xRes;
+ }
+ }
+
+ return nullptr;
+ }
+ };
+}
+
+
+IMPL_LINK_NOARG(FmXFormView, OnActivate, void*, void)
+{
+ m_nActivationEvent = nullptr;
+
+ if ( !m_pView )
+ {
+ OSL_FAIL( "FmXFormView::OnActivate: well... seems we have a timing problem (the view already died)!" );
+ return;
+ }
+
+ // setting the controller to activate
+ if (!(m_pView->GetFormShell() && m_pView->GetActualOutDev() && m_pView->GetActualOutDev()->GetOutDevType() == OUTDEV_WINDOW))
+ return;
+
+ FmXFormShell* const pShImpl = m_pView->GetFormShell()->GetImpl();
+
+ if(!pShImpl)
+ return;
+
+ find_active_databaseform fad(pShImpl->getActiveController_Lock());
+
+ vcl::Window* pWindow = m_pView->GetActualOutDev()->GetOwnerWindow();
+ rtl::Reference< FormViewPageWindowAdapter > pAdapter = m_aPageWindowAdapters.empty() ? nullptr : m_aPageWindowAdapters[0];
+ for (const auto& rpPageWindowAdapter : m_aPageWindowAdapters)
+ {
+ if ( pWindow == rpPageWindowAdapter->getWindow() )
+ pAdapter = rpPageWindowAdapter;
+ }
+
+ if ( !pAdapter.is() )
+ return;
+
+ Reference< XFormController > xControllerToActivate;
+ for (const Reference< XFormController > & xController : pAdapter->GetList())
+ {
+ if ( !xController.is() )
+ continue;
+
+ {
+ Reference< XFormController > xActiveController(fad(xController));
+ if (xActiveController.is())
+ {
+ xControllerToActivate = xActiveController;
+ break;
+ }
+ }
+
+ if(xControllerToActivate.is() || !isActivableDatabaseForm(xController))
+ continue;
+
+ xControllerToActivate = xController;
+ }
+ pShImpl->setActiveController_Lock(xControllerToActivate);
+}
+
+
+void FmXFormView::Activate(bool bSync)
+{
+ if (m_nActivationEvent)
+ {
+ Application::RemoveUserEvent(m_nActivationEvent);
+ m_nActivationEvent = nullptr;
+ }
+
+ if (bSync)
+ {
+ LINK(this,FmXFormView,OnActivate).Call(nullptr);
+ }
+ else
+ m_nActivationEvent = Application::PostUserEvent(LINK(this,FmXFormView,OnActivate));
+}
+
+
+void FmXFormView::Deactivate(bool bDeactivateController)
+{
+ if (m_nActivationEvent)
+ {
+ Application::RemoveUserEvent(m_nActivationEvent);
+ m_nActivationEvent = nullptr;
+ }
+
+ FmXFormShell* pShImpl = m_pView->GetFormShell() ? m_pView->GetFormShell()->GetImpl() : nullptr;
+ if (pShImpl && bDeactivateController)
+ pShImpl->setActiveController_Lock(nullptr);
+}
+
+
+FmFormShell* FmXFormView::GetFormShell() const
+{
+ return m_pView ? m_pView->GetFormShell() : nullptr;
+}
+
+void FmXFormView::AutoFocus()
+{
+ if (m_nAutoFocusEvent)
+ Application::RemoveUserEvent(m_nAutoFocusEvent);
+
+ m_nAutoFocusEvent = Application::PostUserEvent(LINK(this, FmXFormView, OnAutoFocus));
+}
+
+
+bool FmXFormView::isFocusable( const Reference< XControl >& i_rControl )
+{
+ if ( !i_rControl.is() )
+ return false;
+
+ try
+ {
+ Reference< XPropertySet > xModelProps( i_rControl->getModel(), UNO_QUERY_THROW );
+
+ // only enabled controls are allowed to participate
+ bool bEnabled = false;
+ OSL_VERIFY( xModelProps->getPropertyValue( FM_PROP_ENABLED ) >>= bEnabled );
+ if ( !bEnabled )
+ return false;
+
+ // check the class id of the control model
+ sal_Int16 nClassId = FormComponentType::CONTROL;
+ OSL_VERIFY( xModelProps->getPropertyValue( FM_PROP_CLASSID ) >>= nClassId );
+
+ // controls which are not focussable
+ if ( ( FormComponentType::CONTROL != nClassId )
+ && ( FormComponentType::IMAGEBUTTON != nClassId )
+ && ( FormComponentType::GROUPBOX != nClassId )
+ && ( FormComponentType::FIXEDTEXT != nClassId )
+ && ( FormComponentType::HIDDENCONTROL != nClassId )
+ && ( FormComponentType::IMAGECONTROL != nClassId )
+ && ( FormComponentType::SCROLLBAR != nClassId )
+ && ( FormComponentType::SPINBUTTON!= nClassId )
+ )
+ {
+ return true;
+ }
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return false;
+}
+
+
+static Reference< XControl > lcl_firstFocussableControl( const Sequence< Reference< XControl > >& _rControls )
+{
+ Reference< XControl > xReturn;
+
+ // loop through all the controls
+ for ( auto const & control : _rControls )
+ {
+ if ( !control.is() )
+ continue;
+
+ if ( FmXFormView::isFocusable( control ) )
+ {
+ xReturn = control;
+ break;
+ }
+ }
+
+ if ( !xReturn.is() && _rControls.hasElements() )
+ xReturn = _rControls[0];
+
+ return xReturn;
+}
+
+
+namespace
+{
+
+ void lcl_ensureControlsOfFormExist_nothrow( const SdrPage& _rPage, const SdrView& _rView, const vcl::Window& _rWindow, const Reference< XForm >& _rxForm )
+ {
+ try
+ {
+ Reference< XInterface > xNormalizedForm( _rxForm, UNO_QUERY_THROW );
+
+ SdrObjListIter aSdrObjectLoop( &_rPage, SdrIterMode::DeepNoGroups );
+ while ( aSdrObjectLoop.IsMore() )
+ {
+ FmFormObj* pFormObject = FmFormObj::GetFormObject( aSdrObjectLoop.Next() );
+ if ( !pFormObject )
+ continue;
+
+ Reference< XChild > xModel( pFormObject->GetUnoControlModel(), UNO_QUERY_THROW );
+ Reference< XInterface > xModelParent( xModel->getParent(), UNO_QUERY );
+
+ if ( xNormalizedForm.get() != xModelParent.get() )
+ continue;
+
+ pFormObject->GetUnoControl( _rView, *_rWindow.GetOutDev() );
+ }
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+}
+
+
+Reference< XFormController > FmXFormView::getFormController( const Reference< XForm >& _rxForm, const OutputDevice& _rDevice ) const
+{
+ Reference< XFormController > xController;
+
+ for (const rtl::Reference< FormViewPageWindowAdapter >& pAdapter : m_aPageWindowAdapters)
+ {
+ if ( !pAdapter )
+ {
+ SAL_WARN( "svx.form", "FmXFormView::getFormController: invalid page window adapter!" );
+ continue;
+ }
+
+ if ( pAdapter->getWindow() != _rDevice.GetOwnerWindow() )
+ // wrong device
+ continue;
+
+ xController = pAdapter->getController( _rxForm );
+ if ( xController.is() )
+ break;
+ }
+ return xController;
+}
+
+
+IMPL_LINK_NOARG(FmXFormView, OnAutoFocus, void*, void)
+{
+ m_nAutoFocusEvent = nullptr;
+
+ // go to the first form of our page, examine it's TabController, go to its first (in terms of the tab order)
+ // control, give it the focus
+
+ SdrPageView *pPageView = m_pView ? m_pView->GetSdrPageView() : nullptr;
+ SdrPage *pSdrPage = pPageView ? pPageView->GetPage() : nullptr;
+ // get the forms collection of the page we belong to
+ FmFormPage* pPage = dynamic_cast<FmFormPage*>( pSdrPage );
+ Reference< XIndexAccess > xForms( pPage ? Reference< XIndexAccess >( pPage->GetForms() ) : Reference< XIndexAccess >() );
+
+ const rtl::Reference< FormViewPageWindowAdapter > pAdapter = m_aPageWindowAdapters.empty() ? nullptr : m_aPageWindowAdapters[0];
+ const vcl::Window* pWindow = pAdapter ? pAdapter->getWindow() : nullptr;
+
+ ENSURE_OR_RETURN_VOID( xForms.is() && pWindow, "FmXFormView::OnAutoFocus: could not collect all essentials!" );
+
+ try
+ {
+ // go for the tab controller of the first form
+ if ( !xForms->getCount() )
+ return;
+ Reference< XForm > xForm( xForms->getByIndex( 0 ), UNO_QUERY_THROW );
+ Reference< XTabController > xTabController( pAdapter->getController( xForm ), UNO_QUERY_THROW );
+
+ // go for the first control of the controller
+ Sequence< Reference< XControl > > aControls( xTabController->getControls() );
+ if ( !aControls.hasElements() )
+ {
+ Reference< XElementAccess > xFormElementAccess( xForm, UNO_QUERY_THROW );
+ if (xFormElementAccess->hasElements() && pPage && m_pView)
+ {
+ // there are control models in the form, but no controls, yet.
+ // Well, since some time controls are created on demand only. In particular,
+ // they're normally created when they're first painted.
+ // Unfortunately, the FormController does not have any way to
+ // trigger the creation itself, so we must hack this ...
+ lcl_ensureControlsOfFormExist_nothrow( *pPage, *m_pView, *pWindow, xForm );
+ aControls = xTabController->getControls();
+ OSL_ENSURE( aControls.hasElements(), "FmXFormView::OnAutoFocus: no controls at all!" );
+ }
+ }
+
+ // set the focus to this first control
+ Reference< XWindow > xControlWindow( lcl_firstFocussableControl( aControls ), UNO_QUERY );
+ if ( !xControlWindow.is() )
+ return;
+
+ xControlWindow->setFocus();
+
+ // ensure that the control is visible
+ // 80210 - 12/07/00 - FS
+ const OutputDevice* pOut = m_pView ? m_pView->GetActualOutDev() : nullptr;
+ const vcl::Window* pCurrentWindow = pOut ? pOut->GetOwnerWindow() : nullptr;
+ if ( pCurrentWindow )
+ {
+ awt::Rectangle aRect = xControlWindow->getPosSize();
+ ::tools::Rectangle aNonUnoRect( aRect.X, aRect.Y, aRect.X + aRect.Width, aRect.Y + aRect.Height );
+ m_pView->MakeVisible( pCurrentWindow->PixelToLogic( aNonUnoRect ), *const_cast< vcl::Window* >( pCurrentWindow ) );
+ }
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+
+void FmXFormView::onCreatedFormObject( FmFormObj const & _rFormObject )
+{
+ FmFormShell* pShell = m_pView ? m_pView->GetFormShell() : nullptr;
+ FmXFormShell* pShellImpl = pShell ? pShell->GetImpl() : nullptr;
+ OSL_ENSURE( pShellImpl, "FmXFormView::onCreatedFormObject: no form shell!" );
+ if ( !pShellImpl )
+ return;
+
+ // it is valid that the form shell's forms collection is not initialized, yet
+ pShellImpl->UpdateForms_Lock(true);
+
+ m_xLastCreatedControlModel.set( _rFormObject.GetUnoControlModel(), UNO_QUERY );
+ if ( !m_xLastCreatedControlModel.is() )
+ return;
+
+ // some initial property defaults
+ FormControlFactory aControlFactory;
+ aControlFactory.initializeControlModel(pShellImpl->getDocumentType_Lock(), _rFormObject);
+
+ if (!pShellImpl->GetWizardUsing_Lock())
+ return;
+
+ // #i31958# don't call wizards in XForms mode
+ if (pShellImpl->isEnhancedForm_Lock())
+ return;
+
+ // #i46898# no wizards if there is no Base installed - currently, all wizards are
+ // database related
+ if ( !SvtModuleOptions().IsModuleInstalled( SvtModuleOptions::EModule::DATABASE ) )
+ return;
+
+ if ( m_nControlWizardEvent )
+ Application::RemoveUserEvent( m_nControlWizardEvent );
+ m_nControlWizardEvent = Application::PostUserEvent( LINK( this, FmXFormView, OnStartControlWizard ) );
+}
+
+void FmXFormView::breakCreateFormObject()
+{
+ if (m_nControlWizardEvent != nullptr)
+ {
+ Application::RemoveUserEvent(m_nControlWizardEvent);
+ m_nControlWizardEvent = nullptr;
+ }
+ m_xLastCreatedControlModel.clear();
+}
+
+Reference<XWindow> FmXFormView::GetParentWindow() const
+{
+ const OutputDevice* pOut = m_pView ? m_pView->GetActualOutDev() : nullptr;
+ const vcl::Window* pCurrentWindow = pOut ? pOut->GetOwnerWindow() : nullptr;
+ return VCLUnoHelper::GetInterface(const_cast<vcl::Window*>(pCurrentWindow));
+}
+
+IMPL_LINK_NOARG( FmXFormView, OnStartControlWizard, void*, void )
+{
+ m_nControlWizardEvent = nullptr;
+ OSL_PRECOND( m_xLastCreatedControlModel.is(), "FmXFormView::OnStartControlWizard: illegal call!" );
+ if ( !m_xLastCreatedControlModel.is() )
+ return;
+
+ sal_Int16 nClassId = FormComponentType::CONTROL;
+ try
+ {
+ OSL_VERIFY( m_xLastCreatedControlModel->getPropertyValue( FM_PROP_CLASSID ) >>= nClassId );
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ const char* pWizardAsciiName = nullptr;
+ switch ( nClassId )
+ {
+ case FormComponentType::GRIDCONTROL:
+ pWizardAsciiName = "com.sun.star.sdb.GridControlAutoPilot";
+ break;
+ case FormComponentType::LISTBOX:
+ case FormComponentType::COMBOBOX:
+ pWizardAsciiName = "com.sun.star.sdb.ListComboBoxAutoPilot";
+ break;
+ case FormComponentType::GROUPBOX:
+ pWizardAsciiName = "com.sun.star.sdb.GroupBoxAutoPilot";
+ break;
+ }
+
+ if ( pWizardAsciiName )
+ {
+ // build the argument list
+ ::comphelper::NamedValueCollection aWizardArgs;
+ aWizardArgs.put("ObjectModel", m_xLastCreatedControlModel);
+ aWizardArgs.put("ParentWindow", GetParentWindow());
+
+ // create the wizard object
+ Reference< XExecutableDialog > xWizard;
+ try
+ {
+ Reference<XComponentContext> xContext = comphelper::getProcessComponentContext();
+ xWizard.set( xContext->getServiceManager()->createInstanceWithArgumentsAndContext( OUString::createFromAscii(pWizardAsciiName), aWizardArgs.getWrappedPropertyValues(), xContext ), UNO_QUERY);
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ if ( !xWizard.is() )
+ {
+ ShowServiceNotAvailableError( nullptr, OUString::createFromAscii(pWizardAsciiName), true );
+ }
+ else
+ {
+ // execute the wizard
+ try
+ {
+ xWizard->execute();
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ }
+
+ m_xLastCreatedControlModel.clear();
+}
+
+
+namespace
+{
+ void lcl_insertIntoFormComponentHierarchy_throw( const FmFormView& _rView, const SdrUnoObj& _rSdrObj,
+ const Reference< XDataSource >& _rxDataSource, const OUString& _rDataSourceName,
+ const OUString& _rCommand, const sal_Int32 _nCommandType )
+ {
+ FmFormPage& rPage = static_cast< FmFormPage& >( *_rView.GetSdrPageView()->GetPage() );
+
+ Reference< XFormComponent > xFormComponent( _rSdrObj.GetUnoControlModel(), UNO_QUERY_THROW );
+ Reference< XForm > xTargetForm(
+ rPage.GetImpl().findPlaceInFormComponentHierarchy( xFormComponent, _rxDataSource, _rDataSourceName, _rCommand, _nCommandType ),
+ UNO_SET_THROW );
+
+ FmFormPageImpl::setUniqueName( xFormComponent, xTargetForm );
+
+ Reference< XIndexContainer > xFormAsContainer( xTargetForm, UNO_QUERY_THROW );
+ xFormAsContainer->insertByIndex( xFormAsContainer->getCount(), Any( xFormComponent ) );
+ }
+}
+
+
+rtl::Reference<SdrObject> FmXFormView::implCreateFieldControl( const svx::ODataAccessDescriptor& _rColumnDescriptor )
+{
+ // not if we're in design mode
+ if ( !m_pView->IsDesignMode() )
+ return nullptr;
+
+ OUString sCommand, sFieldName;
+ sal_Int32 nCommandType = CommandType::COMMAND;
+ SharedConnection xConnection;
+
+ OUString sDataSource = _rColumnDescriptor.getDataSource();
+ _rColumnDescriptor[ DataAccessDescriptorProperty::Command ] >>= sCommand;
+ _rColumnDescriptor[ DataAccessDescriptorProperty::ColumnName ] >>= sFieldName;
+ _rColumnDescriptor[ DataAccessDescriptorProperty::CommandType ] >>= nCommandType;
+ {
+ Reference< XConnection > xExternalConnection;
+ _rColumnDescriptor[ DataAccessDescriptorProperty::Connection ] >>= xExternalConnection;
+ xConnection.reset( xExternalConnection, SharedConnection::NoTakeOwnership );
+ }
+
+ if ( sCommand.isEmpty()
+ || sFieldName.isEmpty()
+ || ( sDataSource.isEmpty()
+ && !xConnection.is()
+ )
+ )
+ {
+ OSL_FAIL( "FmXFormView::implCreateFieldControl: nonsense!" );
+ }
+
+ Reference< XDataSource > xDataSource;
+ SQLErrorEvent aError;
+ try
+ {
+ if ( xConnection.is() && !xDataSource.is() && sDataSource.isEmpty() )
+ {
+ Reference< XChild > xChild( xConnection, UNO_QUERY );
+ if ( xChild.is() )
+ xDataSource.set(xChild->getParent(), css::uno::UNO_QUERY);
+ }
+
+ // obtain the data source
+ if ( !xDataSource.is() )
+ xDataSource = getDataSource( sDataSource, comphelper::getProcessComponentContext() );
+
+ // and the connection, if necessary
+ if ( !xConnection.is() )
+ xConnection.reset( getConnection_withFeedback(
+ sDataSource,
+ OUString(),
+ OUString(),
+ comphelper::getProcessComponentContext(),
+ nullptr
+ ) );
+ }
+ catch (const SQLException&)
+ {
+ aError.Reason = ::cppu::getCaughtException();
+ }
+ catch (const Exception& )
+ {
+ /* will be asserted below */
+ }
+ if (aError.Reason.hasValue())
+ {
+ displayAsyncErrorMessage( aError );
+ return nullptr;
+ }
+
+ // need a data source and a connection here
+ if (!xDataSource.is() || !xConnection.is())
+ {
+ OSL_FAIL("FmXFormView::implCreateFieldControl : could not retrieve the data source or the connection!");
+ return nullptr;
+ }
+
+ Reference< XComponent > xKeepFieldsAlive;
+ // go
+ try
+ {
+ // determine the table/query field which we should create a control for
+ Reference< XPropertySet > xField;
+
+ Reference< XNameAccess > xFields = getFieldsByCommandDescriptor(
+ xConnection, nCommandType, sCommand, xKeepFieldsAlive );
+
+ if (xFields.is() && xFields->hasByName(sFieldName))
+ xFields->getByName(sFieldName) >>= xField;
+ if ( !xField.is() )
+ return nullptr;
+
+ Reference< XNumberFormatsSupplier > xSupplier( getNumberFormats( xConnection ), UNO_SET_THROW );
+ Reference< XNumberFormats > xNumberFormats( xSupplier->getNumberFormats(), UNO_SET_THROW );
+
+ OUString sLabelPostfix;
+
+
+ // only for text size
+ OutputDevice* pOutDev = nullptr;
+ if (m_pView->GetActualOutDev() && m_pView->GetActualOutDev()->GetOutDevType() == OUTDEV_WINDOW)
+ pOutDev = const_cast<OutputDevice*>(m_pView->GetActualOutDev());
+ else
+ {// find OutDev
+ if (SdrPageView* pPageView = m_pView->GetSdrPageView())
+ {
+ // const SdrPageViewWinList& rWinList = pPageView->GetWinList();
+ // const SdrPageViewWindows& rPageViewWindows = pPageView->GetPageViewWindows();
+
+ for( sal_uInt32 i = 0; i < pPageView->PageWindowCount(); i++ )
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(i);
+
+ if( rPageWindow.GetPaintWindow().OutputToWindow())
+ {
+ pOutDev = &rPageWindow.GetPaintWindow().GetOutputDevice();
+ break;
+ }
+ }
+ }
+ }
+
+ if ( !pOutDev )
+ return nullptr;
+
+ sal_Int32 nDataType = ::comphelper::getINT32(xField->getPropertyValue(FM_PROP_FIELDTYPE));
+ if ((DataType::BINARY == nDataType) || (DataType::VARBINARY == nDataType))
+ return nullptr;
+
+
+ // determine the control type by examining the data type of the bound column
+ SdrObjKind nOBJID = SdrObjKind::NONE;
+ bool bDateNTimeField = false;
+
+ bool bIsCurrency = false;
+ if (::comphelper::hasProperty(FM_PROP_ISCURRENCY, xField))
+ bIsCurrency = ::comphelper::getBOOL(xField->getPropertyValue(FM_PROP_ISCURRENCY));
+
+ if (bIsCurrency)
+ nOBJID = SdrObjKind::FormCurrencyField;
+ else
+ switch (nDataType)
+ {
+ case DataType::BLOB:
+ case DataType::LONGVARBINARY:
+ nOBJID = SdrObjKind::FormImageControl;
+ break;
+ case DataType::LONGVARCHAR:
+ case DataType::CLOB:
+ nOBJID = SdrObjKind::FormEdit;
+ break;
+ case DataType::BINARY:
+ case DataType::VARBINARY:
+ return nullptr;
+ case DataType::BIT:
+ case DataType::BOOLEAN:
+ nOBJID = SdrObjKind::FormCheckbox;
+ break;
+ case DataType::TINYINT:
+ case DataType::SMALLINT:
+ case DataType::INTEGER:
+ nOBJID = SdrObjKind::FormNumericField;
+ break;
+ case DataType::REAL:
+ case DataType::DOUBLE:
+ case DataType::NUMERIC:
+ case DataType::DECIMAL:
+ nOBJID = SdrObjKind::FormFormattedField;
+ break;
+ case DataType::TIMESTAMP:
+ bDateNTimeField = true;
+ sLabelPostfix = SvxResId(RID_STR_POSTFIX_DATE);
+ [[fallthrough]];
+ case DataType::DATE:
+ nOBJID = SdrObjKind::FormDateField;
+ break;
+ case DataType::TIME:
+ nOBJID = SdrObjKind::FormTimeField;
+ break;
+ case DataType::CHAR:
+ case DataType::VARCHAR:
+ default:
+ nOBJID = SdrObjKind::FormEdit;
+ break;
+ }
+ if (nOBJID == SdrObjKind::NONE)
+ return nullptr;
+
+ rtl::Reference<SdrUnoObj> pLabel;
+ rtl::Reference<SdrUnoObj> pControl;
+ if ( !createControlLabelPair( *pOutDev, 0, 0, xField, xNumberFormats, nOBJID, sLabelPostfix,
+ pLabel, pControl, xDataSource, sDataSource, sCommand, nCommandType )
+ )
+ {
+ return nullptr;
+ }
+
+
+ // group objects
+ bool bCheckbox = ( SdrObjKind::FormCheckbox == nOBJID );
+ OSL_ENSURE( !bCheckbox || !pLabel, "FmXFormView::implCreateFieldControl: why was there a label created for a check box?" );
+ if ( bCheckbox )
+ return pControl;
+
+ rtl::Reference<SdrObjGroup> pGroup = new SdrObjGroup(getView()->getSdrModelFromSdrView());
+ SdrObjList* pObjList = pGroup->GetSubList();
+ pObjList->InsertObject( pLabel.get() );
+ pObjList->InsertObject( pControl.get() );
+
+ if ( bDateNTimeField )
+ { // so far we created a date field only, but we also need a time field
+ if ( createControlLabelPair( *pOutDev, 0, 1000, xField, xNumberFormats, SdrObjKind::FormTimeField,
+ SvxResId(RID_STR_POSTFIX_TIME), pLabel, pControl,
+ xDataSource, sDataSource, sCommand, nCommandType )
+ )
+ {
+ pObjList->InsertObject( pLabel.get() );
+ pObjList->InsertObject( pControl.get() );
+ }
+ }
+
+ return pGroup; // and done
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+
+ return nullptr;
+}
+
+
+rtl::Reference<SdrObject> FmXFormView::implCreateXFormsControl( const svx::OXFormsDescriptor &_rDesc )
+{
+ // not if we're in design mode
+ if ( !m_pView->IsDesignMode() )
+ return nullptr;
+
+ // go
+ try
+ {
+ // determine the table/query field which we should create a control for
+ Reference< XNumberFormats > xNumberFormats;
+ OUString sLabelPostfix = _rDesc.szName;
+
+
+ // only for text size
+ OutputDevice* pOutDev = nullptr;
+ if (m_pView->GetActualOutDev() && m_pView->GetActualOutDev()->GetOutDevType() == OUTDEV_WINDOW)
+ pOutDev = const_cast<OutputDevice*>(m_pView->GetActualOutDev());
+ else
+ {// find OutDev
+ if (SdrPageView* pPageView = m_pView->GetSdrPageView())
+ {
+ // const SdrPageViewWinList& rWinList = pPageView->GetWinList();
+ // const SdrPageViewWindows& rPageViewWindows = pPageView->GetPageViewWindows();
+
+ for( sal_uInt32 i = 0; i < pPageView->PageWindowCount(); i++ )
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(i);
+
+ if( rPageWindow.GetPaintWindow().GetOutputDevice().GetOutDevType() == OUTDEV_WINDOW)
+ {
+ pOutDev = &rPageWindow.GetPaintWindow().GetOutputDevice();
+ break;
+ }
+ }
+ }
+ }
+
+ if ( !pOutDev )
+ return nullptr;
+
+
+ // The service name decides which control should be created
+ SdrObjKind nOBJID = SdrObjKind::FormEdit;
+ if(_rDesc.szServiceName == FM_SUN_COMPONENT_NUMERICFIELD)
+ nOBJID = SdrObjKind::FormNumericField;
+ if(_rDesc.szServiceName == FM_SUN_COMPONENT_CHECKBOX)
+ nOBJID = SdrObjKind::FormCheckbox;
+ if(_rDesc.szServiceName == FM_COMPONENT_COMMANDBUTTON)
+ nOBJID = SdrObjKind::FormButton;
+
+ Reference< css::form::submission::XSubmission > xSubmission(_rDesc.xPropSet, UNO_QUERY);
+
+ // xform control or submission button?
+ if ( !xSubmission.is() )
+ {
+ rtl::Reference<SdrUnoObj> pLabel;
+ rtl::Reference<SdrUnoObj> pControl;
+ if ( !createControlLabelPair( *pOutDev, 0, 0, nullptr, xNumberFormats, nOBJID, sLabelPostfix,
+ pLabel, pControl, nullptr, "", "", -1 )
+ )
+ {
+ return nullptr;
+ }
+
+
+ // Now build the connection between the control and the data item.
+ Reference< XValueBinding > xValueBinding(_rDesc.xPropSet,UNO_QUERY);
+ Reference< XBindableValue > xBindableValue(pControl->GetUnoControlModel(),UNO_QUERY);
+
+ DBG_ASSERT( xBindableValue.is(), "FmXFormView::implCreateXFormsControl: control's not bindable!" );
+ if ( xBindableValue.is() )
+ xBindableValue->setValueBinding(xValueBinding);
+
+ bool bCheckbox = ( SdrObjKind::FormCheckbox == nOBJID );
+ OSL_ENSURE( !bCheckbox || !pLabel, "FmXFormView::implCreateXFormsControl: why was there a label created for a check box?" );
+ if ( bCheckbox )
+ return pControl;
+
+
+ // group objects
+ rtl::Reference<SdrObjGroup> pGroup = new SdrObjGroup(getView()->getSdrModelFromSdrView());
+ SdrObjList* pObjList = pGroup->GetSubList();
+ pObjList->InsertObject(pLabel.get());
+ pObjList->InsertObject(pControl.get());
+
+ return pGroup;
+ }
+ else {
+
+ // create a button control
+ const MapMode& eTargetMode( pOutDev->GetMapMode() );
+ const MapMode eSourceMode(MapUnit::Map100thMM);
+ const SdrObjKind nObjID = SdrObjKind::FormButton;
+ ::Size controlSize(4000, 500);
+ rtl::Reference<FmFormObj> pControl = static_cast<FmFormObj*>(
+ SdrObjFactory::MakeNewObject(
+ getView()->getSdrModelFromSdrView(),
+ SdrInventor::FmForm,
+ nObjID).get());
+ controlSize.setWidth( tools::Long(controlSize.Width() * eTargetMode.GetScaleX()) );
+ controlSize.setHeight( tools::Long(controlSize.Height() * eTargetMode.GetScaleY()) );
+ ::Point controlPos( OutputDevice::LogicToLogic( ::Point( controlSize.Width(), 0 ), eSourceMode, eTargetMode ) );
+ ::tools::Rectangle controlRect( controlPos, OutputDevice::LogicToLogic( controlSize, eSourceMode, eTargetMode ) );
+ pControl->SetLogicRect(controlRect);
+
+ // set the button label
+ Reference< XPropertySet > xControlSet(pControl->GetUnoControlModel(), UNO_QUERY);
+ xControlSet->setPropertyValue(FM_PROP_LABEL, Any(_rDesc.szName));
+
+ // connect the submission with the submission supplier (aka the button)
+ xControlSet->setPropertyValue( FM_PROP_BUTTON_TYPE,
+ Any( FormButtonType_SUBMIT ) );
+ Reference< css::form::submission::XSubmissionSupplier > xSubmissionSupplier(pControl->GetUnoControlModel(), UNO_QUERY);
+ xSubmissionSupplier->setSubmission(xSubmission);
+
+ return rtl::Reference<SdrObject>(pControl);
+ }
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx.form", "caught an exception while creating the control !");
+ }
+
+
+ return nullptr;
+}
+
+bool FmXFormView::createControlLabelPair( OutputDevice const & _rOutDev, sal_Int32 _nXOffsetMM, sal_Int32 _nYOffsetMM,
+ const Reference< XPropertySet >& _rxField, const Reference< XNumberFormats >& _rxNumberFormats,
+ SdrObjKind _nControlObjectID, std::u16string_view _rFieldPostfix,
+ rtl::Reference<SdrUnoObj>& _rpLabel,
+ rtl::Reference<SdrUnoObj>& _rpControl,
+ const Reference< XDataSource >& _rxDataSource, const OUString& _rDataSourceName,
+ const OUString& _rCommand, const sal_Int32 _nCommandType )
+{
+ if(!createControlLabelPair(
+ _rOutDev,
+ _nXOffsetMM,
+ _nYOffsetMM,
+ _rxField,
+ _rxNumberFormats,
+ _nControlObjectID,
+ _rFieldPostfix,
+ SdrInventor::FmForm,
+ SdrObjKind::FormFixedText,
+
+ // tdf#118963 Hand over a SdrModel to SdrObject-creation. It uses the local m_pView
+ // and already returning false when nullptr == getView() could be done, but m_pView
+ // is already dereferenced here in many places (see below), so just use it for now.
+ getView()->getSdrModelFromSdrView(),
+
+ _rpLabel,
+ _rpControl))
+ {
+ return false;
+ }
+
+ // insert the control model(s) into the form component hierarchy
+ if ( _rpLabel )
+ lcl_insertIntoFormComponentHierarchy_throw( *m_pView, *_rpLabel, _rxDataSource, _rDataSourceName, _rCommand, _nCommandType );
+ lcl_insertIntoFormComponentHierarchy_throw( *m_pView, *_rpControl, _rxDataSource, _rDataSourceName, _rCommand, _nCommandType );
+
+ // some context-dependent initializations
+ FormControlFactory aControlFactory;
+ if ( _rpLabel )
+ aControlFactory.initializeControlModel( impl_getDocumentType(), *_rpLabel );
+ aControlFactory.initializeControlModel( impl_getDocumentType(), *_rpControl );
+
+ return true;
+}
+
+
+bool FmXFormView::createControlLabelPair( OutputDevice const & _rOutDev, sal_Int32 _nXOffsetMM, sal_Int32 _nYOffsetMM,
+ const Reference< XPropertySet >& _rxField,
+ const Reference< XNumberFormats >& _rxNumberFormats, SdrObjKind _nControlObjectID,
+ std::u16string_view _rFieldPostfix, SdrInventor _nInventor, SdrObjKind _nLabelObjectID,
+ SdrModel& _rModel,
+ rtl::Reference<SdrUnoObj>& _rpLabel, rtl::Reference<SdrUnoObj>& _rpControl)
+{
+ sal_Int32 nDataType = 0;
+ OUString sFieldName;
+ Any aFieldName;
+ if ( _rxField.is() )
+ {
+ nDataType = ::comphelper::getINT32(_rxField->getPropertyValue(FM_PROP_FIELDTYPE));
+ aFieldName = _rxField->getPropertyValue(FM_PROP_NAME);
+ aFieldName >>= sFieldName;
+ }
+
+ // calculate the positions, respecting the settings of the target device
+ ::Size aTextSize( _rOutDev.GetTextWidth(sFieldName + _rFieldPostfix), _rOutDev.GetTextHeight() );
+
+ MapMode eTargetMode( _rOutDev.GetMapMode() ),
+ eSourceMode( MapUnit::Map100thMM );
+
+ // text width is at least 4 centimeters
+ // text height is always half a centimeter
+ ::Size aDefTxtSize(4000, 500);
+ ::Size aDefSize(4000, 500);
+ ::Size aDefImageSize(4000, 4000);
+
+ ::Size aRealSize = OutputDevice::LogicToLogic(aTextSize, eTargetMode, eSourceMode);
+ aRealSize.setWidth( std::max(aRealSize.Width(), aDefTxtSize.Width()) );
+ aRealSize.setHeight( aDefSize.Height() );
+
+ // adjust to scaling of the target device (#53523#)
+ aRealSize.setWidth( tools::Long(Fraction(aRealSize.Width(), 1) * eTargetMode.GetScaleX()) );
+ aRealSize.setHeight( tools::Long(Fraction(aRealSize.Height(), 1) * eTargetMode.GetScaleY()) );
+
+ // for boolean fields, we do not create a label, but just a checkbox
+ bool bNeedLabel = ( _nControlObjectID != SdrObjKind::FormCheckbox );
+
+ // the label
+ rtl::Reference< SdrUnoObj > pLabel;
+ Reference< XPropertySet > xLabelModel;
+
+ if ( bNeedLabel )
+ {
+ pLabel = dynamic_cast< SdrUnoObj* >(
+ SdrObjFactory::MakeNewObject(
+ _rModel,
+ _nInventor,
+ _nLabelObjectID).get() );
+
+ OSL_ENSURE(pLabel, "FmXFormView::createControlLabelPair: could not create the label!");
+
+ if (!pLabel)
+ return false;
+
+ xLabelModel.set( pLabel->GetUnoControlModel(), UNO_QUERY );
+ if ( xLabelModel.is() )
+ {
+ OUString sLabel;
+ if ( _rxField.is() && _rxField->getPropertySetInfo()->hasPropertyByName(FM_PROP_LABEL) )
+ _rxField->getPropertyValue(FM_PROP_LABEL) >>= sLabel;
+ if ( sLabel.isEmpty() )
+ sLabel = sFieldName;
+
+ xLabelModel->setPropertyValue( FM_PROP_LABEL, Any( sLabel + _rFieldPostfix ) );
+ OUString sObjectLabel(SvxResId(RID_STR_OBJECT_LABEL).replaceAll("#object#", sFieldName));
+ xLabelModel->setPropertyValue(FM_PROP_NAME, Any(sObjectLabel));
+ }
+
+ pLabel->SetLogicRect( ::tools::Rectangle(
+ OutputDevice::LogicToLogic( ::Point( _nXOffsetMM, _nYOffsetMM ), eSourceMode, eTargetMode ),
+ OutputDevice::LogicToLogic( aRealSize, eSourceMode, eTargetMode )
+ ) );
+ }
+
+ // the control
+ rtl::Reference< SdrUnoObj > pControl( dynamic_cast< SdrUnoObj* >(
+ SdrObjFactory::MakeNewObject(
+ _rModel,
+ _nInventor,
+ _nControlObjectID).get() ));
+
+ OSL_ENSURE(pControl, "FmXFormView::createControlLabelPair: could not create the control!");
+
+ if (!pControl)
+ return false;
+
+ Reference< XPropertySet > xControlSet( pControl->GetUnoControlModel(), UNO_QUERY );
+ if ( !xControlSet.is() )
+ return false;
+
+ // size of the control
+ ::Size aControlSize( aDefSize );
+ switch ( nDataType )
+ {
+ case DataType::BIT:
+ case DataType::BOOLEAN:
+ aControlSize = aDefSize;
+ break;
+ case DataType::LONGVARCHAR:
+ case DataType::CLOB:
+ case DataType::LONGVARBINARY:
+ case DataType::BLOB:
+ aControlSize = aDefImageSize;
+ break;
+ }
+
+ if ( SdrObjKind::FormImageControl == _nControlObjectID )
+ aControlSize = aDefImageSize;
+
+ aControlSize.setWidth( tools::Long(Fraction(aControlSize.Width(), 1) * eTargetMode.GetScaleX()) );
+ aControlSize.setHeight( tools::Long(Fraction(aControlSize.Height(), 1) * eTargetMode.GetScaleY()) );
+
+ pControl->SetLogicRect( ::tools::Rectangle(
+ OutputDevice::LogicToLogic( ::Point( aRealSize.Width() + _nXOffsetMM, _nYOffsetMM ), eSourceMode, eTargetMode ),
+ OutputDevice::LogicToLogic( aControlSize, eSourceMode, eTargetMode )
+ ) );
+
+ // some initializations
+ Reference< XPropertySetInfo > xControlPropInfo = xControlSet->getPropertySetInfo();
+
+ if ( aFieldName.hasValue() )
+ {
+ xControlSet->setPropertyValue( FM_PROP_CONTROLSOURCE, aFieldName );
+ xControlSet->setPropertyValue( FM_PROP_NAME, aFieldName );
+ if ( !bNeedLabel )
+ {
+ // no dedicated label control => use the label property
+ if ( xControlPropInfo->hasPropertyByName( FM_PROP_LABEL ) )
+ xControlSet->setPropertyValue( FM_PROP_LABEL, Any( sFieldName + _rFieldPostfix ) );
+ else
+ OSL_FAIL( "FmXFormView::createControlLabelPair: can't set a label for the control!" );
+ }
+ }
+
+ if ( (nDataType == DataType::LONGVARCHAR || nDataType == DataType::CLOB) && xControlPropInfo->hasPropertyByName( FM_PROP_MULTILINE ) )
+ {
+ xControlSet->setPropertyValue( FM_PROP_MULTILINE, Any( true ) );
+ }
+
+ // announce the label to the control
+ if ( xControlPropInfo->hasPropertyByName( FM_PROP_CONTROLLABEL ) && xLabelModel.is() )
+ {
+ try
+ {
+ xControlSet->setPropertyValue( FM_PROP_CONTROLLABEL, Any( xLabelModel ) );
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+ if ( _rxField.is() )
+ {
+ FormControlFactory::initializeFieldDependentProperties( _rxField, xControlSet, _rxNumberFormats );
+ }
+
+ _rpLabel = std::move(pLabel);
+ _rpControl = std::move(pControl);
+ return true;
+}
+
+
+FmXFormView::ObjectRemoveListener::ObjectRemoveListener( FmXFormView* pParent )
+ :m_pParent( pParent )
+{
+}
+
+
+void FmXFormView::ObjectRemoveListener::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
+{
+ if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint)
+ return;
+ const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
+ if (pSdrHint->GetKind() == SdrHintKind::ObjectRemoved)
+ m_pParent->ObjectRemovedInAliveMode(pSdrHint->GetObject());
+}
+
+
+void FmXFormView::ObjectRemovedInAliveMode( const SdrObject* pObject )
+{
+ // if the remote object in my MarkList, which I have memorized when switching to the
+ // Alive mode, I have to take it out now, because I otherwise try to set the mark
+ // again when switching back (interestingly, this fails only with grouped objects
+ // (when accessing their ObjList GPF), not with individual ones)
+
+ const size_t nCount = m_aMark.GetMarkCount();
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ SdrMark* pMark = m_aMark.GetMark(i);
+ SdrObject* pCurrent = pMark->GetMarkedSdrObj();
+ if (pObject == pCurrent)
+ {
+ m_aMark.DeleteMark(i);
+ return;
+ }
+ // I do not need to descend into GroupObjects: if an object is deleted there,
+ // then the pointer, which I have, to the GroupObject still remains valid ...
+ }
+}
+
+
+void FmXFormView::stopMarkListWatching()
+{
+ if ( m_pWatchStoredList )
+ {
+ m_pWatchStoredList->EndListeningAll();
+ m_pWatchStoredList.reset();
+ }
+}
+
+
+void FmXFormView::startMarkListWatching()
+{
+ if ( !m_pWatchStoredList )
+ {
+ FmFormModel* pModel = GetFormShell() ? GetFormShell()->GetFormModel() : nullptr;
+ DBG_ASSERT( pModel != nullptr, "FmXFormView::startMarkListWatching: shell has no model!" );
+ if (pModel)
+ {
+ m_pWatchStoredList.reset(new ObjectRemoveListener( this ));
+ m_pWatchStoredList->StartListening( *static_cast< SfxBroadcaster* >( pModel ) );
+ }
+ }
+ else
+ {
+ OSL_FAIL( "FmXFormView::startMarkListWatching: already listening!" );
+ }
+}
+
+void FmXFormView::saveMarkList()
+{
+ if ( m_pView )
+ {
+ m_aMark = m_pView->GetMarkedObjectList();
+ const size_t nCount = m_aMark.GetMarkCount( );
+ for ( size_t i = 0; i < nCount; ++i )
+ {
+ SdrMark* pMark = m_aMark.GetMark(i);
+ SdrObject* pObj = pMark->GetMarkedSdrObj();
+
+ if ( m_pView->IsObjMarked( pObj ) )
+ {
+ if ( pObj->IsGroupObject() )
+ {
+ SdrObjListIter aIter( pObj->GetSubList() );
+ bool bMixed = false;
+ while ( aIter.IsMore() && !bMixed )
+ bMixed = ( aIter.Next()->GetObjInventor() != SdrInventor::FmForm );
+
+ if ( !bMixed )
+ {
+ // all objects in the group are form objects
+ m_pView->MarkObj( pMark->GetMarkedSdrObj(), pMark->GetPageView(), true /* unmark! */ );
+ }
+ }
+ else
+ {
+ if ( pObj->GetObjInventor() == SdrInventor::FmForm )
+ { // this is a form layer object
+ m_pView->MarkObj( pMark->GetMarkedSdrObj(), pMark->GetPageView(), true /* unmark! */ );
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ OSL_FAIL( "FmXFormView::saveMarkList: invalid view!" );
+ m_aMark.Clear();
+ }
+}
+
+static bool lcl_hasObject( SdrObjListIter& rIter, SdrObject const * pObj )
+{
+ bool bFound = false;
+ while (rIter.IsMore() && !bFound)
+ bFound = pObj == rIter.Next();
+
+ rIter.Reset();
+ return bFound;
+}
+
+
+void FmXFormView::restoreMarkList( SdrMarkList& _rRestoredMarkList )
+{
+ if ( !m_pView )
+ return;
+
+ _rRestoredMarkList.Clear();
+
+ const SdrMarkList& rCurrentList = m_pView->GetMarkedObjectList();
+ FmFormPage* pPage = GetFormShell() ? GetFormShell()->GetCurPage() : nullptr;
+ if (!pPage)
+ return;
+
+ if (rCurrentList.GetMarkCount())
+ { // there is a current mark ... hmm. Is it a subset of the mark we remembered in saveMarkList?
+ bool bMisMatch = false;
+
+ // loop through all current marks
+ const size_t nCurrentCount = rCurrentList.GetMarkCount();
+ for ( size_t i=0; i<nCurrentCount && !bMisMatch; ++i )
+ {
+ const SdrObject* pCurrentMarked = rCurrentList.GetMark( i )->GetMarkedSdrObj();
+
+ // loop through all saved marks, check for equality
+ bool bFound = false;
+ const size_t nSavedCount = m_aMark.GetMarkCount();
+ for ( size_t j=0; j<nSavedCount && !bFound; ++j )
+ {
+ if ( m_aMark.GetMark( j )->GetMarkedSdrObj() == pCurrentMarked )
+ bFound = true;
+ }
+
+ // did not find a current mark in the saved marks
+ if ( !bFound )
+ bMisMatch = true;
+ }
+
+ if ( bMisMatch )
+ {
+ m_aMark.Clear();
+ _rRestoredMarkList = rCurrentList;
+ return;
+ }
+ }
+ // it is important that the objects of the mark list are not accessed,
+ // because they can be already destroyed
+ SdrPageView* pCurPageView = m_pView->GetSdrPageView();
+ SdrObjListIter aPageIter( pPage );
+ bool bFound = true;
+
+ // do all objects still exist
+ const size_t nCount = m_aMark.GetMarkCount();
+ for (size_t i = 0; i < nCount && bFound; ++i)
+ {
+ SdrMark* pMark = m_aMark.GetMark(i);
+ SdrObject* pObj = pMark->GetMarkedSdrObj();
+ if (pObj->IsGroupObject())
+ {
+ SdrObjListIter aIter(pObj->GetSubList());
+ while (aIter.IsMore() && bFound)
+ bFound = lcl_hasObject(aPageIter, aIter.Next());
+ }
+ else
+ bFound = lcl_hasObject(aPageIter, pObj);
+
+ bFound = bFound && pCurPageView == pMark->GetPageView();
+ }
+
+ if (bFound)
+ {
+ // evaluate the LastObject
+ if (nCount) // now mark the objects
+ {
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ SdrMark* pMark = m_aMark.GetMark(i);
+ SdrObject* pObj = pMark->GetMarkedSdrObj();
+ if ( pObj->GetObjInventor() == SdrInventor::FmForm )
+ if ( !m_pView->IsObjMarked( pObj ) )
+ m_pView->MarkObj( pObj, pMark->GetPageView() );
+ }
+
+ _rRestoredMarkList = m_aMark;
+ }
+ }
+ m_aMark.Clear();
+}
+
+void SAL_CALL FmXFormView::focusGained( const FocusEvent& /*e*/ )
+{
+ if ( m_xWindow.is() && m_pView )
+ {
+ m_pView->SetMoveOutside( true, FmFormView::ImplAccess() );
+ }
+}
+
+void SAL_CALL FmXFormView::focusLost( const FocusEvent& /*e*/ )
+{
+ // when switch the focus outside the office the mark didn't change
+ // so we can not remove us as focus listener
+ if ( m_xWindow.is() && m_pView )
+ {
+ m_pView->SetMoveOutside( false, FmFormView::ImplAccess() );
+ }
+}
+
+DocumentType FmXFormView::impl_getDocumentType() const
+{
+ if ( GetFormShell() && GetFormShell()->GetImpl() )
+ return GetFormShell()->GetImpl()->getDocumentType_Lock();
+ return eUnknownDocumentType;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/formcontrolfactory.cxx b/svx/source/form/formcontrolfactory.cxx
new file mode 100644
index 0000000000..dc879a0848
--- /dev/null
+++ b/svx/source/form/formcontrolfactory.cxx
@@ -0,0 +1,694 @@
+/* -*- 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 <formcontrolfactory.hxx>
+#include <fmcontrollayout.hxx>
+#include <fmprop.hxx>
+#include <svx/strings.hrc>
+#include <fmservs.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/svdouno.hxx>
+
+#include <com/sun/star/form/XFormComponent.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/awt/ScrollBarOrientation.hpp>
+#include <com/sun/star/awt/MouseWheelBehavior.hpp>
+#include <com/sun/star/form/XGridColumnFactory.hpp>
+#include <com/sun/star/style/VerticalAlignment.hpp>
+#include <com/sun/star/awt/LineEndFormat.hpp>
+#include <com/sun/star/awt/ImageScaleMode.hpp>
+#include <com/sun/star/sdbc/DataType.hpp>
+#include <com/sun/star/sdbc/XDataSource.hpp>
+#include <com/sun/star/util/XNumberFormatTypes.hpp>
+#include <com/sun/star/sdbc/ColumnValue.hpp>
+#include <com/sun/star/text/WritingMode2.hpp>
+
+#include <comphelper/numbers.hxx>
+#include <comphelper/processfactory.hxx>
+#include <unotools/syslocale.hxx>
+#include <tools/gen.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <connectivity/dbtools.hxx>
+#include <i18nlangtag/languagetag.hxx>
+
+#include <set>
+
+using namespace ::dbtools;
+
+namespace svxform
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::uno::UNO_SET_THROW;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::beans::XPropertySet;
+ using ::com::sun::star::form::XFormComponent;
+ using ::com::sun::star::container::XIndexAccess;
+ using ::com::sun::star::beans::XPropertySetInfo;
+ using ::com::sun::star::beans::PropertyValue;
+ using ::com::sun::star::container::XChild;
+ using ::com::sun::star::form::XGridColumnFactory;
+ using ::com::sun::star::style::VerticalAlignment_MIDDLE;
+ using ::com::sun::star::beans::Property;
+ using ::com::sun::star::uno::TypeClass_DOUBLE;
+ using ::com::sun::star::uno::TypeClass_LONG;
+ using ::com::sun::star::util::XNumberFormats;
+ using ::com::sun::star::util::XNumberFormatTypes;
+ using ::com::sun::star::lang::XServiceInfo;
+ using ::com::sun::star::container::XNameAccess;
+
+ namespace FormComponentType = ::com::sun::star::form::FormComponentType;
+ namespace ScrollBarOrientation = ::com::sun::star::awt::ScrollBarOrientation;
+ namespace MouseWheelBehavior = ::com::sun::star::awt::MouseWheelBehavior;
+ namespace LineEndFormat = ::com::sun::star::awt::LineEndFormat;
+ namespace ImageScaleMode = ::com::sun::star::awt::ImageScaleMode;
+ namespace DataType = ::com::sun::star::sdbc::DataType;
+ namespace ColumnValue = ::com::sun::star::sdbc::ColumnValue;
+ namespace WritingMode2 = ::com::sun::star::text::WritingMode2;
+
+ FormControlFactory::FormControlFactory( const Reference<XComponentContext>& _rContext )
+ :m_xContext( _rContext )
+ {
+ }
+
+ FormControlFactory::FormControlFactory( )
+ :m_xContext( comphelper::getProcessComponentContext() )
+ {
+ }
+
+
+ FormControlFactory::~FormControlFactory()
+ {
+ }
+
+
+ sal_Int16 FormControlFactory::initializeControlModel( const DocumentType _eDocType, const SdrUnoObj& _rObject )
+ {
+ return initializeControlModel(
+ _eDocType,
+ Reference< XPropertySet >( _rObject.GetUnoControlModel(), UNO_QUERY ),
+ _rObject.GetCurrentBoundRect()
+ );
+ }
+
+
+ void FormControlFactory::initializeControlModel( const DocumentType _eDocType, const Reference< XPropertySet >& _rxControlModel )
+ {
+ initializeControlModel(
+ _eDocType, _rxControlModel, tools::Rectangle()
+ );
+ }
+
+
+ namespace
+ {
+
+ OUString lcl_getUniqueLabel_nothrow( const Reference< XPropertySet >& _rxControlModel, const OUString& _rBaseLabel )
+ {
+ OUString sLabel( _rBaseLabel );
+ try
+ {
+ typedef ::std::set< OUString > StringBag;
+ StringBag aUsedLabels;
+
+ Reference< XFormComponent > xFormComponent( _rxControlModel, UNO_QUERY_THROW );
+ Reference< XIndexAccess > xContainer( xFormComponent->getParent(), UNO_QUERY_THROW );
+ // loop through all siblings of the control model, and collect their labels
+ for ( sal_Int32 index=xContainer->getCount(); index>0; )
+ {
+ Reference< XPropertySet > xElement( xContainer->getByIndex( --index ), UNO_QUERY_THROW );
+ if ( xElement == _rxControlModel )
+ continue;
+
+ Reference< XPropertySetInfo > xPSI( xElement->getPropertySetInfo(), UNO_SET_THROW );
+ if ( !xPSI->hasPropertyByName( FM_PROP_LABEL ) )
+ continue;
+
+ OUString sElementLabel;
+ OSL_VERIFY( xElement->getPropertyValue( FM_PROP_LABEL ) >>= sElementLabel );
+ aUsedLabels.insert( sElementLabel );
+ }
+
+ // now find a free label
+ sal_Int32 i=2;
+ while ( aUsedLabels.find( sLabel ) != aUsedLabels.end() )
+ sLabel = _rBaseLabel + " " + OUString::number( i++ );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return sLabel;
+ }
+
+
+ Sequence< PropertyValue > lcl_getDataSourceIndirectProperties( const Reference< XPropertySet >& _rxControlModel,
+ const Reference<XComponentContext>& _rContext )
+ {
+ OSL_PRECOND( _rxControlModel.is(), "lcl_getDataSourceIndirectProperties: invalid model!" );
+
+ Sequence< PropertyValue > aInfo;
+ try
+ {
+ Reference< XChild > xChild( _rxControlModel, UNO_QUERY );
+ Reference< XPropertySet > xForm;
+ if ( xChild.is() )
+ xForm.set(xChild->getParent(), css::uno::UNO_QUERY);
+
+ if ( Reference< XGridColumnFactory >( xForm, UNO_QUERY ).is() )
+ { // hmm. the model is a grid column, in real
+ xChild.set(xForm, css::uno::UNO_QUERY);
+ xForm.set(xChild->getParent(), css::uno::UNO_QUERY);
+ }
+
+ OSL_ENSURE( xForm.is(), "lcl_getDataSourceIndirectProperties: could not determine the form!" );
+ if ( !xForm.is() )
+ return aInfo;
+ OUString sDataSourceName;
+ xForm->getPropertyValue( FM_PROP_DATASOURCE ) >>= sDataSourceName;
+
+ Reference< XPropertySet > xDsProperties;
+ if ( !sDataSourceName.isEmpty() )
+ xDsProperties.set(getDataSource( sDataSourceName, _rContext ), css::uno::UNO_QUERY);
+ if ( xDsProperties.is() )
+ xDsProperties->getPropertyValue("Info") >>= aInfo;
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "lcl_getDataSourceIndirectProperties" );
+ }
+ return aInfo;
+ }
+
+
+ const char* aCharacterAndParagraphProperties[] =
+ {
+ "CharFontName",
+ "CharFontStyleName",
+ "CharFontFamily",
+ "CharFontCharSet",
+ "CharFontPitch",
+ "CharColor",
+ "CharEscapement",
+ "CharHeight",
+ "CharUnderline",
+ "CharWeight",
+ "CharPosture",
+ "CharAutoKerning",
+ "CharBackColor",
+ "CharBackTransparent",
+ "CharCaseMap",
+ "CharCrossedOut",
+ "CharFlash",
+ "CharStrikeout",
+ "CharWordMode",
+ "CharKerning",
+ "CharLocale",
+ "CharKeepTogether",
+ "CharNoLineBreak",
+ "CharShadowed",
+ "CharFontType",
+ "CharStyleName",
+ "CharContoured",
+ "CharCombineIsOn",
+ "CharCombinePrefix",
+ "CharCombineSuffix",
+ "CharEmphasize",
+ "CharRelief",
+ "RubyText",
+ "RubyAdjust",
+ "RubyCharStyleName",
+ "RubyIsAbove",
+ "CharRotation",
+ "CharRotationIsFitToLine",
+ "CharScaleWidth",
+ "HyperLinkURL",
+ "HyperLinkTarget",
+ "HyperLinkName",
+ "VisitedCharStyleName",
+ "UnvisitedCharStyleName",
+ "CharEscapementHeight",
+ "CharNoHyphenation",
+ "CharUnderlineColor",
+ "CharUnderlineHasColor",
+ "CharStyleNames",
+ "CharHeightAsian",
+ "CharWeightAsian",
+ "CharFontNameAsian",
+ "CharFontStyleNameAsian",
+ "CharFontFamilyAsian",
+ "CharFontCharSetAsian",
+ "CharFontPitchAsian",
+ "CharPostureAsian",
+ "CharLocaleAsian",
+ "ParaIsCharacterDistance",
+ "ParaIsForbiddenRules",
+ "ParaIsHangingPunctuation",
+ "CharHeightComplex",
+ "CharWeightComplex",
+ "CharFontNameComplex",
+ "CharFontStyleNameComplex",
+ "CharFontFamilyComplex",
+ "CharFontCharSetComplex",
+ "CharFontPitchComplex",
+ "CharPostureComplex",
+ "CharLocaleComplex",
+ "ParaAdjust",
+ "ParaLineSpacing",
+ "ParaBackColor",
+ "ParaBackTransparent",
+ "ParaBackGraphic",
+ "ParaBackGraphicURL",
+ "ParaBackGraphicFilter",
+ "ParaBackGraphicLocation",
+ "ParaLastLineAdjust",
+ "ParaExpandSingleWord",
+ "ParaLeftMargin",
+ "ParaRightMargin",
+ "ParaTopMargin",
+ "ParaBottomMargin",
+ "ParaLineNumberCount",
+ "ParaLineNumberStartValue",
+ "PageDescName",
+ "PageNumberOffset",
+ "ParaRegisterModeActive",
+ "ParaTabStops",
+ "ParaStyleName",
+ "DropCapFormat",
+ "DropCapWholeWord",
+ "ParaKeepTogether",
+ "Setting",
+ "ParaSplit",
+ "Setting",
+ "NumberingLevel",
+ "NumberingRules",
+ "NumberingStartValue",
+ "ParaIsNumberingRestart",
+ "NumberingStyleName",
+ "ParaOrphans",
+ "ParaWidows",
+ "ParaShadowFormat",
+ "LeftBorder",
+ "RightBorder",
+ "TopBorder",
+ "BottomBorder",
+ "BorderDistance",
+ "LeftBorderDistance",
+ "RightBorderDistance",
+ "TopBorderDistance",
+ "BottomBorderDistance",
+ "BreakType",
+ "DropCapCharStyleName",
+ "ParaFirstLineIndent",
+ "ParaIsAutoFirstLineIndent",
+ "ParaIsHyphenation",
+ "ParaHyphenationMaxHyphens",
+ "ParaHyphenationMaxLeadingChars",
+ "ParaHyphenationMaxTrailingChars",
+ "ParaVertAlignment",
+ "ParaUserDefinedAttributes",
+ "NumberingIsNumber",
+ "ParaIsConnectBorder",
+ nullptr
+ };
+
+
+ void lcl_initializeCharacterAttributes( const Reference< XPropertySet >& _rxModel )
+ {
+ try
+ {
+ Reference< XPropertySet > xStyle( ControlLayouter::getDefaultDocumentTextStyle( _rxModel ), UNO_SET_THROW );
+
+ // transfer all properties which are described by the style
+ Reference< XPropertySetInfo > xSourcePropInfo( xStyle->getPropertySetInfo(), UNO_SET_THROW );
+ Reference< XPropertySetInfo > xDestPropInfo( _rxModel->getPropertySetInfo(), UNO_SET_THROW );
+
+ OUString sPropertyName;
+ const char** pCharacterProperty = aCharacterAndParagraphProperties;
+ while ( *pCharacterProperty )
+ {
+ sPropertyName = OUString::createFromAscii( *pCharacterProperty );
+
+ if ( xSourcePropInfo->hasPropertyByName( sPropertyName ) && xDestPropInfo->hasPropertyByName( sPropertyName ) )
+ _rxModel->setPropertyValue( sPropertyName, xStyle->getPropertyValue( sPropertyName ) );
+
+ ++pCharacterProperty;
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ }
+
+
+ sal_Int16 FormControlFactory::initializeControlModel( const DocumentType _eDocType, const Reference< XPropertySet >& _rxControlModel,
+ const tools::Rectangle& _rControlBoundRect )
+ {
+ sal_Int16 nClassId = FormComponentType::CONTROL;
+
+ OSL_ENSURE( _rxControlModel.is(), "FormControlFactory::initializeControlModel: invalid model!" );
+ if ( !_rxControlModel.is() )
+ return nClassId;
+
+ try
+ {
+ ControlLayouter::initializeControlLayout( _rxControlModel, _eDocType );
+
+ _rxControlModel->getPropertyValue( FM_PROP_CLASSID ) >>= nClassId;
+ Reference< XPropertySetInfo > xPSI( _rxControlModel->getPropertySetInfo(), UNO_SET_THROW );
+ switch ( nClassId )
+ {
+ case FormComponentType::SCROLLBAR:
+ _rxControlModel->setPropertyValue("LiveScroll", Any( true ) );
+ [[fallthrough]];
+ case FormComponentType::SPINBUTTON:
+ {
+ sal_Int32 eOrientation = ScrollBarOrientation::HORIZONTAL;
+ if ( !_rControlBoundRect.IsEmpty() && ( _rControlBoundRect.GetWidth() < _rControlBoundRect.GetHeight() ) )
+ eOrientation = ScrollBarOrientation::VERTICAL;
+ _rxControlModel->setPropertyValue( FM_PROP_ORIENTATION, Any( eOrientation ) );
+ }
+ break;
+
+ case FormComponentType::LISTBOX:
+ case FormComponentType::COMBOBOX:
+ {
+ bool bDropDown = !_rControlBoundRect.IsEmpty() && ( _rControlBoundRect.GetWidth() >= 3 * _rControlBoundRect.GetHeight() );
+ if ( xPSI->hasPropertyByName( FM_PROP_DROPDOWN ) )
+ _rxControlModel->setPropertyValue( FM_PROP_DROPDOWN, Any( bDropDown ) );
+ _rxControlModel->setPropertyValue( FM_PROP_LINECOUNT, Any( sal_Int16( 20 ) ) );
+ }
+ break;
+
+ case FormComponentType::TEXTFIELD:
+ {
+ initializeTextFieldLineEnds( _rxControlModel );
+ lcl_initializeCharacterAttributes( _rxControlModel );
+
+ if ( !_rControlBoundRect.IsEmpty()
+ && ( _rControlBoundRect.GetWidth() <= 4 * _rControlBoundRect.GetHeight() )
+ )
+ {
+ if ( xPSI->hasPropertyByName( FM_PROP_MULTILINE ) )
+ _rxControlModel->setPropertyValue( FM_PROP_MULTILINE, Any( true ) );
+ }
+ }
+ break;
+
+ case FormComponentType::RADIOBUTTON:
+ case FormComponentType::CHECKBOX:
+ case FormComponentType::FIXEDTEXT:
+ {
+ OUString sVertAlignPropertyName( "VerticalAlign" );
+ if ( xPSI->hasPropertyByName( sVertAlignPropertyName ) )
+ _rxControlModel->setPropertyValue( sVertAlignPropertyName, Any( VerticalAlignment_MIDDLE ) );
+ }
+ break;
+
+ case FormComponentType::IMAGEBUTTON:
+ case FormComponentType::IMAGECONTROL:
+ {
+ static constexpr OUString sScaleModeProperty( u"ScaleMode"_ustr );
+ if ( xPSI->hasPropertyByName( sScaleModeProperty ) )
+ _rxControlModel->setPropertyValue( sScaleModeProperty, Any( ImageScaleMode::ISOTROPIC ) );
+ }
+ break;
+ }
+
+ // initial default label for the control
+ if ( xPSI->hasPropertyByName( FM_PROP_LABEL ) )
+ {
+ OUString sExistingLabel;
+ OSL_VERIFY( _rxControlModel->getPropertyValue( FM_PROP_LABEL ) >>= sExistingLabel );
+ if ( sExistingLabel.isEmpty() )
+ {
+ OUString sInitialLabel;
+ OSL_VERIFY( _rxControlModel->getPropertyValue( FM_PROP_NAME ) >>= sInitialLabel );
+
+ TranslateId pTitleResId;
+ switch ( nClassId )
+ {
+ case FormComponentType::COMMANDBUTTON: pTitleResId = RID_STR_PROPTITLE_PUSHBUTTON; break;
+ case FormComponentType::RADIOBUTTON: pTitleResId = RID_STR_PROPTITLE_RADIOBUTTON; break;
+ case FormComponentType::CHECKBOX: pTitleResId = RID_STR_PROPTITLE_CHECKBOX; break;
+ case FormComponentType::GROUPBOX: pTitleResId = RID_STR_PROPTITLE_GROUPBOX; break;
+ case FormComponentType::FIXEDTEXT: pTitleResId = RID_STR_PROPTITLE_FIXEDTEXT; break;
+ }
+
+ if (pTitleResId)
+ sInitialLabel = SvxResId(pTitleResId);
+
+ _rxControlModel->setPropertyValue(
+ FM_PROP_LABEL,
+ Any( lcl_getUniqueLabel_nothrow( _rxControlModel, sInitialLabel ) )
+ );
+ }
+ }
+
+ // strict format = yes is the default (i93467)
+ if ( xPSI->hasPropertyByName( FM_PROP_STRICTFORMAT ) )
+ {
+ _rxControlModel->setPropertyValue( FM_PROP_STRICTFORMAT, Any( true ) );
+ }
+
+ // mouse wheel: don't use it for scrolling by default (i110036)
+ if ( xPSI->hasPropertyByName( FM_PROP_MOUSE_WHEEL_BEHAVIOR ) )
+ {
+ _rxControlModel->setPropertyValue( FM_PROP_MOUSE_WHEEL_BEHAVIOR, Any( MouseWheelBehavior::SCROLL_DISABLED ) );
+ }
+
+ if ( xPSI->hasPropertyByName( FM_PROP_WRITING_MODE ) )
+ _rxControlModel->setPropertyValue( FM_PROP_WRITING_MODE, Any( WritingMode2::CONTEXT ) );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return nClassId;
+ }
+
+
+ void FormControlFactory::initializeTextFieldLineEnds( const Reference< XPropertySet >& _rxModel )
+ {
+ OSL_PRECOND( _rxModel.is(), "initializeTextFieldLineEnds: invalid model!" );
+ if ( !_rxModel.is() )
+ return;
+
+ try
+ {
+ Reference< XPropertySetInfo > xInfo = _rxModel->getPropertySetInfo();
+ if ( !xInfo.is() || !xInfo->hasPropertyByName( FM_PROP_LINEENDFORMAT ) )
+ return;
+
+ // let's see if the data source which the form belongs to (if any)
+ // has a setting for the preferred line end format
+ bool bDosLineEnds = false;
+ const Sequence< PropertyValue > aInfo = lcl_getDataSourceIndirectProperties( _rxModel, m_xContext );
+ const PropertyValue* pInfo = std::find_if(aInfo.begin(), aInfo.end(),
+ [](const PropertyValue& rInfo) { return rInfo.Name == "PreferDosLikeLineEnds"; });
+ if (pInfo != aInfo.end())
+ pInfo->Value >>= bDosLineEnds;
+
+ sal_Int16 nLineEndFormat = bDosLineEnds ? LineEndFormat::CARRIAGE_RETURN_LINE_FEED : LineEndFormat::LINE_FEED;
+ _rxModel->setPropertyValue( FM_PROP_LINEENDFORMAT, Any( nLineEndFormat ) );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+
+ void FormControlFactory::initializeFieldDependentProperties( const Reference< XPropertySet >& _rxDatabaseField,
+ const Reference< XPropertySet >& _rxControlModel, const Reference< XNumberFormats >& _rxNumberFormats )
+ {
+ OSL_PRECOND( _rxDatabaseField.is() && _rxControlModel.is(),
+ "FormControlFactory::initializeFieldDependentProperties: illegal params!" );
+ if ( !_rxDatabaseField.is() || !_rxControlModel.is() )
+ return;
+
+ try
+ {
+
+ // if the field has a numeric format, and the model has a "Scale" property, sync it
+ Reference< XPropertySetInfo > xFieldPSI( _rxDatabaseField->getPropertySetInfo(), UNO_SET_THROW );
+ Reference< XPropertySetInfo > xModelPSI( _rxControlModel->getPropertySetInfo(), UNO_SET_THROW );
+
+ if ( xModelPSI->hasPropertyByName( FM_PROP_DECIMAL_ACCURACY ) )
+ {
+ sal_Int32 nFormatKey = 0;
+ if ( xFieldPSI->hasPropertyByName( FM_PROP_FORMATKEY ) )
+ {
+ _rxDatabaseField->getPropertyValue( FM_PROP_FORMATKEY ) >>= nFormatKey;
+ }
+ else
+ {
+ nFormatKey = getDefaultNumberFormat(
+ _rxDatabaseField,
+ Reference< XNumberFormatTypes >( _rxNumberFormats, UNO_QUERY ),
+ SvtSysLocale().GetLanguageTag().getLocale()
+ );
+ }
+
+ Any aScaleVal( ::comphelper::getNumberFormatDecimals( _rxNumberFormats, nFormatKey ) );
+ _rxControlModel->setPropertyValue( FM_PROP_DECIMAL_ACCURACY, aScaleVal );
+ }
+
+
+ // minimum and maximum of the control according to the type of the database field
+ sal_Int32 nDataType = DataType::OTHER;
+ OSL_VERIFY( _rxDatabaseField->getPropertyValue( FM_PROP_FIELDTYPE ) >>= nDataType );
+
+ if ( xModelPSI->hasPropertyByName( FM_PROP_VALUEMIN )
+ && xModelPSI->hasPropertyByName( FM_PROP_VALUEMAX )
+ )
+ {
+ sal_Int32 nMinValue = -1000000000, nMaxValue = 1000000000;
+ switch ( nDataType )
+ {
+ case DataType::TINYINT : nMinValue = 0; nMaxValue = 255; break;
+ case DataType::SMALLINT : nMinValue = -32768; nMaxValue = 32767; break;
+ case DataType::INTEGER : nMinValue = 0x80000000; nMaxValue = 0x7FFFFFFF; break;
+ // double and singles are ignored
+ }
+
+ Any aValue;
+
+ // both the minimum and the maximum value properties can be either Long or Double
+ Property aProperty = xModelPSI->getPropertyByName( FM_PROP_VALUEMIN );
+ if ( aProperty.Type.getTypeClass() == TypeClass_DOUBLE )
+ aValue <<= static_cast<double>(nMinValue);
+ else if ( aProperty.Type.getTypeClass() == TypeClass_LONG )
+ aValue <<= nMinValue;
+ else
+ {
+ OSL_FAIL( "FormControlFactory::initializeFieldDependentProperties: unexpected property type (MinValue)!" );
+ }
+ _rxControlModel->setPropertyValue( FM_PROP_VALUEMIN, aValue );
+
+ // both the minimum and the maximum value properties can be either Long or Double
+ aProperty = xModelPSI->getPropertyByName( FM_PROP_VALUEMAX );
+ if ( aProperty.Type.getTypeClass() == TypeClass_DOUBLE )
+ aValue <<= static_cast<double>(nMaxValue);
+ else if ( aProperty.Type.getTypeClass() == TypeClass_LONG )
+ aValue <<= nMaxValue;
+ else
+ {
+ OSL_FAIL( "FormControlFactory::initializeFieldDependentProperties: unexpected property type (MaxValue)!" );
+ }
+ _rxControlModel->setPropertyValue( FM_PROP_VALUEMAX, aValue );
+ }
+
+
+ // a check box can be tristate if and only if the column it is bound to is nullable
+ sal_Int16 nClassId = FormComponentType::CONTROL;
+ OSL_VERIFY( _rxControlModel->getPropertyValue( FM_PROP_CLASSID ) >>= nClassId );
+ if ( nClassId == FormComponentType::CHECKBOX )
+ {
+ sal_Int32 nNullable = ColumnValue::NULLABLE_UNKNOWN;
+ OSL_VERIFY( _rxDatabaseField->getPropertyValue( FM_PROP_ISNULLABLE ) >>= nNullable );
+ _rxControlModel->setPropertyValue( FM_PROP_TRISTATE, Any( ColumnValue::NO_NULLS != nNullable ) );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+
+ OUString FormControlFactory::getDefaultName( sal_Int16 _nClassId, const Reference< XServiceInfo >& _rxObject )
+ {
+ TranslateId pResId;
+
+ switch ( _nClassId )
+ {
+ case FormComponentType::COMMANDBUTTON: pResId = RID_STR_PROPTITLE_PUSHBUTTON; break;
+ case FormComponentType::RADIOBUTTON: pResId = RID_STR_PROPTITLE_RADIOBUTTON; break;
+ case FormComponentType::CHECKBOX: pResId = RID_STR_PROPTITLE_CHECKBOX; break;
+ case FormComponentType::LISTBOX: pResId = RID_STR_PROPTITLE_LISTBOX; break;
+ case FormComponentType::COMBOBOX: pResId = RID_STR_PROPTITLE_COMBOBOX; break;
+ case FormComponentType::GROUPBOX: pResId = RID_STR_PROPTITLE_GROUPBOX; break;
+ case FormComponentType::IMAGEBUTTON: pResId = RID_STR_PROPTITLE_IMAGEBUTTON; break;
+ case FormComponentType::FIXEDTEXT: pResId = RID_STR_PROPTITLE_FIXEDTEXT; break;
+ case FormComponentType::GRIDCONTROL: pResId = RID_STR_PROPTITLE_DBGRID; break;
+ case FormComponentType::FILECONTROL: pResId = RID_STR_PROPTITLE_FILECONTROL; break;
+ case FormComponentType::DATEFIELD: pResId = RID_STR_PROPTITLE_DATEFIELD; break;
+ case FormComponentType::TIMEFIELD: pResId = RID_STR_PROPTITLE_TIMEFIELD; break;
+ case FormComponentType::NUMERICFIELD: pResId = RID_STR_PROPTITLE_NUMERICFIELD; break;
+ case FormComponentType::CURRENCYFIELD: pResId = RID_STR_PROPTITLE_CURRENCYFIELD; break;
+ case FormComponentType::PATTERNFIELD: pResId = RID_STR_PROPTITLE_PATTERNFIELD; break;
+ case FormComponentType::IMAGECONTROL: pResId = RID_STR_PROPTITLE_IMAGECONTROL; break;
+ case FormComponentType::HIDDENCONTROL: pResId = RID_STR_PROPTITLE_HIDDEN; break;
+ case FormComponentType::SCROLLBAR: pResId = RID_STR_PROPTITLE_SCROLLBAR; break;
+ case FormComponentType::SPINBUTTON: pResId = RID_STR_PROPTITLE_SPINBUTTON; break;
+ case FormComponentType::NAVIGATIONBAR: pResId = RID_STR_PROPTITLE_NAVBAR; break;
+
+ case FormComponentType::TEXTFIELD:
+ pResId = RID_STR_PROPTITLE_EDIT;
+ if ( _rxObject.is() && _rxObject->supportsService( FM_SUN_COMPONENT_FORMATTEDFIELD ) )
+ pResId = RID_STR_PROPTITLE_FORMATTED;
+ break;
+
+ default:
+ pResId = RID_STR_CONTROL; break;
+ }
+
+ return SvxResId(pResId);
+ }
+
+
+ OUString FormControlFactory::getDefaultUniqueName_ByComponentType( const Reference< XNameAccess >& _rxContainer,
+ const Reference< XPropertySet >& _rxObject )
+ {
+ sal_Int16 nClassId = FormComponentType::CONTROL;
+ OSL_VERIFY( _rxObject->getPropertyValue( FM_PROP_CLASSID ) >>= nClassId );
+ OUString sBaseName = getDefaultName( nClassId, Reference< XServiceInfo >( _rxObject, UNO_QUERY ) );
+
+ return getUniqueName( _rxContainer, sBaseName );
+ }
+
+
+ OUString FormControlFactory::getUniqueName( const Reference< XNameAccess >& _rxContainer, std::u16string_view _rBaseName )
+ {
+ sal_Int32 n = 0;
+ OUString sName;
+ do
+ {
+ sName = OUString::Concat(_rBaseName) + " " + OUString::number( ++n );
+ }
+ while ( _rxContainer->hasByName( sName ) );
+
+ return sName;
+ }
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/formcontroller.cxx b/svx/source/form/formcontroller.cxx
new file mode 100644
index 0000000000..4198c5c367
--- /dev/null
+++ b/svx/source/form/formcontroller.cxx
@@ -0,0 +1,4161 @@
+/* -*- 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 <fmcontrolbordermanager.hxx>
+#include <fmcontrollayout.hxx>
+#include <formcontroller.hxx>
+#include <formfeaturedispatcher.hxx>
+#include <fmdocumentclassification.hxx>
+#include <formcontrolling.hxx>
+#include <fmprop.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <fmservs.hxx>
+#include <svx/fmtools.hxx>
+#include <fmurl.hxx>
+
+#include <com/sun/star/awt/FocusChangeReason.hpp>
+#include <com/sun/star/awt/XCheckBox.hpp>
+#include <com/sun/star/awt/XComboBox.hpp>
+#include <com/sun/star/awt/XListBox.hpp>
+#include <com/sun/star/awt/XVclWindowPeer.hpp>
+#include <com/sun/star/awt/TabController.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/container/XIdentifierReplace.hpp>
+#include <com/sun/star/form/TabulatorCycle.hpp>
+#include <com/sun/star/form/validation/XValidatableFormComponent.hpp>
+#include <com/sun/star/form/XBoundComponent.hpp>
+#include <com/sun/star/form/XBoundControl.hpp>
+#include <com/sun/star/form/XGridControl.hpp>
+#include <com/sun/star/form/XLoadable.hpp>
+#include <com/sun/star/form/XReset.hpp>
+#include <com/sun/star/form/control/FilterControl.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/NoSupportException.hpp>
+#include <com/sun/star/sdb/ParametersRequest.hpp>
+#include <com/sun/star/sdb/RowChangeAction.hpp>
+#include <com/sun/star/sdb/SQLFilterOperator.hpp>
+#include <com/sun/star/sdb/XInteractionSupplyParameters.hpp>
+#include <com/sun/star/sdbc/ColumnValue.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/form/runtime/FormOperations.hpp>
+#include <com/sun/star/form/runtime/FormFeature.hpp>
+#include <com/sun/star/container/XContainer.hpp>
+#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
+#include <com/sun/star/util/NumberFormatter.hpp>
+#include <com/sun/star/sdb/SQLContext.hpp>
+#include <com/sun/star/sdb/XColumn.hpp>
+
+#include <comphelper/enumhelper.hxx>
+#include <comphelper/interaction.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/flagguard.hxx>
+#include <comphelper/types.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <connectivity/IParseContext.hxx>
+#include <connectivity/dbtools.hxx>
+#include <connectivity/sqlparse.hxx>
+#include <toolkit/controls/unocontrol.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/mutex.hxx>
+#include <sal/log.hxx>
+
+#include <algorithm>
+#include <iterator>
+
+using namespace ::com::sun::star;
+using namespace ::comphelper;
+using namespace ::connectivity;
+using namespace ::dbtools;
+
+
+css::uno::Reference< css::uno::XInterface >
+ FormController_NewInstance_Impl( const css::uno::Reference< css::lang::XMultiServiceFactory > & _rxORB )
+{
+ return *( new ::svxform::FormController( comphelper::getComponentContext(_rxORB) ) );
+}
+
+namespace svxform
+{
+
+ using ::com::sun::star::sdb::XColumn;
+ using ::com::sun::star::awt::XControl;
+ using ::com::sun::star::awt::TabController;
+ using ::com::sun::star::awt::XToolkit;
+ using ::com::sun::star::awt::XWindowPeer;
+ using ::com::sun::star::form::XGrid;
+ using ::com::sun::star::beans::XPropertySet;
+ using ::com::sun::star::uno::UNO_SET_THROW;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::container::XIndexAccess;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::beans::XPropertySetInfo;
+ using ::com::sun::star::beans::PropertyValue;
+ using ::com::sun::star::lang::IndexOutOfBoundsException;
+ using ::com::sun::star::sdb::XInteractionSupplyParameters;
+ using ::com::sun::star::awt::XTextComponent;
+ using ::com::sun::star::awt::XTextListener;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::frame::XDispatch;
+ using ::com::sun::star::lang::XMultiServiceFactory;
+ using ::com::sun::star::uno::Type;
+ using ::com::sun::star::lang::IllegalArgumentException;
+ using ::com::sun::star::sdbc::XConnection;
+ using ::com::sun::star::sdbc::XRowSet;
+ using ::com::sun::star::sdbc::XDatabaseMetaData;
+ using ::com::sun::star::util::XNumberFormatsSupplier;
+ using ::com::sun::star::util::NumberFormatter;
+ using ::com::sun::star::util::XNumberFormatter;
+ using ::com::sun::star::sdbcx::XColumnsSupplier;
+ using ::com::sun::star::container::XNameAccess;
+ using ::com::sun::star::lang::EventObject;
+ using ::com::sun::star::beans::Property;
+ using ::com::sun::star::container::XEnumeration;
+ using ::com::sun::star::form::XFormComponent;
+ using ::com::sun::star::form::runtime::XFormOperations;
+ using ::com::sun::star::form::runtime::FilterEvent;
+ using ::com::sun::star::form::runtime::XFilterControllerListener;
+ using ::com::sun::star::awt::XControlContainer;
+ using ::com::sun::star::container::XIdentifierReplace;
+ using ::com::sun::star::form::XFormControllerListener;
+ using ::com::sun::star::awt::XWindow;
+ using ::com::sun::star::sdbc::XResultSet;
+ using ::com::sun::star::awt::XControlModel;
+ using ::com::sun::star::awt::XTabControllerModel;
+ using ::com::sun::star::beans::PropertyChangeEvent;
+ using ::com::sun::star::form::validation::XValidatableFormComponent;
+ using ::com::sun::star::form::XLoadable;
+ using ::com::sun::star::form::XBoundControl;
+ using ::com::sun::star::beans::XPropertyChangeListener;
+ using ::com::sun::star::awt::TextEvent;
+ using ::com::sun::star::form::XBoundComponent;
+ using ::com::sun::star::awt::XCheckBox;
+ using ::com::sun::star::awt::XComboBox;
+ using ::com::sun::star::awt::XListBox;
+ using ::com::sun::star::awt::ItemEvent;
+ using ::com::sun::star::util::XModifyListener;
+ using ::com::sun::star::form::XReset;
+ using ::com::sun::star::frame::XDispatchProviderInterception;
+ using ::com::sun::star::form::XGridControl;
+ using ::com::sun::star::awt::XVclWindowPeer;
+ using ::com::sun::star::form::validation::XValidator;
+ using ::com::sun::star::awt::FocusEvent;
+ using ::com::sun::star::sdb::SQLContext;
+ using ::com::sun::star::container::XChild;
+ using ::com::sun::star::form::TabulatorCycle_RECORDS;
+ using ::com::sun::star::container::ContainerEvent;
+ using ::com::sun::star::lang::DisposedException;
+ using ::com::sun::star::lang::Locale;
+ using ::com::sun::star::lang::NoSupportException;
+ using ::com::sun::star::sdb::RowChangeEvent;
+ using ::com::sun::star::frame::XStatusListener;
+ using ::com::sun::star::frame::XDispatchProviderInterceptor;
+ using ::com::sun::star::sdb::SQLErrorEvent;
+ using ::com::sun::star::form::DatabaseParameterEvent;
+ using ::com::sun::star::sdb::ParametersRequest;
+ using ::com::sun::star::task::XInteractionRequest;
+ using ::com::sun::star::util::URL;
+ using ::com::sun::star::frame::FeatureStateEvent;
+ using ::com::sun::star::form::runtime::XFormControllerContext;
+ using ::com::sun::star::task::InteractionHandler;
+ using ::com::sun::star::task::XInteractionHandler;
+ using ::com::sun::star::form::runtime::FormOperations;
+ using ::com::sun::star::container::XContainer;
+ using ::com::sun::star::sdbc::SQLWarning;
+
+ namespace ColumnValue = ::com::sun::star::sdbc::ColumnValue;
+ namespace PropertyAttribute = ::com::sun::star::beans::PropertyAttribute;
+ namespace FocusChangeReason = ::com::sun::star::awt::FocusChangeReason;
+ namespace RowChangeAction = ::com::sun::star::sdb::RowChangeAction;
+ namespace FormFeature = ::com::sun::star::form::runtime::FormFeature;
+
+namespace {
+
+struct ColumnInfo
+{
+ // information about the column itself
+ Reference< XColumn > xColumn;
+ sal_Int32 nNullable;
+ bool bAutoIncrement;
+ bool bReadOnly;
+ OUString sName;
+
+ // information about the control(s) bound to this column
+
+ /// the first control which is bound to the given column, and which requires input
+ Reference< XControl > xFirstControlWithInputRequired;
+ /** the first grid control which contains a column which is bound to the given database column, and requires
+ input
+ */
+ Reference< XGrid > xFirstGridWithInputRequiredColumn;
+ /** if xFirstControlWithInputRequired is a grid control, then nRequiredGridColumn specifies the position
+ of the grid column which is actually bound
+ */
+ sal_Int32 nRequiredGridColumn;
+
+ ColumnInfo()
+ :nNullable( ColumnValue::NULLABLE_UNKNOWN )
+ ,bAutoIncrement( false )
+ ,bReadOnly( false )
+ ,nRequiredGridColumn( -1 )
+ {
+ }
+};
+
+}
+
+class ColumnInfoCache
+{
+public:
+ explicit ColumnInfoCache( const Reference< XColumnsSupplier >& _rxColSupplier );
+
+ size_t getColumnCount() const { return m_aColumns.size(); }
+ const ColumnInfo& getColumnInfo( size_t _pos );
+
+ bool controlsInitialized() const { return m_bControlsInitialized; }
+ void initializeControls( const Sequence< Reference< XControl > >& _rControls );
+ void deinitializeControls();
+
+private:
+ typedef ::std::vector< ColumnInfo > ColumnInfos;
+ ColumnInfos m_aColumns;
+ bool m_bControlsInitialized;
+};
+
+
+ColumnInfoCache::ColumnInfoCache( const Reference< XColumnsSupplier >& _rxColSupplier )
+ :m_bControlsInitialized( false )
+{
+ try
+ {
+ m_aColumns.clear();
+
+ Reference< XIndexAccess > xColumns( _rxColSupplier->getColumns(), UNO_QUERY_THROW );
+ sal_Int32 nColumnCount = xColumns->getCount();
+ m_aColumns.reserve( nColumnCount );
+
+ Reference< XPropertySet > xColumnProps;
+ for ( sal_Int32 i = 0; i < nColumnCount; ++i )
+ {
+ ColumnInfo aColInfo;
+ aColInfo.xColumn.set( xColumns->getByIndex(i), UNO_QUERY_THROW );
+
+ xColumnProps.set( aColInfo.xColumn, UNO_QUERY_THROW );
+ OSL_VERIFY( xColumnProps->getPropertyValue( FM_PROP_ISNULLABLE ) >>= aColInfo.nNullable );
+ OSL_VERIFY( xColumnProps->getPropertyValue( FM_PROP_AUTOINCREMENT ) >>= aColInfo.bAutoIncrement );
+ OSL_VERIFY( xColumnProps->getPropertyValue( FM_PROP_NAME ) >>= aColInfo.sName );
+ OSL_VERIFY( xColumnProps->getPropertyValue( FM_PROP_ISREADONLY ) >>= aColInfo.bReadOnly );
+
+ m_aColumns.push_back( aColInfo );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+
+namespace
+{
+ bool lcl_isBoundTo( const Reference< XPropertySet >& _rxControlModel, const Reference< XInterface >& _rxNormDBField )
+ {
+ Reference< XInterface > xNormBoundField( _rxControlModel->getPropertyValue( FM_PROP_BOUNDFIELD ), UNO_QUERY );
+ return ( xNormBoundField == _rxNormDBField );
+ }
+
+ bool lcl_isInputRequired( const Reference< XPropertySet >& _rxControlModel )
+ {
+ bool bInputRequired = false;
+ OSL_VERIFY( _rxControlModel->getPropertyValue( FM_PROP_INPUT_REQUIRED ) >>= bInputRequired );
+ return bInputRequired;
+ }
+
+ void lcl_resetColumnControlInfo( ColumnInfo& _rColInfo )
+ {
+ _rColInfo.xFirstControlWithInputRequired.clear();
+ _rColInfo.xFirstGridWithInputRequiredColumn.clear();
+ _rColInfo.nRequiredGridColumn = -1;
+ }
+}
+
+
+void ColumnInfoCache::deinitializeControls()
+{
+ for (auto& rCol : m_aColumns)
+ {
+ lcl_resetColumnControlInfo( rCol );
+ }
+ m_bControlsInitialized = false;
+}
+
+
+void ColumnInfoCache::initializeControls( const Sequence< Reference< XControl > >& _rControls )
+{
+ try
+ {
+ // for every of our known columns, find the controls which are bound to this column
+ for (auto& rCol : m_aColumns)
+ {
+ OSL_ENSURE( !rCol.xFirstControlWithInputRequired.is() && !rCol.xFirstGridWithInputRequiredColumn.is()
+ && ( rCol.nRequiredGridColumn == -1 ), "ColumnInfoCache::initializeControls: called me twice?" );
+
+ lcl_resetColumnControlInfo( rCol );
+
+ Reference< XInterface > xNormColumn( rCol.xColumn, UNO_QUERY_THROW );
+
+ const Reference< XControl >* pControl( _rControls.getConstArray() );
+ const Reference< XControl >* pControlEnd( pControl + _rControls.getLength() );
+ for ( ; pControl != pControlEnd; ++pControl )
+ {
+ if ( !pControl->is() )
+ continue;
+
+ Reference< XPropertySet > xModel( (*pControl)->getModel(), UNO_QUERY_THROW );
+ Reference< XPropertySetInfo > xModelPSI( xModel->getPropertySetInfo(), UNO_SET_THROW );
+
+ // special handling for grid controls
+ Reference< XGrid > xGrid( *pControl, UNO_QUERY );
+ if ( xGrid.is() )
+ {
+ Reference< XIndexAccess > xGridColAccess( xModel, UNO_QUERY_THROW );
+ sal_Int32 gridColCount = xGridColAccess->getCount();
+ sal_Int32 gridCol = 0;
+ for ( gridCol = 0; gridCol < gridColCount; ++gridCol )
+ {
+ Reference< XPropertySet > xGridColumnModel( xGridColAccess->getByIndex( gridCol ), UNO_QUERY_THROW );
+
+ if ( !lcl_isBoundTo( xGridColumnModel, xNormColumn )
+ || !lcl_isInputRequired( xGridColumnModel )
+ )
+ continue; // with next grid column
+
+ break;
+ }
+
+ if ( gridCol < gridColCount )
+ {
+ // found a grid column which is bound to the given
+ rCol.xFirstGridWithInputRequiredColumn = xGrid;
+ rCol.nRequiredGridColumn = gridCol;
+ break;
+ }
+
+ continue; // with next control
+ }
+
+ if ( !xModelPSI->hasPropertyByName( FM_PROP_BOUNDFIELD )
+ || !lcl_isBoundTo( xModel, xNormColumn )
+ || !lcl_isInputRequired( xModel )
+ )
+ continue; // with next control
+
+ break;
+ }
+
+ if ( pControl == pControlEnd )
+ // did not find a control which is bound to this particular column, and for which the input is required
+ continue; // with next DB column
+
+ rCol.xFirstControlWithInputRequired = *pControl;
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ m_bControlsInitialized = true;
+}
+
+
+const ColumnInfo& ColumnInfoCache::getColumnInfo( size_t _pos )
+{
+ if ( _pos >= m_aColumns.size() )
+ throw IndexOutOfBoundsException();
+
+ return m_aColumns[ _pos ];
+}
+
+namespace {
+
+class OParameterContinuation : public OInteraction< XInteractionSupplyParameters >
+{
+ Sequence< PropertyValue > m_aValues;
+
+public:
+ OParameterContinuation() { }
+
+ const Sequence< PropertyValue >& getValues() const { return m_aValues; }
+
+// XInteractionSupplyParameters
+ virtual void SAL_CALL setParameters( const Sequence< PropertyValue >& _rValues ) override;
+};
+
+}
+
+void SAL_CALL OParameterContinuation::setParameters( const Sequence< PropertyValue >& _rValues )
+{
+ m_aValues = _rValues;
+}
+
+
+// FmXAutoControl
+
+struct FmFieldInfo
+{
+ OUString aFieldName;
+ Reference< XPropertySet > xField;
+ Reference< XTextComponent > xText;
+
+ FmFieldInfo(const Reference< XPropertySet >& _xField, const Reference< XTextComponent >& _xText)
+ :xField(_xField)
+ ,xText(_xText)
+ {xField->getPropertyValue(FM_PROP_NAME) >>= aFieldName;}
+};
+
+namespace {
+
+class FmXAutoControl: public UnoControl
+
+{
+public:
+ FmXAutoControl()
+ {
+ }
+
+ virtual OUString GetComponentServiceName() const override {return "Edit";}
+ virtual void SAL_CALL createPeer( const Reference< XToolkit > & rxToolkit, const Reference< XWindowPeer > & rParentPeer ) override;
+
+protected:
+ virtual void ImplSetPeerProperty( const OUString& rPropName, const Any& rVal ) override;
+};
+
+}
+
+void FmXAutoControl::createPeer( const Reference< XToolkit > & rxToolkit, const Reference< XWindowPeer > & rParentPeer )
+{
+ UnoControl::createPeer( rxToolkit, rParentPeer );
+
+ Reference< XTextComponent > xText(getPeer() , UNO_QUERY);
+ if (xText.is())
+ {
+ xText->setText(SvxResId(RID_STR_AUTOFIELD));
+ xText->setEditable(false);
+ }
+}
+
+
+void FmXAutoControl::ImplSetPeerProperty( const OUString& rPropName, const Any& rVal )
+{
+ // these properties are ignored
+ if (rPropName == FM_PROP_TEXT)
+ return;
+
+ UnoControl::ImplSetPeerProperty( rPropName, rVal );
+}
+
+
+IMPL_LINK_NOARG( FormController, OnActivateTabOrder, Timer*, void )
+{
+ activateTabOrder();
+}
+
+namespace {
+
+struct UpdateAllListeners
+{
+ bool operator()( const Reference< XDispatch >& _rxDispatcher ) const
+ {
+ static_cast< svx::OSingleFeatureDispatcher* >( _rxDispatcher.get() )->updateAllListeners();
+ // the return is a dummy only so we can use this struct in a lambda expression
+ return true;
+ }
+};
+
+}
+
+IMPL_LINK_NOARG( FormController, OnInvalidateFeatures, Timer*, void )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ for (const auto& rFeature : m_aInvalidFeatures)
+ {
+ DispatcherContainer::const_iterator aDispatcherPos = m_aFeatureDispatchers.find( rFeature );
+ if ( aDispatcherPos != m_aFeatureDispatchers.end() )
+ {
+ // TODO: for the real and actual listener notifications, we should release
+ // our mutex
+ UpdateAllListeners( )( aDispatcherPos->second );
+ }
+ }
+}
+
+FormController::FormController(const Reference< css::uno::XComponentContext > & _rxORB )
+ :FormController_BASE( m_aMutex )
+ ,OPropertySetHelper( FormController_BASE::rBHelper )
+ ,OSQLParserClient( _rxORB )
+ ,m_xComponentContext( _rxORB )
+ ,m_aActivateListeners(m_aMutex)
+ ,m_aModifyListeners(m_aMutex)
+ ,m_aErrorListeners(m_aMutex)
+ ,m_aDeleteListeners(m_aMutex)
+ ,m_aRowSetApproveListeners(m_aMutex)
+ ,m_aParameterListeners(m_aMutex)
+ ,m_aFilterListeners(m_aMutex)
+ ,m_aTabActivationIdle("svx FormController m_aTabActivationIdle")
+ ,m_aFeatureInvalidationTimer("svx FormController m_aFeatureInvalidationTimer")
+ ,m_aMode( OUString( "DataMode" ) )
+ ,m_aLoadEvent( LINK( this, FormController, OnLoad ) )
+ ,m_aToggleEvent( LINK( this, FormController, OnToggleAutoFields ) )
+ ,m_aActivationEvent( LINK( this, FormController, OnActivated ) )
+ ,m_aDeactivationEvent( LINK( this, FormController, OnDeactivated ) )
+ ,m_nCurrentFilterPosition(-1)
+ ,m_bCurrentRecordModified(false)
+ ,m_bCurrentRecordNew(false)
+ ,m_bLocked(false)
+ ,m_bDBConnection(false)
+ ,m_bCycle(false)
+ ,m_bCanInsert(false)
+ ,m_bCanUpdate(false)
+ ,m_bCommitLock(false)
+ ,m_bModified(false)
+ ,m_bControlsSorted(false)
+ ,m_bFiltering(false)
+ ,m_bAttachEvents(true)
+ ,m_bDetachEvents(true)
+ ,m_bAttemptedHandlerCreation( false )
+ ,m_bSuspendFilterTextListening( false )
+{
+
+ osl_atomic_increment(&m_refCount);
+ {
+ m_xTabController = TabController::create( m_xComponentContext );
+ m_xAggregate.set( m_xTabController, UNO_QUERY_THROW );
+ m_xAggregate->setDelegator( *this );
+ }
+ osl_atomic_decrement(&m_refCount);
+
+ m_aTabActivationIdle.SetPriority( TaskPriority::LOWEST );
+ m_aTabActivationIdle.SetInvokeHandler( LINK( this, FormController, OnActivateTabOrder ) );
+
+ m_aFeatureInvalidationTimer.SetTimeout( 200 );
+ m_aFeatureInvalidationTimer.SetInvokeHandler( LINK( this, FormController, OnInvalidateFeatures ) );
+}
+
+
+FormController::~FormController()
+{
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ m_aLoadEvent.CancelPendingCall();
+ m_aToggleEvent.CancelPendingCall();
+ m_aActivationEvent.CancelPendingCall();
+ m_aDeactivationEvent.CancelPendingCall();
+
+ if ( m_aTabActivationIdle.IsActive() )
+ m_aTabActivationIdle.Stop();
+ }
+
+ if ( m_aFeatureInvalidationTimer.IsActive() )
+ m_aFeatureInvalidationTimer.Stop();
+
+ disposeAllFeaturesAndDispatchers();
+
+ if ( m_xFormOperations.is() )
+ m_xFormOperations->dispose();
+ m_xFormOperations.clear();
+
+ // release of aggregation
+ if ( m_xAggregate.is() )
+ {
+ m_xAggregate->setDelegator( nullptr );
+ m_xAggregate.clear();
+ }
+}
+
+
+void SAL_CALL FormController::acquire() noexcept
+{
+ FormController_BASE::acquire();
+}
+
+
+void SAL_CALL FormController::release() noexcept
+{
+ FormController_BASE::release();
+}
+
+
+Any SAL_CALL FormController::queryInterface( const Type& _rType )
+{
+ Any aRet = FormController_BASE::queryInterface( _rType );
+ if ( !aRet.hasValue() )
+ aRet = OPropertySetHelper::queryInterface( _rType );
+ if ( !aRet.hasValue() )
+ aRet = m_xAggregate->queryAggregation( _rType );
+ return aRet;
+}
+
+
+Sequence< sal_Int8 > SAL_CALL FormController::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+Sequence< Type > SAL_CALL FormController::getTypes( )
+{
+ return comphelper::concatSequences(
+ FormController_BASE::getTypes(),
+ ::cppu::OPropertySetHelper::getTypes()
+ );
+}
+
+// XServiceInfo
+sal_Bool SAL_CALL FormController::supportsService(const OUString& ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+OUString SAL_CALL FormController::getImplementationName()
+{
+ return "org.openoffice.comp.svx.FormController";
+}
+
+Sequence< OUString> SAL_CALL FormController::getSupportedServiceNames()
+{
+ // service names which are supported only, but cannot be used to created an
+ // instance at a service factory
+ Sequence<OUString> aNonCreatableServiceNames { "com.sun.star.form.FormControllerDispatcher" };
+
+ // services which can be used to created an instance at a service factory
+ Sequence< OUString > aCreatableServiceNames( getSupportedServiceNames_Static() );
+ return ::comphelper::concatSequences( aCreatableServiceNames, aNonCreatableServiceNames );
+}
+
+
+sal_Bool SAL_CALL FormController::approveReset(const EventObject& /*rEvent*/)
+{
+ return true;
+}
+
+
+void SAL_CALL FormController::resetted(const EventObject& rEvent)
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+ if (getCurrentControl().is() && (getCurrentControl()->getModel() == rEvent.Source))
+ m_bModified = false;
+}
+
+
+Sequence< OUString> const & FormController::getSupportedServiceNames_Static()
+{
+ static Sequence< OUString> const aServices
+ {
+ "com.sun.star.form.runtime.FormController",
+ "com.sun.star.awt.control.TabController"
+ };
+ return aServices;
+}
+
+
+namespace
+{
+ struct ResetComponentText
+ {
+ void operator()( const Reference< XTextComponent >& _rxText )
+ {
+ _rxText->setText( OUString() );
+ }
+ };
+
+ struct RemoveComponentTextListener
+ {
+ explicit RemoveComponentTextListener( const Reference< XTextListener >& _rxListener )
+ :m_xListener( _rxListener )
+ {
+ }
+
+ void operator()( const Reference< XTextComponent >& _rxText )
+ {
+ _rxText->removeTextListener( m_xListener );
+ }
+
+ private:
+ Reference< XTextListener > m_xListener;
+ };
+}
+
+
+void FormController::impl_setTextOnAllFilter_throw()
+{
+ m_bSuspendFilterTextListening = true;
+ ::comphelper::FlagGuard aResetFlag( m_bSuspendFilterTextListening );
+
+ // reset the text for all controls
+ ::std::for_each( m_aFilterComponents.begin(), m_aFilterComponents.end(), ResetComponentText() );
+
+ if ( m_aFilterRows.empty() )
+ // nothing to do anymore
+ return;
+
+ if ( m_nCurrentFilterPosition < 0 )
+ return;
+
+ // set the text for all filters
+ OSL_ENSURE( m_aFilterRows.size() > o3tl::make_unsigned(m_nCurrentFilterPosition),
+ "FormController::impl_setTextOnAllFilter_throw: m_nCurrentFilterPosition too big" );
+
+ if ( o3tl::make_unsigned(m_nCurrentFilterPosition) < m_aFilterRows.size() )
+ {
+ FmFilterRow& rRow = m_aFilterRows[ m_nCurrentFilterPosition ];
+ for (const auto& rEntry : rRow)
+ {
+ rEntry.first->setText( rEntry.second );
+ }
+ }
+}
+// OPropertySetHelper
+
+sal_Bool FormController::convertFastPropertyValue( Any & /*rConvertedValue*/, Any & /*rOldValue*/,
+ sal_Int32 /*nHandle*/, const Any& /*rValue*/ )
+{
+ return false;
+}
+
+
+void FormController::setFastPropertyValue_NoBroadcast( sal_Int32 /*nHandle*/, const Any& /*rValue*/ )
+{
+}
+
+
+void FormController::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const
+{
+ switch (nHandle)
+ {
+ case FM_ATTR_FILTER:
+ {
+ OUStringBuffer aFilter;
+ Reference<XConnection> xConnection(getConnection(Reference< XRowSet>(m_xModelAsIndex, UNO_QUERY)));
+ if (xConnection.is())
+ {
+ Reference< XNumberFormatsSupplier> xFormatSupplier( getNumberFormats( xConnection, true ) );
+ Reference< XNumberFormatter> xFormatter = NumberFormatter::create(m_xComponentContext);
+ xFormatter->attachNumberFormatsSupplier(xFormatSupplier);
+
+ // now add the filter rows
+ try
+ {
+ for (const FmFilterRow& rRow : m_aFilterRows)
+ {
+ if ( rRow.empty() )
+ continue;
+
+ OUStringBuffer aRowFilter;
+ for ( FmFilterRow::const_iterator condition = rRow.begin(); condition != rRow.end(); ++condition )
+ {
+ // get the field of the controls map
+ Reference< XControl > xControl( condition->first, UNO_QUERY_THROW );
+ Reference< XPropertySet > xModelProps( xControl->getModel(), UNO_QUERY_THROW );
+ Reference< XPropertySet > xField( xModelProps->getPropertyValue( FM_PROP_BOUNDFIELD ), UNO_QUERY_THROW );
+
+ OUString sFilterValue( condition->second );
+
+ OUString sErrorMsg;
+ const std::unique_ptr< OSQLParseNode > pParseNode =
+ predicateTree( sErrorMsg, sFilterValue, xFormatter, xField );
+ OSL_ENSURE( pParseNode != nullptr, "FormController::getFastPropertyValue: could not parse the field value predicate!" );
+ if ( pParseNode != nullptr )
+ {
+ OUString sCriteria;
+ // don't use a parse context here, we need it unlocalized
+ pParseNode->parseNodeToStr( sCriteria, xConnection );
+ if ( condition != rRow.begin() )
+ aRowFilter.append( " AND " );
+ aRowFilter.append( sCriteria );
+ }
+ }
+ if ( !aRowFilter.isEmpty() )
+ {
+ if ( !aFilter.isEmpty() )
+ aFilter.append( " OR " );
+
+ aFilter.append( "( " + aRowFilter + " )" );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ aFilter.setLength(0);
+ }
+ }
+ rValue <<= aFilter.makeStringAndClear();
+ }
+ break;
+
+ case FM_ATTR_FORM_OPERATIONS:
+ rValue <<= m_xFormOperations;
+ break;
+ }
+}
+
+
+Reference< XPropertySetInfo > FormController::getPropertySetInfo()
+{
+ static Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) );
+ return xInfo;
+}
+
+
+void FormController::fillProperties(
+ Sequence< Property >& /* [out] */ _rProps,
+ Sequence< Property >& /* [out] */ /*_rAggregateProps*/
+ ) const
+{
+ _rProps.realloc(2);
+ sal_Int32 nPos = 0;
+ Property* pDesc = _rProps.getArray();
+
+ pDesc[nPos++] = Property(FM_PROP_FILTER, FM_ATTR_FILTER,
+ cppu::UnoType<OUString>::get(),
+ PropertyAttribute::READONLY);
+ pDesc[nPos++] = Property(FM_PROP_FORM_OPERATIONS, FM_ATTR_FORM_OPERATIONS,
+ cppu::UnoType<XFormOperations>::get(),
+ PropertyAttribute::READONLY);
+}
+
+
+::cppu::IPropertyArrayHelper& FormController::getInfoHelper()
+{
+ return *getArrayHelper();
+}
+
+// XFilterController
+
+void SAL_CALL FormController::addFilterControllerListener( const Reference< XFilterControllerListener >& Listener )
+{
+ m_aFilterListeners.addInterface( Listener );
+}
+
+
+void SAL_CALL FormController::removeFilterControllerListener( const Reference< XFilterControllerListener >& Listener )
+{
+ m_aFilterListeners.removeInterface( Listener );
+}
+
+
+::sal_Int32 SAL_CALL FormController::getFilterComponents()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ return m_aFilterComponents.size();
+}
+
+
+::sal_Int32 SAL_CALL FormController::getDisjunctiveTerms()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ return m_aFilterRows.size();
+}
+
+
+void SAL_CALL FormController::setPredicateExpression( ::sal_Int32 Component, ::sal_Int32 Term, const OUString& PredicateExpression )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ if ( ( Component < 0 ) || ( Component >= getFilterComponents() ) || ( Term < 0 ) || ( Term >= getDisjunctiveTerms() ) )
+ throw IndexOutOfBoundsException( OUString(), *this );
+
+ Reference< XTextComponent > xText( m_aFilterComponents[ Component ] );
+ xText->setText( PredicateExpression );
+
+ FmFilterRow& rFilterRow = m_aFilterRows[ Term ];
+ if ( !PredicateExpression.isEmpty() )
+ rFilterRow[ xText ] = PredicateExpression;
+ else
+ rFilterRow.erase( xText );
+}
+
+
+Reference< XControl > FormController::getFilterComponent( ::sal_Int32 Component )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ if ( ( Component < 0 ) || ( Component >= getFilterComponents() ) )
+ throw IndexOutOfBoundsException( OUString(), *this );
+
+ return Reference< XControl >( m_aFilterComponents[ Component ], UNO_QUERY );
+}
+
+
+Sequence< Sequence< OUString > > FormController::getPredicateExpressions()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ Sequence< Sequence< OUString > > aExpressions( m_aFilterRows.size() );
+ auto aExpressionsRange = asNonConstRange(aExpressions);
+ sal_Int32 termIndex = 0;
+ for (const FmFilterRow& rRow : m_aFilterRows)
+ {
+ Sequence< OUString > aConjunction( m_aFilterComponents.size() );
+ auto aConjunctionRange = asNonConstRange(aConjunction);
+ sal_Int32 componentIndex = 0;
+ for (const auto& rComp : m_aFilterComponents)
+ {
+ FmFilterRow::const_iterator predicate = rRow.find( rComp );
+ if ( predicate != rRow.end() )
+ aConjunctionRange[ componentIndex ] = predicate->second;
+ ++componentIndex;
+ }
+
+ aExpressionsRange[ termIndex ] = aConjunction;
+ ++termIndex;
+ }
+
+ return aExpressions;
+}
+
+
+void SAL_CALL FormController::removeDisjunctiveTerm( ::sal_Int32 Term )
+{
+ // SYNCHRONIZED -->
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ if ( ( Term < 0 ) || ( Term >= getDisjunctiveTerms() ) )
+ throw IndexOutOfBoundsException( OUString(), *this );
+
+ // if the to-be-deleted row is our current row, we need to shift
+ if ( Term == m_nCurrentFilterPosition )
+ {
+ if ( m_nCurrentFilterPosition < sal_Int32( m_aFilterRows.size() - 1 ) )
+ ++m_nCurrentFilterPosition;
+ else
+ --m_nCurrentFilterPosition;
+ }
+
+ FmFilterRows::iterator pos = m_aFilterRows.begin() + Term;
+ m_aFilterRows.erase( pos );
+
+ // adjust m_nCurrentFilterPosition if the removed row preceded it
+ if ( Term < m_nCurrentFilterPosition )
+ --m_nCurrentFilterPosition;
+
+ SAL_WARN_IF( !( ( m_nCurrentFilterPosition < 0 ) != ( m_aFilterRows.empty() ) ),
+ "svx.form", "FormController::removeDisjunctiveTerm: inconsistency!" );
+
+ // update the texts in the filter controls
+ impl_setTextOnAllFilter_throw();
+
+ FilterEvent aEvent;
+ aEvent.Source = *this;
+ aEvent.DisjunctiveTerm = Term;
+ aGuard.clear();
+ // <-- SYNCHRONIZED
+
+ m_aFilterListeners.notifyEach( &XFilterControllerListener::disjunctiveTermRemoved, aEvent );
+}
+
+
+void SAL_CALL FormController::appendEmptyDisjunctiveTerm()
+{
+ // SYNCHRONIZED -->
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ impl_appendEmptyFilterRow( aGuard );
+ // <-- SYNCHRONIZED
+}
+
+
+::sal_Int32 SAL_CALL FormController::getActiveTerm()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ return m_nCurrentFilterPosition;
+}
+
+
+void SAL_CALL FormController::setActiveTerm( ::sal_Int32 ActiveTerm )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ if ( ( ActiveTerm < 0 ) || ( ActiveTerm >= getDisjunctiveTerms() ) )
+ throw IndexOutOfBoundsException( OUString(), *this );
+
+ if ( ActiveTerm == getActiveTerm() )
+ return;
+
+ m_nCurrentFilterPosition = ActiveTerm;
+ impl_setTextOnAllFilter_throw();
+}
+
+// XElementAccess
+
+sal_Bool SAL_CALL FormController::hasElements()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return !m_aChildren.empty();
+}
+
+
+Type SAL_CALL FormController::getElementType()
+{
+ return cppu::UnoType<XFormController>::get();
+
+}
+
+// XEnumerationAccess
+
+Reference< XEnumeration > SAL_CALL FormController::createEnumeration()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return new ::comphelper::OEnumerationByIndex(this);
+}
+
+// XIndexAccess
+
+sal_Int32 SAL_CALL FormController::getCount()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return m_aChildren.size();
+}
+
+
+Any SAL_CALL FormController::getByIndex(sal_Int32 Index)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (Index < 0 ||
+ o3tl::make_unsigned(Index) >= m_aChildren.size())
+ throw IndexOutOfBoundsException();
+
+ return Any( m_aChildren[ Index ] );
+}
+
+// EventListener
+
+void SAL_CALL FormController::disposing(const EventObject& e)
+{
+ // has the container been disposed
+ ::osl::MutexGuard aGuard( m_aMutex );
+ Reference< XControlContainer > xContainer(e.Source, UNO_QUERY);
+ if (xContainer.is())
+ {
+ setContainer(Reference< XControlContainer > ());
+ }
+ else
+ {
+ // has a control been disposed
+ Reference< XControl > xControl(e.Source, UNO_QUERY);
+ if (xControl.is())
+ {
+ if (getContainer().is())
+ removeControl(xControl);
+ }
+ }
+}
+
+// OComponentHelper
+
+void FormController::disposeAllFeaturesAndDispatchers()
+{
+ for (auto& rDispatcher : m_aFeatureDispatchers)
+ {
+ try
+ {
+ ::comphelper::disposeComponent( rDispatcher.second );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ m_aFeatureDispatchers.clear();
+}
+
+
+void FormController::disposing()
+{
+ EventObject aEvt( *this );
+
+ // if we're still active, simulate a "deactivated" event
+ if ( m_xActiveControl.is() )
+ m_aActivateListeners.notifyEach( &XFormControllerListener::formDeactivated, aEvt );
+
+ // notify all our listeners
+ m_aActivateListeners.disposeAndClear(aEvt);
+ m_aModifyListeners.disposeAndClear(aEvt);
+ m_aErrorListeners.disposeAndClear(aEvt);
+ m_aDeleteListeners.disposeAndClear(aEvt);
+ m_aRowSetApproveListeners.disposeAndClear(aEvt);
+ m_aParameterListeners.disposeAndClear(aEvt);
+ m_aFilterListeners.disposeAndClear(aEvt);
+
+ removeBoundFieldListener();
+ stopFiltering();
+
+ m_aControlBorderManager.restoreAll();
+
+ m_aFilterRows.clear();
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_xActiveControl = nullptr;
+ implSetCurrentControl( nullptr );
+
+ // clean up our children
+ for (const auto& rpChild : m_aChildren)
+ {
+ // search the position of the model within the form
+ Reference< XFormComponent > xForm(rpChild->getModel(), UNO_QUERY);
+ sal_uInt32 nPos = m_xModelAsIndex->getCount();
+ Reference< XFormComponent > xTemp;
+ for( ; nPos; )
+ {
+
+ m_xModelAsIndex->getByIndex( --nPos ) >>= xTemp;
+ if ( xForm.get() == xTemp.get() )
+ {
+ Reference< XInterface > xIfc( rpChild, UNO_QUERY );
+ m_xModelAsManager->detach( nPos, xIfc );
+ break;
+ }
+ }
+
+ Reference< XComponent > (rpChild, UNO_QUERY_THROW)->dispose();
+ }
+ m_aChildren.clear();
+
+ disposeAllFeaturesAndDispatchers();
+
+ if ( m_xFormOperations.is() )
+ m_xFormOperations->dispose();
+ m_xFormOperations.clear();
+
+ if (m_bDBConnection)
+ unload();
+
+ setContainer( nullptr );
+ setModel( nullptr );
+ setParent( nullptr );
+
+ ::comphelper::disposeComponent( m_xComposer );
+
+ m_bDBConnection = false;
+}
+
+
+namespace
+{
+ bool lcl_shouldUseDynamicControlBorder( const Reference< XInterface >& _rxForm, const Any& _rDynamicColorProp )
+ {
+ bool bDoUse = false;
+ if ( !( _rDynamicColorProp >>= bDoUse ) )
+ {
+ DocumentType eDocType = DocumentClassification::classifyHostDocument( _rxForm );
+ return ControlLayouter::useDynamicBorderColor( eDocType );
+ }
+ return bDoUse;
+ }
+}
+
+
+void SAL_CALL FormController::propertyChange(const PropertyChangeEvent& evt)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ if ( evt.PropertyName == FM_PROP_BOUNDFIELD )
+ {
+ Reference<XPropertySet> xOldBound;
+ evt.OldValue >>= xOldBound;
+ if ( !xOldBound.is() && evt.NewValue.hasValue() )
+ {
+ Reference< XControlModel > xControlModel(evt.Source,UNO_QUERY);
+ Reference< XControl > xControl = findControl(m_aControls,xControlModel,false,false);
+ if ( xControl.is() )
+ {
+ startControlModifyListening( xControl );
+ Reference<XPropertySet> xProp(xControlModel,UNO_QUERY);
+ if ( xProp.is() )
+ xProp->removePropertyChangeListener(FM_PROP_BOUNDFIELD, this);
+ }
+ }
+ }
+ else
+ {
+ bool bModifiedChanged = (evt.PropertyName == FM_PROP_ISMODIFIED);
+ bool bNewChanged = (evt.PropertyName == FM_PROP_ISNEW);
+ if (bModifiedChanged || bNewChanged)
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (bModifiedChanged)
+ m_bCurrentRecordModified = ::comphelper::getBOOL(evt.NewValue);
+ else
+ m_bCurrentRecordNew = ::comphelper::getBOOL(evt.NewValue);
+
+ // toggle the locking
+ if (m_bLocked != determineLockState())
+ {
+ m_bLocked = !m_bLocked;
+ setLocks();
+ if (isListeningForChanges())
+ startListening();
+ else
+ stopListening();
+ }
+
+ if ( bNewChanged )
+ m_aToggleEvent.Call();
+
+ if (!m_bCurrentRecordModified)
+ m_bModified = false;
+ }
+ else if ( evt.PropertyName == FM_PROP_DYNAMIC_CONTROL_BORDER )
+ {
+ bool bEnable = lcl_shouldUseDynamicControlBorder( evt.Source, evt.NewValue );
+ if ( bEnable )
+ {
+ m_aControlBorderManager.enableDynamicBorderColor();
+ if ( m_xActiveControl.is() )
+ m_aControlBorderManager.focusGained( m_xActiveControl );
+ }
+ else
+ {
+ m_aControlBorderManager.disableDynamicBorderColor();
+ }
+ }
+ }
+}
+
+
+bool FormController::replaceControl( const Reference< XControl >& _rxExistentControl, const Reference< XControl >& _rxNewControl )
+{
+ bool bSuccess = false;
+ try
+ {
+ Reference< XIdentifierReplace > xContainer( getContainer(), UNO_QUERY );
+ DBG_ASSERT( xContainer.is(), "FormController::replaceControl: yes, it's not required by the service description, but XIdentifierReplace would be nice!" );
+ if ( xContainer.is() )
+ {
+ // look up the ID of _rxExistentControl
+ const Sequence< sal_Int32 > aIdentifiers( xContainer->getIdentifiers() );
+ const sal_Int32* pIdentifiers = std::find_if(aIdentifiers.begin(), aIdentifiers.end(),
+ [&xContainer, &_rxExistentControl](const sal_Int32 nId) {
+ Reference< XControl > xCheck( xContainer->getByIdentifier( nId ), UNO_QUERY );
+ return xCheck == _rxExistentControl;
+ });
+ DBG_ASSERT( pIdentifiers != aIdentifiers.end(), "FormController::replaceControl: did not find the control in the container!" );
+ if ( pIdentifiers != aIdentifiers.end() )
+ {
+ bool bReplacedWasActive = ( m_xActiveControl.get() == _rxExistentControl.get() );
+ bool bReplacedWasCurrent = ( m_xCurrentControl.get() == _rxExistentControl.get() );
+
+ if ( bReplacedWasActive )
+ {
+ m_xActiveControl = nullptr;
+ implSetCurrentControl( nullptr );
+ }
+ else if ( bReplacedWasCurrent )
+ {
+ implSetCurrentControl( _rxNewControl );
+ }
+
+ // carry over the model
+ _rxNewControl->setModel( _rxExistentControl->getModel() );
+
+ xContainer->replaceByIdentifer( *pIdentifiers, Any( _rxNewControl ) );
+ bSuccess = true;
+
+ if ( bReplacedWasActive )
+ {
+ Reference< XWindow > xControlWindow( _rxNewControl, UNO_QUERY );
+ if ( xControlWindow.is() )
+ xControlWindow->setFocus();
+ }
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ Reference< XControl > xDisposeIt( bSuccess ? _rxExistentControl : _rxNewControl );
+ ::comphelper::disposeComponent( xDisposeIt );
+ return bSuccess;
+}
+
+
+void FormController::toggleAutoFields(bool bAutoFields)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+
+
+ Sequence< Reference< XControl > > aControlsCopy( m_aControls );
+ const Reference< XControl >* pControls = aControlsCopy.getConstArray();
+ sal_Int32 nControls = aControlsCopy.getLength();
+
+ if (bAutoFields)
+ {
+ // as we don't want new controls to be attached to the scripting environment
+ // we change attach flags
+ m_bAttachEvents = false;
+ for (sal_Int32 i = nControls; i > 0;)
+ {
+ Reference< XControl > xControl = pControls[--i];
+ if (xControl.is())
+ {
+ Reference< XPropertySet > xSet(xControl->getModel(), UNO_QUERY);
+ if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
+ {
+ // does the model use a bound field ?
+ Reference< XPropertySet > xField;
+ xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
+
+ // is it an autofield?
+ if ( xField.is()
+ && ::comphelper::hasProperty( FM_PROP_AUTOINCREMENT, xField )
+ && ::comphelper::getBOOL( xField->getPropertyValue( FM_PROP_AUTOINCREMENT ) )
+ )
+ {
+ replaceControl( xControl, new FmXAutoControl() );
+ }
+ }
+ }
+ }
+ m_bAttachEvents = true;
+ }
+ else
+ {
+ m_bDetachEvents = false;
+ for (sal_Int32 i = nControls; i > 0;)
+ {
+ Reference< XControl > xControl = pControls[--i];
+ if (xControl.is())
+ {
+ Reference< XPropertySet > xSet(xControl->getModel(), UNO_QUERY);
+ if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
+ {
+ // does the model use a bound field ?
+ Reference< XPropertySet > xField;
+ xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
+
+ // is it an autofield?
+ if ( xField.is()
+ && ::comphelper::hasProperty( FM_PROP_AUTOINCREMENT, xField )
+ && ::comphelper::getBOOL( xField->getPropertyValue(FM_PROP_AUTOINCREMENT ) )
+ )
+ {
+ OUString sServiceName;
+ OSL_VERIFY( xSet->getPropertyValue( FM_PROP_DEFAULTCONTROL ) >>= sServiceName );
+ Reference< XControl > xNewControl( m_xComponentContext->getServiceManager()->createInstanceWithContext( sServiceName, m_xComponentContext ), UNO_QUERY );
+ replaceControl( xControl, xNewControl );
+ }
+ }
+ }
+ }
+ m_bDetachEvents = true;
+ }
+}
+
+
+IMPL_LINK_NOARG(FormController, OnToggleAutoFields, void*, void)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+
+ toggleAutoFields(m_bCurrentRecordNew);
+}
+
+// XTextListener
+void SAL_CALL FormController::textChanged(const TextEvent& e)
+{
+ // SYNCHRONIZED -->
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ if ( !m_bFiltering )
+ {
+ impl_onModify();
+ return;
+ }
+
+ if ( m_bSuspendFilterTextListening )
+ return;
+
+ Reference< XTextComponent > xText(e.Source,UNO_QUERY);
+ OUString aText = xText->getText();
+
+ if ( m_aFilterRows.empty() )
+ appendEmptyDisjunctiveTerm();
+
+ // find the current row
+ if ( ( m_nCurrentFilterPosition < 0 ) || ( o3tl::make_unsigned(m_nCurrentFilterPosition) >= m_aFilterRows.size() ) )
+ {
+ OSL_ENSURE( false, "FormController::textChanged: m_nCurrentFilterPosition is wrong!" );
+ return;
+ }
+
+ FmFilterRow& rRow = m_aFilterRows[ m_nCurrentFilterPosition ];
+
+ // do we have a new filter
+ if (!aText.isEmpty())
+ rRow[xText] = aText;
+ else
+ {
+ // do we have the control in the row
+ FmFilterRow::iterator iter = rRow.find(xText);
+ // erase the entry out of the row
+ if (iter != rRow.end())
+ rRow.erase(iter);
+ }
+
+ // multiplex the event to our FilterControllerListeners
+ FilterEvent aEvent;
+ aEvent.Source = *this;
+ aEvent.FilterComponent = ::std::find( m_aFilterComponents.begin(), m_aFilterComponents.end(), xText ) - m_aFilterComponents.begin();
+ aEvent.DisjunctiveTerm = getActiveTerm();
+ aEvent.PredicateExpression = aText;
+
+ aGuard.clear();
+ // <-- SYNCHRONIZED
+
+ // notify the changed filter expression
+ m_aFilterListeners.notifyEach( &XFilterControllerListener::predicateExpressionChanged, aEvent );
+}
+
+// XItemListener
+void SAL_CALL FormController::itemStateChanged(const ItemEvent& /*rEvent*/)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ impl_onModify();
+}
+
+// XModificationBroadcaster
+void SAL_CALL FormController::addModifyListener(const Reference< XModifyListener > & l)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+ m_aModifyListeners.addInterface( l );
+}
+
+void FormController::removeModifyListener(const Reference< XModifyListener > & l)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+ m_aModifyListeners.removeInterface( l );
+}
+
+// XModificationListener
+void FormController::modified( const EventObject& _rEvent )
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+
+ try
+ {
+ if ( _rEvent.Source != m_xActiveControl )
+ { // let this control grab the focus
+ // (this case may happen if somebody moves the scroll wheel of the mouse over a control
+ // which does not have the focus)
+ // 85511 - 29.05.2001 - frank.schoenheit@germany.sun.com
+
+ // also, it happens when an image control gets a new image by double-clicking it
+ // #i88458# / 2009-01-12 / frank.schoenheit@sun.com
+ Reference< XWindow > xControlWindow( _rEvent.Source, UNO_QUERY_THROW );
+ xControlWindow->setFocus();
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ impl_onModify();
+}
+
+void FormController::impl_checkDisposed_throw() const
+{
+ if ( impl_isDisposed_nofail() )
+ throw DisposedException( OUString(), *const_cast< FormController* >( this ) );
+}
+
+void FormController::impl_onModify()
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( !m_bModified )
+ m_bModified = true;
+ }
+
+ EventObject aEvt(getXWeak());
+ m_aModifyListeners.notifyEach( &XModifyListener::modified, aEvt );
+}
+
+void FormController::impl_addFilterRow( const FmFilterRow& _row )
+{
+ m_aFilterRows.push_back( _row );
+
+ if ( m_aFilterRows.size() == 1 )
+ { // that's the first row ever
+ OSL_ENSURE( m_nCurrentFilterPosition == -1, "FormController::impl_addFilterRow: inconsistency!" );
+ m_nCurrentFilterPosition = 0;
+ }
+}
+
+void FormController::impl_appendEmptyFilterRow( ::osl::ClearableMutexGuard& _rClearBeforeNotify )
+{
+ // SYNCHRONIZED -->
+ impl_addFilterRow( FmFilterRow() );
+
+ // notify the listeners
+ FilterEvent aEvent;
+ aEvent.Source = *this;
+ aEvent.DisjunctiveTerm = static_cast<sal_Int32>(m_aFilterRows.size()) - 1;
+ _rClearBeforeNotify.clear();
+ // <-- SYNCHRONIZED
+ m_aFilterListeners.notifyEach( &XFilterControllerListener::disjunctiveTermAdded, aEvent );
+}
+
+bool FormController::determineLockState() const
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ // a.) in filter mode we are always locked
+ // b.) if we have no valid model or our model (a result set) is not alive -> we're locked
+ // c.) if we are inserting everything is OK and we are not locked
+ // d.) if are not updatable or on invalid position
+ Reference< XResultSet > xResultSet(m_xModelAsIndex, UNO_QUERY);
+ if (m_bFiltering || !xResultSet.is() || !isRowSetAlive(xResultSet))
+ return true;
+ else
+ return !(m_bCanInsert && m_bCurrentRecordNew)
+ && (xResultSet->isBeforeFirst() || xResultSet->isAfterLast() || xResultSet->rowDeleted() || !m_bCanUpdate);
+}
+
+// FocusListener
+void FormController::focusGained(const FocusEvent& e)
+{
+ // SYNCHRONIZED -->
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ m_aControlBorderManager.focusGained( e.Source );
+
+ Reference< XControl > xControl(e.Source, UNO_QUERY);
+ if (m_bDBConnection)
+ {
+ // do we need to keep the locking of the commit
+ // we hold the lock as long as the control differs from the current
+ // otherwise we disabled the lock
+ m_bCommitLock = m_bCommitLock && xControl.get() != m_xCurrentControl.get();
+ if (m_bCommitLock)
+ return;
+
+ // when do we have to commit a value to form or a filter
+ // a.) if the current value is modified
+ // b.) there must be a current control
+ // c.) and it must be different from the new focus owning control or
+ // d.) the focus is moving around (so we have only one control)
+
+ if ( ( m_bModified || m_bFiltering )
+ && m_xCurrentControl.is()
+ && ( ( xControl.get() != m_xCurrentControl.get() )
+ || ( ( e.FocusFlags & FocusChangeReason::AROUND )
+ && ( m_bCycle || m_bFiltering )
+ )
+ )
+ )
+ {
+ // check the old control if the content is ok
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ Reference< XBoundControl > xLockingTest(m_xCurrentControl, UNO_QUERY);
+ bool bControlIsLocked = xLockingTest.is() && xLockingTest->getLock();
+ assert(!bControlIsLocked && "FormController::Gained: I'm modified and the current control is locked ? How this ?");
+ // normally, a locked control should not be modified, so probably my bModified must
+ // have been set from a different context, which I would not understand ...
+#endif
+ DBG_ASSERT(m_xCurrentControl.is(), "no CurrentControl set");
+ // first the control ask if it supports the IFace
+ Reference< XBoundComponent > xBound(m_xCurrentControl, UNO_QUERY);
+ if (!xBound.is() && m_xCurrentControl.is())
+ xBound.set(m_xCurrentControl->getModel(), UNO_QUERY);
+
+ // lock if we lose the focus during commit
+ m_bCommitLock = true;
+
+ // commit unsuccessful, reset focus
+ if (xBound.is() && !xBound->commit())
+ {
+ // the commit failed and we don't commit again until the current control
+ // which couldn't be commit gains the focus again
+ Reference< XWindow > xWindow(m_xCurrentControl, UNO_QUERY);
+ if (xWindow.is())
+ xWindow->setFocus();
+ return;
+ }
+ else
+ {
+ m_bModified = false;
+ m_bCommitLock = false;
+ }
+ }
+
+ if (!m_bFiltering && m_bCycle && (e.FocusFlags & FocusChangeReason::AROUND) && m_xCurrentControl.is())
+ {
+ OSL_ENSURE( m_xFormOperations.is(), "FormController::focusGained: hmm?" );
+ // should have been created in setModel
+ try
+ {
+ if ( e.FocusFlags & FocusChangeReason::FORWARD )
+ {
+ if ( m_xFormOperations.is() && m_xFormOperations->isEnabled( FormFeature::MoveToNext ) )
+ m_xFormOperations->execute( FormFeature::MoveToNext );
+ }
+ else // backward
+ {
+ if ( m_xFormOperations.is() && m_xFormOperations->isEnabled( FormFeature::MoveToPrevious ) )
+ m_xFormOperations->execute( FormFeature::MoveToPrevious );
+ }
+ }
+ catch ( const Exception& )
+ {
+ // don't handle this any further. That's an ... admissible error.
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ }
+
+ // still one and the same control
+ if ( ( m_xActiveControl == xControl )
+ && ( xControl == m_xCurrentControl )
+ )
+ {
+ DBG_ASSERT(m_xCurrentControl.is(), "No CurrentControl selected");
+ return;
+ }
+
+ bool bActivated = !m_xActiveControl.is() && xControl.is();
+
+ m_xActiveControl = xControl;
+
+ implSetCurrentControl( xControl );
+ SAL_WARN_IF( !m_xCurrentControl.is(), "svx.form", "implSetCurrentControl did nonsense!" );
+
+ if ( bActivated )
+ {
+ // (asynchronously) call activation handlers
+ m_aActivationEvent.Call();
+
+ // call modify listeners
+ if ( m_bModified )
+ m_aModifyListeners.notifyEach( &XModifyListener::modified, EventObject( *this ) );
+ }
+
+ // invalidate all features which depend on the currently focused control
+ if ( m_bDBConnection && !m_bFiltering )
+ implInvalidateCurrentControlDependentFeatures();
+
+ if ( !m_xCurrentControl.is() )
+ return;
+
+ // control gets focus, then possibly in the visible range
+ Reference< XFormControllerContext > xContext( m_xFormControllerContext );
+ Reference< XControl > xCurrentControl( m_xCurrentControl );
+ aGuard.clear();
+ // <-- SYNCHRONIZED
+
+ if ( xContext.is() )
+ xContext->makeVisible( xCurrentControl );
+}
+
+IMPL_LINK_NOARG( FormController, OnActivated, void*, void )
+{
+ EventObject aEvent;
+ aEvent.Source = *this;
+ m_aActivateListeners.notifyEach( &XFormControllerListener::formActivated, aEvent );
+}
+
+IMPL_LINK_NOARG( FormController, OnDeactivated, void*, void )
+{
+ EventObject aEvent;
+ aEvent.Source = *this;
+ m_aActivateListeners.notifyEach( &XFormControllerListener::formDeactivated, aEvent );
+}
+
+void FormController::focusLost(const FocusEvent& e)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+
+ m_aControlBorderManager.focusLost( e.Source );
+
+ Reference< XWindowPeer > xNext(e.NextFocus, UNO_QUERY);
+ // if focus hasn't passed to some other window, e.g. focus in a welded item, don't deactivate
+ if (!xNext)
+ return;
+ Reference< XControl > xNextControl = isInList(xNext);
+ if (!xNextControl.is())
+ {
+ m_xActiveControl = nullptr;
+ m_aDeactivationEvent.Call();
+ }
+}
+
+void SAL_CALL FormController::mousePressed( const awt::MouseEvent& /*_rEvent*/ )
+{
+ // not interested in
+}
+
+void SAL_CALL FormController::mouseReleased( const awt::MouseEvent& /*_rEvent*/ )
+{
+ // not interested in
+}
+
+void SAL_CALL FormController::mouseEntered( const awt::MouseEvent& _rEvent )
+{
+ m_aControlBorderManager.mouseEntered( _rEvent.Source );
+}
+
+void SAL_CALL FormController::mouseExited( const awt::MouseEvent& _rEvent )
+{
+ m_aControlBorderManager.mouseExited( _rEvent.Source );
+}
+
+void SAL_CALL FormController::componentValidityChanged( const EventObject& _rSource )
+{
+ Reference< XControl > xControl( findControl( m_aControls, Reference< XControlModel >( _rSource.Source, UNO_QUERY ), false, false ) );
+ Reference< XValidatableFormComponent > xValidatable( _rSource.Source, UNO_QUERY );
+
+ OSL_ENSURE( xControl.is() && xValidatable.is(), "FormController::componentValidityChanged: huh?" );
+
+ if ( xControl.is() && xValidatable.is() )
+ m_aControlBorderManager.validityChanged( xControl, xValidatable );
+}
+
+
+void FormController::setModel(const Reference< XTabControllerModel > & Model)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ DBG_ASSERT(m_xTabController.is(), "FormController::setModel : invalid aggregate !");
+
+ try
+ {
+ // disconnect from the old model
+ if (m_xModelAsIndex.is())
+ {
+ if (m_bDBConnection)
+ {
+ // we are currently working on the model
+ EventObject aEvt(m_xModelAsIndex);
+ unloaded(aEvt);
+ }
+
+ Reference< XLoadable > xForm(m_xModelAsIndex, UNO_QUERY);
+ if (xForm.is())
+ xForm->removeLoadListener(this);
+
+ Reference< XSQLErrorBroadcaster > xBroadcaster(m_xModelAsIndex, UNO_QUERY);
+ if (xBroadcaster.is())
+ xBroadcaster->removeSQLErrorListener(this);
+
+ Reference< XDatabaseParameterBroadcaster > xParamBroadcaster(m_xModelAsIndex, UNO_QUERY);
+ if (xParamBroadcaster.is())
+ xParamBroadcaster->removeParameterListener(this);
+
+ }
+
+ disposeAllFeaturesAndDispatchers();
+
+ if ( m_xFormOperations.is() )
+ m_xFormOperations->dispose();
+ m_xFormOperations.clear();
+
+ // set the new model wait for the load event
+ if (m_xTabController.is())
+ m_xTabController->setModel(Model);
+ m_xModelAsIndex.set(Model, UNO_QUERY);
+ m_xModelAsManager.set(Model, UNO_QUERY);
+
+ // only if both ifaces exit, the controller will work successful
+ if (!m_xModelAsIndex.is() || !m_xModelAsManager.is())
+ {
+ m_xModelAsManager = nullptr;
+ m_xModelAsIndex = nullptr;
+ }
+
+ if (m_xModelAsIndex.is())
+ {
+ // re-create m_xFormOperations
+ m_xFormOperations = FormOperations::createWithFormController( m_xComponentContext, this );
+ m_xFormOperations->setFeatureInvalidation( this );
+
+ // adding load and ui interaction listeners
+ Reference< XLoadable > xForm(Model, UNO_QUERY);
+ if (xForm.is())
+ xForm->addLoadListener(this);
+
+ Reference< XSQLErrorBroadcaster > xBroadcaster(Model, UNO_QUERY);
+ if (xBroadcaster.is())
+ xBroadcaster->addSQLErrorListener(this);
+
+ Reference< XDatabaseParameterBroadcaster > xParamBroadcaster(Model, UNO_QUERY);
+ if (xParamBroadcaster.is())
+ xParamBroadcaster->addParameterListener(this);
+
+ // well, is the database already loaded?
+ // then we have to simulate a load event
+ Reference< XLoadable > xCursor(m_xModelAsIndex, UNO_QUERY);
+ if (xCursor.is() && xCursor->isLoaded())
+ {
+ EventObject aEvt(xCursor);
+ loaded(aEvt);
+ }
+
+ Reference< XPropertySet > xModelProps( m_xModelAsIndex, UNO_QUERY );
+ Reference< XPropertySetInfo > xPropInfo( xModelProps->getPropertySetInfo() );
+ if ( xPropInfo.is()
+ && xPropInfo->hasPropertyByName( FM_PROP_DYNAMIC_CONTROL_BORDER )
+ && xPropInfo->hasPropertyByName( FM_PROP_CONTROL_BORDER_COLOR_FOCUS )
+ && xPropInfo->hasPropertyByName( FM_PROP_CONTROL_BORDER_COLOR_MOUSE )
+ && xPropInfo->hasPropertyByName( FM_PROP_CONTROL_BORDER_COLOR_INVALID )
+ )
+ {
+ bool bEnableDynamicControlBorder = lcl_shouldUseDynamicControlBorder(
+ xModelProps, xModelProps->getPropertyValue( FM_PROP_DYNAMIC_CONTROL_BORDER ) );
+ if ( bEnableDynamicControlBorder )
+ m_aControlBorderManager.enableDynamicBorderColor();
+ else
+ m_aControlBorderManager.disableDynamicBorderColor();
+
+ Color nColor;
+ if ( xModelProps->getPropertyValue( FM_PROP_CONTROL_BORDER_COLOR_FOCUS ) >>= nColor )
+ m_aControlBorderManager.setStatusColor( ControlStatus::Focused, nColor );
+ if ( xModelProps->getPropertyValue( FM_PROP_CONTROL_BORDER_COLOR_MOUSE ) >>= nColor )
+ m_aControlBorderManager.setStatusColor( ControlStatus::MouseHover, nColor );
+ if ( xModelProps->getPropertyValue( FM_PROP_CONTROL_BORDER_COLOR_INVALID ) >>= nColor )
+ m_aControlBorderManager.setStatusColor( ControlStatus::Invalid, nColor );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+
+Reference< XTabControllerModel > FormController::getModel()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ DBG_ASSERT(m_xTabController.is(), "FormController::getModel : invalid aggregate !");
+ if (!m_xTabController.is())
+ return Reference< XTabControllerModel > ();
+ return m_xTabController->getModel();
+}
+
+
+void FormController::addToEventAttacher(const Reference< XControl > & xControl)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ OSL_ENSURE( xControl.is(), "FormController::addToEventAttacher: invalid control - how did you reach this?" );
+ if ( !xControl.is() )
+ return; /* throw IllegalArgumentException(); */
+
+ // register at the event attacher
+ Reference< XFormComponent > xComp(xControl->getModel(), UNO_QUERY);
+ if (!(xComp.is() && m_xModelAsIndex.is()))
+ return;
+
+ // and look for the position of the ControlModel in it
+ sal_uInt32 nPos = m_xModelAsIndex->getCount();
+ Reference< XFormComponent > xTemp;
+ for( ; nPos; )
+ {
+ m_xModelAsIndex->getByIndex(--nPos) >>= xTemp;
+ if (xComp.get() == xTemp.get())
+ {
+ m_xModelAsManager->attach( nPos, Reference<XInterface>( xControl, UNO_QUERY ), Any(xControl) );
+ break;
+ }
+ }
+}
+
+
+void FormController::removeFromEventAttacher(const Reference< XControl > & xControl)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ OSL_ENSURE( xControl.is(), "FormController::removeFromEventAttacher: invalid control - how did you reach this?" );
+ if ( !xControl.is() )
+ return; /* throw IllegalArgumentException(); */
+
+ // register at the event attacher
+ Reference< XFormComponent > xComp(xControl->getModel(), UNO_QUERY);
+ if ( !(xComp.is() && m_xModelAsIndex.is()) )
+ return;
+
+ // and look for the position of the ControlModel in it
+ sal_uInt32 nPos = m_xModelAsIndex->getCount();
+ Reference< XFormComponent > xTemp;
+ for( ; nPos; )
+ {
+ m_xModelAsIndex->getByIndex(--nPos) >>= xTemp;
+ if (xComp.get() == xTemp.get())
+ {
+ m_xModelAsManager->detach( nPos, Reference<XInterface>( xControl, UNO_QUERY ) );
+ break;
+ }
+ }
+}
+
+
+void FormController::setContainer(const Reference< XControlContainer > & xContainer)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ Reference< XTabControllerModel > xTabModel(getModel());
+ DBG_ASSERT(xTabModel.is() || !xContainer.is(), "No Model defined");
+ // if we have a new container we need a model
+ DBG_ASSERT(m_xTabController.is(), "FormController::setContainer : invalid aggregate !");
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+ Reference< XContainer > xCurrentContainer;
+ if (m_xTabController.is())
+ xCurrentContainer.set(m_xTabController->getContainer(), UNO_QUERY);
+ if (xCurrentContainer.is())
+ {
+ xCurrentContainer->removeContainerListener(this);
+
+ if ( m_aTabActivationIdle.IsActive() )
+ m_aTabActivationIdle.Stop();
+
+ // clear the filter map
+ ::std::for_each( m_aFilterComponents.begin(), m_aFilterComponents.end(), RemoveComponentTextListener( this ) );
+ m_aFilterComponents.clear();
+
+ // collecting the controls
+ for ( const Reference< XControl >& rControl : std::as_const(m_aControls) )
+ implControlRemoved( rControl, true );
+
+ // make database-specific things
+ if (m_bDBConnection && isListeningForChanges())
+ stopListening();
+
+ m_aControls.realloc( 0 );
+ }
+
+ if (m_xTabController.is())
+ m_xTabController->setContainer(xContainer);
+
+ // What controls belong to the container?
+ if (xContainer.is() && xTabModel.is())
+ {
+ const Sequence< Reference< XControlModel > > aModels = xTabModel->getControlModels();
+ Sequence< Reference< XControl > > aAllControls = xContainer->getControls();
+
+ sal_Int32 nCount = aModels.getLength();
+ m_aControls = Sequence< Reference< XControl > >( nCount );
+ Reference< XControl > * pControls = m_aControls.getArray();
+
+ // collecting the controls
+ sal_Int32 j = 0;
+ for (const Reference< XControlModel >& rModel : aModels )
+ {
+ Reference< XControl > xControl = findControl( aAllControls, rModel, false, true );
+ if ( xControl.is() )
+ {
+ pControls[j++] = xControl;
+ implControlInserted( xControl, true );
+ }
+ }
+
+ // not every model had an associated control
+ if (j != nCount)
+ m_aControls.realloc(j);
+
+ // listen at the container
+ Reference< XContainer > xNewContainer(xContainer, UNO_QUERY);
+ if (xNewContainer.is())
+ xNewContainer->addContainerListener(this);
+
+ // make database-specific things
+ if (m_bDBConnection)
+ {
+ m_bLocked = determineLockState();
+ setLocks();
+ if (!isLocked())
+ startListening();
+ }
+ }
+ // the controls are in the right order
+ m_bControlsSorted = true;
+}
+
+
+Reference< XControlContainer > FormController::getContainer()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ DBG_ASSERT(m_xTabController.is(), "FormController::getContainer : invalid aggregate !");
+ if (!m_xTabController.is())
+ return Reference< XControlContainer > ();
+ return m_xTabController->getContainer();
+}
+
+
+Sequence< Reference< XControl > > FormController::getControls()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ if (!m_bControlsSorted)
+ {
+ Reference< XTabControllerModel > xModel = getModel();
+ if (!xModel.is())
+ return m_aControls;
+
+ const Sequence< Reference< XControlModel > > aControlModels = xModel->getControlModels();
+ sal_Int32 nModels = aControlModels.getLength();
+
+ Sequence< Reference< XControl > > aNewControls(nModels);
+
+ Reference< XControl > * pControls = aNewControls.getArray();
+ Reference< XControl > xControl;
+
+ // rearrange the controls according to the tab order sequence
+ sal_Int32 j = 0;
+ for ( const Reference< XControlModel >& rModel : aControlModels )
+ {
+ xControl = findControl( m_aControls, rModel, true, true );
+ if ( xControl.is() )
+ pControls[j++] = xControl;
+ }
+
+ // not every model had an associated control
+ if ( j != nModels )
+ aNewControls.realloc( j );
+
+ m_aControls = aNewControls;
+ m_bControlsSorted = true;
+ }
+ return m_aControls;
+}
+
+
+void FormController::autoTabOrder()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ DBG_ASSERT(m_xTabController.is(), "FormController::autoTabOrder : invalid aggregate !");
+ if (m_xTabController.is())
+ m_xTabController->autoTabOrder();
+}
+
+
+void FormController::activateTabOrder()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ DBG_ASSERT(m_xTabController.is(), "FormController::activateTabOrder : invalid aggregate !");
+ if (m_xTabController.is())
+ m_xTabController->activateTabOrder();
+}
+
+
+void FormController::setControlLock(const Reference< XControl > & xControl)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ bool bLocked = isLocked();
+
+ // It is locked
+ // a. if the entire record is locked
+ // b. if the associated field is locked
+ Reference< XBoundControl > xBound(xControl, UNO_QUERY);
+ if (!(xBound.is() &&
+ ( (bLocked && bLocked != bool(xBound->getLock())) ||
+ !bLocked))) // always uncheck individual fields when unlocking
+ return;
+
+ // there is a data source
+ Reference< XPropertySet > xSet(xControl->getModel(), UNO_QUERY);
+ if (!(xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet)))
+ return;
+
+ // what about the ReadOnly and Enable properties
+ bool bTouch = true;
+ if (::comphelper::hasProperty(FM_PROP_ENABLED, xSet))
+ bTouch = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ENABLED));
+ if (::comphelper::hasProperty(FM_PROP_READONLY, xSet))
+ bTouch = !::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_READONLY));
+
+ if (!bTouch)
+ return;
+
+ Reference< XPropertySet > xField;
+ xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
+ if (!xField.is())
+ return;
+
+ if (bLocked)
+ xBound->setLock(bLocked);
+ else
+ {
+ try
+ {
+ Any aVal = xField->getPropertyValue(FM_PROP_ISREADONLY);
+ if (aVal.hasValue() && ::comphelper::getBOOL(aVal))
+ xBound->setLock(true);
+ else
+ xBound->setLock(bLocked);
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ }
+}
+
+
+void FormController::setLocks()
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ // lock/unlock all controls connected to a data source
+ for ( const Reference< XControl >& rControl : std::as_const(m_aControls) )
+ setControlLock( rControl );
+}
+
+
+namespace
+{
+ bool lcl_shouldListenForModifications( const Reference< XControl >& _rxControl, const Reference< XPropertyChangeListener >& _rxBoundFieldListener )
+ {
+ bool bShould = false;
+
+ Reference< XBoundComponent > xBound( _rxControl, UNO_QUERY );
+ if ( xBound.is() )
+ {
+ bShould = true;
+ }
+ else if ( _rxControl.is() )
+ {
+ Reference< XPropertySet > xModelProps( _rxControl->getModel(), UNO_QUERY );
+ if ( xModelProps.is() && ::comphelper::hasProperty( FM_PROP_BOUNDFIELD, xModelProps ) )
+ {
+ Reference< XPropertySet > xField;
+ xModelProps->getPropertyValue( FM_PROP_BOUNDFIELD ) >>= xField;
+ bShould = xField.is();
+
+ if ( !bShould && _rxBoundFieldListener.is() )
+ xModelProps->addPropertyChangeListener( FM_PROP_BOUNDFIELD, _rxBoundFieldListener );
+ }
+ }
+
+ return bShould;
+ }
+}
+
+
+void FormController::startControlModifyListening(const Reference< XControl > & xControl)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+
+ bool bModifyListening = lcl_shouldListenForModifications( xControl, this );
+
+ // artificial while
+ while ( bModifyListening )
+ {
+ Reference< XModifyBroadcaster > xMod(xControl, UNO_QUERY);
+ if (xMod.is())
+ {
+ xMod->addModifyListener(this);
+ break;
+ }
+
+ // all the text to prematurely recognize a modified
+ Reference< XTextComponent > xText(xControl, UNO_QUERY);
+ if (xText.is())
+ {
+ xText->addTextListener(this);
+ break;
+ }
+
+ Reference< XCheckBox > xBox(xControl, UNO_QUERY);
+ if (xBox.is())
+ {
+ xBox->addItemListener(this);
+ break;
+ }
+
+ Reference< XComboBox > xCbBox(xControl, UNO_QUERY);
+ if (xCbBox.is())
+ {
+ xCbBox->addItemListener(this);
+ break;
+ }
+
+ Reference< XListBox > xListBox(xControl, UNO_QUERY);
+ if (xListBox.is())
+ {
+ xListBox->addItemListener(this);
+ break;
+ }
+ break;
+ }
+}
+
+
+void FormController::stopControlModifyListening(const Reference< XControl > & xControl)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+
+ bool bModifyListening = lcl_shouldListenForModifications( xControl, nullptr );
+
+ // artificial while
+ while (bModifyListening)
+ {
+ Reference< XModifyBroadcaster > xMod(xControl, UNO_QUERY);
+ if (xMod.is())
+ {
+ xMod->removeModifyListener(this);
+ break;
+ }
+ // all the text to prematurely recognize a modified
+ Reference< XTextComponent > xText(xControl, UNO_QUERY);
+ if (xText.is())
+ {
+ xText->removeTextListener(this);
+ break;
+ }
+
+ Reference< XCheckBox > xBox(xControl, UNO_QUERY);
+ if (xBox.is())
+ {
+ xBox->removeItemListener(this);
+ break;
+ }
+
+ Reference< XComboBox > xCbBox(xControl, UNO_QUERY);
+ if (xCbBox.is())
+ {
+ xCbBox->removeItemListener(this);
+ break;
+ }
+
+ Reference< XListBox > xListBox(xControl, UNO_QUERY);
+ if (xListBox.is())
+ {
+ xListBox->removeItemListener(this);
+ break;
+ }
+ break;
+ }
+}
+
+
+void FormController::startListening()
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ m_bModified = false;
+
+ // now register at bound fields
+ for ( const Reference< XControl >& rControl : std::as_const(m_aControls) )
+ startControlModifyListening( rControl );
+}
+
+
+void FormController::stopListening()
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ m_bModified = false;
+
+ // now register at bound fields
+ for ( const Reference< XControl >& rControl : std::as_const(m_aControls) )
+ stopControlModifyListening( rControl );
+}
+
+
+Reference< XControl > FormController::findControl(Sequence< Reference< XControl > >& _rControls, const Reference< XControlModel > & xCtrlModel ,bool _bRemove,bool _bOverWrite) const
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ DBG_ASSERT( xCtrlModel.is(), "findControl - which ?!" );
+
+ const Reference< XControl >* pControls = std::find_if(std::cbegin(_rControls), std::cend(_rControls),
+ [&xCtrlModel](const Reference< XControl >& rControl) {
+ return rControl.is() && rControl->getModel().get() == xCtrlModel.get(); });
+ if (pControls != std::cend(_rControls))
+ {
+ Reference< XControl > xControl( *pControls );
+ auto i = static_cast<sal_Int32>(std::distance(std::cbegin(_rControls), pControls));
+ if ( _bRemove )
+ ::comphelper::removeElementAt( _rControls, i );
+ else if ( _bOverWrite )
+ _rControls.getArray()[i].clear();
+ return xControl;
+ }
+ return Reference< XControl > ();
+}
+
+
+void FormController::implControlInserted( const Reference< XControl>& _rxControl, bool _bAddToEventAttacher )
+{
+ Reference< XWindow > xWindow( _rxControl, UNO_QUERY );
+ if ( xWindow.is() )
+ {
+ xWindow->addFocusListener( this );
+ xWindow->addMouseListener( this );
+
+ if ( _bAddToEventAttacher )
+ addToEventAttacher( _rxControl );
+ }
+
+ // add a dispatch interceptor to the control (if supported)
+ Reference< XDispatchProviderInterception > xInterception( _rxControl, UNO_QUERY );
+ if ( xInterception.is() )
+ createInterceptor( xInterception );
+
+ if ( !_rxControl.is() )
+ return;
+
+ Reference< XControlModel > xModel( _rxControl->getModel() );
+
+ // we want to know about the reset of the model of our controls
+ // (for correctly resetting m_bModified)
+ Reference< XReset > xReset( xModel, UNO_QUERY );
+ if ( xReset.is() )
+ xReset->addResetListener( this );
+
+ // and we want to know about the validity, to visually indicate it
+ Reference< XValidatableFormComponent > xValidatable( xModel, UNO_QUERY );
+ if ( xValidatable.is() )
+ {
+ xValidatable->addFormComponentValidityListener( this );
+ m_aControlBorderManager.validityChanged( _rxControl, xValidatable );
+ }
+
+}
+
+
+void FormController::implControlRemoved( const Reference< XControl>& _rxControl, bool _bRemoveFromEventAttacher )
+{
+ Reference< XWindow > xWindow( _rxControl, UNO_QUERY );
+ if ( xWindow.is() )
+ {
+ xWindow->removeFocusListener( this );
+ xWindow->removeMouseListener( this );
+
+ if ( _bRemoveFromEventAttacher )
+ removeFromEventAttacher( _rxControl );
+ }
+
+ Reference< XDispatchProviderInterception > xInterception( _rxControl, UNO_QUERY);
+ if ( xInterception.is() )
+ deleteInterceptor( xInterception );
+
+ if ( _rxControl.is() )
+ {
+ Reference< XControlModel > xModel( _rxControl->getModel() );
+
+ Reference< XReset > xReset( xModel, UNO_QUERY );
+ if ( xReset.is() )
+ xReset->removeResetListener( this );
+
+ Reference< XValidatableFormComponent > xValidatable( xModel, UNO_QUERY );
+ if ( xValidatable.is() )
+ xValidatable->removeFormComponentValidityListener( this );
+ }
+}
+
+
+void FormController::implSetCurrentControl( const Reference< XControl >& _rxControl )
+{
+ if ( m_xCurrentControl.get() == _rxControl.get() )
+ return;
+
+ Reference< XGridControl > xGridControl( m_xCurrentControl, UNO_QUERY );
+ if ( xGridControl.is() )
+ xGridControl->removeGridControlListener( this );
+
+ m_xCurrentControl = _rxControl;
+
+ xGridControl.set( m_xCurrentControl, UNO_QUERY );
+ if ( xGridControl.is() )
+ xGridControl->addGridControlListener( this );
+}
+
+
+void FormController::insertControl(const Reference< XControl > & xControl)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ m_bControlsSorted = false;
+ m_aControls.realloc(m_aControls.getLength() + 1);
+ m_aControls.getArray()[m_aControls.getLength() - 1] = xControl;
+
+ if (m_pColumnInfoCache)
+ m_pColumnInfoCache->deinitializeControls();
+
+ implControlInserted( xControl, m_bAttachEvents );
+
+ if (m_bDBConnection && !m_bFiltering)
+ setControlLock(xControl);
+
+ if (isListeningForChanges() && m_bAttachEvents)
+ startControlModifyListening( xControl );
+}
+
+
+void FormController::removeControl(const Reference< XControl > & xControl)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ auto pControl = std::find_if(std::cbegin(m_aControls), std::cend(m_aControls),
+ [&xControl](const Reference< XControl >& rControl) { return xControl.get() == rControl.get(); });
+ if (pControl != std::cend(m_aControls))
+ {
+ auto nIndex = static_cast<sal_Int32>(std::distance(std::cbegin(m_aControls), pControl));
+ ::comphelper::removeElementAt( m_aControls, nIndex );
+ }
+
+ FilterComponents::iterator componentPos = ::std::find( m_aFilterComponents.begin(), m_aFilterComponents.end(), xControl );
+ if ( componentPos != m_aFilterComponents.end() )
+ m_aFilterComponents.erase( componentPos );
+
+ implControlRemoved( xControl, m_bDetachEvents );
+
+ if ( isListeningForChanges() && m_bDetachEvents )
+ stopControlModifyListening( xControl );
+}
+
+// XLoadListener
+
+void FormController::loaded(const EventObject& rEvent)
+{
+ OSL_ENSURE( rEvent.Source == m_xModelAsIndex, "FormController::loaded: where did this come from?" );
+
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ ::osl::MutexGuard aGuard( m_aMutex );
+ Reference< XRowSet > xForm(rEvent.Source, UNO_QUERY);
+ // do we have a connected data source
+ if (xForm.is() && getConnection(xForm).is())
+ {
+ Reference< XPropertySet > xSet(xForm, UNO_QUERY);
+ if (xSet.is())
+ {
+ Any aVal = xSet->getPropertyValue(FM_PROP_CYCLE);
+ sal_Int32 aVal2 = 0;
+ ::cppu::enum2int(aVal2,aVal);
+ m_bCycle = !aVal.hasValue() || static_cast<form::TabulatorCycle>(aVal2) == TabulatorCycle_RECORDS;
+ m_bCanUpdate = canUpdate(xSet);
+ m_bCanInsert = canInsert(xSet);
+ m_bCurrentRecordModified = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISMODIFIED));
+ m_bCurrentRecordNew = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW));
+
+ startFormListening( xSet, false );
+
+ // set the locks for the current controls
+ if (getContainer().is())
+ {
+ m_aLoadEvent.Call();
+ }
+ }
+ else
+ {
+ m_bCanInsert = m_bCanUpdate = m_bCycle = false;
+ m_bCurrentRecordModified = false;
+ m_bCurrentRecordNew = false;
+ m_bLocked = false;
+ }
+ m_bDBConnection = true;
+ }
+ else
+ {
+ m_bDBConnection = false;
+ m_bCanInsert = m_bCanUpdate = m_bCycle = false;
+ m_bCurrentRecordModified = false;
+ m_bCurrentRecordNew = false;
+ m_bLocked = false;
+ }
+
+ Reference< XColumnsSupplier > xFormColumns( xForm, UNO_QUERY );
+ m_pColumnInfoCache.reset( xFormColumns.is() ? new ColumnInfoCache( xFormColumns ) : nullptr );
+
+ updateAllDispatchers();
+}
+
+
+void FormController::updateAllDispatchers() const
+{
+ ::std::for_each(
+ m_aFeatureDispatchers.begin(),
+ m_aFeatureDispatchers.end(),
+ [] (const DispatcherContainer::value_type& dispatcher) {
+ UpdateAllListeners()(dispatcher.second);
+ });
+}
+
+
+IMPL_LINK_NOARG(FormController, OnLoad, void*, void)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ m_bLocked = determineLockState();
+
+ setLocks();
+
+ if (!m_bLocked)
+ startListening();
+
+ // just one exception toggle the auto values
+ if (m_bCurrentRecordNew)
+ toggleAutoFields(true);
+}
+
+
+void FormController::unloaded(const EventObject& /*rEvent*/)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ updateAllDispatchers();
+}
+
+
+void FormController::reloading(const EventObject& /*aEvent*/)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ // do the same like in unloading
+ // just one exception toggle the auto values
+ m_aToggleEvent.CancelPendingCall();
+ unload();
+}
+
+
+void FormController::reloaded(const EventObject& aEvent)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ loaded(aEvent);
+}
+
+
+void FormController::unloading(const EventObject& /*aEvent*/)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ unload();
+}
+
+
+void FormController::unload()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ m_aLoadEvent.CancelPendingCall();
+
+ // be sure not to have autofields
+ if (m_bCurrentRecordNew)
+ toggleAutoFields(false);
+
+ // remove bound field listing again
+ removeBoundFieldListener();
+
+ if (m_bDBConnection && isListeningForChanges())
+ stopListening();
+
+ Reference< XPropertySet > xSet( m_xModelAsIndex, UNO_QUERY );
+ if ( m_bDBConnection && xSet.is() )
+ stopFormListening( xSet, false );
+
+ m_bDBConnection = false;
+ m_bCanInsert = m_bCanUpdate = m_bCycle = false;
+ m_bCurrentRecordModified = m_bCurrentRecordNew = m_bLocked = false;
+
+ m_pColumnInfoCache.reset();
+}
+
+
+void FormController::removeBoundFieldListener()
+{
+ for ( const Reference< XControl >& rControl : std::as_const(m_aControls) )
+ {
+ Reference< XPropertySet > xProp( rControl, UNO_QUERY );
+ if ( xProp.is() )
+ xProp->removePropertyChangeListener( FM_PROP_BOUNDFIELD, this );
+ }
+}
+
+
+void FormController::startFormListening( const Reference< XPropertySet >& _rxForm, bool _bPropertiesOnly )
+{
+ try
+ {
+ if ( m_bCanInsert || m_bCanUpdate ) // form can be modified
+ {
+ _rxForm->addPropertyChangeListener( FM_PROP_ISNEW, this );
+ _rxForm->addPropertyChangeListener( FM_PROP_ISMODIFIED, this );
+
+ if ( !_bPropertiesOnly )
+ {
+ // set the Listener for UI interaction
+ Reference< XRowSetApproveBroadcaster > xApprove( _rxForm, UNO_QUERY );
+ if ( xApprove.is() )
+ xApprove->addRowSetApproveListener( this );
+
+ // listener for row set changes
+ Reference< XRowSet > xRowSet( _rxForm, UNO_QUERY );
+ if ( xRowSet.is() )
+ xRowSet->addRowSetListener( this );
+ }
+ }
+
+ Reference< XPropertySetInfo > xInfo = _rxForm->getPropertySetInfo();
+ if ( xInfo.is() && xInfo->hasPropertyByName( FM_PROP_DYNAMIC_CONTROL_BORDER ) )
+ _rxForm->addPropertyChangeListener( FM_PROP_DYNAMIC_CONTROL_BORDER, this );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+
+void FormController::stopFormListening( const Reference< XPropertySet >& _rxForm, bool _bPropertiesOnly )
+{
+ try
+ {
+ if ( m_bCanInsert || m_bCanUpdate )
+ {
+ _rxForm->removePropertyChangeListener( FM_PROP_ISNEW, this );
+ _rxForm->removePropertyChangeListener( FM_PROP_ISMODIFIED, this );
+
+ if ( !_bPropertiesOnly )
+ {
+ Reference< XRowSetApproveBroadcaster > xApprove( _rxForm, UNO_QUERY );
+ if (xApprove.is())
+ xApprove->removeRowSetApproveListener(this);
+
+ Reference< XRowSet > xRowSet( _rxForm, UNO_QUERY );
+ if ( xRowSet.is() )
+ xRowSet->removeRowSetListener( this );
+ }
+ }
+
+ Reference< XPropertySetInfo > xInfo = _rxForm->getPropertySetInfo();
+ if ( xInfo.is() && xInfo->hasPropertyByName( FM_PROP_DYNAMIC_CONTROL_BORDER ) )
+ _rxForm->removePropertyChangeListener( FM_PROP_DYNAMIC_CONTROL_BORDER, this );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+// css::sdbc::XRowSetListener
+
+void FormController::cursorMoved(const EventObject& /*event*/)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ // toggle the locking ?
+ if (m_bLocked != determineLockState())
+ {
+ m_bLocked = !m_bLocked;
+ setLocks();
+ if (isListeningForChanges())
+ startListening();
+ else
+ stopListening();
+ }
+
+ // neither the current control nor the current record are modified anymore
+ m_bCurrentRecordModified = m_bModified = false;
+}
+
+
+void FormController::rowChanged(const EventObject& /*event*/)
+{
+ // not interested in ...
+}
+
+void FormController::rowSetChanged(const EventObject& /*event*/)
+{
+ // not interested in ...
+}
+
+
+// XContainerListener
+
+void SAL_CALL FormController::elementInserted(const ContainerEvent& evt)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ Reference< XControl > xControl( evt.Element, UNO_QUERY );
+ if ( !xControl.is() )
+ return;
+
+ Reference< XFormComponent > xModel(xControl->getModel(), UNO_QUERY);
+ if (xModel.is() && m_xModelAsIndex == xModel->getParent())
+ {
+ insertControl(xControl);
+
+ if ( m_aTabActivationIdle.IsActive() )
+ m_aTabActivationIdle.Stop();
+
+ m_aTabActivationIdle.Start();
+ }
+ // are we in filtermode and a XModeSelector has inserted an element
+ else if (m_bFiltering && Reference< XModeSelector > (evt.Source, UNO_QUERY).is())
+ {
+ xModel.set(evt.Source, UNO_QUERY);
+ if (xModel.is() && m_xModelAsIndex == xModel->getParent())
+ {
+ Reference< XPropertySet > xSet(xControl->getModel(), UNO_QUERY);
+ if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
+ {
+ // does the model use a bound field ?
+ Reference< XPropertySet > xField;
+ xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
+
+ Reference< XTextComponent > xText(xControl, UNO_QUERY);
+ // may we filter the field?
+ if (xText.is() && xField.is() && ::comphelper::hasProperty(FM_PROP_SEARCHABLE, xField) &&
+ ::comphelper::getBOOL(xField->getPropertyValue(FM_PROP_SEARCHABLE)))
+ {
+ m_aFilterComponents.push_back( xText );
+ xText->addTextListener( this );
+ }
+ }
+ }
+ }
+}
+
+
+void SAL_CALL FormController::elementReplaced(const ContainerEvent& evt)
+{
+ // simulate an elementRemoved
+ ContainerEvent aRemoveEvent( evt );
+ aRemoveEvent.Element = evt.ReplacedElement;
+ aRemoveEvent.ReplacedElement = Any();
+ elementRemoved( aRemoveEvent );
+
+ // simulate an elementInserted
+ ContainerEvent aInsertEvent( evt );
+ aInsertEvent.ReplacedElement = Any();
+ elementInserted( aInsertEvent );
+}
+
+
+void SAL_CALL FormController::elementRemoved(const ContainerEvent& evt)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ Reference< XControl > xControl;
+ evt.Element >>= xControl;
+ if (!xControl.is())
+ return;
+
+ Reference< XFormComponent > xModel(xControl->getModel(), UNO_QUERY);
+ if (xModel.is() && m_xModelAsIndex == xModel->getParent())
+ {
+ removeControl(xControl);
+ // Do not recalculate TabOrder, because it must already work internally!
+ }
+ // are we in filtermode and a XModeSelector has inserted an element
+ else if (m_bFiltering && Reference< XModeSelector > (evt.Source, UNO_QUERY).is())
+ {
+ FilterComponents::iterator componentPos = ::std::find(
+ m_aFilterComponents.begin(), m_aFilterComponents.end(), xControl );
+ if ( componentPos != m_aFilterComponents.end() )
+ m_aFilterComponents.erase( componentPos );
+ }
+}
+
+
+Reference< XControl > FormController::isInList(const Reference< XWindowPeer > & xPeer) const
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ const Reference< XControl >* pControls = m_aControls.getConstArray();
+
+ sal_uInt32 nCtrls = m_aControls.getLength();
+ for ( sal_uInt32 n = 0; n < nCtrls && xPeer.is(); ++n, ++pControls )
+ {
+ if ( pControls->is() )
+ {
+ Reference< XVclWindowPeer > xCtrlPeer( (*pControls)->getPeer(), UNO_QUERY);
+ if ( ( xCtrlPeer.get() == xPeer.get() ) || xCtrlPeer->isChild( xPeer ) )
+ return *pControls;
+ }
+ }
+ return Reference< XControl > ();
+}
+
+
+void FormController::activateFirst()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ DBG_ASSERT(m_xTabController.is(), "FormController::activateFirst : invalid aggregate !");
+ if (m_xTabController.is())
+ m_xTabController->activateFirst();
+}
+
+
+void FormController::activateLast()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ DBG_ASSERT(m_xTabController.is(), "FormController::activateLast : invalid aggregate !");
+ if (m_xTabController.is())
+ m_xTabController->activateLast();
+}
+
+// XFormController
+
+Reference< XFormOperations > SAL_CALL FormController::getFormOperations()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ return m_xFormOperations;
+}
+
+
+Reference< XControl> SAL_CALL FormController::getCurrentControl()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+ return m_xCurrentControl;
+}
+
+
+void SAL_CALL FormController::addActivateListener(const Reference< XFormControllerListener > & l)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+ m_aActivateListeners.addInterface(l);
+}
+
+void SAL_CALL FormController::removeActivateListener(const Reference< XFormControllerListener > & l)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+ m_aActivateListeners.removeInterface(l);
+}
+
+
+void SAL_CALL FormController::addChildController( const Reference< XFormController >& ChildController )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ if ( !ChildController.is() )
+ throw IllegalArgumentException( OUString(), *this, 1 );
+ // TODO: (localized) error message
+
+ // the parent of our (to-be-)child must be our own model
+ Reference< XFormComponent > xFormOfChild( ChildController->getModel(), UNO_QUERY );
+ if ( !xFormOfChild.is() )
+ throw IllegalArgumentException( OUString(), *this, 1 );
+ // TODO: (localized) error message
+
+ if ( xFormOfChild->getParent() != m_xModelAsIndex )
+ throw IllegalArgumentException( OUString(), *this, 1 );
+ // TODO: (localized) error message
+
+ m_aChildren.push_back( ChildController );
+ ChildController->setParent( *this );
+
+ // search the position of the model within the form
+ sal_uInt32 nPos = m_xModelAsIndex->getCount();
+ Reference< XFormComponent > xTemp;
+ for( ; nPos; )
+ {
+ m_xModelAsIndex->getByIndex(--nPos) >>= xTemp;
+ if ( xFormOfChild == xTemp )
+ {
+ m_xModelAsManager->attach( nPos, Reference<XInterface>( ChildController, UNO_QUERY ), Any( ChildController) );
+ break;
+ }
+ }
+}
+
+
+Reference< XFormControllerContext > SAL_CALL FormController::getContext()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+ return m_xFormControllerContext;
+}
+
+
+void SAL_CALL FormController::setContext( const Reference< XFormControllerContext >& _context )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+ m_xFormControllerContext = _context;
+}
+
+
+Reference< XInteractionHandler > SAL_CALL FormController::getInteractionHandler()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+ return m_xInteractionHandler;
+}
+
+
+void SAL_CALL FormController::setInteractionHandler( const Reference< XInteractionHandler >& _interactionHandler )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+ m_xInteractionHandler = _interactionHandler;
+}
+
+
+void FormController::setFilter(::std::vector<FmFieldInfo>& rFieldInfos)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ // create the composer
+ Reference< XRowSet > xForm(m_xModelAsIndex, UNO_QUERY);
+ Reference< XConnection > xConnection(getConnection(xForm));
+ if (xForm.is())
+ {
+ try
+ {
+ Reference< XMultiServiceFactory > xFactory( xConnection, UNO_QUERY_THROW );
+ m_xComposer.set(
+ xFactory->createInstance("com.sun.star.sdb.SingleSelectQueryComposer"),
+ UNO_QUERY_THROW );
+
+ Reference< XPropertySet > xSet( xForm, UNO_QUERY );
+ OUString sStatement = ::comphelper::getString( xSet->getPropertyValue( FM_PROP_ACTIVECOMMAND ) );
+ OUString sFilter = ::comphelper::getString( xSet->getPropertyValue( FM_PROP_FILTER ) );
+ m_xComposer->setElementaryQuery( sStatement );
+ m_xComposer->setFilter( sFilter );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+ if (m_xComposer.is())
+ {
+ const Sequence< Sequence < PropertyValue > > aFilterRows = m_xComposer->getStructuredFilter();
+
+ // ok, we receive the list of filters as sequence of fieldnames, value
+ // now we have to transform the fieldname into UI names, that could be a label of the field or
+ // an aliasname or the fieldname itself
+
+ // first adjust the field names if necessary
+ Reference< XNameAccess > xQueryColumns =
+ Reference< XColumnsSupplier >( m_xComposer, UNO_QUERY_THROW )->getColumns();
+
+ for (auto& rFieldInfo : rFieldInfos)
+ {
+ if ( xQueryColumns->hasByName(rFieldInfo.aFieldName) )
+ {
+ if ( (xQueryColumns->getByName(rFieldInfo.aFieldName) >>= rFieldInfo.xField) && rFieldInfo.xField.is() )
+ rFieldInfo.xField->getPropertyValue(FM_PROP_REALNAME) >>= rFieldInfo.aFieldName;
+ }
+ }
+
+ Reference< XDatabaseMetaData> xMetaData(xConnection->getMetaData());
+ // now transfer the filters into Value/TextComponent pairs
+ ::comphelper::UStringMixEqual aCompare(xMetaData->storesMixedCaseQuotedIdentifiers());
+
+ // need to parse criteria localized
+ Reference< XNumberFormatsSupplier> xFormatSupplier( getNumberFormats(xConnection, true));
+ Reference< XNumberFormatter> xFormatter = NumberFormatter::create(m_xComponentContext);
+ xFormatter->attachNumberFormatsSupplier(xFormatSupplier);
+ Locale aAppLocale = Application::GetSettings().GetUILanguageTag().getLocale();
+ const LocaleDataWrapper& rLocaleWrapper( Application::GetSettings().GetUILocaleDataWrapper() );
+ OUString strDecimalSeparator = rLocaleWrapper.getNumDecimalSep();
+
+ // retrieving the filter
+ for (const Sequence < PropertyValue >& rRow : aFilterRows)
+ {
+ FmFilterRow aRow;
+
+ // search a field for the given name
+ for (const PropertyValue& rRefValue : rRow)
+ {
+ // look for the text component
+ Reference< XPropertySet > xField;
+ try
+ {
+ Reference< XPropertySet > xSet;
+ OUString aRealName;
+
+ // first look with the given name
+ if (xQueryColumns->hasByName(rRefValue.Name))
+ {
+ xQueryColumns->getByName(rRefValue.Name) >>= xSet;
+
+ // get the RealName
+ xSet->getPropertyValue("RealName") >>= aRealName;
+
+ // compare the condition field name and the RealName
+ if (aCompare(aRealName, rRefValue.Name))
+ xField = xSet;
+ }
+ if (!xField.is())
+ {
+ // no we have to check every column to find the realname
+ Reference< XIndexAccess > xColumnsByIndex(xQueryColumns, UNO_QUERY);
+ for (sal_Int32 n = 0, nCount = xColumnsByIndex->getCount(); n < nCount; n++)
+ {
+ xColumnsByIndex->getByIndex(n) >>= xSet;
+ xSet->getPropertyValue("RealName") >>= aRealName;
+ if (aCompare(aRealName, rRefValue.Name))
+ {
+ // get the column by its alias
+ xField = xSet;
+ break;
+ }
+ }
+ }
+ if (!xField.is())
+ continue;
+ }
+ catch (const Exception&)
+ {
+ continue;
+ }
+
+ // find the text component
+ for (const auto& rFieldInfo : rFieldInfos)
+ {
+ // we found the field so insert a new entry to the filter row
+ if (rFieldInfo.xField == xField)
+ {
+ // do we already have the control ?
+ if (aRow.find(rFieldInfo.xText) != aRow.end())
+ {
+ OString aVal = m_pParser->getContext().getIntlKeywordAscii(IParseContext::InternationalKeyCode::And);
+ OUString aCompText = aRow[rFieldInfo.xText] + " " +
+ OStringToOUString(aVal, RTL_TEXTENCODING_ASCII_US) + " " +
+ ::comphelper::getString(rRefValue.Value);
+ aRow[rFieldInfo.xText] = aCompText;
+ }
+ else
+ {
+ OUString sPredicate,sErrorMsg;
+ rRefValue.Value >>= sPredicate;
+ std::unique_ptr< OSQLParseNode > pParseNode = predicateTree(sErrorMsg, sPredicate, xFormatter, xField);
+ if ( pParseNode != nullptr )
+ {
+ OUString sCriteria;
+ switch (rRefValue.Handle)
+ {
+ case css::sdb::SQLFilterOperator::EQUAL:
+ sCriteria += "=";
+ break;
+ case css::sdb::SQLFilterOperator::NOT_EQUAL:
+ sCriteria += "!=";
+ break;
+ case css::sdb::SQLFilterOperator::LESS:
+ sCriteria += "<";
+ break;
+ case css::sdb::SQLFilterOperator::GREATER:
+ sCriteria += ">";
+ break;
+ case css::sdb::SQLFilterOperator::LESS_EQUAL:
+ sCriteria += "<=";
+ break;
+ case css::sdb::SQLFilterOperator::GREATER_EQUAL:
+ sCriteria += ">=";
+ break;
+ case css::sdb::SQLFilterOperator::LIKE:
+ sCriteria += "LIKE ";
+ break;
+ case css::sdb::SQLFilterOperator::NOT_LIKE:
+ sCriteria += "NOT LIKE ";
+ break;
+ case css::sdb::SQLFilterOperator::SQLNULL:
+ sCriteria += "IS NULL";
+ break;
+ case css::sdb::SQLFilterOperator::NOT_SQLNULL:
+ sCriteria += "IS NOT NULL";
+ break;
+ }
+ pParseNode->parseNodeToPredicateStr( sCriteria
+ ,xConnection
+ ,xFormatter
+ ,xField
+ ,OUString()
+ ,aAppLocale
+ ,strDecimalSeparator
+ ,getParseContext());
+ aRow[rFieldInfo.xText] = sCriteria;
+ }
+ }
+ }
+ }
+ }
+
+ if (aRow.empty())
+ continue;
+
+ impl_addFilterRow( aRow );
+ }
+ }
+
+ // now set the filter controls
+ for (const auto& rFieldInfo : rFieldInfos)
+ {
+ m_aFilterComponents.push_back( rFieldInfo.xText );
+ }
+}
+
+
+void FormController::startFiltering()
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+
+ Reference< XConnection > xConnection( getConnection( Reference< XRowSet >( m_xModelAsIndex, UNO_QUERY ) ) );
+ if ( !xConnection.is() )
+ // nothing to do - can't filter a form which is not connected
+ return;
+
+ // stop listening for controls
+ if (isListeningForChanges())
+ stopListening();
+
+ m_bFiltering = true;
+
+ // as we don't want new controls to be attached to the scripting environment
+ // we change attach flags
+ m_bAttachEvents = false;
+
+ // exchanging the controls for the current form
+ Sequence< Reference< XControl > > aControlsCopy( m_aControls );
+ const Reference< XControl >* pControls = aControlsCopy.getConstArray();
+ sal_Int32 nControlCount = aControlsCopy.getLength();
+
+ // the control we have to activate after replacement
+ Reference< XNumberFormatsSupplier > xFormatSupplier = getNumberFormats(xConnection, true);
+ Reference< XNumberFormatter > xFormatter = NumberFormatter::create(m_xComponentContext);
+ xFormatter->attachNumberFormatsSupplier(xFormatSupplier);
+
+ // structure for storing the field info
+ ::std::vector<FmFieldInfo> aFieldInfos;
+
+ for (sal_Int32 i = nControlCount; i > 0;)
+ {
+ Reference< XControl > xControl = pControls[--i];
+ if (xControl.is())
+ {
+ // no events for the control anymore
+ removeFromEventAttacher(xControl);
+
+ // do we have a mode selector
+ Reference< XModeSelector > xSelector(xControl, UNO_QUERY);
+ if (xSelector.is())
+ {
+ xSelector->setMode( "FilterMode" );
+
+ // listening for new controls of the selector
+ Reference< XContainer > xContainer(xSelector, UNO_QUERY);
+ if (xContainer.is())
+ xContainer->addContainerListener(this);
+
+ Reference< XEnumerationAccess > xElementAccess(xSelector, UNO_QUERY);
+ if (xElementAccess.is())
+ {
+ Reference< XEnumeration > xEnumeration(xElementAccess->createEnumeration());
+ Reference< XControl > xSubControl;
+ while (xEnumeration->hasMoreElements())
+ {
+ xEnumeration->nextElement() >>= xSubControl;
+ if (xSubControl.is())
+ {
+ Reference< XPropertySet > xSet(xSubControl->getModel(), UNO_QUERY);
+ if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
+ {
+ // does the model use a bound field ?
+ Reference< XPropertySet > xField;
+ xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
+
+ Reference< XTextComponent > xText(xSubControl, UNO_QUERY);
+ // may we filter the field?
+ if (xText.is() && xField.is() && ::comphelper::hasProperty(FM_PROP_SEARCHABLE, xField) &&
+ ::comphelper::getBOOL(xField->getPropertyValue(FM_PROP_SEARCHABLE)))
+ {
+ aFieldInfos.emplace_back(xField, xText);
+ xText->addTextListener(this);
+ }
+ }
+ }
+ }
+ }
+ continue;
+ }
+
+ Reference< XPropertySet > xModel( xControl->getModel(), UNO_QUERY );
+ if (xModel.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xModel))
+ {
+ // does the model use a bound field ?
+ Any aVal = xModel->getPropertyValue(FM_PROP_BOUNDFIELD);
+ Reference< XPropertySet > xField;
+ aVal >>= xField;
+
+ // may we filter the field?
+
+ if ( xField.is()
+ && ::comphelper::hasProperty( FM_PROP_SEARCHABLE, xField )
+ && ::comphelper::getBOOL( xField->getPropertyValue( FM_PROP_SEARCHABLE ) )
+ )
+ {
+ // create a filter control
+ Reference< XControl > xFilterControl = form::control::FilterControl::createWithFormat(
+ m_xComponentContext,
+ getDialogParentWindow(this),
+ xFormatter,
+ xModel);
+
+ if ( replaceControl( xControl, xFilterControl ) )
+ {
+ Reference< XTextComponent > xFilterText( xFilterControl, UNO_QUERY );
+ aFieldInfos.emplace_back( xField, xFilterText );
+ xFilterText->addTextListener(this);
+ }
+ }
+ }
+ else
+ {
+ // unsubscribe from EventManager
+ }
+ }
+ }
+
+ // we have all filter controls now, so the next step is to read the filters from the form
+ // resolve all aliases and set the current filter to the according structure
+ setFilter(aFieldInfos);
+
+ Reference< XPropertySet > xSet( m_xModelAsIndex, UNO_QUERY );
+ if ( xSet.is() )
+ stopFormListening( xSet, true );
+
+ impl_setTextOnAllFilter_throw();
+
+ // lock all controls which are not used for filtering
+ m_bLocked = determineLockState();
+ setLocks();
+ m_bAttachEvents = true;
+}
+
+
+void FormController::stopFiltering()
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ if ( !m_bFiltering ) // #104693# OJ
+ { // nothing to do
+ return;
+ }
+
+ m_bFiltering = false;
+ m_bDetachEvents = false;
+
+ ::comphelper::disposeComponent(m_xComposer);
+
+ // exchanging the controls for the current form
+ Sequence< Reference< XControl > > aControlsCopy( m_aControls );
+ const Reference< XControl > * pControls = aControlsCopy.getConstArray();
+ sal_Int32 nControlCount = aControlsCopy.getLength();
+
+ // clear the filter control map
+ ::std::for_each( m_aFilterComponents.begin(), m_aFilterComponents.end(), RemoveComponentTextListener( this ) );
+ m_aFilterComponents.clear();
+
+ for ( sal_Int32 i = nControlCount; i > 0; )
+ {
+ Reference< XControl > xControl = pControls[--i];
+ if (xControl.is())
+ {
+ // now enable event handling again
+ addToEventAttacher(xControl);
+
+ Reference< XModeSelector > xSelector(xControl, UNO_QUERY);
+ if (xSelector.is())
+ {
+ xSelector->setMode( "DataMode" );
+
+ // listening for new controls of the selector
+ Reference< XContainer > xContainer(xSelector, UNO_QUERY);
+ if (xContainer.is())
+ xContainer->removeContainerListener(this);
+ continue;
+ }
+
+ Reference< XPropertySet > xSet(xControl->getModel(), UNO_QUERY);
+ if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
+ {
+ // does the model use a bound field ?
+ Reference< XPropertySet > xField;
+ xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
+
+ // may we filter the field?
+ if ( xField.is()
+ && ::comphelper::hasProperty( FM_PROP_SEARCHABLE, xField )
+ && ::comphelper::getBOOL( xField->getPropertyValue( FM_PROP_SEARCHABLE ) )
+ )
+ {
+ OUString sServiceName;
+ OSL_VERIFY( xSet->getPropertyValue( FM_PROP_DEFAULTCONTROL ) >>= sServiceName );
+ Reference< XControl > xNewControl( m_xComponentContext->getServiceManager()->createInstanceWithContext( sServiceName, m_xComponentContext ), UNO_QUERY );
+ replaceControl( xControl, xNewControl );
+ }
+ }
+ }
+ }
+
+ Reference< XPropertySet > xSet( m_xModelAsIndex, UNO_QUERY );
+ if ( xSet.is() )
+ startFormListening( xSet, true );
+
+ m_bDetachEvents = true;
+
+ m_aFilterRows.clear();
+ m_nCurrentFilterPosition = -1;
+
+ // release the locks if possible
+ // lock all controls which are not used for filtering
+ m_bLocked = determineLockState();
+ setLocks();
+
+ // restart listening for control modifications
+ if (isListeningForChanges())
+ startListening();
+}
+
+// XModeSelector
+
+void FormController::setMode(const OUString& Mode)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ if (!supportsMode(Mode))
+ throw NoSupportException();
+
+ if (Mode == m_aMode)
+ return;
+
+ m_aMode = Mode;
+
+ if ( Mode == "FilterMode" )
+ startFiltering();
+ else
+ stopFiltering();
+
+ for (const auto& rChild : m_aChildren)
+ {
+ Reference< XModeSelector > xMode(rChild, UNO_QUERY);
+ if ( xMode.is() )
+ xMode->setMode(Mode);
+ }
+}
+
+
+OUString SAL_CALL FormController::getMode()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ return m_aMode;
+}
+
+
+Sequence< OUString > SAL_CALL FormController::getSupportedModes()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ static Sequence< OUString > const aModes
+ {
+ "DataMode",
+ "FilterMode"
+ };
+ return aModes;
+}
+
+sal_Bool SAL_CALL FormController::supportsMode(const OUString& Mode)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ Sequence< OUString > aModes(getSupportedModes());
+ return comphelper::findValue(aModes, Mode) != -1;
+}
+
+css::uno::Reference<css::awt::XWindow> FormController::getDialogParentWindow(css::uno::Reference<css::form::runtime::XFormController> xFormController)
+{
+ try
+ {
+ Reference< XControl > xContainerControl( xFormController->getContainer(), UNO_QUERY_THROW );
+ Reference<XWindow> xContainerWindow(xContainerControl->getPeer(), UNO_QUERY_THROW);
+ return xContainerWindow;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return nullptr;
+}
+
+bool FormController::checkFormComponentValidity( OUString& /* [out] */ _rFirstInvalidityExplanation, Reference< XControlModel >& /* [out] */ _rxFirstInvalidModel )
+{
+ try
+ {
+ Reference< XEnumerationAccess > xControlEnumAcc( getModel(), UNO_QUERY );
+ Reference< XEnumeration > xControlEnumeration;
+ if ( xControlEnumAcc.is() )
+ xControlEnumeration = xControlEnumAcc->createEnumeration();
+ OSL_ENSURE( xControlEnumeration.is(), "FormController::checkFormComponentValidity: cannot enumerate the controls!" );
+ if ( !xControlEnumeration.is() )
+ // assume all valid
+ return true;
+
+ Reference< XValidatableFormComponent > xValidatable;
+ while ( xControlEnumeration->hasMoreElements() )
+ {
+ if ( !( xControlEnumeration->nextElement() >>= xValidatable ) )
+ // control does not support validation
+ continue;
+
+ if ( xValidatable->isValid() )
+ continue;
+
+ Reference< XValidator > xValidator( xValidatable->getValidator() );
+ OSL_ENSURE( xValidator.is(), "FormController::checkFormComponentValidity: invalid, but no validator?" );
+ if ( !xValidator.is() )
+ // this violates the interface definition of css.form.validation.XValidatableFormComponent ...
+ continue;
+
+ _rFirstInvalidityExplanation = xValidator->explainInvalid( xValidatable->getCurrentValue() );
+ _rxFirstInvalidModel.set(xValidatable, css::uno::UNO_QUERY);
+ return false;
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return true;
+}
+
+
+Reference< XControl > FormController::locateControl( const Reference< XControlModel >& _rxModel )
+{
+ try
+ {
+ const Sequence< Reference< XControl > > aControls( getControls() );
+
+ for ( auto const & control : aControls )
+ {
+ OSL_ENSURE( control.is(), "FormController::locateControl: NULL-control?" );
+ if ( control.is() )
+ {
+ if ( control->getModel() == _rxModel )
+ return control;
+ }
+ }
+ OSL_FAIL( "FormController::locateControl: did not find a control for this model!" );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return nullptr;
+}
+
+
+namespace
+{
+ void displayErrorSetFocus(const OUString& _rMessage, const Reference<XControl>& _rxFocusControl,
+ const css::uno::Reference<css::awt::XWindow>& rDialogParent)
+ {
+ SQLContext aError(SvxResId(RID_STR_WRITEERROR), {}, {}, 0, {}, _rMessage);
+ displayException(aError, rDialogParent);
+
+ if ( _rxFocusControl.is() )
+ {
+ Reference< XWindow > xControlWindow( _rxFocusControl, UNO_QUERY );
+ OSL_ENSURE( xControlWindow.is(), "displayErrorSetFocus: invalid control!" );
+ if ( xControlWindow.is() )
+ xControlWindow->setFocus();
+ }
+ }
+
+ bool lcl_shouldValidateRequiredFields_nothrow( const Reference< XInterface >& _rxForm )
+ {
+ try
+ {
+ static constexpr OUString s_sFormsCheckRequiredFields = u"FormsCheckRequiredFields"_ustr;
+
+ // first, check whether the form has a property telling us the answer
+ // this allows people to use the XPropertyContainer interface of a form to control
+ // the behaviour on a per-form basis.
+ Reference< XPropertySet > xFormProps( _rxForm, UNO_QUERY_THROW );
+ Reference< XPropertySetInfo > xPSI( xFormProps->getPropertySetInfo() );
+ if ( xPSI->hasPropertyByName( s_sFormsCheckRequiredFields ) )
+ {
+ bool bShouldValidate = true;
+ OSL_VERIFY( xFormProps->getPropertyValue( s_sFormsCheckRequiredFields ) >>= bShouldValidate );
+ return bShouldValidate;
+ }
+
+ // next, check the data source which created the connection
+ Reference< XChild > xConnectionAsChild( xFormProps->getPropertyValue( FM_PROP_ACTIVE_CONNECTION ), UNO_QUERY_THROW );
+ Reference< XPropertySet > xDataSource( xConnectionAsChild->getParent(), UNO_QUERY );
+ if ( !xDataSource.is() )
+ // seldom (but possible): this is not a connection created by a data source
+ return true;
+
+ Reference< XPropertySet > xDataSourceSettings(
+ xDataSource->getPropertyValue("Settings"),
+ UNO_QUERY_THROW );
+
+ bool bShouldValidate = true;
+ OSL_VERIFY( xDataSourceSettings->getPropertyValue( s_sFormsCheckRequiredFields ) >>= bShouldValidate );
+ return bShouldValidate;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ return true;
+ }
+}
+
+// XRowSetApproveListener
+
+sal_Bool SAL_CALL FormController::approveRowChange(const RowChangeEvent& _rEvent)
+{
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ ::comphelper::OInterfaceIteratorHelper3 aIter(m_aRowSetApproveListeners);
+ bool bValid = true;
+ if (aIter.hasMoreElements())
+ {
+ RowChangeEvent aEvt( _rEvent );
+ aEvt.Source = *this;
+ bValid = aIter.next()->approveRowChange(aEvt);
+ }
+
+ if ( !bValid )
+ return bValid;
+
+ if ( ( _rEvent.Action != RowChangeAction::INSERT )
+ && ( _rEvent.Action != RowChangeAction::UPDATE )
+ )
+ return bValid;
+
+ // if some of the control models are bound to validators, check them
+ OUString sInvalidityExplanation;
+ Reference< XControlModel > xInvalidModel;
+ if ( !checkFormComponentValidity( sInvalidityExplanation, xInvalidModel ) )
+ {
+ Reference< XControl > xControl( locateControl( xInvalidModel ) );
+ aGuard.clear();
+ displayErrorSetFocus( sInvalidityExplanation, xControl, getDialogParentWindow(this) );
+ return false;
+ }
+
+ // check values on NULL and required flag
+ if ( !lcl_shouldValidateRequiredFields_nothrow( _rEvent.Source ) )
+ return true;
+
+ OSL_ENSURE(m_pColumnInfoCache, "FormController::approveRowChange: no column infos!");
+ if (!m_pColumnInfoCache)
+ return true;
+
+ try
+ {
+ if ( !m_pColumnInfoCache->controlsInitialized() )
+ m_pColumnInfoCache->initializeControls( getControls() );
+
+ size_t colCount = m_pColumnInfoCache->getColumnCount();
+ for ( size_t col = 0; col < colCount; ++col )
+ {
+ const ColumnInfo& rColInfo = m_pColumnInfoCache->getColumnInfo( col );
+
+ if ( rColInfo.bAutoIncrement )
+ continue;
+
+ if ( rColInfo.bReadOnly )
+ continue;
+
+ if ( !rColInfo.xFirstControlWithInputRequired.is() && !rColInfo.xFirstGridWithInputRequiredColumn.is() )
+ {
+ continue;
+ }
+
+ // TODO: in case of binary fields, this "getString" below is extremely expensive
+ if ( !rColInfo.xColumn->getString().isEmpty() || !rColInfo.xColumn->wasNull() )
+ continue;
+
+ OUString sMessage( SvxResId( RID_ERR_FIELDREQUIRED ) );
+ sMessage = sMessage.replaceFirst( "#", rColInfo.sName );
+
+ // the control to focus
+ Reference< XControl > xControl( rColInfo.xFirstControlWithInputRequired );
+ if ( !xControl.is() )
+ xControl.set( rColInfo.xFirstGridWithInputRequiredColumn, UNO_QUERY );
+
+ aGuard.clear();
+ displayErrorSetFocus( sMessage, rColInfo.xFirstControlWithInputRequired, getDialogParentWindow(this) );
+ return false;
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ return true;
+}
+
+
+sal_Bool SAL_CALL FormController::approveCursorMove(const EventObject& event)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ ::comphelper::OInterfaceIteratorHelper3 aIter(m_aRowSetApproveListeners);
+ if (aIter.hasMoreElements())
+ {
+ EventObject aEvt(event);
+ aEvt.Source = *this;
+ return aIter.next()->approveCursorMove(aEvt);
+ }
+
+ return true;
+}
+
+
+sal_Bool SAL_CALL FormController::approveRowSetChange(const EventObject& event)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ ::comphelper::OInterfaceIteratorHelper3 aIter(m_aRowSetApproveListeners);
+ if (aIter.hasMoreElements())
+ {
+ EventObject aEvt(event);
+ aEvt.Source = *this;
+ return aIter.next()->approveRowSetChange(aEvt);
+ }
+
+ return true;
+}
+
+// XRowSetApproveBroadcaster
+
+void SAL_CALL FormController::addRowSetApproveListener(const Reference< XRowSetApproveListener > & _rxListener)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ m_aRowSetApproveListeners.addInterface(_rxListener);
+}
+
+
+void SAL_CALL FormController::removeRowSetApproveListener(const Reference< XRowSetApproveListener > & _rxListener)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ m_aRowSetApproveListeners.removeInterface(_rxListener);
+}
+
+// XErrorListener
+
+void SAL_CALL FormController::errorOccured(const SQLErrorEvent& aEvent)
+{
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ ::comphelper::OInterfaceIteratorHelper3 aIter(m_aErrorListeners);
+ if (aIter.hasMoreElements())
+ {
+ SQLErrorEvent aEvt(aEvent);
+ aEvt.Source = *this;
+ aIter.next()->errorOccured(aEvt);
+ }
+ else
+ {
+ aGuard.clear();
+ displayException(aEvent, getDialogParentWindow(this));
+ }
+}
+
+// XErrorBroadcaster
+
+void SAL_CALL FormController::addSQLErrorListener(const Reference< XSQLErrorListener > & aListener)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ m_aErrorListeners.addInterface(aListener);
+}
+
+
+void SAL_CALL FormController::removeSQLErrorListener(const Reference< XSQLErrorListener > & aListener)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ m_aErrorListeners.removeInterface(aListener);
+}
+
+// XDatabaseParameterBroadcaster2
+
+void SAL_CALL FormController::addDatabaseParameterListener(const Reference< XDatabaseParameterListener > & aListener)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ m_aParameterListeners.addInterface(aListener);
+}
+
+
+void SAL_CALL FormController::removeDatabaseParameterListener(const Reference< XDatabaseParameterListener > & aListener)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ m_aParameterListeners.removeInterface(aListener);
+}
+
+// XDatabaseParameterBroadcaster
+
+void SAL_CALL FormController::addParameterListener(const Reference< XDatabaseParameterListener > & aListener)
+{
+ FormController::addDatabaseParameterListener( aListener );
+}
+
+
+void SAL_CALL FormController::removeParameterListener(const Reference< XDatabaseParameterListener > & aListener)
+{
+ FormController::removeDatabaseParameterListener( aListener );
+}
+
+// XDatabaseParameterListener
+
+sal_Bool SAL_CALL FormController::approveParameter(const DatabaseParameterEvent& aEvent)
+{
+ SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ ::comphelper::OInterfaceIteratorHelper3 aIter(m_aParameterListeners);
+ if (aIter.hasMoreElements())
+ {
+ DatabaseParameterEvent aEvt(aEvent);
+ aEvt.Source = *this;
+ return aIter.next()->approveParameter(aEvt);
+ }
+ else
+ {
+ // default handling: instantiate an interaction handler and let it handle the parameter request
+ try
+ {
+ if ( !ensureInteractionHandler() )
+ return false;
+
+ // two continuations allowed: OK and Cancel
+ rtl::Reference<OParameterContinuation> pParamValues = new OParameterContinuation;
+ rtl::Reference<OInteractionAbort> pAbort = new OInteractionAbort;
+ // the request
+ ParametersRequest aRequest;
+ aRequest.Parameters = aEvent.Parameters;
+ aRequest.Connection = getConnection(Reference< XRowSet >(aEvent.Source, UNO_QUERY));
+ rtl::Reference<OInteractionRequest> pParamRequest = new OInteractionRequest(Any(aRequest));
+ // some knittings
+ pParamRequest->addContinuation(pParamValues);
+ pParamRequest->addContinuation(pAbort);
+
+ // handle the request
+ m_xInteractionHandler->handle(pParamRequest);
+
+ if (!pParamValues->wasSelected())
+ // canceled
+ return false;
+
+ // transfer the values into the parameter supplier
+ Sequence< PropertyValue > aFinalValues = pParamValues->getValues();
+ if (aFinalValues.getLength() != aRequest.Parameters->getCount())
+ {
+ OSL_FAIL("FormController::approveParameter: the InteractionHandler returned nonsense!");
+ return false;
+ }
+ const PropertyValue* pFinalValues = aFinalValues.getConstArray();
+ for (sal_Int32 i=0; i<aFinalValues.getLength(); ++i, ++pFinalValues)
+ {
+ Reference< XPropertySet > xParam(
+ aRequest.Parameters->getByIndex(i), css::uno::UNO_QUERY);
+ if (xParam.is())
+ {
+#ifdef DBG_UTIL
+ OUString sName;
+ xParam->getPropertyValue(FM_PROP_NAME) >>= sName;
+ DBG_ASSERT(sName == pFinalValues->Name, "FormController::approveParameter: suspicious value names!");
+#endif
+ try { xParam->setPropertyValue(FM_PROP_VALUE, pFinalValues->Value); }
+ catch(Exception&)
+ {
+ OSL_FAIL("FormController::approveParameter: setting one of the properties failed!");
+ }
+ }
+ }
+ }
+ catch(Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ return true;
+}
+
+// XConfirmDeleteBroadcaster
+
+void SAL_CALL FormController::addConfirmDeleteListener(const Reference< XConfirmDeleteListener > & aListener)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ m_aDeleteListeners.addInterface(aListener);
+}
+
+
+void SAL_CALL FormController::removeConfirmDeleteListener(const Reference< XConfirmDeleteListener > & aListener)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ m_aDeleteListeners.removeInterface(aListener);
+}
+
+// XConfirmDeleteListener
+
+sal_Bool SAL_CALL FormController::confirmDelete(const RowChangeEvent& aEvent)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ ::comphelper::OInterfaceIteratorHelper3 aIter(m_aDeleteListeners);
+ if (aIter.hasMoreElements())
+ {
+ RowChangeEvent aEvt(aEvent);
+ aEvt.Source = *this;
+ return aIter.next()->confirmDelete(aEvt);
+ }
+ // default handling: instantiate an interaction handler and let it handle the request
+
+ OUString sTitle;
+ sal_Int32 nLength = aEvent.Rows;
+ if ( nLength > 1 )
+ {
+ sTitle = SvxResId( RID_STR_DELETECONFIRM_RECORDS );
+ sTitle = sTitle.replaceFirst( "#", OUString::number(nLength) );
+ }
+ else
+ sTitle = SvxResId( RID_STR_DELETECONFIRM_RECORD );
+
+ try
+ {
+ if ( !ensureInteractionHandler() )
+ return false;
+
+ // two continuations allowed: Yes and No
+ rtl::Reference<OInteractionApprove> pApprove = new OInteractionApprove;
+ rtl::Reference<OInteractionDisapprove> pDisapprove = new OInteractionDisapprove;
+
+ // the request
+ SQLWarning aDetails(SvxResId(RID_STR_DELETECONFIRM), {}, {}, 0, {});
+ SQLWarning aWarning(sTitle, {}, {}, 0, css::uno::Any(aDetails));
+
+ rtl::Reference<OInteractionRequest> pRequest = new OInteractionRequest( Any( aWarning ) );
+
+ // some knittings
+ pRequest->addContinuation( pApprove );
+ pRequest->addContinuation( pDisapprove );
+
+ // handle the request
+ m_xInteractionHandler->handle( pRequest );
+
+ if ( pApprove->wasSelected() )
+ return true;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ return false;
+}
+
+
+void SAL_CALL FormController::invalidateFeatures( const Sequence< ::sal_Int16 >& Features )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ // for now, just copy the ids of the features, because...
+ m_aInvalidFeatures.insert( Features.begin(), Features.end() );
+
+ // ... we will do the real invalidation asynchronously
+ if ( !m_aFeatureInvalidationTimer.IsActive() )
+ m_aFeatureInvalidationTimer.Start();
+}
+
+
+void SAL_CALL FormController::invalidateAllFeatures( )
+{
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+
+ Sequence< sal_Int16 > aInterceptedFeatures( comphelper::mapKeysToSequence(m_aFeatureDispatchers) );
+
+ aGuard.clear();
+ if ( aInterceptedFeatures.hasElements() )
+ invalidateFeatures( aInterceptedFeatures );
+}
+
+
+Reference< XDispatch >
+FormController::interceptedQueryDispatch( const URL& aURL,
+ const OUString& /*aTargetFrameName*/, sal_Int32 /*nSearchFlags*/)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ Reference< XDispatch > xReturn;
+ // dispatches handled by ourself
+ if ( ( aURL.Complete == FMURL_CONFIRM_DELETION )
+ || ( ( aURL.Complete == "private:/InteractionHandler" )
+ && ensureInteractionHandler()
+ )
+ )
+ xReturn = static_cast< XDispatch* >( this );
+
+ // dispatches of FormSlot-URLs we have to translate
+ if ( !xReturn.is() && m_xFormOperations.is() )
+ {
+ // find the slot id which corresponds to the URL
+ sal_Int32 nFeatureSlotId = svx::FeatureSlotTranslation::getControllerFeatureSlotIdForURL( aURL.Main );
+ sal_Int16 nFormFeature = ( nFeatureSlotId != -1 ) ? svx::FeatureSlotTranslation::getFormFeatureForSlotId( nFeatureSlotId ) : -1;
+ if ( nFormFeature > 0 )
+ {
+ // get the dispatcher for this feature, create if necessary
+ DispatcherContainer::const_iterator aDispatcherPos = m_aFeatureDispatchers.find( nFormFeature );
+ if ( aDispatcherPos == m_aFeatureDispatchers.end() )
+ {
+ aDispatcherPos = m_aFeatureDispatchers.emplace(
+ nFormFeature, new svx::OSingleFeatureDispatcher( aURL, nFormFeature, m_xFormOperations, m_aMutex )
+ ).first;
+ }
+
+ OSL_ENSURE( aDispatcherPos->second.is(), "FormController::interceptedQueryDispatch: should have a dispatcher by now!" );
+ return aDispatcherPos->second;
+ }
+ }
+
+ // no more to offer
+ return xReturn;
+}
+
+
+void SAL_CALL FormController::dispatch( const URL& _rURL, const Sequence< PropertyValue >& _rArgs )
+{
+ if ( _rArgs.getLength() != 1 )
+ {
+ OSL_FAIL( "FormController::dispatch: no arguments -> no dispatch!" );
+ return;
+ }
+
+ if ( _rURL.Complete == "private:/InteractionHandler" )
+ {
+ Reference< XInteractionRequest > xRequest;
+ OSL_VERIFY( _rArgs[0].Value >>= xRequest );
+ if ( xRequest.is() )
+ handle( xRequest );
+ return;
+ }
+
+ if ( _rURL.Complete == FMURL_CONFIRM_DELETION )
+ {
+ OSL_FAIL( "FormController::dispatch: How do you expect me to return something via this call?" );
+ // confirmDelete has a return value - dispatch hasn't
+ return;
+ }
+
+ OSL_FAIL( "FormController::dispatch: unknown URL!" );
+}
+
+
+void SAL_CALL FormController::addStatusListener( const Reference< XStatusListener >& _rxListener, const URL& _rURL )
+{
+ if (_rURL.Complete == FMURL_CONFIRM_DELETION)
+ {
+ if (_rxListener.is())
+ { // send an initial statusChanged event
+ FeatureStateEvent aEvent;
+ aEvent.FeatureURL = _rURL;
+ aEvent.IsEnabled = true;
+ _rxListener->statusChanged(aEvent);
+ // and don't add the listener at all (the status will never change)
+ }
+ }
+ else
+ OSL_FAIL("FormController::addStatusListener: invalid (unsupported) URL!");
+}
+
+
+Reference< XInterface > SAL_CALL FormController::getParent()
+{
+ return m_xParent;
+}
+
+
+void SAL_CALL FormController::setParent( const Reference< XInterface >& Parent)
+{
+ m_xParent = Parent;
+}
+
+
+void SAL_CALL FormController::removeStatusListener( const Reference< XStatusListener >& /*_rxListener*/, const URL& _rURL )
+{
+ OSL_ENSURE(_rURL.Complete == FMURL_CONFIRM_DELETION, "FormController::removeStatusListener: invalid (unsupported) URL!");
+ // we never really added the listener, so we don't need to remove it
+}
+
+
+Reference< XDispatchProviderInterceptor > FormController::createInterceptor(const Reference< XDispatchProviderInterception > & _xInterception)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+#ifdef DBG_UTIL
+ // check if we already have an interceptor for the given object
+ for ( const auto & it : m_aControlDispatchInterceptors )
+ {
+ if (it->getIntercepted() == _xInterception)
+ OSL_FAIL("FormController::createInterceptor : we already do intercept this objects dispatches !");
+ }
+#endif
+
+ rtl::Reference<DispatchInterceptionMultiplexer> pInterceptor(new DispatchInterceptionMultiplexer( _xInterception, this ));
+ m_aControlDispatchInterceptors.push_back( pInterceptor );
+
+ return pInterceptor;
+}
+
+
+bool FormController::ensureInteractionHandler()
+{
+ if ( m_xInteractionHandler.is() )
+ return true;
+ if ( m_bAttemptedHandlerCreation )
+ return false;
+ m_bAttemptedHandlerCreation = true;
+
+ m_xInteractionHandler = InteractionHandler::createWithParent(m_xComponentContext,
+ getDialogParentWindow(this));
+ return m_xInteractionHandler.is();
+}
+
+
+void SAL_CALL FormController::handle( const Reference< XInteractionRequest >& _rRequest )
+{
+ if ( !ensureInteractionHandler() )
+ return;
+ m_xInteractionHandler->handle( _rRequest );
+}
+
+
+void FormController::deleteInterceptor(const Reference< XDispatchProviderInterception > & _xInterception)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ // search the interceptor responsible for the given object
+ auto aIter = std::find_if(m_aControlDispatchInterceptors.begin(), m_aControlDispatchInterceptors.end(),
+ [&_xInterception](const rtl::Reference<DispatchInterceptionMultiplexer>& rpInterceptor) {
+ return rpInterceptor->getIntercepted() == _xInterception;
+ });
+ if (aIter != m_aControlDispatchInterceptors.end())
+ {
+ // log off the interception from its interception object
+ (*aIter)->dispose();
+ // remove the interceptor from our array
+ m_aControlDispatchInterceptors.erase(aIter);
+ }
+}
+
+
+void FormController::implInvalidateCurrentControlDependentFeatures()
+{
+ Sequence< sal_Int16 > aCurrentControlDependentFeatures
+ {
+ FormFeature::SortAscending,
+ FormFeature::SortDescending,
+ FormFeature::AutoFilter,
+ FormFeature::RefreshCurrentControl
+ };
+
+ invalidateFeatures( aCurrentControlDependentFeatures );
+}
+
+
+void SAL_CALL FormController::columnChanged( const EventObject& /*_event*/ )
+{
+ implInvalidateCurrentControlDependentFeatures();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/formcontrolling.cxx b/svx/source/form/formcontrolling.cxx
new file mode 100644
index 0000000000..224a88112e
--- /dev/null
+++ b/svx/source/form/formcontrolling.cxx
@@ -0,0 +1,497 @@
+/* -*- 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 <sal/macros.h>
+#include <formcontrolling.hxx>
+#include <fmurl.hxx>
+#include <svx/svxids.hrc>
+#include <fmprop.hxx>
+#include <formcontroller.hxx>
+#include <svx/fmtools.hxx>
+
+#include <com/sun/star/form/runtime/FormOperations.hpp>
+#include <com/sun/star/form/runtime/FormFeature.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+
+#include <comphelper/diagnose_ex.hxx>
+#include <comphelper/anytostring.hxx>
+#include <comphelper/processfactory.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <osl/diagnose.h>
+
+#include <algorithm>
+
+
+namespace svx
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::form::runtime::XFormController;
+ using ::com::sun::star::form::runtime::FormOperations;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::sdbc::XRowSet;
+ using ::com::sun::star::form::runtime::FeatureState;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::beans::NamedValue;
+ using ::com::sun::star::beans::XPropertySet;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::sdbc::SQLException;
+ using ::com::sun::star::sdb::SQLErrorEvent;
+ using ::com::sun::star::lang::EventObject;
+
+ namespace FormFeature = ::com::sun::star::form::runtime::FormFeature;
+
+
+ //= FeatureSlotTranslation
+
+ namespace
+ {
+ struct FeatureDescription
+ {
+ OUString sURL; // the URL
+ sal_Int32 nSlotId; // the SFX-compatible slot ID
+ sal_Int16 nFormFeature; // the css.form.runtime.FormFeature ID
+ };
+ typedef ::std::vector< FeatureDescription > FeatureDescriptions;
+
+
+ const FeatureDescriptions& getFeatureDescriptions()
+ {
+ static const FeatureDescriptions s_aFeatureDescriptions({
+ { FMURL_FORM_POSITION, SID_FM_RECORD_ABSOLUTE, FormFeature::MoveAbsolute },
+ { FMURL_FORM_RECORDCOUNT, SID_FM_RECORD_TOTAL, FormFeature::TotalRecords },
+ { FMURL_RECORD_MOVEFIRST, SID_FM_RECORD_FIRST, FormFeature::MoveToFirst },
+ { FMURL_RECORD_MOVEPREV, SID_FM_RECORD_PREV, FormFeature::MoveToPrevious },
+ { FMURL_RECORD_MOVENEXT, SID_FM_RECORD_NEXT, FormFeature::MoveToNext },
+ { FMURL_RECORD_MOVELAST, SID_FM_RECORD_LAST, FormFeature::MoveToLast },
+ { FMURL_RECORD_MOVETONEW, SID_FM_RECORD_NEW, FormFeature::MoveToInsertRow },
+ { FMURL_RECORD_SAVE, SID_FM_RECORD_SAVE, FormFeature::SaveRecordChanges },
+ { FMURL_RECORD_DELETE, SID_FM_RECORD_DELETE, FormFeature::DeleteRecord },
+ { FMURL_FORM_REFRESH, SID_FM_REFRESH, FormFeature::ReloadForm },
+ { FMURL_FORM_REFRESH_CURRENT_CONTROL,
+ SID_FM_REFRESH_FORM_CONTROL,FormFeature::RefreshCurrentControl },
+ { FMURL_RECORD_UNDO, SID_FM_RECORD_UNDO, FormFeature::UndoRecordChanges },
+ { FMURL_FORM_SORT_UP, SID_FM_SORTUP, FormFeature::SortAscending },
+ { FMURL_FORM_SORT_DOWN, SID_FM_SORTDOWN, FormFeature::SortDescending },
+ { FMURL_FORM_SORT, SID_FM_ORDERCRIT, FormFeature::InteractiveSort },
+ { FMURL_FORM_AUTO_FILTER, SID_FM_AUTOFILTER, FormFeature::AutoFilter },
+ { FMURL_FORM_FILTER, SID_FM_FILTERCRIT, FormFeature::InteractiveFilter },
+ { FMURL_FORM_APPLY_FILTER, SID_FM_FORM_FILTERED, FormFeature::ToggleApplyFilter },
+ { FMURL_FORM_REMOVE_FILTER, SID_FM_REMOVE_FILTER_SORT, FormFeature::RemoveFilterAndSort }
+ });
+ return s_aFeatureDescriptions;
+ }
+ }
+
+
+ namespace
+ {
+
+ struct MatchFeatureDescriptionByURL
+ {
+ const OUString& m_rURL;
+ explicit MatchFeatureDescriptionByURL( const OUString& _rURL ) :m_rURL( _rURL ) { }
+
+ bool operator()( const FeatureDescription& _compare )
+ {
+ return m_rURL == _compare.sURL;
+ }
+ };
+
+
+ struct MatchFeatureDescriptionBySlotId
+ {
+ sal_Int32 m_nSlotId;
+ explicit MatchFeatureDescriptionBySlotId( sal_Int32 _nSlotId ) :m_nSlotId( _nSlotId ) { }
+
+ bool operator()( const FeatureDescription& _compare )
+ {
+ return m_nSlotId == _compare.nSlotId;
+ }
+ };
+
+
+ struct MatchFeatureDescriptionByFormFeature
+ {
+ sal_Int32 m_nFormFeature;
+ explicit MatchFeatureDescriptionByFormFeature( sal_Int32 _nFormFeature ) :m_nFormFeature( _nFormFeature ) { }
+
+ bool operator()( const FeatureDescription& _compare )
+ {
+ return m_nFormFeature == _compare.nFormFeature;
+ }
+ };
+
+
+ struct FormFeatureToSlotId
+ {
+ sal_Int32 operator()( sal_Int16 FormFeature )
+ {
+ return FeatureSlotTranslation::getSlotIdForFormFeature( FormFeature );
+ }
+ };
+ }
+
+
+ sal_Int32 FeatureSlotTranslation::getControllerFeatureSlotIdForURL( const OUString& _rMainURL )
+ {
+ const FeatureDescriptions& rDescriptions( getFeatureDescriptions() );
+ FeatureDescriptions::const_iterator pos = ::std::find_if( rDescriptions.begin(), rDescriptions.end(), MatchFeatureDescriptionByURL( _rMainURL ) );
+ return ( pos != rDescriptions.end() ) ? pos->nSlotId : -1;
+ }
+
+
+ sal_Int16 FeatureSlotTranslation::getFormFeatureForSlotId( sal_Int32 _nSlotId )
+ {
+ const FeatureDescriptions& rDescriptions( getFeatureDescriptions() );
+ FeatureDescriptions::const_iterator pos = ::std::find_if( rDescriptions.begin(), rDescriptions.end(), MatchFeatureDescriptionBySlotId( _nSlotId ) );
+ OSL_ENSURE( pos != rDescriptions.end(), "FeatureSlotTranslation::getFormFeatureForSlotId: not found!" );
+ return ( pos != rDescriptions.end() ) ? pos->nFormFeature : -1;
+ }
+
+
+ sal_Int32 FeatureSlotTranslation::getSlotIdForFormFeature( sal_Int16 _nFormFeature )
+ {
+ const FeatureDescriptions& rDescriptions( getFeatureDescriptions() );
+ FeatureDescriptions::const_iterator pos = ::std::find_if( rDescriptions.begin(), rDescriptions.end(), MatchFeatureDescriptionByFormFeature( _nFormFeature ) );
+ OSL_ENSURE( pos != rDescriptions.end(), "FeatureSlotTranslation::getSlotIdForFormFeature: not found!" );
+ return ( pos != rDescriptions.end() ) ? pos->nSlotId : -1;
+ }
+
+ ControllerFeatures::ControllerFeatures( IControllerFeatureInvalidation* _pInvalidationCallback )
+ :m_pInvalidationCallback( _pInvalidationCallback )
+ {
+ }
+
+
+ ControllerFeatures::ControllerFeatures( const Reference< XFormController >& _rxController )
+ :m_pInvalidationCallback( nullptr )
+ {
+ assign( _rxController );
+ }
+
+
+ void ControllerFeatures::assign( const Reference< XFormController >& _rxController )
+ {
+ dispose();
+ m_pImpl = new FormControllerHelper( _rxController, m_pInvalidationCallback );
+ }
+
+
+ ControllerFeatures::~ControllerFeatures()
+ {
+ dispose();
+ }
+
+
+ void ControllerFeatures::dispose()
+ {
+ if ( m_pImpl.is() )
+ {
+ m_pImpl->dispose();
+ m_pImpl.clear();
+ }
+ }
+
+ FormControllerHelper::FormControllerHelper( const Reference< XFormController >& _rxController, IControllerFeatureInvalidation* _pInvalidationCallback )
+ :m_pInvalidationCallback( _pInvalidationCallback )
+ {
+ osl_atomic_increment( &m_refCount );
+ try
+ {
+ m_xFormOperations = FormOperations::createWithFormController( comphelper::getProcessComponentContext(), _rxController );
+ if ( m_xFormOperations.is() )
+ m_xFormOperations->setFeatureInvalidation( this );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ osl_atomic_decrement( &m_refCount );
+ }
+
+
+ FormControllerHelper::~FormControllerHelper( )
+ {
+ try
+ {
+ acquire();
+ dispose();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+
+ void FormControllerHelper::dispose()
+ {
+ if ( m_xFormOperations.is() )
+ m_xFormOperations->dispose();
+ m_xFormOperations.clear();
+ }
+
+
+ bool FormControllerHelper::isEnabled( sal_Int32 _nSlotId ) const
+ {
+ if ( !m_xFormOperations.is() )
+ return false;
+ return m_xFormOperations->isEnabled( FeatureSlotTranslation::getFormFeatureForSlotId( _nSlotId ) );
+ }
+
+
+ Reference< XRowSet > FormControllerHelper::getCursor() const
+ {
+ Reference< XRowSet > xCursor;
+ if ( m_xFormOperations.is() )
+ xCursor = m_xFormOperations->getCursor();
+ return xCursor;
+ }
+
+
+ void FormControllerHelper::getState( sal_Int32 _nSlotId, FeatureState& _rState ) const
+ {
+ if ( m_xFormOperations.is() )
+ _rState = m_xFormOperations->getState( FeatureSlotTranslation::getFormFeatureForSlotId( _nSlotId ) );
+ }
+
+
+ bool FormControllerHelper::commitCurrentControl( ) const
+ {
+ return impl_operateForm_nothrow( COMMIT_CONTROL );
+ }
+
+
+ bool FormControllerHelper::commitCurrentRecord() const
+ {
+ return impl_operateForm_nothrow( COMMIT_RECORD );
+ }
+
+
+ void FormControllerHelper::execute( sal_Int32 _nSlotId, const OUString& _rParamName, const Any& _rParamValue ) const
+ {
+ Sequence< NamedValue > aArguments { { _rParamName, _rParamValue } };
+ impl_operateForm_nothrow( EXECUTE_ARGS, FeatureSlotTranslation::getFormFeatureForSlotId( _nSlotId ), aArguments );
+ }
+
+
+ bool FormControllerHelper::impl_operateForm_nothrow( const FormOperation _eWhat, const sal_Int16 _nFeature,
+ const Sequence< NamedValue >& _rArguments ) const
+ {
+ if ( !m_xFormOperations.is() )
+ return false;
+
+ Any aError;
+ bool bSuccess = false;
+ const_cast< FormControllerHelper* >( this )->m_aOperationError.clear();
+ try
+ {
+ // to prevent the controller from displaying any error messages which happen while we operate on it,
+ // we add ourself as XSQLErrorListener. By contract, a FormController displays errors if and only if
+ // no SQLErrorListeners are registered.
+ m_xFormOperations->getController()->addSQLErrorListener( const_cast< FormControllerHelper* >(this) );
+
+ switch ( _eWhat )
+ {
+ case COMMIT_CONTROL:
+ bSuccess = m_xFormOperations->commitCurrentControl();
+ break;
+
+ case COMMIT_RECORD:
+ {
+ sal_Bool bDummy( false );
+ bSuccess = m_xFormOperations->commitCurrentRecord( bDummy );
+ }
+ break;
+
+ case EXECUTE:
+ m_xFormOperations->execute( _nFeature );
+ bSuccess = true;
+ break;
+
+ case EXECUTE_ARGS:
+ m_xFormOperations->executeWithArguments( _nFeature, _rArguments );
+ bSuccess = true;
+ break;
+ }
+ }
+ catch ( const SQLException& )
+ {
+ aError = ::cppu::getCaughtException();
+ m_xFormOperations->getController()->removeSQLErrorListener( const_cast< FormControllerHelper* >(this) );
+ }
+ catch( const Exception& )
+ {
+ aError = ::cppu::getCaughtException();
+ m_xFormOperations->getController()->removeSQLErrorListener( const_cast< FormControllerHelper* >(this) );
+ SQLException aFallbackError(::comphelper::anyToString(aError), {}, {}, 0, {});
+ aError <<= aFallbackError;
+ }
+
+ if ( bSuccess )
+ return true;
+
+ // display the error. Prefer the one reported in errorOccurred over the one caught.
+ if ( m_aOperationError.hasValue() )
+ displayException(m_aOperationError, svxform::FormController::getDialogParentWindow(m_xFormOperations->getController()));
+ else if ( aError.hasValue() )
+ displayException(aError, svxform::FormController::getDialogParentWindow(m_xFormOperations->getController()));
+ else
+ OSL_FAIL( "FormControllerHelper::impl_operateForm_nothrow: no success, but no error?" );
+
+ return false;
+ }
+
+
+ void FormControllerHelper::execute( sal_Int32 _nSlotId ) const
+ {
+ impl_operateForm_nothrow( EXECUTE, FeatureSlotTranslation::getFormFeatureForSlotId( _nSlotId ),
+ Sequence< NamedValue >() );
+ }
+
+
+ void SAL_CALL FormControllerHelper::invalidateFeatures( const Sequence< ::sal_Int16 >& Features )
+ {
+ if ( !m_pInvalidationCallback )
+ // nobody's interested in ...
+ return;
+
+ ::std::vector< sal_Int32 > aFeatures( Features.getLength() );
+ ::std::transform(
+ Features.begin(),
+ Features.end(),
+ aFeatures.begin(),
+ FormFeatureToSlotId()
+ );
+
+ m_pInvalidationCallback->invalidateFeatures( aFeatures );
+ }
+
+
+ void SAL_CALL FormControllerHelper::invalidateAllFeatures()
+ {
+ if ( !m_pInvalidationCallback )
+ // nobody's interested in ...
+ return;
+
+ // actually, it's a little bit more than the supported features,
+ // but on the medium term, we are to support everything listed
+ // here
+ ::std::vector< sal_Int32 > aSupportedFeatures;
+ const sal_Int32 pSupportedFeatures[] =
+ {
+ SID_FM_RECORD_FIRST,
+ SID_FM_RECORD_NEXT,
+ SID_FM_RECORD_PREV,
+ SID_FM_RECORD_LAST,
+ SID_FM_RECORD_NEW,
+ SID_FM_RECORD_DELETE,
+ SID_FM_RECORD_ABSOLUTE,
+ SID_FM_RECORD_TOTAL,
+ SID_FM_RECORD_SAVE,
+ SID_FM_RECORD_UNDO,
+ SID_FM_REMOVE_FILTER_SORT,
+ SID_FM_SORTUP,
+ SID_FM_SORTDOWN,
+ SID_FM_ORDERCRIT,
+ SID_FM_AUTOFILTER,
+ SID_FM_FILTERCRIT,
+ SID_FM_FORM_FILTERED,
+ SID_FM_REFRESH,
+ SID_FM_REFRESH_FORM_CONTROL,
+ SID_FM_SEARCH,
+ SID_FM_FILTER_START,
+ SID_FM_VIEW_AS_GRID
+ };
+ sal_Int32 nFeatureCount = SAL_N_ELEMENTS( pSupportedFeatures );
+ aSupportedFeatures.reserve(nFeatureCount); // work around GCC12 spurious -Werror=stringop-overflow=
+ aSupportedFeatures.insert( aSupportedFeatures.begin(), pSupportedFeatures, pSupportedFeatures + nFeatureCount );
+
+ m_pInvalidationCallback->invalidateFeatures( aSupportedFeatures );
+ }
+
+
+ void SAL_CALL FormControllerHelper::errorOccured( const SQLErrorEvent& Event )
+ {
+ OSL_ENSURE( !m_aOperationError.hasValue(), "FormControllerHelper::errorOccurred: two errors during one operation?" );
+ m_aOperationError = Event.Reason;
+ }
+
+
+ void SAL_CALL FormControllerHelper::disposing( const EventObject& /*_Source*/ )
+ {
+ // not interested in
+ }
+
+
+ bool FormControllerHelper::isInsertionRow() const
+ {
+ bool bIs = false;
+ if ( m_xFormOperations.is() )
+ bIs = m_xFormOperations->isInsertionRow();
+ return bIs;
+ }
+
+
+ bool FormControllerHelper::isModifiedRow() const
+ {
+ bool bIs = false;
+ if ( m_xFormOperations.is() )
+ bIs = m_xFormOperations->isModifiedRow();
+ return bIs;
+ }
+
+ bool FormControllerHelper::canDoFormFilter() const
+ {
+ if ( !m_xFormOperations.is() )
+ return false;
+
+ bool bCanDo = false;
+ try
+ {
+ Reference< XPropertySet > xCursorProperties( m_xFormOperations->getCursor(), UNO_QUERY_THROW );
+
+ bool bEscapeProcessing( false );
+ OSL_VERIFY( xCursorProperties->getPropertyValue( FM_PROP_ESCAPE_PROCESSING ) >>= bEscapeProcessing );
+
+ OUString sActiveCommand;
+ OSL_VERIFY( xCursorProperties->getPropertyValue( FM_PROP_ACTIVECOMMAND ) >>= sActiveCommand );
+
+ bool bInsertOnlyForm( false );
+ OSL_VERIFY( xCursorProperties->getPropertyValue( FM_PROP_INSERTONLY ) >>= bInsertOnlyForm );
+
+ bCanDo = bEscapeProcessing && !sActiveCommand.isEmpty() && !bInsertOnlyForm;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return bCanDo;
+ }
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/formdispatchinterceptor.cxx b/svx/source/form/formdispatchinterceptor.cxx
new file mode 100644
index 0000000000..cc75b0b01f
--- /dev/null
+++ b/svx/source/form/formdispatchinterceptor.cxx
@@ -0,0 +1,176 @@
+/* -*- 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 <formdispatchinterceptor.hxx>
+
+namespace svxform
+{
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::frame::XDispatchProviderInterception;
+ using ::com::sun::star::frame::XDispatchProviderInterceptor;
+ using ::com::sun::star::lang::XComponent;
+ using ::com::sun::star::util::URL;
+ using ::com::sun::star::frame::XDispatch;
+ using ::com::sun::star::frame::DispatchDescriptor;
+ using ::com::sun::star::frame::XDispatchProvider;
+ using ::com::sun::star::lang::EventObject;
+
+ DispatchInterceptionMultiplexer::DispatchInterceptionMultiplexer(
+ const Reference< XDispatchProviderInterception >& _rxToIntercept, DispatchInterceptor* _pMaster )
+ :DispatchInterceptionMultiplexer_BASE(_pMaster && _pMaster->getInterceptorMutex() ? *_pMaster->getInterceptorMutex() : m_aFallback)
+ ,m_pMutex( _pMaster && _pMaster->getInterceptorMutex() ? _pMaster->getInterceptorMutex() : &m_aFallback )
+ ,m_xIntercepted(_rxToIntercept)
+ ,m_bListening(false)
+ ,m_pMaster(_pMaster)
+ {
+
+ ::osl::MutexGuard aGuard( *m_pMutex );
+ osl_atomic_increment(&m_refCount);
+ if (_rxToIntercept.is())
+ {
+ _rxToIntercept->registerDispatchProviderInterceptor(static_cast<XDispatchProviderInterceptor*>(this));
+ // this should make us the top-level dispatch-provider for the component, via a call to our
+ // setDispatchProvider we should have got a fallback for requests we (i.e. our master) cannot fulfill
+ Reference< XComponent> xInterceptedComponent(_rxToIntercept, UNO_QUERY);
+ if (xInterceptedComponent.is())
+ {
+ xInterceptedComponent->addEventListener(this);
+ m_bListening = true;
+ }
+ }
+ osl_atomic_decrement(&m_refCount);
+ }
+
+
+ DispatchInterceptionMultiplexer::~DispatchInterceptionMultiplexer()
+ {
+ if (!rBHelper.bDisposed)
+ dispose();
+
+ }
+
+
+ Reference< XDispatch > SAL_CALL DispatchInterceptionMultiplexer::queryDispatch( const URL& aURL, const OUString& aTargetFrameName, sal_Int32 nSearchFlags )
+ {
+ ::osl::MutexGuard aGuard( *m_pMutex );
+ Reference< XDispatch> xResult;
+ // ask our 'real' interceptor
+ if (m_pMaster)
+ xResult = m_pMaster->interceptedQueryDispatch( aURL, aTargetFrameName, nSearchFlags);
+
+ // ask our slave provider
+ if (!xResult.is() && m_xSlaveDispatcher.is())
+ xResult = m_xSlaveDispatcher->queryDispatch(aURL, aTargetFrameName, nSearchFlags);
+
+ return xResult;
+ }
+
+
+ Sequence< Reference< XDispatch > > SAL_CALL
+ DispatchInterceptionMultiplexer::queryDispatches( const Sequence< DispatchDescriptor >& aDescripts )
+ {
+ ::osl::MutexGuard aGuard( *m_pMutex );
+ Sequence< Reference< XDispatch> > aReturn(aDescripts.getLength());
+ std::transform(aDescripts.begin(), aDescripts.end(), aReturn.getArray(),
+ [this](const DispatchDescriptor& rDescript) -> Reference< XDispatch> {
+ return queryDispatch(rDescript.FeatureURL, rDescript.FrameName, rDescript.SearchFlags); });
+ return aReturn;
+ }
+
+
+ Reference< XDispatchProvider > SAL_CALL DispatchInterceptionMultiplexer::getSlaveDispatchProvider( )
+ {
+ ::osl::MutexGuard aGuard( *m_pMutex );
+ return m_xSlaveDispatcher;
+ }
+
+
+ void SAL_CALL DispatchInterceptionMultiplexer::setSlaveDispatchProvider(const Reference< XDispatchProvider>& xNewDispatchProvider)
+ {
+ ::osl::MutexGuard aGuard( *m_pMutex );
+ m_xSlaveDispatcher = xNewDispatchProvider;
+ }
+
+
+ Reference< XDispatchProvider> SAL_CALL DispatchInterceptionMultiplexer::getMasterDispatchProvider()
+ {
+ ::osl::MutexGuard aGuard( *m_pMutex );
+ return m_xMasterDispatcher;
+ }
+
+
+ void SAL_CALL DispatchInterceptionMultiplexer::setMasterDispatchProvider(const Reference< XDispatchProvider>& xNewSupplier)
+ {
+ ::osl::MutexGuard aGuard( *m_pMutex );
+ m_xMasterDispatcher = xNewSupplier;
+ }
+
+
+ void SAL_CALL DispatchInterceptionMultiplexer::disposing(const EventObject& Source)
+ {
+ if (m_bListening)
+ {
+ Reference< XDispatchProviderInterception > xIntercepted(m_xIntercepted.get(), UNO_QUERY);
+ if (Source.Source == xIntercepted)
+ ImplDetach();
+ }
+ }
+
+
+ void DispatchInterceptionMultiplexer::ImplDetach()
+ {
+ ::osl::MutexGuard aGuard( *m_pMutex );
+ OSL_ENSURE(m_bListening, "DispatchInterceptionMultiplexer::ImplDetach: invalid call!");
+
+ // deregister ourself from the interception component
+ Reference< XDispatchProviderInterception > xIntercepted(m_xIntercepted.get(), UNO_QUERY);
+ if (xIntercepted.is())
+ xIntercepted->releaseDispatchProviderInterceptor(static_cast<XDispatchProviderInterceptor*>(this));
+
+ // m_xIntercepted.clear();
+ // Don't reset m_xIntercepted: It may be needed by our owner to check for which object we were
+ // responsible. As we hold the object with a weak reference only, this should be no problem.
+ // 88936 - 23.07.2001 - frank.schoenheit@sun.com
+ m_pMaster = nullptr;
+ m_pMutex = &m_aFallback;
+ m_bListening = false;
+ }
+
+
+ void DispatchInterceptionMultiplexer::disposing()
+ {
+ // remove ourself as event listener from the interception component
+ if (m_bListening)
+ {
+ Reference< XComponent> xInterceptedComponent(m_xIntercepted.get(), UNO_QUERY);
+ if (xInterceptedComponent.is())
+ xInterceptedComponent->removeEventListener(static_cast<XEventListener*>(this));
+
+ // detach from the interception component
+ ImplDetach();
+ }
+ }
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/formfeaturedispatcher.cxx b/svx/source/form/formfeaturedispatcher.cxx
new file mode 100644
index 0000000000..ae15ad1923
--- /dev/null
+++ b/svx/source/form/formfeaturedispatcher.cxx
@@ -0,0 +1,195 @@
+/* -*- 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 <formfeaturedispatcher.hxx>
+
+#include <comphelper/namedvaluecollection.hxx>
+#include <utility>
+#include <comphelper/diagnose_ex.hxx>
+
+
+namespace svx
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::frame;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::form::runtime;
+
+ OSingleFeatureDispatcher::OSingleFeatureDispatcher( URL _aFeatureURL, const sal_Int16 _nFormFeature,
+ const Reference< XFormOperations >& _rxFormOperations, ::osl::Mutex& _rMutex )
+ :m_rMutex( _rMutex )
+ ,m_aStatusListeners( _rMutex )
+ ,m_xFormOperations( _rxFormOperations )
+ ,m_aFeatureURL(std::move( _aFeatureURL ))
+ ,m_nFormFeature( _nFormFeature )
+ ,m_bLastKnownEnabled( false )
+ {
+ }
+
+
+ void OSingleFeatureDispatcher::getUnoState( FeatureStateEvent& /* [out] */ _rState ) const
+ {
+ _rState.Source = *const_cast< OSingleFeatureDispatcher* >( this );
+
+ FeatureState aState( m_xFormOperations->getState( m_nFormFeature ) );
+
+ _rState.FeatureURL = m_aFeatureURL;
+ _rState.IsEnabled = aState.Enabled;
+ _rState.Requery = false;
+ _rState.State = aState.State;
+ }
+
+
+ void OSingleFeatureDispatcher::updateAllListeners()
+ {
+ ::osl::ClearableMutexGuard aGuard( m_rMutex );
+
+ FeatureStateEvent aUnoState;
+ getUnoState( aUnoState );
+
+ if ( ( m_aLastKnownState == aUnoState.State ) && ( m_bLastKnownEnabled == bool(aUnoState.IsEnabled) ) )
+ return;
+
+ m_aLastKnownState = aUnoState.State;
+ m_bLastKnownEnabled = aUnoState.IsEnabled;
+
+ notifyStatus( nullptr, aGuard );
+ }
+
+
+ void OSingleFeatureDispatcher::notifyStatus( const Reference< XStatusListener >& _rxListener, ::osl::ClearableMutexGuard& _rFreeForNotification )
+ {
+ FeatureStateEvent aUnoState;
+ getUnoState( aUnoState );
+
+ if ( _rxListener.is() )
+ {
+ try
+ {
+ _rFreeForNotification.clear();
+ _rxListener->statusChanged( aUnoState );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "OSingleFeatureDispatcher::notifyStatus" );
+ }
+ }
+ else
+ {
+ ::comphelper::OInterfaceIteratorHelper3 aIter( m_aStatusListeners );
+ _rFreeForNotification.clear();
+
+ while ( aIter.hasMoreElements() )
+ {
+ try
+ {
+ aIter.next()->statusChanged( aUnoState );
+ }
+ catch( const DisposedException& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.form",
+ "caught a DisposedException - removing the listener!");
+ aIter.remove( );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION(
+ "svx.form",
+ "caught a generic exception while notifying a single listener!");
+ }
+ }
+ }
+ }
+
+
+ void SAL_CALL OSingleFeatureDispatcher::dispatch( const URL& _rURL, const Sequence< PropertyValue >& _rArguments )
+ {
+ ::osl::ClearableMutexGuard aGuard( m_rMutex );
+
+ OSL_ENSURE( _rURL.Complete == m_aFeatureURL.Complete, "OSingleFeatureDispatcher::dispatch: not responsible for this URL!" );
+
+ if ( !m_xFormOperations->isEnabled( m_nFormFeature ) )
+ return;
+
+ // release our mutex before executing the command
+ sal_Int16 nFormFeature( m_nFormFeature );
+ Reference< XFormOperations > xFormOperations( m_xFormOperations );
+ aGuard.clear();
+
+ try
+ {
+ if ( !_rArguments.hasElements() )
+ {
+ xFormOperations->execute( nFormFeature );
+ }
+ else
+ { // at the moment we only support one parameter
+ ::comphelper::NamedValueCollection aArgs( _rArguments );
+ xFormOperations->executeWithArguments( nFormFeature, aArgs.getNamedValues() );
+ }
+ }
+ catch( const RuntimeException& )
+ {
+ throw;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+
+ void SAL_CALL OSingleFeatureDispatcher::addStatusListener( const Reference< XStatusListener >& _rxControl, const URL& _rURL )
+ {
+ OSL_ENSURE( _rURL.Complete == m_aFeatureURL.Complete, "OSingleFeatureDispatcher::addStatusListener: unexpected URL!" );
+ OSL_ENSURE( _rxControl.is(), "OSingleFeatureDispatcher::addStatusListener: senseless call!" );
+ if ( !_rxControl.is() )
+ return;
+
+ ::osl::ClearableMutexGuard aGuard( m_rMutex );
+
+ m_aStatusListeners.addInterface( _rxControl );
+
+ // initially update the status
+ notifyStatus( _rxControl, aGuard );
+ }
+
+
+ void SAL_CALL OSingleFeatureDispatcher::removeStatusListener( const Reference< XStatusListener >& _rxControl, const URL& _rURL )
+ {
+ OSL_ENSURE( _rURL.Complete == m_aFeatureURL.Complete, "OSingleFeatureDispatcher::removeStatusListener: unexpected URL!" );
+ OSL_ENSURE( _rxControl.is(), "OSingleFeatureDispatcher::removeStatusListener: senseless call!" );
+ if ( !_rxControl.is() )
+ return;
+
+ ::osl::MutexGuard aGuard( m_rMutex );
+
+ m_aStatusListeners.removeInterface( _rxControl );
+ }
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/formtoolbars.cxx b/svx/source/form/formtoolbars.cxx
new file mode 100644
index 0000000000..a848ba9552
--- /dev/null
+++ b/svx/source/form/formtoolbars.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 <formtoolbars.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <osl/diagnose.h>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <svx/svxids.hrc>
+
+
+namespace svxform
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::frame;
+ using namespace ::com::sun::star::beans;
+
+ FormToolboxes::FormToolboxes( const Reference< XFrame >& _rxFrame )
+ {
+ // the layout manager
+ Reference< XPropertySet > xFrameProps( _rxFrame, UNO_QUERY );
+ if ( xFrameProps.is() )
+ xFrameProps->getPropertyValue("LayoutManager") >>= m_xLayouter;
+ }
+
+
+ void FormToolboxes::toggleToolbox( sal_uInt16 _nSlotId ) const
+ {
+ try
+ {
+ Reference< XLayoutManager > xManager( m_xLayouter );
+ OSL_ENSURE( xManager. is(), "FormToolboxes::toggleToolbox: couldn't obtain the layout manager!" );
+ if ( xManager. is() )
+ {
+ OUString sToolboxResource( getToolboxResourceName( _nSlotId ) );
+ if ( xManager->isElementVisible( sToolboxResource ) )
+ {
+ xManager->hideElement( sToolboxResource );
+ xManager->destroyElement( sToolboxResource );
+ }
+ else
+ {
+ xManager->createElement( sToolboxResource );
+ xManager->showElement( sToolboxResource );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "FormToolboxes::toggleToolbox" );
+ }
+ }
+
+
+ bool FormToolboxes::isToolboxVisible( sal_uInt16 _nSlotId ) const
+ {
+ return m_xLayouter.is() && m_xLayouter->isElementVisible(
+ getToolboxResourceName( _nSlotId ) );
+ }
+
+
+ OUString FormToolboxes::getToolboxResourceName( sal_uInt16 _nSlotId )
+ {
+ OSL_ENSURE( _nSlotId == SID_FM_FORM_DESIGN_TOOLS ,
+ "FormToolboxes::getToolboxResourceName: unsupported slot!" );
+
+ return "private:resource/toolbar/formdesign";
+ }
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/labelitemwindow.cxx b/svx/source/form/labelitemwindow.cxx
new file mode 100644
index 0000000000..c9afd1534a
--- /dev/null
+++ b/svx/source/form/labelitemwindow.cxx
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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/.
+ */
+
+#include <svx/labelitemwindow.hxx>
+
+LabelItemWindow::LabelItemWindow(vcl::Window* pParent, const OUString& rLabel)
+ : InterimItemWindow(pParent, "svx/ui/labelbox.ui", "LabelBox")
+ , m_xBox(m_xBuilder->weld_box("LabelBox"))
+ , m_xLabel(m_xBuilder->weld_label("label"))
+ , m_xImage(m_xBuilder->weld_image("image"))
+{
+ InitControlBase(m_xLabel.get());
+
+ m_xLabel->set_label(rLabel);
+ m_xImage->hide();
+ m_xImage->set_size_request(24, 24); // vcl/res/infobar.png is 32x32 - too large here
+
+ SetOptimalSize();
+
+ m_xLabel->set_toolbar_background();
+}
+
+void LabelItemWindow::SetOptimalSize()
+{
+ Size aSize(m_xBox->get_preferred_size());
+ aSize.AdjustWidth(12);
+
+ SetSizePixel(aSize);
+}
+
+void LabelItemWindow::set_label(const OUString& rLabel, const LabelItemWindowType eType)
+{
+ m_xLabel->set_visible(false); // a11y announcement
+ m_xLabel->set_label(rLabel);
+ if ((eType == LabelItemWindowType::Text) || rLabel.isEmpty())
+ {
+ m_xImage->hide();
+ m_xLabel->set_font_color(COL_AUTO);
+ m_xBox->set_background(COL_AUTO);
+ }
+ else if (eType == LabelItemWindowType::Info)
+ {
+ m_xImage->show();
+ m_xLabel->set_font_color(Color(0x00, 0x47, 0x85));
+ m_xBox->set_background(Color(0xBD, 0xE5, 0xF8)); // same as InfobarType::INFO
+ }
+ m_xLabel->set_visible(
+ true); // always show and not just if !rLabel.isEmpty() to not make the chevron appear
+}
+
+OUString LabelItemWindow::get_label() const { return m_xLabel->get_label(); }
+
+void LabelItemWindow::dispose()
+{
+ m_xLabel.reset();
+ InterimItemWindow::dispose();
+}
+
+LabelItemWindow::~LabelItemWindow() { disposeOnce(); }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/svx/source/form/legacyformcontroller.cxx b/svx/source/form/legacyformcontroller.cxx
new file mode 100644
index 0000000000..94b2ae9834
--- /dev/null
+++ b/svx/source/form/legacyformcontroller.cxx
@@ -0,0 +1,201 @@
+/* -*- 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 <fmservs.hxx>
+
+#include <com/sun/star/form/XFormController.hpp>
+#include <com/sun/star/form/runtime/FormController.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/processfactory.hxx>
+
+
+namespace svxform
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::lang::XMultiServiceFactory;
+ using ::com::sun::star::awt::XControl;
+ using ::com::sun::star::awt::XTabControllerModel;
+ using ::com::sun::star::awt::XControlContainer;
+ using ::com::sun::star::lang::XServiceInfo;
+ using ::com::sun::star::form::runtime::FormController;
+
+ using namespace ::com::sun::star;
+
+
+ //= LegacyFormController
+
+ typedef ::cppu::WeakImplHelper < form::XFormController
+ , XServiceInfo
+ > LegacyFormController_Base;
+
+ namespace {
+
+ /** is an implementation of the legacy form controller service, namely css.form.FormController, supporting the
+ css.form.XFormController interface.
+
+ This legacy API is superseded by css.form.runtime.(X)FormController, and though we migrated all OOo-internal
+ usage of this old API, their might be clients external to OOo still using it (though this is rather unlikely).
+ */
+ class LegacyFormController : public LegacyFormController_Base
+ {
+ public:
+ static Reference< XInterface > Create( const Reference< XMultiServiceFactory >& _rxFactory )
+ {
+ return *( new LegacyFormController( comphelper::getComponentContext(_rxFactory) ) );
+ }
+
+ protected:
+ explicit LegacyFormController( const Reference< XComponentContext >& _rxContext )
+ :m_xDelegator( FormController::create(_rxContext) )
+ {
+ }
+
+ // form::XFormController
+ virtual Reference< XControl > SAL_CALL getCurrentControl( ) override;
+ virtual void SAL_CALL addActivateListener( const Reference< form::XFormControllerListener >& l ) override;
+ virtual void SAL_CALL removeActivateListener( const Reference< form::XFormControllerListener >& l ) override;
+
+ // awt::XTabController
+ virtual void SAL_CALL setModel( const Reference< XTabControllerModel >& Model ) override;
+ virtual Reference< XTabControllerModel > SAL_CALL getModel( ) override;
+ virtual void SAL_CALL setContainer( const Reference< XControlContainer >& Container ) override;
+ virtual Reference< XControlContainer > SAL_CALL getContainer( ) override;
+ virtual Sequence< Reference< XControl > > SAL_CALL getControls( ) override;
+ virtual void SAL_CALL autoTabOrder( ) override;
+ virtual void SAL_CALL activateTabOrder( ) override;
+ virtual void SAL_CALL activateFirst( ) override;
+ virtual void SAL_CALL activateLast( ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ private:
+ const Reference< form::runtime::XFormController > m_xDelegator;
+ };
+
+ }
+
+ Reference< XControl > SAL_CALL LegacyFormController::getCurrentControl( )
+ {
+ return m_xDelegator->getCurrentControl();
+ }
+
+
+ void SAL_CALL LegacyFormController::addActivateListener( const Reference< form::XFormControllerListener >& _listener )
+ {
+ m_xDelegator->addActivateListener( _listener );
+ }
+
+
+ void SAL_CALL LegacyFormController::removeActivateListener( const Reference< form::XFormControllerListener >& _listener )
+ {
+ m_xDelegator->removeActivateListener( _listener );
+ }
+
+
+ void SAL_CALL LegacyFormController::setModel( const Reference< XTabControllerModel >& _model )
+ {
+ m_xDelegator->setModel( _model );
+ }
+
+
+ Reference< XTabControllerModel > SAL_CALL LegacyFormController::getModel( )
+ {
+ return m_xDelegator->getModel();
+ }
+
+
+ void SAL_CALL LegacyFormController::setContainer( const Reference< XControlContainer >& _container )
+ {
+ m_xDelegator->setContainer( _container );
+ }
+
+
+ Reference< XControlContainer > SAL_CALL LegacyFormController::getContainer( )
+ {
+ return m_xDelegator->getContainer();
+ }
+
+
+ Sequence< Reference< XControl > > SAL_CALL LegacyFormController::getControls( )
+ {
+ return m_xDelegator->getControls();
+ }
+
+
+ void SAL_CALL LegacyFormController::autoTabOrder( )
+ {
+ m_xDelegator->autoTabOrder();
+ }
+
+
+ void SAL_CALL LegacyFormController::activateTabOrder( )
+ {
+ m_xDelegator->activateTabOrder();
+ }
+
+
+ void SAL_CALL LegacyFormController::activateFirst( )
+ {
+ m_xDelegator->activateFirst();
+ }
+
+
+ void SAL_CALL LegacyFormController::activateLast( )
+ {
+ m_xDelegator->activateLast();
+ }
+
+
+ OUString SAL_CALL LegacyFormController::getImplementationName( )
+ {
+ return "org.openoffice.comp.svx.LegacyFormController";
+ }
+
+ sal_Bool SAL_CALL LegacyFormController::supportsService( const OUString& _serviceName )
+ {
+ return cppu::supportsService(this, _serviceName);
+ }
+
+ Sequence< OUString > SAL_CALL LegacyFormController::getSupportedServiceNames( )
+ {
+ return { "com.sun.star.form.FormController", "com.sun.star.awt.control.TabController" };
+ }
+
+}
+
+css::uno::Reference< css::uno::XInterface >
+ LegacyFormController_NewInstance_Impl( const css::uno::Reference< css::lang::XMultiServiceFactory > & _rxORB )
+{
+ return ::svxform::LegacyFormController::Create( _rxORB );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/navigatortree.cxx b/svx/source/form/navigatortree.cxx
new file mode 100644
index 0000000000..8ee866edb8
--- /dev/null
+++ b/svx/source/form/navigatortree.cxx
@@ -0,0 +1,2045 @@
+/* -*- 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 <svx/dialmgr.hxx>
+#include <svx/fmshell.hxx>
+#include <svx/fmmodel.hxx>
+#include <svx/fmpage.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svditer.hxx>
+
+#include <helpids.h>
+#include <fmexpl.hxx>
+#include <fmshimp.hxx>
+#include <fmservs.hxx>
+#include <fmundo.hxx>
+#include <fmpgeimp.hxx>
+#include <fmobj.hxx>
+#include <fmprop.hxx>
+#include <sal/log.hxx>
+#include <vcl/svapp.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/types.hxx>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/sdb/CommandType.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/script/XEventAttacherManager.hpp>
+#include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
+#include <com/sun/star/datatransfer/XTransferable.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <svx/sdrpaintwindow.hxx>
+
+#include <svx/strings.hrc>
+#include <comphelper/diagnose_ex.hxx>
+#include <svx/svxids.hrc>
+#include <bitmaps.hlst>
+#include <vcl/commandevent.hxx>
+
+namespace svxform
+{
+ #define EXPLORER_SYNC_DELAY 200
+ // Time (in ms) until explorer synchronizes the view after select or deselect
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::form;
+ using namespace ::com::sun::star::awt;
+ using namespace ::com::sun::star::container;
+ using namespace ::com::sun::star::script;
+ using namespace ::com::sun::star::datatransfer;
+ using namespace ::com::sun::star::datatransfer::clipboard;
+ using namespace ::com::sun::star::sdb;
+
+
+ // helper
+
+
+ typedef ::std::map< Reference< XInterface >, SdrObject* > MapModelToShape;
+
+
+ static void collectShapeModelMapping( SdrPage const * _pPage, MapModelToShape& _rMapping )
+ {
+ OSL_ENSURE( _pPage, "collectShapeModelMapping: invalid arg!" );
+
+ _rMapping.clear();
+
+ SdrObjListIter aIter( _pPage );
+ while ( aIter.IsMore() )
+ {
+ SdrObject* pSdrObject = aIter.Next();
+ FmFormObj* pFormObject = FmFormObj::GetFormObject( pSdrObject );
+ if ( !pFormObject )
+ continue;
+
+ Reference< XInterface > xNormalizedModel( pFormObject->GetUnoControlModel(), UNO_QUERY );
+ // note that this is normalized (i.e. queried for XInterface explicitly)
+
+ ::std::pair< MapModelToShape::iterator, bool > aPos =
+ _rMapping.emplace( xNormalizedModel, pSdrObject );
+ DBG_ASSERT( aPos.second, "collectShapeModelMapping: model was already existent!" );
+ // if this asserts, this would mean we have 2 shapes pointing to the same model
+ }
+ }
+
+ NavigatorTreeDropTarget::NavigatorTreeDropTarget(NavigatorTree& rTreeView)
+ : DropTargetHelper(rTreeView.get_widget().get_drop_target())
+ , m_rTreeView(rTreeView)
+ {
+ }
+
+ sal_Int8 NavigatorTreeDropTarget::AcceptDrop(const AcceptDropEvent& rEvt)
+ {
+ sal_Int8 nAccept = m_rTreeView.AcceptDrop(rEvt);
+
+ if (nAccept != DND_ACTION_NONE)
+ {
+ // to enable the autoscroll when we're close to the edges
+ weld::TreeView& rWidget = m_rTreeView.get_widget();
+ rWidget.get_dest_row_at_pos(rEvt.maPosPixel, nullptr, true);
+ }
+
+ return nAccept;
+ }
+
+ sal_Int8 NavigatorTreeDropTarget::ExecuteDrop(const ExecuteDropEvent& rEvt)
+ {
+ return m_rTreeView.ExecuteDrop(rEvt);
+ }
+
+ NavigatorTree::NavigatorTree(std::unique_ptr<weld::TreeView> xTreeView)
+ :m_xTreeView(std::move(xTreeView))
+ ,m_aDropTargetHelper(*this)
+ ,m_aSynchronizeTimer("svx NavigatorTree m_aSynchronizeTimer")
+ ,nEditEvent(nullptr)
+ ,m_sdiState(SDI_DIRTY)
+ ,m_nSelectLock(0)
+ ,m_nFormsSelected(0)
+ ,m_nControlsSelected(0)
+ ,m_nHiddenControls(0)
+ ,m_bDragDataDirty(false)
+ ,m_bPrevSelectionMixed(false)
+ ,m_bRootSelected(false)
+ ,m_bInitialUpdate(true)
+ ,m_bKeyboardCut( false )
+ ,m_bEditing( false )
+ {
+ m_xTreeView->set_help_id(HID_FORM_NAVIGATOR);
+ m_xTreeView->set_size_request(200, 200);
+
+ m_xTreeView->set_selection_mode(SelectionMode::Multiple);
+
+ m_pNavModel.reset(new NavigatorTreeModel());
+ Clear();
+
+ StartListening( *m_pNavModel );
+
+ m_aSynchronizeTimer.SetInvokeHandler(LINK(this, NavigatorTree, OnSynchronizeTimer));
+ m_xTreeView->connect_changed(LINK(this, NavigatorTree, OnEntrySelDesel));
+ m_xTreeView->connect_key_press(LINK(this, NavigatorTree, KeyInputHdl));
+ m_xTreeView->connect_popup_menu(LINK(this, NavigatorTree, PopupMenuHdl));
+ m_xTreeView->connect_editing(LINK(this, NavigatorTree, EditingEntryHdl),
+ LINK(this, NavigatorTree, EditedEntryHdl));
+ m_xTreeView->connect_drag_begin(LINK(this, NavigatorTree, DragBeginHdl));
+ }
+
+ NavigatorTree::~NavigatorTree()
+ {
+ if( nEditEvent )
+ Application::RemoveUserEvent( nEditEvent );
+
+ if (m_aSynchronizeTimer.IsActive())
+ m_aSynchronizeTimer.Stop();
+
+ DBG_ASSERT(GetNavModel() != nullptr, "NavigatorTree::~NavigatorTree : unexpected : no ExplorerModel");
+ EndListening( *m_pNavModel );
+ Clear();
+ m_pNavModel.reset();
+ }
+
+ void NavigatorTree::Clear()
+ {
+ m_pNavModel->Clear();
+ }
+
+ void NavigatorTree::UpdateContent( FmFormShell* pFormShell )
+ {
+ if (m_bInitialUpdate)
+ {
+ GrabFocus();
+ m_bInitialUpdate = false;
+ }
+
+ FmFormShell* pOldShell = GetNavModel()->GetFormShell();
+ FmFormPage* pOldPage = GetNavModel()->GetFormPage();
+ FmFormPage* pNewPage = pFormShell ? pFormShell->GetCurPage() : nullptr;
+
+ if ((pOldShell != pFormShell) || (pOldPage != pNewPage))
+ {
+ // new shell during editing
+ if (IsEditingActive())
+ {
+ m_xTreeView->end_editing();
+ m_bEditing = false;
+ }
+
+ m_bDragDataDirty = true; // as a precaution, although I don't drag
+ }
+ GetNavModel()->UpdateContent( pFormShell );
+
+ // if there is a form, expand root
+ if (m_xRootEntry && !m_xTreeView->get_row_expanded(*m_xRootEntry))
+ m_xTreeView->expand_row(*m_xRootEntry);
+ // if there is EXACTLY ONE form, expand it too
+ if (m_xRootEntry)
+ {
+ std::unique_ptr<weld::TreeIter> xFirst(m_xTreeView->make_iterator(m_xRootEntry.get()));
+ bool bFirst = m_xTreeView->iter_children(*xFirst);
+ if (bFirst)
+ {
+ std::unique_ptr<weld::TreeIter> xSibling(m_xTreeView->make_iterator(xFirst.get()));
+ if (!m_xTreeView->iter_next_sibling(*xSibling))
+ m_xTreeView->expand_row(*xFirst);
+ }
+ }
+ }
+
+ bool NavigatorTree::implAllowExchange( sal_Int8 _nAction, bool* _pHasNonHidden )
+ {
+ bool bCurEntry = m_xTreeView->get_cursor(nullptr);
+ if (!bCurEntry)
+ return false;
+
+ // Information for AcceptDrop and Execute Drop
+ CollectSelectionData(SDI_ALL);
+ if (m_arrCurrentSelection.empty())
+ // nothing to do
+ return false;
+
+ // check whether there are only hidden controls
+ // I may add a format to pCtrlExch
+ bool bHasNonHidden = std::any_of(m_arrCurrentSelection.begin(), m_arrCurrentSelection.end(),
+ [this](const auto& rEntry) {
+ FmEntryData* pCurrent = weld::fromId<FmEntryData*>(m_xTreeView->get_id(*rEntry));
+ return !IsHiddenControl( pCurrent );
+ });
+
+ if ( bHasNonHidden && ( 0 == ( _nAction & DND_ACTION_MOVE ) ) )
+ // non-hidden controls need to be moved
+ return false;
+
+ if ( _pHasNonHidden )
+ *_pHasNonHidden = bHasNonHidden;
+
+ return true;
+ }
+
+ bool NavigatorTree::implPrepareExchange( sal_Int8 _nAction )
+ {
+ bool bHasNonHidden = false;
+ if ( !implAllowExchange( _nAction, &bHasNonHidden ) )
+ return false;
+
+ m_aControlExchange.prepareDrag();
+ m_aControlExchange->setFocusEntry(m_xTreeView->get_cursor(nullptr));
+
+ for (const auto& rpEntry : m_arrCurrentSelection)
+ m_aControlExchange->addSelectedEntry(m_xTreeView->make_iterator(rpEntry.get()));
+
+ m_aControlExchange->setFormsRoot( GetNavModel()->GetFormPage()->GetForms() );
+ m_aControlExchange->buildPathFormat(m_xTreeView.get(), m_xRootEntry.get());
+
+ if (!bHasNonHidden)
+ {
+ // create a sequence
+ Sequence< Reference< XInterface > > seqIFaces(m_arrCurrentSelection.size());
+ Reference< XInterface >* pArray = seqIFaces.getArray();
+ for (const auto& rpEntry : m_arrCurrentSelection)
+ {
+ *pArray = weld::fromId<FmEntryData*>(m_xTreeView->get_id(*rpEntry))->GetElement();
+ ++pArray;
+ }
+ // and the new format
+ m_aControlExchange->addHiddenControlsFormat(seqIFaces);
+ }
+
+ m_bDragDataDirty = false;
+ return true;
+ }
+
+ IMPL_LINK(NavigatorTree, DragBeginHdl, bool&, rUnsetDragIcon, bool)
+ {
+ rUnsetDragIcon = false;
+
+ bool bSuccess = implPrepareExchange(DND_ACTION_COPYMOVE);
+ if (bSuccess)
+ {
+ OControlExchange& rExchange = *m_aControlExchange;
+ rtl::Reference<TransferDataContainer> xHelper(&rExchange);
+ m_xTreeView->enable_drag_source(xHelper, DND_ACTION_COPYMOVE);
+ rExchange.setDragging(true);
+ }
+ return !bSuccess;
+ }
+
+ IMPL_LINK(NavigatorTree, PopupMenuHdl, const CommandEvent&, rEvt, bool)
+ {
+ bool bHandled = false;
+ switch( rEvt.GetCommand() )
+ {
+ case CommandEventId::ContextMenu:
+ {
+ // Position of click
+ ::Point ptWhere;
+ if (rEvt.IsMouseEvent())
+ {
+ ptWhere = rEvt.GetMousePosPixel();
+ std::unique_ptr<weld::TreeIter> xClickedOn(m_xTreeView->make_iterator());
+ if (!m_xTreeView->get_dest_row_at_pos(ptWhere, xClickedOn.get(), false))
+ break;
+ if (!m_xTreeView->is_selected(*xClickedOn))
+ {
+ m_xTreeView->unselect_all();
+ m_xTreeView->select(*xClickedOn);
+ m_xTreeView->set_cursor(*xClickedOn);
+ }
+ }
+ else
+ {
+ if (m_arrCurrentSelection.empty()) // only happens with context menu via keyboard
+ break;
+
+ std::unique_ptr<weld::TreeIter> xCurrent(m_xTreeView->make_iterator());
+ if (!m_xTreeView->get_cursor(xCurrent.get()))
+ break;
+ ptWhere = m_xTreeView->get_row_area(*xCurrent).Center();
+ }
+
+ // update my selection data
+ CollectSelectionData(SDI_ALL);
+
+ // if there is at least one no-root-entry and the root selected, I deselect root
+ if ( (m_arrCurrentSelection.size() > 1) && m_bRootSelected )
+ {
+ const std::unique_ptr<weld::TreeIter>& rIter = *m_arrCurrentSelection.begin();
+ m_xTreeView->set_cursor(*rIter);
+ m_xTreeView->unselect(*m_xRootEntry);
+ }
+ bool bSingleSelection = (m_arrCurrentSelection.size() == 1);
+
+
+ DBG_ASSERT( (!m_arrCurrentSelection.empty()) || m_bRootSelected, "no entries selected" );
+ // shouldn't happen, because I would have selected one during call to IsSelected,
+ // if there was none before
+
+
+ // create menu
+ FmFormShell* pFormShell = GetNavModel()->GetFormShell();
+ FmFormModel* pFormModel = pFormShell ? pFormShell->GetFormModel() : nullptr;
+ if( pFormShell && pFormModel )
+ {
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xTreeView.get(), "svx/ui/formnavimenu.ui"));
+ std::unique_ptr<weld::Menu> xContextMenu(xBuilder->weld_menu("menu"));
+ std::unique_ptr<weld::Menu> xSubMenuNew(xBuilder->weld_menu("submenu"));
+
+ // menu 'New' only exists, if only the root or only one form is selected
+ bool bShowNew = bSingleSelection && (m_nFormsSelected || m_bRootSelected);
+ if (!bShowNew)
+ xContextMenu->remove("new");
+
+ // 'New'\'Form' under the same terms
+ bool bShowForm = bSingleSelection && (m_nFormsSelected || m_bRootSelected);
+ if (bShowForm)
+ xSubMenuNew->append("form", SvxResId(RID_STR_FORM), RID_SVXBMP_FORM);
+
+ // 'New'\'hidden...', if exactly one form is selected
+ bool bShowHidden = bSingleSelection && m_nFormsSelected;
+ if (bShowHidden)
+ xSubMenuNew->append("hidden", SvxResId(RID_STR_HIDDEN), RID_SVXBMP_HIDDEN);
+
+ // 'Delete': everything which is not root can be removed
+ if (m_bRootSelected)
+ xContextMenu->remove("delete");
+
+ // 'Cut', 'Copy' and 'Paste'
+ bool bShowCut = !m_bRootSelected && implAllowExchange(DND_ACTION_MOVE);
+ if (!bShowCut)
+ xContextMenu->remove("cut");
+ bool bShowCopy = !m_bRootSelected && implAllowExchange(DND_ACTION_COPY);
+ if (!bShowCopy)
+ xContextMenu->remove("copy");
+ if (!implAcceptPaste())
+ xContextMenu->remove("paste");
+
+ // TabDialog, if exactly one form
+ bool bShowTabOrder = bSingleSelection && m_nFormsSelected;
+ if (!bShowTabOrder)
+ xContextMenu->remove("taborder");
+
+ bool bShowProps = true;
+ // in XML forms, we don't allow for the properties of a form
+ // #i36484#
+ if (pFormShell->GetImpl()->isEnhancedForm_Lock() && !m_nControlsSelected)
+ bShowProps = false;
+ // if the property browser is already open, we don't allow for the properties, too
+ if (pFormShell->GetImpl()->IsPropBrwOpen_Lock())
+ bShowProps = false;
+
+ // and finally, if there's a mixed selection of forms and controls, disable the entry, too
+ if (bShowProps && !pFormShell->GetImpl()->IsPropBrwOpen_Lock())
+ bShowProps =
+ (m_nControlsSelected && !m_nFormsSelected) || (!m_nControlsSelected && m_nFormsSelected);
+
+ if (!bShowProps)
+ xContextMenu->remove("props");
+
+ // rename, if one element and no root
+ bool bShowRename = bSingleSelection && !m_bRootSelected;
+ if (!bShowRename)
+ xContextMenu->remove("rename");
+
+ if (!m_bRootSelected)
+ {
+ // Readonly-entry is only for root
+ xContextMenu->remove("designmode");
+ // the same for automatic control focus
+ xContextMenu->remove("controlfocus");
+ }
+
+ std::unique_ptr<weld::Menu> xConversionMenu(xBuilder->weld_menu("changemenu"));
+ // ConvertTo-Slots are enabled, if one control is selected
+ // the corresponding slot is disabled
+ if (!m_bRootSelected && !m_nFormsSelected && (m_nControlsSelected == 1))
+ {
+ FmXFormShell::GetConversionMenu_Lock(*xConversionMenu);
+#if OSL_DEBUG_LEVEL > 0
+ const std::unique_ptr<weld::TreeIter>& rIter = *m_arrCurrentSelection.begin();
+ FmControlData* pCurrent = weld::fromId<FmControlData*>(m_xTreeView->get_id(*rIter));
+ OSL_ENSURE( pFormShell->GetImpl()->isSolelySelected_Lock( pCurrent->GetFormComponent() ),
+ "NavigatorTree::Command: inconsistency between the navigator selection, and the selection as the shell knows it!" );
+#endif
+
+ pFormShell->GetImpl()->checkControlConversionSlotsForCurrentSelection_Lock(*xConversionMenu);
+ }
+ else
+ xContextMenu->remove("change");
+
+ if (m_bRootSelected)
+ {
+ // set OpenReadOnly
+ xContextMenu->set_active("designmode", pFormModel->GetOpenInDesignMode());
+ xContextMenu->set_active("controlfocus", pFormModel->GetAutoControlFocus());
+ }
+
+ OUString sIdent = xContextMenu->popup_at_rect(m_xTreeView.get(), tools::Rectangle(ptWhere, ::Size(1, 1)));
+ if (sIdent == "form")
+ {
+ OUString aStr(SvxResId(RID_STR_FORM));
+ OUString aUndoStr = SvxResId(RID_STR_UNDO_CONTAINER_INSERT).replaceAll("#", aStr);
+
+ pFormModel->BegUndo(aUndoStr);
+ // slot was only available, if there is only one selected entry,
+ // which is a root or a form
+ const std::unique_ptr<weld::TreeIter>& rIter = *m_arrCurrentSelection.begin();
+ NewForm(*rIter);
+ pFormModel->EndUndo();
+ }
+ else if (sIdent == "hidden")
+ {
+ OUString aStr(SvxResId(RID_STR_CONTROL));
+ OUString aUndoStr = SvxResId(RID_STR_UNDO_CONTAINER_INSERT).replaceAll("#", aStr);
+
+ pFormModel->BegUndo(aUndoStr);
+ // slot was valid for (exactly) one selected form
+ const std::unique_ptr<weld::TreeIter>& rIter = *m_arrCurrentSelection.begin();
+ NewControl(FM_COMPONENT_HIDDEN, *rIter, true);
+ pFormModel->EndUndo();
+ }
+ else if (sIdent == "cut")
+ doCut();
+ else if (sIdent == "copy")
+ doCopy();
+ else if (sIdent == "paste")
+ doPaste();
+ else if (sIdent == "delete")
+ DeleteSelection();
+ else if (sIdent == "taborder")
+ {
+ // this slot was effective for exactly one selected form
+ const std::unique_ptr<weld::TreeIter>& rSelectedForm = *m_arrCurrentSelection.begin();
+ DBG_ASSERT( IsFormEntry(*rSelectedForm), "NavigatorTree::Command: This entry must be a FormEntry." );
+
+ FmFormData* pFormData = weld::fromId<FmFormData*>(m_xTreeView->get_id(*rSelectedForm));
+ const Reference< XForm >& xForm( pFormData->GetFormIface());
+
+ Reference< XTabControllerModel > xTabController(xForm, UNO_QUERY);
+ if( !xTabController.is() )
+ break;
+ GetNavModel()->GetFormShell()->GetImpl()->ExecuteTabOrderDialog_Lock(xTabController);
+ }
+ else if (sIdent == "props")
+ ShowSelectionProperties(true);
+ else if (sIdent == "rename")
+ {
+ // only allowed for one no-root-entry
+ const std::unique_ptr<weld::TreeIter>& rIter = *m_arrCurrentSelection.begin();
+ m_xTreeView->start_editing(*rIter);
+ m_bEditing = true;
+ }
+ else if (sIdent == "designmode")
+ {
+ pFormModel->SetOpenInDesignMode( !pFormModel->GetOpenInDesignMode() );
+ pFormShell->GetViewShell()->GetViewFrame().GetBindings().Invalidate(SID_FM_OPEN_READONLY);
+ }
+ else if (sIdent == "controlfocus")
+ {
+ pFormModel->SetAutoControlFocus( !pFormModel->GetAutoControlFocus() );
+ pFormShell->GetViewShell()->GetViewFrame().GetBindings().Invalidate(SID_FM_AUTOCONTROLFOCUS);
+ }
+ else if (FmXFormShell::isControlConversionSlot(sIdent))
+ {
+ const std::unique_ptr<weld::TreeIter>& rIter = *m_arrCurrentSelection.begin();
+ FmControlData* pCurrent = weld::fromId<FmControlData*>(m_xTreeView->get_id(*rIter));
+ if (pFormShell->GetImpl()->executeControlConversionSlot_Lock(pCurrent->GetFormComponent(), sIdent))
+ ShowSelectionProperties();
+ }
+ }
+ bHandled = true;
+ }
+ break;
+ default: break;
+ }
+
+ return bHandled;
+ }
+
+ std::unique_ptr<weld::TreeIter> NavigatorTree::FindEntry(FmEntryData* pEntryData)
+ {
+ std::unique_ptr<weld::TreeIter> xRet;
+ if(!pEntryData)
+ return xRet;
+
+ m_xTreeView->all_foreach([this, pEntryData, &xRet](weld::TreeIter& rEntry){
+ FmEntryData* pCurEntryData = weld::fromId<FmEntryData*>(m_xTreeView->get_id(rEntry));
+ if (pCurEntryData && pCurEntryData->IsEqualWithoutChildren(pEntryData))
+ {
+ xRet = m_xTreeView->make_iterator(&rEntry);
+ return true;
+ }
+ return false;
+ });
+
+ return xRet;
+ }
+
+ void NavigatorTree::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
+ {
+ if( auto pRemovedHint = dynamic_cast<const FmNavRemovedHint*>(&rHint) )
+ {
+ FmEntryData* pEntryData = pRemovedHint->GetEntryData();
+ Remove( pEntryData );
+ }
+ else if( auto pInsertedHint = dynamic_cast<const FmNavInsertedHint*>(&rHint) )
+ {
+ FmEntryData* pEntryData = pInsertedHint->GetEntryData();
+ sal_uInt32 nRelPos = pInsertedHint->GetRelPos();
+ Insert( pEntryData, nRelPos );
+ }
+ else if( auto pReplacedHint = dynamic_cast<const FmNavModelReplacedHint*>(&rHint) )
+ {
+ FmEntryData* pData = pReplacedHint->GetEntryData();
+ std::unique_ptr<weld::TreeIter> xEntry = FindEntry(pData);
+ if (xEntry)
+ {
+ // reset image
+ m_xTreeView->set_image(*xEntry, pData->GetNormalImage());
+ }
+ }
+ else if( auto pNameChangedHint = dynamic_cast<const FmNavNameChangedHint*>(&rHint) )
+ {
+ std::unique_ptr<weld::TreeIter> xEntry = FindEntry(pNameChangedHint->GetEntryData());
+ if (xEntry)
+ m_xTreeView->set_text(*xEntry, pNameChangedHint->GetNewName());
+ }
+ else if( dynamic_cast<const FmNavClearedHint*>(&rHint) )
+ {
+ m_aCutEntries.clear();
+ if (m_aControlExchange.isDataExchangeActive())
+ m_aControlExchange.clear();
+ m_xTreeView->clear();
+
+ // default-entry "Forms"
+ OUString sText(SvxResId(RID_STR_FORMS));
+ m_xRootEntry = m_xTreeView->make_iterator();
+ m_xTreeView->insert(nullptr, -1, &sText, nullptr, nullptr, nullptr,
+ false, m_xRootEntry.get());
+ m_xTreeView->set_image(*m_xRootEntry, RID_SVXBMP_FORMS);
+ m_xTreeView->set_sensitive(*m_xRootEntry, true);
+ }
+ else if (auto pSelectHint = dynamic_cast<FmNavRequestSelectHint*>(const_cast<SfxHint*>(&rHint)))
+ {
+ FmEntryDataArray& arredToSelect = pSelectHint->GetItems();
+ SynchronizeSelection(arredToSelect);
+
+ if (pSelectHint->IsMixedSelection())
+ // in this case I deselect all, although the view had a mixed selection
+ // during next selection, I must adapt the navigator to the view
+ m_bPrevSelectionMixed = true;
+ }
+ }
+
+ std::unique_ptr<weld::TreeIter> NavigatorTree::Insert(const FmEntryData* pEntryData, int nRelPos)
+ {
+ // insert current entry
+ std::unique_ptr<weld::TreeIter> xParentEntry = FindEntry( pEntryData->GetParent() );
+ std::unique_ptr<weld::TreeIter> xNewEntry(m_xTreeView->make_iterator());
+ OUString sId(weld::toId(pEntryData));
+
+ if(!xParentEntry)
+ {
+ m_xTreeView->insert(m_xRootEntry.get(), nRelPos, &pEntryData->GetText(), &sId,
+ nullptr, nullptr, false, xNewEntry.get());
+ }
+ else
+ {
+ m_xTreeView->insert(xParentEntry.get(), nRelPos, &pEntryData->GetText(), &sId,
+ nullptr, nullptr, false, xNewEntry.get());
+ }
+
+ m_xTreeView->set_image(*xNewEntry, pEntryData->GetNormalImage());
+ m_xTreeView->set_sensitive(*xNewEntry, true);
+
+ // If root-entry, expand root
+ if (!xParentEntry)
+ m_xTreeView->expand_row(*m_xRootEntry);
+
+ // insert children
+ FmEntryDataList* pChildList = pEntryData->GetChildList();
+ size_t nChildCount = pChildList->size();
+ for( size_t i = 0; i < nChildCount; i++ )
+ {
+ FmEntryData* pChildData = pChildList->at( i );
+ Insert(pChildData, -1);
+ }
+
+ return xNewEntry;
+ }
+
+ void NavigatorTree::Remove( FmEntryData* pEntryData )
+ {
+ if( !pEntryData )
+ return;
+
+ // entry for the data
+ std::unique_ptr<weld::TreeIter> xEntry = FindEntry(pEntryData);
+ if (!xEntry)
+ return;
+
+ // delete entry from TreeListBox
+ // I'm not allowed, to treat the selection, which I trigger:
+ // select changes the MarkList of the view, if somebody else does this at the same time
+ // and removes a selection, we get a problem
+ // e.g. Group controls with open navigator
+ LockSelectionHandling();
+
+ // little problem: I remember the selected data, but if somebody deletes one of these entries,
+ // I get inconsistent... this would be bad
+ m_xTreeView->unselect(*xEntry);
+
+ // selection can be modified during deletion,
+ // but because I disabled SelectionHandling, I have to do it later
+ auto nExpectedSelectionCount = m_xTreeView->count_selected_rows();
+
+ ModelHasRemoved(xEntry.get());
+ m_xTreeView->remove(*xEntry);
+
+ if (nExpectedSelectionCount != m_xTreeView->count_selected_rows())
+ SynchronizeSelection();
+
+ // by default I treat the selection of course
+ UnlockSelectionHandling();
+ }
+
+ bool NavigatorTree::IsFormEntry(const weld::TreeIter& rEntry)
+ {
+ FmEntryData* pEntryData = weld::fromId<FmEntryData*>(m_xTreeView->get_id(rEntry));
+ return !pEntryData || dynamic_cast<const FmFormData*>( pEntryData) != nullptr;
+ }
+
+ bool NavigatorTree::IsFormComponentEntry(const weld::TreeIter& rEntry)
+ {
+ FmEntryData* pEntryData = weld::fromId<FmEntryData*>(m_xTreeView->get_id(rEntry));
+ return dynamic_cast<const FmControlData*>( pEntryData) != nullptr;
+ }
+
+ bool NavigatorTree::implAcceptPaste( )
+ {
+ auto nSelectedEntries = m_xTreeView->count_selected_rows();
+ if (nSelectedEntries != 1)
+ // no selected entry, or at least two selected entries
+ return false;
+
+ // get the clipboard
+ TransferableDataHelper aClipboardContent(TransferableDataHelper::CreateFromClipboard(m_xTreeView->get_clipboard()));
+
+ sal_Int8 nAction = m_aControlExchange.isClipboardOwner() && doingKeyboardCut( ) ? DND_ACTION_MOVE : DND_ACTION_COPY;
+ std::unique_ptr<weld::TreeIter> xSelected(m_xTreeView->make_iterator());
+ if (!m_xTreeView->get_selected(xSelected.get()))
+ xSelected.reset();
+ return nAction == implAcceptDataTransfer(aClipboardContent.GetDataFlavorExVector(), nAction, xSelected.get(), false);
+ }
+
+ sal_Int8 NavigatorTree::implAcceptDataTransfer( const DataFlavorExVector& _rFlavors, sal_Int8 _nAction, const weld::TreeIter* _pTargetEntry, bool _bDnD )
+ {
+ // no target -> no drop
+ if (!_pTargetEntry)
+ return DND_ACTION_NONE;
+
+ // format check
+ bool bHasDefControlFormat = OControlExchange::hasFieldExchangeFormat( _rFlavors );
+ bool bHasControlPathFormat = OControlExchange::hasControlPathFormat( _rFlavors );
+ bool bHasHiddenControlsFormat = OControlExchange::hasHiddenControlModelsFormat( _rFlavors );
+ if (!bHasDefControlFormat && !bHasControlPathFormat && !bHasHiddenControlsFormat)
+ return DND_ACTION_NONE;
+
+ bool bSelfSource = _bDnD ? m_aControlExchange.isDragSource() : m_aControlExchange.isClipboardOwner();
+
+ if ( bHasHiddenControlsFormat )
+ { // bHasHiddenControlsFormat means that only hidden controls are part of the data
+
+ // hidden controls can be copied to a form only
+ if (m_xTreeView->iter_compare(*_pTargetEntry, *m_xRootEntry) == 0 || !IsFormEntry(*_pTargetEntry))
+ return DND_ACTION_NONE;
+
+ return bSelfSource ? ( DND_ACTION_COPYMOVE & _nAction ) : DND_ACTION_COPY;
+ }
+
+ if ( !bSelfSource )
+ {
+ // DnD or CnP crossing navigator boundaries
+ // The main problem here is that the current API does not allow us to sneak into the content which
+ // is to be inserted. So we have to allow it for the moment, but maybe reject later on (in the real drop).
+
+ // TODO: this smart behaviour later on ... at the moment, we disallow data transfer crossing navigator
+ // boundaries.
+
+ return DND_ACTION_NONE;
+ }
+
+ DBG_ASSERT( _bDnD ? m_aControlExchange.isDragSource() : m_aControlExchange.isClipboardOwner(),
+ "NavigatorTree::implAcceptDataTransfer: here only with source=dest!" );
+ // somebody changed the logic of this method ...
+
+ // from here on, I can work with m_aControlExchange instead of _rData!
+
+ bool bForeignCollection = m_aControlExchange->getFormsRoot().get() != GetNavModel()->GetFormPage()->GetForms().get();
+ if ( bForeignCollection )
+ {
+ // crossing shell/page boundaries, we can exchange hidden controls only
+ // But if we survived the checks above, we do not have hidden controls.
+ // -> no data transfer
+ DBG_ASSERT( !bHasHiddenControlsFormat, "NavigatorTree::implAcceptDataTransfer: still hidden controls format!" );
+ // somebody changed the logic of this method ...
+
+ return DND_ACTION_COPY;
+ }
+
+ if (DND_ACTION_MOVE != _nAction) // 'normal' controls within a shell are moved only (never copied)
+ return DND_ACTION_NONE;
+
+ if ( m_bDragDataDirty || !bHasDefControlFormat )
+ {
+ if (!bHasControlPathFormat)
+ // I am in the shell/page, which has the controls, but I have no format,
+ // which survived the shell change (SVX_FM_CONTROLS_AS_PATH)
+ return DND_ACTION_NONE;
+
+ // I must recreate the list of the ExchangeObjects, because the shell was changed during dragging
+ // (there are SvLBoxEntries in it, and we lost them during change)
+ m_aControlExchange->buildListFromPath(m_xTreeView.get(), m_xRootEntry.get());
+ m_bDragDataDirty = false;
+ }
+
+ // List of dropped entries from DragServer
+ const ListBoxEntrySet& rDropped = m_aControlExchange->selected();
+ DBG_ASSERT(!rDropped.empty(), "NavigatorTree::implAcceptDataTransfer: no entries !");
+
+ bool bDropTargetIsComponent = IsFormComponentEntry( *_pTargetEntry );
+
+ // conditions to disallow the drop
+ // 0) the root entry is part of the list (can't DnD the root!)
+ // 1) one of the dragged entries is to be dropped onto its own parent
+ // 2) - " - is to be dropped onto itself
+ // 3) - " - is a Form and to be dropped onto one of its descendants
+ // 4) one of the entries is a control and to be dropped onto the root
+ // 5) a control or form will be dropped onto a control which is _not_ a sibling (dropping onto a sibling
+ // means moving the control)
+
+ // collect the ancestors of the drop target (speeds up 3)
+ SvLBoxEntrySortedArray arrDropAncestors;
+ std::unique_ptr<weld::TreeIter> xLoop(m_xTreeView->make_iterator(_pTargetEntry));
+ do
+ {
+ arrDropAncestors.emplace(m_xTreeView->make_iterator(xLoop.get()));
+ }
+ while (m_xTreeView->iter_parent(*xLoop));
+
+ for (const auto& rCurrent : rDropped)
+ {
+ // test for 0)
+ if (m_xTreeView->iter_compare(*rCurrent, *m_xRootEntry) == 0)
+ return DND_ACTION_NONE;
+
+ std::unique_ptr<weld::TreeIter> xCurrentParent(m_xTreeView->make_iterator(rCurrent.get()));
+ m_xTreeView->iter_parent(*xCurrentParent);
+
+ // test for 1)
+ if (m_xTreeView->iter_compare(*_pTargetEntry, *xCurrentParent) == 0)
+ return DND_ACTION_NONE;
+
+ // test for 2)
+ if (m_xTreeView->iter_compare(*rCurrent, *_pTargetEntry) == 0)
+ return DND_ACTION_NONE;
+
+ // test for 5)
+ if (bDropTargetIsComponent)
+ return DND_ACTION_NONE;
+
+ // test for 3)
+ if (IsFormEntry(*rCurrent))
+ {
+ auto aIter = std::find_if(arrDropAncestors.begin(), arrDropAncestors.end(),
+ [this, &rCurrent](const auto& rElem) {
+ return m_xTreeView->iter_compare(*rElem, *rCurrent) == 0;
+ });
+
+ if ( aIter != arrDropAncestors.end() )
+ return DND_ACTION_NONE;
+ }
+ else if (IsFormComponentEntry(*rCurrent))
+ {
+ // test for 4)
+ if (m_xTreeView->iter_compare(*_pTargetEntry, *m_xRootEntry) == 0)
+ return DND_ACTION_NONE;
+ }
+ }
+ return DND_ACTION_MOVE;
+ }
+
+ sal_Int8 NavigatorTree::AcceptDrop( const AcceptDropEvent& rEvt )
+ {
+ ::Point aDropPos = rEvt.maPosPixel;
+ std::unique_ptr<weld::TreeIter> xDropTarget(m_xTreeView->make_iterator());
+ // get_dest_row_at_pos with false cause we must drop exactly "on" a form to paste a control into it
+ if (!m_xTreeView->get_dest_row_at_pos(aDropPos, xDropTarget.get(), false))
+ xDropTarget.reset();
+ return implAcceptDataTransfer(m_aDropTargetHelper.GetDataFlavorExVector(), rEvt.mnAction, xDropTarget.get(), true);
+ }
+
+ sal_Int8 NavigatorTree::implExecuteDataTransfer( const OControlTransferData& _rData, sal_Int8 _nAction, const ::Point& _rDropPos, bool _bDnD )
+ {
+ std::unique_ptr<weld::TreeIter> xDrop(m_xTreeView->make_iterator());
+ // get_dest_row_at_pos with false cause we must drop exactly "on" a form to paste a control into it
+ if (!m_xTreeView->get_dest_row_at_pos(_rDropPos, xDrop.get(), false))
+ xDrop.reset();
+ return implExecuteDataTransfer( _rData, _nAction, xDrop.get(), _bDnD );
+ }
+
+ sal_Int8 NavigatorTree::implExecuteDataTransfer( const OControlTransferData& _rData, sal_Int8 _nAction, const weld::TreeIter* _pTargetEntry, bool _bDnD )
+ {
+ const DataFlavorExVector& rDataFlavors = _rData.GetDataFlavorExVector();
+
+ if ( DND_ACTION_NONE == implAcceptDataTransfer( rDataFlavors, _nAction, _pTargetEntry, _bDnD ) )
+ // under some platforms, it may happen that ExecuteDrop is called though AcceptDrop returned DND_ACTION_NONE
+ return DND_ACTION_NONE;
+
+ if (!_pTargetEntry)
+ // no target -> no drop
+ return DND_ACTION_NONE;
+
+ // format checks
+#ifdef DBG_UTIL
+ bool bHasHiddenControlsFormat = OControlExchange::hasHiddenControlModelsFormat( rDataFlavors );
+ bool bForeignCollection = _rData.getFormsRoot().get() != GetNavModel()->GetFormPage()->GetForms().get();
+ DBG_ASSERT(!bForeignCollection || bHasHiddenControlsFormat, "NavigatorTree::implExecuteDataTransfer: invalid format (AcceptDrop shouldn't have let this pass) !");
+ DBG_ASSERT(bForeignCollection || !m_bDragDataDirty, "NavigatorTree::implExecuteDataTransfer: invalid state (shell changed since last exchange resync) !");
+ // this should be done in AcceptDrop: the list of controls is created in _rData
+ // and m_bDragDataDirty is reset
+#endif
+
+ if ( DND_ACTION_COPY == _nAction )
+ { // bHasHiddenControlsFormat means that only hidden controls are part of the data
+#ifdef DBG_UTIL
+ DBG_ASSERT( bHasHiddenControlsFormat, "NavigatorTree::implExecuteDataTransfer: copy allowed for hidden controls only!" );
+#endif
+ DBG_ASSERT( _pTargetEntry && m_xTreeView->iter_compare(*_pTargetEntry, *m_xRootEntry) != 0 && IsFormEntry( *_pTargetEntry ),
+ "NavigatorTree::implExecuteDataTransfer: should not be here!" );
+ // implAcceptDataTransfer should have caught both cases
+
+#ifdef DBG_UTIL
+ DBG_ASSERT(bHasHiddenControlsFormat, "NavigatorTree::implExecuteDataTransfer: only copying of hidden controls is supported !");
+ // should be caught by AcceptDrop
+#endif
+
+ // because i want to select all targets (and only them)
+ m_xTreeView->unselect_all();
+
+ const Sequence< Reference< XInterface > >& aControls = _rData.hiddenControls();
+ sal_Int32 nCount = aControls.getLength();
+ const Reference< XInterface >* pControls = aControls.getConstArray();
+
+ FmFormShell* pFormShell = GetNavModel()->GetFormShell();
+ FmFormModel* pFormModel = pFormShell ? pFormShell->GetFormModel() : nullptr;
+
+ // within undo
+ if (pFormModel)
+ {
+ OUString aStr(SvxResId(RID_STR_CONTROL));
+ OUString aUndoStr = SvxResId(RID_STR_UNDO_CONTAINER_INSERT).replaceAll("#", aStr);
+ pFormModel->BegUndo(aUndoStr);
+ }
+
+ // copy controls
+ for (sal_Int32 i=0; i<nCount; ++i)
+ {
+ // create new control
+ FmControlData* pNewControlData = NewControl( FM_COMPONENT_HIDDEN, *_pTargetEntry, false);
+ Reference< XPropertySet > xNewPropSet( pNewControlData->GetPropertySet() );
+
+ // copy properties form old control to new one
+ Reference< XPropertySet > xCurrent(pControls[i], UNO_QUERY);
+#if (OSL_DEBUG_LEVEL > 0)
+ // check whether it is a hidden control
+ sal_Int16 nClassId = ::comphelper::getINT16(xCurrent->getPropertyValue(FM_PROP_CLASSID));
+ OSL_ENSURE(nClassId == FormComponentType::HIDDENCONTROL, "NavigatorTree::implExecuteDataTransfer: invalid control in drop list !");
+ // if SVX_FM_HIDDEN_CONTROLS-format exists, the sequence
+ // should only contain hidden controls
+#endif // (OSL_DEBUG_LEVEL > 0)
+ Reference< XPropertySetInfo > xPropInfo( xCurrent->getPropertySetInfo());
+ const Sequence< Property> seqAllCurrentProps = xPropInfo->getProperties();
+ for (Property const & currentProp : seqAllCurrentProps)
+ {
+ if (((currentProp.Attributes & PropertyAttribute::READONLY) == 0) && (currentProp.Name != FM_PROP_NAME))
+ { // (read-only attribs aren't set, ditto name,
+ // NewControl defined it uniquely
+ xNewPropSet->setPropertyValue(currentProp.Name, xCurrent->getPropertyValue(currentProp.Name));
+ }
+ }
+
+ std::unique_ptr<weld::TreeIter> xToSelect = FindEntry(pNewControlData);
+ m_xTreeView->select(*xToSelect);
+ if (i == 0)
+ m_xTreeView->set_cursor(*xToSelect);
+ }
+
+ if (pFormModel)
+ pFormModel->EndUndo();
+
+ return _nAction;
+ }
+
+ if ( !OControlExchange::hasFieldExchangeFormat( _rData.GetDataFlavorExVector() ) )
+ {
+ // can't do anything without the internal format here ... usually happens when doing DnD or CnP
+ // over navigator boundaries
+ return DND_ACTION_NONE;
+ }
+
+ // some data for the target
+ bool bDropTargetIsForm = IsFormEntry(*_pTargetEntry);
+ FmFormData* pTargetData = bDropTargetIsForm ? weld::fromId<FmFormData*>(m_xTreeView->get_id(*_pTargetEntry)) : nullptr;
+
+ DBG_ASSERT( DND_ACTION_COPY != _nAction, "NavigatorTree::implExecuteDataTransfer: somebody changed the logics!" );
+
+ // list of dragged entries
+ const ListBoxEntrySet& rDropped = _rData.selected();
+ DBG_ASSERT(!rDropped.empty(), "NavigatorTree::implExecuteDataTransfer: no entries!");
+
+ // make a copy because rDropped is updated on deleting an entry which we do in the processing loop
+ ListBoxEntrySet aDropped;
+ for (const auto& rEntry : rDropped)
+ aDropped.emplace(m_xTreeView->make_iterator(rEntry.get()));
+
+ // shell and model
+ FmFormShell* pFormShell = GetNavModel()->GetFormShell();
+ FmFormModel* pFormModel = pFormShell ? pFormShell->GetFormModel() : nullptr;
+ if (!pFormModel)
+ return DND_ACTION_NONE;
+
+ // for Undo
+ const bool bUndo = pFormModel->IsUndoEnabled();
+
+ if( bUndo )
+ {
+ OUString strUndoDescription(SvxResId(RID_STR_UNDO_CONTAINER_REPLACE));
+ pFormModel->BegUndo(strUndoDescription);
+ }
+
+ // remove selection before adding an entry, so the mark doesn't flicker
+ // -> lock action of selection
+ LockSelectionHandling();
+
+ // go through all dropped entries
+ for ( ListBoxEntrySet::const_iterator dropped = aDropped.begin();
+ dropped != aDropped.end();
+ ++dropped
+ )
+ {
+ bool bFirstEntry = aDropped.begin() == dropped;
+
+ // some data of the current element
+ const auto& rCurrent = *dropped;
+ DBG_ASSERT(rCurrent, "NavigatorTree::implExecuteDataTransfer: invalid entry");
+ DBG_ASSERT(m_xTreeView->get_iter_depth(*rCurrent) != 0, "NavigatorTree::implExecuteDataTransfer: invalid entry");
+ // don't drag root
+
+ FmEntryData* pCurrentUserData = weld::fromId<FmEntryData*>(m_xTreeView->get_id(*rCurrent));
+
+ Reference< XChild > xCurrentChild = pCurrentUserData->GetChildIFace();
+ Reference< XIndexContainer > xContainer(xCurrentChild->getParent(), UNO_QUERY);
+
+ FmFormData* pCurrentParentUserData = static_cast<FmFormData*>(pCurrentUserData->GetParent());
+ DBG_ASSERT(pCurrentParentUserData == nullptr || dynamic_cast<const FmFormData*>(pCurrentUserData->GetParent()) != nullptr, "NavigatorTree::implExecuteDataTransfer: invalid parent");
+
+ // remove from parent
+ if (pCurrentParentUserData)
+ pCurrentParentUserData->GetChildList()->removeNoDelete( pCurrentUserData );
+ else
+ GetNavModel()->GetRootList()->removeNoDelete( pCurrentUserData );
+
+ // remove from container
+ sal_Int32 nIndex = getElementPos(xContainer, xCurrentChild);
+ GetNavModel()->m_pPropChangeList->Lock();
+ // UndoAction for removal
+ if ( bUndo && GetNavModel()->m_pPropChangeList->CanUndo())
+ {
+ pFormModel->AddUndo(std::make_unique<FmUndoContainerAction>(*pFormModel, FmUndoContainerAction::Removed,
+ xContainer, xCurrentChild, nIndex));
+ }
+ else if( !GetNavModel()->m_pPropChangeList->CanUndo() )
+ {
+ FmUndoContainerAction::DisposeElement( xCurrentChild );
+ }
+
+ // copy events
+ Reference< XEventAttacherManager > xManager(xContainer, UNO_QUERY);
+ Sequence< ScriptEventDescriptor > aEvts;
+
+ if (xManager.is() && nIndex >= 0)
+ aEvts = xManager->getScriptEvents(nIndex);
+ xContainer->removeByIndex(nIndex);
+
+ // remove selection
+ m_xTreeView->unselect(*rCurrent);
+ // and delete it
+ Remove(pCurrentUserData);
+
+ // position in DropParents, where to insert dropped entries
+ if (pTargetData)
+ xContainer.set(pTargetData->GetElement(), UNO_QUERY);
+ else
+ xContainer = GetNavModel()->GetForms();
+
+ // always insert at the end
+ nIndex = xContainer->getCount();
+
+ // UndoAction for insertion
+ if ( bUndo && GetNavModel()->m_pPropChangeList->CanUndo())
+ pFormModel->AddUndo(std::make_unique<FmUndoContainerAction>(*pFormModel, FmUndoContainerAction::Inserted,
+ xContainer, xCurrentChild, nIndex));
+
+ // insert in new container
+ if (pTargetData)
+ {
+ // insert in a form needs a FormComponent
+ xContainer->insertByIndex( nIndex,
+ Any( Reference< XFormComponent >( xCurrentChild, UNO_QUERY ) ) );
+ }
+ else
+ {
+ xContainer->insertByIndex( nIndex,
+ Any( Reference< XForm >( xCurrentChild, UNO_QUERY ) ) );
+ }
+
+ if (aEvts.hasElements())
+ {
+ xManager.set(xContainer, UNO_QUERY);
+ if (xManager.is())
+ xManager->registerScriptEvents(nIndex, aEvts);
+ }
+
+ GetNavModel()->m_pPropChangeList->UnLock();
+
+ // give an entry the new parent
+ pCurrentUserData->SetParent(pTargetData);
+
+ // give parent the new child
+ if (pTargetData)
+ pTargetData->GetChildList()->insert( std::unique_ptr<FmEntryData>(pCurrentUserData), nIndex );
+ else
+ GetNavModel()->GetRootList()->insert( std::unique_ptr<FmEntryData>(pCurrentUserData), nIndex );
+
+ // announce to myself and reselect
+ std::unique_ptr<weld::TreeIter> xNew = Insert( pCurrentUserData, nIndex );
+ if (bFirstEntry && xNew)
+ {
+ if (m_xTreeView->iter_parent(*xNew))
+ m_xTreeView->expand_row(*xNew);
+ }
+ }
+
+ UnlockSelectionHandling();
+
+ if( bUndo )
+ pFormModel->EndUndo();
+
+ // During the move, the markings of the underlying view did not change (because the view is not affected by the logical
+ // hierarchy of the form/control models. But my selection changed - which means I have to adjust it according to the
+ // view marks, again.
+ SynchronizeSelection();
+
+ // in addition, with the move of controls such things as "the current form" may have changed - force the shell
+ // to update itself accordingly
+ if( pFormShell && pFormShell->GetImpl() && pFormShell->GetFormView() )
+ pFormShell->GetImpl()->DetermineSelection_Lock( pFormShell->GetFormView()->GetMarkedObjectList() );
+
+ if ( m_aControlExchange.isClipboardOwner() && ( DND_ACTION_MOVE == _nAction ) )
+ m_aControlExchange->clear();
+
+ return _nAction;
+ }
+
+ sal_Int8 NavigatorTree::ExecuteDrop( const ExecuteDropEvent& rEvt )
+ {
+ sal_Int8 nResult( DND_ACTION_NONE );
+ if ( m_aControlExchange.isDragSource() )
+ nResult = implExecuteDataTransfer( *m_aControlExchange, rEvt.mnAction, rEvt.maPosPixel, true );
+ else
+ {
+ OControlTransferData aDroppedData( rEvt.maDropEvent.Transferable );
+ nResult = implExecuteDataTransfer( aDroppedData, rEvt.mnAction, rEvt.maPosPixel, true );
+ }
+ return nResult;
+ }
+
+ void NavigatorTree::doPaste()
+ {
+ std::unique_ptr<weld::TreeIter> xSelected(m_xTreeView->make_iterator());
+ if (!m_xTreeView->get_selected(xSelected.get()))
+ xSelected.reset();
+
+ try
+ {
+ if ( m_aControlExchange.isClipboardOwner() )
+ {
+ implExecuteDataTransfer( *m_aControlExchange, doingKeyboardCut( ) ? DND_ACTION_MOVE : DND_ACTION_COPY, xSelected.get(), false );
+ }
+ else
+ {
+ // the clipboard content
+ Reference< XClipboard > xClipboard(m_xTreeView->get_clipboard());
+ Reference< XTransferable > xTransferable;
+ if ( xClipboard.is() )
+ xTransferable = xClipboard->getContents();
+
+ OControlTransferData aClipboardContent( xTransferable );
+ implExecuteDataTransfer( aClipboardContent, DND_ACTION_COPY, xSelected.get(), false );
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "NavigatorTree::doPaste" );
+ }
+ }
+
+ void NavigatorTree::doCopy()
+ {
+ if ( implPrepareExchange( DND_ACTION_COPY ) )
+ {
+ m_aControlExchange.setClipboardListener( LINK( this, NavigatorTree, OnClipboardAction ) );
+ m_aControlExchange.copyToClipboard(*m_xTreeView);
+ }
+ }
+
+ void NavigatorTree::ModelHasRemoved(const weld::TreeIter* pTypedEntry)
+ {
+ if (doingKeyboardCut())
+ {
+ auto aIter = std::find_if(m_aCutEntries.begin(), m_aCutEntries.end(),
+ [this, pTypedEntry](const auto& rElem) {
+ return m_xTreeView->iter_compare(*rElem, *pTypedEntry) == 0;
+ });
+ if (aIter != m_aCutEntries.end())
+ m_aCutEntries.erase(aIter);
+ }
+
+ if (m_aControlExchange.isDataExchangeActive())
+ {
+ if (0 == m_aControlExchange->onEntryRemoved(m_xTreeView.get(), pTypedEntry))
+ {
+ // last of the entries which we put into the clipboard has been deleted from the tree.
+ // Give up the clipboard ownership.
+ m_aControlExchange.clear();
+ }
+ }
+ }
+
+ void NavigatorTree::doCut()
+ {
+ if ( !implPrepareExchange( DND_ACTION_MOVE ) )
+ return;
+
+ m_aControlExchange.setClipboardListener( LINK( this, NavigatorTree, OnClipboardAction ) );
+ m_aControlExchange.copyToClipboard(*m_xTreeView);
+ m_bKeyboardCut = true;
+
+ // mark all the entries we just "cut" into the clipboard as "nearly moved"
+ for (const auto& rEntry : m_arrCurrentSelection )
+ {
+ if (!rEntry)
+ continue;
+ m_aCutEntries.emplace(m_xTreeView->make_iterator(rEntry.get()));
+ m_xTreeView->set_sensitive(*rEntry, false);
+ }
+ }
+
+ IMPL_LINK(NavigatorTree, KeyInputHdl, const ::KeyEvent&, rKEvt, bool)
+ {
+ const vcl::KeyCode& rCode = rKEvt.GetKeyCode();
+
+ // delete?
+ if (rCode.GetCode() == KEY_DELETE && !rCode.GetModifier())
+ {
+ DeleteSelection();
+ return true;
+ }
+
+ // copy'n'paste?
+ switch ( rCode.GetFunction() )
+ {
+ case KeyFuncType::CUT:
+ doCut();
+ break;
+
+ case KeyFuncType::PASTE:
+ if ( implAcceptPaste() )
+ doPaste();
+ break;
+
+ case KeyFuncType::COPY:
+ doCopy();
+ break;
+
+ default:
+ break;
+ }
+
+ return false;
+ }
+
+ IMPL_LINK(NavigatorTree, EditingEntryHdl, const weld::TreeIter&, rIter, bool)
+ {
+ // root, which isn't allowed to be renamed, has UserData=NULL
+ m_bEditing = !m_xTreeView->get_id(rIter).isEmpty();
+ return m_bEditing;
+ }
+
+ void NavigatorTree::NewForm(const weld::TreeIter& rParentEntry)
+ {
+ // get ParentFormData
+ if (!IsFormEntry(rParentEntry))
+ return;
+
+ FmFormData* pParentFormData = weld::fromId<FmFormData*>(m_xTreeView->get_id(rParentEntry));
+
+
+ // create new form
+ Reference<XComponentContext> xContext = comphelper::getProcessComponentContext();
+ Reference< XForm > xNewForm(xContext->getServiceManager()->createInstanceWithContext(FM_SUN_COMPONENT_FORM, xContext), UNO_QUERY);
+ if (!xNewForm.is())
+ return;
+
+ Reference< XPropertySet > xPropertySet(xNewForm, UNO_QUERY);
+ if (!xPropertySet.is())
+ return;
+
+ FmFormData* pNewFormData = new FmFormData(xNewForm, pParentFormData);
+
+
+ // set name
+ OUString aName = GenerateName(pNewFormData);
+ pNewFormData->SetText(aName);
+
+ try
+ {
+ xPropertySet->setPropertyValue( FM_PROP_NAME, Any(aName) );
+ // a form should always have the command type table as default
+ xPropertySet->setPropertyValue( FM_PROP_COMMANDTYPE, Any(sal_Int32(CommandType::TABLE)));
+ }
+ catch ( const Exception& )
+ {
+ OSL_FAIL("NavigatorTree::NewForm : could not set essential properties!");
+ }
+
+
+ // insert form
+ GetNavModel()->Insert(pNewFormData, SAL_MAX_UINT32, true);
+
+
+ // set new form as active
+ FmFormShell* pFormShell = GetNavModel()->GetFormShell();
+ if( pFormShell )
+ {
+ InterfaceBag aSelection;
+ aSelection.insert( Reference<XInterface>( xNewForm, UNO_QUERY ) );
+ pFormShell->GetImpl()->setCurrentSelection_Lock(std::move(aSelection));
+
+ pFormShell->GetViewShell()->GetViewFrame().GetBindings().Invalidate(SID_FM_PROPERTIES, true, true);
+ }
+ GetNavModel()->SetModified();
+
+ // switch to EditMode
+ std::unique_ptr<weld::TreeIter> xNewEntry = FindEntry(pNewFormData);
+ m_xTreeView->start_editing(*xNewEntry);
+ m_bEditing = true;
+ }
+
+ FmControlData* NavigatorTree::NewControl(const OUString& rServiceName, const weld::TreeIter& rParentEntry, bool bEditName)
+ {
+ // get ParentForm
+ if (!GetNavModel()->GetFormShell())
+ return nullptr;
+ if (!IsFormEntry(rParentEntry))
+ return nullptr;
+
+ FmFormData* pParentFormData = weld::fromId<FmFormData*>(m_xTreeView->get_id(rParentEntry));
+ Reference<XForm> xParentForm(pParentFormData->GetFormIface());
+
+ // create new component
+ Reference<XComponentContext> xContext = comphelper::getProcessComponentContext();
+ Reference<XFormComponent> xNewComponent( xContext->getServiceManager()->createInstanceWithContext(rServiceName, xContext), UNO_QUERY);
+ if (!xNewComponent.is())
+ return nullptr;
+
+ FmControlData* pNewFormControlData = new FmControlData(xNewComponent, pParentFormData);
+
+ // set name
+ OUString sName = FmFormPageImpl::setUniqueName( xNewComponent, xParentForm );
+
+ pNewFormControlData->SetText( sName );
+
+ // insert FormComponent
+ GetNavModel()->Insert(pNewFormControlData, SAL_MAX_UINT32, true);
+ GetNavModel()->SetModified();
+
+ if (bEditName)
+ {
+ // switch to EditMode
+ std::unique_ptr<weld::TreeIter> xNewEntry = FindEntry( pNewFormControlData );
+ m_xTreeView->select(*xNewEntry);
+
+ m_xTreeView->start_editing(*xNewEntry);
+ m_bEditing = true;
+ }
+
+ return pNewFormControlData;
+ }
+
+ OUString NavigatorTree::GenerateName( FmEntryData const * pEntryData )
+ {
+ const sal_uInt16 nMaxCount = 99;
+ OUString aNewName;
+
+ // create base name
+ OUString aBaseName;
+ if( dynamic_cast<const FmFormData*>( pEntryData) != nullptr )
+ aBaseName = SvxResId( RID_STR_STDFORMNAME );
+ else if( dynamic_cast<const FmControlData*>( pEntryData) != nullptr )
+ aBaseName = SvxResId( RID_STR_CONTROL );
+
+
+ // create new name
+ FmFormData* pFormParentData = static_cast<FmFormData*>(pEntryData->GetParent());
+
+ for( sal_Int32 i=0; i<nMaxCount; i++ )
+ {
+ aNewName = aBaseName;
+ if( i>0 )
+ {
+ aNewName += " " + OUString::number(i);
+ }
+
+ if( GetNavModel()->FindData(aNewName, pFormParentData,false) == nullptr )
+ break;
+ }
+
+ return aNewName;
+ }
+
+ IMPL_LINK(NavigatorTree, EditedEntryHdl, const IterString&, rIterString, bool)
+ {
+ m_bEditing = false;
+
+ const weld::TreeIter& rIter = rIterString.first;
+
+ FmEntryData* pEntryData = weld::fromId<FmEntryData*>(m_xTreeView->get_id(rIter));
+ bool bRes = NavigatorTreeModel::Rename(pEntryData, rIterString.second);
+ if (!bRes)
+ {
+ m_xEditEntry = m_xTreeView->make_iterator(&rIter);
+ nEditEvent = Application::PostUserEvent(LINK(this, NavigatorTree, OnEdit));
+ }
+
+ return bRes;
+ }
+
+ IMPL_LINK_NOARG(NavigatorTree, OnEdit, void*, void)
+ {
+ nEditEvent = nullptr;
+ m_xTreeView->start_editing(*m_xEditEntry);
+ m_bEditing = true;
+ m_xEditEntry.reset();
+ }
+
+ IMPL_LINK_NOARG(NavigatorTree, OnEntrySelDesel, weld::TreeView&, void)
+ {
+ m_sdiState = SDI_DIRTY;
+
+ if (IsSelectionHandlingLocked())
+ return;
+
+ if (m_aSynchronizeTimer.IsActive())
+ m_aSynchronizeTimer.Stop();
+
+ m_aSynchronizeTimer.SetTimeout(EXPLORER_SYNC_DELAY);
+ m_aSynchronizeTimer.Start();
+ }
+
+ IMPL_LINK_NOARG(NavigatorTree, OnSynchronizeTimer, Timer *, void)
+ {
+ SynchronizeMarkList();
+ }
+
+ IMPL_LINK_NOARG(NavigatorTree, OnClipboardAction, OLocalExchange&, void)
+ {
+ if ( m_aControlExchange.isClipboardOwner() )
+ return;
+
+ if ( !doingKeyboardCut() )
+ return;
+
+ for (const auto& rEntry : m_aCutEntries)
+ {
+ if (!rEntry)
+ continue;
+ m_xTreeView->set_sensitive(*rEntry, true);
+ }
+ ListBoxEntrySet().swap(m_aCutEntries);
+
+ m_bKeyboardCut = false;
+ }
+
+ void NavigatorTree::ShowSelectionProperties(bool bForce)
+ {
+ // at first i need the FormShell
+ FmFormShell* pFormShell = GetNavModel()->GetFormShell();
+ if (!pFormShell)
+ // no shell -> impossible to set curObject -> leave
+ return;
+
+ CollectSelectionData(SDI_ALL);
+ SAL_WARN_IF(static_cast<size_t>(m_nFormsSelected + m_nControlsSelected
+ + (m_bRootSelected ? 1 : 0)) != m_arrCurrentSelection.size(),
+ "svx.form",
+ "NavigatorTree::ShowSelectionProperties : selection meta data invalid !");
+
+
+ InterfaceBag aSelection;
+ bool bSetSelectionAsMarkList = false;
+
+ if (m_bRootSelected)
+ ; // no properties for the root, neither for single nor for multi selection
+ else if ( m_nFormsSelected + m_nControlsSelected == 0 ) // none of the two should be less 0
+ ; // no selection -> no properties
+ else if ( m_nFormsSelected * m_nControlsSelected != 0 )
+ ; // mixed selection -> no properties
+ else
+ { // either only forms, or only controls are selected
+ if (m_arrCurrentSelection.size() == 1)
+ {
+ const std::unique_ptr<weld::TreeIter>& rIter = *m_arrCurrentSelection.begin();
+ if (m_nFormsSelected > 0)
+ { // exactly one form is selected
+ FmFormData* pFormData = weld::fromId<FmFormData*>(m_xTreeView->get_id(*rIter));
+ aSelection.insert( Reference< XInterface >( pFormData->GetFormIface(), UNO_QUERY ) );
+ }
+ else
+ { // exactly one control is selected (whatever hidden or normal)
+ FmEntryData* pEntryData = weld::fromId<FmEntryData*>(m_xTreeView->get_id(*rIter));
+
+ aSelection.insert( Reference< XInterface >( pEntryData->GetElement(), UNO_QUERY ) );
+ }
+ }
+ else
+ { // it's a MultiSelection, so we must build a MultiSet
+ if (m_nFormsSelected > 0)
+ { // ... only forms
+ // first of all collect PropertySet-Interfaces of the forms
+ SvLBoxEntrySortedArray::const_iterator it = m_arrCurrentSelection.begin();
+ for ( sal_Int32 i = 0; i < m_nFormsSelected; ++i )
+ {
+ const std::unique_ptr<weld::TreeIter>& rIter = *it;
+ FmFormData* pFormData = weld::fromId<FmFormData*>(m_xTreeView->get_id(*rIter));
+ aSelection.insert( pFormData->GetPropertySet() );
+ ++it;
+ }
+ }
+ else
+ { // ... only controls
+ if (m_nHiddenControls == m_nControlsSelected)
+ { // a MultiSet for properties of hidden controls
+ SvLBoxEntrySortedArray::const_iterator it = m_arrCurrentSelection.begin();
+ for ( sal_Int32 i = 0; i < m_nHiddenControls; ++i )
+ {
+ const std::unique_ptr<weld::TreeIter>& rIter = *it;
+ FmEntryData* pEntryData = weld::fromId<FmEntryData*>(m_xTreeView->get_id(*rIter));
+ aSelection.insert( pEntryData->GetPropertySet() );
+ ++it;
+ }
+ }
+ else if (m_nHiddenControls == 0)
+ { // only normal controls
+ bSetSelectionAsMarkList = true;
+ }
+ }
+ }
+
+ }
+
+ // and now my form and my SelObject
+ if ( bSetSelectionAsMarkList )
+ pFormShell->GetImpl()->setCurrentSelectionFromMark_Lock(pFormShell->GetFormView()->GetMarkedObjectList());
+ else
+ pFormShell->GetImpl()->setCurrentSelection_Lock(std::move(aSelection));
+
+ if (pFormShell->GetImpl()->IsPropBrwOpen_Lock() || bForce)
+ {
+ // and now deliver all to the PropertyBrowser
+ pFormShell->GetViewShell()->GetViewFrame().GetDispatcher()->Execute( SID_FM_SHOW_PROPERTY_BROWSER, SfxCallMode::ASYNCHRON );
+ }
+ }
+
+
+ void NavigatorTree::DeleteSelection()
+ {
+ // of course, i can't delete root
+ bool bRootSelected = m_xTreeView->is_selected(*m_xRootEntry);
+ auto nSelectedEntries = m_xTreeView->count_selected_rows();
+ if (bRootSelected && (nSelectedEntries > 1)) // root and other elements ?
+ m_xTreeView->unselect(*m_xRootEntry); // yes -> remove root from selection
+
+ if ((nSelectedEntries == 0) || bRootSelected) // still root ?
+ return; // -> only selected element -> leave
+
+ DBG_ASSERT(!m_bPrevSelectionMixed, "NavigatorTree::DeleteSelection() : delete permitted if mark and selection are inconsistent");
+
+ // i need the FormModel later
+ FmFormShell* pFormShell = GetNavModel()->GetFormShell();
+ if (!pFormShell)
+ return;
+ FmFormModel* pFormModel = pFormShell->GetFormModel();
+ if (!pFormModel)
+ return;
+
+ // now I have to safeguard the DeleteList: if you delete a form and a dependent element
+ // - in this order - than the SvLBoxEntryPtr of the dependent element is already invalid,
+ // when it should be deleted... you have to prohibit this GPF, that of course would happen,
+ // so I take the 'normalized' list
+ CollectSelectionData( SDI_NORMALIZED );
+
+ // see below for why we need this mapping from models to shapes
+ FmFormView* pFormView = pFormShell->GetFormView();
+ SdrPageView* pPageView = pFormView ? pFormView->GetSdrPageView() : nullptr;
+ SdrPage* pPage = pPageView ? pPageView->GetPage() : nullptr;
+ DBG_ASSERT( pPage, "NavigatorTree::DeleteSelection: invalid form page!" );
+
+ MapModelToShape aModelShapes;
+ if ( pPage )
+ collectShapeModelMapping( pPage, aModelShapes );
+
+ // problem: we have to use ExplorerModel::Remove, since only this one properly deletes Form objects.
+ // But, the controls themself must be deleted via DeleteMarked (else, the Writer has some problems
+ // somewhere). In case I'd first delete the structure, then the controls, the UNDO would not work
+ // (since UNDO then would mean to first restore the controls, then the structure, means their parent
+ // form). The other way round, the EntryDatas would be invalid, if I'd first delete the controls and
+ // then go on to the structure. This means I have to delete the forms *after* the normal controls, so
+ // that during UNDO, they're restored in the proper order.
+ pFormShell->GetImpl()->EnableTrackProperties_Lock(false);
+ for (SvLBoxEntrySortedArray::reverse_iterator it = m_arrCurrentSelection.rbegin();
+ it != m_arrCurrentSelection.rend(); )
+ {
+ const std::unique_ptr<weld::TreeIter>& rIter = *it;
+ FmEntryData* pCurrent = weld::fromId<FmEntryData*>(m_xTreeView->get_id(*rIter));
+
+ // a form ?
+ auto pFormData = dynamic_cast<FmFormData*>(pCurrent);
+
+ // because deletion is done by the view, and i build on its MarkList,
+ // but normally only direct controls, no indirect ones, are marked in a marked form,
+ // I have to do it later
+ if (pFormData)
+ MarkViewObj(pFormData, true/*deep*/);
+
+ // a hidden control ?
+ bool bIsHidden = IsHiddenControl(pCurrent);
+
+ // keep forms and hidden controls, the rest not
+ if (!pFormData && !bIsHidden)
+ {
+ // well, no form and no hidden control -> we can remove it from m_arrCurrentSelection, as it will
+ // be deleted automatically. This is because for every model (except forms and hidden control models)
+ // there exist a shape, which is marked _if_and_only_if_ the model is selected in our tree.
+ if ( aModelShapes.find( pCurrent->GetElement() ) != aModelShapes.end() )
+ {
+ // if there's a shape for the current entry, then either it is marked or it is in a
+ // hidden layer (#i28502#), or something like this.
+ // In the first case, it will be deleted below, in the second case, we currently don't
+ // delete it, as there's no real (working!) API for this, neither in UNO nor in non-UNO.
+ m_arrCurrentSelection.erase( --(it.base()) );
+ }
+ else
+ ++it;
+ // In case there is no shape for the current entry, we keep the entry in m_arrCurrentSelection,
+ // since then we can definitely remove it.
+ }
+ else
+ ++it;
+ }
+ pFormShell->GetImpl()->EnableTrackProperties_Lock(true);
+
+ // let the view delete the marked controls
+ pFormShell->GetFormView()->DeleteMarked();
+
+ // start UNDO at this point. Unfortunately, this results in 2 UNDO actions, since DeleteMarked is
+ // creating an own one. However, if we'd move it before DeleteMarked, Writer does not really like
+ // this ... :(
+ // #i31038#
+ {
+
+ // initialize UNDO
+ OUString aUndoStr;
+ if ( m_arrCurrentSelection.size() == 1 )
+ {
+ aUndoStr = SvxResId(RID_STR_UNDO_CONTAINER_REMOVE);
+ if (m_nFormsSelected)
+ aUndoStr = aUndoStr.replaceFirst( "#", SvxResId( RID_STR_FORM ) );
+ else
+ // it must be a control (else the root would be selected, but it cannot be deleted)
+ aUndoStr = aUndoStr.replaceFirst( "#", SvxResId( RID_STR_CONTROL ) );
+ }
+ else
+ {
+ aUndoStr = SvxResId(RID_STR_UNDO_CONTAINER_REMOVE_MULTIPLE);
+ aUndoStr = aUndoStr.replaceFirst( "#", OUString::number( m_arrCurrentSelection.size() ) );
+ }
+ pFormModel->BegUndo(aUndoStr);
+ }
+
+ // remove remaining structure
+ for (const auto& rpSelection : m_arrCurrentSelection)
+ {
+ FmEntryData* pCurrent = weld::fromId<FmEntryData*>(m_xTreeView->get_id(*rpSelection));
+
+ // if the entry still has children, we skipped deletion of one of those children.
+ // This may for instance be because the shape is in a hidden layer, where we're unable
+ // to remove it
+ if ( pCurrent->GetChildList()->size() )
+ continue;
+
+ // one remaining subtle problem, before deleting it : if it's a form and the shell
+ // knows it as CurrentObject, I have to tell it something else
+ if (auto pFormData = dynamic_cast<FmFormData*>( pCurrent))
+ {
+ Reference< XForm > xCurrentForm( pFormData->GetFormIface() );
+ if (pFormShell->GetImpl()->getCurrentForm_Lock() == xCurrentForm) // shell knows form to be deleted ?
+ pFormShell->GetImpl()->forgetCurrentForm_Lock(); // -> take away ...
+ }
+ GetNavModel()->Remove(pCurrent, true);
+ }
+ pFormModel->EndUndo();
+ }
+
+
+ void NavigatorTree::CollectSelectionData(SELDATA_ITEMS sdiHow)
+ {
+ DBG_ASSERT(sdiHow != SDI_DIRTY, "NavigatorTree::CollectSelectionData : ever thought about your parameter ? DIRTY ?");
+ if (sdiHow == m_sdiState)
+ return;
+
+ m_arrCurrentSelection.clear();
+ m_nFormsSelected = m_nControlsSelected = m_nHiddenControls = 0;
+ m_bRootSelected = false;
+
+ m_xTreeView->selected_foreach([this, sdiHow](weld::TreeIter& rSelectionLoop){
+ // count different elements
+ if (m_xTreeView->iter_compare(rSelectionLoop, *m_xRootEntry) == 0)
+ m_bRootSelected = true;
+ else
+ {
+ if (IsFormEntry(rSelectionLoop))
+ ++m_nFormsSelected;
+ else
+ {
+ ++m_nControlsSelected;
+ if (IsHiddenControl(weld::fromId<FmEntryData*>(m_xTreeView->get_id(rSelectionLoop))))
+ ++m_nHiddenControls;
+ }
+ }
+
+ if (sdiHow == SDI_NORMALIZED)
+ {
+ // don't take something with a selected ancestor
+ if (m_xTreeView->iter_compare(rSelectionLoop, *m_xRootEntry) == 0)
+ m_arrCurrentSelection.emplace(m_xTreeView->make_iterator(&rSelectionLoop));
+ else
+ {
+ std::unique_ptr<weld::TreeIter> xParentLoop(m_xTreeView->make_iterator(&rSelectionLoop));
+ bool bParentLoop = m_xTreeView->iter_parent(*xParentLoop);
+ while (bParentLoop)
+ {
+ // actually i would have to test, if parent is part of m_arr_CurrentSelection ...
+ // but if it's selected, then it's in m_arrCurrentSelection
+ // or one of its ancestors, which was selected earlier.
+ // In both cases IsSelected is enough
+ if (m_xTreeView->is_selected(*xParentLoop))
+ break;
+ else
+ {
+ if (m_xTreeView->iter_compare(*xParentLoop, *m_xRootEntry) == 0)
+ {
+ // until root (exclusive), there was no selected parent -> entry belongs to normalized list
+ m_arrCurrentSelection.emplace(m_xTreeView->make_iterator(&rSelectionLoop));
+ break;
+ }
+ else
+ bParentLoop = m_xTreeView->iter_parent(*xParentLoop);
+ }
+ }
+ }
+ }
+ else if (sdiHow == SDI_NORMALIZED_FORMARK)
+ {
+ std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(&rSelectionLoop));
+ bool bParent = m_xTreeView->iter_parent(*xParent);
+ if (!bParent || !m_xTreeView->is_selected(*xParent) || IsFormEntry(rSelectionLoop))
+ m_arrCurrentSelection.emplace(m_xTreeView->make_iterator(&rSelectionLoop));
+ }
+ else
+ m_arrCurrentSelection.emplace(m_xTreeView->make_iterator(&rSelectionLoop));
+
+ return false;
+ });
+
+ m_sdiState = sdiHow;
+ }
+
+ void NavigatorTree::SynchronizeSelection(FmEntryDataArray& arredToSelect)
+ {
+ LockSelectionHandling();
+ if (arredToSelect.empty())
+ {
+ m_xTreeView->unselect_all();
+ }
+ else
+ {
+ // compare current selection with requested SelectList
+ m_xTreeView->selected_foreach([this, &arredToSelect](weld::TreeIter& rSelection) {
+ FmEntryData* pCurrent = weld::fromId<FmEntryData*>(m_xTreeView->get_id(rSelection));
+ if (pCurrent != nullptr)
+ {
+ FmEntryDataArray::iterator it = arredToSelect.find(pCurrent);
+ if ( it != arredToSelect.end() )
+ { // entry already selected, but also in SelectList
+ // remove it from there
+ arredToSelect.erase(it);
+ } else
+ { // entry selected, but not in SelectList -> remove selection
+ m_xTreeView->unselect(rSelection);
+ // make it visible (maybe it's the only modification i do in this handler
+ // so you should see it
+ m_xTreeView->scroll_to_row(rSelection);
+ }
+ }
+ else
+ m_xTreeView->unselect(rSelection);
+
+ return false;
+ });
+
+ // now SelectList contains only entries, which have to be selected
+ // two possibilities : 1) run through SelectList, get SvTreeListEntry for every entry and select it (is more intuitive)
+ // 2) run through my SvLBoxEntries and select those, i can find in the SelectList
+ // 1) needs =(k*n) (k=length of SelectList, n=number of entries),
+ // plus the fact, that FindEntry uses extensive IsEqualWithoutChilden instead of comparing pointer to UserData
+ // 2) needs =(n*log k), duplicates some code from FindEntry
+ // This may be a frequently used code ( at every change in mark of the view!),
+ // so i use latter one
+ m_xTreeView->all_foreach([this, &arredToSelect](weld::TreeIter& rLoop){
+ FmEntryData* pCurEntryData = weld::fromId<FmEntryData*>(m_xTreeView->get_id(rLoop));
+ FmEntryDataArray::iterator it = arredToSelect.find(pCurEntryData);
+ if (it != arredToSelect.end())
+ {
+ m_xTreeView->select(rLoop);
+ m_xTreeView->scroll_to_row(rLoop);
+ }
+
+ return false;
+ });
+ }
+ UnlockSelectionHandling();
+ }
+
+
+ void NavigatorTree::SynchronizeSelection()
+ {
+ // shell and view
+ FmFormShell* pFormShell = GetNavModel()->GetFormShell();
+ if(!pFormShell) return;
+
+ FmFormView* pFormView = pFormShell->GetFormView();
+ if (!pFormView) return;
+
+ GetNavModel()->BroadcastMarkedObjects(pFormView->GetMarkedObjectList());
+ }
+
+
+ void NavigatorTree::SynchronizeMarkList()
+ {
+ // i'll need this shell
+ FmFormShell* pFormShell = GetNavModel()->GetFormShell();
+ if (!pFormShell) return;
+
+ CollectSelectionData(SDI_NORMALIZED_FORMARK);
+
+ // the view shouldn't notify now if MarkList changed
+ pFormShell->GetImpl()->EnableTrackProperties_Lock(false);
+
+ UnmarkAllViewObj();
+
+ for (auto& rSelectionLoop : m_arrCurrentSelection)
+ {
+ // When form selection, mark all controls of form
+ if (IsFormEntry(*rSelectionLoop) && m_xTreeView->iter_compare(*rSelectionLoop, *m_xRootEntry) != 0)
+ MarkViewObj(weld::fromId<FmFormData*>(m_xTreeView->get_id(*rSelectionLoop)), false/*deep*/);
+
+ // When control selection, mark Control-SdrObjects
+ else if (IsFormComponentEntry(*rSelectionLoop))
+ {
+ FmControlData* pControlData = weld::fromId<FmControlData*>(m_xTreeView->get_id(*rSelectionLoop));
+ if (pControlData)
+ {
+
+ // When HiddenControl no object can be selected
+ Reference< XFormComponent > xFormComponent( pControlData->GetFormComponent());
+ if (!xFormComponent.is())
+ continue;
+ Reference< XPropertySet > xSet(xFormComponent, UNO_QUERY);
+ if (!xSet.is())
+ continue;
+
+ sal_uInt16 nClassId = ::comphelper::getINT16(xSet->getPropertyValue(FM_PROP_CLASSID));
+ if (nClassId != FormComponentType::HIDDENCONTROL)
+ MarkViewObj(pControlData);
+ }
+ }
+ }
+
+ // if PropertyBrowser is open, I have to adopt it according to my selection
+ // (Not as MarkList of view : if a form is selected, all belonging controls are selected in the view
+ // but of course i want to see the form-properties
+ ShowSelectionProperties();
+
+ // reset flag at view
+ pFormShell->GetImpl()->EnableTrackProperties_Lock(true);
+
+ // if exactly one form is selected now, shell should notice it as CurrentForm
+ // (if selection handling isn't locked, view cares about it in MarkListHasChanged
+ // but mechanism doesn't work, if form is empty for example
+ if ((m_arrCurrentSelection.size() != 1) || (m_nFormsSelected != 1))
+ return;
+
+ std::unique_ptr<weld::TreeIter> xSelected(m_xTreeView->make_iterator());
+ if (!m_xTreeView->get_selected(xSelected.get()))
+ xSelected.reset();
+ FmFormData* pSingleSelectionData = xSelected ? dynamic_cast<FmFormData*>(weld::fromId<FmEntryData*>(m_xTreeView->get_id(*xSelected)))
+ : nullptr;
+ DBG_ASSERT( pSingleSelectionData, "NavigatorTree::SynchronizeMarkList: invalid selected form!" );
+ if ( pSingleSelectionData )
+ {
+ InterfaceBag aSelection;
+ aSelection.insert( Reference< XInterface >( pSingleSelectionData->GetFormIface(), UNO_QUERY ) );
+ pFormShell->GetImpl()->setCurrentSelection_Lock(std::move(aSelection));
+ }
+ }
+
+ bool NavigatorTree::IsHiddenControl(FmEntryData const * pEntryData)
+ {
+ if (pEntryData == nullptr) return false;
+
+ Reference< XPropertySet > xProperties( pEntryData->GetPropertySet() );
+ if (::comphelper::hasProperty(FM_PROP_CLASSID, xProperties))
+ {
+ Any aClassID = xProperties->getPropertyValue( FM_PROP_CLASSID );
+ return (::comphelper::getINT16(aClassID) == FormComponentType::HIDDENCONTROL);
+ }
+ return false;
+ }
+
+ void NavigatorTree::UnmarkAllViewObj()
+ {
+ FmFormShell* pFormShell = GetNavModel()->GetFormShell();
+ if( !pFormShell )
+ return;
+ FmFormView* pFormView = pFormShell->GetFormView();
+ pFormView->UnMarkAll();
+ }
+
+ void NavigatorTree::MarkViewObj(FmFormData const * pFormData, bool bDeep )
+ {
+ FmFormShell* pFormShell = GetNavModel()->GetFormShell();
+ if( !pFormShell )
+ return;
+
+ // first collect all sdrobjects
+ ::std::set< Reference< XFormComponent > > aObjects;
+ CollectObjects(pFormData,bDeep,aObjects);
+
+
+ // find and select appropriate SdrObj in page
+ FmFormView* pFormView = pFormShell->GetFormView();
+ SdrPageView* pPageView = pFormView->GetSdrPageView();
+ SdrPage* pPage = pPageView->GetPage();
+ //FmFormPage* pFormPage = dynamic_cast< FmFormPage* >( pPage );
+
+ SdrObjListIter aIter( pPage );
+ while ( aIter.IsMore() )
+ {
+ SdrObject* pSdrObject = aIter.Next();
+ FmFormObj* pFormObject = FmFormObj::GetFormObject( pSdrObject );
+ if ( !pFormObject )
+ continue;
+
+ Reference< XFormComponent > xControlModel( pFormObject->GetUnoControlModel(),UNO_QUERY );
+ if ( xControlModel.is() && aObjects.find(xControlModel) != aObjects.end() && !pFormView->IsObjMarked( pSdrObject ) )
+ {
+ // unfortunately, the writer doesn't like marking an already-marked object, again, so reset the mark first
+ pFormView->MarkObj( pSdrObject, pPageView );
+ }
+ } // while ( aIter.IsMore() )
+ // make the mark visible
+ ::tools::Rectangle aMarkRect( pFormView->GetAllMarkedRect());
+ for ( sal_uInt32 i = 0; i < pFormView->PaintWindowCount(); ++i )
+ {
+ SdrPaintWindow* pPaintWindow = pFormView->GetPaintWindow( i );
+ OutputDevice& rOutDev = pPaintWindow->GetOutputDevice();
+ if ( ( OUTDEV_WINDOW == rOutDev.GetOutDevType() ) && !aMarkRect.IsEmpty() )
+ {
+ pFormView->MakeVisible( aMarkRect, *rOutDev.GetOwnerWindow() );
+ }
+ } // for ( sal_uInt32 i = 0; i < pFormView->PaintWindowCount(); ++i )
+ }
+
+ void NavigatorTree::CollectObjects(FmFormData const * pFormData, bool bDeep, ::std::set< Reference< XFormComponent > >& _rObjects)
+ {
+ FmEntryDataList* pChildList = pFormData->GetChildList();
+ for( size_t i = 0; i < pChildList->size(); ++i )
+ {
+ FmEntryData* pEntryData = pChildList->at( i );
+ if( auto pControlData = dynamic_cast<FmControlData*>( pEntryData) )
+ {
+ _rObjects.insert(pControlData->GetFormComponent());
+ } // if( dynamic_cast<const FmControlData*>( pEntryData) != nullptr )
+ else if (bDeep)
+ if (auto pEntryFormData = dynamic_cast<FmFormData*>( pEntryData))
+ CollectObjects(pEntryFormData, bDeep, _rObjects);
+ } // for( sal_uInt32 i=0; i<pChildList->Count(); i++ )
+ }
+
+ void NavigatorTree::MarkViewObj( FmControlData const * pControlData)
+ {
+ if( !pControlData )
+ return;
+ FmFormShell* pFormShell = GetNavModel()->GetFormShell();
+ if( !pFormShell )
+ return;
+
+
+ // find and select appropriate SdrObj
+ FmFormView* pFormView = pFormShell->GetFormView();
+ Reference< XFormComponent > xFormComponent( pControlData->GetFormComponent());
+ SdrPageView* pPageView = pFormView->GetSdrPageView();
+ SdrPage* pPage = pPageView->GetPage();
+
+ bool bPaint = false;
+ SdrObjListIter aIter( pPage );
+ while ( aIter.IsMore() )
+ {
+ SdrObject* pSdrObject = aIter.Next();
+ FmFormObj* pFormObject = FmFormObj::GetFormObject( pSdrObject );
+ if ( !pFormObject )
+ continue;
+
+ Reference< XInterface > xControlModel( pFormObject->GetUnoControlModel() );
+ if ( xControlModel != xFormComponent )
+ continue;
+
+ // mark the object
+ if ( !pFormView->IsObjMarked( pSdrObject ) )
+ // unfortunately, the writer doesn't like marking an already-marked object, again, so reset the mark first
+ pFormView->MarkObj( pSdrObject, pPageView );
+
+ bPaint = true;
+
+ } // while ( aIter.IsMore() )
+ if ( !bPaint )
+ return;
+
+ // make the mark visible
+ ::tools::Rectangle aMarkRect( pFormView->GetAllMarkedRect());
+ for ( sal_uInt32 i = 0; i < pFormView->PaintWindowCount(); ++i )
+ {
+ SdrPaintWindow* pPaintWindow = pFormView->GetPaintWindow( i );
+ OutputDevice& rOutDev = pPaintWindow->GetOutputDevice();
+ if ( OUTDEV_WINDOW == rOutDev.GetOutDevType() )
+ {
+ pFormView->MakeVisible( aMarkRect, *rOutDev.GetOwnerWindow() );
+ }
+ } // for ( sal_uInt32 i = 0; i < pFormView->PaintWindowCount(); ++i )
+ }
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/navigatortreemodel.cxx b/svx/source/form/navigatortreemodel.cxx
new file mode 100644
index 0000000000..5fae42901d
--- /dev/null
+++ b/svx/source/form/navigatortreemodel.cxx
@@ -0,0 +1,906 @@
+/* -*- 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 <svx/dialmgr.hxx>
+#include <svx/fmshell.hxx>
+#include <svx/fmmodel.hxx>
+#include <svx/fmpage.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdogrp.hxx>
+
+#include <fmprop.hxx>
+
+#include <fmundo.hxx>
+#include <fmexpl.hxx>
+#include <svx/strings.hrc>
+#include <fmshimp.hxx>
+#include <fmobj.hxx>
+#include <o3tl/safeint.hxx>
+#include <sfx2/objsh.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <com/sun/star/container/XContainer.hpp>
+#include <comphelper/types.hxx>
+
+
+namespace svxform
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::form;
+ using namespace ::com::sun::star::awt;
+ using namespace ::com::sun::star::container;
+ using namespace ::com::sun::star::script;
+ using namespace ::com::sun::star::sdb;
+
+ OFormComponentObserver::OFormComponentObserver(NavigatorTreeModel* _pModel)
+ :m_pNavModel(_pModel)
+ ,m_nLocks(0)
+ ,m_bCanUndo(true)
+ {
+ }
+
+ // XPropertyChangeListener
+
+ void SAL_CALL OFormComponentObserver::disposing(const EventObject& Source)
+ {
+ Remove( Source.Source );
+ }
+
+
+ void SAL_CALL OFormComponentObserver::propertyChange(const PropertyChangeEvent& evt)
+ {
+ if( !m_pNavModel ) return;
+ if( evt.PropertyName != FM_PROP_NAME ) return;
+
+ Reference< XFormComponent > xFormComponent(evt.Source, UNO_QUERY);
+ Reference< XForm > xForm(evt.Source, UNO_QUERY);
+
+ FmEntryData* pEntryData( nullptr );
+ if( xForm.is() )
+ pEntryData = m_pNavModel->FindData( xForm, m_pNavModel->GetRootList() );
+ else if( xFormComponent.is() )
+ pEntryData = m_pNavModel->FindData( xFormComponent, m_pNavModel->GetRootList() );
+
+ if( pEntryData )
+ {
+ OUString aNewName = ::comphelper::getString(evt.NewValue);
+ pEntryData->SetText( aNewName );
+ FmNavNameChangedHint aNameChangedHint( pEntryData, aNewName );
+ m_pNavModel->Broadcast( aNameChangedHint );
+ }
+ }
+
+ // XContainerListener
+
+ void SAL_CALL OFormComponentObserver::elementInserted(const ContainerEvent& evt)
+ {
+ if (IsLocked() || !m_pNavModel)
+ return;
+
+ // insert no Undoaction
+ m_bCanUndo = false;
+
+ Reference< XInterface > xTemp;
+ evt.Element >>= xTemp;
+ Insert(xTemp, ::comphelper::getINT32(evt.Accessor));
+
+ m_bCanUndo = true;
+ }
+
+
+ void OFormComponentObserver::Insert(const Reference< XInterface > & xIface, sal_Int32 nIndex)
+ {
+ Reference< XForm > xForm(xIface, UNO_QUERY);
+ if (xForm.is())
+ {
+ m_pNavModel->InsertForm(xForm, sal_uInt32(nIndex));
+ Reference< XIndexContainer > xContainer(xForm, UNO_QUERY);
+ Reference< XInterface > xTemp;
+ for (sal_Int32 i = 0; i < xContainer->getCount(); i++)
+ {
+ xContainer->getByIndex(i) >>= xTemp;
+ Insert(xTemp, i);
+ }
+ }
+ else
+ {
+ Reference< XFormComponent > xFormComp(xIface, UNO_QUERY);
+ if (xFormComp.is())
+ m_pNavModel->InsertFormComponent(xFormComp, sal_uInt32(nIndex));
+ }
+ }
+
+
+ void SAL_CALL OFormComponentObserver::elementReplaced(const ContainerEvent& evt)
+ {
+ if (IsLocked() || !m_pNavModel)
+ return;
+
+ m_bCanUndo = false;
+
+ // delete EntryData
+ Reference< XFormComponent > xReplaced;
+ evt.ReplacedElement >>= xReplaced;
+ FmEntryData* pEntryData = m_pNavModel->FindData(xReplaced, m_pNavModel->GetRootList());
+ if (pEntryData)
+ {
+ if (dynamic_cast<const FmControlData*>( pEntryData) != nullptr)
+ {
+ Reference< XFormComponent > xComp;
+ evt.Element >>= xComp;
+ DBG_ASSERT(xComp.is(), "OFormComponentObserver::elementReplaced : invalid argument !");
+ // FmControlData should be coupled with XFormComponent
+ m_pNavModel->ReplaceFormComponent(xReplaced, xComp);
+ }
+ else if (dynamic_cast<const FmFormData*>( pEntryData) != nullptr)
+ {
+ OSL_FAIL("replacing forms not implemented yet !");
+ }
+ }
+
+ m_bCanUndo = true;
+ }
+
+
+ void OFormComponentObserver::Remove( const css::uno::Reference< css::uno::XInterface >& _rxElement )
+ {
+ if (IsLocked() || !m_pNavModel)
+ return;
+
+ m_bCanUndo = false;
+
+
+ // delete EntryData
+ FmEntryData* pEntryData = m_pNavModel->FindData( _rxElement, m_pNavModel->GetRootList() );
+ if (pEntryData)
+ m_pNavModel->Remove(pEntryData);
+
+ m_bCanUndo = true;
+ }
+
+
+ void SAL_CALL OFormComponentObserver::elementRemoved(const ContainerEvent& evt)
+ {
+ Reference< XInterface > xElement;
+ evt.Element >>= xElement;
+ Remove( xElement );
+ }
+
+ NavigatorTreeModel::NavigatorTreeModel()
+ :m_pFormShell(nullptr)
+ ,m_pFormPage(nullptr)
+ ,m_pFormModel(nullptr)
+ {
+ m_pPropChangeList = new OFormComponentObserver(this);
+ m_pRootList.reset( new FmEntryDataList() );
+ }
+
+ NavigatorTreeModel::~NavigatorTreeModel()
+ {
+
+ // unregister Listener
+ if( m_pFormShell)
+ {
+ FmFormModel* pFormModel = m_pFormShell->GetFormModel();
+ if( pFormModel && IsListening(*pFormModel))
+ EndListening( *pFormModel );
+
+ if (IsListening(*m_pFormShell))
+ EndListening(*m_pFormShell);
+ }
+
+ Clear();
+ m_pRootList.reset();
+ m_pPropChangeList->ReleaseModel();
+ }
+
+
+ void NavigatorTreeModel::SetModified()
+ {
+ if( !m_pFormShell ) return;
+ SfxObjectShell* pObjShell = m_pFormShell->GetFormModel()->GetObjectShell();
+ if( !pObjShell ) return;
+ pObjShell->SetModified();
+ }
+
+
+ void NavigatorTreeModel::Clear()
+ {
+ Reference< css::form::XForms > xForms( GetForms());
+ if(xForms.is())
+ xForms->removeContainerListener(m_pPropChangeList);
+
+
+ // delete RootList
+ GetRootList()->clear();
+
+
+ // notify UI
+ FmNavClearedHint aClearedHint;
+ Broadcast( aClearedHint );
+ }
+
+
+ Reference< css::form::XForms > NavigatorTreeModel::GetForms() const
+ {
+ if( !m_pFormShell || !m_pFormShell->GetCurPage())
+ return nullptr;
+ else
+ return m_pFormShell->GetCurPage()->GetForms();
+ }
+
+
+ void NavigatorTreeModel::Insert(FmEntryData* pEntry, sal_uInt32 nRelPos, bool bAlterModel)
+ {
+ if (IsListening(*m_pFormModel))
+ EndListening(*m_pFormModel);
+
+ m_pPropChangeList->Lock();
+ FmFormData* pFolder = static_cast<FmFormData*>( pEntry->GetParent() );
+ Reference< XChild > xElement( pEntry->GetChildIFace() );
+ if (bAlterModel)
+ {
+ OUString aStr;
+ if (dynamic_cast<const FmFormData*>( pEntry) != nullptr)
+ aStr = SvxResId(RID_STR_FORM);
+ else
+ aStr = SvxResId(RID_STR_CONTROL);
+
+ Reference< XIndexContainer > xContainer;
+ if (pFolder)
+ xContainer.set(pFolder->GetFormIface(), UNO_QUERY);
+ else
+ xContainer = GetForms();
+
+ bool bUndo = m_pFormModel->IsUndoEnabled();
+
+ if( bUndo )
+ {
+ OUString aUndoStr(SvxResId(RID_STR_UNDO_CONTAINER_INSERT));
+ aUndoStr = aUndoStr.replaceFirst("#", aStr);
+ m_pFormModel->BegUndo(aUndoStr);
+ }
+
+ if (nRelPos >= o3tl::make_unsigned(xContainer->getCount()))
+ nRelPos = static_cast<sal_uInt32>(xContainer->getCount());
+
+ // UndoAction
+ if ( bUndo && m_pPropChangeList->CanUndo())
+ {
+ m_pFormModel->AddUndo(std::make_unique<FmUndoContainerAction>(*m_pFormModel,
+ FmUndoContainerAction::Inserted,
+ xContainer,
+ xElement,
+ nRelPos));
+ }
+
+ // Element has to be of the expected type by the container
+ if (xContainer->getElementType() ==
+ cppu::UnoType<XForm>::get())
+
+ {
+ Reference< XForm > xElementAsForm(xElement, UNO_QUERY);
+ xContainer->insertByIndex(nRelPos, Any(xElementAsForm));
+ }
+ else if (xContainer->getElementType() ==
+ cppu::UnoType<XFormComponent>::get())
+
+ {
+ Reference< XFormComponent > xElementAsComponent(xElement, UNO_QUERY);
+ xContainer->insertByIndex(nRelPos, Any(xElementAsComponent));
+ }
+ else
+ {
+ OSL_FAIL("NavigatorTreeModel::Insert : the parent container needs an elementtype I don't know !");
+ }
+
+ if( bUndo )
+ m_pFormModel->EndUndo();
+ }
+
+ // register as PropertyChangeListener
+ Reference< XPropertySet > xSet(xElement, UNO_QUERY);
+ if( xSet.is() )
+ xSet->addPropertyChangeListener( FM_PROP_NAME, m_pPropChangeList );
+
+
+ // Remove data from model
+ if (dynamic_cast<const FmFormData*>( pEntry) != nullptr)
+ {
+ Reference< XContainer > xContainer(xElement, UNO_QUERY);
+ if (xContainer.is())
+ xContainer->addContainerListener(m_pPropChangeList);
+ }
+
+ if (pFolder)
+ pFolder->GetChildList()->insert( std::unique_ptr<FmEntryData>(pEntry), nRelPos );
+ else
+ GetRootList()->insert( std::unique_ptr<FmEntryData>(pEntry), nRelPos );
+
+
+ // notify UI
+ FmNavInsertedHint aInsertedHint( pEntry, nRelPos );
+ Broadcast( aInsertedHint );
+
+ m_pPropChangeList->UnLock();
+ if (IsListening(*m_pFormModel))
+ StartListening(*m_pFormModel);
+ }
+
+
+ void NavigatorTreeModel::Remove(FmEntryData* pEntry, bool bAlterModel)
+ {
+
+ // get form and parent
+ if (!pEntry || !m_pFormModel)
+ return;
+
+ if (IsListening(*m_pFormModel))
+ EndListening(*m_pFormModel);
+
+ const bool bUndo = m_pFormModel->IsUndoEnabled();
+
+ m_pPropChangeList->Lock();
+ FmFormData* pFolder = static_cast<FmFormData*>( pEntry->GetParent() );
+ Reference< XChild > xElement ( pEntry->GetChildIFace() );
+ if (bAlterModel)
+ {
+ OUString aStr;
+ if (dynamic_cast<const FmFormData*>( pEntry) != nullptr)
+ aStr = SvxResId(RID_STR_FORM);
+ else
+ aStr = SvxResId(RID_STR_CONTROL);
+
+ if( bUndo )
+ {
+ OUString aUndoStr(SvxResId(RID_STR_UNDO_CONTAINER_REMOVE));
+ aUndoStr = aUndoStr.replaceFirst("#", aStr);
+ m_pFormModel->BegUndo(aUndoStr);
+ }
+ }
+
+ // now real deletion of data form model
+ if (auto pFormData = dynamic_cast<FmFormData*>( pEntry))
+ RemoveForm(pFormData);
+ else
+ RemoveFormComponent(static_cast<FmControlData*>(pEntry));
+
+
+ if (bAlterModel)
+ {
+ Reference< XIndexContainer > xContainer(xElement->getParent(), UNO_QUERY);
+ // remove from Container
+ sal_Int32 nContainerIndex = getElementPos(xContainer, xElement);
+ // UndoAction
+ if (nContainerIndex >= 0)
+ {
+ if ( bUndo && m_pPropChangeList->CanUndo())
+ {
+ m_pFormModel->AddUndo(std::make_unique<FmUndoContainerAction>(*m_pFormModel,
+ FmUndoContainerAction::Removed,
+ xContainer,
+ xElement, nContainerIndex ));
+ }
+ else if( !m_pPropChangeList->CanUndo() )
+ {
+ FmUndoContainerAction::DisposeElement( xElement );
+ }
+
+ xContainer->removeByIndex(nContainerIndex );
+ }
+
+ if( bUndo )
+ m_pFormModel->EndUndo();
+ }
+
+ // remove from parent
+ if (pFolder)
+ pFolder->GetChildList()->removeNoDelete( pEntry );
+ else
+ {
+ GetRootList()->removeNoDelete( pEntry );
+
+ // If root has no more form, reset CurForm at shell
+ if ( !GetRootList()->size() )
+ m_pFormShell->GetImpl()->forgetCurrentForm_Lock();
+ }
+
+
+ // notify UI
+ FmNavRemovedHint aRemovedHint( pEntry );
+ Broadcast( aRemovedHint );
+
+ // delete entry
+ delete pEntry;
+
+ m_pPropChangeList->UnLock();
+ StartListening(*m_pFormModel);
+ }
+
+
+ void NavigatorTreeModel::RemoveForm(FmFormData const * pFormData)
+ {
+
+ // get form and parent
+ if (!pFormData || !m_pFormModel)
+ return;
+
+ FmEntryDataList* pChildList = pFormData->GetChildList();
+ for ( size_t i = pChildList->size(); i > 0; )
+ {
+ FmEntryData* pEntryData = pChildList->at( --i );
+
+
+ // Child is form -> recursive call
+ if( auto pChildFormData = dynamic_cast<FmFormData*>( pEntryData) )
+ RemoveForm(pChildFormData);
+ else if( auto pChildControlData = dynamic_cast<FmControlData*>( pEntryData) )
+ RemoveFormComponent(pChildControlData);
+ }
+
+
+ // unregister as PropertyChangeListener
+ Reference< XPropertySet > xSet( pFormData->GetPropertySet() );
+ if ( xSet.is() )
+ xSet->removePropertyChangeListener( FM_PROP_NAME, m_pPropChangeList );
+ }
+
+
+ void NavigatorTreeModel::RemoveFormComponent(FmControlData const * pControlData)
+ {
+
+ // get control and parent
+ if (!pControlData)
+ return;
+
+
+ // unregister as PropertyChangeListener
+ Reference< XPropertySet > xSet( pControlData->GetPropertySet() );
+ if (xSet.is())
+ xSet->removePropertyChangeListener( FM_PROP_NAME, m_pPropChangeList);
+ }
+
+
+ void NavigatorTreeModel::FillBranch( FmFormData* pFormData )
+ {
+
+ // insert forms from root
+ if( pFormData == nullptr )
+ {
+ Reference< XIndexContainer > xForms = GetForms();
+ if (!xForms.is())
+ return;
+
+ Reference< XForm > xSubForm;
+ for (sal_Int32 i=0; i<xForms->getCount(); ++i)
+ {
+ DBG_ASSERT( xForms->getByIndex(i).getValueType() == cppu::UnoType<XForm>::get(),
+ "NavigatorTreeModel::FillBranch : the root container should supply only elements of type XForm");
+
+ xForms->getByIndex(i) >>= xSubForm;
+ FmFormData* pSubFormData = new FmFormData(xSubForm, pFormData);
+ Insert( pSubFormData );
+
+ // new branch, if SubForm contains Subforms itself
+ FillBranch( pSubFormData );
+ }
+ }
+
+
+ // insert components
+ else
+ {
+ Reference< XIndexContainer > xComponents( GetFormComponents(pFormData));
+ if( !xComponents.is() ) return;
+
+ FmControlData* pNewControlData;
+ FmFormData* pSubFormData;
+
+ Reference< XFormComponent > xCurrentComponent;
+ for (sal_Int32 j=0; j<xComponents->getCount(); ++j)
+ {
+ xComponents->getByIndex(j) >>= xCurrentComponent;
+ Reference< XForm > xSubForm(xCurrentComponent, UNO_QUERY);
+
+ if (xSubForm.is())
+ { // actual component is a form
+ pSubFormData = new FmFormData(xSubForm, pFormData);
+ Insert(pSubFormData);
+
+
+ // new branch, if SubForm contains Subforms itself
+ FillBranch(pSubFormData);
+ }
+ else
+ {
+ pNewControlData = new FmControlData(xCurrentComponent, pFormData);
+ Insert(pNewControlData);
+ }
+ }
+ }
+ }
+
+
+ void NavigatorTreeModel::InsertForm(const Reference< XForm > & xForm, sal_uInt32 nRelPos)
+ {
+ FmFormData* pFormData = static_cast<FmFormData*>(FindData( xForm, GetRootList() ));
+ if (pFormData)
+ return;
+
+
+ // set ParentData
+ Reference< XInterface > xIFace( xForm->getParent());
+ Reference< XForm > xParentForm(xIFace, UNO_QUERY);
+ FmFormData* pParentData = nullptr;
+ if (xParentForm.is())
+ pParentData = static_cast<FmFormData*>(FindData( xParentForm, GetRootList() ));
+
+ pFormData = new FmFormData(xForm, pParentData);
+ Insert( pFormData, nRelPos );
+ }
+
+
+ void NavigatorTreeModel::InsertFormComponent(const Reference< XFormComponent > & xComp, sal_uInt32 nRelPos)
+ {
+
+ // set ParentData
+ Reference< XInterface > xIFace( xComp->getParent());
+ Reference< XForm > xForm(xIFace, UNO_QUERY);
+ if (!xForm.is())
+ return;
+
+ FmFormData* pParentData = static_cast<FmFormData*>(FindData( xForm, GetRootList() ));
+ if( !pParentData )
+ {
+ pParentData = new FmFormData(xForm, nullptr);
+ Insert( pParentData );
+ }
+
+ if (!FindData(xComp, pParentData->GetChildList(),false))
+ {
+
+ // set new EntryData
+ FmEntryData* pNewEntryData = new FmControlData(xComp, pParentData);
+
+
+ // insert new EntryData
+ Insert( pNewEntryData, nRelPos );
+ }
+ }
+
+ void NavigatorTreeModel::ReplaceFormComponent(
+ const Reference< XFormComponent > & xOld,
+ const Reference< XFormComponent > & xNew
+ )
+ {
+ FmEntryData* pData = FindData(xOld, GetRootList());
+ assert(dynamic_cast<const FmControlData*>( pData)); //NavigatorTreeModel::ReplaceFormComponent : invalid argument
+ auto pControlData = dynamic_cast<FmControlData*>( pData);
+ if (!pControlData)
+ return;
+ pControlData->ModelReplaced(xNew);
+
+ FmNavModelReplacedHint aReplacedHint( pData );
+ Broadcast( aReplacedHint );
+ }
+
+ FmEntryData* NavigatorTreeModel::FindData(const Reference< XInterface > & xElement, FmEntryDataList* pDataList, bool bRecurs)
+ {
+ // normalize
+ Reference< XInterface > xIFace( xElement, UNO_QUERY );
+
+ for ( size_t i = 0; i < pDataList->size(); i++ )
+ {
+ FmEntryData* pEntryData = pDataList->at( i );
+ if ( pEntryData->GetElement().get() == xIFace.get() )
+ return pEntryData;
+ else if (bRecurs)
+ {
+ pEntryData = FindData( xElement, pEntryData->GetChildList() );
+ if (pEntryData)
+ return pEntryData;
+ }
+ }
+ return nullptr;
+ }
+
+
+ FmEntryData* NavigatorTreeModel::FindData( const OUString& rText, FmFormData const * pParentData, bool bRecurs )
+ {
+ FmEntryDataList* pDataList;
+ if( !pParentData )
+ pDataList = GetRootList();
+ else
+ pDataList = pParentData->GetChildList();
+
+ OUString aEntryText;
+ FmEntryData* pEntryData;
+ FmEntryData* pChildData;
+
+ for( size_t i = 0; i < pDataList->size(); i++ )
+ {
+ pEntryData = pDataList->at( i );
+ aEntryText = pEntryData->GetText();
+
+ if (rText == aEntryText)
+ return pEntryData;
+
+ if (FmFormData* pFormData = bRecurs ? dynamic_cast<FmFormData*>(pEntryData) : nullptr)
+ {
+ pChildData = FindData(rText, pFormData, true);
+ if( pChildData )
+ return pChildData;
+ }
+ }
+
+ return nullptr;
+ }
+
+ void NavigatorTreeModel::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
+ {
+ if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
+ {
+ const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
+ switch( pSdrHint->GetKind() )
+ {
+ case SdrHintKind::ObjectInserted:
+ InsertSdrObj(pSdrHint->GetObject());
+ break;
+ case SdrHintKind::ObjectRemoved:
+ RemoveSdrObj(pSdrHint->GetObject());
+ break;
+ default:
+ break;
+ }
+ }
+ // is shell gone?
+ else if (rHint.GetId() == SfxHintId::Dying)
+ {
+ UpdateContent(nullptr);
+ }
+ // changed mark of controls?
+ else if (const FmNavViewMarksChanged* pvmcHint = dynamic_cast<const FmNavViewMarksChanged*>(&rHint))
+ {
+ BroadcastMarkedObjects(pvmcHint->GetAffectedView()->GetMarkedObjectList());
+ }
+ }
+
+ void NavigatorTreeModel::InsertSdrObj( const SdrObject* pObj )
+ {
+ const FmFormObj* pFormObject = FmFormObj::GetFormObject( pObj );
+ if ( pFormObject )
+ {
+ try
+ {
+ Reference< XFormComponent > xFormComponent( pFormObject->GetUnoControlModel(), UNO_QUERY_THROW );
+ Reference< XIndexAccess > xContainer( xFormComponent->getParent(), UNO_QUERY_THROW );
+
+ sal_Int32 nPos = getElementPos( xContainer, xFormComponent );
+ InsertFormComponent( xFormComponent, nPos );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ else if ( pObj->IsGroupObject() )
+ {
+ SdrObjListIter aIter( pObj->GetSubList() );
+ while ( aIter.IsMore() )
+ InsertSdrObj( aIter.Next() );
+ }
+ }
+
+
+ void NavigatorTreeModel::RemoveSdrObj( const SdrObject* pObj )
+ {
+ const FmFormObj* pFormObject = FmFormObj::GetFormObject( pObj );
+ if ( pFormObject )
+ {
+ try
+ {
+ Reference< XFormComponent > xFormComponent( pFormObject->GetUnoControlModel(), UNO_QUERY_THROW );
+ FmEntryData* pEntryData = FindData( xFormComponent, GetRootList() );
+ if ( pEntryData )
+ Remove( pEntryData );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ else if ( pObj->IsGroupObject() )
+ {
+ SdrObjListIter aIter( pObj->GetSubList() );
+ while ( aIter.IsMore() )
+ RemoveSdrObj( aIter.Next() );
+ }
+ }
+
+ bool NavigatorTreeModel::InsertFormComponent(FmNavRequestSelectHint& rHint, SdrObject* pObject)
+ {
+ if ( auto pObjGroup = dynamic_cast<const SdrObjGroup*>( pObject) )
+ { // descend recursively
+ const SdrObjList *pChildren = pObjGroup->GetSubList();
+ for (const rtl::Reference<SdrObject>& pCurrent : *pChildren)
+ {
+ if (!InsertFormComponent(rHint, pCurrent.get()))
+ return false;
+ }
+ }
+ else
+ {
+ FmFormObj* pFormObject = FmFormObj::GetFormObject( pObject );
+ if ( !pFormObject )
+ return false;
+
+ try
+ {
+ Reference< XFormComponent > xFormViewControl( pFormObject->GetUnoControlModel(), UNO_QUERY_THROW );
+ FmEntryData* pControlData = FindData( xFormViewControl, GetRootList() );
+ if ( !pControlData )
+ return false;
+
+ rHint.AddItem( pControlData );
+ return true;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ void NavigatorTreeModel::BroadcastMarkedObjects(const SdrMarkList& mlMarked)
+ {
+ // search all objects, which can be handled, out of marked objects
+ FmNavRequestSelectHint rshRequestSelection;
+ bool bIsMixedSelection = false;
+
+ for (size_t i=0; (i<mlMarked.GetMarkCount()) && !bIsMixedSelection; ++i)
+ {
+ SdrObject* pobjCurrent = mlMarked.GetMark(i)->GetMarkedSdrObj();
+ bIsMixedSelection |= !InsertFormComponent(rshRequestSelection, pobjCurrent);
+ // if Not-Form-Control, InsertFormComponent returns sal_False !
+ }
+
+ rshRequestSelection.SetMixedSelection(bIsMixedSelection);
+ if (bIsMixedSelection)
+ rshRequestSelection.ClearItems();
+
+ Broadcast(rshRequestSelection);
+ // an empty list causes NavigatorTree to remove his selection
+ }
+
+
+ void NavigatorTreeModel::UpdateContent( const Reference< css::form::XForms > & xForms )
+ {
+
+ // refill model form root upward
+ Clear();
+ if (!xForms.is())
+ return;
+
+ xForms->addContainerListener(m_pPropChangeList);
+
+ FillBranch(nullptr);
+
+ // select same control in tree as in view
+ // (or all of them), if there is one ...
+ if(!m_pFormShell) return; // no shell
+
+ FmFormView* pFormView = m_pFormShell->GetFormView();
+ DBG_ASSERT(pFormView != nullptr, "NavigatorTreeModel::UpdateContent : no FormView");
+ BroadcastMarkedObjects(pFormView->GetMarkedObjectList());
+ }
+
+
+ void NavigatorTreeModel::UpdateContent( FmFormShell* pShell )
+ {
+
+ // If shell is unchanged, do nothing
+ FmFormPage* pNewPage = pShell ? pShell->GetCurPage() : nullptr;
+ if ((pShell == m_pFormShell) && (m_pFormPage == pNewPage))
+ return;
+
+
+ // unregister as Listener
+ if( m_pFormShell )
+ {
+ if (m_pFormModel)
+ EndListening( *m_pFormModel );
+ m_pFormModel = nullptr;
+ EndListening( *m_pFormShell );
+ Clear();
+ }
+
+
+ // entire update
+ m_pFormShell = pShell;
+ if (m_pFormShell)
+ {
+ m_pFormPage = pNewPage;
+ UpdateContent(m_pFormPage->GetForms());
+ } else
+ m_pFormPage = nullptr;
+
+
+ // register as Listener again
+ if( m_pFormShell )
+ {
+ StartListening( *m_pFormShell );
+ m_pFormModel = m_pFormShell->GetFormModel();
+ if( m_pFormModel )
+ StartListening( *m_pFormModel );
+ }
+ }
+
+
+ Reference< XIndexContainer > NavigatorTreeModel::GetFormComponents( FmFormData const * pFormData )
+ {
+
+ // get components from form
+ if (pFormData)
+ return Reference< XIndexContainer > (pFormData->GetFormIface(), UNO_QUERY);
+
+ return Reference< XIndexContainer > ();
+ }
+
+
+ bool NavigatorTreeModel::Rename( FmEntryData* pEntryData, const OUString& rNewText )
+ {
+
+ // If name already exist, error message
+ pEntryData->SetText( rNewText );
+
+
+ // get PropertySet
+ Reference< XFormComponent > xFormComponent;
+
+ if( auto pFormData = dynamic_cast<FmFormData*>( pEntryData))
+ {
+ xFormComponent = pFormData->GetFormIface();
+ }
+
+ if( auto pControlData = dynamic_cast<FmControlData*>( pEntryData) )
+ {
+ xFormComponent = pControlData->GetFormComponent();
+ }
+
+ if( !xFormComponent.is() ) return false;
+ Reference< XPropertySet > xSet(xFormComponent, UNO_QUERY);
+ if( !xSet.is() ) return false;
+
+
+ // set name
+ xSet->setPropertyValue( FM_PROP_NAME, Any(rNewText) );
+
+ return true;
+ }
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/sdbdatacolumn.cxx b/svx/source/form/sdbdatacolumn.cxx
new file mode 100644
index 0000000000..19a7fd016d
--- /dev/null
+++ b/svx/source/form/sdbdatacolumn.cxx
@@ -0,0 +1,55 @@
+/* -*- 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 <sdbdatacolumn.hxx>
+
+
+namespace svxform
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::sdbc;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::io;
+ using namespace ::com::sun::star::container;
+
+
+ //= DataColumn - a class wrapping an object implementing a sdb::DataColumn service
+
+ DataColumn::DataColumn(const Reference< css::beans::XPropertySet>& _rxIFace)
+ {
+ m_xPropertySet = _rxIFace;
+ m_xColumn.set(_rxIFace, UNO_QUERY);
+ m_xColumnUpdate.set(_rxIFace, UNO_QUERY);
+
+ if (!m_xPropertySet.is() || !m_xColumn.is())
+ {
+ m_xPropertySet = nullptr;
+ m_xColumn = nullptr;
+ m_xColumnUpdate = nullptr;
+ }
+ }
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/sqlparserclient.cxx b/svx/source/form/sqlparserclient.cxx
new file mode 100644
index 0000000000..232f0418dd
--- /dev/null
+++ b/svx/source/form/sqlparserclient.cxx
@@ -0,0 +1,50 @@
+/* -*- 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 <sqlparserclient.hxx>
+
+#include <connectivity/sqlparse.hxx>
+
+using namespace ::dbtools;
+using namespace ::connectivity;
+
+namespace svxform
+{
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+
+ OSQLParserClient::OSQLParserClient(const Reference< XComponentContext >& rxContext)
+ : m_pParser(std::make_shared<OSQLParser>(rxContext, getParseContext()))
+ {
+ }
+
+ std::unique_ptr< ::connectivity::OSQLParseNode > OSQLParserClient::predicateTree(
+ OUString& _rErrorMessage,
+ const OUString& _rStatement,
+ const css::uno::Reference< css::util::XNumberFormatter >& _rxFormatter,
+ const css::uno::Reference< css::beans::XPropertySet >& _rxField
+ ) const
+ {
+ return m_pParser->predicateTree(_rErrorMessage, _rStatement, _rxFormatter, _rxField);
+ }
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/tabwin.cxx b/svx/source/form/tabwin.cxx
new file mode 100644
index 0000000000..4c534ef7c3
--- /dev/null
+++ b/svx/source/form/tabwin.cxx
@@ -0,0 +1,309 @@
+/* -*- 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 <tabwin.hxx>
+#include <fmservs.hxx>
+
+#include <svx/strings.hrc>
+#include <svx/svxids.hrc>
+#include <com/sun/star/sdb/CommandType.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/types.hxx>
+
+#include <helpids.h>
+#include <svx/fmshell.hxx>
+#include <fmshimp.hxx>
+
+#include <fmprop.hxx>
+
+#include <svx/dialmgr.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/objitem.hxx>
+#include <sfx2/frame.hxx>
+#include <svx/dataaccessdescriptor.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <tabwin.hrc>
+#include <utility>
+
+const tools::Long STD_WIN_SIZE_X = 120;
+const tools::Long STD_WIN_SIZE_Y = 150;
+
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::datatransfer;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star;
+using namespace ::svxform;
+using namespace ::svx;
+using namespace ::dbtools;
+
+struct ColumnInfo
+{
+ OUString sColumnName;
+ explicit ColumnInfo(OUString i_sColumnName)
+ : sColumnName(std::move(i_sColumnName))
+ {
+ }
+};
+
+void FmFieldWin::addToList(const uno::Reference< container::XNameAccess>& i_xColumns )
+{
+ const uno::Sequence< OUString > aEntries = i_xColumns->getElementNames();
+ for ( const OUString& rEntry : aEntries )
+ {
+ uno::Reference< beans::XPropertySet> xColumn(i_xColumns->getByName(rEntry),UNO_QUERY_THROW);
+ OUString sLabel;
+ if ( xColumn->getPropertySetInfo()->hasPropertyByName(FM_PROP_LABEL) )
+ xColumn->getPropertyValue(FM_PROP_LABEL) >>= sLabel;
+ m_aListBoxData.emplace_back(new ColumnInfo(rEntry));
+ OUString sId(weld::toId(m_aListBoxData.back().get()));
+ if ( !sLabel.isEmpty() )
+ m_xListBox->append(sId, sLabel);
+ else
+ m_xListBox->append(sId, rEntry);
+ }
+}
+
+IMPL_LINK(FmFieldWin, DragBeginHdl, bool&, rUnsetDragIcon, bool)
+{
+ rUnsetDragIcon = false;
+
+ ColumnInfo* pSelected = weld::fromId<ColumnInfo*>(m_xListBox->get_selected_id());
+ if (!pSelected)
+ {
+ // no drag without a field
+ return true;
+ }
+
+ svx::ODataAccessDescriptor aDescriptor;
+ aDescriptor[ DataAccessDescriptorProperty::DataSource ] <<= GetDatabaseName();
+ aDescriptor[ DataAccessDescriptorProperty::Connection ] <<= GetConnection().getTyped();
+ aDescriptor[ DataAccessDescriptorProperty::Command ] <<= GetObjectName();
+ aDescriptor[ DataAccessDescriptorProperty::CommandType ]<<= GetObjectType();
+ aDescriptor[ DataAccessDescriptorProperty::ColumnName ] <<= pSelected->sColumnName;
+
+ m_xHelper->setDescriptor(aDescriptor);
+
+ return false;
+}
+
+FmFieldWin::FmFieldWin(SfxBindings* _pBindings, SfxChildWindow* _pMgr, weld::Window* _pParent)
+ : SfxModelessDialogController(_pBindings, _pMgr, _pParent, "svx/ui/formfielddialog.ui", "FormFieldDialog")
+ , SfxControllerItem(SID_FM_FIELDS_CONTROL, *_pBindings)
+ , comphelper::OPropertyChangeListener2()
+ , m_xListBox(m_xBuilder->weld_tree_view("treeview"))
+ , m_nObjectType(0)
+{
+ m_xDialog->set_help_id(HID_FIELD_SEL_WIN);
+ m_xListBox->set_help_id(HID_FIELD_SEL);
+
+ m_xListBox->connect_row_activated(LINK(this, FmFieldWin, RowActivatedHdl));
+ m_xHelper.set(new OColumnTransferable(
+ ColumnTransferFormatFlags::FIELD_DESCRIPTOR | ColumnTransferFormatFlags::CONTROL_EXCHANGE | ColumnTransferFormatFlags::COLUMN_DESCRIPTOR
+ ));
+ rtl::Reference<TransferDataContainer> xHelper(m_xHelper);
+ m_xListBox->enable_drag_source(xHelper, DND_ACTION_COPY);
+ m_xListBox->connect_drag_begin(LINK(this, FmFieldWin, DragBeginHdl));
+
+ UpdateContent(nullptr);
+ m_xDialog->set_size_request(STD_WIN_SIZE_X, STD_WIN_SIZE_Y);
+}
+
+FmFieldWin::~FmFieldWin()
+{
+ {
+ std::unique_lock g(m_aMutex);
+ if (m_xChangeListener.is())
+ {
+ m_xChangeListener->dispose(g);
+ m_xChangeListener.clear();
+ }
+ }
+ ::SfxControllerItem::dispose();
+}
+
+IMPL_LINK_NOARG(FmFieldWin, RowActivatedHdl, weld::TreeView&, bool)
+{
+ return createSelectionControls();
+}
+
+bool FmFieldWin::createSelectionControls()
+{
+ ColumnInfo* pSelected = weld::fromId<ColumnInfo*>(m_xListBox->get_selected_id());
+ if (pSelected)
+ {
+ // build a descriptor for the currently selected field
+ ODataAccessDescriptor aDescr;
+ aDescr.setDataSource(GetDatabaseName());
+
+ aDescr[ DataAccessDescriptorProperty::Connection ] <<= GetConnection().getTyped();
+
+ aDescr[ DataAccessDescriptorProperty::Command ] <<= GetObjectName();
+ aDescr[ DataAccessDescriptorProperty::CommandType ] <<= GetObjectType();
+ aDescr[ DataAccessDescriptorProperty::ColumnName ] <<= pSelected->sColumnName;
+
+ // transfer this to the SFX world
+ SfxUnoAnyItem aDescriptorItem( SID_FM_DATACCESS_DESCRIPTOR, Any( aDescr.createPropertyValueSequence() ) );
+ const SfxPoolItem* pArgs[] =
+ {
+ &aDescriptorItem, nullptr
+ };
+
+ // execute the create slot
+ GetBindings().Execute( SID_FM_CREATE_FIELDCONTROL, pArgs );
+ }
+
+ return nullptr != pSelected;
+}
+
+void FmFieldWin::_propertyChanged(const css::beans::PropertyChangeEvent& evt)
+{
+ css::uno::Reference< css::form::XForm > xForm(evt.Source, css::uno::UNO_QUERY);
+ UpdateContent(xForm);
+}
+
+void FmFieldWin::StateChangedAtToolBoxControl(sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState)
+{
+ if (!pState || SID_FM_FIELDS_CONTROL != nSID)
+ return;
+
+ if (eState >= SfxItemState::DEFAULT)
+ {
+ FmFormShell* pShell = dynamic_cast<FmFormShell*>( static_cast<const SfxObjectItem*>(pState)->GetShell() );
+ UpdateContent(pShell);
+ }
+ else
+ UpdateContent(nullptr);
+}
+
+void FmFieldWin::UpdateContent(FmFormShell const * pShell)
+{
+ m_xListBox->clear();
+ m_aListBoxData.clear();
+ OUString aTitle(SvxResId(RID_STR_FIELDSELECTION));
+ m_xDialog->set_title(aTitle);
+
+ if (!pShell || !pShell->GetImpl())
+ return;
+
+ Reference<XForm> const xForm = pShell->GetImpl()->getCurrentForm_Lock();
+ if ( xForm.is() )
+ UpdateContent( xForm );
+}
+
+void FmFieldWin::UpdateContent(const css::uno::Reference< css::form::XForm > & xForm)
+{
+ try
+ {
+ // delete ListBox
+ m_xListBox->clear();
+ m_aListBoxData.clear();
+ OUString aTitle(SvxResId(RID_STR_FIELDSELECTION));
+ m_xDialog->set_title(aTitle);
+
+ if (!xForm.is())
+ return;
+
+ Reference< XPropertySet > xSet(xForm, UNO_QUERY);
+
+ m_aObjectName = ::comphelper::getString(xSet->getPropertyValue(FM_PROP_COMMAND));
+ m_aDatabaseName = ::comphelper::getString(xSet->getPropertyValue(FM_PROP_DATASOURCE));
+ m_nObjectType = ::comphelper::getINT32(xSet->getPropertyValue(FM_PROP_COMMANDTYPE));
+
+ // get the connection of the form
+ m_aConnection.reset(
+ connectRowset( Reference< XRowSet >( xForm, UNO_QUERY ), ::comphelper::getProcessComponentContext(), nullptr ),
+ SharedConnection::NoTakeOwnership
+ );
+ // TODO: When incompatible changes (such as extending the "virtualdbtools" interface by ensureRowSetConnection)
+ // are allowed, again, we should change this: dbtools should consistently use SharedConnection all over
+ // the place, and connectRowset should be replaced with ensureRowSetConnection
+
+ // get the fields of the object
+
+ if ( m_aConnection.is() && !m_aObjectName.isEmpty() )
+ {
+ Reference< XComponent > xKeepFieldsAlive;
+ Reference< XNameAccess > xColumns = getFieldsByCommandDescriptor( m_aConnection, m_nObjectType, m_aObjectName,xKeepFieldsAlive );
+ if ( xColumns.is() )
+ addToList(xColumns);
+ }
+
+ // set prefix
+ OUString aPrefix;
+
+ switch (m_nObjectType)
+ {
+ case CommandType::TABLE:
+ aPrefix = SvxResId(RID_RSC_TABWIN_PREFIX[0]);
+ break;
+ case CommandType::QUERY:
+ aPrefix = SvxResId(RID_RSC_TABWIN_PREFIX[1]);
+ break;
+ default:
+ aPrefix = SvxResId(RID_RSC_TABWIN_PREFIX[2]);
+ break;
+ }
+
+ // listen for changes at ControlSource in PropertySet
+ std::unique_lock g(m_aMutex);
+ if (m_xChangeListener.is())
+ {
+ m_xChangeListener->dispose(g);
+ m_xChangeListener.clear();
+ }
+ m_xChangeListener = new ::comphelper::OPropertyChangeMultiplexer2(m_aMutex, g, this, xSet);
+ m_xChangeListener->addProperty(FM_PROP_DATASOURCE);
+ m_xChangeListener->addProperty(FM_PROP_COMMAND);
+ m_xChangeListener->addProperty(FM_PROP_COMMANDTYPE);
+
+ // set title
+ aTitle += " " + aPrefix + " " + m_aObjectName;
+ m_xDialog->set_title(aTitle);
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "FmTabWin::UpdateContent" );
+ }
+}
+
+void FmFieldWin::FillInfo( SfxChildWinInfo& rInfo ) const
+{
+ rInfo.bVisible = false;
+}
+
+SFX_IMPL_MODELESSDIALOGCONTOLLER(FmFieldWinMgr, SID_FM_ADD_FIELD)
+
+FmFieldWinMgr::FmFieldWinMgr(vcl::Window* _pParent, sal_uInt16 _nId,
+ SfxBindings* _pBindings, SfxChildWinInfo const * _pInfo)
+ :SfxChildWindow(_pParent, _nId)
+{
+ auto xDlg = std::make_shared<FmFieldWin>(_pBindings, this, _pParent->GetFrameWeld());
+ SetController(xDlg);
+ SetHideNotDelete(true);
+ xDlg->Initialize(_pInfo);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/tbxform.cxx b/svx/source/form/tbxform.cxx
new file mode 100644
index 0000000000..96f05670d3
--- /dev/null
+++ b/svx/source/form/tbxform.cxx
@@ -0,0 +1,208 @@
+/* -*- 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 <sal/config.h>
+
+#include <comphelper/propertyvalue.hxx>
+#include <svl/intitem.hxx>
+#include <svl/eitem.hxx>
+#include <svl/stritem.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/settings.hxx>
+
+#include <svx/dialmgr.hxx>
+#include <svx/labelitemwindow.hxx>
+#include <svx/svxids.hrc>
+#include <svx/strings.hrc>
+#include <tbxform.hxx>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+
+SvxFmAbsRecWin::SvxFmAbsRecWin(vcl::Window* pParent, SfxToolBoxControl* pController)
+ : RecordItemWindow(pParent)
+ , m_pController(pController)
+{
+ m_xWidget->set_width_chars(6);
+ SetSizePixel(m_xWidget->get_preferred_size());
+}
+
+void SvxFmAbsRecWin::PositionFired(sal_Int64 nRecord)
+{
+ SfxInt32Item aPositionParam( TypedWhichId<SfxInt32Item>(FN_PARAM_1), static_cast<sal_Int32>(nRecord) );
+
+ Any a;
+ aPositionParam.QueryValue( a );
+ Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue("Position", a) };
+ m_pController->Dispatch( ".uno:AbsoluteRecord",
+ aArgs );
+ m_pController->updateStatus();
+}
+
+SFX_IMPL_TOOLBOX_CONTROL( SvxFmTbxCtlAbsRec, SfxInt32Item );
+
+SvxFmTbxCtlAbsRec::SvxFmTbxCtlAbsRec( sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx )
+ :SfxToolBoxControl( nSlotId, nId, rTbx )
+{
+}
+
+
+SvxFmTbxCtlAbsRec::~SvxFmTbxCtlAbsRec()
+{
+}
+
+void SvxFmTbxCtlAbsRec::StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState )
+{
+ ToolBoxItemId nId = GetId();
+ ToolBox* pToolBox = &GetToolBox();
+ SvxFmAbsRecWin* pWin = static_cast<SvxFmAbsRecWin*>( pToolBox->GetItemWindow(nId) );
+
+ assert(pWin && "Control not found!");
+
+ if (pState)
+ {
+ const SfxInt32Item* pItem = dynamic_cast< const SfxInt32Item* >( pState );
+ DBG_ASSERT( pItem, "SvxFmTbxCtlAbsRec::StateChanged: invalid item!" );
+ pWin->set_text(OUString::number(pItem ? pItem->GetValue() : -1));
+ }
+
+ bool bEnable = SfxItemState::DISABLED != eState && pState;
+ if (!bEnable)
+ pWin->set_text(OUString());
+
+
+ // enabling/disabling of the window
+ pToolBox->EnableItem(nId, bEnable);
+ SfxToolBoxControl::StateChangedAtToolBoxControl( nSID, eState,pState );
+}
+
+VclPtr<InterimItemWindow> SvxFmTbxCtlAbsRec::CreateItemWindow( vcl::Window* pParent )
+{
+ return VclPtrInstance<SvxFmAbsRecWin>(pParent, this);
+}
+
+SFX_IMPL_TOOLBOX_CONTROL( SvxFmTbxCtlRecText, SfxBoolItem );
+
+SvxFmTbxCtlRecText::SvxFmTbxCtlRecText( sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx )
+ :SfxToolBoxControl( nSlotId, nId, rTbx )
+{
+ rTbx.SetItemWindowNonInteractive(nId, true);
+}
+
+SvxFmTbxCtlRecText::~SvxFmTbxCtlRecText()
+{
+}
+
+VclPtr<InterimItemWindow> SvxFmTbxCtlRecText::CreateItemWindow( vcl::Window* pParent )
+{
+ OUString aText(SvxResId(RID_STR_REC_TEXT));
+ VclPtrInstance<LabelItemWindow> xFixedText(pParent, aText);
+
+ xFixedText->Show();
+
+ return xFixedText;
+}
+
+SFX_IMPL_TOOLBOX_CONTROL( SvxFmTbxCtlRecFromText, SfxBoolItem );
+
+SvxFmTbxCtlRecFromText::SvxFmTbxCtlRecFromText( sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx )
+ :SfxToolBoxControl( nSlotId, nId, rTbx )
+{
+ rTbx.SetItemWindowNonInteractive(nId, true);
+}
+
+SvxFmTbxCtlRecFromText::~SvxFmTbxCtlRecFromText()
+{
+}
+
+VclPtr<InterimItemWindow> SvxFmTbxCtlRecFromText::CreateItemWindow( vcl::Window* pParent )
+{
+ OUString aText(SvxResId(RID_STR_REC_FROM_TEXT));
+ VclPtrInstance<LabelItemWindow> xFixedText(pParent, aText);
+
+ xFixedText->Show();
+
+ return xFixedText;
+}
+
+SFX_IMPL_TOOLBOX_CONTROL( SvxFmTbxCtlRecTotal, SfxStringItem );
+
+SvxFmTbxCtlRecTotal::SvxFmTbxCtlRecTotal( sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx )
+ : SfxToolBoxControl( nSlotId, nId, rTbx )
+{
+ rTbx.SetItemWindowNonInteractive(nId, true);
+}
+
+SvxFmTbxCtlRecTotal::~SvxFmTbxCtlRecTotal()
+{
+}
+
+VclPtr<InterimItemWindow> SvxFmTbxCtlRecTotal::CreateItemWindow( vcl::Window* pParent )
+{
+ m_xFixedText.reset(VclPtr<LabelItemWindow>::Create(pParent, "123456"));
+ m_xFixedText->set_label("");
+
+ m_xFixedText->Show();
+
+ return m_xFixedText;
+}
+
+void SvxFmTbxCtlRecTotal::StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState )
+{
+ // setting the FixedText
+ if (GetSlotId() != SID_FM_RECORD_TOTAL)
+ return;
+
+ OUString aText;
+ if (pState)
+ aText = static_cast<const SfxStringItem*>(pState)->GetValue();
+ else
+ aText = "?";
+
+ m_xFixedText->set_label(aText);
+
+ SfxToolBoxControl::StateChangedAtToolBoxControl( nSID, eState,pState );
+}
+
+SFX_IMPL_TOOLBOX_CONTROL( SvxFmTbxNextRec, SfxBoolItem );
+
+
+SvxFmTbxNextRec::SvxFmTbxNextRec( sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx )
+ :SfxToolBoxControl( nSlotId, nId, rTbx )
+{
+ rTbx.SetItemBits(nId, rTbx.GetItemBits(nId) | ToolBoxItemBits::REPEAT);
+
+ AllSettings aSettings = rTbx.GetSettings();
+ MouseSettings aMouseSettings = aSettings.GetMouseSettings();
+ aMouseSettings.SetButtonRepeat(aMouseSettings.GetButtonRepeat() / 4);
+ aSettings.SetMouseSettings(aMouseSettings);
+ rTbx.SetSettings(aSettings, true);
+}
+
+SFX_IMPL_TOOLBOX_CONTROL( SvxFmTbxPrevRec, SfxBoolItem );
+
+
+SvxFmTbxPrevRec::SvxFmTbxPrevRec( sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx )
+ :SfxToolBoxControl( nSlotId, nId, rTbx )
+{
+ rTbx.SetItemBits(nId, rTbx.GetItemBits(nId) | ToolBoxItemBits::REPEAT);
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/typemap.cxx b/svx/source/form/typemap.cxx
new file mode 100644
index 0000000000..b169b745c6
--- /dev/null
+++ b/svx/source/form/typemap.cxx
@@ -0,0 +1,52 @@
+/* -*- 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_options.h>
+
+#include <sfx2/objitem.hxx>
+#include <sfx2/msg.hxx>
+#include <svl/memberid.h>
+#include <editeng/wghtitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/postitem.hxx>
+#include <svx/clipfmtitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/charreliefitem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/wrlmitem.hxx>
+#include <editeng/charscaleitem.hxx>
+#include <editeng/autokernitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/lspcitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/kernitem.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/emphasismarkitem.hxx>
+
+#include <editeng/memberids.h>
+#define SFX_TYPEMAP
+#include <svxslots.hxx>
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/xfm_addcondition.cxx b/svx/source/form/xfm_addcondition.cxx
new file mode 100644
index 0000000000..6c8d6ef35a
--- /dev/null
+++ b/svx/source/form/xfm_addcondition.cxx
@@ -0,0 +1,157 @@
+/* -*- 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 <xfm_addcondition.hxx>
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <comphelper/processfactory.hxx>
+#include <vcl/svapp.hxx>
+#include <datanavi.hxx>
+#include <fmservs.hxx>
+
+namespace svxform
+{
+
+#define PROPERTY_ID_BINDING 5724
+#define PROPERTY_ID_FORM_MODEL 5725
+#define PROPERTY_ID_FACET_NAME 5726
+#define PROPERTY_ID_CONDITION_VALUE 5727
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::xforms;
+
+
+ //= OAddConditionDialog
+
+
+ Reference< XInterface > OAddConditionDialog_Create( const Reference< XMultiServiceFactory > & _rxORB )
+ {
+ return OAddConditionDialog::Create( _rxORB );
+ }
+
+
+ Sequence< OUString > OAddConditionDialog_GetSupportedServiceNames()
+ {
+ return { "com.sun.star.xforms.ui.dialogs.AddCondition" };
+ }
+
+
+ OUString OAddConditionDialog_GetImplementationName()
+ {
+ return "org.openoffice.comp.svx.OAddConditionDialog";
+ }
+
+ OAddConditionDialog::OAddConditionDialog( const Reference< XComponentContext >& _rxORB )
+ :OAddConditionDialogBase( _rxORB )
+ {
+ registerProperty(
+ "Binding",
+ PROPERTY_ID_BINDING,
+ PropertyAttribute::TRANSIENT,
+ &m_xBinding,
+ cppu::UnoType<decltype(m_xBinding)>::get()
+ );
+
+ registerProperty(
+ "FacetName",
+ PROPERTY_ID_FACET_NAME,
+ PropertyAttribute::TRANSIENT,
+ &m_sFacetName,
+ cppu::UnoType<decltype(m_sFacetName)>::get()
+ );
+
+ registerProperty(
+ "ConditionValue",
+ PROPERTY_ID_CONDITION_VALUE,
+ PropertyAttribute::TRANSIENT,
+ &m_sConditionValue,
+ cppu::UnoType<decltype(m_sConditionValue)>::get()
+ );
+
+ registerProperty(
+ "FormModel",
+ PROPERTY_ID_FORM_MODEL,
+ PropertyAttribute::TRANSIENT,
+ &m_xWorkModel,
+ cppu::UnoType<decltype(m_xWorkModel)>::get()
+ );
+ }
+
+
+ Sequence<sal_Int8> SAL_CALL OAddConditionDialog::getImplementationId( )
+ {
+ return css::uno::Sequence<sal_Int8>();
+ }
+
+
+ Reference< XInterface > OAddConditionDialog::Create( const Reference< XMultiServiceFactory >& _rxFactory )
+ {
+ return *( new OAddConditionDialog( comphelper::getComponentContext(_rxFactory) ) );
+ }
+
+
+ OUString SAL_CALL OAddConditionDialog::getImplementationName()
+ {
+ return OAddConditionDialog_GetImplementationName();
+ }
+
+
+ Sequence< OUString > SAL_CALL OAddConditionDialog::getSupportedServiceNames()
+ {
+ return OAddConditionDialog_GetSupportedServiceNames();
+ }
+
+
+ Reference<XPropertySetInfo> SAL_CALL OAddConditionDialog::getPropertySetInfo()
+ {
+ return createPropertySetInfo( getInfoHelper() );
+ }
+
+ ::cppu::IPropertyArrayHelper& OAddConditionDialog::getInfoHelper()
+ {
+ return *getArrayHelper();
+ }
+
+ ::cppu::IPropertyArrayHelper* OAddConditionDialog::createArrayHelper( ) const
+ {
+ Sequence< Property > aProperties;
+ describeProperties( aProperties );
+ return new ::cppu::OPropertyArrayHelper( aProperties );
+ }
+
+ std::unique_ptr<weld::DialogController> OAddConditionDialog::createDialog(const css::uno::Reference<css::awt::XWindow>& rParent)
+ {
+ if ( !m_xBinding.is() || m_sFacetName.isEmpty() )
+ throw RuntimeException( OUString(), *this );
+
+ return std::make_unique<AddConditionDialog>(Application::GetFrameWeld(rParent), m_sFacetName, m_xBinding);
+ }
+
+ void OAddConditionDialog::executedDialog( sal_Int16 _nExecutionResult )
+ {
+ OAddConditionDialogBase::executedDialog( _nExecutionResult );
+ if ( _nExecutionResult == RET_OK )
+ m_sConditionValue = static_cast<AddConditionDialog*>(m_xDialog.get())->GetCondition();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */