summaryrefslogtreecommitdiffstats
path: root/svx/source/fmcomp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
commit940b4d1848e8c70ab7642901a68594e8016caffc (patch)
treeeb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /svx/source/fmcomp
parentInitial commit. (diff)
downloadlibreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.tar.xz
libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.zip
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'svx/source/fmcomp')
-rw-r--r--svx/source/fmcomp/dbaexchange.cxx628
-rw-r--r--svx/source/fmcomp/dbaobjectex.cxx133
-rw-r--r--svx/source/fmcomp/fmgridcl.cxx2061
-rw-r--r--svx/source/fmcomp/fmgridif.cxx2764
-rw-r--r--svx/source/fmcomp/gridcell.cxx4634
-rw-r--r--svx/source/fmcomp/gridcols.cxx103
-rw-r--r--svx/source/fmcomp/gridctrl.cxx3645
-rw-r--r--svx/source/fmcomp/xmlexchg.cxx61
8 files changed, 14029 insertions, 0 deletions
diff --git a/svx/source/fmcomp/dbaexchange.cxx b/svx/source/fmcomp/dbaexchange.cxx
new file mode 100644
index 000000000..b11e9770b
--- /dev/null
+++ b/svx/source/fmcomp/dbaexchange.cxx
@@ -0,0 +1,628 @@
+/* -*- 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/dbaexchange.hxx>
+#include <osl/diagnose.h>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sdb/CommandType.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
+#include <fmprop.hxx>
+#include <comphelper/extract.hxx>
+#include <sot/formats.hxx>
+#include <sot/exchange.hxx>
+
+
+namespace svx
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::sdb;
+ using namespace ::com::sun::star::sdbc;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::sdbcx;
+ using namespace ::com::sun::star::container;
+ using namespace ::com::sun::star::datatransfer;
+
+ OColumnTransferable::OColumnTransferable(ColumnTransferFormatFlags nFormats)
+ : m_nFormatFlags(nFormats)
+ {
+ }
+
+ void OColumnTransferable::setDescriptor(const ODataAccessDescriptor& rDescriptor)
+ {
+ ClearFormats();
+
+ OUString sDataSource, sDatabaseLocation, sConnectionResource, sCommand, sFieldName;
+ if ( rDescriptor.has( DataAccessDescriptorProperty::DataSource ) ) rDescriptor[ DataAccessDescriptorProperty::DataSource ] >>= sDataSource;
+ if ( rDescriptor.has( DataAccessDescriptorProperty::DatabaseLocation ) ) rDescriptor[ DataAccessDescriptorProperty::DatabaseLocation ] >>= sDatabaseLocation;
+ if ( rDescriptor.has( DataAccessDescriptorProperty::ConnectionResource ) ) rDescriptor[ DataAccessDescriptorProperty::ConnectionResource ] >>= sConnectionResource;
+ if ( rDescriptor.has( DataAccessDescriptorProperty::Command ) ) rDescriptor[ DataAccessDescriptorProperty::Command ] >>= sCommand;
+ if ( rDescriptor.has( DataAccessDescriptorProperty::ColumnName ) ) rDescriptor[ DataAccessDescriptorProperty::ColumnName ] >>= sFieldName;
+
+ sal_Int32 nCommandType = CommandType::TABLE;
+ OSL_VERIFY( rDescriptor[ DataAccessDescriptorProperty::CommandType ] >>= nCommandType );
+
+ implConstruct(
+ sDataSource.isEmpty() ? sDatabaseLocation : sDataSource,
+ sConnectionResource, nCommandType, sCommand, sFieldName );
+
+ if ( m_nFormatFlags & ColumnTransferFormatFlags::COLUMN_DESCRIPTOR )
+ {
+ if ( rDescriptor.has( DataAccessDescriptorProperty::Connection ) )
+ m_aDescriptor[ DataAccessDescriptorProperty::Connection ] = rDescriptor[ DataAccessDescriptorProperty::Connection ];
+ if ( rDescriptor.has( DataAccessDescriptorProperty::ColumnObject ) )
+ m_aDescriptor[ DataAccessDescriptorProperty::ColumnObject ] = rDescriptor[ DataAccessDescriptorProperty::ColumnObject ];
+ }
+ }
+
+ OColumnTransferable::OColumnTransferable(const Reference< XPropertySet >& _rxForm,
+ const OUString& _rFieldName, const Reference< XPropertySet >& _rxColumn,
+ const Reference< XConnection >& _rxConnection, ColumnTransferFormatFlags _nFormats)
+ :m_nFormatFlags(_nFormats)
+ {
+ OSL_ENSURE(_rxForm.is(), "OColumnTransferable::OColumnTransferable: invalid form!");
+ // collect the necessary information from the form
+ OUString sCommand;
+ sal_Int32 nCommandType = CommandType::TABLE;
+ OUString sDatasource,sURL;
+
+ bool bTryToParse = true;
+ try
+ {
+ _rxForm->getPropertyValue(FM_PROP_COMMANDTYPE) >>= nCommandType;
+ _rxForm->getPropertyValue(FM_PROP_COMMAND) >>= sCommand;
+ _rxForm->getPropertyValue(FM_PROP_DATASOURCE) >>= sDatasource;
+ _rxForm->getPropertyValue(FM_PROP_URL) >>= sURL;
+ bTryToParse = ::cppu::any2bool(_rxForm->getPropertyValue(FM_PROP_ESCAPE_PROCESSING));
+ }
+ catch(Exception&)
+ {
+ OSL_FAIL("OColumnTransferable::OColumnTransferable: could not collect essential data source attributes !");
+ }
+
+ // If the data source is an SQL-statement and simple enough (means "select <field list> from <table> where...")
+ // we are able to fake the drag information we are about to create.
+ if (bTryToParse && (CommandType::COMMAND == nCommandType))
+ {
+ try
+ {
+ Reference< XTablesSupplier > xSupTab;
+ _rxForm->getPropertyValue("SingleSelectQueryComposer") >>= xSupTab;
+
+ if(xSupTab.is())
+ {
+ Reference< XNameAccess > xNames = xSupTab->getTables();
+ if (xNames.is())
+ {
+ Sequence< OUString > aTables = xNames->getElementNames();
+ if (1 == aTables.getLength())
+ {
+ sCommand = aTables[0];
+ nCommandType = CommandType::TABLE;
+ }
+ }
+ }
+ }
+ catch(Exception&)
+ {
+ OSL_FAIL("OColumnTransferable::OColumnTransferable: could not collect essential data source attributes (part two) !");
+ }
+ }
+
+ implConstruct(sDatasource, sURL,nCommandType, sCommand, _rFieldName);
+
+ if ((m_nFormatFlags & ColumnTransferFormatFlags::COLUMN_DESCRIPTOR) == ColumnTransferFormatFlags::COLUMN_DESCRIPTOR)
+ {
+ if (_rxColumn.is())
+ m_aDescriptor[DataAccessDescriptorProperty::ColumnObject] <<= _rxColumn;
+ if (_rxConnection.is())
+ m_aDescriptor[DataAccessDescriptorProperty::Connection] <<= _rxConnection;
+ }
+ }
+
+
+ SotClipboardFormatId OColumnTransferable::getDescriptorFormatId()
+ {
+ static SotClipboardFormatId s_nFormat = static_cast<SotClipboardFormatId>(-1);
+ if (static_cast<SotClipboardFormatId>(-1) == s_nFormat)
+ {
+ s_nFormat = SotExchange::RegisterFormatName("application/x-openoffice;windows_formatname=\"dbaccess.ColumnDescriptorTransfer\"");
+ OSL_ENSURE(static_cast<SotClipboardFormatId>(-1) != s_nFormat, "OColumnTransferable::getDescriptorFormatId: bad exchange id!");
+ }
+ return s_nFormat;
+ }
+
+
+ void OColumnTransferable::implConstruct( const OUString& _rDatasource
+ ,const OUString& _rConnectionResource
+ ,const sal_Int32 _nCommandType
+ ,const OUString& _rCommand
+ , const OUString& _rFieldName)
+ {
+ const sal_Unicode cSeparator = u'\x000B';
+ const OUString sSeparator(&cSeparator, 1);
+
+ m_sCompatibleFormat.clear();
+ m_sCompatibleFormat += _rDatasource;
+ m_sCompatibleFormat += sSeparator;
+ m_sCompatibleFormat += _rCommand;
+ m_sCompatibleFormat += sSeparator;
+
+ sal_Unicode cCommandType;
+ switch (_nCommandType)
+ {
+ case CommandType::TABLE:
+ cCommandType = '0';
+ break;
+ case CommandType::QUERY:
+ cCommandType = '1';
+ break;
+ default:
+ cCommandType = '2';
+ break;
+ }
+ m_sCompatibleFormat += OUString(&cCommandType, 1);
+ m_sCompatibleFormat += sSeparator;
+ m_sCompatibleFormat += _rFieldName;
+
+ m_aDescriptor.clear();
+ if ((m_nFormatFlags & ColumnTransferFormatFlags::COLUMN_DESCRIPTOR) == ColumnTransferFormatFlags::COLUMN_DESCRIPTOR)
+ {
+ m_aDescriptor.setDataSource(_rDatasource);
+ if ( !_rConnectionResource.isEmpty() )
+ m_aDescriptor[DataAccessDescriptorProperty::ConnectionResource] <<= _rConnectionResource;
+
+ m_aDescriptor[DataAccessDescriptorProperty::Command] <<= _rCommand;
+ m_aDescriptor[DataAccessDescriptorProperty::CommandType] <<= _nCommandType;
+ m_aDescriptor[DataAccessDescriptorProperty::ColumnName] <<= _rFieldName;
+ }
+ }
+
+
+ void OColumnTransferable::AddSupportedFormats()
+ {
+ if (ColumnTransferFormatFlags::CONTROL_EXCHANGE & m_nFormatFlags)
+ AddFormat(SotClipboardFormatId::SBA_CTRLDATAEXCHANGE);
+
+ if (ColumnTransferFormatFlags::FIELD_DESCRIPTOR & m_nFormatFlags)
+ AddFormat(SotClipboardFormatId::SBA_FIELDDATAEXCHANGE);
+
+ if (ColumnTransferFormatFlags::COLUMN_DESCRIPTOR & m_nFormatFlags)
+ AddFormat(getDescriptorFormatId());
+ }
+
+
+ bool OColumnTransferable::GetData( const DataFlavor& _rFlavor, const OUString& /*rDestDoc*/ )
+ {
+ const SotClipboardFormatId nFormatId = SotExchange::GetFormat(_rFlavor);
+ switch (nFormatId)
+ {
+ case SotClipboardFormatId::SBA_FIELDDATAEXCHANGE:
+ case SotClipboardFormatId::SBA_CTRLDATAEXCHANGE:
+ return SetString(m_sCompatibleFormat, _rFlavor);
+ default: break;
+ }
+ if (nFormatId == getDescriptorFormatId())
+ return SetAny( makeAny( m_aDescriptor.createPropertyValueSequence() ) );
+
+ return false;
+ }
+
+
+ bool OColumnTransferable::canExtractColumnDescriptor(const DataFlavorExVector& _rFlavors, ColumnTransferFormatFlags _nFormats)
+ {
+ bool bFieldFormat = bool(_nFormats & ColumnTransferFormatFlags::FIELD_DESCRIPTOR);
+ bool bControlFormat = bool(_nFormats & ColumnTransferFormatFlags::CONTROL_EXCHANGE);
+ bool bDescriptorFormat = bool(_nFormats & ColumnTransferFormatFlags::COLUMN_DESCRIPTOR);
+ SotClipboardFormatId nFormatId = getDescriptorFormatId();
+ return std::any_of(_rFlavors.begin(), _rFlavors.end(),
+ [&](const DataFlavorEx& rCheck) {
+ return (bFieldFormat && (SotClipboardFormatId::SBA_FIELDDATAEXCHANGE == rCheck.mnSotId))
+ || (bControlFormat && (SotClipboardFormatId::SBA_CTRLDATAEXCHANGE == rCheck.mnSotId))
+ || (bDescriptorFormat && (nFormatId == rCheck.mnSotId));
+ });
+ }
+
+
+ ODataAccessDescriptor OColumnTransferable::extractColumnDescriptor(const TransferableDataHelper& _rData)
+ {
+ if (_rData.HasFormat(getDescriptorFormatId()))
+ {
+ // the object has a real descriptor object (not just the old compatible format)
+
+ // extract the any from the transferable
+ DataFlavor aFlavor;
+ bool bSuccess =
+ SotExchange::GetFormatDataFlavor(getDescriptorFormatId(), aFlavor);
+ OSL_ENSURE(bSuccess, "OColumnTransferable::extractColumnDescriptor: invalid data format (no flavor)!");
+
+ Any aDescriptor = _rData.GetAny(aFlavor, OUString());
+
+ // extract the property value sequence
+ Sequence< PropertyValue > aDescriptorProps;
+ bSuccess = aDescriptor >>= aDescriptorProps;
+ OSL_ENSURE(bSuccess, "OColumnTransferable::extractColumnDescriptor: invalid clipboard format!");
+
+ // build the real descriptor
+ return ODataAccessDescriptor(aDescriptorProps);
+ }
+
+ // only the old (compatible) format exists -> use the other extract method ...
+ OUString sDatasource, sCommand, sFieldName,sDatabaseLocation,sConnectionResource;
+ sal_Int32 nCommandType = CommandType::COMMAND;
+
+ ODataAccessDescriptor aDescriptor;
+ if (extractColumnDescriptor(_rData, sDatasource, sDatabaseLocation,sConnectionResource,nCommandType, sCommand, sFieldName))
+ {
+ // and build an own descriptor
+ if ( !sDatasource.isEmpty() )
+ aDescriptor[DataAccessDescriptorProperty::DataSource] <<= sDatasource;
+ if ( !sDatabaseLocation.isEmpty() )
+ aDescriptor[DataAccessDescriptorProperty::DatabaseLocation] <<= sDatabaseLocation;
+ if ( !sConnectionResource.isEmpty() )
+ aDescriptor[DataAccessDescriptorProperty::ConnectionResource] <<= sConnectionResource;
+
+ aDescriptor[DataAccessDescriptorProperty::Command] <<= sCommand;
+ aDescriptor[DataAccessDescriptorProperty::CommandType] <<= nCommandType;
+ aDescriptor[DataAccessDescriptorProperty::ColumnName] <<= sFieldName;
+ }
+ return aDescriptor;
+ }
+
+
+ bool OColumnTransferable::extractColumnDescriptor(const TransferableDataHelper& _rData
+ ,OUString& _rDatasource
+ ,OUString& _rDatabaseLocation
+ ,OUString& _rConnectionResource
+ ,sal_Int32& _nCommandType
+ ,OUString& _rCommand
+ ,OUString& _rFieldName)
+ {
+ if ( _rData.HasFormat(getDescriptorFormatId()) )
+ {
+ ODataAccessDescriptor aDescriptor = extractColumnDescriptor(_rData);
+ if ( aDescriptor.has(DataAccessDescriptorProperty::DataSource) )
+ aDescriptor[DataAccessDescriptorProperty::DataSource] >>= _rDatasource;
+ if ( aDescriptor.has(DataAccessDescriptorProperty::DatabaseLocation) )
+ aDescriptor[DataAccessDescriptorProperty::DatabaseLocation] >>= _rDatabaseLocation;
+ if ( aDescriptor.has(DataAccessDescriptorProperty::ConnectionResource) )
+ aDescriptor[DataAccessDescriptorProperty::ConnectionResource] >>= _rConnectionResource;
+
+ aDescriptor[DataAccessDescriptorProperty::Command] >>= _rCommand;
+ aDescriptor[DataAccessDescriptorProperty::CommandType] >>= _nCommandType;
+ aDescriptor[DataAccessDescriptorProperty::ColumnName] >>= _rFieldName;
+ return true;
+ }
+
+ // check if we have a (string) format we can use...
+ SotClipboardFormatId nRecognizedFormat = SotClipboardFormatId::NONE;
+ if (_rData.HasFormat(SotClipboardFormatId::SBA_FIELDDATAEXCHANGE))
+ nRecognizedFormat = SotClipboardFormatId::SBA_FIELDDATAEXCHANGE;
+ if (_rData.HasFormat(SotClipboardFormatId::SBA_CTRLDATAEXCHANGE))
+ nRecognizedFormat = SotClipboardFormatId::SBA_CTRLDATAEXCHANGE;
+ if (nRecognizedFormat == SotClipboardFormatId::NONE)
+ return false;
+
+ OUString sFieldDescription;
+ (void)const_cast<TransferableDataHelper&>(_rData).GetString(nRecognizedFormat, sFieldDescription);
+
+ const sal_Unicode cSeparator = u'\x000B';
+ sal_Int32 nIdx{ 0 };
+ _rDatasource = sFieldDescription.getToken(0, cSeparator, nIdx);
+ _rCommand = sFieldDescription.getToken(0, cSeparator, nIdx);
+ _nCommandType = sFieldDescription.getToken(0, cSeparator, nIdx).toInt32();
+ _rFieldName = sFieldDescription.getToken(0, cSeparator, nIdx);
+
+ return true;
+ }
+
+
+ ODataAccessObjectTransferable::ODataAccessObjectTransferable(
+ const OUString& _rDatasource
+ ,const sal_Int32 _nCommandType
+ ,const OUString& _rCommand
+ )
+ {
+ construct(_rDatasource,OUString(),_nCommandType,_rCommand,nullptr,(CommandType::COMMAND == _nCommandType),_rCommand);
+ }
+
+ ODataAccessObjectTransferable::ODataAccessObjectTransferable(
+ const OUString& _rDatasource
+ ,const sal_Int32 _nCommandType
+ ,const OUString& _rCommand
+ ,const Reference< XConnection >& _rxConnection)
+ {
+ OSL_ENSURE(_rxConnection.is(),"Wrong ctor used.!");
+ construct(_rDatasource,OUString(),_nCommandType,_rCommand,_rxConnection,(CommandType::COMMAND == _nCommandType),_rCommand);
+ }
+
+
+ ODataAccessObjectTransferable::ODataAccessObjectTransferable(const Reference< XPropertySet >& _rxLivingForm)
+ {
+ // collect some properties of the form
+ OUString sDatasourceName,sConnectionResource;
+ sal_Int32 nObjectType = CommandType::COMMAND;
+ OUString sObjectName;
+ Reference< XConnection > xConnection;
+ try
+ {
+ _rxLivingForm->getPropertyValue(FM_PROP_COMMANDTYPE) >>= nObjectType;
+ _rxLivingForm->getPropertyValue(FM_PROP_COMMAND) >>= sObjectName;
+ _rxLivingForm->getPropertyValue(FM_PROP_DATASOURCE) >>= sDatasourceName;
+ _rxLivingForm->getPropertyValue(FM_PROP_URL) >>= sConnectionResource;
+ _rxLivingForm->getPropertyValue(FM_PROP_ACTIVE_CONNECTION) >>= xConnection;
+ }
+ catch(Exception&)
+ {
+ OSL_FAIL("ODataAccessObjectTransferable::ODataAccessObjectTransferable: could not collect essential form attributes !");
+ return;
+ }
+
+ // check if the SQL-statement is modified
+ OUString sCompleteStatement;
+ try
+ {
+ _rxLivingForm->getPropertyValue(FM_PROP_ACTIVECOMMAND) >>= sCompleteStatement;
+ }
+ catch (const Exception&)
+ {
+ OSL_FAIL("ODataAccessObjectTransferable::ODataAccessObjectTransferable: could not collect essential form attributes (part two) !");
+ return;
+ }
+
+ construct( sDatasourceName
+ ,sConnectionResource
+ ,nObjectType
+ ,sObjectName,xConnection
+ ,CommandType::QUERY != nObjectType
+ ,sCompleteStatement);
+ }
+
+
+ void ODataAccessObjectTransferable::AddSupportedFormats()
+ {
+ sal_Int32 nObjectType = CommandType::COMMAND;
+ m_aDescriptor[DataAccessDescriptorProperty::CommandType] >>= nObjectType;
+ switch (nObjectType)
+ {
+ case CommandType::TABLE:
+ AddFormat(SotClipboardFormatId::DBACCESS_TABLE);
+ break;
+ case CommandType::QUERY:
+ AddFormat(SotClipboardFormatId::DBACCESS_QUERY);
+ break;
+ case CommandType::COMMAND:
+ AddFormat(SotClipboardFormatId::DBACCESS_COMMAND);
+ break;
+ }
+
+ if (!m_sCompatibleObjectDescription.isEmpty())
+ AddFormat(SotClipboardFormatId::SBA_DATAEXCHANGE);
+ }
+
+
+ bool ODataAccessObjectTransferable::GetData( const DataFlavor& rFlavor, const OUString& /*rDestDoc*/ )
+ {
+ SotClipboardFormatId nFormat = SotExchange::GetFormat(rFlavor);
+ switch (nFormat)
+ {
+ case SotClipboardFormatId::DBACCESS_TABLE:
+ case SotClipboardFormatId::DBACCESS_QUERY:
+ case SotClipboardFormatId::DBACCESS_COMMAND:
+ return SetAny( makeAny(m_aDescriptor.createPropertyValueSequence()) );
+
+ case SotClipboardFormatId::SBA_DATAEXCHANGE:
+ return SetString(m_sCompatibleObjectDescription, rFlavor);
+ default: break;
+ }
+ return false;
+ }
+
+
+ bool ODataAccessObjectTransferable::canExtractObjectDescriptor(const DataFlavorExVector& _rFlavors)
+ {
+ return std::any_of(_rFlavors.begin(), _rFlavors.end(),
+ [](const DataFlavorEx& rCheck) {
+ return SotClipboardFormatId::DBACCESS_TABLE == rCheck.mnSotId
+ || SotClipboardFormatId::DBACCESS_QUERY == rCheck.mnSotId
+ || SotClipboardFormatId::DBACCESS_COMMAND == rCheck.mnSotId;
+ });
+ }
+
+
+ ODataAccessDescriptor ODataAccessObjectTransferable::extractObjectDescriptor(const TransferableDataHelper& _rData)
+ {
+ SotClipboardFormatId nKnownFormatId = SotClipboardFormatId::NONE;
+ if ( _rData.HasFormat( SotClipboardFormatId::DBACCESS_TABLE ) )
+ nKnownFormatId = SotClipboardFormatId::DBACCESS_TABLE;
+ if ( _rData.HasFormat( SotClipboardFormatId::DBACCESS_QUERY ) )
+ nKnownFormatId = SotClipboardFormatId::DBACCESS_QUERY;
+ if ( _rData.HasFormat( SotClipboardFormatId::DBACCESS_COMMAND ) )
+ nKnownFormatId = SotClipboardFormatId::DBACCESS_COMMAND;
+
+ if (SotClipboardFormatId::NONE != nKnownFormatId)
+ {
+ // extract the any from the transferable
+ DataFlavor aFlavor;
+ bool bSuccess =
+ SotExchange::GetFormatDataFlavor(nKnownFormatId, aFlavor);
+ OSL_ENSURE(bSuccess, "OColumnTransferable::extractColumnDescriptor: invalid data format (no flavor)!");
+
+ Any aDescriptor = _rData.GetAny(aFlavor, OUString());
+
+ // extract the property value sequence
+ Sequence< PropertyValue > aDescriptorProps;
+ bSuccess = aDescriptor >>= aDescriptorProps;
+ OSL_ENSURE(bSuccess, "OColumnTransferable::extractColumnDescriptor: invalid clipboard format!");
+
+ // build the real descriptor
+ return ODataAccessDescriptor(aDescriptorProps);
+ }
+
+ OSL_FAIL( "OColumnTransferable::extractColumnDescriptor: unsupported formats only!" );
+ return ODataAccessDescriptor();
+ }
+
+
+ void ODataAccessObjectTransferable::addCompatibleSelectionDescription( const Sequence< Any >& _rSelRows )
+ {
+ const sal_Unicode cSeparator(11);
+ const OUString sSeparator(&cSeparator, 1);
+
+ for ( const Any& rSelRow : _rSelRows )
+ {
+ sal_Int32 nSelectedRow( 0 );
+ OSL_VERIFY( rSelRow >>= nSelectedRow );
+
+ m_sCompatibleObjectDescription += OUString::number(nSelectedRow);
+ m_sCompatibleObjectDescription += sSeparator;
+ }
+ }
+
+
+ void ODataAccessObjectTransferable::ObjectReleased()
+ {
+ m_aDescriptor.clear();
+ }
+
+ void ODataAccessObjectTransferable::construct( const OUString& _rDatasource
+ ,const OUString& _rConnectionResource
+ ,const sal_Int32 _nCommandType
+ ,const OUString& _rCommand
+ ,const Reference< XConnection >& _rxConnection
+ ,bool _bAddCommand
+ ,const OUString& _sActiveCommand)
+ {
+ m_aDescriptor.setDataSource(_rDatasource);
+ // build the descriptor (the property sequence)
+ if ( !_rConnectionResource.isEmpty() )
+ m_aDescriptor[DataAccessDescriptorProperty::ConnectionResource] <<= _rConnectionResource;
+ if ( _rxConnection.is() )
+ m_aDescriptor[DataAccessDescriptorProperty::Connection] <<= _rxConnection;
+ m_aDescriptor[DataAccessDescriptorProperty::Command] <<= _rCommand;
+ m_aDescriptor[DataAccessDescriptorProperty::CommandType] <<= _nCommandType;
+
+ // extract the single values from the sequence
+
+ OUString sObjectName = _rCommand;
+
+ // for compatibility: create a string which can be used for the SotClipboardFormatId::SBA_DATAEXCHANGE format
+
+ bool bTreatAsStatement = (CommandType::COMMAND == _nCommandType);
+ // statements are - in this old and ugly format - described as queries
+
+ const sal_Unicode cSeparator = u'\x000B';
+ const OUString sSeparator(&cSeparator, 1);
+
+ const sal_Unicode cTableMark = '1';
+ const sal_Unicode cQueryMark = '0';
+
+ // build the descriptor string
+ m_sCompatibleObjectDescription += _rDatasource;
+ m_sCompatibleObjectDescription += sSeparator;
+ m_sCompatibleObjectDescription += bTreatAsStatement ? OUString() : sObjectName;
+ m_sCompatibleObjectDescription += sSeparator;
+ switch (_nCommandType)
+ {
+ case CommandType::TABLE:
+ m_sCompatibleObjectDescription += OUString(&cTableMark, 1);
+ break;
+ case CommandType::QUERY:
+ m_sCompatibleObjectDescription += OUString(&cQueryMark, 1);
+ break;
+ case CommandType::COMMAND:
+ m_sCompatibleObjectDescription += OUString(&cQueryMark, 1);
+ // think of it as a query
+ break;
+ }
+ m_sCompatibleObjectDescription += sSeparator;
+ m_sCompatibleObjectDescription += _bAddCommand ? _sActiveCommand : OUString();
+ m_sCompatibleObjectDescription += sSeparator;
+ }
+
+ OMultiColumnTransferable::OMultiColumnTransferable()
+ {
+ }
+
+ void OMultiColumnTransferable::setDescriptors(const Sequence< PropertyValue >& rDescriptors)
+ {
+ ClearFormats();
+ m_aDescriptors = rDescriptors;
+ }
+
+ SotClipboardFormatId OMultiColumnTransferable::getDescriptorFormatId()
+ {
+ static SotClipboardFormatId s_nFormat = static_cast<SotClipboardFormatId>(-1);
+ if (static_cast<SotClipboardFormatId>(-1) == s_nFormat)
+ {
+ s_nFormat = SotExchange::RegisterFormatName("application/x-openoffice;windows_formatname=\"dbaccess.MultipleColumnDescriptorTransfer\"");
+ OSL_ENSURE(static_cast<SotClipboardFormatId>(-1) != s_nFormat, "OColumnTransferable::getDescriptorFormatId: bad exchange id!");
+ }
+ return s_nFormat;
+ }
+
+ void OMultiColumnTransferable::AddSupportedFormats()
+ {
+ AddFormat(getDescriptorFormatId());
+ }
+
+ bool OMultiColumnTransferable::GetData( const DataFlavor& _rFlavor, const OUString& /*rDestDoc*/ )
+ {
+ const SotClipboardFormatId nFormatId = SotExchange::GetFormat(_rFlavor);
+ if (nFormatId == getDescriptorFormatId())
+ {
+ return SetAny( makeAny( m_aDescriptors ) );
+ }
+
+ return false;
+ }
+
+ bool OMultiColumnTransferable::canExtractDescriptor(const DataFlavorExVector& _rFlavors)
+ {
+ const SotClipboardFormatId nFormatId = getDescriptorFormatId();
+ return std::all_of(_rFlavors.begin(), _rFlavors.end(),
+ [&nFormatId](const DataFlavorEx& rCheck) { return nFormatId == rCheck.mnSotId; });
+ }
+
+ Sequence< PropertyValue > OMultiColumnTransferable::extractDescriptor(const TransferableDataHelper& _rData)
+ {
+ Sequence< PropertyValue > aList;
+ if (_rData.HasFormat(getDescriptorFormatId()))
+ {
+ // extract the any from the transferable
+ DataFlavor aFlavor;
+ bool bSuccess =
+ SotExchange::GetFormatDataFlavor(getDescriptorFormatId(), aFlavor);
+ OSL_ENSURE(bSuccess, "OColumnTransferable::extractColumnDescriptor: invalid data format (no flavor)!");
+
+ _rData.GetAny(aFlavor, OUString()) >>= aList;
+ } // if (_rData.HasFormat(getDescriptorFormatId()))
+ return aList;
+ }
+
+ void OMultiColumnTransferable::ObjectReleased()
+ {
+ m_aDescriptors.realloc(0);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/fmcomp/dbaobjectex.cxx b/svx/source/fmcomp/dbaobjectex.cxx
new file mode 100644
index 000000000..b60b026e6
--- /dev/null
+++ b/svx/source/fmcomp/dbaobjectex.cxx
@@ -0,0 +1,133 @@
+/* -*- 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/dbaobjectex.hxx>
+#include <osl/diagnose.h>
+#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
+#include <com/sun/star/sdb/XSQLQueryComposerFactory.hpp>
+#include <com/sun/star/ucb/XContent.hpp>
+#include <sot/formats.hxx>
+#include <sot/exchange.hxx>
+
+
+namespace svx
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::sdb;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::ucb;
+ using namespace ::com::sun::star::sdbcx;
+ using namespace ::com::sun::star::container;
+ using namespace ::com::sun::star::datatransfer;
+
+ OComponentTransferable::OComponentTransferable(const OUString& _rDatasourceOrLocation
+ ,const Reference< XContent>& _xContent)
+ {
+ m_aDescriptor.setDataSource(_rDatasourceOrLocation);
+ m_aDescriptor[DataAccessDescriptorProperty::Component] <<= _xContent;
+ }
+
+
+ SotClipboardFormatId OComponentTransferable::getDescriptorFormatId(bool _bExtractForm)
+ {
+ static SotClipboardFormatId s_nReportFormat = static_cast<SotClipboardFormatId>(-1);
+ static SotClipboardFormatId s_nFormFormat = static_cast<SotClipboardFormatId>(-1);
+ if ( _bExtractForm && static_cast<SotClipboardFormatId>(-1) == s_nFormFormat )
+ {
+ s_nFormFormat = SotExchange::RegisterFormatName("application/x-openoffice;windows_formatname=\"dbaccess.FormComponentDescriptorTransfer\"");
+ OSL_ENSURE(static_cast<SotClipboardFormatId>(-1) != s_nFormFormat, "OComponentTransferable::getDescriptorFormatId: bad exchange id!");
+ }
+ else if ( !_bExtractForm && static_cast<SotClipboardFormatId>(-1) == s_nReportFormat)
+ {
+ s_nReportFormat = SotExchange::RegisterFormatName("application/x-openoffice;windows_formatname=\"dbaccess.ReportComponentDescriptorTransfer\"");
+ OSL_ENSURE(static_cast<SotClipboardFormatId>(-1) != s_nReportFormat, "OComponentTransferable::getDescriptorFormatId: bad exchange id!");
+ }
+ return _bExtractForm ? s_nFormFormat : s_nReportFormat;
+ }
+
+
+ void OComponentTransferable::AddSupportedFormats()
+ {
+ bool bForm = true;
+ try
+ {
+ Reference<XPropertySet> xProp;
+ m_aDescriptor[DataAccessDescriptorProperty::Component] >>= xProp;
+ if ( xProp.is() )
+ xProp->getPropertyValue("IsForm") >>= bForm;
+ }
+ catch(const Exception&)
+ {}
+ AddFormat(getDescriptorFormatId(bForm));
+ }
+
+
+ bool OComponentTransferable::GetData( const DataFlavor& _rFlavor, const OUString& /*rDestDoc*/ )
+ {
+ const SotClipboardFormatId nFormatId = SotExchange::GetFormat(_rFlavor);
+ if ( nFormatId == getDescriptorFormatId(true) || nFormatId == getDescriptorFormatId(false) )
+ return SetAny( makeAny( m_aDescriptor.createPropertyValueSequence() ) );
+
+ return false;
+ }
+
+
+ bool OComponentTransferable::canExtractComponentDescriptor(const DataFlavorExVector& _rFlavors, bool _bForm )
+ {
+ SotClipboardFormatId nFormatId = getDescriptorFormatId(_bForm);
+ return std::any_of(_rFlavors.begin(), _rFlavors.end(),
+ [&nFormatId](const DataFlavorEx& rCheck) { return nFormatId == rCheck.mnSotId; });
+ }
+
+
+ ODataAccessDescriptor OComponentTransferable::extractComponentDescriptor(const TransferableDataHelper& _rData)
+ {
+ bool bForm = _rData.HasFormat(getDescriptorFormatId(true));
+ if ( bForm || _rData.HasFormat(getDescriptorFormatId(false)) )
+ {
+ // the object has a real descriptor object (not just the old compatible format)
+
+ // extract the any from the transferable
+ DataFlavor aFlavor;
+ bool bSuccess =
+ SotExchange::GetFormatDataFlavor(getDescriptorFormatId(bForm), aFlavor);
+ OSL_ENSURE(bSuccess, "OComponentTransferable::extractColumnDescriptor: invalid data format (no flavor)!");
+
+ Any aDescriptor = _rData.GetAny(aFlavor, OUString());
+
+ // extract the property value sequence
+ Sequence< PropertyValue > aDescriptorProps;
+ bSuccess = aDescriptor >>= aDescriptorProps;
+ OSL_ENSURE(bSuccess, "OComponentTransferable::extractColumnDescriptor: invalid clipboard format!");
+
+ // build the real descriptor
+ return ODataAccessDescriptor(aDescriptorProps);
+ }
+
+ return ODataAccessDescriptor();
+ }
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/fmcomp/fmgridcl.cxx b/svx/source/fmcomp/fmgridcl.cxx
new file mode 100644
index 000000000..68b4b3c53
--- /dev/null
+++ b/svx/source/fmcomp/fmgridcl.cxx
@@ -0,0 +1,2061 @@
+/* -*- 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/fmgridif.hxx>
+#include <fmprop.hxx>
+#include <svx/fmtools.hxx>
+#include <fmservs.hxx>
+#include <fmurl.hxx>
+#include <formcontrolfactory.hxx>
+#include <gridcell.hxx>
+#include <gridcols.hxx>
+#include <svx/dbaexchange.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <svx/fmgridcl.hxx>
+#include <svx/svxdlg.hxx>
+#include <svx/svxids.hrc>
+#include <bitmaps.hlst>
+
+#include <com/sun/star/form/XConfirmDeleteListener.hpp>
+#include <com/sun/star/form/XFormComponent.hpp>
+#include <com/sun/star/form/XGridColumnFactory.hpp>
+#include <com/sun/star/io/XPersistObject.hpp>
+#include <com/sun/star/sdb/CommandType.hpp>
+#include <com/sun/star/sdb/RowChangeAction.hpp>
+#include <com/sun/star/sdb/XQueriesSupplier.hpp>
+#include <com/sun/star/sdbc/DataType.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/sdbc/XPreparedStatement.hpp>
+#include <com/sun/star/sdbc/XResultSetUpdate.hpp>
+#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
+#include <com/sun/star/sdbcx/XDeleteRows.hpp>
+#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
+#include <com/sun/star/util/XNumberFormats.hpp>
+#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/string.hxx>
+#include <comphelper/types.hxx>
+#include <connectivity/dbtools.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svl/eitem.hxx>
+#include <vcl/builder.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/svapp.hxx>
+#include <tools/debug.hxx>
+#include <tools/multisel.hxx>
+#include <tools/diagnose_ex.h>
+#include <vcl/help.hxx>
+#include <vcl/image.hxx>
+#include <vcl/menu.hxx>
+#include <vcl/settings.hxx>
+#include <sal/log.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <memory>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::view;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::sdbcx;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::container;
+using namespace ::cppu;
+using namespace ::svxform;
+using namespace ::svx;
+using namespace ::dbtools;
+
+struct FmGridHeaderData
+{
+ ODataAccessDescriptor aDropData;
+ Point aDropPosPixel;
+ sal_Int8 nDropAction;
+ Reference< XInterface > xDroppedStatement;
+ Reference< XInterface > xDroppedResultSet;
+};
+
+static void SetMenuItem(const OUString& rImgID, const OString &rID, Menu& rMenu, bool bDesignMode)
+{
+ Image aImage(StockImage::Yes, rImgID);
+ sal_uInt16 nID = rMenu.GetItemId(rID);
+ rMenu.SetItemImage(nID, aImage);
+ rMenu.EnableItem(nID, bDesignMode);
+}
+
+FmGridHeader::FmGridHeader( BrowseBox* pParent, WinBits nWinBits)
+ :EditBrowserHeader(pParent, nWinBits)
+ ,DropTargetHelper(this)
+ ,m_pImpl(new FmGridHeaderData)
+{
+}
+
+FmGridHeader::~FmGridHeader()
+{
+ disposeOnce();
+}
+
+void FmGridHeader::dispose()
+{
+ m_pImpl.reset();
+ DropTargetHelper::dispose();
+ svt::EditBrowserHeader::dispose();
+}
+
+sal_uInt16 FmGridHeader::GetModelColumnPos(sal_uInt16 nId) const
+{
+ return static_cast<FmGridControl*>(GetParent())->GetModelColumnPos(nId);
+}
+
+void FmGridHeader::notifyColumnSelect(sal_uInt16 nColumnId)
+{
+ sal_uInt16 nPos = GetModelColumnPos(nColumnId);
+ Reference< XIndexAccess > xColumns = static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns();
+ if ( nPos < xColumns->getCount() )
+ {
+ Reference< XSelectionSupplier > xSelSupplier(xColumns, UNO_QUERY);
+ if ( xSelSupplier.is() )
+ {
+ Reference< XPropertySet > xColumn;
+ xColumns->getByIndex(nPos) >>= xColumn;
+ xSelSupplier->select(makeAny(xColumn));
+ }
+ }
+}
+
+void FmGridHeader::Select()
+{
+ EditBrowserHeader::Select();
+ notifyColumnSelect(GetCurItemId());
+}
+
+void FmGridHeader::RequestHelp( const HelpEvent& rHEvt )
+{
+ sal_uInt16 nItemId = GetItemId( ScreenToOutputPixel( rHEvt.GetMousePosPixel() ) );
+ if ( nItemId )
+ {
+ if ( rHEvt.GetMode() & (HelpEventMode::QUICK | HelpEventMode::BALLOON) )
+ {
+ tools::Rectangle aItemRect = GetItemRect( nItemId );
+ Point aPt = OutputToScreenPixel( aItemRect.TopLeft() );
+ aItemRect.SetLeft( aPt.X() );
+ aItemRect.SetTop( aPt.Y() );
+ aPt = OutputToScreenPixel( aItemRect.BottomRight() );
+ aItemRect.SetRight( aPt.X() );
+ aItemRect.SetBottom( aPt.Y() );
+
+ sal_uInt16 nPos = GetModelColumnPos(nItemId);
+ Reference< css::container::XIndexContainer > xColumns(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns());
+ try
+ {
+ Reference< css::beans::XPropertySet > xColumn(xColumns->getByIndex(nPos),UNO_QUERY);
+ OUString aHelpText;
+ xColumn->getPropertyValue(FM_PROP_HELPTEXT) >>= aHelpText;
+ if ( aHelpText.isEmpty() )
+ xColumn->getPropertyValue(FM_PROP_DESCRIPTION) >>= aHelpText;
+ if ( !aHelpText.isEmpty() )
+ {
+ if ( rHEvt.GetMode() & HelpEventMode::BALLOON )
+ Help::ShowBalloon( this, aItemRect.Center(), aItemRect, aHelpText );
+ else
+ Help::ShowQuickHelp( this, aItemRect, aHelpText );
+ return;
+ }
+ }
+ catch(Exception&)
+ {
+ return;
+ }
+ }
+ }
+ EditBrowserHeader::RequestHelp( rHEvt );
+}
+
+sal_Int8 FmGridHeader::AcceptDrop( const AcceptDropEvent& rEvt )
+{
+ // drop allowed in design mode only
+ if (!static_cast<FmGridControl*>(GetParent())->IsDesignMode())
+ return DND_ACTION_NONE;
+
+ // search for recognized formats
+ const DataFlavorExVector& rFlavors = GetDataFlavorExVector();
+ if (OColumnTransferable::canExtractColumnDescriptor(rFlavors, ColumnTransferFormatFlags::COLUMN_DESCRIPTOR | ColumnTransferFormatFlags::FIELD_DESCRIPTOR))
+ return rEvt.mnAction;
+
+ return DND_ACTION_NONE;
+}
+
+sal_Int8 FmGridHeader::ExecuteDrop( const ExecuteDropEvent& _rEvt )
+{
+ if (!static_cast<FmGridControl*>(GetParent())->IsDesignMode())
+ return DND_ACTION_NONE;
+
+ TransferableDataHelper aDroppedData(_rEvt.maDropEvent.Transferable);
+
+ // check the formats
+ bool bColumnDescriptor = OColumnTransferable::canExtractColumnDescriptor(aDroppedData.GetDataFlavorExVector(), ColumnTransferFormatFlags::COLUMN_DESCRIPTOR);
+ bool bFieldDescriptor = OColumnTransferable::canExtractColumnDescriptor(aDroppedData.GetDataFlavorExVector(), ColumnTransferFormatFlags::FIELD_DESCRIPTOR);
+ if (!bColumnDescriptor && !bFieldDescriptor)
+ {
+ OSL_FAIL("FmGridHeader::ExecuteDrop: should never have reached this (no extractable format)!");
+ return DND_ACTION_NONE;
+ }
+
+ // extract the descriptor
+ OUString sDatasource, sCommand, sFieldName,sDatabaseLocation;
+ sal_Int32 nCommandType = CommandType::COMMAND;
+ Reference< XPreparedStatement > xStatement;
+ Reference< XResultSet > xResultSet;
+ Reference< XPropertySet > xField;
+ Reference< XConnection > xConnection;
+
+ ODataAccessDescriptor aColumn = OColumnTransferable::extractColumnDescriptor(aDroppedData);
+ if (aColumn.has(DataAccessDescriptorProperty::DataSource)) aColumn[DataAccessDescriptorProperty::DataSource] >>= sDatasource;
+ if (aColumn.has(DataAccessDescriptorProperty::DatabaseLocation)) aColumn[DataAccessDescriptorProperty::DatabaseLocation] >>= sDatabaseLocation;
+ if (aColumn.has(DataAccessDescriptorProperty::Command)) aColumn[DataAccessDescriptorProperty::Command] >>= sCommand;
+ if (aColumn.has(DataAccessDescriptorProperty::CommandType)) aColumn[DataAccessDescriptorProperty::CommandType] >>= nCommandType;
+ if (aColumn.has(DataAccessDescriptorProperty::ColumnName)) aColumn[DataAccessDescriptorProperty::ColumnName] >>= sFieldName;
+ if (aColumn.has(DataAccessDescriptorProperty::ColumnObject))aColumn[DataAccessDescriptorProperty::ColumnObject] >>= xField;
+ if (aColumn.has(DataAccessDescriptorProperty::Connection)) aColumn[DataAccessDescriptorProperty::Connection] >>= xConnection;
+
+ if ( sFieldName.isEmpty()
+ || sCommand.isEmpty()
+ || ( sDatasource.isEmpty()
+ && sDatabaseLocation.isEmpty()
+ && !xConnection.is()
+ )
+ )
+ {
+ OSL_FAIL( "FmGridHeader::ExecuteDrop: somebody started a nonsense drag operation!!" );
+ return DND_ACTION_NONE;
+ }
+
+ try
+ {
+ // need a connection
+ if (!xConnection.is())
+ { // the transferable did not contain the connection -> build an own one
+ try
+ {
+ OUString sSignificantSource( sDatasource.isEmpty() ? sDatabaseLocation : sDatasource );
+ xConnection = getConnection_withFeedback(sSignificantSource, OUString(), OUString(),
+ static_cast<FmGridControl*>(GetParent())->getContext(), nullptr );
+ }
+ catch(NoSuchElementException&)
+ { // allowed, means sDatasource isn't a valid data source name...
+ }
+ catch(Exception&)
+ {
+ OSL_FAIL("FmGridHeader::ExecuteDrop: could not retrieve the database access object !");
+ }
+
+ if (!xConnection.is())
+ {
+ OSL_FAIL("FmGridHeader::ExecuteDrop: could not retrieve the database access object !");
+ return DND_ACTION_NONE;
+ }
+ }
+
+ // try to obtain the column object
+ if (!xField.is())
+ {
+#ifdef DBG_UTIL
+ Reference< XServiceInfo > xServiceInfo(xConnection, UNO_QUERY);
+ DBG_ASSERT(xServiceInfo.is() && xServiceInfo->supportsService(SRV_SDB_CONNECTION), "FmGridHeader::ExecuteDrop: invalid connection (no database access connection !)");
+#endif
+
+ Reference< XNameAccess > xFields;
+ switch (nCommandType)
+ {
+ case CommandType::TABLE:
+ {
+ Reference< XTablesSupplier > xSupplyTables(xConnection, UNO_QUERY);
+ Reference< XColumnsSupplier > xSupplyColumns;
+ xSupplyTables->getTables()->getByName(sCommand) >>= xSupplyColumns;
+ xFields = xSupplyColumns->getColumns();
+ }
+ break;
+ case CommandType::QUERY:
+ {
+ Reference< XQueriesSupplier > xSupplyQueries(xConnection, UNO_QUERY);
+ Reference< XColumnsSupplier > xSupplyColumns;
+ xSupplyQueries->getQueries()->getByName(sCommand) >>= xSupplyColumns;
+ xFields = xSupplyColumns->getColumns();
+ }
+ break;
+ default:
+ {
+ xStatement = xConnection->prepareStatement(sCommand);
+ // not interested in any results
+
+ Reference< XPropertySet > xStatProps(xStatement,UNO_QUERY);
+ xStatProps->setPropertyValue("MaxRows", makeAny(sal_Int32(0)));
+
+ xResultSet = xStatement->executeQuery();
+ Reference< XColumnsSupplier > xSupplyCols(xResultSet, UNO_QUERY);
+ if (xSupplyCols.is())
+ xFields = xSupplyCols->getColumns();
+ }
+ }
+
+ if (xFields.is() && xFields->hasByName(sFieldName))
+ xFields->getByName(sFieldName) >>= xField;
+
+ if (!xField.is())
+ {
+ ::comphelper::disposeComponent(xStatement);
+ return DND_ACTION_NONE;
+ }
+ }
+
+ // do the drop asynchronously
+ // (85957 - UI actions within the drop are not allowed, but we want to open a popup menu)
+ m_pImpl->aDropData = aColumn;
+ m_pImpl->aDropData[DataAccessDescriptorProperty::Connection] <<= xConnection;
+ m_pImpl->aDropData[DataAccessDescriptorProperty::ColumnObject] <<= xField;
+
+ m_pImpl->nDropAction = _rEvt.mnAction;
+ m_pImpl->aDropPosPixel = _rEvt.maPosPixel;
+ m_pImpl->xDroppedStatement = xStatement;
+ m_pImpl->xDroppedResultSet = xResultSet;
+
+ PostUserEvent(LINK(this, FmGridHeader, OnAsyncExecuteDrop), nullptr, true);
+ }
+ catch (Exception&)
+ {
+ OSL_FAIL("FmGridHeader::ExecuteDrop: caught an exception while creatin' the column !");
+ ::comphelper::disposeComponent(xStatement);
+ return DND_ACTION_NONE;
+ }
+
+ return DND_ACTION_LINK;
+}
+
+IMPL_LINK_NOARG( FmGridHeader, OnAsyncExecuteDrop, void*, void )
+{
+ OUString sCommand, sFieldName,sURL;
+ sal_Int32 nCommandType = CommandType::COMMAND;
+ Reference< XPropertySet > xField;
+ Reference< XConnection > xConnection;
+
+ OUString sDatasource = m_pImpl->aDropData.getDataSource();
+ if ( sDatasource.isEmpty() && m_pImpl->aDropData.has(DataAccessDescriptorProperty::ConnectionResource) )
+ m_pImpl->aDropData[DataAccessDescriptorProperty::ConnectionResource] >>= sURL;
+ m_pImpl->aDropData[DataAccessDescriptorProperty::Command] >>= sCommand;
+ m_pImpl->aDropData[DataAccessDescriptorProperty::CommandType] >>= nCommandType;
+ m_pImpl->aDropData[DataAccessDescriptorProperty::ColumnName] >>= sFieldName;
+ m_pImpl->aDropData[DataAccessDescriptorProperty::Connection] >>= xConnection;
+ m_pImpl->aDropData[DataAccessDescriptorProperty::ColumnObject] >>= xField;
+
+ try
+ {
+ // need number formats
+ Reference< XNumberFormatsSupplier > xSupplier = getNumberFormats(xConnection, true);
+ Reference< XNumberFormats > xNumberFormats;
+ if (xSupplier.is())
+ xNumberFormats = xSupplier->getNumberFormats();
+ if (!xNumberFormats.is())
+ {
+ ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet);
+ ::comphelper::disposeComponent(m_pImpl->xDroppedStatement);
+ return;
+ }
+
+ // The field now needs two pieces of information:
+ // a.) Name of the field for label and ControlSource
+ // b.) FormatKey, to determine which field is to be created
+ sal_Int32 nDataType = 0;
+ xField->getPropertyValue(FM_PROP_FIELDTYPE) >>= nDataType;
+ // these datatypes can not be processed in Gridcontrol
+ switch (nDataType)
+ {
+ case DataType::BLOB:
+ case DataType::LONGVARBINARY:
+ case DataType::BINARY:
+ case DataType::VARBINARY:
+ case DataType::OTHER:
+ ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet);
+ ::comphelper::disposeComponent(m_pImpl->xDroppedStatement);
+ return;
+ }
+
+ // Creating the column
+ Reference< XIndexContainer > xCols(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns());
+ Reference< XGridColumnFactory > xFactory(xCols, UNO_QUERY);
+
+ sal_uInt16 nColId = GetItemId(m_pImpl->aDropPosPixel);
+ // insert position, always before the current column
+ sal_uInt16 nPos = GetModelColumnPos(nColId);
+ Reference< XPropertySet > xCol, xSecondCol;
+
+ // Create Column based on type, default textfield
+ std::vector<OString> aPossibleTypes;
+ std::vector<OUString> aImgResId;
+ switch (nDataType)
+ {
+ case DataType::BIT:
+ case DataType::BOOLEAN:
+ aPossibleTypes.emplace_back(FM_COL_CHECKBOX);
+ aImgResId.emplace_back(RID_SVXBMP_CHECKBOX);
+ break;
+ case DataType::TINYINT:
+ case DataType::SMALLINT:
+ case DataType::INTEGER:
+ aPossibleTypes.emplace_back(FM_COL_NUMERICFIELD);
+ aImgResId.emplace_back(RID_SVXBMP_NUMERICFIELD);
+ aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
+ aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
+ break;
+ case DataType::REAL:
+ case DataType::DOUBLE:
+ case DataType::NUMERIC:
+ case DataType::DECIMAL:
+ aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
+ aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
+ aPossibleTypes.emplace_back(FM_COL_NUMERICFIELD);
+ aImgResId.emplace_back(RID_SVXBMP_NUMERICFIELD);
+ break;
+ case DataType::TIMESTAMP:
+ aPossibleTypes.emplace_back("dateandtimefield");
+ aImgResId.emplace_back(RID_SVXBMP_DATE_N_TIME_FIELDS);
+ aPossibleTypes.emplace_back(FM_COL_DATEFIELD);
+ aImgResId.emplace_back(RID_SVXBMP_DATEFIELD);
+ aPossibleTypes.emplace_back(FM_COL_TIMEFIELD);
+ aImgResId.emplace_back(RID_SVXBMP_TIMEFIELD);
+ aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
+ aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
+ break;
+ case DataType::DATE:
+ aPossibleTypes.emplace_back(FM_COL_DATEFIELD);
+ aImgResId.emplace_back(RID_SVXBMP_DATEFIELD);
+ aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
+ aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
+ break;
+ case DataType::TIME:
+ aPossibleTypes.emplace_back(FM_COL_TIMEFIELD);
+ aImgResId.emplace_back(RID_SVXBMP_TIMEFIELD);
+ aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
+ aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
+ break;
+ case DataType::CHAR:
+ case DataType::VARCHAR:
+ case DataType::LONGVARCHAR:
+ default:
+ aPossibleTypes.emplace_back(FM_COL_TEXTFIELD);
+ aImgResId.emplace_back(RID_SVXBMP_EDITBOX);
+ aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
+ aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
+ break;
+ }
+ // if it's a currency field, a "currency field" option
+ try
+ {
+ if ( ::comphelper::hasProperty(FM_PROP_ISCURRENCY, xField)
+ && ::comphelper::getBOOL(xField->getPropertyValue(FM_PROP_ISCURRENCY)))
+ {
+ aPossibleTypes.insert(aPossibleTypes.begin(), FM_COL_CURRENCYFIELD);
+ aImgResId.insert(aImgResId.begin(), RID_SVXBMP_CURRENCYFIELD);
+ }
+ }
+ catch (const Exception&)
+ {
+ OSL_FAIL("FmGridHeader::ExecuteDrop: Exception occurred!");
+ }
+
+ assert(aPossibleTypes.size() == aImgResId.size());
+
+ bool bDateNTimeCol = false;
+ if (!aPossibleTypes.empty())
+ {
+ OString sPreferredType = aPossibleTypes[0];
+ if ((m_pImpl->nDropAction == DND_ACTION_LINK) && (aPossibleTypes.size() > 1))
+ {
+ VclBuilder aBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "svx/ui/colsmenu.ui", "");
+ VclPtr<PopupMenu> aInsertMenu(aBuilder.get_menu("menu"));
+ PopupMenu* pTypeMenu = aInsertMenu->GetPopupMenu(aInsertMenu->GetItemId("insert"));
+ pTypeMenu->ShowItem(pTypeMenu->GetItemId("dateandtimefield"));
+ std::vector<OString>::const_iterator iter;
+ std::vector<OUString>::const_iterator imgiter;
+ for (iter = aPossibleTypes.begin(), imgiter = aImgResId.begin();
+ iter != aPossibleTypes.end(); ++iter, ++imgiter)
+ {
+ SetMenuItem(*imgiter, *iter, *pTypeMenu, true);
+ }
+ if (pTypeMenu->Execute(this, m_pImpl->aDropPosPixel))
+ sPreferredType = pTypeMenu->GetCurItemIdent();
+ }
+
+ bDateNTimeCol = sPreferredType == "dateandtimefield";
+ sal_uInt16 nColCount = bDateNTimeCol ? 2 : 1;
+ OUString sFieldService;
+ while (nColCount--)
+ {
+ if (bDateNTimeCol)
+ sPreferredType = nColCount ? FM_COL_DATEFIELD : FM_COL_TIMEFIELD;
+
+ sFieldService = OUString::fromUtf8(sPreferredType);
+ Reference< XPropertySet > xThisRoundCol;
+ if ( !sFieldService.isEmpty() )
+ xThisRoundCol = xFactory->createColumn(sFieldService);
+ if (nColCount)
+ xSecondCol = xThisRoundCol;
+ else
+ xCol = xThisRoundCol;
+ }
+ }
+
+ if (!xCol.is() || (bDateNTimeCol && !xSecondCol.is()))
+ {
+ ::comphelper::disposeComponent(xCol); // in case only the creation of the second column failed
+ ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet);
+ ::comphelper::disposeComponent(m_pImpl->xDroppedStatement);
+ return;
+ }
+
+ if (bDateNTimeCol)
+ {
+ OUString sTimePostfix(SvxResId(RID_STR_POSTFIX_TIME));
+ xCol->setPropertyValue(FM_PROP_LABEL, makeAny( OUString( sFieldName + sTimePostfix ) ) );
+
+ OUString sDatePostfix(SvxResId( RID_STR_POSTFIX_DATE));
+ xSecondCol->setPropertyValue(FM_PROP_LABEL, makeAny( OUString( sFieldName + sDatePostfix ) ) );
+ }
+ else
+ xCol->setPropertyValue(FM_PROP_LABEL, makeAny(sFieldName));
+
+ // insert now
+ Any aElement;
+ aElement <<= xCol;
+
+ xCols->insertByIndex(nPos, aElement);
+
+ FormControlFactory aControlFactory;
+ aControlFactory.initializeControlModel( DocumentClassification::classifyHostDocument( xCols ), xCol );
+ FormControlFactory::initializeFieldDependentProperties( xField, xCol, xNumberFormats );
+
+ xCol->setPropertyValue(FM_PROP_CONTROLSOURCE, makeAny(sFieldName));
+ if ( xSecondCol.is() )
+ xSecondCol->setPropertyValue(FM_PROP_CONTROLSOURCE, makeAny(sFieldName));
+
+ if (bDateNTimeCol)
+ {
+ OUString aPostfix[] = {
+ SvxResId(RID_STR_POSTFIX_DATE),
+ SvxResId(RID_STR_POSTFIX_TIME)
+ };
+
+ for ( size_t i=0; i<2; ++i )
+ {
+ OUString sPurePostfix = comphelper::string::stripStart(aPostfix[i], ' ');
+ sPurePostfix = comphelper::string::stripStart(sPurePostfix, '(');
+ sPurePostfix = comphelper::string::stripEnd(sPurePostfix, ')');
+ OUString sRealName = sFieldName + "_" + sPurePostfix;
+ if (i)
+ xSecondCol->setPropertyValue(FM_PROP_NAME, makeAny(sRealName));
+ else
+ xCol->setPropertyValue(FM_PROP_NAME, makeAny(sRealName));
+ }
+ }
+ else
+ xCol->setPropertyValue(FM_PROP_NAME, makeAny(sFieldName));
+
+ if (bDateNTimeCol)
+ {
+ aElement <<= xSecondCol;
+ xCols->insertByIndex(nPos == sal_uInt16(-1) ? nPos : ++nPos, aElement);
+ }
+
+ // is the component::Form tied to the database?
+ Reference< XFormComponent > xFormCp(xCols, UNO_QUERY);
+ Reference< XPropertySet > xForm(xFormCp->getParent(), UNO_QUERY);
+ if (xForm.is())
+ {
+ if (::comphelper::getString(xForm->getPropertyValue(FM_PROP_DATASOURCE)).isEmpty())
+ {
+ if ( !sDatasource.isEmpty() )
+ xForm->setPropertyValue(FM_PROP_DATASOURCE, makeAny(sDatasource));
+ else
+ xForm->setPropertyValue(FM_PROP_URL, makeAny(sURL));
+ }
+
+ if (::comphelper::getString(xForm->getPropertyValue(FM_PROP_COMMAND)).isEmpty())
+ {
+ xForm->setPropertyValue(FM_PROP_COMMAND, makeAny(sCommand));
+ Any aCommandType;
+ switch (nCommandType)
+ {
+ case CommandType::TABLE:
+ aCommandType <<= sal_Int32(CommandType::TABLE);
+ break;
+ case CommandType::QUERY:
+ aCommandType <<= sal_Int32(CommandType::QUERY);
+ break;
+ default:
+ aCommandType <<= sal_Int32(CommandType::COMMAND);
+ xForm->setPropertyValue(FM_PROP_ESCAPE_PROCESSING, css::uno::Any(2 == nCommandType));
+ break;
+ }
+ xForm->setPropertyValue(FM_PROP_COMMANDTYPE, aCommandType);
+ }
+ }
+ }
+ catch (Exception&)
+ {
+ OSL_FAIL("FmGridHeader::OnAsyncExecuteDrop: caught an exception while creatin' the column !");
+ ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet);
+ ::comphelper::disposeComponent(m_pImpl->xDroppedStatement);
+ return;
+ }
+
+ ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet);
+ ::comphelper::disposeComponent(m_pImpl->xDroppedStatement);
+}
+
+void FmGridHeader::PreExecuteColumnContextMenu(sal_uInt16 nColId, PopupMenu& rMenu)
+{
+ bool bDesignMode = static_cast<FmGridControl*>(GetParent())->IsDesignMode();
+
+ Reference< css::container::XIndexContainer > xCols(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns());
+ // Building of the Insert Menu
+ // mark the column if nColId != HEADERBAR_ITEM_NOTFOUND
+ if(nColId > 0)
+ {
+ sal_uInt16 nPos2 = GetModelColumnPos(nColId);
+
+ Reference< css::container::XIndexContainer > xColumns(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns());
+ Reference< css::beans::XPropertySet> xColumn( xColumns->getByIndex(nPos2), css::uno::UNO_QUERY);
+ Reference< css::view::XSelectionSupplier > xSelSupplier(xColumns, UNO_QUERY);
+ if (xSelSupplier.is())
+ xSelSupplier->select(makeAny(xColumn));
+ }
+
+ // insert position, always before the current column
+ sal_uInt16 nPos = GetModelColumnPos(nColId);
+ bool bMarked = nColId && static_cast<FmGridControl*>(GetParent())->isColumnMarked(nColId);
+
+ PopupMenu* pMenu = rMenu.GetPopupMenu(rMenu.GetItemId("insert"));
+ if (pMenu)
+ {
+ SetMenuItem(RID_SVXBMP_EDITBOX, FM_COL_TEXTFIELD, *pMenu, bDesignMode);
+ SetMenuItem(RID_SVXBMP_CHECKBOX, FM_COL_CHECKBOX, *pMenu, bDesignMode);
+ SetMenuItem(RID_SVXBMP_COMBOBOX, FM_COL_COMBOBOX, *pMenu, bDesignMode);
+ SetMenuItem(RID_SVXBMP_LISTBOX, FM_COL_LISTBOX, *pMenu, bDesignMode);
+ SetMenuItem(RID_SVXBMP_DATEFIELD, FM_COL_DATEFIELD, *pMenu, bDesignMode);
+ SetMenuItem(RID_SVXBMP_TIMEFIELD, FM_COL_TIMEFIELD, *pMenu, bDesignMode);
+ SetMenuItem(RID_SVXBMP_NUMERICFIELD, FM_COL_NUMERICFIELD, *pMenu, bDesignMode);
+ SetMenuItem(RID_SVXBMP_CURRENCYFIELD, FM_COL_CURRENCYFIELD, *pMenu, bDesignMode);
+ SetMenuItem(RID_SVXBMP_PATTERNFIELD, FM_COL_PATTERNFIELD, *pMenu, bDesignMode);
+ SetMenuItem(RID_SVXBMP_FORMATTEDFIELD, FM_COL_FORMATTEDFIELD, *pMenu, bDesignMode);
+ }
+
+ if (pMenu && xCols.is() && nColId)
+ {
+ Reference< css::beans::XPropertySet > xPropSet( xCols->getByIndex(nPos), css::uno::UNO_QUERY);
+
+ Reference< css::io::XPersistObject > xServiceQuestion(xPropSet, UNO_QUERY);
+ sal_Int32 nColType = xServiceQuestion.is() ? getColumnTypeByModelName(xServiceQuestion->getServiceName()) : 0;
+ if (nColType == TYPE_TEXTFIELD)
+ { // edit fields and formatted fields have the same service name, thus getColumnTypeByModelName returns TYPE_TEXTFIELD
+ // in both cases. And as columns don't have a css::lang::XServiceInfo interface, we have to distinguish both
+ // types via the existence of special properties
+ if (xPropSet.is())
+ {
+ Reference< css::beans::XPropertySetInfo > xPropsInfo = xPropSet->getPropertySetInfo();
+ if (xPropsInfo.is() && xPropsInfo->hasPropertyByName(FM_PROP_FORMATSSUPPLIER))
+ nColType = TYPE_FORMATTEDFIELD;
+ }
+ }
+
+ PopupMenu* pControlMenu = rMenu.GetPopupMenu(rMenu.GetItemId("change"));
+ if (pControlMenu)
+ {
+ SetMenuItem(RID_SVXBMP_EDITBOX, FM_COL_TEXTFIELD"1", *pControlMenu, bDesignMode && (nColType != TYPE_TEXTFIELD));
+ SetMenuItem(RID_SVXBMP_CHECKBOX, FM_COL_CHECKBOX"1", *pControlMenu, bDesignMode && (nColType != TYPE_CHECKBOX));
+ SetMenuItem(RID_SVXBMP_COMBOBOX, FM_COL_COMBOBOX"1", *pControlMenu, bDesignMode && (nColType != TYPE_COMBOBOX));
+ SetMenuItem(RID_SVXBMP_LISTBOX, FM_COL_LISTBOX"1", *pControlMenu, bDesignMode && (nColType != TYPE_LISTBOX));
+ SetMenuItem(RID_SVXBMP_DATEFIELD, FM_COL_DATEFIELD"1", *pControlMenu, bDesignMode && (nColType != TYPE_DATEFIELD));
+ SetMenuItem(RID_SVXBMP_TIMEFIELD, FM_COL_TIMEFIELD"1", *pControlMenu, bDesignMode && (nColType != TYPE_TIMEFIELD));
+ SetMenuItem(RID_SVXBMP_NUMERICFIELD, FM_COL_NUMERICFIELD"1", *pControlMenu, bDesignMode && (nColType != TYPE_NUMERICFIELD));
+ SetMenuItem(RID_SVXBMP_CURRENCYFIELD, FM_COL_CURRENCYFIELD"1", *pControlMenu, bDesignMode && (nColType != TYPE_CURRENCYFIELD));
+ SetMenuItem(RID_SVXBMP_PATTERNFIELD, FM_COL_PATTERNFIELD"1", *pControlMenu, bDesignMode && (nColType != TYPE_PATTERNFIELD));
+ SetMenuItem(RID_SVXBMP_FORMATTEDFIELD, FM_COL_FORMATTEDFIELD"1", *pControlMenu, bDesignMode && (nColType != TYPE_FORMATTEDFIELD));
+ }
+ rMenu.EnableItem(rMenu.GetItemId("change"), bDesignMode && bMarked && xCols.is());
+ }
+ else
+ rMenu.EnableItem(rMenu.GetItemId("change"), false);
+
+ rMenu.EnableItem(rMenu.GetItemId("insert"), bDesignMode && xCols.is());
+ rMenu.EnableItem(rMenu.GetItemId("delete"), bDesignMode && bMarked && xCols.is());
+ rMenu.EnableItem(rMenu.GetItemId("column"), bDesignMode && bMarked && xCols.is());
+
+ PopupMenu* pShowColsMenu = rMenu.GetPopupMenu(rMenu.GetItemId("show"));
+ sal_uInt16 nHiddenCols = 0;
+ if (pShowColsMenu)
+ {
+ if (xCols.is())
+ {
+ // check for hidden cols
+ Reference< css::beans::XPropertySet > xCurCol;
+ Any aHidden,aName;
+ for (sal_Int32 i=0; i<xCols->getCount(); ++i)
+ {
+ xCurCol.set(xCols->getByIndex(i), css::uno::UNO_QUERY);
+ DBG_ASSERT(xCurCol.is(), "FmGridHeader::PreExecuteColumnContextMenu : the Peer has invalid columns !");
+ aHidden = xCurCol->getPropertyValue(FM_PROP_HIDDEN);
+ DBG_ASSERT(aHidden.getValueType().getTypeClass() == TypeClass_BOOLEAN,
+ "FmGridHeader::PreExecuteColumnContextMenu : the property 'hidden' should be boolean !");
+ if (::comphelper::getBOOL(aHidden))
+ {
+ // put the column name into the 'show col' menu
+ if (nHiddenCols < 16)
+ { // (only the first 16 items to keep the menu rather small)
+ aName = xCurCol->getPropertyValue(FM_PROP_LABEL);
+ pShowColsMenu->InsertItem(nHiddenCols + 1, ::comphelper::getString(aName),
+ MenuItemBits::NONE, OString(), nHiddenCols);
+ // the ID is arbitrary, but should be unique within the whole menu
+ }
+ ++nHiddenCols;
+ }
+ }
+ }
+ pShowColsMenu->EnableItem(pShowColsMenu->GetItemId("more"), xCols.is() && (nHiddenCols > 16));
+ pShowColsMenu->EnableItem(pShowColsMenu->GetItemId("all"), xCols.is() && (nHiddenCols > 0));
+ }
+
+ // allow the 'hide column' item ?
+ bool bAllowHide = bMarked; // a column is marked
+ bAllowHide = bAllowHide || (!bDesignMode && (nPos != sal_uInt16(-1))); // OR we are in alive mode and have hit a column
+ bAllowHide = bAllowHide && xCols.is(); // AND we have a column container
+ bAllowHide = bAllowHide && (xCols->getCount()-nHiddenCols > 1); // AND there are at least two visible columns
+ rMenu.EnableItem(rMenu.GetItemId("hide"), bAllowHide);
+
+ if (bMarked)
+ {
+
+ SfxViewFrame* pCurrentFrame = SfxViewFrame::Current();
+ SfxItemState eState = SfxItemState::UNKNOWN;
+ // ask the bindings of the current view frame (which should be the one we're residing in) for the state
+ if (pCurrentFrame)
+ {
+ std::unique_ptr<SfxPoolItem> pItem;
+ eState = pCurrentFrame->GetBindings().QueryState(SID_FM_CTL_PROPERTIES, pItem);
+
+ if (eState >= SfxItemState::DEFAULT && pItem != nullptr)
+ {
+ bool bChecked = dynamic_cast<const SfxBoolItem*>( pItem.get()) != nullptr && static_cast<SfxBoolItem*>(pItem.get())->GetValue();
+ rMenu.CheckItem("column", bChecked);
+ }
+ }
+ }
+}
+
+namespace {
+
+enum InspectorAction { eOpenInspector, eCloseInspector, eUpdateInspector, eNone };
+
+}
+
+void FmGridHeader::PostExecuteColumnContextMenu(sal_uInt16 nColId, const PopupMenu& rMenu, sal_uInt16 nExecutionResult)
+{
+ Reference< css::container::XIndexContainer > xCols(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns());
+ sal_uInt16 nPos = GetModelColumnPos(nColId);
+
+ OUString aFieldType;
+ bool bReplace = false;
+ InspectorAction eInspectorAction = eNone;
+
+ OString sExecutionResult = rMenu.GetCurItemIdent();
+ if (sExecutionResult.isEmpty())
+ {
+ PopupMenu* pMenu = rMenu.GetPopupMenu(rMenu.GetItemId("insert"));
+ if (pMenu)
+ sExecutionResult = pMenu->GetCurItemIdent();
+ }
+ if (sExecutionResult.isEmpty())
+ {
+ PopupMenu* pMenu = rMenu.GetPopupMenu(rMenu.GetItemId("change"));
+ if (pMenu)
+ sExecutionResult = pMenu->GetCurItemIdent();
+ }
+ if (sExecutionResult.isEmpty())
+ {
+ PopupMenu* pMenu = rMenu.GetPopupMenu(rMenu.GetItemId("show"));
+ if (pMenu)
+ sExecutionResult = pMenu->GetCurItemIdent();
+ }
+
+ if (sExecutionResult == "delete")
+ {
+ Reference< XInterface > xCol(
+ xCols->getByIndex(nPos), css::uno::UNO_QUERY);
+ xCols->removeByIndex(nPos);
+ ::comphelper::disposeComponent(xCol);
+ }
+ else if (sExecutionResult == "hide")
+ {
+ Reference< css::beans::XPropertySet > xCurCol( xCols->getByIndex(nPos), css::uno::UNO_QUERY);
+ xCurCol->setPropertyValue(FM_PROP_HIDDEN, makeAny(true));
+ }
+ else if (sExecutionResult == "column")
+ {
+ eInspectorAction = rMenu.IsItemChecked(rMenu.GetItemId("column")) ? eOpenInspector : eCloseInspector;
+ }
+ else if (sExecutionResult.startsWith(FM_COL_TEXTFIELD))
+ {
+ if (sExecutionResult != FM_COL_TEXTFIELD)
+ bReplace = true;
+ aFieldType = FM_COL_TEXTFIELD;
+ }
+ else if (sExecutionResult.startsWith(FM_COL_COMBOBOX))
+ {
+ if (sExecutionResult != FM_COL_COMBOBOX)
+ bReplace = true;
+ aFieldType = FM_COL_COMBOBOX;
+ }
+ else if (sExecutionResult.startsWith(FM_COL_LISTBOX))
+ {
+ if (sExecutionResult != FM_COL_LISTBOX)
+ bReplace = true;
+ aFieldType = FM_COL_LISTBOX;
+ }
+ else if (sExecutionResult.startsWith(FM_COL_CHECKBOX))
+ {
+ if (sExecutionResult != FM_COL_CHECKBOX)
+ bReplace = true;
+ aFieldType = FM_COL_CHECKBOX;
+ }
+ else if (sExecutionResult.startsWith(FM_COL_DATEFIELD))
+ {
+ if (sExecutionResult != FM_COL_DATEFIELD)
+ bReplace = true;
+ aFieldType = FM_COL_DATEFIELD;
+ }
+ else if (sExecutionResult.startsWith(FM_COL_TIMEFIELD))
+ {
+ if (sExecutionResult != FM_COL_TIMEFIELD)
+ bReplace = true;
+ aFieldType = FM_COL_TIMEFIELD;
+ }
+ else if (sExecutionResult.startsWith(FM_COL_NUMERICFIELD))
+ {
+ if (sExecutionResult != FM_COL_NUMERICFIELD)
+ bReplace = true;
+ aFieldType = FM_COL_NUMERICFIELD;
+ }
+ else if (sExecutionResult.startsWith(FM_COL_CURRENCYFIELD))
+ {
+ if (sExecutionResult != FM_COL_CURRENCYFIELD)
+ bReplace = true;
+ aFieldType = FM_COL_CURRENCYFIELD;
+ }
+ else if (sExecutionResult.startsWith(FM_COL_PATTERNFIELD))
+ {
+ if (sExecutionResult != FM_COL_PATTERNFIELD)
+ bReplace = true;
+ aFieldType = FM_COL_PATTERNFIELD;
+ }
+ else if (sExecutionResult.startsWith(FM_COL_FORMATTEDFIELD))
+ {
+ if (sExecutionResult != FM_COL_FORMATTEDFIELD)
+ bReplace = true;
+ aFieldType = FM_COL_FORMATTEDFIELD;
+ }
+ else if (sExecutionResult == "more")
+ {
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractFmShowColsDialog> pDlg(pFact->CreateFmShowColsDialog(GetFrameWeld()));
+ pDlg->SetColumns(xCols);
+ pDlg->Execute();
+ }
+ else if (sExecutionResult == "all")
+ {
+ // just iterate through all the cols ...
+ Reference< css::beans::XPropertySet > xCurCol;
+ for (sal_Int32 i=0; i<xCols->getCount(); ++i)
+ {
+ xCurCol.set(xCols->getByIndex(i), css::uno::UNO_QUERY);
+ xCurCol->setPropertyValue(FM_PROP_HIDDEN, makeAny(false));
+ }
+ // TODO : there must be a more clever way to do this...
+ // with the above the view is updated after every single model update ...
+ }
+ else if (nExecutionResult>0 && nExecutionResult<=16)
+ { // it was a "show column/<colname>" command (there are at most 16 such items)
+ // search the nExecutionResult'th hidden col
+ Reference< css::beans::XPropertySet > xCurCol;
+ for (sal_Int32 i=0; i<xCols->getCount() && nExecutionResult; ++i)
+ {
+ xCurCol.set(xCols->getByIndex(i), css::uno::UNO_QUERY);
+ Any aHidden = xCurCol->getPropertyValue(FM_PROP_HIDDEN);
+ if (::comphelper::getBOOL(aHidden))
+ if (!--nExecutionResult)
+ {
+ xCurCol->setPropertyValue(FM_PROP_HIDDEN, makeAny(false));
+ break;
+ }
+ }
+ }
+
+ if ( !aFieldType.isEmpty() )
+ {
+ try
+ {
+ Reference< XGridColumnFactory > xFactory( xCols, UNO_QUERY_THROW );
+ Reference< XPropertySet > xNewCol( xFactory->createColumn( aFieldType ), UNO_SET_THROW );
+
+ if ( bReplace )
+ {
+ // rescue over a few properties
+ Reference< XPropertySet > xReplaced( xCols->getByIndex( nPos ), UNO_QUERY );
+
+ TransferFormComponentProperties(
+ xReplaced, xNewCol, Application::GetSettings().GetUILanguageTag().getLocale() );
+
+ xCols->replaceByIndex( nPos, makeAny( xNewCol ) );
+ ::comphelper::disposeComponent( xReplaced );
+
+ eInspectorAction = eUpdateInspector;
+ }
+ else
+ {
+ FormControlFactory factory;
+
+ OUString sLabel = FormControlFactory::getDefaultUniqueName_ByComponentType(
+ Reference< XNameAccess >( xCols, UNO_QUERY_THROW ), xNewCol );
+ xNewCol->setPropertyValue( FM_PROP_LABEL, makeAny( sLabel ) );
+ xNewCol->setPropertyValue( FM_PROP_NAME, makeAny( sLabel ) );
+
+ factory.initializeControlModel( DocumentClassification::classifyHostDocument( xCols ), xNewCol );
+
+ xCols->insertByIndex( nPos, makeAny( xNewCol ) );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+ SfxViewFrame* pCurrentFrame = SfxViewFrame::Current();
+ OSL_ENSURE( pCurrentFrame, "FmGridHeader::PostExecuteColumnContextMenu: no view frame -> no bindings -> no property browser!" );
+ if ( pCurrentFrame )
+ {
+ if ( eInspectorAction == eUpdateInspector )
+ {
+ if ( !pCurrentFrame->HasChildWindow( SID_FM_SHOW_PROPERTIES ) )
+ eInspectorAction = eNone;
+ }
+
+ if ( eInspectorAction != eNone )
+ {
+ SfxBoolItem aShowItem( SID_FM_SHOW_PROPERTIES, eInspectorAction != eCloseInspector );
+
+ pCurrentFrame->GetBindings().GetDispatcher()->ExecuteList(
+ SID_FM_SHOW_PROPERTY_BROWSER, SfxCallMode::ASYNCHRON,
+ { &aShowItem });
+ }
+ }
+}
+
+void FmGridHeader::triggerColumnContextMenu( const ::Point& _rPreferredPos )
+{
+ // the affected col
+ sal_uInt16 nColId = GetItemId( _rPreferredPos );
+
+ // the menu
+ VclBuilder aBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "svx/ui/colsmenu.ui", "");
+ VclPtr<PopupMenu> aContextMenu(aBuilder.get_menu("menu"));
+
+ // let derivatives modify the menu
+ PreExecuteColumnContextMenu( nColId, *aContextMenu );
+ aContextMenu->RemoveDisabledEntries( true, true );
+
+ // execute the menu
+ sal_uInt16 nResult = aContextMenu->Execute( this, _rPreferredPos );
+
+ // let derivatives handle the result
+ PostExecuteColumnContextMenu( nColId, *aContextMenu, nResult );
+}
+
+void FmGridHeader::Command(const CommandEvent& rEvt)
+{
+ switch (rEvt.GetCommand())
+ {
+ case CommandEventId::ContextMenu:
+ {
+ if (!rEvt.IsMouseEvent())
+ return;
+
+ triggerColumnContextMenu( rEvt.GetMousePosPixel() );
+ }
+ break;
+ default:
+ EditBrowserHeader::Command(rEvt);
+ }
+}
+
+FmGridControl::FmGridControl(
+ const Reference< css::uno::XComponentContext >& _rxContext,
+ vcl::Window* pParent,
+ FmXGridPeer* _pPeer,
+ WinBits nBits)
+ :DbGridControl(_rxContext, pParent, nBits)
+ ,m_pPeer(_pPeer)
+ ,m_nCurrentSelectedColumn(-1)
+ ,m_nMarkedColumnId(BROWSER_INVALIDID)
+ ,m_bSelecting(false)
+ ,m_bInColumnMove(false)
+{
+ EnableInteractiveRowHeight( );
+}
+
+void FmGridControl::Command(const CommandEvent& _rEvt)
+{
+ if ( CommandEventId::ContextMenu == _rEvt.GetCommand() )
+ {
+ FmGridHeader* pMyHeader = static_cast< FmGridHeader* >( GetHeaderBar() );
+ if ( pMyHeader && !_rEvt.IsMouseEvent() )
+ { // context menu requested by keyboard
+ if ( 1 == GetSelectColumnCount() || IsDesignMode() )
+ {
+ sal_uInt16 nSelId = GetColumnId(
+ sal::static_int_cast< sal_uInt16 >( FirstSelectedColumn() ) );
+ ::tools::Rectangle aColRect( GetFieldRectPixel( 0, nSelId, false ) );
+
+ Point aRelativePos( pMyHeader->ScreenToOutputPixel( OutputToScreenPixel( aColRect.TopCenter() ) ) );
+ pMyHeader->triggerColumnContextMenu(aRelativePos);
+
+ // handled
+ return;
+ }
+ }
+ }
+
+ DbGridControl::Command( _rEvt );
+}
+
+// css::beans::XPropertyChangeListener
+void FmGridControl::propertyChange(const css::beans::PropertyChangeEvent& evt)
+{
+ if (evt.PropertyName == FM_PROP_ROWCOUNT)
+ {
+ // if we're not in the main thread call AdjustRows asynchronously
+ implAdjustInSolarThread(true);
+ return;
+ }
+
+ const DbGridRowRef& xRow = GetCurrentRow();
+ // no adjustment of the properties is carried out during positioning
+ Reference<XPropertySet> xSet(evt.Source,UNO_QUERY);
+ if (xRow.is() && (::cppu::any2bool(xSet->getPropertyValue(FM_PROP_ISNEW))|| CompareBookmark(getDataSource()->getBookmark(), xRow->GetBookmark())))
+ {
+ if (evt.PropertyName == FM_PROP_ISMODIFIED)
+ {
+ // modified or clean ?
+ GridRowStatus eStatus = ::comphelper::getBOOL(evt.NewValue) ? GridRowStatus::Modified : GridRowStatus::Clean;
+ if (eStatus != xRow->GetStatus())
+ {
+ xRow->SetStatus(eStatus);
+ SolarMutexGuard aGuard;
+ RowModified(GetCurrentPos());
+ }
+ }
+ }
+}
+
+void FmGridControl::SetDesignMode(bool bMode)
+{
+ bool bOldMode = IsDesignMode();
+ DbGridControl::SetDesignMode(bMode);
+ if (bOldMode != bMode)
+ {
+ if (!bMode)
+ {
+ // cancel selection
+ markColumn(USHRT_MAX);
+ }
+ else
+ {
+ Reference< css::container::XIndexContainer > xColumns(GetPeer()->getColumns());
+ Reference< css::view::XSelectionSupplier > xSelSupplier(xColumns, UNO_QUERY);
+ if (xSelSupplier.is())
+ {
+ Any aSelection = xSelSupplier->getSelection();
+ Reference< css::beans::XPropertySet > xColumn;
+ if (aSelection.getValueType().getTypeClass() == TypeClass_INTERFACE)
+ xColumn.set(aSelection, css::uno::UNO_QUERY);
+ Reference< XInterface > xCurrent;
+ for (sal_Int32 i=0; i<xColumns->getCount(); ++i)
+ {
+ xCurrent.set(xColumns->getByIndex(i), css::uno::UNO_QUERY);
+ if (xCurrent == xColumn)
+ {
+ markColumn(GetColumnIdFromModelPos(i));
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+void FmGridControl::DeleteSelectedRows()
+{
+ if (!m_pSeekCursor)
+ return;
+
+ // how many rows are selected?
+ sal_Int32 nSelectedRows = GetSelectRowCount();
+
+ // the current line should be deleted but it is currently in edit mode
+ if ( IsCurrentAppending() )
+ return;
+ // is the insert row selected
+ if (GetEmptyRow().is() && IsRowSelected(GetRowCount() - 1))
+ nSelectedRows -= 1;
+
+ // nothing to do
+ if (nSelectedRows <= 0)
+ return;
+
+ // try to confirm the delete
+ Reference< css::frame::XDispatchProvider > xDispatcher = static_cast<css::frame::XDispatchProvider*>(GetPeer());
+ if (xDispatcher.is())
+ {
+ css::util::URL aUrl;
+ aUrl.Complete = FMURL_CONFIRM_DELETION;
+ Reference< css::util::XURLTransformer > xTransformer(
+ css::util::URLTransformer::create(::comphelper::getProcessComponentContext()) );
+ xTransformer->parseStrict( aUrl );
+
+ Reference< css::frame::XDispatch > xDispatch = xDispatcher->queryDispatch(aUrl, OUString(), 0);
+ Reference< css::form::XConfirmDeleteListener > xConfirm(xDispatch, UNO_QUERY);
+ if (xConfirm.is())
+ {
+ css::sdb::RowChangeEvent aEvent;
+ aEvent.Source = Reference< XInterface >(*getDataSource());
+ aEvent.Rows = nSelectedRows;
+ aEvent.Action = css::sdb::RowChangeAction::DELETE;
+ if (!xConfirm->confirmDelete(aEvent))
+ return;
+ }
+ }
+
+ const MultiSelection* pRowSelection = GetSelection();
+ if ( pRowSelection && pRowSelection->IsAllSelected() )
+ {
+ BeginCursorAction();
+ CursorWrapper* pCursor = getDataSource();
+ Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*pCursor), UNO_QUERY);
+ try
+ {
+ pCursor->beforeFirst();
+ while( pCursor->next() )
+ xUpdateCursor->deleteRow();
+
+ SetUpdateMode(false);
+ SetNoSelection();
+
+ xUpdateCursor->moveToInsertRow();
+ }
+ catch(const Exception&)
+ {
+ OSL_FAIL("Exception caught while deleting rows!");
+ }
+ // adapt to the data cursor
+ AdjustDataSource(true);
+ EndCursorAction();
+ SetUpdateMode(true);
+ }
+ else
+ {
+ Reference< css::sdbcx::XDeleteRows > xDeleteThem(Reference< XInterface >(*getDataSource()), UNO_QUERY);
+
+ // collect the bookmarks of the selected rows
+ Sequence < Any> aBookmarks = getSelectionBookmarks();
+
+ // determine the next row to position after deletion
+ Any aBookmark;
+ bool bNewPos = false;
+ // if the current row isn't selected we take the row as row after deletion
+ OSL_ENSURE( GetCurrentRow().is(), "FmGridControl::DeleteSelectedRows: no current row here?" );
+ // crash reports suggest it can happen we don't have a current row - how?
+ // #154303# / 2008-04-23 / frank.schoenheit@sun.com
+ if ( !IsRowSelected( GetCurrentPos() ) && !IsCurrentAppending() && GetCurrentRow().is() )
+ {
+ aBookmark = GetCurrentRow()->GetBookmark();
+ bNewPos = true;
+ }
+ else
+ {
+ // we look for the first row after the selected block for selection
+ long nIdx = LastSelectedRow() + 1;
+ if (nIdx < GetRowCount() - 1)
+ {
+ // there is a next row to position on
+ if (SeekCursor(nIdx))
+ {
+ GetSeekRow()->SetState(m_pSeekCursor.get(), true);
+
+ bNewPos = true;
+ // if it's not the row for inserting we keep the bookmark
+ if (!IsInsertionRow(nIdx))
+ aBookmark = m_pSeekCursor->getBookmark();
+ }
+ }
+ else
+ {
+ // we look for the first row before the selected block for selection after deletion
+ nIdx = FirstSelectedRow() - 1;
+ if (nIdx >= 0 && SeekCursor(nIdx))
+ {
+ GetSeekRow()->SetState(m_pSeekCursor.get(), true);
+
+ bNewPos = true;
+ aBookmark = m_pSeekCursor->getBookmark();
+ }
+ }
+ }
+
+ // Are all rows selected?
+ // Second condition if no insertion line exists
+ bool bAllSelected = GetTotalCount() == nSelectedRows || GetRowCount() == nSelectedRows;
+
+ BeginCursorAction();
+
+ // now delete the row
+ Sequence<sal_Int32> aDeletedRows;
+ SetUpdateMode( false );
+ try
+ {
+ aDeletedRows = xDeleteThem->deleteRows(aBookmarks);
+ }
+ catch(SQLException&)
+ {
+ }
+ SetUpdateMode( true );
+
+ // how many rows are deleted?
+ sal_Int32 nDeletedRows = static_cast<sal_Int32>(std::count_if(aDeletedRows.begin(), aDeletedRows.end(),
+ [](const sal_Int32 nRow) { return nRow != 0; }));
+
+ // have rows been deleted?
+ if (nDeletedRows)
+ {
+ SetUpdateMode(false);
+ SetNoSelection();
+ try
+ {
+ // did we delete all the rows than try to move to the next possible row
+ if (nDeletedRows == aDeletedRows.getLength())
+ {
+ // there exists a new position to move on
+ if (bNewPos)
+ {
+ if (aBookmark.hasValue())
+ getDataSource()->moveToBookmark(aBookmark);
+ // no valid bookmark so move to the insert row
+ else
+ {
+ Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*m_pDataCursor), UNO_QUERY);
+ xUpdateCursor->moveToInsertRow();
+ }
+ }
+ else
+ {
+ Reference< css::beans::XPropertySet > xSet(Reference< XInterface >(*m_pDataCursor), UNO_QUERY);
+
+ sal_Int32 nRecordCount(0);
+ xSet->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount;
+ if ( m_pDataCursor->rowDeleted() )
+ --nRecordCount;
+
+ // there are no rows left and we have an insert row
+ if (!nRecordCount && GetEmptyRow().is())
+ {
+ Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*m_pDataCursor), UNO_QUERY);
+ xUpdateCursor->moveToInsertRow();
+ }
+ else if (nRecordCount)
+ // move to the first row
+ getDataSource()->first();
+ }
+ }
+ // not all the rows where deleted, so move to the first row which remained in the resultset
+ else
+ {
+ auto pRow = std::find(aDeletedRows.begin(), aDeletedRows.end(), 0);
+ if (pRow != aDeletedRows.end())
+ {
+ auto i = static_cast<sal_Int32>(std::distance(aDeletedRows.begin(), pRow));
+ getDataSource()->moveToBookmark(aBookmarks[i]);
+ }
+ }
+ }
+ catch(const Exception&)
+ {
+ try
+ {
+ // positioning went wrong so try to move to the first row
+ getDataSource()->first();
+ }
+ catch(const Exception&)
+ {
+ }
+ }
+
+ // adapt to the data cursor
+ AdjustDataSource(true);
+
+ // not all rows could be deleted;
+ // never select again there the ones that could not be deleted
+ if (nDeletedRows < nSelectedRows)
+ {
+ // were all selected
+ if (bAllSelected)
+ {
+ SelectAll();
+ if (IsInsertionRow(GetRowCount() - 1)) // not the insertion row
+ SelectRow(GetRowCount() - 1, false);
+ }
+ else
+ {
+ // select the remaining rows
+ for (const sal_Int32 nSuccess : aDeletedRows)
+ {
+ try
+ {
+ if (!nSuccess)
+ {
+ m_pSeekCursor->moveToBookmark(m_pDataCursor->getBookmark());
+ SetSeekPos(m_pSeekCursor->getRow() - 1);
+ SelectRow(GetSeekPos());
+ }
+ }
+ catch(const Exception&)
+ {
+ // keep the seekpos in all cases
+ SetSeekPos(m_pSeekCursor->getRow() - 1);
+ }
+ }
+ }
+ }
+
+ EndCursorAction();
+ SetUpdateMode(true);
+ }
+ else // row could not be deleted
+ {
+ EndCursorAction();
+ try
+ {
+ // currentrow is the insert row?
+ if (!IsCurrentAppending())
+ getDataSource()->refreshRow();
+ }
+ catch(const Exception&)
+ {
+ }
+ }
+ }
+
+ // if there is no selection anymore we can start editing
+ if (!GetSelectRowCount())
+ ActivateCell();
+}
+
+// XCurrentRecordListener
+void FmGridControl::positioned()
+{
+ SAL_INFO("svx.fmcomp", "FmGridControl::positioned");
+ // position on the data source (force it to be done in the main thread)
+ implAdjustInSolarThread(false);
+}
+
+bool FmGridControl::commit()
+{
+ // execute commit only if an update is not already executed by the
+ // css::form::component::GridControl
+ if (!IsUpdating())
+ {
+ if (Controller().is() && Controller()->IsModified())
+ {
+ if (!SaveModified())
+ return false;
+ }
+ }
+ return true;
+}
+
+void FmGridControl::inserted()
+{
+ const DbGridRowRef& xRow = GetCurrentRow();
+ if (!xRow.is())
+ return;
+
+ // line has been inserted, then reset the status and mode
+ xRow->SetState(m_pDataCursor.get(), false);
+ xRow->SetNew(false);
+
+}
+
+VclPtr<BrowserHeader> FmGridControl::imp_CreateHeaderBar(BrowseBox* pParent)
+{
+ DBG_ASSERT( pParent == this, "FmGridControl::imp_CreateHeaderBar: parent?" );
+ return VclPtr<FmGridHeader>::Create( pParent );
+}
+
+void FmGridControl::markColumn(sal_uInt16 nId)
+{
+ if (GetHeaderBar() && m_nMarkedColumnId != nId)
+ {
+ // deselect
+ if (m_nMarkedColumnId != BROWSER_INVALIDID)
+ {
+ HeaderBarItemBits aBits = GetHeaderBar()->GetItemBits(m_nMarkedColumnId) & ~HeaderBarItemBits::FLAT;
+ GetHeaderBar()->SetItemBits(m_nMarkedColumnId, aBits);
+ }
+
+
+ if (nId != BROWSER_INVALIDID)
+ {
+ HeaderBarItemBits aBits = GetHeaderBar()->GetItemBits(nId) | HeaderBarItemBits::FLAT;
+ GetHeaderBar()->SetItemBits(nId, aBits);
+ }
+ m_nMarkedColumnId = nId;
+ }
+}
+
+bool FmGridControl::isColumnMarked(sal_uInt16 nId) const
+{
+ return m_nMarkedColumnId == nId;
+}
+
+long FmGridControl::QueryMinimumRowHeight()
+{
+ long const nMinimalLogicHeight = 20; // 0.2 cm
+ long nMinimalPixelHeight = LogicToPixel(Point(0, nMinimalLogicHeight), MapMode(MapUnit::Map10thMM)).Y();
+ return CalcZoom( nMinimalPixelHeight );
+}
+
+void FmGridControl::RowHeightChanged()
+{
+ DbGridControl::RowHeightChanged();
+
+ Reference< XPropertySet > xModel( GetPeer()->getColumns(), UNO_QUERY );
+ DBG_ASSERT( xModel.is(), "FmGridControl::RowHeightChanged: no model!" );
+ if ( xModel.is() )
+ {
+ try
+ {
+ sal_Int32 nUnzoomedPixelHeight = CalcReverseZoom( GetDataRowHeight() );
+ Any aProperty = makeAny( static_cast<sal_Int32>(PixelToLogic( Point(0, nUnzoomedPixelHeight), MapMode(MapUnit::Map10thMM)).Y()) );
+ xModel->setPropertyValue( FM_PROP_ROWHEIGHT, aProperty );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "FmGridControl::RowHeightChanged" );
+ }
+ }
+}
+
+void FmGridControl::ColumnResized(sal_uInt16 nId)
+{
+ DbGridControl::ColumnResized(nId);
+
+ // transfer value to the model
+ DbGridColumn* pCol = DbGridControl::GetColumns()[ GetModelColumnPos(nId) ].get();
+ const Reference< css::beans::XPropertySet >& xColModel(pCol->getModel());
+ if (xColModel.is())
+ {
+ Any aWidth;
+ sal_Int32 nColumnWidth = GetColumnWidth(nId);
+ nColumnWidth = CalcReverseZoom(nColumnWidth);
+ // convert to 10THMM
+ aWidth <<= static_cast<sal_Int32>(PixelToLogic(Point(nColumnWidth, 0), MapMode(MapUnit::Map10thMM)).X());
+ xColModel->setPropertyValue(FM_PROP_WIDTH, aWidth);
+ }
+}
+
+void FmGridControl::CellModified()
+{
+ DbGridControl::CellModified();
+ GetPeer()->CellModified();
+}
+
+void FmGridControl::BeginCursorAction()
+{
+ DbGridControl::BeginCursorAction();
+ m_pPeer->stopCursorListening();
+}
+
+void FmGridControl::EndCursorAction()
+{
+ m_pPeer->startCursorListening();
+ DbGridControl::EndCursorAction();
+}
+
+void FmGridControl::ColumnMoved(sal_uInt16 nId)
+{
+ m_bInColumnMove = true;
+
+ DbGridControl::ColumnMoved(nId);
+ Reference< css::container::XIndexContainer > xColumns(GetPeer()->getColumns());
+
+ if (xColumns.is())
+ {
+ // locate the column and move in the model;
+ // get ColumnPos
+ DbGridColumn* pCol = DbGridControl::GetColumns()[ GetModelColumnPos(nId) ].get();
+ Reference< css::beans::XPropertySet > xCol;
+
+ // inserting must be based on the column positions
+ sal_Int32 i;
+ Reference< XInterface > xCurrent;
+ for (i = 0; !xCol.is() && i < xColumns->getCount(); i++)
+ {
+ xCurrent.set(xColumns->getByIndex(i), css::uno::UNO_QUERY);
+ if (xCurrent == pCol->getModel())
+ {
+ xCol = pCol->getModel();
+ break;
+ }
+ }
+
+ DBG_ASSERT(i < xColumns->getCount(), "Wrong css::sdbcx::Index");
+ xColumns->removeByIndex(i);
+ Any aElement;
+ aElement <<= xCol;
+ xColumns->insertByIndex(GetModelColumnPos(nId), aElement);
+ pCol->setModel(xCol);
+ // if the column which is shown here is selected ...
+ if ( isColumnSelected(pCol) )
+ markColumn(nId); // ... -> mark it
+ }
+
+ m_bInColumnMove = false;
+}
+
+void FmGridControl::InitColumnsByModels(const Reference< css::container::XIndexContainer >& xColumns)
+{
+ // reset columns;
+ // if there is only one HandleColumn, then don't
+ if (GetModelColCount())
+ {
+ RemoveColumns();
+ InsertHandleColumn();
+ }
+
+ if (!xColumns.is())
+ return;
+
+ SetUpdateMode(false);
+
+ // inserting must be based on the column positions
+ sal_Int32 i;
+ Any aWidth;
+ for (i = 0; i < xColumns->getCount(); ++i)
+ {
+ Reference< css::beans::XPropertySet > xCol(
+ xColumns->getByIndex(i), css::uno::UNO_QUERY);
+
+ OUString aName(
+ comphelper::getString(xCol->getPropertyValue(FM_PROP_LABEL)));
+
+ aWidth = xCol->getPropertyValue(FM_PROP_WIDTH);
+ sal_Int32 nWidth = 0;
+ if (aWidth >>= nWidth)
+ nWidth = LogicToPixel(Point(nWidth, 0), MapMode(MapUnit::Map10thMM)).X();
+
+ AppendColumn(aName, static_cast<sal_uInt16>(nWidth));
+ DbGridColumn* pCol = DbGridControl::GetColumns()[ i ].get();
+ pCol->setModel(xCol);
+ }
+
+ // and now remove the hidden columns as well
+ // (we did not already make it in the upper loop, since we would then have gotten
+ // problems with the IDs of the columns: AppendColumn allocates them automatically,
+ // but the column _after_ a hidden one needs an ID increased by one ...)
+ Any aHidden;
+ for (i = 0; i < xColumns->getCount(); ++i)
+ {
+ Reference< css::beans::XPropertySet > xCol( xColumns->getByIndex(i), css::uno::UNO_QUERY);
+ aHidden = xCol->getPropertyValue(FM_PROP_HIDDEN);
+ if (::comphelper::getBOOL(aHidden))
+ HideColumn(GetColumnIdFromModelPos(static_cast<sal_uInt16>(i)));
+ }
+
+ SetUpdateMode(true);
+}
+
+void FmGridControl::InitColumnByField(
+ DbGridColumn* _pColumn, const Reference< XPropertySet >& _rxColumnModel,
+ const Reference< XNameAccess >& _rxFieldsByNames, const Reference< XIndexAccess >& _rxFieldsByIndex )
+{
+ DBG_ASSERT( _rxFieldsByNames == _rxFieldsByIndex, "FmGridControl::InitColumnByField: invalid container interfaces!" );
+
+ // lookup the column which belongs to the control source
+ OUString sFieldName;
+ _rxColumnModel->getPropertyValue( FM_PROP_CONTROLSOURCE ) >>= sFieldName;
+ Reference< XPropertySet > xField;
+ _rxColumnModel->getPropertyValue( FM_PROP_BOUNDFIELD ) >>= xField;
+
+
+ if ( !xField.is() && /*sFieldName.getLength() && */_rxFieldsByNames->hasByName( sFieldName ) ) // #i93452# do not check for name length
+ _rxFieldsByNames->getByName( sFieldName ) >>= xField;
+
+ // determine the position of this column
+ sal_Int32 nFieldPos = -1;
+ if ( xField.is() )
+ {
+ Reference< XPropertySet > xCheck;
+ sal_Int32 nFieldCount = _rxFieldsByIndex->getCount();
+ for ( sal_Int32 i = 0; i < nFieldCount; ++i)
+ {
+ _rxFieldsByIndex->getByIndex( i ) >>= xCheck;
+ if ( xField.get() == xCheck.get() )
+ {
+ nFieldPos = i;
+ break;
+ }
+ }
+ }
+
+ if ( xField.is() && ( nFieldPos >= 0 ) )
+ {
+ // some data types are not allowed
+ sal_Int32 nDataType = DataType::OTHER;
+ xField->getPropertyValue( FM_PROP_FIELDTYPE ) >>= nDataType;
+
+ bool bIllegalType = false;
+ switch ( nDataType )
+ {
+ case DataType::BLOB:
+ case DataType::LONGVARBINARY:
+ case DataType::BINARY:
+ case DataType::VARBINARY:
+ case DataType::OTHER:
+ bIllegalType = true;
+ break;
+ }
+
+ if ( bIllegalType )
+ {
+ _pColumn->SetObject( static_cast<sal_Int16>(nFieldPos) );
+ return;
+ }
+ }
+
+ // the control type is determined by the ColumnServiceName
+ static const char s_sPropColumnServiceName[] = "ColumnServiceName";
+ if ( !::comphelper::hasProperty( s_sPropColumnServiceName, _rxColumnModel ) )
+ return;
+
+ _pColumn->setModel( _rxColumnModel );
+
+ OUString sColumnServiceName;
+ _rxColumnModel->getPropertyValue( s_sPropColumnServiceName ) >>= sColumnServiceName;
+
+ sal_Int32 nTypeId = getColumnTypeByModelName( sColumnServiceName );
+ _pColumn->CreateControl( nFieldPos, xField, nTypeId );
+}
+
+void FmGridControl::InitColumnsByFields(const Reference< css::container::XIndexAccess >& _rxFields)
+{
+ if ( !_rxFields.is() )
+ return;
+
+ // initialize columns
+ Reference< XIndexContainer > xColumns( GetPeer()->getColumns() );
+ Reference< XNameAccess > xFieldsAsNames( _rxFields, UNO_QUERY );
+
+ // inserting must be based on the column positions
+ for (sal_Int32 i = 0; i < xColumns->getCount(); i++)
+ {
+ DbGridColumn* pCol = GetColumns()[ i ].get();
+ OSL_ENSURE(pCol,"No grid column!");
+ if ( pCol )
+ {
+ Reference< XPropertySet > xColumnModel(
+ xColumns->getByIndex( i ), css::uno::UNO_QUERY);
+
+ InitColumnByField( pCol, xColumnModel, xFieldsAsNames, _rxFields );
+ }
+ }
+}
+
+void FmGridControl::HideColumn(sal_uInt16 nId)
+{
+ DbGridControl::HideColumn(nId);
+
+ sal_uInt16 nPos = GetModelColumnPos(nId);
+ if (nPos == sal_uInt16(-1))
+ return;
+
+ DbGridColumn* pColumn = GetColumns()[ nPos ].get();
+ if (pColumn->IsHidden())
+ GetPeer()->columnHidden(pColumn);
+
+ if (nId == m_nMarkedColumnId)
+ m_nMarkedColumnId = sal_uInt16(-1);
+}
+
+bool FmGridControl::isColumnSelected(DbGridColumn const * _pColumn)
+{
+ OSL_ENSURE(_pColumn,"Column can not be null!");
+ bool bSelected = false;
+ // if the column which is shown here is selected ...
+ Reference< css::view::XSelectionSupplier > xSelSupplier(GetPeer()->getColumns(), UNO_QUERY);
+ if ( xSelSupplier.is() )
+ {
+ Reference< css::beans::XPropertySet > xColumn;
+ xSelSupplier->getSelection() >>= xColumn;
+ bSelected = (xColumn.get() == _pColumn->getModel().get());
+ }
+ return bSelected;
+}
+
+void FmGridControl::ShowColumn(sal_uInt16 nId)
+{
+ DbGridControl::ShowColumn(nId);
+
+ sal_uInt16 nPos = GetModelColumnPos(nId);
+ if (nPos == sal_uInt16(-1))
+ return;
+
+ DbGridColumn* pColumn = GetColumns()[ nPos ].get();
+ if (!pColumn->IsHidden())
+ GetPeer()->columnVisible(pColumn);
+
+ // if the column which is shown here is selected ...
+ if ( isColumnSelected(pColumn) )
+ markColumn(nId); // ... -> mark it
+}
+
+bool FmGridControl::selectBookmarks(const Sequence< Any >& _rBookmarks)
+{
+ SolarMutexGuard aGuard;
+ // need to lock the SolarMutex so that no paint call disturbs us ...
+
+ if ( !m_pSeekCursor )
+ {
+ OSL_FAIL( "FmGridControl::selectBookmarks: no seek cursor!" );
+ return false;
+ }
+
+ SetNoSelection();
+
+ bool bAllSuccessfull = true;
+ try
+ {
+ for (const Any& rBookmark : _rBookmarks)
+ {
+ // move the seek cursor to the row given
+ if (m_pSeekCursor->moveToBookmark(rBookmark))
+ SelectRow( m_pSeekCursor->getRow() - 1);
+ else
+ bAllSuccessfull = false;
+ }
+ }
+ catch(Exception&)
+ {
+ OSL_FAIL("FmGridControl::selectBookmarks: could not move to one of the bookmarks!");
+ return false;
+ }
+
+ return bAllSuccessfull;
+}
+
+Sequence< Any> FmGridControl::getSelectionBookmarks()
+{
+ // lock our update so no paint-triggered seeks interfere ...
+ SetUpdateMode(false);
+
+ sal_Int32 nSelectedRows = GetSelectRowCount(), i = 0;
+ Sequence< Any> aBookmarks(nSelectedRows);
+ if ( nSelectedRows )
+ {
+ Any* pBookmarks = aBookmarks.getArray();
+
+ // (I'm not sure if the problem isn't deeper: The scenario: a large table displayed by a grid with a
+ // thread-safe cursor (dBase). On loading the sdb-cursor started a counting thread. While this counting progress
+ // was running, I tried do delete 3 records from within the grid. Deletion caused a SeekCursor, which made a
+ // m_pSeekCursor->moveRelative and a m_pSeekCursor->getPosition.
+ // Unfortunately the first call caused a propertyChanged(RECORDCOUNT) which resulted in a repaint of the
+ // navigation bar and the grid. The latter itself will result in SeekRow calls. So after (successfully) returning
+ // from the moveRelative the getPosition returns an invalid value. And so the SeekCursor fails.
+ // In the consequence ALL parts of code where two calls to the seek cursor are done, while the second call _relies_ on
+ // the first one, should be secured against recursion, with a broad-minded interpretation of "recursion": if any of these
+ // code parts is executed, no other should be accessible. But this sounds very difficult to achieve...
+ // )
+
+ // The next problem caused by the same behavior (SeekCursor causes a propertyChanged): when adjusting rows we implicitly
+ // change our selection. So a "FirstSelected(); SeekCursor(); NextSelected();" may produce unpredictable results.
+ // That's why we _first_ collect the indices of the selected rows and _then_ their bookmarks.
+ long nIdx = FirstSelectedRow();
+ while (nIdx != BROWSER_ENDOFSELECTION)
+ {
+ // (we misuse the bookmarks array for this ...)
+ pBookmarks[i++] <<= static_cast<sal_Int32>(nIdx);
+ nIdx = NextSelectedRow();
+ }
+ DBG_ASSERT(i == nSelectedRows, "FmGridControl::DeleteSelectedRows : could not collect the row indices !");
+
+ for (i=0; i<nSelectedRows; ++i)
+ {
+ nIdx = ::comphelper::getINT32(pBookmarks[i]);
+ if (IsInsertionRow(nIdx))
+ {
+ // do not delete empty row
+ aBookmarks.realloc(--nSelectedRows);
+ SelectRow(nIdx, false); // cancel selection for empty row
+ break;
+ }
+
+ // first, position the data cursor on the selected block
+ if (SeekCursor(nIdx))
+ {
+ GetSeekRow()->SetState(m_pSeekCursor.get(), true);
+
+ pBookmarks[i] = m_pSeekCursor->getBookmark();
+ }
+ #ifdef DBG_UTIL
+ else
+ OSL_FAIL("FmGridControl::DeleteSelectedRows : a bookmark could not be determined !");
+ #endif
+ }
+ }
+ SetUpdateMode(true);
+
+ // if one of the SeekCursor-calls failed...
+ aBookmarks.realloc(i);
+
+ // (the alternative : while collecting the bookmarks lock our propertyChanged, this should resolve both our problems.
+ // but this would be incompatible as we need a locking flag, then...)
+
+ return aBookmarks;
+}
+
+namespace
+{
+ OUString getColumnPropertyFromPeer(FmXGridPeer* _pPeer,sal_Int32 _nPosition,const OUString& _sPropName)
+ {
+ OUString sRetText;
+ if ( _pPeer && _nPosition != -1)
+ {
+ Reference<XIndexContainer> xIndex = _pPeer->getColumns();
+ if ( xIndex.is() && xIndex->getCount() > _nPosition )
+ {
+ Reference<XPropertySet> xProp;
+ xIndex->getByIndex( _nPosition ) >>= xProp;
+ if ( xProp.is() )
+ {
+ try {
+ xProp->getPropertyValue( _sPropName ) >>= sRetText;
+ } catch (UnknownPropertyException const&) {
+ TOOLS_WARN_EXCEPTION("svx.fmcomp", "");
+ }
+ }
+ }
+ }
+ return sRetText;
+ }
+}
+
+// Object data and state
+OUString FmGridControl::GetAccessibleObjectName( ::vcl::AccessibleBrowseBoxObjType _eObjType,sal_Int32 _nPosition ) const
+{
+ OUString sRetText;
+ switch( _eObjType )
+ {
+ case ::vcl::BBTYPE_BROWSEBOX:
+ if ( GetPeer() )
+ {
+ Reference<XPropertySet> xProp(GetPeer()->getColumns(),UNO_QUERY);
+ if ( xProp.is() )
+ xProp->getPropertyValue(FM_PROP_NAME) >>= sRetText;
+ }
+ break;
+ case ::vcl::BBTYPE_COLUMNHEADERCELL:
+ sRetText = getColumnPropertyFromPeer(
+ GetPeer(),
+ GetModelColumnPos(
+ sal::static_int_cast< sal_uInt16 >(_nPosition)),
+ FM_PROP_LABEL);
+ break;
+ default:
+ sRetText = DbGridControl::GetAccessibleObjectName(_eObjType,_nPosition);
+ }
+ return sRetText;
+}
+
+OUString FmGridControl::GetAccessibleObjectDescription( ::vcl::AccessibleBrowseBoxObjType _eObjType,sal_Int32 _nPosition ) const
+{
+ OUString sRetText;
+ switch( _eObjType )
+ {
+ case ::vcl::BBTYPE_BROWSEBOX:
+ if ( GetPeer() )
+ {
+ Reference<XPropertySet> xProp(GetPeer()->getColumns(),UNO_QUERY);
+ if ( xProp.is() )
+ {
+ xProp->getPropertyValue(FM_PROP_HELPTEXT) >>= sRetText;
+ if ( sRetText.isEmpty() )
+ xProp->getPropertyValue(FM_PROP_DESCRIPTION) >>= sRetText;
+ }
+ }
+ break;
+ case ::vcl::BBTYPE_COLUMNHEADERCELL:
+ sRetText = getColumnPropertyFromPeer(
+ GetPeer(),
+ GetModelColumnPos(
+ sal::static_int_cast< sal_uInt16 >(_nPosition)),
+ FM_PROP_HELPTEXT);
+ if ( sRetText.isEmpty() )
+ sRetText = getColumnPropertyFromPeer(
+ GetPeer(),
+ GetModelColumnPos(
+ sal::static_int_cast< sal_uInt16 >(_nPosition)),
+ FM_PROP_DESCRIPTION);
+
+ break;
+ default:
+ sRetText = DbGridControl::GetAccessibleObjectDescription(_eObjType,_nPosition);
+ }
+ return sRetText;
+}
+
+void FmGridControl::Select()
+{
+ DbGridControl::Select();
+ // ... does it affect our columns?
+ const MultiSelection* pColumnSelection = GetColumnSelection();
+
+ sal_uInt16 nSelectedColumn =
+ pColumnSelection && pColumnSelection->GetSelectCount()
+ ? sal::static_int_cast< sal_uInt16 >(
+ const_cast<MultiSelection*>(pColumnSelection)->FirstSelected())
+ : SAL_MAX_UINT16;
+ // the HandleColumn is not selected
+ switch (nSelectedColumn)
+ {
+ case SAL_MAX_UINT16: break; // no selection
+ case 0 : nSelectedColumn = SAL_MAX_UINT16; break;
+ // handle col can't be selected
+ default :
+ // get the model col pos instead of the view col pos
+ nSelectedColumn = GetModelColumnPos(GetColumnIdFromViewPos(nSelectedColumn - 1));
+ break;
+ }
+
+ if (nSelectedColumn != m_nCurrentSelectedColumn)
+ {
+ // BEFORE calling the select at the SelectionSupplier!
+ m_nCurrentSelectedColumn = nSelectedColumn;
+
+ if (!m_bSelecting)
+ {
+ m_bSelecting = true;
+
+ try
+ {
+ Reference< XIndexAccess > xColumns = GetPeer()->getColumns();
+ Reference< XSelectionSupplier > xSelSupplier(xColumns, UNO_QUERY);
+ if (xSelSupplier.is())
+ {
+ if (nSelectedColumn != SAL_MAX_UINT16)
+ {
+ Reference< XPropertySet > xColumn(
+ xColumns->getByIndex(nSelectedColumn),
+ css::uno::UNO_QUERY);
+ xSelSupplier->select(makeAny(xColumn));
+ }
+ else
+ {
+ xSelSupplier->select(Any());
+ }
+ }
+ }
+ catch(Exception&)
+ {
+ }
+
+
+ m_bSelecting = false;
+ }
+ }
+}
+
+
+void FmGridControl::KeyInput( const KeyEvent& rKEvt )
+{
+ bool bDone = false;
+ const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
+ if ( IsDesignMode()
+ && !rKeyCode.IsShift()
+ && !rKeyCode.IsMod1()
+ && !rKeyCode.IsMod2()
+ && GetParent() )
+ {
+ switch ( rKeyCode.GetCode() )
+ {
+ case KEY_ESCAPE:
+ GetParent()->GrabFocus();
+ bDone = true;
+ break;
+ case KEY_DELETE:
+ if ( GetSelectColumnCount() && GetPeer() && m_nCurrentSelectedColumn >= 0 )
+ {
+ Reference< css::container::XIndexContainer > xCols(GetPeer()->getColumns());
+ if ( xCols.is() )
+ {
+ try
+ {
+ if ( m_nCurrentSelectedColumn < xCols->getCount() )
+ {
+ Reference< XInterface > xCol;
+ xCols->getByIndex(m_nCurrentSelectedColumn) >>= xCol;
+ xCols->removeByIndex(m_nCurrentSelectedColumn);
+ ::comphelper::disposeComponent(xCol);
+ }
+ }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx", "exception occurred while deleting a column");
+ }
+ }
+ }
+ bDone = true;
+ break;
+ }
+ }
+ if ( !bDone )
+ DbGridControl::KeyInput( rKEvt );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/fmcomp/fmgridif.cxx b/svx/source/fmcomp/fmgridif.cxx
new file mode 100644
index 000000000..400e28e8b
--- /dev/null
+++ b/svx/source/fmcomp/fmgridif.cxx
@@ -0,0 +1,2764 @@
+/* -*- 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/fmgridif.hxx>
+#include <fmprop.hxx>
+#include <fmservs.hxx>
+#include <svx/fmtools.hxx>
+#include <fmurl.hxx>
+#include <formcontrolfactory.hxx>
+#include <gridcell.hxx>
+#include <sdbdatacolumn.hxx>
+#include <svx/fmgridcl.hxx>
+#include <tools/urlobj.hxx>
+
+#include <com/sun/star/awt/PosSize.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/form/XFormComponent.hpp>
+#include <com/sun/star/form/XLoadable.hpp>
+#include <com/sun/star/form/XReset.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/lang/NoSupportException.hpp>
+#include <com/sun/star/sdbc/ResultSetType.hpp>
+#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+#include <com/sun/star/sdbcx/XRowLocate.hpp>
+
+#include <comphelper/enumhelper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/types.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <vcl/unohelp.hxx>
+#include <vcl/svapp.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <sal/macros.h>
+
+using namespace ::svxform;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::view;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star;
+
+using ::com::sun::star::sdbcx::XColumnsSupplier;
+using ::com::sun::star::frame::XDispatchProviderInterceptor;
+using ::com::sun::star::frame::XDispatchProvider;
+using ::com::sun::star::accessibility::XAccessible;
+using ::com::sun::star::accessibility::XAccessibleContext;
+using ::com::sun::star::sdb::XRowSetSupplier;
+using ::com::sun::star::awt::XVclWindowPeer;
+
+
+static css::awt::FontDescriptor ImplCreateFontDescriptor( const vcl::Font& rFont )
+{
+ css::awt::FontDescriptor aFD;
+ aFD.Name = rFont.GetFamilyName();
+ aFD.StyleName = rFont.GetStyleName();
+ aFD.Height = static_cast<sal_Int16>(rFont.GetFontSize().Height());
+ aFD.Width = static_cast<sal_Int16>(rFont.GetFontSize().Width());
+ aFD.Family = static_cast<sal_Int16>(rFont.GetFamilyType());
+ aFD.CharSet = rFont.GetCharSet();
+ aFD.Pitch = static_cast<sal_Int16>(rFont.GetPitch());
+ aFD.CharacterWidth = vcl::unohelper::ConvertFontWidth( rFont.GetWidthType() );
+ aFD.Weight= vcl::unohelper::ConvertFontWeight( rFont.GetWeight() );
+ aFD.Slant = vcl::unohelper::ConvertFontSlant( rFont.GetItalic() );
+ aFD.Underline = static_cast<sal_Int16>(rFont.GetUnderline());
+ aFD.Strikeout = static_cast<sal_Int16>(rFont.GetStrikeout());
+ aFD.Orientation = rFont.GetOrientation();
+ aFD.Kerning = rFont.IsKerning();
+ aFD.WordLineMode = rFont.IsWordLineMode();
+ aFD.Type = 0; // ??? => only to metric...
+ return aFD;
+}
+
+
+static vcl::Font ImplCreateFont( const css::awt::FontDescriptor& rDescr )
+{
+ vcl::Font aFont;
+ aFont.SetFamilyName( rDescr.Name );
+ aFont.SetStyleName( rDescr.StyleName );
+ aFont.SetFontSize( ::Size( rDescr.Width, rDescr.Height ) );
+ aFont.SetFamily( static_cast<FontFamily>(rDescr.Family) );
+ aFont.SetCharSet( static_cast<rtl_TextEncoding>(rDescr.CharSet) );
+ aFont.SetPitch( static_cast<FontPitch>(rDescr.Pitch) );
+ aFont.SetWidthType( vcl::unohelper::ConvertFontWidth( rDescr.CharacterWidth ) );
+ aFont.SetWeight( vcl::unohelper::ConvertFontWeight( rDescr.Weight ) );
+ aFont.SetItalic( static_cast<FontItalic>(rDescr.Slant) );
+ aFont.SetUnderline( static_cast<::FontLineStyle>(rDescr.Underline) );
+ aFont.SetStrikeout( static_cast<::FontStrikeout>(rDescr.Strikeout) );
+ aFont.SetOrientation( static_cast<sal_Int16>(rDescr.Orientation) );
+ aFont.SetKerning( static_cast<FontKerning>(rDescr.Kerning) );
+ aFont.SetWordLineMode( rDescr.WordLineMode );
+ return aFont;
+}
+
+FmXModifyMultiplexer::FmXModifyMultiplexer( ::cppu::OWeakObject& rSource, ::osl::Mutex& _rMutex )
+ :OWeakSubObject( rSource )
+ ,OInterfaceContainerHelper2( _rMutex )
+{
+}
+
+
+Any SAL_CALL FmXModifyMultiplexer::queryInterface(const Type& _rType)
+{
+ Any aReturn = ::cppu::queryInterface(_rType,
+ static_cast< css::util::XModifyListener*>(this),
+ static_cast< XEventListener*>(this)
+ );
+
+ if (!aReturn.hasValue())
+ aReturn = OWeakSubObject::queryInterface( _rType );
+
+ return aReturn;
+}
+
+
+void FmXModifyMultiplexer::disposing(const EventObject& )
+{
+}
+
+
+void FmXModifyMultiplexer::modified(const EventObject& e)
+{
+ EventObject aMulti( e);
+ aMulti.Source = &m_rParent;
+ notifyEach( &XModifyListener::modified, aMulti );
+}
+
+FmXUpdateMultiplexer::FmXUpdateMultiplexer( ::cppu::OWeakObject& rSource, ::osl::Mutex& _rMutex )
+ :OWeakSubObject( rSource )
+ ,OInterfaceContainerHelper2( _rMutex )
+{
+}
+
+
+Any SAL_CALL FmXUpdateMultiplexer::queryInterface(const Type& _rType)
+{
+ Any aReturn = ::cppu::queryInterface(_rType,
+ static_cast< XUpdateListener*>(this),
+ static_cast< XEventListener*>(this)
+ );
+
+ if (!aReturn.hasValue())
+ aReturn = OWeakSubObject::queryInterface( _rType );
+
+ return aReturn;
+}
+
+
+void FmXUpdateMultiplexer::disposing(const EventObject& )
+{
+}
+
+
+sal_Bool FmXUpdateMultiplexer::approveUpdate(const EventObject &e)
+{
+ EventObject aMulti( e );
+ aMulti.Source = &m_rParent;
+
+ bool bResult = true;
+ if (getLength())
+ {
+ ::comphelper::OInterfaceIteratorHelper2 aIter(*this);
+ while ( bResult && aIter.hasMoreElements() )
+ bResult = static_cast< XUpdateListener* >( aIter.next() )->approveUpdate( aMulti );
+ }
+
+ return bResult;
+}
+
+
+void FmXUpdateMultiplexer::updated(const EventObject &e)
+{
+ EventObject aMulti( e );
+ aMulti.Source = &m_rParent;
+ notifyEach( &XUpdateListener::updated, aMulti );
+}
+
+FmXSelectionMultiplexer::FmXSelectionMultiplexer( ::cppu::OWeakObject& rSource, ::osl::Mutex& _rMutex )
+ :OWeakSubObject( rSource )
+ ,OInterfaceContainerHelper2( _rMutex )
+{
+}
+
+
+Any SAL_CALL FmXSelectionMultiplexer::queryInterface(const Type& _rType)
+{
+ Any aReturn = ::cppu::queryInterface(_rType,
+ static_cast< XSelectionChangeListener*>(this),
+ static_cast< XEventListener*>(this)
+ );
+
+ if (!aReturn.hasValue())
+ aReturn = OWeakSubObject::queryInterface( _rType );
+
+ return aReturn;
+}
+
+
+void FmXSelectionMultiplexer::disposing(const EventObject& )
+{
+}
+
+
+void SAL_CALL FmXSelectionMultiplexer::selectionChanged( const EventObject& _rEvent )
+{
+ EventObject aMulti(_rEvent);
+ aMulti.Source = &m_rParent;
+ notifyEach( &XSelectionChangeListener::selectionChanged, aMulti );
+}
+
+FmXContainerMultiplexer::FmXContainerMultiplexer( ::cppu::OWeakObject& rSource, ::osl::Mutex& _rMutex )
+ :OWeakSubObject( rSource )
+ ,OInterfaceContainerHelper2( _rMutex )
+{
+}
+
+
+Any SAL_CALL FmXContainerMultiplexer::queryInterface(const Type& _rType)
+{
+ Any aReturn = ::cppu::queryInterface(_rType,
+ static_cast< XContainerListener*>(this),
+ static_cast< XEventListener*>(this)
+ );
+
+ if (!aReturn.hasValue())
+ aReturn = OWeakSubObject::queryInterface( _rType );
+
+ return aReturn;
+}
+
+
+void FmXContainerMultiplexer::disposing(const EventObject& )
+{
+}
+
+void FmXContainerMultiplexer::elementInserted(const ContainerEvent& e)
+{
+ ContainerEvent aMulti( e );
+ aMulti.Source = &m_rParent;
+ notifyEach( &XContainerListener::elementInserted, aMulti );
+}
+
+
+void FmXContainerMultiplexer::elementRemoved(const ContainerEvent& e)
+{
+ ContainerEvent aMulti( e );
+ aMulti.Source = &m_rParent;
+ notifyEach( &XContainerListener::elementRemoved, aMulti );
+}
+
+
+void FmXContainerMultiplexer::elementReplaced(const ContainerEvent& e)
+{
+ ContainerEvent aMulti( e );
+ aMulti.Source = &m_rParent;
+ notifyEach( &XContainerListener::elementReplaced, aMulti );
+}
+
+FmXGridControlMultiplexer::FmXGridControlMultiplexer( ::cppu::OWeakObject& rSource, ::osl::Mutex& _rMutex )
+ :OWeakSubObject( rSource )
+ ,OInterfaceContainerHelper2( _rMutex )
+{
+}
+
+
+Any SAL_CALL FmXGridControlMultiplexer::queryInterface(const Type& _rType)
+{
+ Any aReturn = ::cppu::queryInterface( _rType,
+ static_cast< XGridControlListener*>(this)
+ );
+
+ if (!aReturn.hasValue())
+ aReturn = OWeakSubObject::queryInterface( _rType );
+
+ return aReturn;
+}
+
+
+void FmXGridControlMultiplexer::disposing( const EventObject& )
+{
+}
+
+
+void SAL_CALL FmXGridControlMultiplexer::columnChanged( const EventObject& _event )
+{
+ EventObject aForwardedEvent( _event );
+ aForwardedEvent.Source = &m_rParent;
+ notifyEach( &XGridControlListener::columnChanged, aForwardedEvent );
+}
+
+
+//= FmXGridControl
+
+
+Reference< XInterface > FmXGridControl_NewInstance_Impl(const Reference< XMultiServiceFactory>& _rxFactory)
+{
+ return *(new FmXGridControl( comphelper::getComponentContext(_rxFactory) ));
+}
+
+FmXGridControl::FmXGridControl(const Reference< XComponentContext >& _rxContext)
+ :UnoControl()
+ ,m_aModifyListeners(*this, GetMutex())
+ ,m_aUpdateListeners(*this, GetMutex())
+ ,m_aContainerListeners(*this, GetMutex())
+ ,m_aSelectionListeners(*this, GetMutex())
+ ,m_aGridControlListeners(*this, GetMutex())
+ ,m_bInDraw(false)
+ ,m_xContext(_rxContext)
+{
+}
+
+
+FmXGridControl::~FmXGridControl()
+{
+}
+
+
+Any SAL_CALL FmXGridControl::queryAggregation(const Type& _rType)
+{
+ Any aReturn = FmXGridControl_BASE::queryInterface(_rType);
+
+ if (!aReturn.hasValue())
+ aReturn = UnoControl::queryAggregation( _rType );
+ return aReturn;
+}
+
+
+Sequence< Type> SAL_CALL FmXGridControl::getTypes( )
+{
+ return comphelper::concatSequences(UnoControl::getTypes(),FmXGridControl_BASE::getTypes());
+}
+
+
+Sequence<sal_Int8> SAL_CALL FmXGridControl::getImplementationId( )
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// XServiceInfo
+sal_Bool SAL_CALL FmXGridControl::supportsService(const OUString& ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+OUString SAL_CALL FmXGridControl::getImplementationName()
+{
+ return "com.sun.star.form.FmXGridControl";
+}
+
+css::uno::Sequence<OUString> SAL_CALL FmXGridControl::getSupportedServiceNames()
+{
+ return { FM_SUN_CONTROL_GRIDCONTROL, "com.sun.star.awt.UnoControl" };
+}
+
+
+void SAL_CALL FmXGridControl::dispose()
+{
+ SolarMutexGuard aGuard;
+
+ EventObject aEvt;
+ aEvt.Source = static_cast< ::cppu::OWeakObject* >(this);
+ m_aModifyListeners.disposeAndClear(aEvt);
+ m_aUpdateListeners.disposeAndClear(aEvt);
+ m_aContainerListeners.disposeAndClear(aEvt);
+
+ UnoControl::dispose();
+}
+
+
+OUString FmXGridControl::GetComponentServiceName()
+{
+ return "DBGrid";
+}
+
+
+sal_Bool SAL_CALL FmXGridControl::setModel(const Reference< css::awt::XControlModel >& rModel)
+{
+ SolarMutexGuard aGuard;
+
+ if (!UnoControl::setModel(rModel))
+ return false;
+
+ Reference< XGridPeer > xGridPeer(getPeer(), UNO_QUERY);
+ if (xGridPeer.is())
+ {
+ Reference< XIndexContainer > xCols(mxModel, UNO_QUERY);
+ xGridPeer->setColumns(xCols);
+ }
+ return true;
+}
+
+
+FmXGridPeer* FmXGridControl::imp_CreatePeer(vcl::Window* pParent)
+{
+ FmXGridPeer* pReturn = new FmXGridPeer(m_xContext);
+
+ // translate properties into WinBits
+ WinBits nStyle = WB_TABSTOP;
+ Reference< XPropertySet > xModelSet(getModel(), UNO_QUERY);
+ if (xModelSet.is())
+ {
+ try
+ {
+ if (::comphelper::getINT16(xModelSet->getPropertyValue(FM_PROP_BORDER)))
+ nStyle |= WB_BORDER;
+ }
+ catch(const Exception&)
+ {
+ OSL_FAIL("Can not get style");
+ }
+ }
+
+ pReturn->Create(pParent, nStyle);
+ return pReturn;
+}
+
+
+void SAL_CALL FmXGridControl::createPeer(const Reference< css::awt::XToolkit >& /*rToolkit*/, const Reference< css::awt::XWindowPeer >& rParentPeer)
+{
+ if ( !mxModel.is() )
+ throw DisposedException( OUString(), *this );
+
+ DBG_ASSERT(/*(0 == m_nPeerCreationLevel) && */!mbCreatingPeer, "FmXGridControl::createPeer : recursion!");
+ // I think this should never assert, now that we're using the base class' mbCreatingPeer in addition to
+ // our own m_nPeerCreationLevel
+ // But I'm not sure as I don't _fully_ understand the underlying toolkit implementations...
+ // (if this asserts, we still need m_nPeerCreationLevel. If not, we could omit it...)
+ // 14.05.2001 - 86836 - frank.schoenheit@germany.sun.com
+
+ // TODO: why the hell this whole class does not use any mutex?
+
+ if (!getPeer().is())
+ {
+ mbCreatingPeer = true;
+ // mbCreatingPeer is virtually the same as m_nPeerCreationLevel, but it's the base class' method
+ // to prevent recursion.
+
+ vcl::Window* pParentWin = nullptr;
+ if (rParentPeer.is())
+ {
+ VCLXWindow* pParent = comphelper::getUnoTunnelImplementation<VCLXWindow>(rParentPeer);
+ if (pParent)
+ pParentWin = pParent->GetWindow().get();
+ }
+
+ FmXGridPeer* pPeer = imp_CreatePeer(pParentWin);
+ DBG_ASSERT(pPeer != nullptr, "FmXGridControl::createPeer : imp_CreatePeer didn't return a peer !");
+ setPeer( pPeer );
+
+ // reading the properties from the model
+// ++m_nPeerCreationLevel;
+ updateFromModel();
+
+ // consider the following ugly scenario: updateFromModel leads to a propertiesChanges on the Control,
+ // which determines, dat a "critical" property has changed (e.g. "Border") and therefore starts a new
+ // Peer, which lands again here in createPeer we also start a second FmXGridPeer and initialise it.
+ // Then we exit from the first incarnation's updateFromModel and continue working with the pPeer,
+ // that is in fact now already obsolete (as another peer is being started in the second incarnation).
+ // Therefore the effort with the PeerCreationLevel, which ensures that we really use the Peer
+ // created at the deepest level, but first initialise it in the top-level.
+// if (--m_nPeerCreationLevel == 0)
+ {
+ DBG_ASSERT(getPeer().is(), "FmXGridControl::createPeer : something went wrong ... no top level peer !");
+ pPeer = comphelper::getUnoTunnelImplementation<FmXGridPeer>(getPeer());
+
+ setPosSize( maComponentInfos.nX, maComponentInfos.nY, maComponentInfos.nWidth, maComponentInfos.nHeight, css::awt::PosSize::POSSIZE );
+
+ Reference< XIndexContainer > xColumns(getModel(), UNO_QUERY);
+ if (xColumns.is())
+ pPeer->setColumns(xColumns);
+
+ if (maComponentInfos.bVisible)
+ pPeer->setVisible(true);
+
+ if (!maComponentInfos.bEnable)
+ pPeer->setEnable(false);
+
+ if (maWindowListeners.getLength())
+ pPeer->addWindowListener( &maWindowListeners );
+
+ if (maFocusListeners.getLength())
+ pPeer->addFocusListener( &maFocusListeners );
+
+ if (maKeyListeners.getLength())
+ pPeer->addKeyListener( &maKeyListeners );
+
+ if (maMouseListeners.getLength())
+ pPeer->addMouseListener( &maMouseListeners );
+
+ if (maMouseMotionListeners.getLength())
+ pPeer->addMouseMotionListener( &maMouseMotionListeners );
+
+ if (maPaintListeners.getLength())
+ pPeer->addPaintListener( &maPaintListeners );
+
+ if (m_aModifyListeners.getLength())
+ pPeer->addModifyListener( &m_aModifyListeners );
+
+ if (m_aUpdateListeners.getLength())
+ pPeer->addUpdateListener( &m_aUpdateListeners );
+
+ if (m_aContainerListeners.getLength())
+ pPeer->addContainerListener( &m_aContainerListeners );
+
+ // forward the design mode
+ bool bForceAlivePeer = m_bInDraw && !maComponentInfos.bVisible;
+ // (we force an alive-mode peer if we're in "draw", cause in this case the peer will be used for drawing in
+ // foreign devices. We ensure this with the visibility check as a living peer is assumed to be noncritical
+ // only if invisible)
+ Any aOldCursorBookmark;
+ if (!mbDesignMode || bForceAlivePeer)
+ {
+ Reference< XFormComponent > xComp(getModel(), UNO_QUERY);
+ if (xComp.is())
+ {
+ Reference< XRowSet > xForm(xComp->getParent(), UNO_QUERY);
+ // is the form alive?
+ // we can see that if the form contains columns
+ Reference< css::sdbcx::XColumnsSupplier > xColumnsSupplier(xForm, UNO_QUERY);
+ if (xColumnsSupplier.is())
+ {
+ if (Reference< XIndexAccess > (xColumnsSupplier->getColumns(),UNO_QUERY_THROW)->getCount())
+ {
+ // we get only a new bookmark if the resultset is not forwardonly
+ if (::comphelper::getINT32(Reference< XPropertySet > (xForm, UNO_QUERY_THROW)->getPropertyValue(FM_PROP_RESULTSET_TYPE)) != ResultSetType::FORWARD_ONLY)
+ {
+ // as the FmGridControl touches the data source it is connected to we have to remember the current
+ // cursor position (and restore afterwards)
+ // OJ: but only when we stand on a valid row
+ if ( !xForm->isBeforeFirst() && !xForm->isAfterLast() )
+ {
+ try
+ {
+ aOldCursorBookmark = Reference< css::sdbcx::XRowLocate > (xForm, UNO_QUERY_THROW)->getBookmark();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ }
+ }
+ }
+ pPeer->setRowSet(xForm);
+ }
+ }
+ pPeer->setDesignMode(mbDesignMode && !bForceAlivePeer);
+
+ try
+ {
+ if (aOldCursorBookmark.hasValue())
+ { // we have a valid bookmark, so we have to restore the cursor's position
+ Reference< XFormComponent > xComp(getModel(), UNO_QUERY);
+ Reference< css::sdbcx::XRowLocate > xLocate(xComp->getParent(), UNO_QUERY);
+ xLocate->moveToBookmark(aOldCursorBookmark);
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ Reference< css::awt::XView > xPeerView(getPeer(), UNO_QUERY);
+ xPeerView->setZoom( maComponentInfos.nZoomX, maComponentInfos.nZoomY );
+ xPeerView->setGraphics( mxGraphics );
+ }
+ mbCreatingPeer = false;
+ }
+}
+
+
+void FmXGridControl::addModifyListener(const Reference< css::util::XModifyListener >& l)
+{
+ m_aModifyListeners.addInterface( l );
+ if( getPeer().is() && m_aModifyListeners.getLength() == 1 )
+ {
+ Reference< css::util::XModifyBroadcaster > xGrid(getPeer(), UNO_QUERY);
+ xGrid->addModifyListener( &m_aModifyListeners);
+ }
+}
+
+
+sal_Bool SAL_CALL FmXGridControl::select( const Any& _rSelection )
+{
+ SolarMutexGuard aGuard;
+ Reference< XSelectionSupplier > xPeer(getPeer(), UNO_QUERY);
+ return xPeer->select(_rSelection);
+}
+
+
+Any SAL_CALL FmXGridControl::getSelection( )
+{
+ SolarMutexGuard aGuard;
+ Reference< XSelectionSupplier > xPeer(getPeer(), UNO_QUERY);
+ return xPeer->getSelection();
+}
+
+
+void SAL_CALL FmXGridControl::addSelectionChangeListener( const Reference< XSelectionChangeListener >& _rxListener )
+{
+ m_aSelectionListeners.addInterface( _rxListener );
+ if( getPeer().is() && 1 == m_aSelectionListeners.getLength() )
+ {
+ Reference< XSelectionSupplier > xGrid(getPeer(), UNO_QUERY);
+ xGrid->addSelectionChangeListener( &m_aSelectionListeners);
+ }
+}
+
+
+void SAL_CALL FmXGridControl::removeSelectionChangeListener( const Reference< XSelectionChangeListener >& _rxListener )
+{
+ if( getPeer().is() && 1 == m_aSelectionListeners.getLength() )
+ {
+ Reference< XSelectionSupplier > xGrid(getPeer(), UNO_QUERY);
+ xGrid->removeSelectionChangeListener( &m_aSelectionListeners);
+ }
+ m_aSelectionListeners.removeInterface( _rxListener );
+}
+
+
+Sequence< sal_Bool > SAL_CALL FmXGridControl::queryFieldDataType( const Type& xType )
+{
+ if (getPeer().is())
+ {
+ Reference< XGridFieldDataSupplier > xPeerSupplier(getPeer(), UNO_QUERY);
+ if (xPeerSupplier.is())
+ return xPeerSupplier->queryFieldDataType(xType);
+ }
+
+ return Sequence<sal_Bool>();
+}
+
+
+Sequence< Any > SAL_CALL FmXGridControl::queryFieldData( sal_Int32 nRow, const Type& xType )
+{
+ if (getPeer().is())
+ {
+ Reference< XGridFieldDataSupplier > xPeerSupplier(getPeer(), UNO_QUERY);
+ if (xPeerSupplier.is())
+ return xPeerSupplier->queryFieldData(nRow, xType);
+ }
+
+ return Sequence< Any>();
+}
+
+
+void SAL_CALL FmXGridControl::removeModifyListener(const Reference< css::util::XModifyListener >& l)
+{
+ if( getPeer().is() && m_aModifyListeners.getLength() == 1 )
+ {
+ Reference< css::util::XModifyBroadcaster > xGrid(getPeer(), UNO_QUERY);
+ xGrid->removeModifyListener( &m_aModifyListeners);
+ }
+ m_aModifyListeners.removeInterface( l );
+}
+
+
+void SAL_CALL FmXGridControl::draw( sal_Int32 x, sal_Int32 y )
+{
+ SolarMutexGuard aGuard;
+ m_bInDraw = true;
+ UnoControl::draw(x, y);
+ m_bInDraw = false;
+}
+
+
+void SAL_CALL FmXGridControl::setDesignMode(sal_Bool bOn)
+{
+ css::util::ModeChangeEvent aModeChangeEvent;
+
+ // --- <mutex_lock> ---
+ {
+ SolarMutexGuard aGuard;
+
+ Reference< XRowSetSupplier > xGrid(getPeer(), UNO_QUERY);
+
+ if (xGrid.is() && (bool(bOn) != mbDesignMode || (!bOn && !xGrid->getRowSet().is())))
+ {
+ if (bOn)
+ {
+ xGrid->setRowSet(Reference< XRowSet > ());
+ }
+ else
+ {
+ Reference< XFormComponent > xComp(getModel(), UNO_QUERY);
+ if (xComp.is())
+ {
+ Reference< XRowSet > xForm(xComp->getParent(), UNO_QUERY);
+ xGrid->setRowSet(xForm);
+ }
+ }
+
+ // Avoid infinite recursion when calling XVclWindowPeer::setDesignMode below
+ mbDesignMode = bOn;
+
+ Reference< XVclWindowPeer > xVclWindowPeer( getPeer(), UNO_QUERY );
+ if (xVclWindowPeer.is())
+ xVclWindowPeer->setDesignMode(bOn);
+ }
+ else
+ {
+ mbDesignMode = bOn;
+ }
+
+ // dispose our current AccessibleContext, if we have one
+ // (changing the design mode implies having a new implementation for this context,
+ // so the old one must be declared DEFUNC)
+ DisposeAccessibleContext(
+ Reference<XComponent>(maAccessibleContext, UNO_QUERY));
+ maAccessibleContext.clear();
+
+ // prepare firing an event
+ aModeChangeEvent.Source = *this;
+ aModeChangeEvent.NewMode = mbDesignMode ? OUStringLiteral( "design" ) : OUStringLiteral( "alive" );
+ }
+
+ // --- </mutex_lock> ---
+ maModeChangeListeners.notifyEach( &XModeChangeListener::modeChanged, aModeChangeEvent );
+}
+
+// XBoundComponent
+
+void SAL_CALL FmXGridControl::addUpdateListener(const Reference< XUpdateListener >& l)
+{
+ m_aUpdateListeners.addInterface( l );
+ if( getPeer().is() && m_aUpdateListeners.getLength() == 1 )
+ {
+ Reference< XBoundComponent > xBound(getPeer(), UNO_QUERY);
+ xBound->addUpdateListener( &m_aUpdateListeners);
+ }
+}
+
+
+void SAL_CALL FmXGridControl::removeUpdateListener(const Reference< XUpdateListener >& l)
+{
+ if( getPeer().is() && m_aUpdateListeners.getLength() == 1 )
+ {
+ Reference< XBoundComponent > xBound(getPeer(), UNO_QUERY);
+ xBound->removeUpdateListener( &m_aUpdateListeners);
+ }
+ m_aUpdateListeners.removeInterface( l );
+}
+
+
+sal_Bool SAL_CALL FmXGridControl::commit()
+{
+ Reference< XBoundComponent > xBound(getPeer(), UNO_QUERY);
+ if (xBound.is())
+ return xBound->commit();
+ else
+ return true;
+}
+
+// XContainer
+
+void SAL_CALL FmXGridControl::addContainerListener(const Reference< XContainerListener >& l)
+{
+ m_aContainerListeners.addInterface( l );
+ if( getPeer().is() && m_aContainerListeners.getLength() == 1 )
+ {
+ Reference< XContainer > xContainer(getPeer(), UNO_QUERY);
+ xContainer->addContainerListener( &m_aContainerListeners);
+ }
+}
+
+
+void SAL_CALL FmXGridControl::removeContainerListener(const Reference< XContainerListener >& l)
+{
+ if( getPeer().is() && m_aContainerListeners.getLength() == 1 )
+ {
+ Reference< XContainer > xContainer(getPeer(), UNO_QUERY);
+ xContainer->removeContainerListener( &m_aContainerListeners);
+ }
+ m_aContainerListeners.removeInterface( l );
+}
+
+
+Reference< css::frame::XDispatch > SAL_CALL FmXGridControl::queryDispatch(const css::util::URL& aURL, const OUString& aTargetFrameName, sal_Int32 nSearchFlags)
+{
+ Reference< css::frame::XDispatchProvider > xPeerProvider(getPeer(), UNO_QUERY);
+ if (xPeerProvider.is())
+ return xPeerProvider->queryDispatch(aURL, aTargetFrameName, nSearchFlags);
+ else
+ return Reference< css::frame::XDispatch > ();
+}
+
+
+Sequence< Reference< css::frame::XDispatch > > SAL_CALL FmXGridControl::queryDispatches(const Sequence< css::frame::DispatchDescriptor>& aDescripts)
+{
+ Reference< css::frame::XDispatchProvider > xPeerProvider(getPeer(), UNO_QUERY);
+ if (xPeerProvider.is())
+ return xPeerProvider->queryDispatches(aDescripts);
+ else
+ return Sequence< Reference< css::frame::XDispatch > >();
+}
+
+
+void SAL_CALL FmXGridControl::registerDispatchProviderInterceptor(const Reference< css::frame::XDispatchProviderInterceptor >& _xInterceptor)
+{
+ Reference< css::frame::XDispatchProviderInterception > xPeerInterception(getPeer(), UNO_QUERY);
+ if (xPeerInterception.is())
+ xPeerInterception->registerDispatchProviderInterceptor(_xInterceptor);
+}
+
+
+void SAL_CALL FmXGridControl::releaseDispatchProviderInterceptor(const Reference< css::frame::XDispatchProviderInterceptor >& _xInterceptor)
+{
+ Reference< css::frame::XDispatchProviderInterception > xPeerInterception(getPeer(), UNO_QUERY);
+ if (xPeerInterception.is())
+ xPeerInterception->releaseDispatchProviderInterceptor(_xInterceptor);
+}
+
+
+void SAL_CALL FmXGridControl::addGridControlListener( const Reference< XGridControlListener >& _listener )
+{
+ ::osl::MutexGuard aGuard( GetMutex() );
+
+ m_aGridControlListeners.addInterface( _listener );
+ if ( getPeer().is() && 1 == m_aGridControlListeners.getLength() )
+ {
+ Reference< XGridControl > xPeerGrid( getPeer(), UNO_QUERY );
+ if ( xPeerGrid.is() )
+ xPeerGrid->addGridControlListener( &m_aGridControlListeners );
+ }
+}
+
+
+void SAL_CALL FmXGridControl::removeGridControlListener( const Reference< XGridControlListener >& _listener )
+{
+ ::osl::MutexGuard aGuard( GetMutex() );
+
+ if( getPeer().is() && 1 == m_aGridControlListeners.getLength() )
+ {
+ Reference< XGridControl > xPeerGrid( getPeer(), UNO_QUERY );
+ if ( xPeerGrid.is() )
+ xPeerGrid->removeGridControlListener( &m_aGridControlListeners );
+ }
+
+ m_aGridControlListeners.removeInterface( _listener );
+}
+
+
+sal_Int16 SAL_CALL FmXGridControl::getCurrentColumnPosition()
+{
+ Reference< XGridControl > xGrid( getPeer(), UNO_QUERY );
+ return xGrid.is() ? xGrid->getCurrentColumnPosition() : -1;
+}
+
+
+void SAL_CALL FmXGridControl::setCurrentColumnPosition(sal_Int16 nPos)
+{
+ Reference< XGridControl > xGrid( getPeer(), UNO_QUERY );
+ if ( xGrid.is() )
+ {
+ SolarMutexGuard aGuard;
+ xGrid->setCurrentColumnPosition( nPos );
+ }
+}
+
+// XElementAccess
+
+sal_Bool SAL_CALL FmXGridControl::hasElements()
+{
+ Reference< XElementAccess > xPeer(getPeer(), UNO_QUERY);
+ return xPeer.is() && xPeer->hasElements();
+}
+
+
+Type SAL_CALL FmXGridControl::getElementType( )
+{
+ return cppu::UnoType<css::awt::XTextComponent>::get();
+}
+
+// XEnumerationAccess
+
+Reference< XEnumeration > SAL_CALL FmXGridControl::createEnumeration()
+{
+ Reference< XEnumerationAccess > xPeer(getPeer(), UNO_QUERY);
+ if (xPeer.is())
+ return xPeer->createEnumeration();
+ else
+ return new ::comphelper::OEnumerationByIndex(this);
+}
+
+// XIndexAccess
+
+sal_Int32 SAL_CALL FmXGridControl::getCount()
+{
+ Reference< XIndexAccess > xPeer(getPeer(), UNO_QUERY);
+ return xPeer.is() ? xPeer->getCount() : 0;
+}
+
+
+Any SAL_CALL FmXGridControl::getByIndex(sal_Int32 _nIndex)
+{
+ Reference< XIndexAccess > xPeer(getPeer(), UNO_QUERY);
+ if (!xPeer.is())
+ throw IndexOutOfBoundsException();
+
+ return xPeer->getByIndex(_nIndex);
+}
+
+// css::util::XModeSelector
+
+void SAL_CALL FmXGridControl::setMode(const OUString& Mode)
+{
+ Reference< css::util::XModeSelector > xPeer(getPeer(), UNO_QUERY);
+ if (!xPeer.is())
+ throw NoSupportException();
+
+ xPeer->setMode(Mode);
+}
+
+
+OUString SAL_CALL FmXGridControl::getMode()
+{
+ Reference< css::util::XModeSelector > xPeer(getPeer(), UNO_QUERY);
+ return xPeer.is() ? xPeer->getMode() : OUString();
+}
+
+
+css::uno::Sequence<OUString> SAL_CALL FmXGridControl::getSupportedModes()
+{
+ Reference< css::util::XModeSelector > xPeer(getPeer(), UNO_QUERY);
+ return xPeer.is() ? xPeer->getSupportedModes() : css::uno::Sequence<OUString>();
+}
+
+
+sal_Bool SAL_CALL FmXGridControl::supportsMode(const OUString& Mode)
+{
+ Reference< css::util::XModeSelector > xPeer(getPeer(), UNO_QUERY);
+ return xPeer.is() && xPeer->supportsMode(Mode);
+}
+
+// helper class which prevents that in the peer's header the FmGridListener must be known
+class FmXGridPeer::GridListenerDelegator : public FmGridListener
+{
+protected:
+ FmXGridPeer* m_pPeer;
+
+public:
+ explicit GridListenerDelegator( FmXGridPeer* _pPeer );
+ virtual ~GridListenerDelegator();
+
+protected:
+ virtual void selectionChanged() override;
+ virtual void columnChanged() override;
+};
+
+
+FmXGridPeer::GridListenerDelegator::GridListenerDelegator(FmXGridPeer* _pPeer)
+ :m_pPeer(_pPeer)
+{
+ DBG_ASSERT(m_pPeer, "GridListenerDelegator::GridListenerDelegator");
+}
+
+FmXGridPeer::GridListenerDelegator::~GridListenerDelegator()
+{
+}
+
+
+void FmXGridPeer::GridListenerDelegator::selectionChanged()
+{
+ m_pPeer->selectionChanged();
+}
+
+
+void FmXGridPeer::GridListenerDelegator::columnChanged()
+{
+ m_pPeer->columnChanged();
+}
+
+void FmXGridPeer::selectionChanged()
+{
+ EventObject aSource;
+ aSource.Source = static_cast< ::cppu::OWeakObject* >(this);
+ m_aSelectionListeners.notifyEach( &XSelectionChangeListener::selectionChanged, aSource);
+}
+
+
+void FmXGridPeer::columnChanged()
+{
+ EventObject aEvent( *this );
+ m_aGridControlListeners.notifyEach( &XGridControlListener::columnChanged, aEvent );
+}
+
+
+FmXGridPeer::FmXGridPeer(const Reference< XComponentContext >& _rxContext)
+ :m_xContext(_rxContext)
+ ,m_aModifyListeners(m_aMutex)
+ ,m_aUpdateListeners(m_aMutex)
+ ,m_aContainerListeners(m_aMutex)
+ ,m_aSelectionListeners(m_aMutex)
+ ,m_aGridControlListeners(m_aMutex)
+ ,m_aMode("DataMode")
+ ,m_nCursorListening(0)
+ ,m_bInterceptingDispatch(false)
+{
+ // Create must be called after this constructor
+ m_pGridListener.reset( new GridListenerDelegator( this ) );
+}
+
+
+VclPtr<FmGridControl> FmXGridPeer::imp_CreateControl(vcl::Window* pParent, WinBits nStyle)
+{
+ return VclPtr<FmGridControl>::Create(m_xContext, pParent, this, nStyle);
+}
+
+
+void FmXGridPeer::Create(vcl::Window* pParent, WinBits nStyle)
+{
+ VclPtr<FmGridControl> pWin = imp_CreateControl(pParent, nStyle);
+ DBG_ASSERT(pWin != nullptr, "FmXGridPeer::Create : imp_CreateControl didn't return a control !");
+
+ pWin->SetStateProvider(LINK(this, FmXGridPeer, OnQueryGridSlotState));
+ pWin->SetSlotExecutor(LINK(this, FmXGridPeer, OnExecuteGridSlot));
+
+ // want to hear about row selections
+ pWin->setGridListener( m_pGridListener.get() );
+
+ // Init must always be called
+ pWin->Init();
+ pWin->SetComponentInterface(this);
+
+ getSupportedURLs();
+}
+
+
+FmXGridPeer::~FmXGridPeer()
+{
+ setRowSet(Reference< XRowSet > ());
+ setColumns(Reference< XIndexContainer > ());
+}
+
+UNO3_GETIMPLEMENTATION2_IMPL(FmXGridPeer, VCLXWindow);
+
+// XEventListener
+
+void FmXGridPeer::disposing(const EventObject& e)
+{
+ bool bKnownSender = false;
+
+ Reference< XIndexContainer > xCols( e.Source, UNO_QUERY );
+ if ( xCols.is() )
+ {
+ setColumns(Reference< XIndexContainer > ());
+ bKnownSender = true;
+ }
+
+ Reference< XRowSet > xCursor(e.Source, UNO_QUERY);
+ if (xCursor.is())
+ {
+ setRowSet( m_xCursor );
+ m_xCursor = nullptr;
+ bKnownSender = true;
+ }
+
+
+ if ( !bKnownSender && m_pDispatchers )
+ {
+ const Sequence< URL>& aSupportedURLs = getSupportedURLs();
+ const URL* pSupportedURLs = aSupportedURLs.getConstArray();
+ for ( sal_Int32 i=0; i < ( aSupportedURLs.getLength() ) && !bKnownSender; ++i, ++pSupportedURLs )
+ {
+ if ( m_pDispatchers[i] == e.Source )
+ {
+ m_pDispatchers[i]->removeStatusListener( static_cast< css::frame::XStatusListener* >( this ), *pSupportedURLs );
+ m_pDispatchers[i] = nullptr;
+ m_pStateCache[i] = false;
+ bKnownSender = true;
+ }
+ }
+ }
+
+ if ( !bKnownSender )
+ VCLXWindow::disposing(e);
+}
+
+
+void FmXGridPeer::addModifyListener(const Reference< css::util::XModifyListener >& l)
+{
+ m_aModifyListeners.addInterface( l );
+}
+
+
+void FmXGridPeer::removeModifyListener(const Reference< css::util::XModifyListener >& l)
+{
+ m_aModifyListeners.removeInterface( l );
+}
+
+
+#define LAST_KNOWN_TYPE FormComponentType::PATTERNFIELD
+Sequence< sal_Bool > SAL_CALL FmXGridPeer::queryFieldDataType( const Type& xType )
+{
+ // a 'conversion table'
+ static const bool bCanConvert[LAST_KNOWN_TYPE][4] =
+ {
+ { false, false, false, false }, // FormComponentType::CONTROL
+ { false, false, false, false }, // FormComponentType::COMMANDBUTTON
+ { false, false, false, false }, // FormComponentType::RADIOBUTTON
+ { false, false, false, false }, // FormComponentType::IMAGEBUTTON
+ { false, false, false, true }, // FormComponentType::CHECKBOX
+ { false, false, false, false }, // FormComponentType::LISTBOX
+ { false, false, false, false }, // FormComponentType::COMBOBOX
+ { false, false, false, false }, // FormComponentType::GROUPBOX
+ { true , false, false, false }, // FormComponentType::TEXTFIELD
+ { false, false, false, false }, // FormComponentType::FIXEDTEXT
+ { false, false, false, false }, // FormComponentType::GRIDCONTROL
+ { false, false, false, false }, // FormComponentType::FILECONTROL
+ { false, false, false, false }, // FormComponentType::HIDDENCONTROL
+ { false, false, false, false }, // FormComponentType::IMAGECONTROL
+ { true , true , true , false }, // FormComponentType::DATEFIELD
+ { true , true , false, false }, // FormComponentType::TIMEFIELD
+ { true , true , false, false }, // FormComponentType::NUMERICFIELD
+ { true , true , false, false }, // FormComponentType::CURRENCYFIELD
+ { true , false, false, false } // FormComponentType::PATTERNFIELD
+ };
+
+
+ sal_Int16 nMapColumn = -1;
+ switch (xType.getTypeClass())
+ {
+ case TypeClass_STRING : nMapColumn = 0; break;
+ case TypeClass_FLOAT:
+ case TypeClass_DOUBLE : nMapColumn = 1; break;
+ case TypeClass_SHORT:
+ case TypeClass_LONG:
+ case TypeClass_UNSIGNED_LONG:
+ case TypeClass_UNSIGNED_SHORT : nMapColumn = 2; break;
+ case TypeClass_BOOLEAN : nMapColumn = 3; break;
+ default:
+ break;
+ }
+
+ Reference< XIndexContainer > xColumns = getColumns();
+
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ sal_Int32 nColumns = pGrid->GetViewColCount();
+
+ std::vector< std::unique_ptr<DbGridColumn> > const & aColumns = pGrid->GetColumns();
+
+ Sequence<sal_Bool> aReturnSequence(nColumns);
+ sal_Bool* pReturnArray = aReturnSequence.getArray();
+
+ bool bRequestedAsAny = (xType.getTypeClass() == TypeClass_ANY);
+
+ DbGridColumn* pCol;
+ Reference< css::sdb::XColumn > xFieldContent;
+ Reference< XPropertySet > xCurrentColumn;
+ for (sal_Int32 i=0; i<nColumns; ++i)
+ {
+ if (bRequestedAsAny)
+ {
+ pReturnArray[i] = true;
+ continue;
+ }
+
+ pReturnArray[i] = false;
+
+ sal_uInt16 nModelPos = pGrid->GetModelColumnPos(pGrid->GetColumnIdFromViewPos(static_cast<sal_uInt16>(i)));
+ DBG_ASSERT(nModelPos != sal_uInt16(-1), "FmXGridPeer::queryFieldDataType : no model pos !");
+
+ pCol = aColumns[ nModelPos ].get();
+ const DbGridRowRef xRow = pGrid->GetSeekRow();
+ xFieldContent = (xRow.is() && xRow->HasField(pCol->GetFieldPos())) ? xRow->GetField(pCol->GetFieldPos()).getColumn() : Reference< css::sdb::XColumn > ();
+ if (!xFieldContent.is())
+ // can't supply anything without a field content
+ // FS - 07.12.99 - 54391
+ continue;
+
+ xColumns->getByIndex(nModelPos) >>= xCurrentColumn;
+ if (!::comphelper::hasProperty(FM_PROP_CLASSID, xCurrentColumn))
+ continue;
+
+ sal_Int16 nClassId = sal_Int16();
+ xCurrentColumn->getPropertyValue(FM_PROP_CLASSID) >>= nClassId;
+ if (nClassId>LAST_KNOWN_TYPE)
+ continue;
+ DBG_ASSERT(nClassId>0, "FmXGridPeer::queryFieldDataType : somebody changed the definition of the FormComponentType enum !");
+
+ if (nMapColumn != -1)
+ pReturnArray[i] = bCanConvert[nClassId-1][nMapColumn];
+ }
+
+ return aReturnSequence;
+}
+
+
+Sequence< Any > SAL_CALL FmXGridPeer::queryFieldData( sal_Int32 nRow, const Type& xType )
+{
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ DBG_ASSERT(pGrid && pGrid->IsOpen(), "FmXGridPeer::queryFieldData : have no valid grid window !");
+ if (!pGrid || !pGrid->IsOpen())
+ return Sequence< Any>();
+
+ // move the control to the specified row
+ if (!pGrid->SeekRow(nRow))
+ {
+ throw IllegalArgumentException();
+ }
+
+ // don't use GetCurrentRow as this isn't affected by the above SeekRow
+ // FS - 30.09.99 - 68644
+ DbGridRowRef xPaintRow = pGrid->GetPaintRow();
+ ENSURE_OR_THROW( xPaintRow.is(), "invalid paint row" );
+
+ // I need the columns of the control for GetFieldText
+ std::vector< std::unique_ptr<DbGridColumn> > const & aColumns = pGrid->GetColumns();
+
+ // and through all the columns
+ sal_Int32 nColumnCount = pGrid->GetViewColCount();
+
+ Sequence< Any> aReturnSequence(nColumnCount);
+ Any* pReturnArray = aReturnSequence.getArray();
+
+ bool bRequestedAsAny = (xType.getTypeClass() == TypeClass_ANY);
+ Reference< css::sdb::XColumn > xFieldContent;
+ for (sal_Int32 i=0; i < nColumnCount; ++i)
+ {
+ sal_uInt16 nModelPos = pGrid->GetModelColumnPos(pGrid->GetColumnIdFromViewPos(static_cast<sal_uInt16>(i)));
+ DBG_ASSERT(nModelPos != sal_uInt16(-1), "FmXGridPeer::queryFieldData : invalid model pos !");
+
+ // don't use GetCurrentFieldValue to determine the field content as this isn't affected by the above SeekRow
+ // FS - 30.09.99 - 68644
+ DbGridColumn* pCol = aColumns[ nModelPos ].get();
+ xFieldContent = xPaintRow->HasField( pCol->GetFieldPos() )
+ ? xPaintRow->GetField( pCol->GetFieldPos() ).getColumn()
+ : Reference< XColumn > ();
+
+ if ( !xFieldContent.is() )
+ continue;
+
+ if (bRequestedAsAny)
+ {
+ Reference< XPropertySet > xFieldSet(xFieldContent, UNO_QUERY);
+ pReturnArray[i] = xFieldSet->getPropertyValue(FM_PROP_VALUE);
+ }
+ else
+ {
+ switch (xType.getTypeClass())
+ {
+ // Strings are dealt with directly by the GetFieldText
+ case TypeClass_STRING :
+ {
+ OUString sText = aColumns[ nModelPos ]->GetCellText( xPaintRow.get(), pGrid->getNumberFormatter() );
+ pReturnArray[i] <<= sText;
+ }
+ break;
+ // everything else is requested in the DatabaseVariant
+ case TypeClass_FLOAT : pReturnArray[i] <<= xFieldContent->getFloat(); break;
+ case TypeClass_DOUBLE : pReturnArray[i] <<= xFieldContent->getDouble(); break;
+ case TypeClass_SHORT : pReturnArray[i] <<= xFieldContent->getShort(); break;
+ case TypeClass_LONG : pReturnArray[i] <<= static_cast<sal_Int32>(xFieldContent->getLong()); break;
+ case TypeClass_UNSIGNED_SHORT : pReturnArray[i] <<= static_cast<sal_uInt16>(xFieldContent->getShort()); break;
+ case TypeClass_UNSIGNED_LONG : pReturnArray[i] <<= static_cast<sal_uInt32>(xFieldContent->getLong()); break;
+ case TypeClass_BOOLEAN : pReturnArray[i] <<= xFieldContent->getBoolean(); break;
+ default:
+ {
+ throw IllegalArgumentException();
+ }
+ }
+ }
+ }
+ return aReturnSequence;
+}
+
+
+void FmXGridPeer::CellModified()
+{
+ EventObject aEvt;
+ aEvt.Source = static_cast< ::cppu::OWeakObject* >(this);
+ m_aModifyListeners.notifyEach( &XModifyListener::modified, aEvt );
+}
+
+// XPropertyChangeListener
+
+void FmXGridPeer::propertyChange(const PropertyChangeEvent& evt)
+{
+ SolarMutexGuard aGuard;
+ // want to do a lot of VCL stuff here ...
+ // this should not be (deadlock) critical, as by definition, every component should release
+ // any own mutexes before notifying
+
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ if (!pGrid)
+ return;
+
+ // Database event
+ if (evt.PropertyName == FM_PROP_VALUE || m_xCursor == evt.Source)
+ pGrid->propertyChange(evt);
+ else if (pGrid && m_xColumns.is() && m_xColumns->hasElements())
+ {
+ // next find which column has changed
+ css::uno::Reference<css::uno::XInterface> xCurrent;
+ sal_Int32 i;
+
+ for ( i = 0; i < m_xColumns->getCount(); i++)
+ {
+ xCurrent.set(m_xColumns->getByIndex(i), css::uno::UNO_QUERY);
+ if (evt.Source == xCurrent)
+ break;
+ }
+
+ if (i >= m_xColumns->getCount())
+ // this is valid because we are listening at the cursor, too (RecordCount, -status, edit mode)
+ return;
+
+ sal_uInt16 nId = pGrid->GetColumnIdFromModelPos(static_cast<sal_uInt16>(i));
+ bool bInvalidateColumn = false;
+
+ if (evt.PropertyName == FM_PROP_LABEL)
+ {
+ OUString aName = ::comphelper::getString(evt.NewValue);
+ if (aName != pGrid->GetColumnTitle(nId))
+ pGrid->SetColumnTitle(nId, aName);
+ }
+ else if (evt.PropertyName == FM_PROP_WIDTH)
+ {
+ sal_Int32 nWidth = 0;
+ if (evt.NewValue.getValueType().getTypeClass() == TypeClass_VOID)
+ nWidth = pGrid->GetDefaultColumnWidth(pGrid->GetColumnTitle(nId));
+ // GetDefaultColumnWidth already considered the zoom factor
+ else
+ {
+ sal_Int32 nTest = 0;
+ if (evt.NewValue >>= nTest)
+ {
+ nWidth = pGrid->LogicToPixel(Point(nTest, 0), MapMode(MapUnit::Map10thMM)).X();
+ // take the zoom factor into account
+ nWidth = pGrid->CalcZoom(nWidth);
+ }
+ }
+ if (nWidth != (sal_Int32(pGrid->GetColumnWidth(nId))))
+ {
+ if (pGrid->IsEditing())
+ {
+ pGrid->DeactivateCell();
+ pGrid->ActivateCell();
+ }
+ pGrid->SetColumnWidth(nId, nWidth);
+ }
+ }
+ else if (evt.PropertyName == FM_PROP_HIDDEN)
+ {
+ DBG_ASSERT(evt.NewValue.getValueType().getTypeClass() == TypeClass_BOOLEAN,
+ "FmXGridPeer::propertyChange : the property 'hidden' should be of type boolean !");
+ if (::comphelper::getBOOL(evt.NewValue))
+ pGrid->HideColumn(nId);
+ else
+ pGrid->ShowColumn(nId);
+ }
+ else if (evt.PropertyName == FM_PROP_ALIGN)
+ {
+ // in design mode it doesn't matter
+ if (!isDesignMode())
+ {
+ DbGridColumn* pCol = pGrid->GetColumns()[i].get();
+
+ pCol->SetAlignmentFromModel(-1);
+ bInvalidateColumn = true;
+ }
+ }
+ else if (evt.PropertyName == FM_PROP_FORMATKEY)
+ {
+ if (!isDesignMode())
+ bInvalidateColumn = true;
+ }
+
+ // need to invalidate the affected column ?
+ if (bInvalidateColumn)
+ {
+ bool bWasEditing = pGrid->IsEditing();
+ if (bWasEditing)
+ pGrid->DeactivateCell();
+
+ ::tools::Rectangle aColRect = pGrid->GetFieldRect(nId);
+ aColRect.SetTop( 0 );
+ aColRect.SetBottom( pGrid->GetSizePixel().Height() );
+ pGrid->Invalidate(aColRect);
+
+ if (bWasEditing)
+ pGrid->ActivateCell();
+ }
+ }
+}
+
+// XBoundComponent
+
+void FmXGridPeer::addUpdateListener(const Reference< XUpdateListener >& l)
+{
+ m_aUpdateListeners.addInterface(l);
+}
+
+
+void FmXGridPeer::removeUpdateListener(const Reference< XUpdateListener >& l)
+{
+ m_aUpdateListeners.removeInterface(l);
+}
+
+
+sal_Bool FmXGridPeer::commit()
+{
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ if (!m_xCursor.is() || !pGrid)
+ return true;
+
+ EventObject aEvt(static_cast< ::cppu::OWeakObject* >(this));
+ ::comphelper::OInterfaceIteratorHelper2 aIter(m_aUpdateListeners);
+ bool bCancel = false;
+ while (aIter.hasMoreElements() && !bCancel)
+ if ( !static_cast< XUpdateListener* >( aIter.next() )->approveUpdate( aEvt ) )
+ bCancel = true;
+
+ if (!bCancel)
+ bCancel = !pGrid->commit();
+
+ if (!bCancel)
+ m_aUpdateListeners.notifyEach( &XUpdateListener::updated, aEvt );
+ return !bCancel;
+}
+
+
+void FmXGridPeer::cursorMoved(const EventObject& _rEvent)
+{
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ // we are not interested in moving to insert row only in the reset event
+ // which is fired after positioning and the insert row
+ if (pGrid && pGrid->IsOpen() && !::comphelper::getBOOL(Reference< XPropertySet > (_rEvent.Source, UNO_QUERY_THROW)->getPropertyValue(FM_PROP_ISNEW)))
+ pGrid->positioned();
+}
+
+
+void FmXGridPeer::rowChanged(const EventObject& /*_rEvent*/)
+{
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ if (pGrid && pGrid->IsOpen())
+ {
+ if (m_xCursor->rowUpdated() && !pGrid->IsCurrentAppending())
+ pGrid->RowModified(pGrid->GetCurrentPos());
+ else if (m_xCursor->rowInserted())
+ pGrid->inserted();
+ }
+}
+
+
+void FmXGridPeer::rowSetChanged(const EventObject& /*event*/)
+{
+ // not interested in ...
+ // (our parent is a form which means we get a loaded or reloaded after this rowSetChanged)
+}
+
+// XLoadListener
+
+void FmXGridPeer::loaded(const EventObject& /*rEvent*/)
+{
+ updateGrid(m_xCursor);
+}
+
+
+void FmXGridPeer::unloaded(const EventObject& /*rEvent*/)
+{
+ updateGrid( Reference< XRowSet > (nullptr) );
+}
+
+
+void FmXGridPeer::reloading(const EventObject& /*aEvent*/)
+{
+ // empty the grid
+ updateGrid( Reference< XRowSet > (nullptr) );
+}
+
+
+void FmXGridPeer::unloading(const EventObject& /*aEvent*/)
+{
+ // empty the grid
+ updateGrid( Reference< XRowSet > (nullptr) );
+}
+
+
+void FmXGridPeer::reloaded(const EventObject& aEvent)
+{
+ {
+ const sal_Int32 cnt = m_xColumns->getCount();
+ for(sal_Int32 i=0; i<cnt; ++i)
+ {
+ Reference< XLoadListener> xll(m_xColumns->getByIndex(i), UNO_QUERY);
+ if(xll.is())
+ {
+ xll->reloaded(aEvent);
+ }
+ }
+ }
+ updateGrid(m_xCursor);
+}
+
+// XGridPeer
+
+Reference< XIndexContainer > FmXGridPeer::getColumns()
+{
+ return m_xColumns;
+}
+
+
+void FmXGridPeer::addColumnListeners(const Reference< XPropertySet >& xCol)
+{
+ static const OUStringLiteral aPropsListenedTo[] =
+ {
+ FM_PROP_LABEL, FM_PROP_WIDTH, FM_PROP_HIDDEN, FM_PROP_ALIGN,
+ FM_PROP_FORMATKEY
+ };
+
+ // as not all properties have to be supported by all columns we have to check this
+ // before adding a listener
+ Reference< XPropertySetInfo > xInfo = xCol->getPropertySetInfo();
+ for (size_t i=0; i<SAL_N_ELEMENTS(aPropsListenedTo); ++i)
+ {
+ if ( xInfo->hasPropertyByName( aPropsListenedTo[i] ) )
+ {
+ Property aPropDesc = xInfo->getPropertyByName( aPropsListenedTo[i] );
+ if ( 0 != ( aPropDesc.Attributes & PropertyAttribute::BOUND ) )
+ xCol->addPropertyChangeListener( aPropsListenedTo[i], this );
+ }
+ }
+}
+
+
+void FmXGridPeer::removeColumnListeners(const Reference< XPropertySet >& xCol)
+{
+ // the same props as in addColumnListeners... linux has problems with global static UStrings, so
+ // we have to do it this way...
+ static const OUStringLiteral aPropsListenedTo[] =
+ {
+ FM_PROP_LABEL, FM_PROP_WIDTH, FM_PROP_HIDDEN, FM_PROP_ALIGN,
+ FM_PROP_FORMATKEY
+ };
+
+ Reference< XPropertySetInfo > xInfo = xCol->getPropertySetInfo();
+ for (const auto & i : aPropsListenedTo)
+ if (xInfo->hasPropertyByName(i))
+ xCol->removePropertyChangeListener(i, this);
+}
+
+
+void FmXGridPeer::setColumns(const Reference< XIndexContainer >& Columns)
+{
+ SolarMutexGuard aGuard;
+
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+
+ if (m_xColumns.is())
+ {
+ Reference< XPropertySet > xCol;
+ for (sal_Int32 i = 0; i < m_xColumns->getCount(); i++)
+ {
+ xCol.set(m_xColumns->getByIndex(i), css::uno::UNO_QUERY);
+ removeColumnListeners(xCol);
+ }
+ Reference< XContainer > xContainer(m_xColumns, UNO_QUERY);
+ xContainer->removeContainerListener(this);
+
+ Reference< XSelectionSupplier > xSelSupplier(m_xColumns, UNO_QUERY);
+ xSelSupplier->removeSelectionChangeListener(this);
+
+ Reference< XReset > xColumnReset(m_xColumns, UNO_QUERY);
+ if (xColumnReset.is())
+ xColumnReset->removeResetListener(static_cast<XResetListener*>(this));
+ }
+ if (Columns.is())
+ {
+ Reference< XContainer > xContainer(Columns, UNO_QUERY);
+ xContainer->addContainerListener(this);
+
+ Reference< XSelectionSupplier > xSelSupplier(Columns, UNO_QUERY);
+ xSelSupplier->addSelectionChangeListener(this);
+
+ Reference< XPropertySet > xCol;
+ for (sal_Int32 i = 0; i < Columns->getCount(); i++)
+ {
+ xCol.set(Columns->getByIndex(i), css::uno::UNO_QUERY);
+ addColumnListeners(xCol);
+ }
+
+ Reference< XReset > xColumnReset(Columns, UNO_QUERY);
+ if (xColumnReset.is())
+ xColumnReset->addResetListener(static_cast<XResetListener*>(this));
+ }
+ m_xColumns = Columns;
+ if (pGrid)
+ {
+ pGrid->InitColumnsByModels(m_xColumns);
+
+ if (m_xColumns.is())
+ {
+ EventObject aEvt(m_xColumns);
+ selectionChanged(aEvt);
+ }
+ }
+}
+
+
+void FmXGridPeer::setDesignMode(sal_Bool bOn)
+{
+ if (bOn != isDesignMode())
+ {
+ VclPtr<vcl::Window> pWin = GetWindow();
+ if (pWin)
+ static_cast<FmGridControl*>(pWin.get())->SetDesignMode(bOn);
+ }
+
+ if (bOn)
+ DisConnectFromDispatcher();
+ else
+ UpdateDispatches(); // will connect if not already connected and just update else
+}
+
+
+sal_Bool FmXGridPeer::isDesignMode()
+{
+ VclPtr<vcl::Window> pWin = GetWindow();
+ if (pWin)
+ return static_cast<FmGridControl*>(pWin.get())->IsDesignMode();
+ else
+ return false;
+}
+
+
+void FmXGridPeer::elementInserted(const ContainerEvent& evt)
+{
+ SolarMutexGuard aGuard;
+
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ // take handle column into account
+ if (!pGrid || !m_xColumns.is() || pGrid->IsInColumnMove() || m_xColumns->getCount() == static_cast<sal_Int32>(pGrid->GetModelColCount()))
+ return;
+
+ Reference< XPropertySet > xNewColumn(evt.Element, css::uno::UNO_QUERY);
+ addColumnListeners(xNewColumn);
+
+ OUString aName = ::comphelper::getString(xNewColumn->getPropertyValue(FM_PROP_LABEL));
+ Any aWidth = xNewColumn->getPropertyValue(FM_PROP_WIDTH);
+ sal_Int32 nWidth = 0;
+ if (aWidth >>= nWidth)
+ nWidth = pGrid->LogicToPixel(Point(nWidth, 0), MapMode(MapUnit::Map10thMM)).X();
+
+ pGrid->AppendColumn(aName, static_cast<sal_uInt16>(nWidth), static_cast<sal_Int16>(::comphelper::getINT32(evt.Accessor)));
+
+ // now set the column
+ DbGridColumn* pCol = pGrid->GetColumns()[ ::comphelper::getINT32(evt.Accessor) ].get();
+ pCol->setModel(xNewColumn);
+
+ Any aHidden = xNewColumn->getPropertyValue(FM_PROP_HIDDEN);
+ if (::comphelper::getBOOL(aHidden))
+ pGrid->HideColumn(pCol->GetId());
+
+ FormControlFactory( m_xContext ).initializeTextFieldLineEnds( xNewColumn );
+}
+
+
+void FmXGridPeer::elementReplaced(const ContainerEvent& evt)
+{
+ SolarMutexGuard aGuard;
+
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+
+ // take handle column into account
+ if (!pGrid || !m_xColumns.is() || pGrid->IsInColumnMove())
+ return;
+
+ Reference< XPropertySet > xNewColumn(evt.Element, css::uno::UNO_QUERY);
+ Reference< XPropertySet > xOldColumn(
+ evt.ReplacedElement, css::uno::UNO_QUERY);
+
+ bool bWasEditing = pGrid->IsEditing();
+ if (bWasEditing)
+ pGrid->DeactivateCell();
+
+ pGrid->RemoveColumn(pGrid->GetColumnIdFromModelPos(static_cast<sal_uInt16>(::comphelper::getINT32(evt.Accessor))));
+
+ removeColumnListeners(xOldColumn);
+ addColumnListeners(xNewColumn);
+
+ OUString aName = ::comphelper::getString(xNewColumn->getPropertyValue(FM_PROP_LABEL));
+ Any aWidth = xNewColumn->getPropertyValue(FM_PROP_WIDTH);
+ sal_Int32 nWidth = 0;
+ if (aWidth >>= nWidth)
+ nWidth = pGrid->LogicToPixel(Point(nWidth, 0), MapMode(MapUnit::Map10thMM)).X();
+ sal_uInt16 nNewId = pGrid->AppendColumn(aName, static_cast<sal_uInt16>(nWidth), static_cast<sal_Int16>(::comphelper::getINT32(evt.Accessor)));
+ sal_uInt16 nNewPos = pGrid->GetModelColumnPos(nNewId);
+
+ // set the model of the new column
+ DbGridColumn* pCol = pGrid->GetColumns()[ nNewPos ].get();
+
+ // for initializing this grid column, we need the fields of the grid's data source
+ Reference< XColumnsSupplier > xSuppColumns;
+ CursorWrapper* pGridDataSource = pGrid->getDataSource();
+ if ( pGridDataSource )
+ xSuppColumns.set(Reference< XInterface >( *pGridDataSource ), css::uno::UNO_QUERY);
+ Reference< XNameAccess > xColumnsByName;
+ if ( xSuppColumns.is() )
+ xColumnsByName = xSuppColumns->getColumns();
+ Reference< XIndexAccess > xColumnsByIndex( xColumnsByName, UNO_QUERY );
+
+ if ( xColumnsByIndex.is() )
+ FmGridControl::InitColumnByField( pCol, xNewColumn, xColumnsByName, xColumnsByIndex );
+ else
+ // the simple version, applies when the grid is not yet connected to a data source
+ pCol->setModel(xNewColumn);
+
+ if (bWasEditing)
+ pGrid->ActivateCell();
+}
+
+
+void FmXGridPeer::elementRemoved(const ContainerEvent& evt)
+{
+ SolarMutexGuard aGuard;
+
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+
+ // take handle column into account
+ if (!pGrid || !m_xColumns.is() || pGrid->IsInColumnMove() || m_xColumns->getCount() == static_cast<sal_Int32>(pGrid->GetModelColCount()))
+ return;
+
+ pGrid->RemoveColumn(pGrid->GetColumnIdFromModelPos(static_cast<sal_uInt16>(::comphelper::getINT32(evt.Accessor))));
+
+ Reference< XPropertySet > xOldColumn(evt.Element, css::uno::UNO_QUERY);
+ removeColumnListeners(xOldColumn);
+}
+
+
+void FmXGridPeer::setProperty( const OUString& PropertyName, const Any& Value)
+{
+ SolarMutexGuard aGuard;
+
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+
+ bool bVoid = !Value.hasValue();
+
+ if ( PropertyName == FM_PROP_TEXTLINECOLOR )
+ {
+ ::Color aTextLineColor( bVoid ? COL_TRANSPARENT : ::Color(::comphelper::getINT32( Value )) );
+ if (bVoid)
+ {
+ pGrid->SetTextLineColor();
+ pGrid->GetDataWindow().SetTextLineColor();
+ }
+ else
+ {
+ pGrid->SetTextLineColor(aTextLineColor);
+ pGrid->GetDataWindow().SetTextLineColor(aTextLineColor);
+ }
+
+ // need to forward this to the columns
+ std::vector< std::unique_ptr<DbGridColumn> > const & rColumns = pGrid->GetColumns();
+ for (auto const & pLoop : rColumns)
+ {
+ FmXGridCell* pXCell = pLoop->GetCell();
+ if (pXCell)
+ {
+ if (bVoid)
+ pXCell->SetTextLineColor();
+ else
+ pXCell->SetTextLineColor(aTextLineColor);
+ }
+ }
+
+ if (isDesignMode())
+ pGrid->Invalidate();
+ }
+ else if ( PropertyName == FM_PROP_FONTEMPHASISMARK )
+ {
+ vcl::Font aGridFont = pGrid->GetControlFont();
+ sal_Int16 nValue = ::comphelper::getINT16(Value);
+ aGridFont.SetEmphasisMark( static_cast<FontEmphasisMark>(nValue) );
+ pGrid->SetControlFont( aGridFont );
+ }
+ else if ( PropertyName == FM_PROP_FONTRELIEF )
+ {
+ vcl::Font aGridFont = pGrid->GetControlFont();
+ sal_Int16 nValue = ::comphelper::getINT16(Value);
+ aGridFont.SetRelief( static_cast<FontRelief>(nValue) );
+ pGrid->SetControlFont( aGridFont );
+ }
+ else if ( PropertyName == FM_PROP_HELPURL )
+ {
+ OUString sHelpURL;
+ OSL_VERIFY( Value >>= sHelpURL );
+ INetURLObject aHID( sHelpURL );
+ if ( aHID.GetProtocol() == INetProtocol::Hid )
+ sHelpURL = aHID.GetURLPath();
+ pGrid->SetHelpId( OUStringToOString( sHelpURL, RTL_TEXTENCODING_UTF8 ) );
+ }
+ else if ( PropertyName == FM_PROP_DISPLAYSYNCHRON )
+ {
+ pGrid->setDisplaySynchron(::comphelper::getBOOL(Value));
+ }
+ else if ( PropertyName == FM_PROP_CURSORCOLOR )
+ {
+ if (bVoid)
+ pGrid->SetCursorColor(COL_TRANSPARENT);
+ else
+ pGrid->SetCursorColor( ::Color(::comphelper::getINT32(Value)));
+ if (isDesignMode())
+ pGrid->Invalidate();
+ }
+ else if ( PropertyName == FM_PROP_ALWAYSSHOWCURSOR )
+ {
+ pGrid->EnablePermanentCursor(::comphelper::getBOOL(Value));
+ if (isDesignMode())
+ pGrid->Invalidate();
+ }
+ else if ( PropertyName == FM_PROP_FONT )
+ {
+ if ( bVoid )
+ pGrid->SetControlFont( vcl::Font() );
+ else
+ {
+ css::awt::FontDescriptor aFont;
+ if (Value >>= aFont)
+ {
+ vcl::Font aNewVclFont;
+ if (aFont != ::comphelper::getDefaultFont()) // is this the default
+ aNewVclFont = ImplCreateFont( aFont );
+
+ // need to add relief and emphasis (they're stored in a VCL-Font, but not in a FontDescriptor
+ vcl::Font aOldVclFont = pGrid->GetControlFont();
+ aNewVclFont.SetRelief( aOldVclFont.GetRelief() );
+ aNewVclFont.SetEmphasisMark( aOldVclFont.GetEmphasisMark() );
+
+ // now set it ...
+ pGrid->SetControlFont( aNewVclFont );
+
+ // if our row-height property is void (which means "calculate it font-dependent") we have
+ // to adjust the control's row height
+ Reference< XPropertySet > xModelSet(getColumns(), UNO_QUERY);
+ if (xModelSet.is() && ::comphelper::hasProperty(FM_PROP_ROWHEIGHT, xModelSet))
+ {
+ Any aHeight = xModelSet->getPropertyValue(FM_PROP_ROWHEIGHT);
+ if (!aHeight.hasValue())
+ pGrid->SetDataRowHeight(0);
+ }
+
+ }
+ }
+ }
+ else if ( PropertyName == FM_PROP_BACKGROUNDCOLOR )
+ {
+ if ( bVoid )
+ {
+ pGrid->SetControlBackground();
+ }
+ else
+ {
+ ::Color aColor( ::comphelper::getINT32(Value) );
+ pGrid->SetBackground( aColor );
+ pGrid->SetControlBackground( aColor );
+ }
+ }
+ else if ( PropertyName == FM_PROP_TEXTCOLOR )
+ {
+ if ( bVoid )
+ {
+ pGrid->SetControlForeground();
+ }
+ else
+ {
+ ::Color aColor( ::comphelper::getINT32(Value) );
+ pGrid->SetTextColor( aColor );
+ pGrid->SetControlForeground( aColor );
+ }
+ }
+ else if ( PropertyName == FM_PROP_ROWHEIGHT )
+ {
+ sal_Int32 nLogHeight(0);
+ if (Value >>= nLogHeight)
+ {
+ sal_Int32 nHeight = pGrid->LogicToPixel(Point(0, nLogHeight), MapMode(MapUnit::Map10thMM)).Y();
+ // take the zoom factor into account
+ nHeight = pGrid->CalcZoom(nHeight);
+ pGrid->SetDataRowHeight(nHeight);
+ }
+ else if (bVoid)
+ pGrid->SetDataRowHeight(0);
+ }
+ else if ( PropertyName == FM_PROP_HASNAVIGATION )
+ {
+ bool bValue( true );
+ OSL_VERIFY( Value >>= bValue );
+ pGrid->EnableNavigationBar( bValue );
+ }
+ else if ( PropertyName == FM_PROP_RECORDMARKER )
+ {
+ bool bValue( true );
+ OSL_VERIFY( Value >>= bValue );
+ pGrid->EnableHandle( bValue );
+ }
+ else if ( PropertyName == FM_PROP_ENABLED )
+ {
+ bool bValue( true );
+ OSL_VERIFY( Value >>= bValue );
+
+ // In design mode, disable only the data window.
+ // Else the control cannot be configured anymore.
+ if (isDesignMode())
+ pGrid->GetDataWindow().Enable( bValue );
+ else
+ pGrid->Enable( bValue );
+ }
+ else
+ VCLXWindow::setProperty( PropertyName, Value );
+}
+
+
+Reference< XAccessibleContext > FmXGridPeer::CreateAccessibleContext()
+{
+ Reference< XAccessibleContext > xContext;
+
+ // use the AccessibleContext provided by the VCL window
+ VclPtr<vcl::Window> pGrid = GetWindow();
+ if ( pGrid )
+ {
+ Reference< XAccessible > xAcc( pGrid->GetAccessible() );
+ if ( xAcc.is() )
+ xContext = xAcc->getAccessibleContext();
+ // TODO: this has a slight conceptual problem:
+
+ // We know that the XAccessible and XAccessibleContext implementation of the browse
+ // box is the same (the class implements both interfaces), which, speaking strictly,
+ // is bad here (means when a browse box acts as UnoControl): We (the FmXGridPeer) are
+ // the XAccessible here, and the browse box should be able to provide us an XAccessibleContext,
+ // but it should _not_ be the XAccessible itself.
+ // However, as long as no client implementation uses dirty hacks such as querying an
+ // XAccessibleContext for XAccessible, this should not be a problem.
+ }
+
+ if ( !xContext.is() )
+ xContext = VCLXWindow::CreateAccessibleContext( );
+
+ return xContext;
+}
+
+
+Any FmXGridPeer::getProperty( const OUString& _rPropertyName )
+{
+ Any aProp;
+ if (GetWindow())
+ {
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ vcl::Window* pDataWindow = &pGrid->GetDataWindow();
+
+ if ( _rPropertyName == FM_PROP_NAME )
+ {
+ vcl::Font aFont = pDataWindow->GetControlFont();
+ aProp <<= ImplCreateFontDescriptor( aFont );
+ }
+ else if ( _rPropertyName == FM_PROP_TEXTCOLOR )
+ {
+ aProp <<= pDataWindow->GetControlForeground();
+ }
+ else if ( _rPropertyName == FM_PROP_BACKGROUNDCOLOR )
+ {
+ aProp <<= pDataWindow->GetControlBackground();
+ }
+ else if ( _rPropertyName == FM_PROP_ROWHEIGHT )
+ {
+ sal_Int32 nPixelHeight = pGrid->GetDataRowHeight();
+ // take the zoom factor into account
+ nPixelHeight = pGrid->CalcReverseZoom(nPixelHeight);
+ aProp <<= static_cast<sal_Int32>(pGrid->PixelToLogic(Point(0, nPixelHeight), MapMode(MapUnit::Map10thMM)).Y());
+ }
+ else if ( _rPropertyName == FM_PROP_HASNAVIGATION )
+ {
+ bool bHasNavBar = pGrid->HasNavigationBar();
+ aProp <<= bHasNavBar;
+ }
+ else if ( _rPropertyName == FM_PROP_RECORDMARKER )
+ {
+ bool bHasHandle = pGrid->HasHandle();
+ aProp <<= bHasHandle;
+ }
+ else if ( _rPropertyName == FM_PROP_ENABLED )
+ {
+ aProp <<= pDataWindow->IsEnabled();
+ }
+ else
+ aProp = VCLXWindow::getProperty( _rPropertyName );
+ }
+ return aProp;
+}
+
+
+void FmXGridPeer::dispose()
+{
+ EventObject aEvt;
+ aEvt.Source = static_cast< ::cppu::OWeakObject* >(this);
+ m_aModifyListeners.disposeAndClear(aEvt);
+ m_aUpdateListeners.disposeAndClear(aEvt);
+ m_aContainerListeners.disposeAndClear(aEvt);
+ VCLXWindow::dispose();
+
+ // release all interceptors
+ Reference< XDispatchProviderInterceptor > xInterceptor( m_xFirstDispatchInterceptor );
+ m_xFirstDispatchInterceptor.clear();
+ while ( xInterceptor.is() )
+ {
+ // tell the interceptor it has a new (means no) predecessor
+ xInterceptor->setMasterDispatchProvider( nullptr );
+
+ // ask for its successor
+ Reference< XDispatchProvider > xSlave = xInterceptor->getSlaveDispatchProvider();
+ // and give it the new (means no) successoert
+ xInterceptor->setSlaveDispatchProvider( nullptr );
+
+ // start over with the next chain element
+ xInterceptor.set(xSlave, css::uno::UNO_QUERY);
+ }
+
+ DisConnectFromDispatcher();
+ setRowSet(Reference< XRowSet > ());
+}
+
+// XContainer
+
+void FmXGridPeer::addContainerListener(const Reference< XContainerListener >& l)
+{
+ m_aContainerListeners.addInterface( l );
+}
+
+void FmXGridPeer::removeContainerListener(const Reference< XContainerListener >& l)
+{
+ m_aContainerListeners.removeInterface( l );
+}
+
+// css::data::XDatabaseCursorSupplier
+
+void FmXGridPeer::startCursorListening()
+{
+ if (!m_nCursorListening)
+ {
+ if (m_xCursor.is())
+ m_xCursor->addRowSetListener(this);
+
+ Reference< XReset > xReset(m_xCursor, UNO_QUERY);
+ if (xReset.is())
+ xReset->addResetListener(this);
+
+ // register all listeners
+ Reference< XPropertySet > xSet(m_xCursor, UNO_QUERY);
+ if (xSet.is())
+ {
+ xSet->addPropertyChangeListener(FM_PROP_ISMODIFIED, this);
+ xSet->addPropertyChangeListener(FM_PROP_ROWCOUNT, this);
+ }
+ }
+ m_nCursorListening++;
+}
+
+
+void FmXGridPeer::stopCursorListening()
+{
+ if (!--m_nCursorListening)
+ {
+ if (m_xCursor.is())
+ m_xCursor->removeRowSetListener(this);
+
+ Reference< XReset > xReset(m_xCursor, UNO_QUERY);
+ if (xReset.is())
+ xReset->removeResetListener(this);
+
+ Reference< XPropertySet > xSet(m_xCursor, UNO_QUERY);
+ if (xSet.is())
+ {
+ xSet->removePropertyChangeListener(FM_PROP_ISMODIFIED, this);
+ xSet->removePropertyChangeListener(FM_PROP_ROWCOUNT, this);
+ }
+ }
+}
+
+
+void FmXGridPeer::updateGrid(const Reference< XRowSet >& _rxCursor)
+{
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ if (pGrid)
+ pGrid->setDataSource(_rxCursor);
+}
+
+
+Reference< XRowSet > FmXGridPeer::getRowSet()
+{
+ return m_xCursor;
+}
+
+
+void FmXGridPeer::setRowSet(const Reference< XRowSet >& _rDatabaseCursor)
+{
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ if (!pGrid || !m_xColumns.is() || !m_xColumns->getCount())
+ return;
+ // unregister all listeners
+ if (m_xCursor.is())
+ {
+ Reference< XLoadable > xLoadable(m_xCursor, UNO_QUERY);
+ // only if the form is loaded we set the rowset
+ if (xLoadable.is())
+ {
+ stopCursorListening();
+ xLoadable->removeLoadListener(this);
+ }
+ }
+
+ m_xCursor = _rDatabaseCursor;
+
+ if (pGrid)
+ {
+ Reference< XLoadable > xLoadable(m_xCursor, UNO_QUERY);
+ // only if the form is loaded we set the rowset
+ if (xLoadable.is() && xLoadable->isLoaded())
+ pGrid->setDataSource(m_xCursor);
+ else
+ pGrid->setDataSource(Reference< XRowSet > ());
+
+ if (xLoadable.is())
+ {
+ startCursorListening();
+ xLoadable->addLoadListener(this);
+ }
+ }
+}
+
+
+void SAL_CALL FmXGridPeer::addGridControlListener( const Reference< XGridControlListener >& _listener )
+{
+ m_aGridControlListeners.addInterface( _listener );
+}
+
+
+void SAL_CALL FmXGridPeer::removeGridControlListener( const Reference< XGridControlListener >& _listener )
+{
+ m_aGridControlListeners.removeInterface( _listener );
+}
+
+
+sal_Int16 FmXGridPeer::getCurrentColumnPosition()
+{
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ return pGrid ? pGrid->GetViewColumnPos(pGrid->GetCurColumnId()) : -1;
+}
+
+
+void FmXGridPeer::setCurrentColumnPosition(sal_Int16 nPos)
+{
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ if (pGrid)
+ pGrid->GoToColumnId(pGrid->GetColumnIdFromViewPos(nPos));
+}
+
+
+void FmXGridPeer::selectionChanged(const EventObject& evt)
+{
+ SolarMutexGuard aGuard;
+
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ if (pGrid)
+ {
+ Reference< css::view::XSelectionSupplier > xSelSupplier(evt.Source, UNO_QUERY);
+ Any aSelection = xSelSupplier->getSelection();
+ DBG_ASSERT(aSelection.getValueType().getTypeClass() == TypeClass_INTERFACE, "FmXGridPeer::selectionChanged : invalid selection !");
+ Reference< XPropertySet > xSelection;
+ aSelection >>= xSelection;
+ if (xSelection.is())
+ {
+ Reference< XPropertySet > xCol;
+ sal_Int32 i = 0;
+ sal_Int32 nColCount = m_xColumns->getCount();
+
+ for (; i < nColCount; ++i)
+ {
+ m_xColumns->getByIndex(i) >>= xCol;
+ if ( xCol == xSelection )
+ {
+ pGrid->markColumn(pGrid->GetColumnIdFromModelPos(static_cast<sal_uInt16>(i)));
+ break;
+ }
+ }
+ // The columns have to be 1-based for the VCL control.
+ // If necessary, pass on the selection to the VCL control
+ if ( i != pGrid->GetSelectedColumn() )
+ { // (if this does not take effect, the selectionChanged was implicitly triggered by the control itself)
+ if ( i < nColCount )
+ {
+ pGrid->SelectColumnPos(pGrid->GetViewColumnPos(pGrid->GetColumnIdFromModelPos( static_cast<sal_uInt16>(i) )) + 1);
+ // SelectColumnPos has led to an implicit ActivateCell again
+ if (pGrid->IsEditing())
+ pGrid->DeactivateCell();
+ }
+ else
+ pGrid->SetNoSelection();
+ }
+ }
+ else
+ pGrid->markColumn(USHRT_MAX);
+ }
+}
+
+// XElementAccess
+
+sal_Bool FmXGridPeer::hasElements()
+{
+ return getCount() != 0;
+}
+
+
+Type SAL_CALL FmXGridPeer::getElementType( )
+{
+ return cppu::UnoType<css::awt::XControl>::get();
+}
+
+// XEnumerationAccess
+
+Reference< XEnumeration > FmXGridPeer::createEnumeration()
+{
+ return new ::comphelper::OEnumerationByIndex(this);
+}
+
+// XIndexAccess
+
+sal_Int32 FmXGridPeer::getCount()
+{
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ if (pGrid)
+ return pGrid->GetViewColCount();
+ else
+ return 0;
+}
+
+
+Any FmXGridPeer::getByIndex(sal_Int32 _nIndex)
+{
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ if (_nIndex < 0 ||
+ _nIndex >= getCount() || !pGrid)
+ throw IndexOutOfBoundsException();
+
+ Any aElement;
+ // get the columnid
+ sal_uInt16 nId = pGrid->GetColumnIdFromViewPos(static_cast<sal_uInt16>(_nIndex));
+ // get the list position
+ sal_uInt16 nPos = pGrid->GetModelColumnPos(nId);
+
+ if ( nPos == GRID_COLUMN_NOT_FOUND )
+ return aElement;
+
+ DbGridColumn* pCol = pGrid->GetColumns()[ nPos ].get();
+ Reference< css::awt::XControl > xControl(pCol->GetCell());
+ aElement <<= xControl;
+
+ return aElement;
+}
+
+// css::util::XModeSelector
+
+void FmXGridPeer::setMode(const OUString& Mode)
+{
+ if (!supportsMode(Mode))
+ throw NoSupportException();
+
+ if (Mode == m_aMode)
+ return;
+
+ m_aMode = Mode;
+
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ if ( Mode == "FilterMode" )
+ pGrid->SetFilterMode(true);
+ else
+ {
+ pGrid->SetFilterMode(false);
+ pGrid->setDataSource(m_xCursor);
+ }
+}
+
+
+OUString FmXGridPeer::getMode()
+{
+ return m_aMode;
+}
+
+
+css::uno::Sequence<OUString> FmXGridPeer::getSupportedModes()
+{
+ static css::uno::Sequence<OUString> const aModes
+ {
+ "DataMode",
+ "FilterMode"
+ };
+ return aModes;
+}
+
+
+sal_Bool FmXGridPeer::supportsMode(const OUString& Mode)
+{
+ css::uno::Sequence<OUString> aModes(getSupportedModes());
+ return comphelper::findValue(aModes, Mode) != -1;
+}
+
+
+void FmXGridPeer::columnVisible(DbGridColumn const * pColumn)
+{
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+
+ sal_Int32 _nIndex = pGrid->GetModelColumnPos(pColumn->GetId());
+ Reference< css::awt::XControl > xControl(pColumn->GetCell());
+ ContainerEvent aEvt;
+ aEvt.Source = static_cast<XContainer*>(this);
+ aEvt.Accessor <<= _nIndex;
+ aEvt.Element <<= xControl;
+
+ m_aContainerListeners.notifyEach( &XContainerListener::elementInserted, aEvt );
+}
+
+
+void FmXGridPeer::columnHidden(DbGridColumn const * pColumn)
+{
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+
+ sal_Int32 _nIndex = pGrid->GetModelColumnPos(pColumn->GetId());
+ Reference< css::awt::XControl > xControl(pColumn->GetCell());
+ ContainerEvent aEvt;
+ aEvt.Source = static_cast<XContainer*>(this);
+ aEvt.Accessor <<= _nIndex;
+ aEvt.Element <<= xControl;
+
+ m_aContainerListeners.notifyEach( &XContainerListener::elementRemoved, aEvt );
+}
+
+
+void FmXGridPeer::draw( sal_Int32 x, sal_Int32 y )
+{
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ EditBrowseBoxFlags nOldFlags = pGrid->GetBrowserFlags();
+ pGrid->SetBrowserFlags(nOldFlags | EditBrowseBoxFlags::NO_HANDLE_COLUMN_CONTENT);
+
+ VCLXWindow::draw(x, y);
+
+ pGrid->SetBrowserFlags(nOldFlags);
+}
+
+
+Reference< css::frame::XDispatch > FmXGridPeer::queryDispatch(const css::util::URL& aURL, const OUString& aTargetFrameName, sal_Int32 nSearchFlags)
+{
+ Reference< css::frame::XDispatch > xResult;
+
+ // first ask our interceptor chain
+ if (m_xFirstDispatchInterceptor.is() && !m_bInterceptingDispatch)
+ {
+ m_bInterceptingDispatch = true;
+ // safety against recursion : as we are master of the first chain element and slave of the last one we would
+ // have an infinite loop without this if no dispatcher can fulfill the request
+ xResult = m_xFirstDispatchInterceptor->queryDispatch(aURL, aTargetFrameName, nSearchFlags);
+ m_bInterceptingDispatch = false;
+ }
+
+ // then ask ourself : we don't have any dispatches
+ return xResult;
+}
+
+
+Sequence< Reference< css::frame::XDispatch > > FmXGridPeer::queryDispatches(const Sequence< css::frame::DispatchDescriptor>& aDescripts)
+{
+ if (m_xFirstDispatchInterceptor.is())
+ return m_xFirstDispatchInterceptor->queryDispatches(aDescripts);
+
+ // then ask ourself : we don't have any dispatches
+ return Sequence< Reference< css::frame::XDispatch > >();
+}
+
+
+void FmXGridPeer::registerDispatchProviderInterceptor(const Reference< css::frame::XDispatchProviderInterceptor >& _xInterceptor)
+{
+ if (_xInterceptor.is())
+ {
+ if (m_xFirstDispatchInterceptor.is())
+ {
+ // there is already an interceptor; the new one will become its master
+ _xInterceptor->setSlaveDispatchProvider(m_xFirstDispatchInterceptor);
+ m_xFirstDispatchInterceptor->setMasterDispatchProvider(m_xFirstDispatchInterceptor);
+ }
+ else
+ {
+ // it is the first interceptor; set ourself as slave
+ _xInterceptor->setSlaveDispatchProvider(static_cast<css::frame::XDispatchProvider*>(this));
+ }
+
+ // we are the master of the chain's first interceptor
+ m_xFirstDispatchInterceptor = _xInterceptor;
+ m_xFirstDispatchInterceptor->setMasterDispatchProvider(static_cast<css::frame::XDispatchProvider*>(this));
+
+ // we have a new interceptor and we're alive ?
+ if (!isDesignMode())
+ // -> check for new dispatchers
+ UpdateDispatches();
+ }
+}
+
+
+void FmXGridPeer::releaseDispatchProviderInterceptor(const Reference< css::frame::XDispatchProviderInterceptor >& _xInterceptor)
+{
+ if (!_xInterceptor.is())
+ return;
+
+ Reference< css::frame::XDispatchProviderInterceptor > xChainWalk(m_xFirstDispatchInterceptor);
+
+ if (m_xFirstDispatchInterceptor == _xInterceptor)
+ { // our chain will have a new first element
+ Reference< css::frame::XDispatchProviderInterceptor > xSlave(m_xFirstDispatchInterceptor->getSlaveDispatchProvider(), UNO_QUERY);
+ m_xFirstDispatchInterceptor = xSlave;
+ }
+ // do this before removing the interceptor from the chain as we won't know it's slave afterwards)
+
+ while (xChainWalk.is())
+ {
+ // walk along the chain of interceptors and look for the interceptor that has to be removed
+ Reference< css::frame::XDispatchProviderInterceptor > xSlave(xChainWalk->getSlaveDispatchProvider(), UNO_QUERY);
+
+ if (xChainWalk == _xInterceptor)
+ {
+ // old master may be an interceptor too
+ Reference< css::frame::XDispatchProviderInterceptor > xMaster(xChainWalk->getMasterDispatchProvider(), UNO_QUERY);
+
+ // unchain the interceptor that has to be removed
+ xChainWalk->setSlaveDispatchProvider(Reference< css::frame::XDispatchProvider > ());
+ xChainWalk->setMasterDispatchProvider(Reference< css::frame::XDispatchProvider > ());
+
+ // reconnect the chain
+ if (xMaster.is())
+ {
+ if (xSlave.is())
+ xMaster->setSlaveDispatchProvider(Reference< css::frame::XDispatchProvider >::query(xSlave));
+ else
+ // it's the first interceptor of the chain, set ourself as slave
+ xMaster->setSlaveDispatchProvider(static_cast<css::frame::XDispatchProvider*>(this));
+ }
+ else
+ {
+ // the chain's first element was removed, set ourself as new master of the second one
+ if (xSlave.is())
+ xSlave->setMasterDispatchProvider(static_cast<css::frame::XDispatchProvider*>(this));
+ }
+ }
+
+ xChainWalk = xSlave;
+ }
+ // our interceptor chain has changed and we're alive ?
+ if (!isDesignMode())
+ // -> check the dispatchers
+ UpdateDispatches();
+}
+
+
+void FmXGridPeer::statusChanged(const css::frame::FeatureStateEvent& Event)
+{
+ DBG_ASSERT(m_pStateCache, "FmXGridPeer::statusChanged : invalid call !");
+ DBG_ASSERT(m_pDispatchers, "FmXGridPeer::statusChanged : invalid call !");
+
+ Sequence< css::util::URL>& aUrls = getSupportedURLs();
+
+ const std::vector<DbGridControlNavigationBarState>& aSlots = getSupportedGridSlots();
+
+ auto pUrl = std::find_if(aUrls.begin(), aUrls.end(),
+ [&Event](const css::util::URL& rUrl) { return rUrl.Main == Event.FeatureURL.Main; });
+ if (pUrl != aUrls.end())
+ {
+ auto i = static_cast<sal_uInt32>(std::distance(aUrls.begin(), pUrl));
+ DBG_ASSERT(m_pDispatchers[i] == Event.Source, "FmXGridPeer::statusChanged : the event source is a little bit suspect !");
+ m_pStateCache[i] = Event.IsEnabled;
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ if (aSlots[i] != DbGridControlNavigationBarState::Undo)
+ pGrid->GetNavigationBar().InvalidateState(aSlots[i]);
+ }
+ DBG_ASSERT(pUrl != aUrls.end(), "FmXGridPeer::statusChanged : got a call for an unknown url !");
+}
+
+
+sal_Bool FmXGridPeer::approveReset(const EventObject& /*rEvent*/)
+{
+ return true;
+}
+
+
+sal_Bool SAL_CALL FmXGridPeer::select( const Any& _rSelection )
+{
+ Sequence< Any > aBookmarks;
+ if ( !( _rSelection >>= aBookmarks ) )
+ throw IllegalArgumentException();
+
+ return GetAs< FmGridControl >()->selectBookmarks(aBookmarks);
+
+ // TODO:
+ // speaking strictly, we would have to adjust our model, as our ColumnSelection may have changed.
+ // Our model is a XSelectionSupplier, too, it handles the selection of single columns.
+ // This is somewhat strange, as selection should be a view (not a model) aspect.
+ // So for a clean solution, we should handle column selection ourself, and the model shouldn't
+ // deal with selection at all.
+}
+
+
+Any SAL_CALL FmXGridPeer::getSelection( )
+{
+ VclPtr< FmGridControl > pVclControl = GetAs< FmGridControl >();
+ Sequence< Any > aSelectionBookmarks = pVclControl->getSelectionBookmarks();
+ return makeAny(aSelectionBookmarks);
+}
+
+
+void SAL_CALL FmXGridPeer::addSelectionChangeListener( const Reference< XSelectionChangeListener >& _rxListener )
+{
+ m_aSelectionListeners.addInterface( _rxListener );
+}
+
+
+void SAL_CALL FmXGridPeer::removeSelectionChangeListener( const Reference< XSelectionChangeListener >& _rxListener )
+{
+ m_aSelectionListeners.removeInterface( _rxListener );
+}
+
+
+void FmXGridPeer::resetted(const EventObject& rEvent)
+{
+ if (m_xColumns == rEvent.Source)
+ { // my model was reset -> refresh the grid content
+ SolarMutexGuard aGuard;
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ if (!pGrid)
+ return;
+ pGrid->resetCurrentRow();
+ }
+ // if the cursor fired a reset event we seem to be on the insert row
+ else if (m_xCursor == rEvent.Source)
+ {
+ SolarMutexGuard aGuard;
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ if (pGrid && pGrid->IsOpen())
+ pGrid->positioned();
+ }
+}
+
+
+const std::vector<DbGridControlNavigationBarState>& FmXGridPeer::getSupportedGridSlots()
+{
+ static const std::vector<DbGridControlNavigationBarState> aSupported {
+ DbGridControlNavigationBarState::First,
+ DbGridControlNavigationBarState::Prev,
+ DbGridControlNavigationBarState::Next,
+ DbGridControlNavigationBarState::Last,
+ DbGridControlNavigationBarState::New,
+ DbGridControlNavigationBarState::Undo
+ };
+ return aSupported;
+}
+
+
+Sequence< css::util::URL>& FmXGridPeer::getSupportedURLs()
+{
+ static Sequence< css::util::URL> aSupported = [&]()
+ {
+ static const char* sSupported[] = {
+ FMURL_RECORD_MOVEFIRST,
+ FMURL_RECORD_MOVEPREV,
+ FMURL_RECORD_MOVENEXT,
+ FMURL_RECORD_MOVELAST,
+ FMURL_RECORD_MOVETONEW,
+ FMURL_RECORD_UNDO
+ };
+ Sequence< css::util::URL> tmp(SAL_N_ELEMENTS(sSupported));
+ css::util::URL* pSupported = tmp.getArray();
+
+ for ( sal_Int32 i = 0; i < tmp.getLength(); ++i, ++pSupported)
+ pSupported->Complete = OUString::createFromAscii(sSupported[i]);
+
+ // let a css::util::URL-transformer normalize the URLs
+ Reference< css::util::XURLTransformer > xTransformer(
+ util::URLTransformer::create(::comphelper::getProcessComponentContext()) );
+ for (css::util::URL & rURL : tmp)
+ xTransformer->parseStrict(rURL);
+ return tmp;
+ }();
+
+ return aSupported;
+}
+
+
+void FmXGridPeer::UpdateDispatches()
+{
+ if (!m_pStateCache)
+ { // we don't have any dispatchers yet -> do the initial connect
+ ConnectToDispatcher();
+ return;
+ }
+
+ sal_uInt16 nDispatchersGot = 0;
+ const Sequence< css::util::URL>& aSupportedURLs = getSupportedURLs();
+ const css::util::URL* pSupportedURLs = aSupportedURLs.getConstArray();
+ Reference< css::frame::XDispatch > xNewDispatch;
+ for (sal_Int32 i=0; i<aSupportedURLs.getLength(); ++i, ++pSupportedURLs)
+ {
+ xNewDispatch = queryDispatch(*pSupportedURLs, OUString(), 0);
+ if (xNewDispatch != m_pDispatchers[i])
+ {
+ if (m_pDispatchers[i].is())
+ m_pDispatchers[i]->removeStatusListener(static_cast<css::frame::XStatusListener*>(this), *pSupportedURLs);
+ m_pDispatchers[i] = xNewDispatch;
+ if (m_pDispatchers[i].is())
+ m_pDispatchers[i]->addStatusListener(static_cast<css::frame::XStatusListener*>(this), *pSupportedURLs);
+ }
+ if (m_pDispatchers[i].is())
+ ++nDispatchersGot;
+ }
+
+ if (!nDispatchersGot)
+ {
+ m_pStateCache.reset();
+ m_pDispatchers.reset();
+ }
+}
+
+
+void FmXGridPeer::ConnectToDispatcher()
+{
+ DBG_ASSERT((m_pStateCache != nullptr) == (m_pDispatchers != nullptr), "FmXGridPeer::ConnectToDispatcher : inconsistent !");
+ if (m_pStateCache)
+ { // already connected -> just do an update
+ UpdateDispatches();
+ return;
+ }
+
+ const Sequence< css::util::URL>& aSupportedURLs = getSupportedURLs();
+
+ // _before_ adding the status listeners (as the add should result in a statusChanged-call) !
+ m_pStateCache.reset(new bool[aSupportedURLs.getLength()]);
+ m_pDispatchers.reset(new Reference< css::frame::XDispatch > [aSupportedURLs.getLength()]);
+
+ sal_uInt16 nDispatchersGot = 0;
+ const css::util::URL* pSupportedURLs = aSupportedURLs.getConstArray();
+ for (sal_Int32 i=0; i<aSupportedURLs.getLength(); ++i, ++pSupportedURLs)
+ {
+ m_pStateCache[i] = false;
+ m_pDispatchers[i] = queryDispatch(*pSupportedURLs, OUString(), 0);
+ if (m_pDispatchers[i].is())
+ {
+ m_pDispatchers[i]->addStatusListener(static_cast<css::frame::XStatusListener*>(this), *pSupportedURLs);
+ ++nDispatchersGot;
+ }
+ }
+
+ if (!nDispatchersGot)
+ {
+ m_pStateCache.reset();
+ m_pDispatchers.reset();
+ }
+}
+
+
+void FmXGridPeer::DisConnectFromDispatcher()
+{
+ if (!m_pStateCache || !m_pDispatchers)
+ return;
+ // we're not connected
+
+ const Sequence< css::util::URL>& aSupportedURLs = getSupportedURLs();
+ const css::util::URL* pSupportedURLs = aSupportedURLs.getConstArray();
+ for (sal_Int32 i=0; i<aSupportedURLs.getLength(); ++i, ++pSupportedURLs)
+ {
+ if (m_pDispatchers[i].is())
+ m_pDispatchers[i]->removeStatusListener(static_cast<css::frame::XStatusListener*>(this), *pSupportedURLs);
+ }
+
+ m_pStateCache.reset();
+ m_pDispatchers.reset();
+}
+
+
+IMPL_LINK(FmXGridPeer, OnQueryGridSlotState, DbGridControlNavigationBarState, nSlot, int)
+{
+ if (!m_pStateCache)
+ return -1; // unspecified
+
+ // search the given slot with our supported sequence
+ const std::vector<DbGridControlNavigationBarState>& aSupported = getSupportedGridSlots();
+ for (size_t i=0; i<aSupported.size(); ++i)
+ {
+ if (aSupported[i] == nSlot)
+ {
+ if (!m_pDispatchers[i].is())
+ return -1; // nothing known about this slot
+ else
+ return m_pStateCache[i] ? 1 : 0;
+ }
+ }
+
+ return -1;
+}
+
+
+IMPL_LINK(FmXGridPeer, OnExecuteGridSlot, DbGridControlNavigationBarState, nSlot, bool)
+{
+ if (!m_pDispatchers)
+ return false; // not handled
+
+ Sequence< css::util::URL>& aUrls = getSupportedURLs();
+ const css::util::URL* pUrls = aUrls.getConstArray();
+
+ const std::vector<DbGridControlNavigationBarState>& aSlots = getSupportedGridSlots();
+
+ DBG_ASSERT(static_cast<sal_Int32>(aSlots.size()) == aUrls.getLength(), "FmXGridPeer::OnExecuteGridSlot : inconsistent data returned by getSupportedURLs/getSupportedGridSlots!");
+
+ for (size_t i=0; i<aSlots.size(); ++i, ++pUrls)
+ {
+ if (aSlots[i] == nSlot)
+ {
+ if (m_pDispatchers[i].is())
+ {
+ // commit any changes done so far, if it's not the undoRecord URL
+ if ( pUrls->Complete == FMURL_RECORD_UNDO || commit() )
+ m_pDispatchers[i]->dispatch(*pUrls, Sequence< PropertyValue>());
+
+ return true; // handled
+ }
+ }
+ }
+
+ return false; // not handled
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/fmcomp/gridcell.cxx b/svx/source/fmcomp/gridcell.cxx
new file mode 100644
index 000000000..cc02e1a3a
--- /dev/null
+++ b/svx/source/fmcomp/gridcell.cxx
@@ -0,0 +1,4634 @@
+/* -*- 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 <sal/log.hxx>
+#include <fmprop.hxx>
+#include <svx/strings.hrc>
+#include <svx/fmtools.hxx>
+#include <gridcell.hxx>
+#include <gridcols.hxx>
+#include <sdbdatacolumn.hxx>
+
+#include <com/sun/star/awt/LineEndFormat.hpp>
+#include <com/sun/star/awt/MouseWheelBehavior.hpp>
+#include <com/sun/star/awt/VisualEffect.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/form/XBoundComponent.hpp>
+#include <com/sun/star/script/XEventAttacherManager.hpp>
+#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
+#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
+#include <com/sun/star/sdbc/DataType.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/sdbc/XRowSet.hpp>
+#include <com/sun/star/sdbc/XStatement.hpp>
+#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
+#include <com/sun/star/util/XNumberFormatter.hpp>
+#include <com/sun/star/util/Time.hpp>
+#include <com/sun/star/util/Date.hpp>
+
+#include <comphelper/numbers.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/string.hxx>
+#include <comphelper/types.hxx>
+#include <connectivity/formattedcolumnvalue.hxx>
+#include <i18nlangtag/lang.h>
+
+#include <rtl/math.hxx>
+#include <svtools/calendar.hxx>
+#include <vcl/button.hxx>
+#include <vcl/fmtfield.hxx>
+#include <svl/numuno.hxx>
+#include <svl/zforlist.hxx>
+#include <svx/dialmgr.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <vcl/longcurr.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <connectivity/dbtools.hxx>
+#include <connectivity/dbconversion.hxx>
+#include <connectivity/sqlnode.hxx>
+
+using namespace ::connectivity;
+using namespace ::svxform;
+using namespace ::comphelper;
+using namespace ::svt;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::sdbcx;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::form;
+using namespace ::dbtools::DBTypeConversion;
+using namespace ::dbtools;
+
+using ::com::sun::star::util::XNumberFormatter;
+
+const char INVALIDTEXT[] = "###";
+const char OBJECTTEXT[] = "<OBJECT>";
+
+
+//= helper
+
+namespace
+{
+ LineEnd getModelLineEndSetting( const Reference< XPropertySet >& _rxModel )
+ {
+ LineEnd eFormat = LINEEND_LF;
+
+ try
+ {
+ sal_Int16 nLineEndFormat = awt::LineEndFormat::LINE_FEED;
+
+ Reference< XPropertySetInfo > xPSI;
+ if ( _rxModel.is() )
+ xPSI = _rxModel->getPropertySetInfo();
+
+ OSL_ENSURE( xPSI.is(), "getModelLineEndSetting: invalid column model!" );
+ if ( xPSI.is() && xPSI->hasPropertyByName( FM_PROP_LINEENDFORMAT ) )
+ {
+ OSL_VERIFY( _rxModel->getPropertyValue( FM_PROP_LINEENDFORMAT ) >>= nLineEndFormat );
+
+ switch ( nLineEndFormat )
+ {
+ case awt::LineEndFormat::CARRIAGE_RETURN: eFormat = LINEEND_CR; break;
+ case awt::LineEndFormat::LINE_FEED: eFormat = LINEEND_LF; break;
+ case awt::LineEndFormat::CARRIAGE_RETURN_LINE_FEED: eFormat = LINEEND_CRLF; break;
+ default:
+ OSL_FAIL( "getModelLineEndSetting: what's this?" );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "getModelLineEndSetting" );
+ }
+ return eFormat;
+ }
+}
+
+
+//= DbGridColumn
+
+
+CellControllerRef DbGridColumn::s_xEmptyController;
+
+
+void DbGridColumn::CreateControl(sal_Int32 _nFieldPos, const Reference< css::beans::XPropertySet >& xField, sal_Int32 nTypeId)
+{
+ Clear();
+
+ m_nTypeId = static_cast<sal_Int16>(nTypeId);
+ if (xField != m_xField)
+ {
+ // initial setting
+ m_xField = xField;
+ xField->getPropertyValue(FM_PROP_FORMATKEY) >>= m_nFormatKey;
+ m_nFieldPos = static_cast<sal_Int16>(_nFieldPos);
+ m_bReadOnly = ::comphelper::getBOOL(xField->getPropertyValue(FM_PROP_ISREADONLY));
+ m_bAutoValue = ::comphelper::getBOOL(xField->getPropertyValue(FM_PROP_AUTOINCREMENT));
+ m_nFieldType = static_cast<sal_Int16>(::comphelper::getINT32(xField->getPropertyValue(FM_PROP_FIELDTYPE)));
+
+ switch (m_nFieldType)
+ {
+ case DataType::DATE:
+ case DataType::TIME:
+ case DataType::TIMESTAMP:
+ case DataType::BIT:
+ case DataType::BOOLEAN:
+ case DataType::TINYINT:
+ case DataType::SMALLINT:
+ case DataType::INTEGER:
+ case DataType::BIGINT:
+ case DataType::FLOAT:
+ case DataType::REAL:
+ case DataType::DOUBLE:
+ case DataType::NUMERIC:
+ case DataType::DECIMAL:
+ m_nAlign = css::awt::TextAlign::RIGHT;
+ m_bNumeric = true;
+ break;
+ default:
+ m_nAlign = css::awt::TextAlign::LEFT;
+ break;
+ }
+ }
+
+ std::unique_ptr<DbCellControl> pCellControl;
+ if (m_rParent.IsFilterMode())
+ {
+ pCellControl.reset(new DbFilterField(m_rParent.getContext(),*this));
+ }
+ else
+ {
+
+ switch (nTypeId)
+ {
+ case TYPE_CHECKBOX: pCellControl.reset(new DbCheckBox(*this)); break;
+ case TYPE_COMBOBOX: pCellControl.reset(new DbComboBox(*this)); break;
+ case TYPE_CURRENCYFIELD: pCellControl.reset(new DbCurrencyField(*this)); break;
+ case TYPE_DATEFIELD: pCellControl.reset(new DbDateField(*this)); break;
+ case TYPE_LISTBOX: pCellControl.reset(new DbListBox(*this)); break;
+ case TYPE_NUMERICFIELD: pCellControl.reset(new DbNumericField(*this)); break;
+ case TYPE_PATTERNFIELD: pCellControl.reset(new DbPatternField( *this, m_rParent.getContext() )); break;
+ case TYPE_TEXTFIELD: pCellControl.reset(new DbTextField(*this)); break;
+ case TYPE_TIMEFIELD: pCellControl.reset(new DbTimeField(*this)); break;
+ case TYPE_FORMATTEDFIELD: pCellControl.reset(new DbFormattedField(*this)); break;
+ default:
+ OSL_FAIL("DbGridColumn::CreateControl: Unknown Column");
+ return;
+ }
+
+ }
+ Reference< XRowSet > xCur;
+ if (m_rParent.getDataSource())
+ xCur.set(Reference< XInterface >(*m_rParent.getDataSource()), UNO_QUERY);
+ // TODO : the cursor wrapper should use an XRowSet interface, too
+
+ pCellControl->Init( m_rParent.GetDataWindow(), xCur );
+
+ // now create the control wrapper
+ auto pTempCellControl = pCellControl.get();
+ if (m_rParent.IsFilterMode())
+ m_pCell = new FmXFilterCell(this, std::unique_ptr<DbFilterField>(static_cast<DbFilterField*>(pCellControl.release())));
+ else
+ {
+ switch (nTypeId)
+ {
+ case TYPE_CHECKBOX: m_pCell = new FmXCheckBoxCell( this, std::move(pCellControl) ); break;
+ case TYPE_LISTBOX: m_pCell = new FmXListBoxCell( this, std::move(pCellControl) ); break;
+ case TYPE_COMBOBOX: m_pCell = new FmXComboBoxCell( this, std::move(pCellControl) ); break;
+ default:
+ m_pCell = new FmXEditCell( this, std::move(pCellControl) );
+ }
+ }
+ m_pCell->init();
+
+ impl_toggleScriptManager_nothrow( true );
+
+ // only if we use have a bound field, we use a controller for displaying the
+ // window in the grid
+ if (m_xField.is())
+ m_xController = pTempCellControl->CreateController();
+}
+
+
+void DbGridColumn::impl_toggleScriptManager_nothrow( bool _bAttach )
+{
+ try
+ {
+ Reference< container::XChild > xChild( m_xModel, UNO_QUERY_THROW );
+ Reference< script::XEventAttacherManager > xManager( xChild->getParent(), UNO_QUERY_THROW );
+ Reference< container::XIndexAccess > xContainer( xChild->getParent(), UNO_QUERY_THROW );
+
+ sal_Int32 nIndexInParent( getElementPos( xContainer, m_xModel ) );
+
+ Reference< XInterface > xCellInterface( *m_pCell, UNO_QUERY );
+ if ( _bAttach )
+ xManager->attach( nIndexInParent, xCellInterface, makeAny( xCellInterface ) );
+ else
+ xManager->detach( nIndexInParent, xCellInterface );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+void DbGridColumn::UpdateFromField(const DbGridRow* pRow, const Reference< XNumberFormatter >& xFormatter)
+{
+ if (FmXFilterCell* pCell = dynamic_cast<FmXFilterCell*>(m_pCell.get()))
+ pCell->Update();
+ else if (pRow && pRow->IsValid() && m_nFieldPos >= 0 && m_pCell.is() && pRow->HasField(m_nFieldPos))
+ {
+ dynamic_cast<FmXDataCell&>(*m_pCell).UpdateFromField( pRow->GetField( m_nFieldPos ).getColumn(), xFormatter );
+ }
+}
+
+bool DbGridColumn::Commit()
+{
+ bool bResult = true;
+ if (!m_bInSave && m_pCell.is())
+ {
+ m_bInSave = true;
+ bResult = m_pCell->Commit();
+
+ // store the data into the model
+ FmXDataCell* pDataCell = dynamic_cast<FmXDataCell*>( m_pCell.get() );
+ if (bResult && pDataCell)
+ {
+ Reference< css::form::XBoundComponent > xComp(m_xModel, UNO_QUERY);
+ if (xComp.is())
+ bResult = xComp->commit();
+ }
+ m_bInSave = false;
+ }
+ return bResult;
+}
+
+
+DbGridColumn::~DbGridColumn()
+{
+ Clear();
+}
+
+
+void DbGridColumn::setModel(const css::uno::Reference< css::beans::XPropertySet >& _xModel)
+{
+ if ( m_pCell.is() )
+ impl_toggleScriptManager_nothrow( false );
+
+ m_xModel = _xModel;
+
+ if ( m_pCell.is() )
+ impl_toggleScriptManager_nothrow( true );
+}
+
+
+void DbGridColumn::Clear()
+{
+ if ( m_pCell.is() )
+ {
+ impl_toggleScriptManager_nothrow( false );
+
+ m_pCell->dispose();
+ m_pCell.clear();
+ }
+
+ m_xController = nullptr;
+ m_xField = nullptr;
+
+ m_nFormatKey = 0;
+ m_nFieldPos = -1;
+ m_bReadOnly = true;
+ m_bAutoValue = false;
+ m_nFieldType = DataType::OTHER;
+}
+
+
+sal_Int16 DbGridColumn::SetAlignment(sal_Int16 _nAlign)
+{
+ if (_nAlign == -1)
+ { // 'Standard'
+ if (m_xField.is())
+ {
+ sal_Int32 nType = 0;
+ m_xField->getPropertyValue(FM_PROP_FIELDTYPE) >>= nType;
+
+ switch (nType)
+ {
+ case DataType::NUMERIC:
+ case DataType::DECIMAL:
+ case DataType::DOUBLE:
+ case DataType::REAL:
+ case DataType::BIGINT:
+ case DataType::INTEGER:
+ case DataType::SMALLINT:
+ case DataType::TINYINT:
+ case DataType::DATE:
+ case DataType::TIME:
+ case DataType::TIMESTAMP:
+ _nAlign = css::awt::TextAlign::RIGHT;
+ break;
+ case DataType::BIT:
+ case DataType::BOOLEAN:
+ _nAlign = css::awt::TextAlign::CENTER;
+ break;
+ default:
+ _nAlign = css::awt::TextAlign::LEFT;
+ break;
+ }
+ }
+ else
+ _nAlign = css::awt::TextAlign::LEFT;
+ }
+
+ m_nAlign = _nAlign;
+ if (m_pCell.is() && m_pCell->isAlignedController())
+ m_pCell->AlignControl(m_nAlign);
+
+ return m_nAlign;
+}
+
+
+sal_Int16 DbGridColumn::SetAlignmentFromModel(sal_Int16 nStandardAlign)
+{
+ Any aAlign( m_xModel->getPropertyValue(FM_PROP_ALIGN));
+ if (aAlign.hasValue())
+ {
+ sal_Int16 nTest = sal_Int16();
+ if (aAlign >>= nTest)
+ nStandardAlign = nTest;
+ }
+ return SetAlignment(nStandardAlign);
+}
+
+
+void DbGridColumn::setLock(bool _bLock)
+{
+ if (m_bLocked == _bLock)
+ return;
+ m_bLocked = _bLock;
+
+ // is the column we represent active ?
+ if (m_bHidden)
+ return; // no, it isn't (or at least it shouldn't be ...)
+
+ if (m_rParent.GetCurColumnId() == m_nId)
+ {
+ m_rParent.DeactivateCell();
+ m_rParent.ActivateCell(m_rParent.GetCurRow(), m_rParent.GetCurColumnId());
+ }
+}
+
+
+OUString DbGridColumn::GetCellText(const DbGridRow* pRow, const Reference< XNumberFormatter >& xFormatter) const
+{
+ OUString aText;
+ if (m_pCell.is() && dynamic_cast<const FmXFilterCell*>( m_pCell.get() ) != nullptr)
+ return aText;
+
+ if (!pRow || !pRow->IsValid())
+ aText = INVALIDTEXT;
+ else if (pRow->HasField(m_nFieldPos))
+ {
+ aText = GetCellText( pRow->GetField( m_nFieldPos ).getColumn(), xFormatter );
+ }
+ return aText;
+}
+
+
+OUString DbGridColumn::GetCellText(const Reference< css::sdb::XColumn >& xField, const Reference< XNumberFormatter >& xFormatter) const
+{
+ OUString aText;
+ if (xField.is())
+ {
+ FmXTextCell* pTextCell = dynamic_cast<FmXTextCell*>( m_pCell.get() );
+ if (pTextCell)
+ aText = pTextCell->GetText(xField, xFormatter);
+ else if (m_bObject)
+ aText = OBJECTTEXT;
+ }
+ return aText;
+}
+
+
+Reference< css::sdb::XColumn > DbGridColumn::GetCurrentFieldValue() const
+{
+ Reference< css::sdb::XColumn > xField;
+ const DbGridRowRef xRow = m_rParent.GetCurrentRow();
+ if (xRow.is() && xRow->HasField(m_nFieldPos))
+ {
+ xField = xRow->GetField(m_nFieldPos).getColumn();
+ }
+ return xField;
+}
+
+
+void DbGridColumn::Paint(OutputDevice& rDev,
+ const tools::Rectangle& rRect,
+ const DbGridRow* pRow,
+ const Reference< XNumberFormatter >& xFormatter)
+{
+ bool bEnabled = ( rDev.GetOutDevType() != OUTDEV_WINDOW )
+ || ( static_cast< vcl::Window& >( rDev ).IsEnabled() );
+
+ FmXDataCell* pDataCell = dynamic_cast<FmXDataCell*>( m_pCell.get() );
+ if (pDataCell)
+ {
+ if (!pRow || !pRow->IsValid())
+ {
+ DrawTextFlags nStyle = DrawTextFlags::Clip | DrawTextFlags::Center;
+ if ( !bEnabled )
+ nStyle |= DrawTextFlags::Disable;
+
+ rDev.DrawText(rRect, OUString(INVALIDTEXT), nStyle);
+ }
+ else if (m_bAutoValue && pRow->IsNew())
+ {
+ DrawTextFlags nStyle = DrawTextFlags::Clip | DrawTextFlags::VCenter;
+ if ( !bEnabled )
+ nStyle |= DrawTextFlags::Disable;
+
+ switch (GetAlignment())
+ {
+ case css::awt::TextAlign::RIGHT:
+ nStyle |= DrawTextFlags::Right;
+ break;
+ case css::awt::TextAlign::CENTER:
+ nStyle |= DrawTextFlags::Center;
+ break;
+ default:
+ nStyle |= DrawTextFlags::Left;
+ }
+
+ rDev.DrawText(rRect, SvxResId(RID_STR_AUTOFIELD), nStyle);
+ }
+ else if (pRow->HasField(m_nFieldPos))
+ {
+ pDataCell->PaintFieldToCell(rDev, rRect, pRow->GetField( m_nFieldPos ).getColumn(), xFormatter);
+ }
+ }
+ else if (!m_pCell.is())
+ {
+ if (!pRow || !pRow->IsValid())
+ {
+ DrawTextFlags nStyle = DrawTextFlags::Clip | DrawTextFlags::Center;
+ if ( !bEnabled )
+ nStyle |= DrawTextFlags::Disable;
+
+ rDev.DrawText(rRect, OUString(INVALIDTEXT), nStyle);
+ }
+ else if (pRow->HasField(m_nFieldPos) && m_bObject)
+ {
+ DrawTextFlags nStyle = DrawTextFlags::Clip | DrawTextFlags::Center;
+ if ( !bEnabled )
+ nStyle |= DrawTextFlags::Disable;
+ rDev.DrawText(rRect, OUString(OBJECTTEXT), nStyle);
+ }
+ }
+ else if ( dynamic_cast<const FmXFilterCell*>( m_pCell.get() ) != nullptr )
+ static_cast< FmXFilterCell* >( m_pCell.get() )->PaintCell( rDev, rRect );
+}
+
+
+void DbGridColumn::ImplInitWindow( vcl::Window const & rParent, const InitWindowFacet _eInitWhat )
+{
+ if ( m_pCell.is() )
+ m_pCell->ImplInitWindow( rParent, _eInitWhat );
+}
+
+
+//= cell controls
+
+
+DbCellControl::DbCellControl( DbGridColumn& _rColumn )
+ :OPropertyChangeListener(m_aMutex)
+ ,m_bTransparent( false )
+ ,m_bAlignedController( true )
+ ,m_bAccessingValueProperty( false )
+ ,m_rColumn( _rColumn )
+ ,m_pPainter( nullptr )
+ ,m_pWindow( nullptr )
+{
+ Reference< XPropertySet > xColModelProps = _rColumn.getModel();
+ if ( !xColModelProps.is() )
+ return;
+
+ // if our model's format key changes we want to propagate the new value to our windows
+ m_pModelChangeBroadcaster = new ::comphelper::OPropertyChangeMultiplexer(this, _rColumn.getModel());
+
+ // be listener for some common properties
+ implDoPropertyListening( FM_PROP_READONLY, false );
+ implDoPropertyListening( FM_PROP_ENABLED, false );
+
+ // add as listener for all known "value" properties
+ implDoPropertyListening( FM_PROP_VALUE, false );
+ implDoPropertyListening( FM_PROP_STATE, false );
+ implDoPropertyListening( FM_PROP_TEXT, false );
+ implDoPropertyListening( FM_PROP_EFFECTIVE_VALUE, false );
+ implDoPropertyListening( FM_PROP_SELECT_SEQ, false );
+ implDoPropertyListening( FM_PROP_DATE, false );
+ implDoPropertyListening( FM_PROP_TIME, false );
+
+ // be listener at the bound field as well
+ try
+ {
+ Reference< XPropertySetInfo > xPSI( xColModelProps->getPropertySetInfo(), UNO_SET_THROW );
+ if ( xPSI->hasPropertyByName( FM_PROP_BOUNDFIELD ) )
+ {
+ Reference< XPropertySet > xField;
+ xColModelProps->getPropertyValue( FM_PROP_BOUNDFIELD ) >>= xField;
+ if ( xField.is() )
+ {
+ m_pFieldChangeBroadcaster = new ::comphelper::OPropertyChangeMultiplexer(this, xField);
+ m_pFieldChangeBroadcaster->addProperty( FM_PROP_ISREADONLY );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "DbCellControl::doPropertyListening" );
+ }
+}
+
+
+void DbCellControl::implDoPropertyListening(const OUString& _rPropertyName, bool _bWarnIfNotExistent)
+{
+ try
+ {
+ Reference< XPropertySet > xColModelProps = m_rColumn.getModel();
+ Reference< XPropertySetInfo > xPSI;
+ if ( xColModelProps.is() )
+ xPSI = xColModelProps->getPropertySetInfo();
+
+ DBG_ASSERT( !_bWarnIfNotExistent || ( xPSI.is() && xPSI->hasPropertyByName( _rPropertyName ) ),
+ "DbCellControl::doPropertyListening: no property set info or non-existent property!" );
+
+ if ( xPSI.is() && xPSI->hasPropertyByName( _rPropertyName ) )
+ m_pModelChangeBroadcaster->addProperty( _rPropertyName );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "DbCellControl::doPropertyListening" );
+ }
+}
+
+
+void DbCellControl::doPropertyListening(const OUString& _rPropertyName)
+{
+ implDoPropertyListening( _rPropertyName, true );
+}
+
+static void lcl_clearBroadCaster(rtl::Reference<::comphelper::OPropertyChangeMultiplexer>& _pBroadcaster)
+{
+ if ( _pBroadcaster.is() )
+ {
+ _pBroadcaster->dispose();
+ _pBroadcaster.clear();
+ // no delete, this is done implicitly
+ }
+}
+
+DbCellControl::~DbCellControl()
+{
+ lcl_clearBroadCaster(m_pModelChangeBroadcaster);
+ lcl_clearBroadCaster(m_pFieldChangeBroadcaster);
+
+ m_pWindow.disposeAndClear();
+ m_pPainter.disposeAndClear();
+}
+
+void DbCellControl::implValuePropertyChanged( )
+{
+ OSL_ENSURE( !isValuePropertyLocked(),
+ "DbCellControl::implValuePropertyChanged: not to be called with the value property locked!" );
+
+ if ( m_pWindow )
+ {
+ if ( m_rColumn.getModel().is() )
+ updateFromModel( m_rColumn.getModel() );
+ }
+}
+
+
+void DbCellControl::implAdjustGenericFieldSetting( const Reference< XPropertySet >& /*_rxModel*/ )
+{
+ // nothing to do here
+}
+
+
+void DbCellControl::_propertyChanged(const PropertyChangeEvent& _rEvent)
+{
+ SolarMutexGuard aGuard;
+
+ Reference< XPropertySet > xSourceProps( _rEvent.Source, UNO_QUERY );
+
+ if ( _rEvent.PropertyName == FM_PROP_VALUE
+ || _rEvent.PropertyName == FM_PROP_STATE
+ || _rEvent.PropertyName == FM_PROP_TEXT
+ || _rEvent.PropertyName == FM_PROP_EFFECTIVE_VALUE
+ || _rEvent.PropertyName == FM_PROP_SELECT_SEQ
+ || _rEvent.PropertyName == FM_PROP_DATE
+ || _rEvent.PropertyName == FM_PROP_TIME
+ )
+ { // it was one of the known "value" properties
+ if ( !isValuePropertyLocked() )
+ {
+ implValuePropertyChanged( );
+ }
+ }
+ else if ( _rEvent.PropertyName == FM_PROP_READONLY )
+ {
+ implAdjustReadOnly( xSourceProps, true);
+ }
+ else if ( _rEvent.PropertyName == FM_PROP_ISREADONLY )
+ {
+ bool bReadOnly = true;
+ _rEvent.NewValue >>= bReadOnly;
+ m_rColumn.SetReadOnly(bReadOnly);
+ implAdjustReadOnly( xSourceProps, false);
+ }
+ else if ( _rEvent.PropertyName == FM_PROP_ENABLED )
+ {
+ implAdjustEnabled( xSourceProps );
+ }
+ else
+ implAdjustGenericFieldSetting( xSourceProps );
+}
+
+
+bool DbCellControl::Commit()
+{
+ // lock the listening for value property changes
+ lockValueProperty();
+ // commit the content of the control into the model's value property
+ bool bReturn = false;
+ try
+ {
+ bReturn = commitControl();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ // unlock the listening for value property changes
+ unlockValueProperty();
+ // outta here
+ return bReturn;
+}
+
+
+void DbCellControl::ImplInitWindow( vcl::Window const & rParent, const InitWindowFacet _eInitWhat )
+{
+ vcl::Window* pWindows[] = { m_pPainter, m_pWindow };
+
+ if (_eInitWhat & InitWindowFacet::WritingMode)
+ {
+ for (vcl::Window* pWindow : pWindows)
+ {
+ if (pWindow)
+ pWindow->EnableRTL(rParent.IsRTLEnabled());
+ }
+ }
+
+ if (_eInitWhat & InitWindowFacet::Font)
+ {
+ for (vcl::Window* pWindow : pWindows)
+ {
+ if (!pWindow)
+ continue;
+
+ pWindow->SetZoom(rParent.GetZoom());
+
+ const StyleSettings& rStyleSettings = pWindow->GetSettings().GetStyleSettings();
+ vcl::Font aFont = rStyleSettings.GetFieldFont();
+ aFont.SetTransparent(isTransparent());
+
+ if (rParent.IsControlFont())
+ {
+ pWindow->SetControlFont(rParent.GetControlFont());
+ aFont.Merge(rParent.GetControlFont());
+ }
+ else
+ pWindow->SetControlFont();
+
+ pWindow->SetZoomedPointFont(*pWindow, aFont); // FIXME RenderContext
+ }
+ }
+
+ if ((_eInitWhat & InitWindowFacet::Font) || (_eInitWhat & InitWindowFacet::Foreground))
+ {
+ Color aTextColor(rParent.IsControlForeground() ? rParent.GetControlForeground() : rParent.GetTextColor());
+
+ bool bTextLineColor = rParent.IsTextLineColor();
+ Color aTextLineColor(rParent.GetTextLineColor());
+
+ for (vcl::Window* pWindow : pWindows)
+ {
+ if (pWindow)
+ {
+ pWindow->SetTextColor(aTextColor);
+ if (rParent.IsControlForeground())
+ pWindow->SetControlForeground(aTextColor);
+
+ if (bTextLineColor)
+ pWindow->SetTextLineColor();
+ else
+ pWindow->SetTextLineColor(aTextLineColor);
+ }
+ }
+ }
+
+ if (_eInitWhat & InitWindowFacet::Background)
+ {
+ if (rParent.IsControlBackground())
+ {
+ Color aColor(rParent.GetControlBackground());
+ for (vcl::Window* pWindow : pWindows)
+ {
+ if (pWindow)
+ {
+ if (isTransparent())
+ pWindow->SetBackground();
+ else
+ {
+ pWindow->SetBackground(aColor);
+ pWindow->SetControlBackground(aColor);
+ }
+ pWindow->SetFillColor(aColor);
+ }
+ }
+ }
+ else
+ {
+ if (m_pPainter)
+ {
+ if (isTransparent())
+ m_pPainter->SetBackground();
+ else
+ m_pPainter->SetBackground(rParent.GetBackground());
+ m_pPainter->SetFillColor(rParent.GetFillColor());
+ }
+
+ if (m_pWindow)
+ {
+ if (isTransparent())
+ m_pWindow->SetBackground(rParent.GetBackground());
+ else
+ m_pWindow->SetFillColor(rParent.GetFillColor());
+ }
+ }
+ }
+}
+
+
+void DbCellControl::implAdjustReadOnly( const Reference< XPropertySet >& _rxModel,bool i_bReadOnly )
+{
+ DBG_ASSERT( m_pWindow, "DbCellControl::implAdjustReadOnly: not to be called without window!" );
+ DBG_ASSERT( _rxModel.is(), "DbCellControl::implAdjustReadOnly: invalid model!" );
+ if ( m_pWindow && _rxModel.is() )
+ {
+ Edit* pEditWindow = dynamic_cast< Edit* >( m_pWindow.get() );
+ if ( pEditWindow )
+ {
+ bool bReadOnly = m_rColumn.IsReadOnly();
+ if ( !bReadOnly )
+ {
+ _rxModel->getPropertyValue( i_bReadOnly ? OUString(FM_PROP_READONLY) : OUString(FM_PROP_ISREADONLY)) >>= bReadOnly;
+ }
+ static_cast< Edit* >( m_pWindow.get() )->SetReadOnly( bReadOnly );
+ }
+ }
+}
+
+
+void DbCellControl::implAdjustEnabled( const Reference< XPropertySet >& _rxModel )
+{
+ DBG_ASSERT( m_pWindow, "DbCellControl::implAdjustEnabled: not to be called without window!" );
+ DBG_ASSERT( _rxModel.is(), "DbCellControl::implAdjustEnabled: invalid model!" );
+ if ( m_pWindow && _rxModel.is() )
+ {
+ bool bEnable = true;
+ _rxModel->getPropertyValue( FM_PROP_ENABLED ) >>= bEnable;
+ m_pWindow->Enable( bEnable );
+ }
+}
+
+
+void DbCellControl::Init( vcl::Window& rParent, const Reference< XRowSet >& _rxCursor )
+{
+ ImplInitWindow( rParent, InitWindowFacet::All );
+
+ if ( m_pWindow )
+ {
+ // align the control
+ if ( isAlignedController() )
+ AlignControl( m_rColumn.GetAlignment() );
+
+ try
+ {
+ // some other common properties
+ Reference< XPropertySet > xModel( m_rColumn.getModel(), UNO_SET_THROW );
+ Reference< XPropertySetInfo > xModelPSI( xModel->getPropertySetInfo(), UNO_SET_THROW );
+
+ if ( xModelPSI->hasPropertyByName( FM_PROP_READONLY ) )
+ {
+ implAdjustReadOnly( xModel,true );
+ }
+
+ if ( xModelPSI->hasPropertyByName( FM_PROP_ENABLED ) )
+ {
+ implAdjustEnabled( xModel );
+ }
+
+ if ( xModelPSI->hasPropertyByName( FM_PROP_MOUSE_WHEEL_BEHAVIOR ) )
+ {
+ sal_Int16 nWheelBehavior = css::awt::MouseWheelBehavior::SCROLL_FOCUS_ONLY;
+ OSL_VERIFY( xModel->getPropertyValue( FM_PROP_MOUSE_WHEEL_BEHAVIOR ) >>= nWheelBehavior );
+ MouseWheelBehaviour nVclSetting = MouseWheelBehaviour::FocusOnly;
+ switch ( nWheelBehavior )
+ {
+ case css::awt::MouseWheelBehavior::SCROLL_DISABLED: nVclSetting = MouseWheelBehaviour::Disable; break;
+ case css::awt::MouseWheelBehavior::SCROLL_FOCUS_ONLY: nVclSetting = MouseWheelBehaviour::FocusOnly; break;
+ case css::awt::MouseWheelBehavior::SCROLL_ALWAYS: nVclSetting = MouseWheelBehaviour::ALWAYS; break;
+ default:
+ OSL_FAIL( "DbCellControl::Init: invalid MouseWheelBehavior!" );
+ break;
+ }
+
+ AllSettings aSettings = m_pWindow->GetSettings();
+ MouseSettings aMouseSettings = aSettings.GetMouseSettings();
+ aMouseSettings.SetWheelBehavior( nVclSetting );
+ aSettings.SetMouseSettings( aMouseSettings );
+ m_pWindow->SetSettings( aSettings, true );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ m_xCursor = _rxCursor;
+ if ( m_rColumn.getModel().is() )
+ updateFromModel( m_rColumn.getModel() );
+}
+
+
+void DbCellControl::SetTextLineColor()
+{
+ if (m_pWindow)
+ m_pWindow->SetTextLineColor();
+ if (m_pPainter)
+ m_pPainter->SetTextLineColor();
+}
+
+
+void DbCellControl::SetTextLineColor(const Color& _rColor)
+{
+ if (m_pWindow)
+ m_pWindow->SetTextLineColor(_rColor);
+ if (m_pPainter)
+ m_pPainter->SetTextLineColor(_rColor);
+}
+
+namespace
+{
+ void lcl_implAlign( vcl::Window* _pWindow, WinBits _nAlignmentBit )
+ {
+ WinBits nStyle = _pWindow->GetStyle();
+ nStyle &= ~(WB_LEFT | WB_RIGHT | WB_CENTER);
+ _pWindow->SetStyle( nStyle | _nAlignmentBit );
+ }
+}
+
+
+void DbCellControl::AlignControl(sal_Int16 nAlignment)
+{
+ WinBits nAlignmentBit = 0;
+ switch (nAlignment)
+ {
+ case css::awt::TextAlign::RIGHT:
+ nAlignmentBit = WB_RIGHT;
+ break;
+ case css::awt::TextAlign::CENTER:
+ nAlignmentBit = WB_CENTER;
+ break;
+ default:
+ nAlignmentBit = WB_LEFT;
+ break;
+ }
+ lcl_implAlign( m_pWindow, nAlignmentBit );
+ if ( m_pPainter )
+ lcl_implAlign( m_pPainter, nAlignmentBit );
+}
+
+void DbCellControl::PaintCell( OutputDevice& _rDev, const tools::Rectangle& _rRect )
+{
+ if ( m_pPainter->GetParent() == &_rDev )
+ {
+ m_pPainter->SetPaintTransparent( true );
+ m_pPainter->SetBackground( );
+ m_pPainter->SetControlBackground( _rDev.GetFillColor() );
+ m_pPainter->SetControlForeground( _rDev.GetTextColor() );
+ m_pPainter->SetTextColor( _rDev.GetTextColor() );
+ m_pPainter->SetTextFillColor( _rDev.GetTextColor() );
+
+ vcl::Font aFont( _rDev.GetFont() );
+ aFont.SetTransparent( true );
+ m_pPainter->SetFont( aFont );
+
+ m_pPainter->SetPosSizePixel( _rRect.TopLeft(), _rRect.GetSize() );
+ m_pPainter->Show();
+ m_pPainter->PaintImmediately();
+ m_pPainter->SetParentUpdateMode( false );
+ m_pPainter->Hide();
+ m_pPainter->SetParentUpdateMode( true );
+ }
+ else
+ {
+ m_pPainter->SetSizePixel( _rRect.GetSize() );
+ m_pPainter->Draw( &_rDev, _rRect.TopLeft(), DrawFlags::NONE );
+ }
+}
+
+void DbCellControl::PaintFieldToCell( OutputDevice& _rDev, const tools::Rectangle& _rRect, const Reference< XColumn >& _rxField, const Reference< XNumberFormatter >& _rxFormatter )
+{
+ m_pPainter->SetText( GetFormatText( _rxField, _rxFormatter ) );
+ PaintCell( _rDev, _rRect );
+}
+
+
+double DbCellControl::GetValue(const Reference< css::sdb::XColumn >& _rxField, const Reference< XNumberFormatter >& xFormatter) const
+{
+ double fValue = 0;
+ if (m_rColumn.IsNumeric())
+ {
+ try
+ {
+ fValue = _rxField->getDouble();
+ }
+ catch(const Exception&) { }
+ }
+ else
+ {
+ bool bSuccess = false;
+ try
+ {
+ fValue = _rxField->getDouble();
+ bSuccess = true;
+ }
+ catch(const Exception&) { }
+ if (!bSuccess)
+ {
+ try
+ {
+ fValue = xFormatter->convertStringToNumber(m_rColumn.GetKey(), _rxField->getString());
+ }
+ catch(const Exception&) { }
+ }
+ }
+ return fValue;
+}
+
+
+void DbCellControl::invalidatedController()
+{
+ m_rColumn.GetParent().refreshController(m_rColumn.GetId(), DbGridControl::GrantControlAccess());
+}
+
+// CellModels
+
+DbLimitedLengthField::DbLimitedLengthField( DbGridColumn& _rColumn )
+ :DbCellControl( _rColumn )
+{
+ doPropertyListening( FM_PROP_MAXTEXTLEN );
+}
+
+
+void DbLimitedLengthField::implAdjustGenericFieldSetting( const Reference< XPropertySet >& _rxModel )
+{
+ DBG_ASSERT( m_pWindow, "DbLimitedLengthField::implAdjustGenericFieldSetting: not to be called without window!" );
+ DBG_ASSERT( _rxModel.is(), "DbLimitedLengthField::implAdjustGenericFieldSetting: invalid model!" );
+ if ( m_pWindow && _rxModel.is() )
+ {
+ sal_Int16 nMaxLen = 0;
+ _rxModel->getPropertyValue( FM_PROP_MAXTEXTLEN ) >>= nMaxLen;
+ implSetMaxTextLen( nMaxLen );
+ }
+}
+
+void DbLimitedLengthField::implSetEffectiveMaxTextLen( sal_Int32 _nMaxLen )
+{
+ dynamic_cast<Edit&>(*m_pWindow).SetMaxTextLen(_nMaxLen);
+ if (m_pPainter)
+ dynamic_cast<Edit&>(*m_pPainter).SetMaxTextLen(_nMaxLen);
+}
+
+DbTextField::DbTextField(DbGridColumn& _rColumn)
+ :DbLimitedLengthField(_rColumn)
+ ,m_bIsSimpleEdit( true )
+{
+}
+
+
+DbTextField::~DbTextField( )
+{
+ m_pPainterImplementation.reset();
+ m_pEdit.reset();
+}
+
+
+void DbTextField::Init( vcl::Window& rParent, const Reference< XRowSet >& xCursor)
+{
+ sal_Int16 nAlignment = m_rColumn.SetAlignmentFromModel(-1);
+
+ Reference< XPropertySet > xModel( m_rColumn.getModel() );
+
+ WinBits nStyle = WB_LEFT;
+ switch (nAlignment)
+ {
+ case awt::TextAlign::RIGHT:
+ nStyle = WB_RIGHT;
+ break;
+
+ case awt::TextAlign::CENTER:
+ nStyle = WB_CENTER;
+ break;
+ }
+
+ // is this a multi-line field?
+ bool bIsMultiLine = false;
+ try
+ {
+ if ( xModel.is() )
+ {
+ OSL_VERIFY( xModel->getPropertyValue( FM_PROP_MULTILINE ) >>= bIsMultiLine );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ OSL_FAIL( "DbTextField::Init: caught an exception while determining the multi-line capabilities!" );
+ }
+
+ m_bIsSimpleEdit = !bIsMultiLine;
+ if ( bIsMultiLine )
+ {
+ m_pWindow = VclPtr<MultiLineTextCell>::Create( &rParent, nStyle );
+ m_pEdit.reset(new MultiLineEditImplementation( *static_cast< MultiLineTextCell* >( m_pWindow.get() ) ));
+
+ m_pPainter = VclPtr<MultiLineTextCell>::Create( &rParent, nStyle );
+ m_pPainterImplementation.reset(new MultiLineEditImplementation( *static_cast< MultiLineTextCell* >( m_pPainter.get() ) ));
+ }
+ else
+ {
+ m_pWindow = VclPtr<Edit>::Create( &rParent, nStyle );
+ m_pEdit.reset(new EditImplementation( *static_cast< Edit* >( m_pWindow.get() ) ));
+
+ m_pPainter = VclPtr<Edit>::Create( &rParent, nStyle );
+ m_pPainterImplementation.reset(new EditImplementation( *static_cast< Edit* >( m_pPainter.get() ) ));
+ }
+
+ if ( WB_LEFT == nStyle )
+ {
+ // this is so that when getting the focus, the selection is oriented left-to-right
+ AllSettings aSettings = m_pWindow->GetSettings();
+ StyleSettings aStyleSettings = aSettings.GetStyleSettings();
+ aStyleSettings.SetSelectionOptions(
+ aStyleSettings.GetSelectionOptions() | SelectionOptions::ShowFirst);
+ aSettings.SetStyleSettings(aStyleSettings);
+ m_pWindow->SetSettings(aSettings);
+ }
+
+ implAdjustGenericFieldSetting( xModel );
+
+ DbLimitedLengthField::Init( rParent, xCursor );
+}
+
+
+CellControllerRef DbTextField::CreateController() const
+{
+ return new EditCellController( m_pEdit.get() );
+}
+
+
+void DbTextField::PaintFieldToCell( OutputDevice& _rDev, const tools::Rectangle& _rRect, const Reference< XColumn >& _rxField, const Reference< XNumberFormatter >& _rxFormatter )
+{
+ if ( m_pPainterImplementation )
+ m_pPainterImplementation->SetText( GetFormatText( _rxField, _rxFormatter ) );
+
+ DbLimitedLengthField::PaintFieldToCell( _rDev, _rRect, _rxField, _rxFormatter );
+}
+
+
+OUString DbTextField::GetFormatText(const Reference< XColumn >& _rxField, const Reference< XNumberFormatter >& xFormatter, Color** /*ppColor*/)
+{
+ if (!_rxField.is())
+ return OUString();
+
+ const css::uno::Reference<css::beans::XPropertySet> xPS(_rxField, UNO_QUERY);
+ FormattedColumnValue fmter( xFormatter, xPS );
+
+ try
+ {
+ return fmter.getFormattedValue();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return OUString();
+
+}
+
+
+void DbTextField::UpdateFromField(const Reference< css::sdb::XColumn >& _rxField, const Reference< XNumberFormatter >& xFormatter)
+{
+ m_pEdit->SetText( GetFormatText( _rxField, xFormatter ) );
+ m_pEdit->SetSelection( Selection( SELECTION_MAX, SELECTION_MIN ) );
+}
+
+
+void DbTextField::updateFromModel( Reference< XPropertySet > _rxModel )
+{
+ OSL_ENSURE( _rxModel.is() && m_pWindow, "DbTextField::updateFromModel: invalid call!" );
+
+ OUString sText;
+ _rxModel->getPropertyValue( FM_PROP_TEXT ) >>= sText;
+
+ sal_Int32 nMaxTextLen = m_pEdit->GetMaxTextLen();
+ if ( EDIT_NOLIMIT != nMaxTextLen && sText.getLength() > nMaxTextLen )
+ {
+ sal_Int32 nDiff = sText.getLength() - nMaxTextLen;
+ sText = sText.replaceAt(sText.getLength() - nDiff,nDiff, OUString());
+ }
+
+
+ m_pEdit->SetText( sText );
+ m_pEdit->SetSelection( Selection( SELECTION_MAX, SELECTION_MIN ) );
+}
+
+
+bool DbTextField::commitControl()
+{
+ OUString aText( m_pEdit->GetText( getModelLineEndSetting( m_rColumn.getModel() ) ) );
+ // we have to check if the length before we can decide if the value was modified
+ sal_Int32 nMaxTextLen = m_pEdit->GetMaxTextLen();
+ if ( EDIT_NOLIMIT != nMaxTextLen )
+ {
+ OUString sOldValue;
+ m_rColumn.getModel()->getPropertyValue( FM_PROP_TEXT ) >>= sOldValue;
+ // if the new value didn't change we must set the old long value again
+ if ( sOldValue.getLength() > nMaxTextLen && sOldValue.compareTo(aText,nMaxTextLen) == 0 )
+ aText = sOldValue;
+ }
+ m_rColumn.getModel()->setPropertyValue( FM_PROP_TEXT, makeAny( aText ) );
+ return true;
+}
+
+
+void DbTextField::implSetEffectiveMaxTextLen( sal_Int32 _nMaxLen )
+{
+ if ( m_pEdit )
+ m_pEdit->SetMaxTextLen( _nMaxLen );
+ if ( m_pPainterImplementation )
+ m_pPainterImplementation->SetMaxTextLen( _nMaxLen );
+}
+
+DbFormattedField::DbFormattedField(DbGridColumn& _rColumn)
+ :DbLimitedLengthField(_rColumn)
+{
+ // if our model's format key changes we want to propagate the new value to our windows
+ doPropertyListening( FM_PROP_FORMATKEY );
+}
+
+
+DbFormattedField::~DbFormattedField()
+{
+}
+
+
+void DbFormattedField::Init( vcl::Window& rParent, const Reference< XRowSet >& xCursor)
+{
+ sal_Int16 nAlignment = m_rColumn.SetAlignmentFromModel(-1);
+
+ Reference< css::beans::XPropertySet > xUnoModel = m_rColumn.getModel();
+
+ switch (nAlignment)
+ {
+ case css::awt::TextAlign::RIGHT:
+ m_pWindow = VclPtr<FormattedField>::Create( &rParent, WB_RIGHT );
+ m_pPainter = VclPtr<FormattedField>::Create( &rParent, WB_RIGHT );
+ break;
+
+ case css::awt::TextAlign::CENTER:
+ m_pWindow = VclPtr<FormattedField>::Create( &rParent, WB_CENTER );
+ m_pPainter = VclPtr<FormattedField>::Create( &rParent, WB_CENTER );
+ break;
+ default:
+ m_pWindow = VclPtr<FormattedField>::Create( &rParent, WB_LEFT );
+ m_pPainter = VclPtr<FormattedField>::Create( &rParent, WB_LEFT );
+
+ // Everything just so that the selection goes from right to left when getting focus
+ AllSettings aSettings = m_pWindow->GetSettings();
+ StyleSettings aStyleSettings = aSettings.GetStyleSettings();
+ aStyleSettings.SetSelectionOptions(
+ aStyleSettings.GetSelectionOptions() | SelectionOptions::ShowFirst);
+ aSettings.SetStyleSettings(aStyleSettings);
+ m_pWindow->SetSettings(aSettings);
+ }
+
+ implAdjustGenericFieldSetting( xUnoModel );
+
+ static_cast< FormattedField* >( m_pWindow.get() )->SetStrictFormat( false );
+ static_cast< FormattedField* >( m_pPainter.get() )->SetStrictFormat( false );
+ // if one allows any formatting, one cannot make an entry check anyway
+ // (the FormattedField does not support that anyway, only derived classes)
+
+ // get the formatter from the uno model
+ // (I could theoretically also go via the css::util::NumberFormatter, which the cursor would
+ // surely give me. The problem is that I can not really rely on the fact that the two
+ // formatters are the same. Clean is the whole thing if I go via the UNO model.)
+ sal_Int32 nFormatKey = -1;
+
+ // let's see if the model has one ...
+ DBG_ASSERT(::comphelper::hasProperty(FM_PROP_FORMATSSUPPLIER, xUnoModel), "DbFormattedField::Init : invalid UNO model !");
+ Any aSupplier( xUnoModel->getPropertyValue(FM_PROP_FORMATSSUPPLIER));
+ if (aSupplier.hasValue())
+ {
+ m_xSupplier.set(aSupplier, css::uno::UNO_QUERY);
+ if (m_xSupplier.is())
+ {
+ // if we take the supplier from the model, then also the key
+ Any aFmtKey( xUnoModel->getPropertyValue(FM_PROP_FORMATKEY));
+ if (aFmtKey.hasValue())
+ {
+ DBG_ASSERT(aFmtKey.getValueType().getTypeClass() == TypeClass_LONG, "DbFormattedField::Init : invalid format key property (no sal_Int32) !");
+ nFormatKey = ::comphelper::getINT32(aFmtKey);
+ }
+ else
+ {
+ SAL_INFO("svx.fmcomp", "DbFormattedField::Init : my uno-model has no format-key, but a formats supplier !");
+ // the OFormattedModel which we usually are working with ensures that the model has a format key
+ // as soon as the form is loaded. Unfortunally this method here is called from within loaded, too.
+ // So if our LoadListener is called before the LoadListener of the model, this "else case" is
+ // allowed.
+ // Of course our property listener for the FormatKey property will notify us if the prop is changed,
+ // so this here isn't really bad...
+ nFormatKey = 0;
+ }
+ }
+ }
+
+ // No? Maybe the css::form::component::Form behind the cursor?
+ if (!m_xSupplier.is())
+ {
+ if (xCursor.is())
+ { // If we take the formatter from the cursor, then also the key from the field to which we are bound
+ m_xSupplier = getNumberFormats(getConnection(xCursor));
+
+ if (m_rColumn.GetField().is())
+ nFormatKey = ::comphelper::getINT32(m_rColumn.GetField()->getPropertyValue(FM_PROP_FORMATKEY));
+ }
+ }
+
+ SvNumberFormatter* pFormatterUsed = nullptr;
+ if (m_xSupplier.is())
+ {
+ SvNumberFormatsSupplierObj* pImplmentation = comphelper::getUnoTunnelImplementation<SvNumberFormatsSupplierObj>(m_xSupplier);
+ if (pImplmentation)
+ pFormatterUsed = pImplmentation->GetNumberFormatter();
+ else
+ // Everything is invalid: the supplier is of the wrong type, then we can not
+ // rely on a standard formatter to know the (possibly non-standard) key.
+ nFormatKey = -1;
+ }
+
+ // a standard formatter ...
+ if (pFormatterUsed == nullptr)
+ {
+ pFormatterUsed = static_cast<FormattedField*>(m_pWindow.get())->StandardFormatter();
+ DBG_ASSERT(pFormatterUsed != nullptr, "DbFormattedField::Init : no standard formatter given by the numeric field !");
+ }
+ // ... and a standard key
+ if (nFormatKey == -1)
+ nFormatKey = 0;
+
+ static_cast<FormattedField*>(m_pWindow.get())->SetFormatter(pFormatterUsed);
+ static_cast<FormattedField*>(m_pPainter.get())->SetFormatter(pFormatterUsed);
+
+ static_cast<FormattedField*>(m_pWindow.get())->SetFormatKey(nFormatKey);
+ static_cast<FormattedField*>(m_pPainter.get())->SetFormatKey(nFormatKey);
+
+ static_cast<FormattedField*>(m_pWindow.get())->TreatAsNumber(m_rColumn.IsNumeric());
+ static_cast<FormattedField*>(m_pPainter.get())->TreatAsNumber(m_rColumn.IsNumeric());
+
+ // min and max values
+ if (m_rColumn.IsNumeric())
+ {
+ bool bClearMin = true;
+ if (::comphelper::hasProperty(FM_PROP_EFFECTIVE_MIN, xUnoModel))
+ {
+ Any aMin( xUnoModel->getPropertyValue(FM_PROP_EFFECTIVE_MIN));
+ if (aMin.getValueType().getTypeClass() != TypeClass_VOID)
+ {
+ DBG_ASSERT(aMin.getValueType().getTypeClass() == TypeClass_DOUBLE, "DbFormattedField::Init : the model has an invalid min value !");
+ double dMin = ::comphelper::getDouble(aMin);
+ static_cast<FormattedField*>(m_pWindow.get())->SetMinValue(dMin);
+ static_cast<FormattedField*>(m_pPainter.get())->SetMinValue(dMin);
+ bClearMin = false;
+ }
+ }
+ if (bClearMin)
+ {
+ static_cast<FormattedField*>(m_pWindow.get())->ClearMinValue();
+ static_cast<FormattedField*>(m_pPainter.get())->ClearMinValue();
+ }
+ bool bClearMax = true;
+ if (::comphelper::hasProperty(FM_PROP_EFFECTIVE_MAX, xUnoModel))
+ {
+ Any aMin( xUnoModel->getPropertyValue(FM_PROP_EFFECTIVE_MAX));
+ if (aMin.getValueType().getTypeClass() != TypeClass_VOID)
+ {
+ DBG_ASSERT(aMin.getValueType().getTypeClass() == TypeClass_DOUBLE, "DbFormattedField::Init : the model has an invalid max value !");
+ double dMin = ::comphelper::getDouble(aMin);
+ static_cast<FormattedField*>(m_pWindow.get())->SetMaxValue(dMin);
+ static_cast<FormattedField*>(m_pPainter.get())->SetMaxValue(dMin);
+ bClearMax = false;
+ }
+ }
+ if (bClearMax)
+ {
+ static_cast<FormattedField*>(m_pWindow.get())->ClearMaxValue();
+ static_cast<FormattedField*>(m_pPainter.get())->ClearMaxValue();
+ }
+ }
+
+ // the default value
+ Any aDefault( xUnoModel->getPropertyValue(FM_PROP_EFFECTIVE_DEFAULT));
+ if (aDefault.hasValue())
+ { // the thing can be a double or a string
+ switch (aDefault.getValueType().getTypeClass())
+ {
+ case TypeClass_DOUBLE:
+ if (m_rColumn.IsNumeric())
+ {
+ static_cast<FormattedField*>(m_pWindow.get())->SetDefaultValue(::comphelper::getDouble(aDefault));
+ static_cast<FormattedField*>(m_pPainter.get())->SetDefaultValue(::comphelper::getDouble(aDefault));
+ }
+ else
+ {
+ OUString sConverted;
+ Color* pDummy;
+ pFormatterUsed->GetOutputString(::comphelper::getDouble(aDefault), 0, sConverted, &pDummy);
+ static_cast<FormattedField*>(m_pWindow.get())->SetDefaultText(sConverted);
+ static_cast<FormattedField*>(m_pPainter.get())->SetDefaultText(sConverted);
+ }
+ break;
+ case TypeClass_STRING:
+ {
+ OUString sDefault( ::comphelper::getString(aDefault) );
+ if (m_rColumn.IsNumeric())
+ {
+ double dVal;
+ sal_uInt32 nTestFormat(0);
+ if (pFormatterUsed->IsNumberFormat(sDefault, nTestFormat, dVal))
+ {
+ static_cast<FormattedField*>(m_pWindow.get())->SetDefaultValue(dVal);
+ static_cast<FormattedField*>(m_pPainter.get())->SetDefaultValue(dVal);
+ }
+ }
+ else
+ {
+ static_cast<FormattedField*>(m_pWindow.get())->SetDefaultText(sDefault);
+ static_cast<FormattedField*>(m_pPainter.get())->SetDefaultText(sDefault);
+ }
+ }
+ break;
+ default:
+ OSL_FAIL( "DbFormattedField::Init: unexpected value type!" );
+ break;
+ }
+ }
+ DbLimitedLengthField::Init( rParent, xCursor );
+}
+
+
+CellControllerRef DbFormattedField::CreateController() const
+{
+ return new ::svt::FormattedFieldCellController( static_cast< FormattedField* >( m_pWindow.get() ) );
+}
+
+
+void DbFormattedField::_propertyChanged( const PropertyChangeEvent& _rEvent )
+{
+ if (_rEvent.PropertyName == FM_PROP_FORMATKEY )
+ {
+ sal_Int32 nNewKey = _rEvent.NewValue.hasValue() ? ::comphelper::getINT32(_rEvent.NewValue) : 0;
+
+ DBG_ASSERT(m_pWindow && m_pPainter, "DbFormattedField::_propertyChanged : where are my windows ?");
+ if (m_pWindow)
+ static_cast< FormattedField* >( m_pWindow.get() )->SetFormatKey( nNewKey );
+ if (m_pPainter)
+ static_cast< FormattedField* >( m_pPainter.get() )->SetFormatKey( nNewKey );
+ }
+ else
+ {
+ DbLimitedLengthField::_propertyChanged( _rEvent );
+ }
+}
+
+
+OUString DbFormattedField::GetFormatText(const Reference< css::sdb::XColumn >& _rxField, const Reference< XNumberFormatter >& /*xFormatter*/, Color** ppColor)
+{
+ // no color specification by default
+ if (ppColor != nullptr)
+ *ppColor = nullptr;
+
+ // NULL value -> empty text
+ if (!_rxField.is())
+ return OUString();
+
+ OUString aText;
+ try
+ {
+ if (m_rColumn.IsNumeric())
+ {
+ // The IsNumeric at the column says nothing about the class of the used format, but
+ // about the class of the field bound to the column. So when you bind a FormattedField
+ // column to a double field and format it as text, m_rColumn.IsNumeric() returns
+ // sal_True. So that simply means that I can query the contents of the variant using
+ // getDouble, and then I can leave the rest (the formatting) to the FormattedField.
+ double dValue = getValue( _rxField, m_rColumn.GetParent().getNullDate() );
+ if (_rxField->wasNull())
+ return aText;
+ static_cast<FormattedField*>(m_pPainter.get())->SetValue(dValue);
+ }
+ else
+ {
+ // Here I can not work with a double, since the field can not provide it to me.
+ // So simply bind the text from the css::util::NumberFormatter to the correct css::form::component::Form.
+ aText = _rxField->getString();
+ if (_rxField->wasNull())
+ return aText;
+ static_cast<FormattedField*>(m_pPainter.get())->SetTextFormatted(aText);
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ aText = m_pPainter->GetText();
+ if (ppColor != nullptr)
+ *ppColor = static_cast<FormattedField*>(m_pPainter.get())->GetLastOutputColor();
+
+ return aText;
+}
+
+
+void DbFormattedField::UpdateFromField(const Reference< css::sdb::XColumn >& _rxField, const Reference< XNumberFormatter >& /*xFormatter*/)
+{
+ try
+ {
+ FormattedField* pFormattedWindow = static_cast<FormattedField*>(m_pWindow.get());
+ if (!_rxField.is())
+ { // NULL value -> empty text
+ m_pWindow->SetText(OUString());
+ }
+ else if (m_rColumn.IsNumeric())
+ {
+ // The IsNumeric at the column says nothing about the class of the used format, but
+ // about the class of the field bound to the column. So when you bind a FormattedField
+ // column to a double field and format it as text, m_rColumn.IsNumeric() returns
+ // sal_True. So that simply means that I can query the contents of the variant using
+ // getDouble, and then I can leave the rest (the formatting) to the FormattedField.
+ double dValue = getValue( _rxField, m_rColumn.GetParent().getNullDate() );
+ if (_rxField->wasNull())
+ m_pWindow->SetText(OUString());
+ else
+ pFormattedWindow->SetValue(dValue);
+ }
+ else
+ {
+ // Here I can not work with a double, since the field can not provide it to me.
+ // So simply bind the text from the css::util::NumberFormatter to the correct css::form::component::Form.
+ OUString sText( _rxField->getString());
+
+ pFormattedWindow->SetTextFormatted( sText );
+ pFormattedWindow->SetSelection( Selection( SELECTION_MAX, SELECTION_MIN ) );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+
+void DbFormattedField::updateFromModel( Reference< XPropertySet > _rxModel )
+{
+ OSL_ENSURE( _rxModel.is() && m_pWindow, "DbFormattedField::updateFromModel: invalid call!" );
+
+ FormattedField* pFormattedWindow = static_cast< FormattedField* >( m_pWindow.get() );
+
+ OUString sText;
+ Any aValue = _rxModel->getPropertyValue( FM_PROP_EFFECTIVE_VALUE );
+ if ( !aValue.hasValue() || (aValue >>= sText) )
+ { // our effective value is transferred as string
+ pFormattedWindow->SetTextFormatted( sText );
+ pFormattedWindow->SetSelection( Selection( SELECTION_MAX, SELECTION_MIN ) );
+ }
+ else
+ {
+ double dValue = 0;
+ aValue >>= dValue;
+ pFormattedWindow->SetValue(dValue);
+ }
+}
+
+
+bool DbFormattedField::commitControl()
+{
+ Any aNewVal;
+ FormattedField& rField = *static_cast<FormattedField*>(m_pWindow.get());
+ DBG_ASSERT(&rField == m_pWindow, "DbFormattedField::commitControl : can't work with a window other than my own !");
+ if (m_rColumn.IsNumeric())
+ {
+ if (!rField.GetText().isEmpty())
+ aNewVal <<= rField.GetValue();
+ // an empty string is passed on as void by default, to start with
+ }
+ else
+ aNewVal <<= rField.GetTextValue();
+
+ m_rColumn.getModel()->setPropertyValue(FM_PROP_EFFECTIVE_VALUE, aNewVal);
+ return true;
+}
+
+DbCheckBox::DbCheckBox( DbGridColumn& _rColumn )
+ :DbCellControl( _rColumn )
+{
+ setAlignedController( false );
+}
+
+namespace
+{
+ void setCheckBoxStyle( vcl::Window* _pWindow, bool bMono )
+ {
+ AllSettings aSettings = _pWindow->GetSettings();
+ StyleSettings aStyleSettings = aSettings.GetStyleSettings();
+ if( bMono )
+ aStyleSettings.SetOptions( aStyleSettings.GetOptions() | StyleSettingsOptions::Mono );
+ else
+ aStyleSettings.SetOptions( aStyleSettings.GetOptions() & (~StyleSettingsOptions::Mono) );
+ aSettings.SetStyleSettings( aStyleSettings );
+ _pWindow->SetSettings( aSettings );
+ }
+}
+
+
+void DbCheckBox::Init( vcl::Window& rParent, const Reference< XRowSet >& xCursor )
+{
+ setTransparent( true );
+
+ m_pWindow = VclPtr<CheckBoxControl>::Create( &rParent );
+ m_pPainter = VclPtr<CheckBoxControl>::Create( &rParent );
+
+ m_pWindow->SetPaintTransparent( true );
+ m_pPainter->SetPaintTransparent( true );
+
+ m_pPainter->SetBackground();
+
+ try
+ {
+ Reference< XPropertySet > xModel( m_rColumn.getModel(), UNO_SET_THROW );
+
+ sal_Int16 nStyle = awt::VisualEffect::LOOK3D;
+ OSL_VERIFY( xModel->getPropertyValue( FM_PROP_VISUALEFFECT ) >>= nStyle );
+
+ setCheckBoxStyle( m_pWindow, nStyle == awt::VisualEffect::FLAT );
+ setCheckBoxStyle( m_pPainter, nStyle == awt::VisualEffect::FLAT );
+
+ bool bTristate = true;
+ OSL_VERIFY( xModel->getPropertyValue( FM_PROP_TRISTATE ) >>= bTristate );
+ static_cast< CheckBoxControl* >( m_pWindow.get() )->GetBox().EnableTriState( bTristate );
+ static_cast< CheckBoxControl* >( m_pPainter.get() )->GetBox().EnableTriState( bTristate );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ DbCellControl::Init( rParent, xCursor );
+}
+
+
+CellControllerRef DbCheckBox::CreateController() const
+{
+ return new CheckBoxCellController(static_cast<CheckBoxControl*>(m_pWindow.get()));
+}
+
+static void lcl_setCheckBoxState( const Reference< css::sdb::XColumn >& _rxField,
+ CheckBoxControl* _pCheckBoxControl )
+{
+ TriState eState = TRISTATE_INDET;
+ if (_rxField.is())
+ {
+ try
+ {
+ bool bValue = _rxField->getBoolean();
+ if (!_rxField->wasNull())
+ eState = bValue ? TRISTATE_TRUE : TRISTATE_FALSE;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ _pCheckBoxControl->GetBox().SetState(eState);
+}
+
+
+void DbCheckBox::UpdateFromField(const Reference< css::sdb::XColumn >& _rxField, const Reference< XNumberFormatter >& /*xFormatter*/)
+{
+ lcl_setCheckBoxState( _rxField, static_cast<CheckBoxControl*>(m_pWindow.get()) );
+}
+
+
+void DbCheckBox::PaintFieldToCell(OutputDevice& rDev, const tools::Rectangle& rRect,
+ const Reference< css::sdb::XColumn >& _rxField,
+ const Reference< XNumberFormatter >& xFormatter)
+{
+ lcl_setCheckBoxState( _rxField, static_cast<CheckBoxControl*>(m_pPainter.get()) );
+ DbCellControl::PaintFieldToCell( rDev, rRect, _rxField, xFormatter );
+}
+
+
+void DbCheckBox::updateFromModel( Reference< XPropertySet > _rxModel )
+{
+ OSL_ENSURE( _rxModel.is() && m_pWindow, "DbCheckBox::updateFromModel: invalid call!" );
+
+ sal_Int16 nState = TRISTATE_INDET;
+ _rxModel->getPropertyValue( FM_PROP_STATE ) >>= nState;
+ static_cast< CheckBoxControl* >( m_pWindow.get() )->GetBox().SetState( static_cast< TriState >( nState ) );
+}
+
+
+bool DbCheckBox::commitControl()
+{
+ m_rColumn.getModel()->setPropertyValue( FM_PROP_STATE,
+ makeAny( static_cast<sal_Int16>( static_cast< CheckBoxControl* >( m_pWindow.get() )->GetBox().GetState() ) ) );
+ return true;
+}
+
+
+OUString DbCheckBox::GetFormatText(const Reference< XColumn >& /*_rxField*/, const Reference< XNumberFormatter >& /*xFormatter*/, Color** /*ppColor*/)
+{
+ return OUString();
+}
+
+DbPatternField::DbPatternField( DbGridColumn& _rColumn, const Reference<XComponentContext>& _rContext )
+ :DbCellControl( _rColumn )
+ ,m_xContext( _rContext )
+{
+ doPropertyListening( FM_PROP_LITERALMASK );
+ doPropertyListening( FM_PROP_EDITMASK );
+ doPropertyListening( FM_PROP_STRICTFORMAT );
+}
+
+
+void DbPatternField::implAdjustGenericFieldSetting( const Reference< XPropertySet >& _rxModel )
+{
+ DBG_ASSERT( m_pWindow, "DbPatternField::implAdjustGenericFieldSetting: not to be called without window!" );
+ DBG_ASSERT( _rxModel.is(), "DbPatternField::implAdjustGenericFieldSetting: invalid model!" );
+ if ( !m_pWindow || !_rxModel.is() )
+ return;
+
+ OUString aLitMask;
+ OUString aEditMask;
+ bool bStrict = false;
+
+ _rxModel->getPropertyValue( FM_PROP_LITERALMASK ) >>= aLitMask;
+ _rxModel->getPropertyValue( FM_PROP_EDITMASK ) >>= aEditMask;
+ _rxModel->getPropertyValue( FM_PROP_STRICTFORMAT ) >>= bStrict;
+
+ OString aAsciiEditMask(OUStringToOString(aEditMask, RTL_TEXTENCODING_ASCII_US));
+
+ static_cast< PatternField* >( m_pWindow.get() )->SetMask( aAsciiEditMask, aLitMask );
+ static_cast< PatternField* >( m_pPainter.get() )->SetMask( aAsciiEditMask, aLitMask );
+ static_cast< PatternField* >( m_pWindow.get() )->SetStrictFormat( bStrict );
+ static_cast< PatternField* >( m_pPainter.get() )->SetStrictFormat( bStrict );
+}
+
+
+void DbPatternField::Init( vcl::Window& rParent, const Reference< XRowSet >& xCursor)
+{
+ m_rColumn.SetAlignmentFromModel(-1);
+
+ m_pWindow = VclPtr<PatternField>::Create( &rParent, 0 );
+ m_pPainter= VclPtr<PatternField>::Create( &rParent, 0 );
+
+ Reference< XPropertySet > xModel( m_rColumn.getModel() );
+ implAdjustGenericFieldSetting( xModel );
+
+ DbCellControl::Init( rParent, xCursor );
+}
+
+
+CellControllerRef DbPatternField::CreateController() const
+{
+ return new SpinCellController( static_cast< PatternField* >( m_pWindow.get() ) );
+}
+
+
+OUString DbPatternField::impl_formatText( const OUString& _rText )
+{
+ m_pPainter->SetText( _rText );
+ static_cast< PatternField* >( m_pPainter.get() )->ReformatAll();
+ return m_pPainter->GetText();
+}
+
+
+OUString DbPatternField::GetFormatText(const Reference< css::sdb::XColumn >& _rxField, const Reference< XNumberFormatter >& /*xFormatter*/, Color** /*ppColor*/)
+{
+ bool bIsForPaint = _rxField != m_rColumn.GetField();
+ ::std::unique_ptr< FormattedColumnValue >& rpFormatter = bIsForPaint ? m_pPaintFormatter : m_pValueFormatter;
+
+ if (!rpFormatter)
+ {
+ rpFormatter = std::make_unique< FormattedColumnValue> (
+ m_xContext, getCursor(), Reference< XPropertySet >( _rxField, UNO_QUERY ) );
+ OSL_ENSURE(rpFormatter, "DbPatternField::Init: no value formatter!");
+ }
+ else
+ OSL_ENSURE( rpFormatter->getColumn() == _rxField, "DbPatternField::GetFormatText: my value formatter is working for another field ...!" );
+ // re-creating the value formatter here every time would be quite expensive ...
+
+ OUString sText;
+ if (rpFormatter)
+ sText = rpFormatter->getFormattedValue();
+
+ return impl_formatText( sText );
+}
+
+
+void DbPatternField::UpdateFromField( const Reference< XColumn >& _rxField, const Reference< XNumberFormatter >& _rxFormatter )
+{
+ static_cast< Edit* >( m_pWindow.get() )->SetText( GetFormatText( _rxField, _rxFormatter ) );
+ static_cast< Edit* >( m_pWindow.get() )->SetSelection( Selection( SELECTION_MAX, SELECTION_MIN ) );
+}
+
+
+void DbPatternField::updateFromModel( Reference< XPropertySet > _rxModel )
+{
+ OSL_ENSURE( _rxModel.is() && m_pWindow, "DbPatternField::updateFromModel: invalid call!" );
+
+ OUString sText;
+ _rxModel->getPropertyValue( FM_PROP_TEXT ) >>= sText;
+
+ static_cast< Edit* >( m_pWindow.get() )->SetText( impl_formatText( sText ) );
+ static_cast< Edit* >( m_pWindow.get() )->SetSelection( Selection( SELECTION_MAX, SELECTION_MIN ) );
+}
+
+
+bool DbPatternField::commitControl()
+{
+ OUString aText(m_pWindow->GetText());
+ m_rColumn.getModel()->setPropertyValue(FM_PROP_TEXT, makeAny(aText));
+ return true;
+}
+
+DbSpinField::DbSpinField( DbGridColumn& _rColumn, sal_Int16 _nStandardAlign )
+ :DbCellControl( _rColumn )
+ ,m_nStandardAlign( _nStandardAlign )
+{
+}
+
+
+void DbSpinField::Init( vcl::Window& _rParent, const Reference< XRowSet >& _rxCursor )
+{
+ m_rColumn.SetAlignmentFromModel( m_nStandardAlign );
+
+ Reference< XPropertySet > xModel( m_rColumn.getModel() );
+
+ // determine the WinBits for the field
+ WinBits nFieldStyle = 0;
+ if ( ::comphelper::getBOOL( xModel->getPropertyValue( FM_PROP_SPIN ) ) )
+ nFieldStyle = WB_REPEAT | WB_SPIN;
+ // create the fields
+ m_pWindow = createField( &_rParent, nFieldStyle, xModel );
+ m_pPainter = createField( &_rParent, nFieldStyle, xModel );
+
+ // adjust all other settings which depend on the property values
+ implAdjustGenericFieldSetting( xModel );
+
+ // call the base class
+ DbCellControl::Init( _rParent, _rxCursor );
+}
+
+
+CellControllerRef DbSpinField::CreateController() const
+{
+ return new SpinCellController( static_cast< SpinField* >( m_pWindow.get() ) );
+}
+
+DbNumericField::DbNumericField( DbGridColumn& _rColumn )
+ :DbSpinField( _rColumn )
+{
+ doPropertyListening( FM_PROP_DECIMAL_ACCURACY );
+ doPropertyListening( FM_PROP_VALUEMIN );
+ doPropertyListening( FM_PROP_VALUEMAX );
+ doPropertyListening( FM_PROP_VALUESTEP );
+ doPropertyListening( FM_PROP_STRICTFORMAT );
+ doPropertyListening( FM_PROP_SHOWTHOUSANDSEP );
+}
+
+
+void DbNumericField::implAdjustGenericFieldSetting( const Reference< XPropertySet >& _rxModel )
+{
+ DBG_ASSERT( m_pWindow, "DbNumericField::implAdjustGenericFieldSetting: not to be called without window!" );
+ DBG_ASSERT( _rxModel.is(), "DbNumericField::implAdjustGenericFieldSetting: invalid model!" );
+ if ( !m_pWindow || !_rxModel.is() )
+ return;
+
+ sal_Int32 nMin = static_cast<sal_Int32>(getDouble( _rxModel->getPropertyValue( FM_PROP_VALUEMIN ) ));
+ sal_Int32 nMax = static_cast<sal_Int32>(getDouble( _rxModel->getPropertyValue( FM_PROP_VALUEMAX ) ));
+ sal_Int32 nStep = static_cast<sal_Int32>(getDouble( _rxModel->getPropertyValue( FM_PROP_VALUESTEP ) ));
+ bool bStrict = getBOOL( _rxModel->getPropertyValue( FM_PROP_STRICTFORMAT ) );
+ sal_Int16 nScale = getINT16( _rxModel->getPropertyValue( FM_PROP_DECIMAL_ACCURACY ) );
+ bool bThousand = getBOOL( _rxModel->getPropertyValue( FM_PROP_SHOWTHOUSANDSEP ) );
+
+ static_cast< DoubleNumericField* >( m_pWindow.get() )->SetMinValue(nMin);
+ static_cast< DoubleNumericField* >( m_pWindow.get() )->SetMaxValue(nMax);
+ static_cast< DoubleNumericField* >( m_pWindow.get() )->SetSpinSize(nStep);
+ static_cast< DoubleNumericField* >( m_pWindow.get() )->SetStrictFormat(bStrict);
+
+ static_cast< DoubleNumericField* >( m_pPainter.get() )->SetMinValue(nMin);
+ static_cast< DoubleNumericField* >( m_pPainter.get() )->SetMaxValue(nMax);
+ static_cast< DoubleNumericField* >( m_pPainter.get() )->SetStrictFormat(bStrict);
+
+
+ // give a formatter to the field and the painter;
+ // test first if I can get from the service behind a connection
+ Reference< css::util::XNumberFormatsSupplier > xSupplier;
+ Reference< XRowSet > xForm;
+ if ( m_rColumn.GetParent().getDataSource() )
+ xForm.set( Reference< XInterface >(*m_rColumn.GetParent().getDataSource()), UNO_QUERY );
+ if ( xForm.is() )
+ xSupplier = getNumberFormats( getConnection( xForm ), true );
+ SvNumberFormatter* pFormatterUsed = nullptr;
+ if ( xSupplier.is() )
+ {
+ SvNumberFormatsSupplierObj* pImplmentation = comphelper::getUnoTunnelImplementation<SvNumberFormatsSupplierObj>( xSupplier );
+ pFormatterUsed = pImplmentation ? pImplmentation->GetNumberFormatter() : nullptr;
+ }
+ if ( nullptr == pFormatterUsed )
+ { // the cursor didn't lead to success -> standard
+ pFormatterUsed = static_cast< DoubleNumericField* >( m_pWindow.get() )->StandardFormatter();
+ DBG_ASSERT( pFormatterUsed != nullptr, "DbNumericField::implAdjustGenericFieldSetting: no standard formatter given by the numeric field !" );
+ }
+ static_cast< DoubleNumericField* >( m_pWindow.get() )->SetFormatter( pFormatterUsed );
+ static_cast< DoubleNumericField* >( m_pPainter.get() )->SetFormatter( pFormatterUsed );
+
+ // and then generate a format which has the desired length after the decimal point, etc.
+ LanguageType aAppLanguage = Application::GetSettings().GetUILanguageTag().getLanguageType();
+ OUString sFormatString = pFormatterUsed->GenerateFormat(0, aAppLanguage, bThousand, false, nScale);
+
+ static_cast< DoubleNumericField* >( m_pWindow.get() )->SetFormat( sFormatString, aAppLanguage );
+ static_cast< DoubleNumericField* >( m_pPainter.get() )->SetFormat( sFormatString, aAppLanguage );
+}
+
+
+VclPtr<SpinField> DbNumericField::createField( vcl::Window* _pParent, WinBits _nFieldStyle, const Reference< XPropertySet >& /*_rxModel*/ )
+{
+ return VclPtr<DoubleNumericField>::Create( _pParent, _nFieldStyle );
+}
+
+namespace
+{
+
+ OUString lcl_setFormattedNumeric_nothrow( DoubleNumericField& _rField, const DbCellControl& _rControl,
+ const Reference< XColumn >& _rxField, const Reference< XNumberFormatter >& _rxFormatter )
+ {
+ OUString sValue;
+ if ( _rxField.is() )
+ {
+ try
+ {
+ double fValue = _rControl.GetValue( _rxField, _rxFormatter );
+ if ( !_rxField->wasNull() )
+ {
+ _rField.SetValue( fValue );
+ sValue = _rField.GetText();
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ return sValue;
+ }
+}
+
+
+OUString DbNumericField::GetFormatText(const Reference< css::sdb::XColumn >& _rxField, const Reference< css::util::XNumberFormatter >& _rxFormatter, Color** /*ppColor*/)
+{
+ return lcl_setFormattedNumeric_nothrow(dynamic_cast<DoubleNumericField&>(*m_pPainter), *this, _rxField, _rxFormatter);
+}
+
+
+void DbNumericField::UpdateFromField(const Reference< css::sdb::XColumn >& _rxField, const Reference< css::util::XNumberFormatter >& _rxFormatter)
+{
+ lcl_setFormattedNumeric_nothrow(dynamic_cast<DoubleNumericField&>(*m_pWindow), *this, _rxField, _rxFormatter);
+}
+
+
+void DbNumericField::updateFromModel( Reference< XPropertySet > _rxModel )
+{
+ OSL_ENSURE( _rxModel.is() && m_pWindow, "DbNumericField::updateFromModel: invalid call!" );
+
+ double dValue = 0;
+ if ( _rxModel->getPropertyValue( FM_PROP_VALUE ) >>= dValue )
+ static_cast< DoubleNumericField* >( m_pWindow.get() )->SetValue( dValue );
+ else
+ m_pWindow->SetText( OUString() );
+}
+
+
+bool DbNumericField::commitControl()
+{
+ OUString aText( m_pWindow->GetText());
+ Any aVal;
+
+ if (!aText.isEmpty()) // not empty
+ {
+ double fValue = static_cast<DoubleNumericField*>(m_pWindow.get())->GetValue();
+ aVal <<= fValue;
+ }
+ m_rColumn.getModel()->setPropertyValue(FM_PROP_VALUE, aVal);
+ return true;
+}
+
+DbCurrencyField::DbCurrencyField(DbGridColumn& _rColumn)
+ :DbSpinField( _rColumn )
+ ,m_nScale( 0 )
+{
+ doPropertyListening( FM_PROP_DECIMAL_ACCURACY );
+ doPropertyListening( FM_PROP_VALUEMIN );
+ doPropertyListening( FM_PROP_VALUEMAX );
+ doPropertyListening( FM_PROP_VALUESTEP );
+ doPropertyListening( FM_PROP_STRICTFORMAT );
+ doPropertyListening( FM_PROP_SHOWTHOUSANDSEP );
+ doPropertyListening( FM_PROP_CURRENCYSYMBOL );
+}
+
+
+void DbCurrencyField::implAdjustGenericFieldSetting( const Reference< XPropertySet >& _rxModel )
+{
+ DBG_ASSERT( m_pWindow, "DbCurrencyField::implAdjustGenericFieldSetting: not to be called without window!" );
+ DBG_ASSERT( _rxModel.is(), "DbCurrencyField::implAdjustGenericFieldSetting: invalid model!" );
+ if ( !m_pWindow || !_rxModel.is() )
+ return;
+
+ m_nScale = getINT16( _rxModel->getPropertyValue( FM_PROP_DECIMAL_ACCURACY ) );
+ double nMin = getDouble( _rxModel->getPropertyValue( FM_PROP_VALUEMIN ) );
+ double nMax = getDouble( _rxModel->getPropertyValue( FM_PROP_VALUEMAX ) );
+ double nStep = getDouble( _rxModel->getPropertyValue( FM_PROP_VALUESTEP ) );
+ bool bStrict = getBOOL( _rxModel->getPropertyValue( FM_PROP_STRICTFORMAT ) );
+ bool bThousand = getBOOL( _rxModel->getPropertyValue( FM_PROP_SHOWTHOUSANDSEP ) );
+ OUString aStr( getString( _rxModel->getPropertyValue(FM_PROP_CURRENCYSYMBOL ) ) );
+
+ //fdo#42747 the min/max/first/last of vcl NumericFormatters needs to be
+ //multiplied by the no of decimal places. See also
+ //VclBuilder::mungeAdjustment
+ int nMul = rtl_math_pow10Exp(1, m_nScale);
+ nMin *= nMul;
+ nMax *= nMul;
+
+ static_cast< LongCurrencyField* >( m_pWindow.get() )->SetUseThousandSep( bThousand );
+ static_cast< LongCurrencyField* >( m_pWindow.get() )->SetDecimalDigits( m_nScale );
+ static_cast< LongCurrencyField* >( m_pWindow.get() )->SetCurrencySymbol( aStr );
+ static_cast< LongCurrencyField* >( m_pWindow.get() )->SetFirst( nMin );
+ static_cast< LongCurrencyField* >( m_pWindow.get() )->SetLast( nMax );
+ static_cast< LongCurrencyField* >( m_pWindow.get() )->SetMin( nMin );
+ static_cast< LongCurrencyField* >( m_pWindow.get() )->SetMax( nMax );
+ static_cast< LongCurrencyField* >( m_pWindow.get() )->SetSpinSize( nStep );
+ static_cast< LongCurrencyField* >( m_pWindow.get() )->SetStrictFormat( bStrict );
+
+ static_cast< LongCurrencyField* >( m_pPainter.get() )->SetUseThousandSep( bThousand );
+ static_cast< LongCurrencyField* >( m_pPainter.get() )->SetDecimalDigits( m_nScale );
+ static_cast< LongCurrencyField* >( m_pPainter.get() )->SetCurrencySymbol( aStr );
+ static_cast< LongCurrencyField* >( m_pPainter.get() )->SetFirst( nMin );
+ static_cast< LongCurrencyField* >( m_pPainter.get() )->SetLast( nMax );
+ static_cast< LongCurrencyField* >( m_pPainter.get() )->SetMin( nMin );
+ static_cast< LongCurrencyField* >( m_pPainter.get() )->SetMax( nMax );
+ static_cast< LongCurrencyField* >( m_pPainter.get() )->SetStrictFormat( bStrict );
+}
+
+
+VclPtr<SpinField> DbCurrencyField::createField( vcl::Window* _pParent, WinBits _nFieldStyle, const Reference< XPropertySet >& /*_rxModel*/ )
+{
+ return VclPtr<LongCurrencyField>::Create( _pParent, _nFieldStyle );
+}
+
+
+double DbCurrencyField::GetCurrency(const Reference< css::sdb::XColumn >& _rxField, const Reference< XNumberFormatter >& xFormatter) const
+{
+ double fValue = GetValue(_rxField, xFormatter);
+ if (m_nScale)
+ {
+ // SAL_INFO("svx",("double = %.64f ",fValue);
+ fValue = ::rtl::math::pow10Exp(fValue, m_nScale);
+ fValue = ::rtl::math::round(fValue);
+ }
+ return fValue;
+}
+
+namespace
+{
+
+ OUString lcl_setFormattedCurrency_nothrow( LongCurrencyField& _rField, const DbCurrencyField& _rControl,
+ const Reference< XColumn >& _rxField, const Reference< XNumberFormatter >& _rxFormatter )
+ {
+ OUString sValue;
+ if ( _rxField.is() )
+ {
+ try
+ {
+ double fValue = _rControl.GetCurrency( _rxField, _rxFormatter );
+ if ( !_rxField->wasNull() )
+ {
+ _rField.SetValue( fValue );
+ sValue = _rField.GetText();
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ return sValue;
+ }
+}
+
+
+OUString DbCurrencyField::GetFormatText(const Reference< css::sdb::XColumn >& _rxField, const Reference< css::util::XNumberFormatter >& _rxFormatter, Color** /*ppColor*/)
+{
+ return lcl_setFormattedCurrency_nothrow( dynamic_cast< LongCurrencyField& >( *m_pPainter ), *this, _rxField, _rxFormatter );
+}
+
+
+void DbCurrencyField::UpdateFromField(const Reference< css::sdb::XColumn >& _rxField, const Reference< css::util::XNumberFormatter >& _rxFormatter)
+{
+ lcl_setFormattedCurrency_nothrow( dynamic_cast< LongCurrencyField& >( *m_pWindow ), *this, _rxField, _rxFormatter );
+}
+
+
+void DbCurrencyField::updateFromModel( Reference< XPropertySet > _rxModel )
+{
+ OSL_ENSURE( _rxModel.is() && m_pWindow, "DbCurrencyField::updateFromModel: invalid call!" );
+
+ double dValue = 0;
+ if ( _rxModel->getPropertyValue( FM_PROP_VALUE ) >>= dValue )
+ {
+ if ( m_nScale )
+ {
+ dValue = ::rtl::math::pow10Exp( dValue, m_nScale );
+ dValue = ::rtl::math::round(dValue);
+ }
+
+ static_cast< LongCurrencyField* >( m_pWindow.get() )->SetValue( dValue );
+ }
+ else
+ m_pWindow->SetText( OUString() );
+}
+
+
+bool DbCurrencyField::commitControl()
+{
+ OUString aText(m_pWindow->GetText());
+ Any aVal;
+ if (!aText.isEmpty()) // not empty
+ {
+ double fValue = static_cast<LongCurrencyField*>(m_pWindow.get())->GetValue();
+ if (m_nScale)
+ {
+ fValue /= ::rtl::math::pow10Exp(1.0, m_nScale);
+ }
+ aVal <<= fValue;
+ }
+ m_rColumn.getModel()->setPropertyValue(FM_PROP_VALUE, aVal);
+ return true;
+}
+
+DbDateField::DbDateField( DbGridColumn& _rColumn )
+ :DbSpinField( _rColumn )
+{
+ doPropertyListening( FM_PROP_DATEFORMAT );
+ doPropertyListening( FM_PROP_DATEMIN );
+ doPropertyListening( FM_PROP_DATEMAX );
+ doPropertyListening( FM_PROP_STRICTFORMAT );
+ doPropertyListening( FM_PROP_DATE_SHOW_CENTURY );
+}
+
+
+VclPtr<SpinField> DbDateField::createField( vcl::Window* _pParent, WinBits _nFieldStyle, const Reference< XPropertySet >& _rxModel )
+{
+ // check if there is a DropDown property set to TRUE
+ bool bDropDown = !hasProperty( FM_PROP_DROPDOWN, _rxModel )
+ || getBOOL( _rxModel->getPropertyValue( FM_PROP_DROPDOWN ) );
+ if ( bDropDown )
+ _nFieldStyle |= WB_DROPDOWN;
+
+ VclPtr<CalendarField> pField = VclPtr<CalendarField>::Create( _pParent, _nFieldStyle );
+
+ pField->EnableToday();
+ pField->EnableNone();
+
+ return pField;
+}
+
+
+void DbDateField::implAdjustGenericFieldSetting( const Reference< XPropertySet >& _rxModel )
+{
+ DBG_ASSERT( m_pWindow, "DbDateField::implAdjustGenericFieldSetting: not to be called without window!" );
+ DBG_ASSERT( _rxModel.is(), "DbDateField::implAdjustGenericFieldSetting: invalid model!" );
+ if ( !m_pWindow || !_rxModel.is() )
+ return;
+
+ sal_Int16 nFormat = getINT16( _rxModel->getPropertyValue( FM_PROP_DATEFORMAT ) );
+ util::Date aMin;
+ OSL_VERIFY( _rxModel->getPropertyValue( FM_PROP_DATEMIN ) >>= aMin );
+ util::Date aMax;
+ OSL_VERIFY( _rxModel->getPropertyValue( FM_PROP_DATEMAX ) >>= aMax );
+ bool bStrict = getBOOL( _rxModel->getPropertyValue( FM_PROP_STRICTFORMAT ) );
+
+ Any aCentury = _rxModel->getPropertyValue( FM_PROP_DATE_SHOW_CENTURY );
+ if ( aCentury.getValueType().getTypeClass() != TypeClass_VOID )
+ {
+ bool bShowDateCentury = getBOOL( aCentury );
+
+ static_cast<DateField*>( m_pWindow.get() )->SetShowDateCentury( bShowDateCentury );
+ static_cast<DateField*>( m_pPainter.get() )->SetShowDateCentury( bShowDateCentury );
+ }
+
+ static_cast< DateField* >( m_pWindow.get() )->SetExtDateFormat( static_cast<ExtDateFieldFormat>(nFormat) );
+ static_cast< DateField* >( m_pWindow.get() )->SetMin( aMin );
+ static_cast< DateField* >( m_pWindow.get() )->SetMax( aMax );
+ static_cast< DateField* >( m_pWindow.get() )->SetStrictFormat( bStrict );
+ static_cast< DateField* >( m_pWindow.get() )->EnableEmptyFieldValue( true );
+
+ static_cast< DateField* >( m_pPainter.get() )->SetExtDateFormat( static_cast<ExtDateFieldFormat>(nFormat) );
+ static_cast< DateField* >( m_pPainter.get() )->SetMin( aMin );
+ static_cast< DateField* >( m_pPainter.get() )->SetMax( aMax );
+ static_cast< DateField* >( m_pPainter.get() )->SetStrictFormat( bStrict );
+ static_cast< DateField* >( m_pPainter.get() )->EnableEmptyFieldValue( true );
+}
+
+namespace
+{
+
+ OUString lcl_setFormattedDate_nothrow( DateField& _rField, const Reference< XColumn >& _rxField )
+ {
+ OUString sDate;
+ if ( _rxField.is() )
+ {
+ try
+ {
+ css::util::Date aValue = _rxField->getDate();
+ if ( _rxField->wasNull() )
+ _rField.SetText( sDate );
+ else
+ {
+ _rField.SetDate( ::Date( aValue.Day, aValue.Month, aValue.Year ) );
+ sDate = _rField.GetText();
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ return sDate;
+ }
+}
+
+OUString DbDateField::GetFormatText(const Reference< css::sdb::XColumn >& _rxField, const Reference< css::util::XNumberFormatter >& /*xFormatter*/, Color** /*ppColor*/)
+{
+ return lcl_setFormattedDate_nothrow(dynamic_cast<DateField&>(*m_pPainter), _rxField);
+}
+
+
+void DbDateField::UpdateFromField(const Reference< css::sdb::XColumn >& _rxField, const Reference< XNumberFormatter >& /*xFormatter*/)
+{
+ lcl_setFormattedDate_nothrow(dynamic_cast<DateField&>(*m_pWindow), _rxField);
+}
+
+
+void DbDateField::updateFromModel( Reference< XPropertySet > _rxModel )
+{
+ OSL_ENSURE( _rxModel.is() && m_pWindow, "DbDateField::updateFromModel: invalid call!" );
+
+ util::Date aDate;
+ if ( _rxModel->getPropertyValue( FM_PROP_DATE ) >>= aDate )
+ static_cast< DateField* >( m_pWindow.get() )->SetDate( ::Date( aDate ) );
+ else
+ static_cast< DateField* >( m_pWindow.get() )->SetText( OUString() );
+}
+
+
+bool DbDateField::commitControl()
+{
+ OUString aText(m_pWindow->GetText());
+ Any aVal;
+ if (!aText.isEmpty())
+ aVal <<= static_cast<DateField*>(m_pWindow.get())->GetDate().GetUNODate();
+ else
+ aVal.clear();
+
+ m_rColumn.getModel()->setPropertyValue(FM_PROP_DATE, aVal);
+ return true;
+}
+
+DbTimeField::DbTimeField( DbGridColumn& _rColumn )
+ :DbSpinField( _rColumn, css::awt::TextAlign::LEFT )
+{
+ doPropertyListening( FM_PROP_TIMEFORMAT );
+ doPropertyListening( FM_PROP_TIMEMIN );
+ doPropertyListening( FM_PROP_TIMEMAX );
+ doPropertyListening( FM_PROP_STRICTFORMAT );
+}
+
+
+VclPtr<SpinField> DbTimeField::createField( vcl::Window* _pParent, WinBits _nFieldStyle, const Reference< XPropertySet >& /*_rxModel*/ )
+{
+ return VclPtr<TimeField>::Create( _pParent, _nFieldStyle );
+}
+
+
+void DbTimeField::implAdjustGenericFieldSetting( const Reference< XPropertySet >& _rxModel )
+{
+ DBG_ASSERT( m_pWindow, "DbTimeField::implAdjustGenericFieldSetting: not to be called without window!" );
+ DBG_ASSERT( _rxModel.is(), "DbTimeField::implAdjustGenericFieldSetting: invalid model!" );
+ if ( !m_pWindow || !_rxModel.is() )
+ return;
+
+ sal_Int16 nFormat = getINT16( _rxModel->getPropertyValue( FM_PROP_TIMEFORMAT ) );
+ util::Time aMin;
+ OSL_VERIFY( _rxModel->getPropertyValue( FM_PROP_TIMEMIN ) >>= aMin );
+ util::Time aMax;
+ OSL_VERIFY( _rxModel->getPropertyValue( FM_PROP_TIMEMAX ) >>= aMax );
+ bool bStrict = getBOOL( _rxModel->getPropertyValue( FM_PROP_STRICTFORMAT ) );
+
+ static_cast< TimeField* >( m_pWindow.get() )->SetExtFormat( static_cast<ExtTimeFieldFormat>(nFormat) );
+ static_cast< TimeField* >( m_pWindow.get() )->SetMin( aMin );
+ static_cast< TimeField* >( m_pWindow.get() )->SetMax( aMax );
+ static_cast< TimeField* >( m_pWindow.get() )->SetStrictFormat( bStrict );
+ static_cast< TimeField* >( m_pWindow.get() )->EnableEmptyFieldValue( true );
+
+ static_cast< TimeField* >( m_pPainter.get() )->SetExtFormat( static_cast<ExtTimeFieldFormat>(nFormat) );
+ static_cast< TimeField* >( m_pPainter.get() )->SetMin( aMin );
+ static_cast< TimeField* >( m_pPainter.get() )->SetMax( aMax );
+ static_cast< TimeField* >( m_pPainter.get() )->SetStrictFormat( bStrict );
+ static_cast< TimeField* >( m_pPainter.get() )->EnableEmptyFieldValue( true );
+}
+
+namespace
+{
+
+ OUString lcl_setFormattedTime_nothrow( TimeField& _rField, const Reference< XColumn >& _rxField )
+ {
+ OUString sTime;
+ if ( _rxField.is() )
+ {
+ try
+ {
+ css::util::Time aValue = _rxField->getTime();
+ if ( _rxField->wasNull() )
+ _rField.SetText( sTime );
+ else
+ {
+ _rField.SetTime( ::tools::Time( aValue ) );
+ sTime = _rField.GetText();
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ return sTime;
+ }
+}
+
+OUString DbTimeField::GetFormatText(const Reference< css::sdb::XColumn >& _rxField, const Reference< css::util::XNumberFormatter >& /*xFormatter*/, Color** /*ppColor*/)
+{
+ return lcl_setFormattedTime_nothrow( *static_cast< TimeField* >( m_pPainter.get() ), _rxField );
+}
+
+
+void DbTimeField::UpdateFromField(const Reference< css::sdb::XColumn >& _rxField, const Reference< XNumberFormatter >& /*xFormatter*/)
+{
+ lcl_setFormattedTime_nothrow( *static_cast< TimeField* >( m_pWindow.get() ), _rxField );
+}
+
+
+void DbTimeField::updateFromModel( Reference< XPropertySet > _rxModel )
+{
+ OSL_ENSURE( _rxModel.is() && m_pWindow, "DbTimeField::updateFromModel: invalid call!" );
+
+ util::Time aTime;
+ if ( _rxModel->getPropertyValue( FM_PROP_TIME ) >>= aTime )
+ static_cast< TimeField* >( m_pWindow.get() )->SetTime( ::tools::Time( aTime ) );
+ else
+ static_cast< TimeField* >( m_pWindow.get() )->SetText( OUString() );
+}
+
+
+bool DbTimeField::commitControl()
+{
+ OUString aText(m_pWindow->GetText());
+ Any aVal;
+ if (!aText.isEmpty())
+ aVal <<= static_cast<TimeField*>(m_pWindow.get())->GetTime().GetUNOTime();
+ else
+ aVal.clear();
+
+ m_rColumn.getModel()->setPropertyValue(FM_PROP_TIME, aVal);
+ return true;
+}
+
+DbComboBox::DbComboBox(DbGridColumn& _rColumn)
+ :DbCellControl(_rColumn)
+{
+ setAlignedController( false );
+
+ doPropertyListening( FM_PROP_STRINGITEMLIST );
+ doPropertyListening( FM_PROP_LINECOUNT );
+}
+
+void DbComboBox::_propertyChanged( const PropertyChangeEvent& _rEvent )
+{
+ if ( _rEvent.PropertyName == FM_PROP_STRINGITEMLIST )
+ {
+ SetList(_rEvent.NewValue);
+ }
+ else
+ {
+ DbCellControl::_propertyChanged( _rEvent ) ;
+ }
+}
+
+void DbComboBox::SetList(const Any& rItems)
+{
+ ComboBoxControl* pField = static_cast<ComboBoxControl*>(m_pWindow.get());
+ weld::ComboBox& rComboBox = pField->get_widget();
+ rComboBox.clear();
+
+ css::uno::Sequence<OUString> aTest;
+ if (rItems >>= aTest)
+ {
+ for (const OUString& rString : std::as_const(aTest))
+ rComboBox.append_text(rString);
+
+ // tell the grid control that this controller is invalid and has to be re-initialized
+ invalidatedController();
+ }
+}
+
+void DbComboBox::implAdjustGenericFieldSetting(const Reference<XPropertySet>&)
+{
+ // we no longer pay attention to FM_PROP_LINECOUNT
+}
+
+void DbComboBox::Init( vcl::Window& rParent, const Reference< XRowSet >& xCursor )
+{
+ m_rColumn.SetAlignmentFromModel(css::awt::TextAlign::LEFT);
+
+ m_pWindow = VclPtr<ComboBoxControl>::Create( &rParent );
+
+ // selection from right to left
+ AllSettings aSettings = m_pWindow->GetSettings();
+ StyleSettings aStyleSettings = aSettings.GetStyleSettings();
+ aStyleSettings.SetSelectionOptions(
+ aStyleSettings.GetSelectionOptions() | SelectionOptions::ShowFirst);
+ aSettings.SetStyleSettings(aStyleSettings);
+ m_pWindow->SetSettings(aSettings, true);
+
+ // some initial properties
+ Reference< XPropertySet > xModel(m_rColumn.getModel());
+ SetList( xModel->getPropertyValue( FM_PROP_STRINGITEMLIST ) );
+ implAdjustGenericFieldSetting( xModel );
+
+ DbCellControl::Init( rParent, xCursor );
+}
+
+
+CellControllerRef DbComboBox::CreateController() const
+{
+ return new ComboBoxCellController(static_cast<ComboBoxControl*>(m_pWindow.get()));
+}
+
+
+OUString DbComboBox::GetFormatText(const Reference< css::sdb::XColumn >& _rxField, const Reference< XNumberFormatter >& xFormatter, Color** /*ppColor*/)
+{
+ const css::uno::Reference<css::beans::XPropertySet> xPS(_rxField, UNO_QUERY);
+ ::dbtools::FormattedColumnValue fmter( xFormatter, xPS );
+
+ return fmter.getFormattedValue();
+}
+
+void DbComboBox::UpdateFromField(const Reference< css::sdb::XColumn >& _rxField, const Reference< XNumberFormatter >& xFormatter)
+{
+ ComboBoxControl* pControl = static_cast<ComboBoxControl*>(m_pWindow.get());
+ pControl->get_widget().set_entry_text(GetFormatText(_rxField, xFormatter));
+}
+
+void DbComboBox::updateFromModel( Reference< XPropertySet > _rxModel )
+{
+ OSL_ENSURE( _rxModel.is() && m_pWindow, "DbComboBox::updateFromModel: invalid call!" );
+
+ OUString sText;
+ _rxModel->getPropertyValue( FM_PROP_TEXT ) >>= sText;
+
+ ComboBoxControl* pControl = static_cast<ComboBoxControl*>(m_pWindow.get());
+ weld::ComboBox& rComboBox = pControl->get_widget();
+ rComboBox.set_entry_text(sText);
+ rComboBox.select_entry_region(0, -1);
+}
+
+bool DbComboBox::commitControl()
+{
+ ComboBoxControl* pControl = static_cast<ComboBoxControl*>(m_pWindow.get());
+ weld::ComboBox& rComboBox = pControl->get_widget();
+ OUString aText(rComboBox.get_active_text());
+ m_rColumn.getModel()->setPropertyValue(FM_PROP_TEXT, makeAny(aText));
+ return true;
+}
+
+
+DbListBox::DbListBox(DbGridColumn& _rColumn)
+ :DbCellControl(_rColumn)
+ ,m_bBound(false)
+{
+ setAlignedController( false );
+
+ doPropertyListening( FM_PROP_STRINGITEMLIST );
+ doPropertyListening( FM_PROP_LINECOUNT );
+}
+
+void DbListBox::_propertyChanged( const css::beans::PropertyChangeEvent& _rEvent )
+{
+ if ( _rEvent.PropertyName == FM_PROP_STRINGITEMLIST )
+ {
+ SetList(_rEvent.NewValue);
+ }
+ else
+ {
+ DbCellControl::_propertyChanged( _rEvent ) ;
+ }
+}
+
+void DbListBox::SetList(const Any& rItems)
+{
+ ListBoxControl* pField = static_cast<ListBoxControl*>(m_pWindow.get());
+
+ weld::ComboBox& rFieldList = pField->get_widget();
+
+ rFieldList.clear();
+ m_bBound = false;
+
+ css::uno::Sequence<OUString> aTest;
+ if (rItems >>= aTest)
+ {
+ if (aTest.hasElements())
+ {
+ for (const OUString& rString : std::as_const(aTest))
+ rFieldList.append_text(rString);
+
+ m_rColumn.getModel()->getPropertyValue(FM_PROP_VALUE_SEQ) >>= m_aValueList;
+ m_bBound = m_aValueList.hasElements();
+
+ // tell the grid control that this controller is invalid and has to be re-initialized
+ invalidatedController();
+ }
+ }
+}
+
+void DbListBox::Init( vcl::Window& rParent, const Reference< XRowSet >& xCursor)
+{
+ m_rColumn.SetAlignment(css::awt::TextAlign::LEFT);
+
+ m_pWindow = VclPtr<ListBoxControl>::Create( &rParent );
+
+ // some initial properties
+ Reference< XPropertySet > xModel( m_rColumn.getModel() );
+ SetList( xModel->getPropertyValue( FM_PROP_STRINGITEMLIST ) );
+ implAdjustGenericFieldSetting( xModel );
+
+ DbCellControl::Init( rParent, xCursor );
+}
+
+void DbListBox::implAdjustGenericFieldSetting( const Reference< XPropertySet >& /*rxModel*/ )
+{
+ // ignore FM_PROP_LINECOUNT
+}
+
+CellControllerRef DbListBox::CreateController() const
+{
+ return new ListBoxCellController(static_cast<ListBoxControl*>(m_pWindow.get()));
+}
+
+OUString DbListBox::GetFormatText(const Reference< css::sdb::XColumn >& _rxField, const Reference< XNumberFormatter >& /*xFormatter*/, Color** /*ppColor*/)
+{
+ OUString sText;
+ if ( _rxField.is() )
+ {
+ try
+ {
+ sText = _rxField->getString();
+ if ( m_bBound )
+ {
+ sal_Int32 nPos = ::comphelper::findValue( m_aValueList, sText );
+ if ( nPos != -1 )
+ sText = static_cast<svt::ListBoxControl*>(m_pWindow.get())->get_widget().get_text(nPos);
+ else
+ sText.clear();
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ return sText;
+}
+
+void DbListBox::UpdateFromField(const Reference< css::sdb::XColumn >& _rxField, const Reference< XNumberFormatter >& xFormatter)
+{
+ OUString sFormattedText( GetFormatText( _rxField, xFormatter ) );
+ weld::ComboBox& rComboBox = static_cast<ListBoxControl*>(m_pWindow.get())->get_widget();
+ if (!sFormattedText.isEmpty())
+ rComboBox.set_active_text(sFormattedText);
+ else
+ rComboBox.set_active(-1);
+}
+
+void DbListBox::updateFromModel( Reference< XPropertySet > _rxModel )
+{
+ OSL_ENSURE( _rxModel.is() && m_pWindow, "DbListBox::updateFromModel: invalid call!" );
+
+ Sequence< sal_Int16 > aSelection;
+ _rxModel->getPropertyValue( FM_PROP_SELECT_SEQ ) >>= aSelection;
+
+ sal_Int16 nSelection = -1;
+ if ( aSelection.hasElements() )
+ nSelection = aSelection[ 0 ];
+
+ weld::ComboBox& rComboBox = static_cast<ListBoxControl*>(m_pWindow.get())->get_widget();
+
+ if (nSelection >= 0 && nSelection < rComboBox.get_count())
+ rComboBox.set_active(nSelection);
+ else
+ rComboBox.set_active(-1);
+}
+
+bool DbListBox::commitControl()
+{
+ Any aVal;
+ Sequence<sal_Int16> aSelectSeq;
+ weld::ComboBox& rComboBox = static_cast<ListBoxControl*>(m_pWindow.get())->get_widget();
+ auto nActive = rComboBox.get_active();
+ if (nActive != -1)
+ {
+ aSelectSeq.realloc(1);
+ *aSelectSeq.getArray() = static_cast<sal_Int16>(nActive);
+ }
+ aVal <<= aSelectSeq;
+ m_rColumn.getModel()->setPropertyValue(FM_PROP_SELECT_SEQ, aVal);
+ return true;
+}
+
+DbFilterField::DbFilterField(const Reference< XComponentContext >& rxContext,DbGridColumn& _rColumn)
+ :DbCellControl(_rColumn)
+ ,OSQLParserClient(rxContext)
+ ,m_nControlClass(css::form::FormComponentType::TEXTFIELD)
+ ,m_bFilterList(false)
+ ,m_bFilterListFilled(false)
+{
+
+ setAlignedController( false );
+}
+
+DbFilterField::~DbFilterField()
+{
+ if (m_nControlClass == css::form::FormComponentType::CHECKBOX)
+ static_cast<CheckBoxControl*>(m_pWindow.get())->SetClickHdl( Link<VclPtr<CheckBox>,void>() );
+
+}
+
+void DbFilterField::PaintCell(OutputDevice& rDev, const tools::Rectangle& rRect)
+{
+ static const DrawTextFlags nStyle = DrawTextFlags::Clip | DrawTextFlags::VCenter | DrawTextFlags::Left;
+ switch (m_nControlClass)
+ {
+ case FormComponentType::CHECKBOX:
+ DbCellControl::PaintCell( rDev, rRect );
+ break;
+ case FormComponentType::LISTBOX:
+ rDev.DrawText(rRect, static_cast<ListBoxControl*>(m_pWindow.get())->get_widget().get_active_text(), nStyle);
+ break;
+ default:
+ rDev.DrawText(rRect, m_aText, nStyle);
+ }
+}
+
+void DbFilterField::SetList(const Any& rItems, bool bComboBox)
+{
+ css::uno::Sequence<OUString> aTest;
+ rItems >>= aTest;
+ if (aTest.hasElements())
+ {
+ if (bComboBox)
+ {
+ ComboBoxControl* pField = static_cast<ComboBoxControl*>(m_pWindow.get());
+ weld::ComboBox& rComboBox = pField->get_widget();
+ for (const OUString& rString : std::as_const(aTest))
+ rComboBox.append_text(rString);
+ }
+ else
+ {
+ ListBoxControl* pField = static_cast<ListBoxControl*>(m_pWindow.get());
+ weld::ComboBox& rFieldBox = pField->get_widget();
+ for (const OUString& rString : std::as_const(aTest))
+ rFieldBox.append_text(rString);
+
+ m_rColumn.getModel()->getPropertyValue(FM_PROP_VALUE_SEQ) >>= m_aValueList;
+ }
+ }
+}
+
+void DbFilterField::CreateControl(vcl::Window* pParent, const Reference< css::beans::XPropertySet >& xModel)
+{
+ switch (m_nControlClass)
+ {
+ case css::form::FormComponentType::CHECKBOX:
+ m_pWindow = VclPtr<CheckBoxControl>::Create(pParent);
+ m_pWindow->SetPaintTransparent( true );
+ static_cast<CheckBoxControl*>(m_pWindow.get())->SetClickHdl( LINK( this, DbFilterField, OnClick ) );
+
+ m_pPainter = VclPtr<CheckBoxControl>::Create(pParent);
+ m_pPainter->SetPaintTransparent( true );
+ m_pPainter->SetBackground();
+ break;
+ case css::form::FormComponentType::LISTBOX:
+ {
+ m_pWindow = VclPtr<ListBoxControl>::Create(pParent);
+ Any aItems = xModel->getPropertyValue(FM_PROP_STRINGITEMLIST);
+ SetList(aItems, false);
+ } break;
+ case css::form::FormComponentType::COMBOBOX:
+ {
+ m_pWindow = VclPtr<ComboBoxControl>::Create(pParent);
+
+ AllSettings aSettings = m_pWindow->GetSettings();
+ StyleSettings aStyleSettings = aSettings.GetStyleSettings();
+ aStyleSettings.SetSelectionOptions(
+ aStyleSettings.GetSelectionOptions() | SelectionOptions::ShowFirst);
+ aSettings.SetStyleSettings(aStyleSettings);
+ m_pWindow->SetSettings(aSettings, true);
+
+ if (!m_bFilterList)
+ {
+ Any aItems = xModel->getPropertyValue(FM_PROP_STRINGITEMLIST);
+ SetList(aItems, true);
+ }
+
+ } break;
+ default:
+ {
+ m_pWindow = VclPtr<Edit>::Create(pParent, WB_LEFT);
+ AllSettings aSettings = m_pWindow->GetSettings();
+ StyleSettings aStyleSettings = aSettings.GetStyleSettings();
+ aStyleSettings.SetSelectionOptions(
+ aStyleSettings.GetSelectionOptions() | SelectionOptions::ShowFirst);
+ aSettings.SetStyleSettings(aStyleSettings);
+ m_pWindow->SetSettings(aSettings, true);
+ }
+ }
+}
+
+
+void DbFilterField::Init( vcl::Window& rParent, const Reference< XRowSet >& xCursor )
+{
+ Reference< css::beans::XPropertySet > xModel(m_rColumn.getModel());
+ m_rColumn.SetAlignment(css::awt::TextAlign::LEFT);
+
+ if (xModel.is())
+ {
+ m_bFilterList = ::comphelper::hasProperty(FM_PROP_FILTERPROPOSAL, xModel) && ::comphelper::getBOOL(xModel->getPropertyValue(FM_PROP_FILTERPROPOSAL));
+ if (m_bFilterList)
+ m_nControlClass = css::form::FormComponentType::COMBOBOX;
+ else
+ {
+ sal_Int16 nClassId = ::comphelper::getINT16(xModel->getPropertyValue(FM_PROP_CLASSID));
+ switch (nClassId)
+ {
+ case FormComponentType::CHECKBOX:
+ case FormComponentType::LISTBOX:
+ case FormComponentType::COMBOBOX:
+ m_nControlClass = nClassId;
+ break;
+ default:
+ if (m_bFilterList)
+ m_nControlClass = FormComponentType::COMBOBOX;
+ else
+ m_nControlClass = FormComponentType::TEXTFIELD;
+ }
+ }
+ }
+
+ CreateControl( &rParent, xModel );
+ DbCellControl::Init( rParent, xCursor );
+
+ // filter cells are never readonly
+ Edit* pAsEdit = dynamic_cast< Edit* >( m_pWindow.get() );
+ if ( pAsEdit )
+ pAsEdit->SetReadOnly( false );
+}
+
+CellControllerRef DbFilterField::CreateController() const
+{
+ CellControllerRef xController;
+ switch (m_nControlClass)
+ {
+ case css::form::FormComponentType::CHECKBOX:
+ xController = new CheckBoxCellController(static_cast<CheckBoxControl*>(m_pWindow.get()));
+ break;
+ case css::form::FormComponentType::LISTBOX:
+ xController = new ListBoxCellController(static_cast<ListBoxControl*>(m_pWindow.get()));
+ break;
+ case css::form::FormComponentType::COMBOBOX:
+ xController = new ComboBoxCellController(static_cast<ComboBoxControl*>(m_pWindow.get()));
+ break;
+ default:
+ if (m_bFilterList)
+ xController = new ComboBoxCellController(static_cast<ComboBoxControl*>(m_pWindow.get()));
+ else
+ xController = new EditCellController(static_cast<Edit*>(m_pWindow.get()));
+ }
+ return xController;
+}
+
+void DbFilterField::updateFromModel( Reference< XPropertySet > _rxModel )
+{
+ OSL_ENSURE( _rxModel.is() && m_pWindow, "DbFilterField::updateFromModel: invalid call!" );
+
+ OSL_FAIL( "DbListBox::updateFromModel: not implemented yet (how the hell did you reach this?)!" );
+ // TODO: implement this.
+ // remember: updateFromModel should be some kind of opposite of commitControl
+}
+
+bool DbFilterField::commitControl()
+{
+ OUString aText(m_aText);
+ switch (m_nControlClass)
+ {
+ case css::form::FormComponentType::CHECKBOX:
+ return true;
+ case css::form::FormComponentType::LISTBOX:
+ {
+ aText.clear();
+ weld::ComboBox& rComboBox = static_cast<svt::ListBoxControl*>(m_pWindow.get())->get_widget();
+ auto nActive = rComboBox.get_active();
+ if (nActive != -1)
+ {
+ sal_Int16 nPos = static_cast<sal_Int16>(nActive);
+ if ( ( nPos >= 0 ) && ( nPos < m_aValueList.getLength() ) )
+ aText = m_aValueList.getConstArray()[nPos];
+ }
+
+ if (m_aText != aText)
+ {
+ m_aText = aText;
+ m_aCommitLink.Call(*this);
+ }
+ return true;
+ }
+ default:
+ aText = m_pWindow->GetText();
+ }
+
+ if (m_aText != aText)
+ {
+ // check the text with the SQL-Parser
+ OUString aNewText(comphelper::string::stripEnd(aText, ' '));
+ if (!aNewText.isEmpty())
+ {
+ OUString aErrorMsg;
+ Reference< XNumberFormatter > xNumberFormatter(m_rColumn.GetParent().getNumberFormatter());
+
+ std::unique_ptr< OSQLParseNode > pParseNode = predicateTree(aErrorMsg, aNewText,xNumberFormatter, m_rColumn.GetField());
+ if (pParseNode != nullptr)
+ {
+ OUString aPreparedText;
+
+ css::lang::Locale aAppLocale = Application::GetSettings().GetUILanguageTag().getLocale();
+
+ Reference< XRowSet > xDataSourceRowSet(
+ Reference< XInterface >(*m_rColumn.GetParent().getDataSource()), UNO_QUERY);
+ Reference< XConnection > xConnection(getConnection(xDataSourceRowSet));
+
+ pParseNode->parseNodeToPredicateStr(aPreparedText,
+ xConnection,
+ xNumberFormatter,
+ m_rColumn.GetField(),
+ OUString(),
+ aAppLocale,
+ OUString("."),
+ getParseContext());
+ m_aText = aPreparedText;
+ }
+ else
+ {
+
+ SQLException aError;
+ aError.Message = aErrorMsg;
+ displayException(aError, m_pWindow->GetParent());
+ // TODO: transport the title
+
+ return false;
+ }
+ }
+ else
+ m_aText = aText;
+
+ m_pWindow->SetText(m_aText);
+ m_aCommitLink.Call(*this);
+ }
+ return true;
+}
+
+
+void DbFilterField::SetText(const OUString& rText)
+{
+ m_aText = rText;
+ switch (m_nControlClass)
+ {
+ case css::form::FormComponentType::CHECKBOX:
+ {
+ TriState eState;
+ if (rText == "1")
+ eState = TRISTATE_TRUE;
+ else if (rText == "0")
+ eState = TRISTATE_FALSE;
+ else
+ eState = TRISTATE_INDET;
+
+ static_cast<CheckBoxControl*>(m_pWindow.get())->GetBox().SetState(eState);
+ static_cast<CheckBoxControl*>(m_pPainter.get())->GetBox().SetState(eState);
+ } break;
+ case css::form::FormComponentType::LISTBOX:
+ {
+ sal_Int32 nPos = ::comphelper::findValue(m_aValueList, m_aText);
+ static_cast<ListBoxControl*>(m_pWindow.get())->get_widget().set_active(nPos);
+ } break;
+ default:
+ m_pWindow->SetText(m_aText);
+ }
+
+ // now force a repaint on the window
+ m_rColumn.GetParent().RowModified(0);
+}
+
+
+void DbFilterField::Update()
+{
+ // should we fill the combobox with a filter proposal?
+ if (!m_bFilterList || m_bFilterListFilled)
+ return;
+
+ m_bFilterListFilled = true;
+ Reference< css::beans::XPropertySet > xField = m_rColumn.GetField();
+ if (!xField.is())
+ return;
+
+ OUString aName;
+ xField->getPropertyValue(FM_PROP_NAME) >>= aName;
+
+ // the columnmodel
+ Reference< css::container::XChild > xModelAsChild(m_rColumn.getModel(), UNO_QUERY);
+ // the grid model
+ xModelAsChild.set(xModelAsChild->getParent(),UNO_QUERY);
+ Reference< XRowSet > xForm(xModelAsChild->getParent(), UNO_QUERY);
+ if (!xForm.is())
+ return;
+
+ Reference<XPropertySet> xFormProp(xForm,UNO_QUERY);
+ Reference< XTablesSupplier > xSupTab;
+ xFormProp->getPropertyValue("SingleSelectQueryComposer") >>= xSupTab;
+
+ Reference< XConnection > xConnection(getConnection(xForm));
+ if (!xSupTab.is())
+ return;
+
+ // search the field
+ Reference< XColumnsSupplier > xSupCol(xSupTab,UNO_QUERY);
+ Reference< css::container::XNameAccess > xFieldNames = xSupCol->getColumns();
+ if (!xFieldNames->hasByName(aName))
+ return;
+
+ Reference< css::container::XNameAccess > xTablesNames = xSupTab->getTables();
+ Reference< css::beans::XPropertySet > xComposerFieldAsSet(xFieldNames->getByName(aName),UNO_QUERY);
+
+ if (!xComposerFieldAsSet.is() ||
+ !::comphelper::hasProperty(FM_PROP_TABLENAME, xComposerFieldAsSet) ||
+ !::comphelper::hasProperty(FM_PROP_FIELDSOURCE, xComposerFieldAsSet))
+ return;
+
+ OUString aFieldName;
+ OUString aTableName;
+ xComposerFieldAsSet->getPropertyValue(FM_PROP_FIELDSOURCE) >>= aFieldName;
+ xComposerFieldAsSet->getPropertyValue(FM_PROP_TABLENAME) >>= aTableName;
+
+ // no possibility to create a select statement
+ // looking for the complete table name
+ if (!xTablesNames->hasByName(aTableName))
+ return;
+
+ // build a statement and send as query;
+ // Access to the connection
+ Reference< XStatement > xStatement;
+ Reference< XResultSet > xListCursor;
+ Reference< css::sdb::XColumn > xDataField;
+
+ try
+ {
+ Reference< XDatabaseMetaData > xMeta = xConnection->getMetaData();
+
+ OUString aQuote(xMeta->getIdentifierQuoteString());
+ OUStringBuffer aStatement("SELECT DISTINCT ");
+ aStatement.append(quoteName(aQuote, aName));
+ if (!aFieldName.isEmpty() && aName != aFieldName)
+ {
+ aStatement.append(" AS ");
+ aStatement.append(quoteName(aQuote, aFieldName));
+ }
+
+ aStatement.append(" FROM ");
+
+ Reference< XPropertySet > xTableNameAccess(xTablesNames->getByName(aTableName), UNO_QUERY_THROW);
+ aStatement.append(composeTableNameForSelect(xConnection, xTableNameAccess));
+
+ xStatement = xConnection->createStatement();
+ Reference< css::beans::XPropertySet > xStatementProps(xStatement, UNO_QUERY);
+ xStatementProps->setPropertyValue(FM_PROP_ESCAPE_PROCESSING, makeAny(true));
+
+ xListCursor = xStatement->executeQuery(aStatement.makeStringAndClear());
+
+ Reference< css::sdbcx::XColumnsSupplier > xSupplyCols(xListCursor, UNO_QUERY);
+ Reference< css::container::XIndexAccess > xFields(xSupplyCols->getColumns(), UNO_QUERY);
+ xDataField.set(xFields->getByIndex(0), css::uno::UNO_QUERY);
+ if (!xDataField.is())
+ return;
+ }
+ catch(const Exception&)
+ {
+ ::comphelper::disposeComponent(xStatement);
+ return;
+ }
+
+ sal_Int16 i = 0;
+ ::std::vector< OUString > aStringList;
+ aStringList.reserve(16);
+ OUString aStr;
+ css::util::Date aNullDate = m_rColumn.GetParent().getNullDate();
+ sal_Int32 nFormatKey = m_rColumn.GetKey();
+ Reference< XNumberFormatter > xFormatter = m_rColumn.GetParent().getNumberFormatter();
+ sal_Int16 nKeyType = ::comphelper::getNumberFormatType(xFormatter->getNumberFormatsSupplier()->getNumberFormats(), nFormatKey);
+
+ while (!xListCursor->isAfterLast() && i++ < SHRT_MAX) // max number of entries
+ {
+ aStr = getFormattedValue(xDataField, xFormatter, aNullDate, nFormatKey, nKeyType);
+ aStringList.push_back(aStr);
+ (void)xListCursor->next();
+ }
+
+ ComboBoxControl* pField = static_cast<ComboBoxControl*>(m_pWindow.get());
+ weld::ComboBox& rComboBox = pField->get_widget();
+ // filling the entries for the combobox
+ for (const auto& rString : aStringList)
+ rComboBox.append_text(rString);
+}
+
+OUString DbFilterField::GetFormatText(const Reference< XColumn >& /*_rxField*/, const Reference< XNumberFormatter >& /*xFormatter*/, Color** /*ppColor*/)
+{
+ return OUString();
+}
+
+
+void DbFilterField::UpdateFromField(const Reference< XColumn >& /*_rxField*/, const Reference< XNumberFormatter >& /*xFormatter*/)
+{
+ OSL_FAIL( "DbFilterField::UpdateFromField: cannot update a filter control from a field!" );
+}
+
+
+IMPL_LINK_NOARG(DbFilterField, OnClick, VclPtr<CheckBox>, void)
+{
+ TriState eState = static_cast<CheckBoxControl*>(m_pWindow.get())->GetBox().GetState();
+ OUStringBuffer aTextBuf;
+
+ Reference< XRowSet > xDataSourceRowSet(
+ Reference< XInterface >(*m_rColumn.GetParent().getDataSource()), UNO_QUERY);
+ Reference< XConnection > xConnection(getConnection(xDataSourceRowSet));
+ const sal_Int32 nBooleanComparisonMode = ::dbtools::DatabaseMetaData( xConnection ).getBooleanComparisonMode();
+
+ switch (eState)
+ {
+ case TRISTATE_TRUE:
+ ::dbtools::getBooleanComparisonPredicate("", true, nBooleanComparisonMode, aTextBuf);
+ break;
+ case TRISTATE_FALSE:
+ ::dbtools::getBooleanComparisonPredicate("", false, nBooleanComparisonMode, aTextBuf);
+ break;
+ case TRISTATE_INDET:
+ break;
+ }
+
+ const OUString aText(aTextBuf.makeStringAndClear());
+
+ if (m_aText != aText)
+ {
+ m_aText = aText;
+ m_aCommitLink.Call(*this);
+ }
+}
+
+
+FmXGridCell::FmXGridCell( DbGridColumn* pColumn, std::unique_ptr<DbCellControl> _pControl )
+ :OComponentHelper(m_aMutex)
+ ,m_pColumn(pColumn)
+ ,m_pCellControl( std::move(_pControl) )
+ ,m_aWindowListeners( m_aMutex )
+ ,m_aFocusListeners( m_aMutex )
+ ,m_aKeyListeners( m_aMutex )
+ ,m_aMouseListeners( m_aMutex )
+ ,m_aMouseMotionListeners( m_aMutex )
+{
+}
+
+
+void FmXGridCell::init()
+{
+ vcl::Window* pEventWindow( getEventWindow() );
+ if ( pEventWindow )
+ pEventWindow->AddEventListener( LINK( this, FmXGridCell, OnWindowEvent ) );
+}
+
+
+vcl::Window* FmXGridCell::getEventWindow() const
+{
+ if ( m_pCellControl )
+ return &m_pCellControl->GetWindow();
+ return nullptr;
+}
+
+
+FmXGridCell::~FmXGridCell()
+{
+ if (!OComponentHelper::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+
+}
+
+
+void FmXGridCell::SetTextLineColor()
+{
+ if (m_pCellControl)
+ m_pCellControl->SetTextLineColor();
+}
+
+
+void FmXGridCell::SetTextLineColor(const Color& _rColor)
+{
+ if (m_pCellControl)
+ m_pCellControl->SetTextLineColor(_rColor);
+}
+
+// XTypeProvider
+
+Sequence< Type > SAL_CALL FmXGridCell::getTypes( )
+{
+ Sequence< uno::Type > aTypes = ::comphelper::concatSequences(
+ ::cppu::OComponentHelper::getTypes(),
+ FmXGridCell_Base::getTypes()
+ );
+ if ( m_pCellControl )
+ aTypes = ::comphelper::concatSequences(
+ aTypes,
+ FmXGridCell_WindowBase::getTypes()
+ );
+ return aTypes;
+}
+
+
+IMPLEMENT_GET_IMPLEMENTATION_ID( FmXGridCell )
+
+// OComponentHelper
+
+void FmXGridCell::disposing()
+{
+ lang::EventObject aEvent( *this );
+ m_aWindowListeners.disposeAndClear( aEvent );
+ m_aFocusListeners.disposeAndClear( aEvent );
+ m_aKeyListeners.disposeAndClear( aEvent );
+ m_aMouseListeners.disposeAndClear( aEvent );
+ m_aMouseMotionListeners.disposeAndClear( aEvent );
+
+ OComponentHelper::disposing();
+ m_pColumn = nullptr;
+ m_pCellControl.reset();
+}
+
+
+Any SAL_CALL FmXGridCell::queryAggregation( const css::uno::Type& _rType )
+{
+ Any aReturn = OComponentHelper::queryAggregation( _rType );
+
+ if ( !aReturn.hasValue() )
+ aReturn = FmXGridCell_Base::queryInterface( _rType );
+
+ if ( !aReturn.hasValue() && ( m_pCellControl != nullptr ) )
+ aReturn = FmXGridCell_WindowBase::queryInterface( _rType );
+
+ return aReturn;
+}
+
+// css::awt::XControl
+
+Reference< XInterface > FmXGridCell::getContext()
+{
+ return Reference< XInterface > ();
+}
+
+
+Reference< css::awt::XControlModel > FmXGridCell::getModel()
+{
+ checkDisposed(OComponentHelper::rBHelper.bDisposed);
+ return Reference< css::awt::XControlModel > (m_pColumn->getModel(), UNO_QUERY);
+}
+
+// css::form::XBoundControl
+
+sal_Bool FmXGridCell::getLock()
+{
+ checkDisposed(OComponentHelper::rBHelper.bDisposed);
+ return m_pColumn->isLocked();
+}
+
+
+void FmXGridCell::setLock(sal_Bool _bLock)
+{
+ checkDisposed(OComponentHelper::rBHelper.bDisposed);
+ if (getLock() == _bLock)
+ return;
+ else
+ {
+ ::osl::MutexGuard aGuard(m_aMutex);
+ m_pColumn->setLock(_bLock);
+ }
+}
+
+
+void SAL_CALL FmXGridCell::setPosSize( ::sal_Int32, ::sal_Int32, ::sal_Int32, ::sal_Int32, ::sal_Int16 )
+{
+ OSL_FAIL( "FmXGridCell::setPosSize: not implemented" );
+ // not allowed to tamper with this for a grid cell
+}
+
+
+awt::Rectangle SAL_CALL FmXGridCell::getPosSize( )
+{
+ OSL_FAIL( "FmXGridCell::getPosSize: not implemented" );
+ return awt::Rectangle();
+}
+
+
+void SAL_CALL FmXGridCell::setVisible( sal_Bool )
+{
+ OSL_FAIL( "FmXGridCell::setVisible: not implemented" );
+ // not allowed to tamper with this for a grid cell
+}
+
+
+void SAL_CALL FmXGridCell::setEnable( sal_Bool )
+{
+ OSL_FAIL( "FmXGridCell::setEnable: not implemented" );
+ // not allowed to tamper with this for a grid cell
+}
+
+
+void SAL_CALL FmXGridCell::setFocus( )
+{
+ OSL_FAIL( "FmXGridCell::setFocus: not implemented" );
+ // not allowed to tamper with this for a grid cell
+}
+
+
+void SAL_CALL FmXGridCell::addWindowListener( const Reference< awt::XWindowListener >& _rxListener )
+{
+ checkDisposed(OComponentHelper::rBHelper.bDisposed);
+ m_aWindowListeners.addInterface( _rxListener );
+}
+
+
+void SAL_CALL FmXGridCell::removeWindowListener( const Reference< awt::XWindowListener >& _rxListener )
+{
+ checkDisposed(OComponentHelper::rBHelper.bDisposed);
+ m_aWindowListeners.removeInterface( _rxListener );
+}
+
+
+void SAL_CALL FmXGridCell::addFocusListener( const Reference< awt::XFocusListener >& _rxListener )
+{
+ checkDisposed(OComponentHelper::rBHelper.bDisposed);
+ m_aFocusListeners.addInterface( _rxListener );
+}
+
+
+void SAL_CALL FmXGridCell::removeFocusListener( const Reference< awt::XFocusListener >& _rxListener )
+{
+ checkDisposed(OComponentHelper::rBHelper.bDisposed);
+ m_aFocusListeners.removeInterface( _rxListener );
+}
+
+
+void SAL_CALL FmXGridCell::addKeyListener( const Reference< awt::XKeyListener >& _rxListener )
+{
+ checkDisposed(OComponentHelper::rBHelper.bDisposed);
+ m_aKeyListeners.addInterface( _rxListener );
+}
+
+
+void SAL_CALL FmXGridCell::removeKeyListener( const Reference< awt::XKeyListener >& _rxListener )
+{
+ checkDisposed(OComponentHelper::rBHelper.bDisposed);
+ m_aKeyListeners.removeInterface( _rxListener );
+}
+
+
+void SAL_CALL FmXGridCell::addMouseListener( const Reference< awt::XMouseListener >& _rxListener )
+{
+ checkDisposed(OComponentHelper::rBHelper.bDisposed);
+ m_aMouseListeners.addInterface( _rxListener );
+}
+
+
+void SAL_CALL FmXGridCell::removeMouseListener( const Reference< awt::XMouseListener >& _rxListener )
+{
+ checkDisposed(OComponentHelper::rBHelper.bDisposed);
+ m_aMouseListeners.removeInterface( _rxListener );
+}
+
+
+void SAL_CALL FmXGridCell::addMouseMotionListener( const Reference< awt::XMouseMotionListener >& _rxListener )
+{
+ checkDisposed(OComponentHelper::rBHelper.bDisposed);
+ m_aMouseMotionListeners.addInterface( _rxListener );
+}
+
+
+void SAL_CALL FmXGridCell::removeMouseMotionListener( const Reference< awt::XMouseMotionListener >& _rxListener )
+{
+ checkDisposed(OComponentHelper::rBHelper.bDisposed);
+ m_aMouseMotionListeners.removeInterface( _rxListener );
+}
+
+
+void SAL_CALL FmXGridCell::addPaintListener( const Reference< awt::XPaintListener >& )
+{
+ OSL_FAIL( "FmXGridCell::addPaintListener: not implemented" );
+}
+
+
+void SAL_CALL FmXGridCell::removePaintListener( const Reference< awt::XPaintListener >& )
+{
+ OSL_FAIL( "FmXGridCell::removePaintListener: not implemented" );
+}
+
+
+IMPL_LINK( FmXGridCell, OnWindowEvent, VclWindowEvent&, _rEvent, void )
+{
+ ENSURE_OR_THROW( _rEvent.GetWindow(), "illegal window" );
+ onWindowEvent( _rEvent.GetId(), *_rEvent.GetWindow(), _rEvent.GetData() );
+}
+
+
+void FmXGridCell::onFocusGained( const awt::FocusEvent& _rEvent )
+{
+ checkDisposed(OComponentHelper::rBHelper.bDisposed);
+ m_aFocusListeners.notifyEach( &awt::XFocusListener::focusGained, _rEvent );
+}
+
+
+void FmXGridCell::onFocusLost( const awt::FocusEvent& _rEvent )
+{
+ checkDisposed(OComponentHelper::rBHelper.bDisposed);
+ m_aFocusListeners.notifyEach( &awt::XFocusListener::focusLost, _rEvent );
+}
+
+
+void FmXGridCell::onWindowEvent( const VclEventId _nEventId, const vcl::Window& _rWindow, const void* _pEventData )
+{
+ switch ( _nEventId )
+ {
+ case VclEventId::ControlGetFocus:
+ case VclEventId::WindowGetFocus:
+ case VclEventId::ControlLoseFocus:
+ case VclEventId::WindowLoseFocus:
+ {
+ if ( ( _rWindow.IsCompoundControl()
+ && ( _nEventId == VclEventId::ControlGetFocus
+ || _nEventId == VclEventId::ControlLoseFocus
+ )
+ )
+ || ( !_rWindow.IsCompoundControl()
+ && ( _nEventId == VclEventId::WindowGetFocus
+ || _nEventId == VclEventId::WindowLoseFocus
+ )
+ )
+ )
+ {
+ if ( !m_aFocusListeners.getLength() )
+ break;
+
+ bool bFocusGained = ( _nEventId == VclEventId::ControlGetFocus ) || ( _nEventId == VclEventId::WindowGetFocus );
+
+ awt::FocusEvent aEvent;
+ aEvent.Source = *this;
+ aEvent.FocusFlags = static_cast<sal_Int16>(_rWindow.GetGetFocusFlags());
+ aEvent.Temporary = false;
+
+ if ( bFocusGained )
+ onFocusGained( aEvent );
+ else
+ onFocusLost( aEvent );
+ }
+ }
+ break;
+ case VclEventId::WindowMouseButtonDown:
+ case VclEventId::WindowMouseButtonUp:
+ {
+ if ( !m_aMouseListeners.getLength() )
+ break;
+
+ const bool bButtonDown = ( _nEventId == VclEventId::WindowMouseButtonDown );
+
+ awt::MouseEvent aEvent( VCLUnoHelper::createMouseEvent( *static_cast< const ::MouseEvent* >( _pEventData ), *this ) );
+ m_aMouseListeners.notifyEach( bButtonDown ? &awt::XMouseListener::mousePressed : &awt::XMouseListener::mouseReleased, aEvent );
+ }
+ break;
+ case VclEventId::WindowMouseMove:
+ {
+ const MouseEvent& rMouseEvent = *static_cast< const ::MouseEvent* >( _pEventData );
+ if ( rMouseEvent.IsEnterWindow() || rMouseEvent.IsLeaveWindow() )
+ {
+ if ( m_aMouseListeners.getLength() != 0 )
+ {
+ awt::MouseEvent aEvent( VCLUnoHelper::createMouseEvent( rMouseEvent, *this ) );
+ m_aMouseListeners.notifyEach( rMouseEvent.IsEnterWindow() ? &awt::XMouseListener::mouseEntered: &awt::XMouseListener::mouseExited, aEvent );
+ }
+ }
+ else if ( !rMouseEvent.IsEnterWindow() && !rMouseEvent.IsLeaveWindow() )
+ {
+ if ( m_aMouseMotionListeners.getLength() != 0 )
+ {
+ awt::MouseEvent aEvent( VCLUnoHelper::createMouseEvent( rMouseEvent, *this ) );
+ aEvent.ClickCount = 0;
+ const bool bSimpleMove = bool( rMouseEvent.GetMode() & MouseEventModifiers::SIMPLEMOVE );
+ m_aMouseMotionListeners.notifyEach( bSimpleMove ? &awt::XMouseMotionListener::mouseMoved: &awt::XMouseMotionListener::mouseDragged, aEvent );
+ }
+ }
+ }
+ break;
+ case VclEventId::WindowKeyInput:
+ case VclEventId::WindowKeyUp:
+ {
+ if ( !m_aKeyListeners.getLength() )
+ break;
+
+ const bool bKeyPressed = ( _nEventId == VclEventId::WindowKeyInput );
+ awt::KeyEvent aEvent( VCLUnoHelper::createKeyEvent( *static_cast< const ::KeyEvent* >( _pEventData ), *this ) );
+ m_aKeyListeners.notifyEach( bKeyPressed ? &awt::XKeyListener::keyPressed: &awt::XKeyListener::keyReleased, aEvent );
+ }
+ break;
+ default: break;
+ }
+}
+
+
+void FmXDataCell::PaintFieldToCell(OutputDevice& rDev, const tools::Rectangle& rRect,
+ const Reference< css::sdb::XColumn >& _rxField,
+ const Reference< XNumberFormatter >& xFormatter)
+{
+ m_pCellControl->PaintFieldToCell( rDev, rRect, _rxField, xFormatter );
+}
+
+
+void FmXDataCell::UpdateFromColumn()
+{
+ Reference< css::sdb::XColumn > xField(m_pColumn->GetCurrentFieldValue());
+ if (xField.is())
+ m_pCellControl->UpdateFromField(xField, m_pColumn->GetParent().getNumberFormatter());
+}
+
+
+FmXTextCell::FmXTextCell( DbGridColumn* pColumn, std::unique_ptr<DbCellControl> pControl )
+ :FmXDataCell( pColumn, std::move(pControl) )
+ ,m_bFastPaint( true )
+{
+}
+
+
+void FmXTextCell::PaintFieldToCell(OutputDevice& rDev,
+ const tools::Rectangle& rRect,
+ const Reference< css::sdb::XColumn >& _rxField,
+ const Reference< XNumberFormatter >& xFormatter)
+{
+ if ( !m_bFastPaint )
+ {
+ FmXDataCell::PaintFieldToCell( rDev, rRect, _rxField, xFormatter );
+ return;
+ }
+
+ DrawTextFlags nStyle = DrawTextFlags::Clip | DrawTextFlags::VCenter;
+ if ( ( rDev.GetOutDevType() == OUTDEV_WINDOW ) && !static_cast< vcl::Window& >( rDev ).IsEnabled() )
+ nStyle |= DrawTextFlags::Disable;
+
+ switch (m_pColumn->GetAlignment())
+ {
+ case css::awt::TextAlign::RIGHT:
+ nStyle |= DrawTextFlags::Right;
+ break;
+ case css::awt::TextAlign::CENTER:
+ nStyle |= DrawTextFlags::Center;
+ break;
+ default:
+ nStyle |= DrawTextFlags::Left;
+ }
+
+ try
+ {
+ Color* pColor = nullptr;
+ OUString aText = GetText(_rxField, xFormatter, &pColor);
+ if (pColor != nullptr)
+ {
+ Color aOldTextColor( rDev.GetTextColor() );
+ rDev.SetTextColor( *pColor );
+ rDev.DrawText(rRect, aText, nStyle);
+ rDev.SetTextColor( aOldTextColor );
+ }
+ else
+ rDev.DrawText(rRect, aText, nStyle);
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx.fmcomp", "PaintFieldToCell");
+ }
+}
+
+FmXEditCell::FmXEditCell( DbGridColumn* pColumn, std::unique_ptr<DbCellControl> pControl )
+ :FmXTextCell( pColumn, std::move(pControl) )
+ ,m_aTextListeners(m_aMutex)
+ ,m_aChangeListeners( m_aMutex )
+ ,m_pEditImplementation( nullptr )
+ ,m_bOwnEditImplementation( false )
+{
+
+ DbTextField* pTextField = dynamic_cast<DbTextField*>( m_pCellControl.get() );
+ if ( pTextField )
+ {
+
+ m_pEditImplementation = pTextField->GetEditImplementation();
+ if ( !pTextField->IsSimpleEdit() )
+ m_bFastPaint = false;
+ }
+ else
+ {
+ m_pEditImplementation = new EditImplementation( static_cast< Edit& >( m_pCellControl->GetWindow() ) );
+ m_bOwnEditImplementation = true;
+ }
+}
+
+
+FmXEditCell::~FmXEditCell()
+{
+ if (!OComponentHelper::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+
+
+}
+
+// OComponentHelper
+
+void FmXEditCell::disposing()
+{
+ css::lang::EventObject aEvt(*this);
+ m_aTextListeners.disposeAndClear(aEvt);
+ m_aChangeListeners.disposeAndClear(aEvt);
+
+ m_pEditImplementation->SetModifyHdl( Link<Edit&,void>() );
+ if ( m_bOwnEditImplementation )
+ delete m_pEditImplementation;
+ m_pEditImplementation = nullptr;
+
+ FmXDataCell::disposing();
+}
+
+
+Any SAL_CALL FmXEditCell::queryAggregation( const css::uno::Type& _rType )
+{
+ Any aReturn = FmXTextCell::queryAggregation( _rType );
+
+ if ( !aReturn.hasValue() )
+ aReturn = FmXEditCell_Base::queryInterface( _rType );
+
+ return aReturn;
+}
+
+
+Sequence< css::uno::Type > SAL_CALL FmXEditCell::getTypes( )
+{
+ return ::comphelper::concatSequences(
+ FmXTextCell::getTypes(),
+ FmXEditCell_Base::getTypes()
+ );
+}
+
+
+IMPLEMENT_GET_IMPLEMENTATION_ID( FmXEditCell )
+
+// css::awt::XTextComponent
+
+void SAL_CALL FmXEditCell::addTextListener(const Reference< css::awt::XTextListener >& l)
+{
+ m_aTextListeners.addInterface( l );
+}
+
+
+void SAL_CALL FmXEditCell::removeTextListener(const Reference< css::awt::XTextListener >& l)
+{
+ m_aTextListeners.removeInterface( l );
+}
+
+
+void SAL_CALL FmXEditCell::setText( const OUString& aText )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( m_pEditImplementation )
+ {
+ m_pEditImplementation->SetText( aText );
+
+ // In Java, a textChanged is fired as well; not in VCL.
+ // css::awt::Toolkit must be Java-compliant...
+ onTextChanged();
+ }
+}
+
+
+void SAL_CALL FmXEditCell::insertText(const css::awt::Selection& rSel, const OUString& aText)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( m_pEditImplementation )
+ {
+ m_pEditImplementation->SetSelection( Selection( rSel.Min, rSel.Max ) );
+ m_pEditImplementation->ReplaceSelected( aText );
+ }
+}
+
+
+OUString SAL_CALL FmXEditCell::getText()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ OUString aText;
+ if ( m_pEditImplementation )
+ {
+ if ( m_pEditImplementation->GetControl().IsVisible() && m_pColumn->GetParent().getDisplaySynchron())
+ {
+ // if the display isn't sync with the cursor we can't ask the edit field
+ LineEnd eLineEndFormat = getModelLineEndSetting( m_pColumn->getModel() );
+ aText = m_pEditImplementation->GetText( eLineEndFormat );
+ }
+ else
+ {
+ Reference< css::sdb::XColumn > xField(m_pColumn->GetCurrentFieldValue());
+ if (xField.is())
+ aText = GetText(xField, m_pColumn->GetParent().getNumberFormatter());
+ }
+ }
+ return aText;
+}
+
+
+OUString SAL_CALL FmXEditCell::getSelectedText()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ OUString aText;
+ if ( m_pEditImplementation )
+ {
+ LineEnd eLineEndFormat = m_pColumn ? getModelLineEndSetting( m_pColumn->getModel() ) : LINEEND_LF;
+ aText = m_pEditImplementation->GetSelected( eLineEndFormat );
+ }
+ return aText;
+}
+
+
+void SAL_CALL FmXEditCell::setSelection( const css::awt::Selection& aSelection )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( m_pEditImplementation )
+ m_pEditImplementation->SetSelection( Selection( aSelection.Min, aSelection.Max ) );
+}
+
+
+css::awt::Selection SAL_CALL FmXEditCell::getSelection()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ Selection aSel;
+ if ( m_pEditImplementation )
+ aSel = m_pEditImplementation->GetSelection();
+
+ return css::awt::Selection(aSel.Min(), aSel.Max());
+}
+
+
+sal_Bool SAL_CALL FmXEditCell::isEditable()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ return m_pEditImplementation && !m_pEditImplementation->IsReadOnly() && m_pEditImplementation->GetControl().IsEnabled();
+}
+
+
+void SAL_CALL FmXEditCell::setEditable( sal_Bool bEditable )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( m_pEditImplementation )
+ m_pEditImplementation->SetReadOnly( !bEditable );
+}
+
+
+sal_Int16 SAL_CALL FmXEditCell::getMaxTextLen()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ return m_pEditImplementation ? m_pEditImplementation->GetMaxTextLen() : 0;
+}
+
+
+void SAL_CALL FmXEditCell::setMaxTextLen( sal_Int16 nLen )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( m_pEditImplementation )
+ m_pEditImplementation->SetMaxTextLen( nLen );
+}
+
+
+void SAL_CALL FmXEditCell::addChangeListener( const Reference< form::XChangeListener >& Listener )
+{
+ m_aChangeListeners.addInterface( Listener );
+}
+
+
+void SAL_CALL FmXEditCell::removeChangeListener( const Reference< form::XChangeListener >& Listener )
+{
+ m_aChangeListeners.removeInterface( Listener );
+}
+
+
+void FmXEditCell::onTextChanged()
+{
+ css::awt::TextEvent aEvent;
+ aEvent.Source = *this;
+ m_aTextListeners.notifyEach( &awt::XTextListener::textChanged, aEvent );
+}
+
+
+void FmXEditCell::onFocusGained( const awt::FocusEvent& _rEvent )
+{
+ FmXTextCell::onFocusGained( _rEvent );
+ m_sValueOnEnter = getText();
+}
+
+
+void FmXEditCell::onFocusLost( const awt::FocusEvent& _rEvent )
+{
+ FmXTextCell::onFocusLost( _rEvent );
+
+ if ( getText() != m_sValueOnEnter )
+ {
+ lang::EventObject aEvent( *this );
+ m_aChangeListeners.notifyEach( &XChangeListener::changed, aEvent );
+ }
+}
+
+
+void FmXEditCell::onWindowEvent( const VclEventId _nEventId, const vcl::Window& _rWindow, const void* _pEventData )
+{
+ switch ( _nEventId )
+ {
+ case VclEventId::EditModify:
+ {
+ if ( m_pEditImplementation && m_aTextListeners.getLength() )
+ onTextChanged();
+ return;
+ }
+ default: break;
+ }
+
+ FmXTextCell::onWindowEvent( _nEventId, _rWindow, _pEventData );
+}
+
+FmXCheckBoxCell::FmXCheckBoxCell( DbGridColumn* pColumn, std::unique_ptr<DbCellControl> pControl )
+ :FmXDataCell( pColumn, std::move(pControl) )
+ ,m_aItemListeners(m_aMutex)
+ ,m_aActionListeners( m_aMutex )
+ ,m_pBox( & static_cast< CheckBoxControl& >( m_pCellControl->GetWindow() ).GetBox() )
+{
+}
+
+
+FmXCheckBoxCell::~FmXCheckBoxCell()
+{
+ if (!OComponentHelper::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+
+}
+
+// OComponentHelper
+
+void FmXCheckBoxCell::disposing()
+{
+ css::lang::EventObject aEvt(*this);
+ m_aItemListeners.disposeAndClear(aEvt);
+ m_aActionListeners.disposeAndClear(aEvt);
+
+ static_cast< CheckBoxControl& >( m_pCellControl->GetWindow() ).SetClickHdl(Link<VclPtr<CheckBox>,void>());
+ m_pBox = nullptr;
+
+ FmXDataCell::disposing();
+}
+
+
+Any SAL_CALL FmXCheckBoxCell::queryAggregation( const css::uno::Type& _rType )
+{
+ Any aReturn = FmXDataCell::queryAggregation( _rType );
+
+ if ( !aReturn.hasValue() )
+ aReturn = FmXCheckBoxCell_Base::queryInterface( _rType );
+
+ return aReturn;
+}
+
+
+Sequence< css::uno::Type > SAL_CALL FmXCheckBoxCell::getTypes( )
+{
+ return ::comphelper::concatSequences(
+ FmXDataCell::getTypes(),
+ FmXCheckBoxCell_Base::getTypes()
+ );
+}
+
+
+IMPLEMENT_GET_IMPLEMENTATION_ID( FmXCheckBoxCell )
+
+
+void SAL_CALL FmXCheckBoxCell::addItemListener( const Reference< css::awt::XItemListener >& l )
+{
+ m_aItemListeners.addInterface( l );
+}
+
+
+void SAL_CALL FmXCheckBoxCell::removeItemListener( const Reference< css::awt::XItemListener >& l )
+{
+ m_aItemListeners.removeInterface( l );
+}
+
+
+void SAL_CALL FmXCheckBoxCell::setState( sal_Int16 n )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if (m_pBox)
+ {
+ UpdateFromColumn();
+ m_pBox->SetState( static_cast<TriState>(n) );
+ }
+}
+
+
+sal_Int16 SAL_CALL FmXCheckBoxCell::getState()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if (m_pBox)
+ {
+ UpdateFromColumn();
+ return static_cast<sal_Int16>(m_pBox->GetState());
+ }
+ return TRISTATE_INDET;
+}
+
+
+void SAL_CALL FmXCheckBoxCell::enableTriState( sal_Bool b )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if (m_pBox)
+ m_pBox->EnableTriState( b );
+}
+
+
+void SAL_CALL FmXCheckBoxCell::addActionListener( const Reference< awt::XActionListener >& Listener )
+{
+ m_aActionListeners.addInterface( Listener );
+}
+
+
+void SAL_CALL FmXCheckBoxCell::removeActionListener( const Reference< awt::XActionListener >& Listener )
+{
+ m_aActionListeners.removeInterface( Listener );
+}
+
+
+void SAL_CALL FmXCheckBoxCell::setLabel( const OUString& Label )
+{
+ SolarMutexGuard aGuard;
+ if ( m_pColumn )
+ {
+ DbGridControl& rGrid( m_pColumn->GetParent() );
+ rGrid.SetColumnTitle( rGrid.GetColumnId( m_pColumn->GetFieldPos() ), Label );
+ }
+}
+
+
+void SAL_CALL FmXCheckBoxCell::setActionCommand( const OUString& Command )
+{
+ m_aActionCommand = Command;
+}
+
+
+vcl::Window* FmXCheckBoxCell::getEventWindow() const
+{
+ return m_pBox;
+}
+
+
+void FmXCheckBoxCell::onWindowEvent( const VclEventId _nEventId, const vcl::Window& _rWindow, const void* _pEventData )
+{
+ switch ( _nEventId )
+ {
+ case VclEventId::CheckboxToggle:
+ {
+ // check boxes are to be committed immediately (this holds for ordinary check box controls in
+ // documents, and this must hold for check boxes in grid columns, too
+ // 91210 - 22.08.2001 - frank.schoenheit@sun.com
+ m_pCellControl->Commit();
+
+ Reference< XWindow > xKeepAlive( this );
+ if ( m_aItemListeners.getLength() && m_pBox )
+ {
+ awt::ItemEvent aEvent;
+ aEvent.Source = *this;
+ aEvent.Highlighted = 0;
+ aEvent.Selected = m_pBox->GetState();
+ m_aItemListeners.notifyEach( &awt::XItemListener::itemStateChanged, aEvent );
+ }
+ if ( m_aActionListeners.getLength() )
+ {
+ awt::ActionEvent aEvent;
+ aEvent.Source = *this;
+ aEvent.ActionCommand = m_aActionCommand;
+ m_aActionListeners.notifyEach( &awt::XActionListener::actionPerformed, aEvent );
+ }
+ }
+ break;
+
+ default:
+ FmXDataCell::onWindowEvent( _nEventId, _rWindow, _pEventData );
+ break;
+ }
+}
+
+FmXListBoxCell::FmXListBoxCell(DbGridColumn* pColumn, std::unique_ptr<DbCellControl> pControl)
+ : FmXTextCell(pColumn, std::move(pControl))
+ , m_aItemListeners(m_aMutex)
+ , m_aActionListeners(m_aMutex)
+ , m_pBox(&static_cast<svt::ListBoxControl&>(m_pCellControl->GetWindow()))
+ , m_nLines(Application::GetSettings().GetStyleSettings().GetListBoxMaximumLineCount())
+ , m_bMulti(false)
+{
+ m_pBox->SetAuxModifyHdl(LINK(this, FmXListBoxCell, ChangedHdl));
+}
+
+FmXListBoxCell::~FmXListBoxCell()
+{
+ if (!OComponentHelper::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+}
+
+// OComponentHelper
+void FmXListBoxCell::disposing()
+{
+ css::lang::EventObject aEvt(*this);
+ m_aItemListeners.disposeAndClear(aEvt);
+ m_aActionListeners.disposeAndClear(aEvt);
+
+ m_pBox->SetAuxModifyHdl(Link<LinkParamNone*,void>());
+ m_pBox = nullptr;
+
+ FmXTextCell::disposing();
+}
+
+Any SAL_CALL FmXListBoxCell::queryAggregation( const css::uno::Type& _rType )
+{
+ Any aReturn = FmXTextCell::queryAggregation(_rType);
+
+ if ( !aReturn.hasValue() )
+ aReturn = FmXListBoxCell_Base::queryInterface( _rType );
+
+ return aReturn;
+}
+
+Sequence< css::uno::Type > SAL_CALL FmXListBoxCell::getTypes( )
+{
+ return ::comphelper::concatSequences(
+ FmXTextCell::getTypes(),
+ FmXListBoxCell_Base::getTypes()
+ );
+}
+
+IMPLEMENT_GET_IMPLEMENTATION_ID( FmXListBoxCell )
+
+void SAL_CALL FmXListBoxCell::addItemListener(const Reference< css::awt::XItemListener >& l)
+{
+ m_aItemListeners.addInterface( l );
+}
+
+void SAL_CALL FmXListBoxCell::removeItemListener(const Reference< css::awt::XItemListener >& l)
+{
+ m_aItemListeners.removeInterface( l );
+}
+
+void SAL_CALL FmXListBoxCell::addActionListener(const Reference< css::awt::XActionListener >& l)
+{
+ m_aActionListeners.addInterface( l );
+}
+
+void SAL_CALL FmXListBoxCell::removeActionListener(const Reference< css::awt::XActionListener >& l)
+{
+ m_aActionListeners.removeInterface( l );
+}
+
+void SAL_CALL FmXListBoxCell::addItem(const OUString& aItem, sal_Int16 nPos)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (m_pBox)
+ {
+ weld::ComboBox& rBox = m_pBox->get_widget();
+ rBox.insert_text(nPos, aItem);
+ }
+}
+
+void SAL_CALL FmXListBoxCell::addItems(const css::uno::Sequence<OUString>& aItems, sal_Int16 nPos)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (m_pBox)
+ {
+ weld::ComboBox& rBox = m_pBox->get_widget();
+ sal_uInt16 nP = nPos;
+ for ( const auto& rItem : aItems )
+ {
+ rBox.insert_text(nP, rItem);
+ if ( nPos != -1 ) // Not if 0xFFFF, because LIST_APPEND
+ nP++;
+ }
+ }
+}
+
+void SAL_CALL FmXListBoxCell::removeItems(sal_Int16 nPos, sal_Int16 nCount)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( m_pBox )
+ {
+ weld::ComboBox& rBox = m_pBox->get_widget();
+ for ( sal_uInt16 n = nCount; n; )
+ rBox.remove( nPos + (--n) );
+ }
+}
+
+sal_Int16 SAL_CALL FmXListBoxCell::getItemCount()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (!m_pBox)
+ return 0;
+ weld::ComboBox& rBox = m_pBox->get_widget();
+ return rBox.get_count();
+}
+
+OUString SAL_CALL FmXListBoxCell::getItem(sal_Int16 nPos)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (!m_pBox)
+ return OUString();
+ weld::ComboBox& rBox = m_pBox->get_widget();
+ return rBox.get_text(nPos);
+}
+
+css::uno::Sequence<OUString> SAL_CALL FmXListBoxCell::getItems()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ css::uno::Sequence<OUString> aSeq;
+ if (m_pBox)
+ {
+ weld::ComboBox& rBox = m_pBox->get_widget();
+ const sal_Int32 nEntries = rBox.get_count();
+ aSeq = css::uno::Sequence<OUString>( nEntries );
+ for ( sal_Int32 n = nEntries; n; )
+ {
+ --n;
+ aSeq.getArray()[n] = rBox.get_text( n );
+ }
+ }
+ return aSeq;
+}
+
+sal_Int16 SAL_CALL FmXListBoxCell::getSelectedItemPos()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (m_pBox)
+ {
+ UpdateFromColumn();
+ weld::ComboBox& rBox = m_pBox->get_widget();
+ sal_Int32 nPos = rBox.get_active();
+ if (nPos > SHRT_MAX || nPos < SHRT_MIN)
+ throw std::out_of_range("awt::XListBox::getSelectedItemPos can only return a short");
+ return nPos;
+ }
+ return 0;
+}
+
+Sequence< sal_Int16 > SAL_CALL FmXListBoxCell::getSelectedItemsPos()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ Sequence<sal_Int16> aSeq;
+
+ if (m_pBox)
+ {
+ UpdateFromColumn();
+ weld::ComboBox& rBox = m_pBox->get_widget();
+ auto nActive = rBox.get_active();
+ if (nActive != -1)
+ {
+ aSeq = Sequence<sal_Int16>(1);
+ aSeq.getArray()[0] = nActive;
+ }
+ }
+ return aSeq;
+}
+
+OUString SAL_CALL FmXListBoxCell::getSelectedItem()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ OUString aItem;
+
+ if (m_pBox)
+ {
+ UpdateFromColumn();
+ weld::ComboBox& rBox = m_pBox->get_widget();
+ aItem = rBox.get_active_text();
+ }
+
+ return aItem;
+}
+
+css::uno::Sequence<OUString> SAL_CALL FmXListBoxCell::getSelectedItems()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ css::uno::Sequence<OUString> aSeq;
+
+ if (m_pBox)
+ {
+ UpdateFromColumn();
+ weld::ComboBox& rBox = m_pBox->get_widget();
+ auto nActive = rBox.get_active();
+ if (nActive != -1)
+ {
+ aSeq = css::uno::Sequence<OUString>(1);
+ aSeq.getArray()[0] = rBox.get_text(nActive);
+ }
+ }
+ return aSeq;
+}
+
+void SAL_CALL FmXListBoxCell::selectItemPos(sal_Int16 nPos, sal_Bool bSelect)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if (m_pBox)
+ {
+ weld::ComboBox& rBox = m_pBox->get_widget();
+ if (bSelect)
+ rBox.set_active(nPos);
+ else if (nPos == rBox.get_active())
+ rBox.set_active(-1);
+ }
+}
+
+void SAL_CALL FmXListBoxCell::selectItemsPos(const Sequence< sal_Int16 >& aPositions, sal_Bool bSelect)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if (m_pBox)
+ {
+ weld::ComboBox& rBox = m_pBox->get_widget();
+ for ( sal_uInt16 n = static_cast<sal_uInt16>(aPositions.getLength()); n; )
+ {
+ auto nPos = static_cast<sal_uInt16>(aPositions.getConstArray()[--n]);
+ if (bSelect)
+ rBox.set_active(nPos);
+ else if (nPos == rBox.get_active())
+ rBox.set_active(-1);
+ }
+ }
+}
+
+void SAL_CALL FmXListBoxCell::selectItem(const OUString& aItem, sal_Bool bSelect)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if (m_pBox)
+ {
+ weld::ComboBox& rBox = m_pBox->get_widget();
+ auto nPos = rBox.find_text(aItem);
+ if (bSelect)
+ rBox.set_active(nPos);
+ else if (nPos == rBox.get_active())
+ rBox.set_active(-1);
+ }
+}
+
+sal_Bool SAL_CALL FmXListBoxCell::isMutipleMode()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ return m_bMulti;
+}
+
+void SAL_CALL FmXListBoxCell::setMultipleMode(sal_Bool bMulti)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ m_bMulti = bMulti;
+}
+
+sal_Int16 SAL_CALL FmXListBoxCell::getDropDownLineCount()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return m_nLines;
+}
+
+void SAL_CALL FmXListBoxCell::setDropDownLineCount(sal_Int16 nLines)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ m_nLines = nLines; // just store it to return it
+}
+
+void SAL_CALL FmXListBoxCell::makeVisible(sal_Int16 /*nEntry*/)
+{
+}
+
+IMPL_LINK_NOARG(FmXListBoxCell, ChangedHdl, LinkParamNone*, void)
+{
+ if (!m_pBox)
+ return;
+
+ weld::ComboBox& rBox = m_pBox->get_widget();
+
+ if (!rBox.changed_by_direct_pick())
+ return;
+
+ OnDoubleClick();
+
+ css::awt::ItemEvent aEvent;
+ aEvent.Source = *this;
+ aEvent.Highlighted = 0;
+
+ // with multiple selection 0xFFFF, otherwise the ID
+ aEvent.Selected = (rBox.get_active() != -1 )
+ ? rBox.get_active() : 0xFFFF;
+
+ m_aItemListeners.notifyEach( &awt::XItemListener::itemStateChanged, aEvent );
+}
+
+void FmXListBoxCell::OnDoubleClick()
+{
+ ::comphelper::OInterfaceIteratorHelper2 aIt( m_aActionListeners );
+
+ css::awt::ActionEvent aEvent;
+ aEvent.Source = *this;
+ weld::ComboBox& rBox = m_pBox->get_widget();
+ aEvent.ActionCommand = rBox.get_active_text();
+
+ while( aIt.hasMoreElements() )
+ static_cast< css::awt::XActionListener *>(aIt.next())->actionPerformed( aEvent );
+}
+
+FmXComboBoxCell::FmXComboBoxCell( DbGridColumn* pColumn, std::unique_ptr<DbCellControl> pControl )
+ :FmXTextCell( pColumn, std::move(pControl) )
+ ,m_aItemListeners( m_aMutex )
+ ,m_aActionListeners( m_aMutex )
+ ,m_pComboBox(&static_cast<ComboBoxControl&>(m_pCellControl->GetWindow()))
+ ,m_nLines(Application::GetSettings().GetStyleSettings().GetListBoxMaximumLineCount())
+{
+ m_pComboBox->SetAuxModifyHdl(LINK(this, FmXComboBoxCell, ChangedHdl));
+}
+
+FmXComboBoxCell::~FmXComboBoxCell()
+{
+ if ( !OComponentHelper::rBHelper.bDisposed )
+ {
+ acquire();
+ dispose();
+ }
+
+}
+
+void FmXComboBoxCell::disposing()
+{
+ css::lang::EventObject aEvt(*this);
+ m_aItemListeners.disposeAndClear(aEvt);
+ m_aActionListeners.disposeAndClear(aEvt);
+
+ m_pComboBox->SetAuxModifyHdl(Link<LinkParamNone*,void>());
+ m_pComboBox = nullptr;
+
+ FmXTextCell::disposing();
+}
+
+Any SAL_CALL FmXComboBoxCell::queryAggregation( const css::uno::Type& _rType )
+{
+ Any aReturn = FmXTextCell::queryAggregation(_rType);
+
+ if ( !aReturn.hasValue() )
+ aReturn = FmXComboBoxCell_Base::queryInterface( _rType );
+
+ return aReturn;
+}
+
+Sequence< Type > SAL_CALL FmXComboBoxCell::getTypes( )
+{
+ return ::comphelper::concatSequences(
+ FmXTextCell::getTypes(),
+ FmXComboBoxCell_Base::getTypes()
+ );
+}
+
+IMPLEMENT_GET_IMPLEMENTATION_ID( FmXComboBoxCell )
+
+void SAL_CALL FmXComboBoxCell::addItemListener(const Reference< awt::XItemListener >& l)
+{
+ m_aItemListeners.addInterface( l );
+}
+
+void SAL_CALL FmXComboBoxCell::removeItemListener(const Reference< awt::XItemListener >& l)
+{
+ m_aItemListeners.removeInterface( l );
+}
+
+void SAL_CALL FmXComboBoxCell::addActionListener(const Reference< awt::XActionListener >& l)
+{
+ m_aActionListeners.addInterface( l );
+}
+
+
+void SAL_CALL FmXComboBoxCell::removeActionListener(const Reference< awt::XActionListener >& l)
+{
+ m_aActionListeners.removeInterface( l );
+}
+
+void SAL_CALL FmXComboBoxCell::addItem( const OUString& Item, sal_Int16 Pos )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (!m_pComboBox)
+ return;
+ weld::ComboBox& rBox = m_pComboBox->get_widget();
+ rBox.insert_text(Pos, Item);
+}
+
+void SAL_CALL FmXComboBoxCell::addItems( const Sequence< OUString >& Items, sal_Int16 Pos )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (!m_pComboBox)
+ return;
+ weld::ComboBox& rBox = m_pComboBox->get_widget();
+ sal_uInt16 nP = Pos;
+ for ( const auto& rItem : Items )
+ {
+ rBox.insert_text(nP, rItem);
+ if ( Pos != -1 )
+ nP++;
+ }
+}
+
+void SAL_CALL FmXComboBoxCell::removeItems( sal_Int16 Pos, sal_Int16 Count )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (!m_pComboBox)
+ return;
+ weld::ComboBox& rBox = m_pComboBox->get_widget();
+ for ( sal_uInt16 n = Count; n; )
+ rBox.remove( Pos + (--n) );
+}
+
+sal_Int16 SAL_CALL FmXComboBoxCell::getItemCount()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (!m_pComboBox)
+ return 0;
+ weld::ComboBox& rBox = m_pComboBox->get_widget();
+ return rBox.get_count();
+}
+
+OUString SAL_CALL FmXComboBoxCell::getItem( sal_Int16 Pos )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (!m_pComboBox)
+ return OUString();
+ weld::ComboBox& rBox = m_pComboBox->get_widget();
+ return rBox.get_text(Pos);
+}
+
+Sequence< OUString > SAL_CALL FmXComboBoxCell::getItems()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ Sequence< OUString > aItems;
+ if (m_pComboBox)
+ {
+ weld::ComboBox& rBox = m_pComboBox->get_widget();
+ const sal_Int32 nEntries = rBox.get_count();
+ aItems.realloc( nEntries );
+ OUString* pItem = aItems.getArray();
+ for ( sal_Int32 n=0; n<nEntries; ++n, ++pItem )
+ *pItem = rBox.get_text(n);
+ }
+ return aItems;
+}
+
+sal_Int16 SAL_CALL FmXComboBoxCell::getDropDownLineCount()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return m_nLines;
+}
+
+void SAL_CALL FmXComboBoxCell::setDropDownLineCount(sal_Int16 nLines)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_nLines = nLines; // just store it to return it
+}
+
+IMPL_LINK_NOARG(FmXComboBoxCell, ChangedHdl, LinkParamNone*, void)
+{
+ if (!m_pComboBox)
+ return;
+
+ weld::ComboBox& rComboBox = m_pComboBox->get_widget();
+
+ if (!rComboBox.changed_by_direct_pick())
+ return;
+
+ awt::ItemEvent aEvent;
+ aEvent.Source = *this;
+ aEvent.Highlighted = 0;
+
+ // with invalid selection 0xFFFF, otherwise the position
+ aEvent.Selected = ( rComboBox.get_active() != -1 )
+ ? rComboBox.get_active()
+ : 0xFFFF;
+ m_aItemListeners.notifyEach( &awt::XItemListener::itemStateChanged, aEvent );
+}
+
+FmXFilterCell::FmXFilterCell(DbGridColumn* pColumn, std::unique_ptr<DbFilterField> pControl )
+ :FmXGridCell( pColumn, std::move(pControl) )
+ ,m_aTextListeners(m_aMutex)
+{
+ static_cast<DbFilterField*>(m_pCellControl.get())->SetCommitHdl( LINK( this, FmXFilterCell, OnCommit ) );
+}
+
+FmXFilterCell::~FmXFilterCell()
+{
+ if (!OComponentHelper::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+
+}
+
+// XUnoTunnel
+sal_Int64 SAL_CALL FmXFilterCell::getSomething( const Sequence< sal_Int8 >& _rIdentifier )
+{
+ sal_Int64 nReturn(0);
+
+ if ( isUnoTunnelId<FmXFilterCell>(_rIdentifier) )
+ {
+ nReturn = reinterpret_cast<sal_Int64>(this);
+ }
+
+ return nReturn;
+}
+
+namespace
+{
+ class theFmXFilterCellUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theFmXFilterCellUnoTunnelId > {};
+}
+
+const Sequence<sal_Int8>& FmXFilterCell::getUnoTunnelId()
+{
+ return theFmXFilterCellUnoTunnelId::get().getSeq();
+}
+
+
+void FmXFilterCell::PaintCell( OutputDevice& rDev, const tools::Rectangle& rRect )
+{
+ static_cast< DbFilterField* >( m_pCellControl.get() )->PaintCell( rDev, rRect );
+}
+
+// OComponentHelper
+
+void FmXFilterCell::disposing()
+{
+ css::lang::EventObject aEvt(*this);
+ m_aTextListeners.disposeAndClear(aEvt);
+
+ static_cast<DbFilterField*>(m_pCellControl.get())->SetCommitHdl(Link<DbFilterField&,void>());
+
+ FmXGridCell::disposing();
+}
+
+
+Any SAL_CALL FmXFilterCell::queryAggregation( const css::uno::Type& _rType )
+{
+ Any aReturn = FmXGridCell::queryAggregation(_rType);
+
+ if ( !aReturn.hasValue() )
+ aReturn = FmXFilterCell_Base::queryInterface( _rType );
+
+ return aReturn;
+}
+
+
+Sequence< css::uno::Type > SAL_CALL FmXFilterCell::getTypes( )
+{
+ return ::comphelper::concatSequences(
+ FmXGridCell::getTypes(),
+ FmXFilterCell_Base::getTypes()
+ );
+}
+
+
+IMPLEMENT_GET_IMPLEMENTATION_ID( FmXFilterCell )
+
+// css::awt::XTextComponent
+
+void SAL_CALL FmXFilterCell::addTextListener(const Reference< css::awt::XTextListener >& l)
+{
+ m_aTextListeners.addInterface( l );
+}
+
+
+void SAL_CALL FmXFilterCell::removeTextListener(const Reference< css::awt::XTextListener >& l)
+{
+ m_aTextListeners.removeInterface( l );
+}
+
+
+void SAL_CALL FmXFilterCell::setText( const OUString& aText )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ static_cast<DbFilterField*>(m_pCellControl.get())->SetText(aText);
+}
+
+
+void SAL_CALL FmXFilterCell::insertText( const css::awt::Selection& /*rSel*/, const OUString& /*aText*/ )
+{
+}
+
+
+OUString SAL_CALL FmXFilterCell::getText()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return static_cast<DbFilterField*>(m_pCellControl.get())->GetText();
+}
+
+
+OUString SAL_CALL FmXFilterCell::getSelectedText()
+{
+ return getText();
+}
+
+
+void SAL_CALL FmXFilterCell::setSelection( const css::awt::Selection& /*aSelection*/ )
+{
+}
+
+
+css::awt::Selection SAL_CALL FmXFilterCell::getSelection()
+{
+ return css::awt::Selection();
+}
+
+
+sal_Bool SAL_CALL FmXFilterCell::isEditable()
+{
+ return true;
+}
+
+
+void SAL_CALL FmXFilterCell::setEditable( sal_Bool /*bEditable*/ )
+{
+}
+
+
+sal_Int16 SAL_CALL FmXFilterCell::getMaxTextLen()
+{
+ return 0;
+}
+
+
+void SAL_CALL FmXFilterCell::setMaxTextLen( sal_Int16 /*nLen*/ )
+{
+}
+
+
+IMPL_LINK_NOARG(FmXFilterCell, OnCommit, DbFilterField&, void)
+{
+ ::comphelper::OInterfaceIteratorHelper2 aIt( m_aTextListeners );
+ css::awt::TextEvent aEvt;
+ aEvt.Source = *this;
+ while( aIt.hasMoreElements() )
+ static_cast< css::awt::XTextListener *>(aIt.next())->textChanged( aEvt );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/fmcomp/gridcols.cxx b/svx/source/fmcomp/gridcols.cxx
new file mode 100644
index 000000000..1e44ece89
--- /dev/null
+++ b/svx/source/fmcomp/gridcols.cxx
@@ -0,0 +1,103 @@
+/* -*- 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 <gridcols.hxx>
+#include <tools/debug.hxx>
+#include <fmservs.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+
+using namespace ::com::sun::star::uno;
+
+
+static const css::uno::Sequence<OUString>& getColumnTypes()
+{
+ static css::uno::Sequence<OUString> aColumnTypes = [&]()
+ {
+ css::uno::Sequence<OUString> tmp(10);
+ OUString* pNames = tmp.getArray();
+ pNames[TYPE_CHECKBOX] = FM_COL_CHECKBOX;
+ pNames[TYPE_COMBOBOX] = FM_COL_COMBOBOX;
+ pNames[TYPE_CURRENCYFIELD] = FM_COL_CURRENCYFIELD;
+ pNames[TYPE_DATEFIELD] = FM_COL_DATEFIELD;
+ pNames[TYPE_FORMATTEDFIELD] = FM_COL_FORMATTEDFIELD;
+ pNames[TYPE_LISTBOX] = FM_COL_LISTBOX;
+ pNames[TYPE_NUMERICFIELD] = FM_COL_NUMERICFIELD;
+ pNames[TYPE_PATTERNFIELD] = FM_COL_PATTERNFIELD;
+ pNames[TYPE_TEXTFIELD] = FM_COL_TEXTFIELD;
+ pNames[TYPE_TIMEFIELD] = FM_COL_TIMEFIELD;
+ return tmp;
+ }();
+ return aColumnTypes;
+}
+
+
+extern "C" {
+
+// comparison of PropertyInfo
+static int NameCompare(const void* pFirst, const void* pSecond)
+{
+ return static_cast<OUString const *>(pFirst)->compareTo(*static_cast<OUString const *>(pSecond));
+}
+
+}
+
+namespace
+{
+
+ sal_Int32 lcl_findPos(const OUString& aStr, const Sequence< OUString>& rList)
+ {
+ const OUString* pStrList = rList.getConstArray();
+ OUString* pResult = static_cast<OUString*>(bsearch(&aStr, static_cast<void const *>(pStrList), rList.getLength(), sizeof(OUString),
+ &NameCompare));
+
+ if (pResult)
+ return (pResult - pStrList);
+ else
+ return -1;
+ }
+}
+
+
+sal_Int32 getColumnTypeByModelName(const OUString& aModelName)
+{
+ const OUString aModelPrefix("com.sun.star.form.component.");
+ const OUString aCompatibleModelPrefix("stardiv.one.form.component.");
+
+ sal_Int32 nTypeId = -1;
+ if (aModelName == FM_COMPONENT_EDIT)
+ nTypeId = TYPE_TEXTFIELD;
+ else
+ {
+ sal_Int32 nPrefixPos = aModelName.indexOf(aModelPrefix);
+#ifdef DBG_UTIL
+ sal_Int32 nCompatiblePrefixPos = aModelName.indexOf(aCompatibleModelPrefix);
+ DBG_ASSERT( (nPrefixPos != -1) || (nCompatiblePrefixPos != -1), "::getColumnTypeByModelName() : wrong service!");
+#endif
+
+ OUString aColumnType = (nPrefixPos != -1)
+ ? aModelName.copy(aModelPrefix.getLength())
+ : aModelName.copy(aCompatibleModelPrefix.getLength());
+
+ const css::uno::Sequence<OUString>& rColumnTypes = getColumnTypes();
+ nTypeId = lcl_findPos(aColumnType, rColumnTypes);
+ }
+ return nTypeId;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/fmcomp/gridctrl.cxx b/svx/source/fmcomp/gridctrl.cxx
new file mode 100644
index 000000000..4c9795bad
--- /dev/null
+++ b/svx/source/fmcomp/gridctrl.cxx
@@ -0,0 +1,3645 @@
+/* -*- 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/log.hxx>
+#include <helpids.h>
+#include <svx/gridctrl.hxx>
+#include <gridcell.hxx>
+#include <svx/fmtools.hxx>
+#include <svtools/stringtransfer.hxx>
+#include <connectivity/dbtools.hxx>
+#include <connectivity/dbconversion.hxx>
+
+#include <fmprop.hxx>
+#include <com/sun/star/sdbc/ResultSetConcurrency.hpp>
+#include <com/sun/star/accessibility/XAccessible.hpp>
+#include <com/sun/star/sdb/XResultSetAccess.hpp>
+#include <com/sun/star/sdb/RowChangeAction.hpp>
+#include <com/sun/star/sdb/XRowsChangeBroadcaster.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/sdbc/XResultSetUpdate.hpp>
+#include <com/sun/star/sdbc/XRowSet.hpp>
+#include <com/sun/star/sdbcx/Privilege.hpp>
+#include <com/sun/star/util/NumberFormatter.hpp>
+#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/PropertyChangeEvent.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <tools/diagnose_ex.h>
+#include <tools/debug.hxx>
+#include <tools/fract.hxx>
+#include <vcl/builder.hxx>
+#include <vcl/button.hxx>
+#include <vcl/fixed.hxx>
+#include <vcl/menu.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/svapp.hxx>
+
+#include <svx/strings.hrc>
+
+#include <svx/dialmgr.hxx>
+#include <sdbdatacolumn.hxx>
+
+#include <comphelper/property.hxx>
+#include <comphelper/types.hxx>
+#include <cppuhelper/implbase.hxx>
+
+#include <algorithm>
+#include <cstdlib>
+#include <map>
+#include <memory>
+
+using namespace ::dbtools;
+using namespace ::dbtools::DBTypeConversion;
+using namespace ::svxform;
+using namespace ::svt;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::sdbcx;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::datatransfer;
+using namespace ::com::sun::star::container;
+using namespace com::sun::star::accessibility;
+
+#define ROWSTATUS(row) (!row.is() ? "NULL" : row->GetStatus() == GridRowStatus::Clean ? "CLEAN" : row->GetStatus() == GridRowStatus::Modified ? "MODIFIED" : row->GetStatus() == GridRowStatus::Deleted ? "DELETED" : "INVALID")
+
+static constexpr auto DEFAULT_BROWSE_MODE =
+ BrowserMode::COLUMNSELECTION
+ | BrowserMode::MULTISELECTION
+ | BrowserMode::KEEPHIGHLIGHT
+ | BrowserMode::TRACKING_TIPS
+ | BrowserMode::HLINES
+ | BrowserMode::VLINES
+ | BrowserMode::HEADERBAR_NEW;
+
+class RowSetEventListener : public ::cppu::WeakImplHelper<XRowsChangeListener>
+{
+ VclPtr<DbGridControl> m_pControl;
+public:
+ explicit RowSetEventListener(DbGridControl* i_pControl) : m_pControl(i_pControl)
+ {
+ }
+
+private:
+ // XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& /*i_aEvt*/) override
+ {
+ }
+ virtual void SAL_CALL rowsChanged(const css::sdb::RowsChangeEvent& i_aEvt) override
+ {
+ if ( i_aEvt.Action == RowChangeAction::UPDATE )
+ {
+ ::DbGridControl::GrantControlAccess aAccess;
+ CursorWrapper* pSeek = m_pControl->GetSeekCursor(aAccess);
+ const DbGridRowRef& rSeekRow = m_pControl->GetSeekRow(aAccess);
+ for(const Any& rBookmark : i_aEvt.Bookmarks)
+ {
+ pSeek->moveToBookmark(rBookmark);
+ // get the data
+ rSeekRow->SetState(pSeek, true);
+ sal_Int32 nSeekPos = pSeek->getRow() - 1;
+ m_pControl->SetSeekPos(nSeekPos,aAccess);
+ m_pControl->RowModified(nSeekPos);
+ }
+ }
+ }
+};
+
+class GridFieldValueListener;
+typedef std::map<sal_uInt16, GridFieldValueListener*> ColumnFieldValueListeners;
+
+class GridFieldValueListener : protected ::comphelper::OPropertyChangeListener
+{
+ osl::Mutex m_aMutex;
+ DbGridControl& m_rParent;
+ rtl::Reference<::comphelper::OPropertyChangeMultiplexer> m_pRealListener;
+ sal_uInt16 m_nId;
+ sal_Int16 m_nSuspended;
+ bool m_bDisposed : 1;
+
+public:
+ GridFieldValueListener(DbGridControl& _rParent, const Reference< XPropertySet >& xField, sal_uInt16 _nId);
+ virtual ~GridFieldValueListener() override;
+
+ virtual void _propertyChanged(const PropertyChangeEvent& evt) override;
+
+ void suspend() { ++m_nSuspended; }
+ void resume() { --m_nSuspended; }
+
+ void dispose();
+};
+
+GridFieldValueListener::GridFieldValueListener(DbGridControl& _rParent, const Reference< XPropertySet >& _rField, sal_uInt16 _nId)
+ :OPropertyChangeListener(m_aMutex)
+ ,m_rParent(_rParent)
+ ,m_nId(_nId)
+ ,m_nSuspended(0)
+ ,m_bDisposed(false)
+{
+ if (_rField.is())
+ {
+ m_pRealListener = new ::comphelper::OPropertyChangeMultiplexer(this, _rField);
+ m_pRealListener->addProperty(FM_PROP_VALUE);
+ }
+}
+
+GridFieldValueListener::~GridFieldValueListener()
+{
+ dispose();
+}
+
+void GridFieldValueListener::_propertyChanged(const PropertyChangeEvent& /*_evt*/)
+{
+ DBG_ASSERT(m_nSuspended>=0, "GridFieldValueListener::_propertyChanged : resume > suspend !");
+ if (m_nSuspended <= 0)
+ m_rParent.FieldValueChanged(m_nId);
+}
+
+void GridFieldValueListener::dispose()
+{
+ if (m_bDisposed)
+ {
+ DBG_ASSERT(m_pRealListener.get() == nullptr, "GridFieldValueListener::dispose : inconsistent !");
+ return;
+ }
+
+ if (m_pRealListener.is())
+ {
+ m_pRealListener->dispose();
+ m_pRealListener.clear();
+ }
+
+ m_bDisposed = true;
+ m_rParent.FieldListenerDisposing(m_nId);
+}
+
+class DisposeListenerGridBridge : public FmXDisposeListener
+{
+ DbGridControl& m_rParent;
+ rtl::Reference<FmXDisposeMultiplexer> m_xRealListener;
+
+public:
+ DisposeListenerGridBridge( DbGridControl& _rParent, const Reference< XComponent >& _rxObject);
+ virtual ~DisposeListenerGridBridge() override;
+
+ virtual void disposing(sal_Int16 _nId) override { m_rParent.disposing(_nId); }
+};
+
+DisposeListenerGridBridge::DisposeListenerGridBridge(DbGridControl& _rParent, const Reference< XComponent >& _rxObject)
+ :FmXDisposeListener()
+ ,m_rParent(_rParent)
+{
+
+ if (_rxObject.is())
+ {
+ m_xRealListener = new FmXDisposeMultiplexer(this, _rxObject);
+ }
+}
+
+DisposeListenerGridBridge::~DisposeListenerGridBridge()
+{
+ if (m_xRealListener.is())
+ {
+ m_xRealListener->dispose();
+ }
+}
+
+static const DbGridControlNavigationBarState ControlMap[] =
+ {
+ DbGridControlNavigationBarState::Text,
+ DbGridControlNavigationBarState::Absolute,
+ DbGridControlNavigationBarState::Of,
+ DbGridControlNavigationBarState::Count,
+ DbGridControlNavigationBarState::First,
+ DbGridControlNavigationBarState::Next,
+ DbGridControlNavigationBarState::Prev,
+ DbGridControlNavigationBarState::Last,
+ DbGridControlNavigationBarState::New,
+ DbGridControlNavigationBarState::NONE
+ };
+
+bool CompareBookmark(const Any& aLeft, const Any& aRight)
+{
+ return aLeft == aRight;
+}
+
+class FmXGridSourcePropListener : public ::comphelper::OPropertyChangeListener
+{
+ VclPtr<DbGridControl> m_pParent;
+
+ // a DbGridControl has no mutex, so we use our own as the base class expects one
+ osl::Mutex m_aMutex;
+ sal_Int16 m_nSuspended;
+
+public:
+ explicit FmXGridSourcePropListener(DbGridControl* _pParent);
+
+ void suspend() { ++m_nSuspended; }
+ void resume() { --m_nSuspended; }
+
+ virtual void _propertyChanged(const PropertyChangeEvent& evt) override;
+};
+
+FmXGridSourcePropListener::FmXGridSourcePropListener(DbGridControl* _pParent)
+ :OPropertyChangeListener(m_aMutex)
+ ,m_pParent(_pParent)
+ ,m_nSuspended(0)
+{
+ DBG_ASSERT(m_pParent, "FmXGridSourcePropListener::FmXGridSourcePropListener : invalid parent !");
+}
+
+void FmXGridSourcePropListener::_propertyChanged(const PropertyChangeEvent& evt)
+{
+ DBG_ASSERT(m_nSuspended>=0, "FmXGridSourcePropListener::_propertyChanged : resume > suspend !");
+ if (m_nSuspended <= 0)
+ m_pParent->DataSourcePropertyChanged(evt);
+}
+
+DbGridControl::NavigationBar::AbsolutePos::AbsolutePos(vcl::Window* pParent, WinBits nStyle)
+ :NumericField(pParent, nStyle)
+{
+ SetMin(1);
+ SetFirst(1);
+ SetSpinSize(1);
+
+ SetDecimalDigits(0);
+ SetStrictFormat(true);
+}
+
+void DbGridControl::NavigationBar::AbsolutePos::KeyInput(const KeyEvent& rEvt)
+{
+ if (rEvt.GetKeyCode() == KEY_RETURN && !GetText().isEmpty())
+ {
+ sal_Int64 nRecord = GetValue();
+ if (nRecord < GetMin() || nRecord > GetMax())
+ return;
+ else
+ static_cast<NavigationBar*>(GetParent())->PositionDataSource(static_cast<sal_Int32>(nRecord));
+ }
+ else if (rEvt.GetKeyCode() == KEY_TAB)
+ GetParent()->GetParent()->GrabFocus();
+ else
+ NumericField::KeyInput(rEvt);
+}
+
+void DbGridControl::NavigationBar::AbsolutePos::LoseFocus()
+{
+ NumericField::LoseFocus();
+ sal_Int64 nRecord = GetValue();
+ if (nRecord < GetMin() || nRecord > GetMax())
+ return;
+ else
+ {
+ static_cast<NavigationBar*>(GetParent())->PositionDataSource(static_cast<sal_Int32>(nRecord));
+ static_cast<NavigationBar*>(GetParent())->InvalidateState(DbGridControlNavigationBarState::Absolute);
+ }
+}
+
+void DbGridControl::NavigationBar::PositionDataSource(sal_Int32 nRecord)
+{
+ if (m_bPositioning)
+ return;
+ // the MoveToPosition may cause a LoseFocus which would lead to a second MoveToPosition,
+ // so protect against this recursion
+ m_bPositioning = true;
+ static_cast<DbGridControl*>(GetParent())->MoveToPosition(nRecord - 1);
+ m_bPositioning = false;
+}
+
+DbGridControl::NavigationBar::NavigationBar(vcl::Window* pParent)
+ :Control(pParent, 0)
+ ,m_aRecordText(VclPtr<FixedText>::Create(this, WB_VCENTER))
+ ,m_aAbsolute(VclPtr<DbGridControl::NavigationBar::AbsolutePos>::Create(this, WB_CENTER | WB_VCENTER))
+ ,m_aRecordOf(VclPtr<FixedText>::Create(this, WB_VCENTER))
+ ,m_aRecordCount(VclPtr<FixedText>::Create(this, WB_VCENTER))
+ ,m_aFirstBtn(VclPtr<ImageButton>::Create(this, WB_RECTSTYLE|WB_NOPOINTERFOCUS))
+ ,m_aPrevBtn(VclPtr<ImageButton>::Create(this, WB_REPEAT|WB_RECTSTYLE|WB_NOPOINTERFOCUS))
+ ,m_aNextBtn(VclPtr<ImageButton>::Create(this, WB_REPEAT|WB_RECTSTYLE|WB_NOPOINTERFOCUS))
+ ,m_aLastBtn(VclPtr<ImageButton>::Create(this, WB_RECTSTYLE|WB_NOPOINTERFOCUS))
+ ,m_aNewBtn(VclPtr<ImageButton>::Create(this, WB_RECTSTYLE|WB_NOPOINTERFOCUS))
+ ,m_nCurrentPos(-1)
+ ,m_bPositioning(false)
+{
+ m_aFirstBtn->SetSymbol(SymbolType::FIRST);
+ m_aPrevBtn->SetSymbol(SymbolType::PREV);
+ m_aNextBtn->SetSymbol(SymbolType::NEXT);
+ m_aLastBtn->SetSymbol(SymbolType::LAST);
+ m_aNewBtn->SetModeImage(static_cast<DbGridControl*>(pParent)->GetImage(EditBrowseBox::NEW));
+
+ m_aFirstBtn->SetHelpId(HID_GRID_TRAVEL_FIRST);
+ m_aPrevBtn->SetHelpId(HID_GRID_TRAVEL_PREV);
+ m_aNextBtn->SetHelpId(HID_GRID_TRAVEL_NEXT);
+ m_aLastBtn->SetHelpId(HID_GRID_TRAVEL_LAST);
+ m_aNewBtn->SetHelpId(HID_GRID_TRAVEL_NEW);
+ m_aAbsolute->SetHelpId(HID_GRID_TRAVEL_ABSOLUTE);
+ m_aRecordCount->SetHelpId(HID_GRID_NUMBEROFRECORDS);
+
+ // set handlers for buttons
+ m_aFirstBtn->SetClickHdl(LINK(this,NavigationBar,OnClick));
+ m_aPrevBtn->SetClickHdl(LINK(this,NavigationBar,OnClick));
+ m_aNextBtn->SetClickHdl(LINK(this,NavigationBar,OnClick));
+ m_aLastBtn->SetClickHdl(LINK(this,NavigationBar,OnClick));
+ m_aNewBtn->SetClickHdl(LINK(this,NavigationBar,OnClick));
+
+ m_aRecordText->SetText(SvxResId(RID_STR_REC_TEXT));
+ m_aRecordOf->SetText(SvxResId(RID_STR_REC_FROM_TEXT));
+ m_aRecordCount->SetText(OUString('?'));
+
+ m_aFirstBtn->Disable();
+ m_aPrevBtn->Disable();
+ m_aNextBtn->Disable();
+ m_aLastBtn->Disable();
+ m_aNewBtn->Disable();
+ m_aRecordText->Disable();
+ m_aRecordOf->Disable();
+ m_aRecordCount->Disable();
+ m_aAbsolute->Disable();
+
+ AllSettings aSettings = m_aNextBtn->GetSettings();
+ MouseSettings aMouseSettings = aSettings.GetMouseSettings();
+ aMouseSettings.SetButtonRepeat(aMouseSettings.GetButtonRepeat() / 4);
+ aSettings.SetMouseSettings(aMouseSettings);
+ m_aNextBtn->SetSettings(aSettings, true);
+ m_aPrevBtn->SetSettings(aSettings, true);
+
+ m_aFirstBtn->Show();
+ m_aPrevBtn->Show();
+ m_aNextBtn->Show();
+ m_aLastBtn->Show();
+ m_aNewBtn->Show();
+ m_aRecordText->Show();
+ m_aRecordOf->Show();
+ m_aRecordCount->Show();
+ m_aAbsolute->Show();
+}
+
+
+DbGridControl::NavigationBar::~NavigationBar()
+{
+ disposeOnce();
+}
+
+void DbGridControl::NavigationBar::dispose()
+{
+ m_aRecordText.disposeAndClear();
+ m_aAbsolute.disposeAndClear();
+ m_aRecordOf.disposeAndClear();
+ m_aRecordCount.disposeAndClear();
+ m_aFirstBtn.disposeAndClear();
+ m_aPrevBtn.disposeAndClear();
+ m_aNextBtn.disposeAndClear();
+ m_aLastBtn.disposeAndClear();
+ m_aNewBtn.disposeAndClear();
+ Control::dispose();
+}
+
+namespace
+{
+ void SetPosAndSize(Button& _rButton,Point& _rPos,const Size& _rSize)
+ {
+ _rButton.SetPosPixel( _rPos );
+ _rButton.SetSizePixel( _rSize );
+ _rPos.AdjustX(static_cast<sal_uInt16>(_rSize.Width()) );
+ }
+}
+
+sal_uInt16 DbGridControl::NavigationBar::ArrangeControls()
+{
+ // positioning of the controls
+ // calculate base size
+ tools::Rectangle aRect(static_cast<DbGridControl*>(GetParent())->GetControlArea());
+ long nH = aRect.GetSize().Height();
+ long nW = GetParent()->GetOutputSizePixel().Width();
+ Size aBorder = LogicToPixel(Size(2, 2), MapMode(MapUnit::MapAppFont));
+ aBorder = Size(CalcZoom(aBorder.Width()), CalcZoom(aBorder.Height()));
+ sal_uInt16 nX = 1;
+ sal_uInt16 nY = 0;
+
+ {
+ vcl::Font aApplFont(GetSettings().GetStyleSettings().GetToolFont());
+ m_aAbsolute->SetControlFont( aApplFont );
+ aApplFont.SetTransparent( true );
+ m_aRecordText->SetControlFont( aApplFont );
+ m_aRecordOf->SetControlFont( aApplFont );
+ m_aRecordCount->SetControlFont( aApplFont );
+ }
+
+ // set size and position of the control
+ OUString aText = m_aRecordText->GetText();
+ long nTextWidth = m_aRecordText->GetTextWidth(aText);
+ m_aRecordText->SetPosPixel(Point(nX,nY));
+ m_aRecordText->SetSizePixel(Size(nTextWidth,nH));
+ nX = sal::static_int_cast< sal_uInt16 >(nX + nTextWidth + aBorder.Width());
+
+ // count an extra hairspace (U+200A) left and right
+ const OUString sevenDigits(m_aAbsolute->CreateFieldText(6000000));
+ const OUString hairSpace(u'\x200A');
+ OUString textPattern = hairSpace + sevenDigits + hairSpace;
+ nTextWidth = m_aAbsolute->GetTextWidth(textPattern);
+ m_aAbsolute->SetPosPixel(Point(nX,nY));
+ m_aAbsolute->SetSizePixel(Size(nTextWidth, nH));
+ nX = sal::static_int_cast< sal_uInt16 >(nX + nTextWidth + aBorder.Width());
+
+ aText = m_aRecordOf->GetText();
+ nTextWidth = m_aRecordOf->GetTextWidth(aText);
+ m_aRecordOf->SetPosPixel(Point(nX,nY));
+ m_aRecordOf->SetSizePixel(Size(nTextWidth,nH));
+ nX = sal::static_int_cast< sal_uInt16 >(nX + nTextWidth + aBorder.Width());
+
+ textPattern = sevenDigits + " * (" + sevenDigits + ")";
+ nTextWidth = m_aRecordCount->GetTextWidth(textPattern);
+ m_aRecordCount->SetPosPixel(Point(nX,nY));
+ m_aRecordCount->SetSizePixel(Size(nTextWidth,nH));
+ nX = sal::static_int_cast< sal_uInt16 >(nX + nTextWidth + aBorder.Width());
+
+ Point aButtonPos(nX,nY);
+ const Size aButtonSize(nH,nH);
+ SetPosAndSize(*m_aFirstBtn, aButtonPos, aButtonSize);
+ SetPosAndSize(*m_aPrevBtn, aButtonPos, aButtonSize);
+ SetPosAndSize(*m_aNextBtn, aButtonPos, aButtonSize);
+ SetPosAndSize(*m_aLastBtn, aButtonPos, aButtonSize);
+ SetPosAndSize(*m_aNewBtn, aButtonPos, aButtonSize);
+
+ nX = sal::static_int_cast< sal_uInt16 >(aButtonPos.X() + 1);
+
+ nW = std::max(nW - GetSettings().GetStyleSettings().GetScrollBarSize(), 0L);
+
+ if (nX > nW)
+ {
+ aButtonPos.setX( nW-nH );
+ m_aNewBtn->SetPosPixel(aButtonPos);
+ aButtonPos.AdjustX( -nH );
+ m_aLastBtn->SetPosPixel(aButtonPos);
+ aButtonPos.AdjustX( -nH );
+ m_aNextBtn->SetPosPixel(aButtonPos);
+ aButtonPos.AdjustX( -nH );
+ m_aPrevBtn->SetPosPixel(aButtonPos);
+ aButtonPos.AdjustX( -nH );
+ m_aFirstBtn->SetPosPixel(aButtonPos);
+
+ auto nDiff = nX - nW;
+
+ Size aSize = m_aAbsolute->GetSizePixel();
+ aSize.AdjustWidth( -(nDiff/3.0) );
+ m_aAbsolute->SetSizePixel(aSize);
+
+ aSize = m_aRecordCount->GetSizePixel();
+ aSize.AdjustWidth( -(nDiff/3.0*2) );
+ m_aRecordCount->SetSizePixel(aSize);
+
+ Point aPos = m_aRecordOf->GetPosPixel();
+ aPos.AdjustX( -(nDiff/3.0) );
+ m_aRecordOf->SetPosPixel(aPos);
+
+ aPos = m_aRecordCount->GetPosPixel();
+ aPos.AdjustX( -(nDiff/3.0) );
+ m_aRecordCount->SetPosPixel(aPos);
+
+ vcl::Window* pWindows[] =
+ {
+ m_aRecordText.get(),
+ m_aAbsolute.get(),
+ m_aRecordOf.get(),
+ m_aRecordCount.get(),
+ m_aFirstBtn.get(),
+ m_aPrevBtn.get(),
+ m_aNextBtn.get(),
+ m_aLastBtn.get(),
+ m_aNewBtn.get()
+ };
+
+ for (vcl::Window* pWindow : pWindows)
+ {
+ if (pWindow->GetPosPixel().X() < 0)
+ pWindow->SetSizePixel(Size(0, nH));
+ aSize = pWindow->GetSizePixel();
+ auto nExcess = (pWindow->GetPosPixel().X() + aSize.Width()) - nW;
+ if (nExcess > 0)
+ {
+ aSize.AdjustWidth( -nExcess );
+ pWindow->SetSizePixel(aSize);
+ }
+ }
+
+ nX = nW;
+ }
+
+ return nX;
+}
+
+IMPL_LINK(DbGridControl::NavigationBar, OnClick, Button *, pButton, void )
+{
+ DbGridControl* pParent = static_cast<DbGridControl*>(GetParent());
+
+ if (pParent->m_aMasterSlotExecutor.IsSet())
+ {
+ bool lResult = false;
+ if (pButton == m_aFirstBtn.get())
+ lResult = pParent->m_aMasterSlotExecutor.Call(DbGridControlNavigationBarState::First);
+ else if( pButton == m_aPrevBtn.get() )
+ lResult = pParent->m_aMasterSlotExecutor.Call(DbGridControlNavigationBarState::Prev);
+ else if( pButton == m_aNextBtn.get() )
+ lResult = pParent->m_aMasterSlotExecutor.Call(DbGridControlNavigationBarState::Next);
+ else if( pButton == m_aLastBtn.get() )
+ lResult = pParent->m_aMasterSlotExecutor.Call(DbGridControlNavigationBarState::Last);
+ else if( pButton == m_aNewBtn.get() )
+ lResult = pParent->m_aMasterSlotExecutor.Call(DbGridControlNavigationBarState::New);
+
+ if (lResult)
+ // the link already handled it
+ return;
+ }
+
+ if (pButton == m_aFirstBtn.get())
+ pParent->MoveToFirst();
+ else if( pButton == m_aPrevBtn.get() )
+ pParent->MoveToPrev();
+ else if( pButton == m_aNextBtn.get() )
+ pParent->MoveToNext();
+ else if( pButton == m_aLastBtn.get() )
+ pParent->MoveToLast();
+ else if( pButton == m_aNewBtn.get() )
+ pParent->AppendNew();
+}
+
+void DbGridControl::NavigationBar::InvalidateAll(sal_Int32 nCurrentPos, bool bAll)
+{
+ if (m_nCurrentPos != nCurrentPos || nCurrentPos < 0 || bAll)
+ {
+ DbGridControl* pParent = static_cast<DbGridControl*>(GetParent());
+
+ sal_Int32 nAdjustedRowCount = pParent->GetRowCount() - ((pParent->GetOptions() & DbGridControlOptions::Insert) ? 2 : 1);
+
+ // check if everything needs to be invalidated
+ bAll = bAll || m_nCurrentPos <= 0;
+ bAll = bAll || nCurrentPos <= 0;
+ bAll = bAll || m_nCurrentPos >= nAdjustedRowCount;
+ bAll = bAll || nCurrentPos >= nAdjustedRowCount;
+
+ if ( bAll )
+ {
+ m_nCurrentPos = nCurrentPos;
+ int i = 0;
+ while (ControlMap[i] != DbGridControlNavigationBarState::NONE)
+ SetState(ControlMap[i++]);
+ }
+ else // is in the center
+ {
+ m_nCurrentPos = nCurrentPos;
+ SetState(DbGridControlNavigationBarState::Count);
+ SetState(DbGridControlNavigationBarState::Absolute);
+ }
+ }
+}
+
+bool DbGridControl::NavigationBar::GetState(DbGridControlNavigationBarState nWhich) const
+{
+ DbGridControl* pParent = static_cast<DbGridControl*>(GetParent());
+
+ if (!pParent->IsOpen() || pParent->IsDesignMode() || !pParent->IsEnabled()
+ || pParent->IsFilterMode() )
+ return false;
+ else
+ {
+ // check if we have a master state provider
+ if (pParent->m_aMasterStateProvider.IsSet())
+ {
+ long nState = pParent->m_aMasterStateProvider.Call( nWhich );
+ if (nState>=0)
+ return (nState>0);
+ }
+
+ bool bAvailable = true;
+
+ switch (nWhich)
+ {
+ case DbGridControlNavigationBarState::First:
+ case DbGridControlNavigationBarState::Prev:
+ bAvailable = m_nCurrentPos > 0;
+ break;
+ case DbGridControlNavigationBarState::Next:
+ if(pParent->m_bRecordCountFinal)
+ {
+ bAvailable = m_nCurrentPos < pParent->GetRowCount() - 1;
+ if (!bAvailable && pParent->GetOptions() & DbGridControlOptions::Insert)
+ bAvailable = (m_nCurrentPos == pParent->GetRowCount() - 2) && pParent->IsModified();
+ }
+ break;
+ case DbGridControlNavigationBarState::Last:
+ if(pParent->m_bRecordCountFinal)
+ {
+ if (pParent->GetOptions() & DbGridControlOptions::Insert)
+ bAvailable = pParent->IsCurrentAppending() ? pParent->GetRowCount() > 1 :
+ m_nCurrentPos != pParent->GetRowCount() - 2;
+ else
+ bAvailable = m_nCurrentPos != pParent->GetRowCount() - 1;
+ }
+ break;
+ case DbGridControlNavigationBarState::New:
+ bAvailable = (pParent->GetOptions() & DbGridControlOptions::Insert) && pParent->GetRowCount() && m_nCurrentPos < pParent->GetRowCount() - 1;
+ break;
+ case DbGridControlNavigationBarState::Absolute:
+ bAvailable = pParent->GetRowCount() > 0;
+ break;
+ default: break;
+ }
+ return bAvailable;
+ }
+}
+
+void DbGridControl::NavigationBar::SetState(DbGridControlNavigationBarState nWhich)
+{
+ bool bAvailable = GetState(nWhich);
+ DbGridControl* pParent = static_cast<DbGridControl*>(GetParent());
+ vcl::Window* pWnd = nullptr;
+ switch (nWhich)
+ {
+ case DbGridControlNavigationBarState::First:
+ pWnd = m_aFirstBtn.get();
+ break;
+ case DbGridControlNavigationBarState::Prev:
+ pWnd = m_aPrevBtn.get();
+ break;
+ case DbGridControlNavigationBarState::Next:
+ pWnd = m_aNextBtn.get();
+ break;
+ case DbGridControlNavigationBarState::Last:
+ pWnd = m_aLastBtn.get();
+ break;
+ case DbGridControlNavigationBarState::New:
+ pWnd = m_aNewBtn.get();
+ break;
+ case DbGridControlNavigationBarState::Absolute:
+ pWnd = m_aAbsolute.get();
+ if (bAvailable)
+ {
+ if (pParent->m_nTotalCount >= 0)
+ {
+ if (pParent->IsCurrentAppending())
+ m_aAbsolute->SetMax(pParent->m_nTotalCount + 1);
+ else
+ m_aAbsolute->SetMax(pParent->m_nTotalCount);
+ }
+ else
+ m_aAbsolute->SetMax(LONG_MAX);
+
+ m_aAbsolute->SetValue(m_nCurrentPos + 1);
+ }
+ else
+ m_aAbsolute->SetText(OUString());
+ break;
+ case DbGridControlNavigationBarState::Text:
+ pWnd = m_aRecordText.get();
+ break;
+ case DbGridControlNavigationBarState::Of:
+ pWnd = m_aRecordOf.get();
+ break;
+ case DbGridControlNavigationBarState::Count:
+ {
+ pWnd = m_aRecordCount.get();
+ OUString aText;
+ if (bAvailable)
+ {
+ if (pParent->GetOptions() & DbGridControlOptions::Insert)
+ {
+ if (pParent->IsCurrentAppending() && !pParent->IsModified())
+ aText = m_aAbsolute->CreateFieldText(pParent->GetRowCount());
+ else
+ aText = m_aAbsolute->CreateFieldText(pParent->GetRowCount() - 1);
+ }
+ else
+ aText = m_aAbsolute->CreateFieldText(pParent->GetRowCount());
+ if(!pParent->m_bRecordCountFinal)
+ aText += " *";
+ }
+ else
+ aText.clear();
+
+ // add the number of selected rows, if applicable
+ if (pParent->GetSelectRowCount())
+ {
+ OUString aExtendedInfo = aText + " (" +
+ m_aAbsolute->CreateFieldText(pParent->GetSelectRowCount()) + ")";
+ pWnd->SetText(aExtendedInfo);
+ }
+ else
+ pWnd->SetText(aText);
+
+ pParent->SetRealRowCount(aText);
+ } break;
+ default: break;
+ }
+ DBG_ASSERT(pWnd, "no window");
+ if (pWnd && (pWnd->IsEnabled() != bAvailable))
+ // this "pWnd->IsEnabled() != bAvailable" is a little hack : Window::Enable always generates a user
+ // event (ImplGenerateMouseMove) even if nothing happened. This may lead to some unwanted effects, so we
+ // do this check.
+ // For further explanation see Bug 69900.
+ pWnd->Enable(bAvailable);
+}
+
+void DbGridControl::NavigationBar::Resize()
+{
+ Control::Resize();
+ ArrangeControls();
+}
+
+void DbGridControl::NavigationBar::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
+{
+ Control::Paint(rRenderContext, rRect);
+ Point aAbsolutePos = m_aAbsolute->GetPosPixel();
+ Size aAbsoluteSize = m_aAbsolute->GetSizePixel();
+
+ rRenderContext.DrawLine(Point(aAbsolutePos.X() - 1, 0 ),
+ Point(aAbsolutePos.X() - 1, aAbsolutePos.Y() + aAbsoluteSize.Height()));
+
+ rRenderContext.DrawLine(Point(aAbsolutePos.X() + aAbsoluteSize.Width() + 1, 0 ),
+ Point(aAbsolutePos.X() + aAbsoluteSize.Width() + 1, aAbsolutePos.Y() + aAbsoluteSize.Height()));
+}
+
+void DbGridControl::NavigationBar::StateChanged(StateChangedType nType)
+{
+ Control::StateChanged(nType);
+
+ vcl::Window* pWindows[] =
+ {
+ m_aRecordText.get(),
+ m_aAbsolute.get(),
+ m_aRecordOf.get(),
+ m_aRecordCount.get(),
+ m_aFirstBtn.get(),
+ m_aPrevBtn.get(),
+ m_aNextBtn.get(),
+ m_aLastBtn.get(),
+ m_aNewBtn.get()
+ };
+
+ switch ( nType )
+ {
+ case StateChangedType::Mirroring:
+ {
+ bool bIsRTLEnabled = IsRTLEnabled();
+ for (vcl::Window* pWindow : pWindows)
+ pWindow->EnableRTL( bIsRTLEnabled );
+ }
+ break;
+
+ case StateChangedType::Zoom:
+ {
+ Fraction aZoom = GetZoom();
+
+ // not all of these controls need to know the new zoom, but to be sure ...
+ vcl::Font aFont(GetSettings().GetStyleSettings().GetToolFont());
+ if (IsControlFont())
+ aFont.Merge(GetControlFont());
+
+ for (vcl::Window* pWindow : pWindows)
+ {
+ pWindow->SetZoom(aZoom);
+ pWindow->SetZoomedPointFont(*pWindow, aFont);
+ }
+
+ SetZoomedPointFont(*this, aFont);
+
+ // rearrange the controls
+ ArrangeControls();
+ }
+ break;
+ default:;
+ }
+}
+
+DbGridRow::DbGridRow():m_eStatus(GridRowStatus::Clean), m_bIsNew(true)
+{}
+
+DbGridRow::DbGridRow(CursorWrapper* pCur, bool bPaintCursor)
+ :m_bIsNew(false)
+{
+
+ if (pCur && pCur->Is())
+ {
+ Reference< XIndexAccess > xColumns(pCur->getColumns(), UNO_QUERY);
+ for (sal_Int32 i = 0; i < xColumns->getCount(); ++i)
+ {
+ Reference< XPropertySet > xColSet(
+ xColumns->getByIndex(i), css::uno::UNO_QUERY);
+ m_aVariants.emplace_back( new DataColumn(xColSet) );
+ }
+
+ if (pCur->rowDeleted())
+ m_eStatus = GridRowStatus::Deleted;
+ else
+ {
+ if (bPaintCursor)
+ m_eStatus = (pCur->isAfterLast() || pCur->isBeforeFirst()) ? GridRowStatus::Invalid : GridRowStatus::Clean;
+ else
+ {
+ const Reference< XPropertySet >& xSet = pCur->getPropertySet();
+ if (xSet.is())
+ {
+ m_bIsNew = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW));
+ if (!m_bIsNew && (pCur->isAfterLast() || pCur->isBeforeFirst()))
+ m_eStatus = GridRowStatus::Invalid;
+ else if (::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISMODIFIED)))
+ m_eStatus = GridRowStatus::Modified;
+ else
+ m_eStatus = GridRowStatus::Clean;
+ }
+ else
+ m_eStatus = GridRowStatus::Invalid;
+ }
+ }
+ if (!m_bIsNew && IsValid())
+ m_aBookmark = pCur->getBookmark();
+ else
+ m_aBookmark = Any();
+ }
+ else
+ m_eStatus = GridRowStatus::Invalid;
+}
+
+DbGridRow::~DbGridRow()
+{
+}
+
+void DbGridRow::SetState(CursorWrapper* pCur, bool bPaintCursor)
+{
+ if (pCur && pCur->Is())
+ {
+ if (pCur->rowDeleted())
+ {
+ m_eStatus = GridRowStatus::Deleted;
+ m_bIsNew = false;
+ }
+ else
+ {
+ m_eStatus = GridRowStatus::Clean;
+ if (!bPaintCursor)
+ {
+ const Reference< XPropertySet >& xSet = pCur->getPropertySet();
+ DBG_ASSERT(xSet.is(), "DbGridRow::SetState : invalid cursor !");
+
+ if (::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISMODIFIED)))
+ m_eStatus = GridRowStatus::Modified;
+ m_bIsNew = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW));
+ }
+ else
+ m_bIsNew = false;
+ }
+
+ try
+ {
+ if (!m_bIsNew && IsValid())
+ m_aBookmark = pCur->getBookmark();
+ else
+ m_aBookmark = Any();
+ }
+ catch(SQLException&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ m_aBookmark = Any();
+ m_eStatus = GridRowStatus::Invalid;
+ m_bIsNew = false;
+ }
+ }
+ else
+ {
+ m_aBookmark = Any();
+ m_eStatus = GridRowStatus::Invalid;
+ m_bIsNew = false;
+ }
+}
+
+DbGridControl::DbGridControl(
+ Reference< XComponentContext > const & _rxContext,
+ vcl::Window* pParent,
+ WinBits nBits)
+ :EditBrowseBox(pParent, EditBrowseBoxFlags::NONE, nBits, DEFAULT_BROWSE_MODE )
+ ,m_xContext(_rxContext)
+ ,m_aBar(VclPtr<DbGridControl::NavigationBar>::Create(this))
+ ,m_nAsynAdjustEvent(nullptr)
+ ,m_pDataSourcePropListener(nullptr)
+ ,m_pFieldListeners(nullptr)
+ ,m_pGridListener(nullptr)
+ ,m_nSeekPos(-1)
+ ,m_nTotalCount(-1)
+ ,m_aNullDate(::dbtools::DBTypeConversion::getStandardDate())
+ ,m_nMode(DEFAULT_BROWSE_MODE)
+ ,m_nCurrentPos(-1)
+ ,m_nDeleteEvent(nullptr)
+ ,m_nOptions(DbGridControlOptions::Readonly)
+ ,m_nOptionMask(DbGridControlOptions::Insert | DbGridControlOptions::Update | DbGridControlOptions::Delete)
+ ,m_nLastColId(sal_uInt16(-1))
+ ,m_nLastRowId(-1)
+ ,m_bDesignMode(false)
+ ,m_bRecordCountFinal(false)
+ ,m_bNavigationBar(true)
+ ,m_bSynchDisplay(true)
+ ,m_bHandle(true)
+ ,m_bFilterMode(false)
+ ,m_bWantDestruction(false)
+ ,m_bPendingAdjustRows(false)
+ ,m_bHideScrollbars( false )
+ ,m_bUpdating(false)
+{
+
+ OUString sName(SvxResId(RID_STR_NAVIGATIONBAR));
+ m_aBar->SetAccessibleName(sName);
+ m_aBar->Show();
+ ImplInitWindow( InitWindowFacet::All );
+}
+
+void DbGridControl::InsertHandleColumn()
+{
+ // BrowseBox has problems when painting without a handleColumn (hide it here)
+ if (HasHandle())
+ BrowseBox::InsertHandleColumn(GetDefaultColumnWidth(OUString()));
+ else
+ BrowseBox::InsertHandleColumn(0);
+}
+
+void DbGridControl::Init()
+{
+ VclPtr<BrowserHeader> pNewHeader = CreateHeaderBar(this);
+ pHeader->SetMouseTransparent(false);
+
+ SetHeaderBar(pNewHeader);
+ SetMode(m_nMode);
+ SetCursorColor(Color(0xFF, 0, 0));
+
+ InsertHandleColumn();
+}
+
+DbGridControl::~DbGridControl()
+{
+ disposeOnce();
+}
+
+void DbGridControl::dispose()
+{
+ if (!IsDisposed())
+ {
+ RemoveColumns();
+
+ m_bWantDestruction = true;
+ osl::MutexGuard aGuard(m_aDestructionSafety);
+ if (m_pFieldListeners)
+ DisconnectFromFields();
+ m_pCursorDisposeListener.reset();
+ }
+
+ if (m_nDeleteEvent)
+ Application::RemoveUserEvent(m_nDeleteEvent);
+
+ if (m_pDataSourcePropMultiplexer.is())
+ {
+ m_pDataSourcePropMultiplexer->dispose();
+ m_pDataSourcePropMultiplexer.clear(); // this should delete the multiplexer
+ delete m_pDataSourcePropListener;
+ m_pDataSourcePropListener = nullptr;
+ }
+ m_xRowSetListener.clear();
+
+ m_pDataCursor.reset();
+ m_pSeekCursor.reset();
+
+ m_aBar.disposeAndClear();
+
+ EditBrowseBox::dispose();
+}
+
+void DbGridControl::StateChanged( StateChangedType nType )
+{
+ EditBrowseBox::StateChanged( nType );
+
+ switch (nType)
+ {
+ case StateChangedType::Mirroring:
+ ImplInitWindow( InitWindowFacet::WritingMode );
+ Invalidate();
+ break;
+
+ case StateChangedType::Zoom:
+ {
+ ImplInitWindow( InitWindowFacet::Font );
+
+ // and give it a chance to rearrange
+ Point aPoint = GetControlArea().TopLeft();
+ sal_uInt16 nX = static_cast<sal_uInt16>(aPoint.X());
+ ArrangeControls(nX, static_cast<sal_uInt16>(aPoint.Y()));
+ ReserveControlArea(nX);
+ }
+ break;
+ case StateChangedType::ControlFont:
+ ImplInitWindow( InitWindowFacet::Font );
+ Invalidate();
+ break;
+ case StateChangedType::ControlForeground:
+ ImplInitWindow( InitWindowFacet::Foreground );
+ Invalidate();
+ break;
+ case StateChangedType::ControlBackground:
+ ImplInitWindow( InitWindowFacet::Background );
+ Invalidate();
+ break;
+ default:;
+ }
+}
+
+void DbGridControl::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ EditBrowseBox::DataChanged( rDCEvt );
+ if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS ) &&
+ (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
+ {
+ ImplInitWindow( InitWindowFacet::All );
+ Invalidate();
+ }
+}
+
+void DbGridControl::Select()
+{
+ EditBrowseBox::Select();
+
+ // as the selected rows may have changed, update the according display in our navigation bar
+ m_aBar->InvalidateState(DbGridControlNavigationBarState::Count);
+
+ if (m_pGridListener)
+ m_pGridListener->selectionChanged();
+}
+
+void DbGridControl::ImplInitWindow( const InitWindowFacet _eInitWhat )
+{
+ for (auto const & pCol : m_aColumns)
+ {
+ pCol->ImplInitWindow( GetDataWindow(), _eInitWhat );
+ }
+
+ if ( _eInitWhat & InitWindowFacet::WritingMode )
+ {
+ if ( m_bNavigationBar )
+ {
+ m_aBar->EnableRTL( IsRTLEnabled() );
+ }
+ }
+
+ if ( _eInitWhat & InitWindowFacet::Font )
+ {
+ if ( m_bNavigationBar )
+ {
+ if ( IsControlFont() )
+ m_aBar->SetControlFont( GetControlFont() );
+ else
+ m_aBar->SetControlFont();
+
+ m_aBar->SetZoom( GetZoom() );
+ }
+ }
+
+ if ( _eInitWhat & InitWindowFacet::Background )
+ {
+ if (IsControlBackground())
+ {
+ GetDataWindow().SetBackground(GetControlBackground());
+ GetDataWindow().SetControlBackground(GetControlBackground());
+ GetDataWindow().SetFillColor(GetControlBackground());
+ }
+ else
+ {
+ GetDataWindow().SetControlBackground();
+ GetDataWindow().SetFillColor(GetFillColor());
+ }
+ }
+}
+
+void DbGridControl::RemoveRows(bool bNewCursor)
+{
+ // Did the data cursor change?
+ if (!bNewCursor)
+ {
+ m_pSeekCursor.reset();
+ m_xPaintRow = m_xDataRow = m_xEmptyRow = m_xCurrentRow = m_xSeekRow = nullptr;
+ m_nCurrentPos = m_nSeekPos = -1;
+ m_nOptions = DbGridControlOptions::Readonly;
+
+ RowRemoved(0, GetRowCount(), false);
+ m_nTotalCount = -1;
+ }
+ else
+ {
+ RemoveRows();
+ }
+}
+
+void DbGridControl::RemoveRows()
+{
+ // we're going to remove all columns and all row, so deactivate the current cell
+ if (IsEditing())
+ DeactivateCell();
+
+ // de-initialize all columns
+ // if there are columns, free all controllers
+ for (auto const & pColumn : m_aColumns)
+ pColumn->Clear();
+
+ m_pSeekCursor.reset();
+ m_pDataCursor.reset();
+
+ m_xPaintRow = m_xDataRow = m_xEmptyRow = m_xCurrentRow = m_xSeekRow = nullptr;
+ m_nCurrentPos = m_nSeekPos = m_nTotalCount = -1;
+ m_nOptions = DbGridControlOptions::Readonly;
+
+ // reset number of sentences to zero in the browser
+ EditBrowseBox::RemoveRows();
+ m_aBar->InvalidateAll(m_nCurrentPos, true);
+}
+
+void DbGridControl::ArrangeControls(sal_uInt16& nX, sal_uInt16 nY)
+{
+ // positioning of the controls
+ if (m_bNavigationBar)
+ {
+ tools::Rectangle aRect(GetControlArea());
+ m_aBar->SetPosSizePixel(Point(0, nY + 1), Size(aRect.GetSize().Width(), aRect.GetSize().Height() - 1));
+ nX = m_aBar->ArrangeControls();
+ }
+}
+
+void DbGridControl::EnableHandle(bool bEnable)
+{
+ if (m_bHandle == bEnable)
+ return;
+
+ // HandleColumn is only hidden because there are a lot of problems while painting otherwise
+ RemoveColumn( HandleColumnId );
+ m_bHandle = bEnable;
+ InsertHandleColumn();
+}
+
+namespace
+{
+ bool adjustModeForScrollbars( BrowserMode& _rMode, bool _bNavigationBar, bool _bHideScrollbars )
+ {
+ BrowserMode nOldMode = _rMode;
+
+ if ( !_bNavigationBar )
+ {
+ _rMode &= ~BrowserMode::AUTO_HSCROLL;
+ }
+
+ if ( _bHideScrollbars )
+ {
+ _rMode |= BrowserMode::NO_HSCROLL | BrowserMode::NO_VSCROLL;
+ _rMode &= ~BrowserMode( BrowserMode::AUTO_HSCROLL | BrowserMode::AUTO_VSCROLL );
+ }
+ else
+ {
+ _rMode |= BrowserMode::AUTO_HSCROLL | BrowserMode::AUTO_VSCROLL;
+ _rMode &= ~BrowserMode( BrowserMode::NO_HSCROLL | BrowserMode::NO_VSCROLL );
+ }
+
+ // note: if we have a navigation bar, we always have an AUTO_HSCROLL. In particular,
+ // _bHideScrollbars is ignored then
+ if ( _bNavigationBar )
+ {
+ _rMode |= BrowserMode::AUTO_HSCROLL;
+ _rMode &= ~BrowserMode::NO_HSCROLL;
+ }
+
+ return nOldMode != _rMode;
+ }
+}
+
+void DbGridControl::EnableNavigationBar(bool bEnable)
+{
+ if (m_bNavigationBar == bEnable)
+ return;
+
+ m_bNavigationBar = bEnable;
+
+ if (bEnable)
+ {
+ m_aBar->Show();
+ m_aBar->Enable();
+ m_aBar->InvalidateAll(m_nCurrentPos, true);
+
+ if ( adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ) )
+ SetMode( m_nMode );
+
+ // get size of the reserved ControlArea
+ Point aPoint = GetControlArea().TopLeft();
+ sal_uInt16 nX = static_cast<sal_uInt16>(aPoint.X());
+
+ ArrangeControls(nX, static_cast<sal_uInt16>(aPoint.Y()));
+ ReserveControlArea(nX);
+ }
+ else
+ {
+ m_aBar->Hide();
+ m_aBar->Disable();
+
+ if ( adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ) )
+ SetMode( m_nMode );
+
+ ReserveControlArea();
+ }
+}
+
+DbGridControlOptions DbGridControl::SetOptions(DbGridControlOptions nOpt)
+{
+ DBG_ASSERT(!m_xCurrentRow.is() || !m_xCurrentRow->IsModified(),
+ "DbGridControl::SetOptions : please do not call when editing a record (things are much easier this way ;) !");
+
+ // for the next setDataSource (which is triggered by a refresh, for instance)
+ m_nOptionMask = nOpt;
+
+ // normalize the new options
+ Reference< XPropertySet > xDataSourceSet = m_pDataCursor->getPropertySet();
+ if (xDataSourceSet.is())
+ {
+ // check what kind of options are available
+ sal_Int32 nPrivileges = 0;
+ xDataSourceSet->getPropertyValue(FM_PROP_PRIVILEGES) >>= nPrivileges;
+ if ((nPrivileges & Privilege::INSERT) == 0)
+ nOpt &= ~DbGridControlOptions::Insert;
+ if ((nPrivileges & Privilege::UPDATE) == 0)
+ nOpt &= ~DbGridControlOptions::Update;
+ if ((nPrivileges & Privilege::DELETE) == 0)
+ nOpt &= ~DbGridControlOptions::Delete;
+ }
+ else
+ nOpt = DbGridControlOptions::Readonly;
+
+ // need to do something after that ?
+ if (nOpt == m_nOptions)
+ return m_nOptions;
+
+ // the 'update' option only affects our BrowserMode (with or w/o focus rect)
+ BrowserMode nNewMode = m_nMode;
+ if (!(m_nMode & BrowserMode::CURSOR_WO_FOCUS))
+ {
+ if (nOpt & DbGridControlOptions::Update)
+ nNewMode |= BrowserMode::HIDECURSOR;
+ else
+ nNewMode &= ~BrowserMode::HIDECURSOR;
+ }
+ else
+ nNewMode &= ~BrowserMode::HIDECURSOR;
+ // should not be necessary if EnablePermanentCursor is used to change the cursor behaviour, but to be sure ...
+
+ if (nNewMode != m_nMode)
+ {
+ SetMode(nNewMode);
+ m_nMode = nNewMode;
+ }
+
+ // _after_ setting the mode because this results in an ActivateCell
+ DeactivateCell();
+
+ bool bInsertChanged = (nOpt & DbGridControlOptions::Insert) != (m_nOptions & DbGridControlOptions::Insert);
+ m_nOptions = nOpt;
+ // we need to set this before the code below because it indirectly uses m_nOptions
+
+ // the 'insert' option affects our empty row
+ if (bInsertChanged)
+ {
+ if (m_nOptions & DbGridControlOptions::Insert)
+ { // the insert option is to be set
+ m_xEmptyRow = new DbGridRow();
+ RowInserted(GetRowCount());
+ }
+ else
+ { // the insert option is to be reset
+ m_xEmptyRow = nullptr;
+ if ((GetCurRow() == GetRowCount() - 1) && (GetCurRow() > 0))
+ GoToRowColumnId(GetCurRow() - 1, GetCurColumnId());
+ RowRemoved(GetRowCount());
+ }
+ }
+
+ // the 'delete' options has no immediate consequences
+
+ ActivateCell();
+ Invalidate();
+ return m_nOptions;
+}
+
+void DbGridControl::ForceHideScrollbars()
+{
+ if ( m_bHideScrollbars )
+ return;
+
+ m_bHideScrollbars = true;
+
+ if ( adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ) )
+ SetMode( m_nMode );
+}
+
+void DbGridControl::EnablePermanentCursor(bool bEnable)
+{
+ if (IsPermanentCursorEnabled() == bEnable)
+ return;
+
+ if (bEnable)
+ {
+ m_nMode &= ~BrowserMode::HIDECURSOR; // without this BrowserMode::CURSOR_WO_FOCUS won't have any affect
+ m_nMode |= BrowserMode::CURSOR_WO_FOCUS;
+ }
+ else
+ {
+ if (m_nOptions & DbGridControlOptions::Update)
+ m_nMode |= BrowserMode::HIDECURSOR; // no cursor at all
+ else
+ m_nMode &= ~BrowserMode::HIDECURSOR; // at least the "non-permanent" cursor
+
+ m_nMode &= ~BrowserMode::CURSOR_WO_FOCUS;
+ }
+ SetMode(m_nMode);
+
+ bool bWasEditing = IsEditing();
+ DeactivateCell();
+ if (bWasEditing)
+ ActivateCell();
+}
+
+bool DbGridControl::IsPermanentCursorEnabled() const
+{
+ return (m_nMode & BrowserMode::CURSOR_WO_FOCUS) && !(m_nMode & BrowserMode::HIDECURSOR);
+}
+
+void DbGridControl::refreshController(sal_uInt16 _nColId, GrantControlAccess /*_aAccess*/)
+{
+ if ((GetCurColumnId() == _nColId) && IsEditing())
+ { // the controller which is currently active needs to be refreshed
+ DeactivateCell();
+ ActivateCell();
+ }
+}
+
+void DbGridControl::setDataSource(const Reference< XRowSet >& _xCursor, DbGridControlOptions nOpts)
+{
+ if (!_xCursor.is() && !m_pDataCursor)
+ return;
+
+ if (m_pDataSourcePropMultiplexer.is())
+ {
+ m_pDataSourcePropMultiplexer->dispose();
+ m_pDataSourcePropMultiplexer.clear(); // this should delete the multiplexer
+ delete m_pDataSourcePropListener;
+ m_pDataSourcePropListener = nullptr;
+ }
+ m_xRowSetListener.clear();
+
+ // is the new cursor valid ?
+ // the cursor is only valid if it contains some columns
+ // if there is no cursor or the cursor is not valid we have to clean up and leave
+ if (!_xCursor.is() || !Reference< XColumnsSupplier > (_xCursor, UNO_QUERY_THROW)->getColumns()->hasElements())
+ {
+ RemoveRows();
+ return;
+ }
+
+ // did the data cursor change?
+ sal_uInt16 nCurPos = GetColumnPos(GetCurColumnId());
+
+ SetUpdateMode(false);
+ RemoveRows();
+ DisconnectFromFields();
+
+ m_pCursorDisposeListener.reset();
+
+ {
+ ::osl::MutexGuard aGuard(m_aAdjustSafety);
+ if (m_nAsynAdjustEvent)
+ {
+ // the adjust was thought to work with the old cursor which we don't have anymore
+ RemoveUserEvent(m_nAsynAdjustEvent);
+ m_nAsynAdjustEvent = nullptr;
+ }
+ }
+
+ // get a new formatter and data cursor
+ m_xFormatter = nullptr;
+ Reference< css::util::XNumberFormatsSupplier > xSupplier = getNumberFormats(getConnection(_xCursor), true);
+ if (xSupplier.is())
+ {
+ m_xFormatter = css::util::NumberFormatter::create(m_xContext);
+ m_xFormatter->attachNumberFormatsSupplier(xSupplier);
+
+ // retrieve the datebase of the Numberformatter
+ try
+ {
+ xSupplier->getNumberFormatSettings()->getPropertyValue("NullDate") >>= m_aNullDate;
+ }
+ catch(Exception&)
+ {
+ }
+ }
+
+ m_pDataCursor.reset(new CursorWrapper(_xCursor));
+
+ // now create a cursor for painting rows
+ // we need that cursor only if we are not in insert only mode
+ Reference< XResultSet > xClone;
+ Reference< XResultSetAccess > xAccess( _xCursor, UNO_QUERY );
+ try
+ {
+ xClone = xAccess.is() ? xAccess->createResultSet() : Reference< XResultSet > ();
+ }
+ catch(Exception&)
+ {
+ }
+ if (xClone.is())
+ m_pSeekCursor.reset(new CursorWrapper(xClone));
+
+ // property listening on the data source
+ // (Normally one class would be sufficient : the multiplexer which could forward the property change to us.
+ // But for that we would have been derived from ::comphelper::OPropertyChangeListener, which isn't exported.
+ // So we introduce a second class, which is a ::comphelper::OPropertyChangeListener (in the implementation file we know this class)
+ // and forwards the property changes to our special method "DataSourcePropertyChanged".)
+ if (m_pDataCursor)
+ {
+ m_pDataSourcePropListener = new FmXGridSourcePropListener(this);
+ m_pDataSourcePropMultiplexer = new ::comphelper::OPropertyChangeMultiplexer(m_pDataSourcePropListener, m_pDataCursor->getPropertySet() );
+ m_pDataSourcePropMultiplexer->addProperty(FM_PROP_ISMODIFIED);
+ m_pDataSourcePropMultiplexer->addProperty(FM_PROP_ISNEW);
+ }
+
+ BrowserMode nOldMode = m_nMode;
+ if (m_pSeekCursor)
+ {
+ try
+ {
+ Reference< XPropertySet > xSet(_xCursor, UNO_QUERY);
+ if (xSet.is())
+ {
+ // check what kind of options are available
+ sal_Int32 nConcurrency = ResultSetConcurrency::READ_ONLY;
+ xSet->getPropertyValue(FM_PROP_RESULTSET_CONCURRENCY) >>= nConcurrency;
+
+ if ( ResultSetConcurrency::UPDATABLE == nConcurrency )
+ {
+ sal_Int32 nPrivileges = 0;
+ xSet->getPropertyValue(FM_PROP_PRIVILEGES) >>= nPrivileges;
+
+ // Insert Option should be set if insert only otherwise you won't see any rows
+ // and no insertion is possible
+ if ((m_nOptionMask & DbGridControlOptions::Insert)
+ && ((nPrivileges & Privilege::INSERT) == Privilege::INSERT) && (nOpts & DbGridControlOptions::Insert))
+ m_nOptions |= DbGridControlOptions::Insert;
+ if ((m_nOptionMask & DbGridControlOptions::Update)
+ && ((nPrivileges & Privilege::UPDATE) == Privilege::UPDATE) && (nOpts & DbGridControlOptions::Update))
+ m_nOptions |= DbGridControlOptions::Update;
+ if ((m_nOptionMask & DbGridControlOptions::Delete)
+ && ((nPrivileges & Privilege::DELETE) == Privilege::DELETE) && (nOpts & DbGridControlOptions::Delete))
+ m_nOptions |= DbGridControlOptions::Delete;
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ bool bPermanentCursor = IsPermanentCursorEnabled();
+ m_nMode = DEFAULT_BROWSE_MODE;
+
+ if ( bPermanentCursor )
+ {
+ m_nMode |= BrowserMode::CURSOR_WO_FOCUS;
+ m_nMode &= ~BrowserMode::HIDECURSOR;
+ }
+ else
+ {
+ // updates are allowed -> no focus rectangle
+ if ( m_nOptions & DbGridControlOptions::Update )
+ m_nMode |= BrowserMode::HIDECURSOR;
+ }
+
+ m_nMode |= BrowserMode::MULTISELECTION;
+
+ adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars );
+
+ Reference< XColumnsSupplier > xSupplyColumns(_xCursor, UNO_QUERY);
+ if (xSupplyColumns.is())
+ InitColumnsByFields(Reference< XIndexAccess > (xSupplyColumns->getColumns(), UNO_QUERY));
+
+ ConnectToFields();
+ }
+
+ sal_uInt32 nRecordCount(0);
+
+ if (m_pSeekCursor)
+ {
+ Reference< XPropertySet > xSet = m_pDataCursor->getPropertySet();
+ xSet->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount;
+ m_bRecordCountFinal = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ROWCOUNTFINAL));
+
+ m_xRowSetListener = new RowSetEventListener(this);
+ Reference< XRowsChangeBroadcaster> xChangeBroad(xSet,UNO_QUERY);
+ if ( xChangeBroad.is( ) )
+ xChangeBroad->addRowsChangeListener(m_xRowSetListener);
+
+
+ // insert the currently known rows
+ // and one row if we are able to insert rows
+ if (m_nOptions & DbGridControlOptions::Insert)
+ {
+ // insert the empty row for insertion
+ m_xEmptyRow = new DbGridRow();
+ ++nRecordCount;
+ }
+ if (nRecordCount)
+ {
+ m_xPaintRow = m_xSeekRow = new DbGridRow(m_pSeekCursor.get(), true);
+ m_xDataRow = new DbGridRow(m_pDataCursor.get(), false);
+ RowInserted(0, nRecordCount, false);
+
+ if (m_xSeekRow->IsValid())
+ try
+ {
+ m_nSeekPos = m_pSeekCursor->getRow() - 1;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ m_nSeekPos = -1;
+ }
+ }
+ else
+ {
+ // no rows so we don't need a seekcursor
+ m_pSeekCursor.reset();
+ }
+ }
+
+ // go to the old column
+ if (nCurPos == BROWSER_INVALIDID || nCurPos >= ColCount())
+ nCurPos = 0;
+
+ // Column zero is a valid choice and guaranteed to exist,
+ // but invisible to the user; if we have at least one
+ // user-visible column, go to that one.
+ if (nCurPos == 0 && ColCount() > 1)
+ nCurPos = 1;
+
+ // there are rows so go to the selected current column
+ if (nRecordCount)
+ GoToRowColumnId(0, GetColumnId(nCurPos));
+ // else stop the editing if necessary
+ else if (IsEditing())
+ DeactivateCell();
+
+ // now reset the mode
+ if (m_nMode != nOldMode)
+ SetMode(m_nMode);
+
+ // RecalcRows was already called while resizing
+ if (!IsResizing() && GetRowCount())
+ RecalcRows(GetTopRow(), GetVisibleRows(), true);
+
+ m_aBar->InvalidateAll(m_nCurrentPos, true);
+ SetUpdateMode(true);
+
+ // start listening on the seek cursor
+ if (m_pSeekCursor)
+ m_pCursorDisposeListener.reset(new DisposeListenerGridBridge(*this, Reference< XComponent > (Reference< XInterface >(*m_pSeekCursor), UNO_QUERY)));
+}
+
+void DbGridControl::RemoveColumns()
+{
+ if ( IsEditing() )
+ DeactivateCell();
+
+ m_aColumns.clear();
+
+ EditBrowseBox::RemoveColumns();
+}
+
+std::unique_ptr<DbGridColumn> DbGridControl::CreateColumn(sal_uInt16 nId)
+{
+ return std::unique_ptr<DbGridColumn>(new DbGridColumn(nId, *this));
+}
+
+sal_uInt16 DbGridControl::AppendColumn(const OUString& rName, sal_uInt16 nWidth, sal_uInt16 nModelPos, sal_uInt16 nId)
+{
+ DBG_ASSERT(nId == BROWSER_INVALIDID, "DbGridControl::AppendColumn : I want to set the ID myself ...");
+ sal_uInt16 nRealPos = nModelPos;
+ if (nModelPos != HEADERBAR_APPEND)
+ {
+ // calc the view pos. we can't use our converting functions because the new column
+ // has no VCL-representation, yet.
+ sal_Int16 nViewPos = nModelPos;
+ while (nModelPos--)
+ {
+ if ( m_aColumns[ nModelPos ]->IsHidden() )
+ --nViewPos;
+ }
+ // restore nModelPos, we need it later
+ nModelPos = nRealPos;
+ // the position the base class gets is the view pos + 1 (because of the handle column)
+ nRealPos = nViewPos + 1;
+ }
+
+ // calculate the new id
+ for (nId=1; (GetModelColumnPos(nId) != GRID_COLUMN_NOT_FOUND) && size_t(nId) <= m_aColumns.size(); ++nId)
+ ;
+ DBG_ASSERT(GetViewColumnPos(nId) == GRID_COLUMN_NOT_FOUND, "DbGridControl::AppendColumn : inconsistent internal state !");
+ // my column's models say "there is no column with id nId", but the view (the base class) says "there is a column ..."
+
+ EditBrowseBox::AppendColumn(rName, nWidth, nRealPos, nId);
+ if (nModelPos == HEADERBAR_APPEND)
+ m_aColumns.push_back( CreateColumn(nId) );
+ else
+ m_aColumns.insert( m_aColumns.begin() + nModelPos, CreateColumn(nId) );
+
+ return nId;
+}
+
+void DbGridControl::RemoveColumn(sal_uInt16 nId)
+{
+ EditBrowseBox::RemoveColumn(nId);
+
+ const sal_uInt16 nIndex = GetModelColumnPos(nId);
+ if(nIndex != GRID_COLUMN_NOT_FOUND)
+ {
+ m_aColumns.erase( m_aColumns.begin()+nIndex );
+ }
+}
+
+void DbGridControl::ColumnMoved(sal_uInt16 nId)
+{
+ EditBrowseBox::ColumnMoved(nId);
+
+ // remove the col from the model
+ sal_uInt16 nOldModelPos = GetModelColumnPos(nId);
+#ifdef DBG_UTIL
+ DbGridColumn* pCol = m_aColumns[ nOldModelPos ].get();
+ DBG_ASSERT(!pCol->IsHidden(), "DbGridControl::ColumnMoved : moved a hidden col ? how this ?");
+#endif
+
+ // for the new model pos we can't use GetModelColumnPos because we are altering the model at the moment
+ // so the method won't work (in fact it would return the old model pos)
+
+ // the new view pos is calculated easily
+ sal_uInt16 nNewViewPos = GetViewColumnPos(nId);
+
+ // from that we can compute the new model pos
+ size_t nNewModelPos;
+ for (nNewModelPos = 0; nNewModelPos < m_aColumns.size(); ++nNewModelPos)
+ {
+ if (!m_aColumns[ nNewModelPos ]->IsHidden())
+ {
+ if (!nNewViewPos)
+ break;
+ else
+ --nNewViewPos;
+ }
+ }
+ DBG_ASSERT( nNewModelPos < m_aColumns.size(), "DbGridControl::ColumnMoved : could not find the new model position !");
+
+ // this will work. of course the model isn't fully consistent with our view right now, but let's
+ // look at the situation : a column has been moved with in the VIEW from pos m to n, say m<n (in the
+ // other case we can use analogue arguments).
+ // All cols k with m<k<=n have been shifted left on pos, the former col m now has pos n.
+ // In the model this affects a range of cols x to y, where x<=m and y<=n. And the number of hidden cols
+ // within this range is constant, so we may calculate the view pos from the model pos in the above way.
+
+ // for instance, let's look at a grid with six columns where the third one is hidden. this will
+ // initially look like this :
+
+ // +---+---+---+---+---+---+
+ // model pos | 0 | 1 |*2*| 3 | 4 | 5 |
+ // +---+---+---+---+---+---+
+ // ID | 1 | 2 | 3 | 4 | 5 | 6 |
+ // +---+---+---+---+---+---+
+ // view pos | 0 | 1 | - | 2 | 3 | 4 |
+ // +---+---+---+---+---+---+
+
+ // if we move the column at (view) pos 1 to (view) pos 3 we have :
+
+ // +---+---+---+---+---+---+
+ // model pos | 0 | 3 |*2*| 4 | 1 | 5 | // not reflecting the changes, yet
+ // +---+---+---+---+---+---+
+ // ID | 1 | 4 | 3 | 5 | 2 | 6 | // already reflecting the changes
+ // +---+---+---+---+---+---+
+ // view pos | 0 | 1 | - | 2 | 3 | 4 |
+ // +---+---+---+---+---+---+
+
+ // or, sorted by the out-of-date model positions :
+
+ // +---+---+---+---+---+---+
+ // model pos | 0 | 1 |*2*| 3 | 4 | 5 |
+ // +---+---+---+---+---+---+
+ // ID | 1 | 2 | 3 | 4 | 5 | 6 |
+ // +---+---+---+---+---+---+
+ // view pos | 0 | 3 | - | 1 | 2 | 4 |
+ // +---+---+---+---+---+---+
+
+ // We know the new view pos (3) of the moved column because our base class tells us. So we look at our
+ // model for the 4th (the pos is zero-based) visible column, it is at (model) position 4. And this is
+ // exactly the pos where we have to re-insert our column's model, so it looks ike this :
+
+ // +---+---+---+---+---+---+
+ // model pos | 0 |*1*| 2 | 3 | 4 | 5 |
+ // +---+---+---+---+---+---+
+ // ID | 1 | 3 | 4 | 5 | 2 | 6 |
+ // +---+---+---+---+---+---+
+ // view pos | 0 | - | 1 | 2 | 3 | 4 |
+ // +---+---+---+---+---+---+
+
+ // Now, all is consistent again.
+ // (except of the hidden column : The cycling of the cols occurred on the model, not on the view. maybe
+ // the user expected the latter but there really is no good argument against our method ;) ...)
+
+ // And no, this large explanation isn't just because I wanted to play a board game or something like
+ // that. It's because it took me a while to see it myself, and the whole theme (hidden cols, model col
+ // positions, view col positions) is really painful (at least for me) so the above pictures helped me a lot ;)
+
+ auto temp = std::move(m_aColumns[ nOldModelPos ]);
+ m_aColumns.erase( m_aColumns.begin() + nOldModelPos );
+ m_aColumns.insert( m_aColumns.begin() + nNewModelPos, std::move(temp) );
+}
+
+bool DbGridControl::SeekRow(long nRow)
+{
+ // in filter mode or in insert only mode we don't have any cursor!
+ if ( !SeekCursor( nRow ) )
+ return false;
+
+ if ( IsFilterMode() )
+ {
+ DBG_ASSERT( IsFilterRow( nRow ), "DbGridControl::SeekRow(): No filter row, wrong mode" );
+ m_xPaintRow = m_xEmptyRow;
+ }
+ else
+ {
+ // on the current position we have to take the current row for display as we want
+ // to have the most recent values for display
+ if ( ( nRow == m_nCurrentPos ) && getDisplaySynchron() )
+ m_xPaintRow = m_xCurrentRow;
+ // seek to the empty insert row
+ else if ( IsInsertionRow( nRow ) )
+ m_xPaintRow = m_xEmptyRow;
+ else
+ {
+ m_xSeekRow->SetState( m_pSeekCursor.get(), true );
+ m_xPaintRow = m_xSeekRow;
+ }
+ }
+
+ EditBrowseBox::SeekRow(nRow);
+
+ return m_nSeekPos >= 0;
+}
+
+// Is called whenever the visible amount of data changes
+void DbGridControl::VisibleRowsChanged( long nNewTopRow, sal_uInt16 nLinesOnScreen )
+{
+ RecalcRows(nNewTopRow, nLinesOnScreen, false);
+}
+
+void DbGridControl::RecalcRows(long nNewTopRow, sal_uInt16 nLinesOnScreen, bool bUpdateCursor)
+{
+ // If no cursor -> no rows in the browser.
+ if (!m_pSeekCursor)
+ {
+ DBG_ASSERT(GetRowCount() == 0,"DbGridControl: without cursor no rows are allowed to be there");
+ return;
+ }
+
+ // ignore any implicitly made updates
+ bool bDisablePaint = !bUpdateCursor && IsPaintEnabled();
+ if (bDisablePaint)
+ EnablePaint(false);
+
+ // adjust cache to the visible area
+ Reference< XPropertySet > xSet = m_pSeekCursor->getPropertySet();
+ sal_Int32 nCacheSize = 0;
+ xSet->getPropertyValue(FM_PROP_FETCHSIZE) >>= nCacheSize;
+ bool bCacheAligned = false;
+ // no further cursor movements after initializing (m_nSeekPos < 0) because it is already
+ // positioned on the first sentence
+ long nDelta = nNewTopRow - GetTopRow();
+ // limit for relative positioning
+ long nLimit = nCacheSize ? nCacheSize / 2 : 0;
+
+ // more lines on screen than in cache
+ if (nLimit < nLinesOnScreen)
+ {
+ Any aCacheSize;
+ aCacheSize <<= sal_Int32(nLinesOnScreen*2);
+ xSet->setPropertyValue(FM_PROP_FETCHSIZE, aCacheSize);
+ // here we need to update the cursor for sure
+ bUpdateCursor = true;
+ bCacheAligned = true;
+ nLimit = nLinesOnScreen;
+ }
+
+ // In the following, all positionings are done as it is
+ // ensured that there are enough lines in the data cache
+
+ // window goes downwards with less than two windows difference or
+ // the cache was updated and no rowcount yet
+ if (nDelta < nLimit && (nDelta > 0
+ || (bCacheAligned && m_nTotalCount < 0)) )
+ SeekCursor(nNewTopRow + nLinesOnScreen - 1);
+ else if (nDelta < 0 && std::abs(nDelta) < nLimit)
+ SeekCursor(nNewTopRow);
+ else if (nDelta != 0 || bUpdateCursor)
+ SeekCursor(nNewTopRow, true);
+
+ AdjustRows();
+
+ // ignore any updates implicit made
+ EnablePaint(true);
+}
+
+void DbGridControl::RowInserted(long nRow, long nNumRows, bool bDoPaint)
+{
+ if (nNumRows)
+ {
+ if (m_bRecordCountFinal && m_nTotalCount < 0)
+ {
+ // if we have an insert row we have to reduce to count by 1
+ // as the total count reflects only the existing rows in database
+ m_nTotalCount = GetRowCount() + nNumRows;
+ if (m_xEmptyRow.is())
+ --m_nTotalCount;
+ }
+ else if (m_nTotalCount >= 0)
+ m_nTotalCount += nNumRows;
+
+ EditBrowseBox::RowInserted(nRow, nNumRows, bDoPaint);
+ m_aBar->InvalidateState(DbGridControlNavigationBarState::Count);
+ }
+}
+
+void DbGridControl::RowRemoved(long nRow, long nNumRows, bool bDoPaint)
+{
+ if (nNumRows)
+ {
+ if (m_bRecordCountFinal && m_nTotalCount < 0)
+ {
+ m_nTotalCount = GetRowCount() - nNumRows;
+ // if we have an insert row reduce by 1
+ if (m_xEmptyRow.is())
+ --m_nTotalCount;
+ }
+ else if (m_nTotalCount >= 0)
+ m_nTotalCount -= nNumRows;
+
+ EditBrowseBox::RowRemoved(nRow, nNumRows, bDoPaint);
+ m_aBar->InvalidateState(DbGridControlNavigationBarState::Count);
+ }
+}
+
+void DbGridControl::AdjustRows()
+{
+ if (!m_pSeekCursor)
+ return;
+
+ Reference< XPropertySet > xSet = m_pDataCursor->getPropertySet();
+
+ // refresh RecordCount
+ sal_Int32 nRecordCount = 0;
+ xSet->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount;
+ if (!m_bRecordCountFinal)
+ m_bRecordCountFinal = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ROWCOUNTFINAL));
+
+ // Did the number of rows change?
+ // Here we need to consider that there might be an additional row for adding new data sets
+
+ // add additional AppendRow for insertion
+ if (m_nOptions & DbGridControlOptions::Insert)
+ ++nRecordCount;
+
+ // If there is currently an insertion, so do not consider this added row in RecordCount or Appendrow
+ if (!IsUpdating() && m_bRecordCountFinal && IsModified() && m_xCurrentRow != m_xEmptyRow &&
+ m_xCurrentRow->IsNew())
+ ++nRecordCount;
+ // ensured with !m_bUpdating: otherwise the edited data set (that SaveRow added and why this
+ // method was called) would be called twice (if m_bUpdating == sal_True): once in RecordCount
+ // and a second time here (60787 - FS)
+
+ if (nRecordCount != GetRowCount())
+ {
+ long nDelta = GetRowCount() - static_cast<long>(nRecordCount);
+ if (nDelta > 0) // too many
+ {
+ RowRemoved(GetRowCount() - nDelta, nDelta, false);
+ // some rows are gone, thus, repaint starting at the current position
+ Invalidate();
+
+ sal_Int32 nNewPos = AlignSeekCursor();
+ if (m_bSynchDisplay)
+ EditBrowseBox::GoToRow(nNewPos);
+
+ SetCurrent(nNewPos);
+ // there are rows so go to the selected current column
+ if (nRecordCount)
+ GoToRowColumnId(nNewPos, GetColumnId(GetCurColumnId()));
+ if (!IsResizing() && GetRowCount())
+ RecalcRows(GetTopRow(), GetVisibleRows(), true);
+ m_aBar->InvalidateAll(m_nCurrentPos, true);
+ }
+ else // too few
+ RowInserted(GetRowCount(), -nDelta);
+ }
+
+ if (m_bRecordCountFinal && m_nTotalCount < 0)
+ {
+ if (m_nOptions & DbGridControlOptions::Insert)
+ m_nTotalCount = GetRowCount() - 1;
+ else
+ m_nTotalCount = GetRowCount();
+ }
+ m_aBar->InvalidateState(DbGridControlNavigationBarState::Count);
+}
+
+svt::EditBrowseBox::RowStatus DbGridControl::GetRowStatus(long nRow) const
+{
+ if (IsFilterRow(nRow))
+ return EditBrowseBox::FILTER;
+ else if (m_nCurrentPos >= 0 && nRow == m_nCurrentPos)
+ {
+ // new row
+ if (!IsValid(m_xCurrentRow))
+ return EditBrowseBox::DELETED;
+ else if (IsModified())
+ return EditBrowseBox::MODIFIED;
+ else if (m_xCurrentRow->IsNew())
+ return EditBrowseBox::CURRENTNEW;
+ else
+ return EditBrowseBox::CURRENT;
+ }
+ else if (IsInsertionRow(nRow))
+ return EditBrowseBox::NEW;
+ else if (!IsValid(m_xSeekRow))
+ return EditBrowseBox::DELETED;
+ else
+ return EditBrowseBox::CLEAN;
+}
+
+void DbGridControl::PaintCell(OutputDevice& rDev, const tools::Rectangle& rRect, sal_uInt16 nColumnId) const
+{
+ if (!IsValid(m_xPaintRow))
+ return;
+
+ size_t Location = GetModelColumnPos(nColumnId);
+ DbGridColumn* pColumn = (Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
+ if (pColumn)
+ {
+ tools::Rectangle aArea(rRect);
+ if ((GetMode() & BrowserMode::CURSOR_WO_FOCUS) == BrowserMode::CURSOR_WO_FOCUS)
+ {
+ aArea.AdjustTop(1 );
+ aArea.AdjustBottom( -1 );
+ }
+ pColumn->Paint(rDev, aArea, m_xPaintRow.get(), getNumberFormatter());
+ }
+}
+
+bool DbGridControl::CursorMoving(long nNewRow, sal_uInt16 nNewCol)
+{
+
+ DeactivateCell( false );
+
+ if ( m_pDataCursor
+ && ( m_nCurrentPos != nNewRow )
+ && !SetCurrent( nNewRow )
+ )
+ {
+ ActivateCell();
+ return false;
+ }
+
+ return EditBrowseBox::CursorMoving( nNewRow, nNewCol );
+}
+
+bool DbGridControl::SetCurrent(long nNewRow)
+{
+ // Each movement of the datacursor must start with BeginCursorAction and end with
+ // EndCursorAction to block all notifications during the movement
+ BeginCursorAction();
+
+ try
+ {
+ // compare positions
+ if (SeekCursor(nNewRow))
+ {
+ if (IsFilterRow(nNewRow)) // special mode for filtering
+ {
+ m_xCurrentRow = m_xDataRow = m_xPaintRow = m_xEmptyRow;
+ m_nCurrentPos = nNewRow;
+ }
+ else
+ {
+ bool bNewRowInserted = false;
+ // Should we go to the insertrow ?
+ if (IsInsertionRow(nNewRow))
+ {
+ // to we need to move the cursor to the insert row?
+ // we need to insert the if the current row isn't the insert row or if the
+ // cursor triggered the move by itself and we need a reinitialization of the row
+ Reference< XPropertySet > xCursorProps = m_pDataCursor->getPropertySet();
+ if ( !::comphelper::getBOOL(xCursorProps->getPropertyValue(FM_PROP_ISNEW)) )
+ {
+ Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*m_pDataCursor), UNO_QUERY);
+ xUpdateCursor->moveToInsertRow();
+ }
+ bNewRowInserted = true;
+ }
+ else
+ {
+
+ if ( !m_pSeekCursor->isBeforeFirst() && !m_pSeekCursor->isAfterLast() )
+ {
+ Any aBookmark = m_pSeekCursor->getBookmark();
+ if (!m_xCurrentRow.is() || m_xCurrentRow->IsNew() || !CompareBookmark(aBookmark, m_pDataCursor->getBookmark()))
+ {
+ // adjust the cursor to the new desired row
+ if (!m_pDataCursor->moveToBookmark(aBookmark))
+ {
+ EndCursorAction();
+ return false;
+ }
+ }
+ }
+ }
+ m_xDataRow->SetState(m_pDataCursor.get(), false);
+ m_xCurrentRow = m_xDataRow;
+
+ long nPaintPos = -1;
+ // do we have to repaint the last regular row in case of setting defaults or autovalues
+ if (m_nCurrentPos >= 0 && m_nCurrentPos >= (GetRowCount() - 2))
+ nPaintPos = m_nCurrentPos;
+
+ m_nCurrentPos = nNewRow;
+
+ // repaint the new row to display all defaults
+ if (bNewRowInserted)
+ RowModified(m_nCurrentPos);
+ if (nPaintPos >= 0)
+ RowModified(nPaintPos);
+ }
+ }
+ else
+ {
+ OSL_FAIL("DbGridControl::SetCurrent : SeekRow failed !");
+ EndCursorAction();
+ return false;
+ }
+ }
+ catch ( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ EndCursorAction();
+ return false;
+ }
+
+ EndCursorAction();
+ return true;
+}
+
+void DbGridControl::CursorMoved()
+{
+
+ // cursor movement due to deletion or insertion of rows
+ if (m_pDataCursor && m_nCurrentPos != GetCurRow())
+ {
+ DeactivateCell();
+ SetCurrent(GetCurRow());
+ }
+
+ EditBrowseBox::CursorMoved();
+ m_aBar->InvalidateAll(m_nCurrentPos);
+
+ // select the new column when they moved
+ if ( IsDesignMode() && GetSelectedColumnCount() > 0 && GetCurColumnId() )
+ {
+ SelectColumnId( GetCurColumnId() );
+ }
+
+ if ( m_nLastColId != GetCurColumnId() )
+ onColumnChange();
+ m_nLastColId = GetCurColumnId();
+
+ if ( m_nLastRowId != GetCurRow() )
+ onRowChange();
+ m_nLastRowId = GetCurRow();
+}
+
+void DbGridControl::onRowChange()
+{
+ // not interested in
+}
+
+void DbGridControl::onColumnChange()
+{
+ if ( m_pGridListener )
+ m_pGridListener->columnChanged();
+}
+
+void DbGridControl::setDisplaySynchron(bool bSync)
+{
+ if (bSync != m_bSynchDisplay)
+ {
+ m_bSynchDisplay = bSync;
+ if (m_bSynchDisplay)
+ AdjustDataSource();
+ }
+}
+
+void DbGridControl::AdjustDataSource(bool bFull)
+{
+ SAL_INFO("svx.fmcomp", "DbGridControl::AdjustDataSource");
+ SolarMutexGuard aGuard;
+ // If the current row is recalculated at the moment, do not adjust
+
+ if (bFull)
+ m_xCurrentRow = nullptr;
+ // if we are on the same row only repaint
+ // but this is only possible for rows which are not inserted, in that case the comparison result
+ // may not be correct
+ else
+ if ( m_xCurrentRow.is()
+ && !m_xCurrentRow->IsNew()
+ && !m_pDataCursor->isBeforeFirst()
+ && !m_pDataCursor->isAfterLast()
+ && !m_pDataCursor->rowDeleted()
+ )
+ {
+ bool bEqualBookmarks = CompareBookmark( m_xCurrentRow->GetBookmark(), m_pDataCursor->getBookmark() );
+
+ bool bDataCursorIsOnNew = false;
+ m_pDataCursor->getPropertySet()->getPropertyValue( FM_PROP_ISNEW ) >>= bDataCursorIsOnNew;
+
+ if ( bEqualBookmarks && !bDataCursorIsOnNew )
+ {
+ // position of my data cursor is the same as the position our current row points tpo
+ // sync the status, repaint, done
+ DBG_ASSERT(m_xDataRow == m_xCurrentRow, "Errors in the data row");
+ SAL_INFO("svx.fmcomp", "same position, new state: " << ROWSTATUS(m_xCurrentRow));
+ RowModified(m_nCurrentPos);
+ return;
+ }
+ }
+
+ // away from the data cursor's row
+ if (m_xPaintRow == m_xCurrentRow)
+ m_xPaintRow = m_xSeekRow;
+
+ // not up-to-date row, thus, adjust completely
+ if (!m_xCurrentRow.is())
+ AdjustRows();
+
+ sal_Int32 nNewPos = AlignSeekCursor();
+ if (nNewPos < 0)// could not find any position
+ return;
+
+ if (nNewPos != m_nCurrentPos)
+ {
+ if (m_bSynchDisplay)
+ EditBrowseBox::GoToRow(nNewPos);
+
+ if (!m_xCurrentRow.is())
+ // Happens e.g. when deleting the n last datasets (n>1) while the cursor was positioned
+ // on the last one. In this case, AdjustRows deletes two rows from BrowseBox, by what
+ // CurrentRow is corrected to point two rows down, so that GoToRow will point into
+ // emptiness (since we are - purportedly - at the correct position)
+ SetCurrent(nNewPos);
+ }
+ else
+ {
+ SetCurrent(nNewPos);
+ RowModified(nNewPos);
+ }
+
+ // if the data cursor was moved from outside, this section is voided
+ SetNoSelection();
+ m_aBar->InvalidateAll(m_nCurrentPos, m_xCurrentRow.is());
+}
+
+sal_Int32 DbGridControl::AlignSeekCursor()
+{
+ // position SeekCursor onto the data cursor, no data transmission
+
+ if (!m_pSeekCursor)
+ return -1;
+
+ Reference< XPropertySet > xSet = m_pDataCursor->getPropertySet();
+
+ // now align the seek cursor and the data cursor
+ if (::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW)))
+ m_nSeekPos = GetRowCount() - 1;
+ else
+ {
+ try
+ {
+ if ( m_pDataCursor->isBeforeFirst() )
+ {
+ // this is somewhat strange, but can nevertheless happen
+ SAL_INFO( "svx.fmcomp", "DbGridControl::AlignSeekCursor: nobody should tamper with my cursor this way (before first)!" );
+ m_pSeekCursor->first();
+ m_pSeekCursor->previous();
+ m_nSeekPos = -1;
+ }
+ else if ( m_pDataCursor->isAfterLast() )
+ {
+ SAL_INFO( "svx.fmcomp", "DbGridControl::AlignSeekCursor: nobody should tamper with my cursor this way (after last)!" );
+ m_pSeekCursor->last();
+ m_pSeekCursor->next();
+ m_nSeekPos = -1;
+ }
+ else
+ {
+ m_pSeekCursor->moveToBookmark(m_pDataCursor->getBookmark());
+ if (!CompareBookmark(m_pDataCursor->getBookmark(), m_pSeekCursor->getBookmark()))
+ // unfortunately, moveToBookmark might lead to a re-positioning of the seek
+ // cursor (if the complex moveToBookmark with all its events fires an update
+ // somewhere) -> retry
+ m_pSeekCursor->moveToBookmark(m_pDataCursor->getBookmark());
+ // Now there is still the chance of a failure but it is less likely.
+ // The alternative would be a loop until everything is fine - no good solution...
+ m_nSeekPos = m_pSeekCursor->getRow() - 1;
+ }
+ }
+ catch(Exception&)
+ {
+ }
+ }
+ return m_nSeekPos;
+}
+
+bool DbGridControl::SeekCursor(long nRow, bool bAbsolute)
+{
+ // position SeekCursor onto the data cursor, no data transmission
+
+ // additions for the filtermode
+ if (IsFilterRow(nRow))
+ {
+ m_nSeekPos = 0;
+ return true;
+ }
+
+ if (!m_pSeekCursor)
+ return false;
+
+ // is this an insertion?
+ if (IsValid(m_xCurrentRow) && m_xCurrentRow->IsNew() &&
+ nRow >= m_nCurrentPos)
+ {
+ // if so, scrolling down must be prevented as this is already the last data set!
+ if (nRow == m_nCurrentPos)
+ {
+ // no adjustment necessary
+ m_nSeekPos = nRow;
+ }
+ else if (IsInsertionRow(nRow)) // blank row for data insertion
+ m_nSeekPos = nRow;
+ }
+ else if (IsInsertionRow(nRow)) // blank row for data insertion
+ m_nSeekPos = nRow;
+ else if ((-1 == nRow) && (GetRowCount() == ((m_nOptions & DbGridControlOptions::Insert) ? 1 : 0)) && m_pSeekCursor->isAfterLast())
+ m_nSeekPos = nRow;
+ else
+ {
+ bool bSuccess = false;
+ long nSteps = 0;
+ try
+ {
+ if ( m_pSeekCursor->rowDeleted() )
+ {
+ // somebody deleted the current row of the seek cursor. Move it away from this row.
+ m_pSeekCursor->next();
+ if ( m_pSeekCursor->isAfterLast() || m_pSeekCursor->isBeforeFirst() )
+ bAbsolute = true;
+ }
+
+ if ( !bAbsolute )
+ {
+ DBG_ASSERT( !m_pSeekCursor->isAfterLast() && !m_pSeekCursor->isBeforeFirst(),
+ "DbGridControl::SeekCursor: how did the seek cursor get to this position?!" );
+ nSteps = nRow - (m_pSeekCursor->getRow() - 1);
+ bAbsolute = std::abs(nSteps) > 100;
+ }
+
+ if ( bAbsolute )
+ {
+ bSuccess = m_pSeekCursor->absolute(nRow + 1);
+ if (bSuccess)
+ m_nSeekPos = nRow;
+ }
+ else
+ {
+ if (nSteps > 0) // position onto the last needed data set
+ {
+ if (m_pSeekCursor->isAfterLast())
+ bSuccess = false;
+ else if (m_pSeekCursor->isBeforeFirst())
+ bSuccess = m_pSeekCursor->absolute(nSteps);
+ else
+ bSuccess = m_pSeekCursor->relative(nSteps);
+ }
+ else if (nSteps < 0)
+ {
+ if (m_pSeekCursor->isBeforeFirst())
+ bSuccess = false;
+ else if (m_pSeekCursor->isAfterLast())
+ bSuccess = m_pSeekCursor->absolute(nSteps);
+ else
+ bSuccess = m_pSeekCursor->relative(nSteps);
+ }
+ else
+ {
+ m_nSeekPos = nRow;
+ return true;
+ }
+ }
+ }
+ catch(Exception&)
+ {
+ OSL_FAIL("DbGridControl::SeekCursor : failed ...");
+ }
+
+ try
+ {
+ if (!bSuccess)
+ {
+ if (bAbsolute || nSteps > 0)
+ {
+ if (m_pSeekCursor->isLast())
+ bSuccess = true;
+ else
+ bSuccess = m_pSeekCursor->last();
+ }
+ else
+ {
+ if (m_pSeekCursor->isFirst())
+ bSuccess = true;
+ else
+ bSuccess = m_pSeekCursor->first();
+ }
+ }
+
+ if (bSuccess)
+ m_nSeekPos = m_pSeekCursor->getRow() - 1;
+ else
+ m_nSeekPos = -1;
+ }
+ catch(Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ OSL_FAIL("DbGridControl::SeekCursor : failed ...");
+ m_nSeekPos = -1; // no further data set available
+ }
+ }
+ return m_nSeekPos == nRow;
+}
+
+void DbGridControl::MoveToFirst()
+{
+ if (m_pSeekCursor && (GetCurRow() != 0))
+ MoveToPosition(0);
+}
+
+void DbGridControl::MoveToLast()
+{
+ if (!m_pSeekCursor)
+ return;
+
+ if (m_nTotalCount < 0) // no RecordCount, yet
+ {
+ try
+ {
+ bool bRes = m_pSeekCursor->last();
+
+ if (bRes)
+ {
+ m_nSeekPos = m_pSeekCursor->getRow() - 1;
+ AdjustRows();
+ }
+ }
+ catch(Exception&)
+ {
+ }
+ }
+
+ // position onto the last data set not on a blank row
+ if (m_nOptions & DbGridControlOptions::Insert)
+ {
+ if ((GetRowCount() - 1) > 0)
+ MoveToPosition(GetRowCount() - 2);
+ }
+ else if (GetRowCount())
+ MoveToPosition(GetRowCount() - 1);
+}
+
+void DbGridControl::MoveToPrev()
+{
+ long nNewRow = std::max(GetCurRow() - 1, 0L);
+ if (GetCurRow() != nNewRow)
+ MoveToPosition(nNewRow);
+}
+
+void DbGridControl::MoveToNext()
+{
+ if (!m_pSeekCursor)
+ return;
+
+ if (m_nTotalCount > 0)
+ {
+ // move the data cursor to the right position
+ long nNewRow = std::min(GetRowCount() - 1, GetCurRow() + 1);
+ if (GetCurRow() != nNewRow)
+ MoveToPosition(nNewRow);
+ }
+ else
+ {
+ bool bOk = false;
+ try
+ {
+ // try to move to next row
+ // when not possible our paint cursor is already on the last row
+ // then we must be sure that the data cursor is on the position
+ // we call ourself again
+ bOk = m_pSeekCursor->next();
+ if (bOk)
+ {
+ m_nSeekPos = m_pSeekCursor->getRow() - 1;
+ MoveToPosition(GetCurRow() + 1);
+ }
+ }
+ catch(SQLException &)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ if(!bOk)
+ {
+ AdjustRows();
+ if (m_nTotalCount > 0) // only to avoid infinite recursion
+ MoveToNext();
+ }
+ }
+}
+
+void DbGridControl::MoveToPosition(sal_uInt32 nPos)
+{
+ if (!m_pSeekCursor)
+ return;
+
+ if (m_nTotalCount < 0 && static_cast<long>(nPos) >= GetRowCount())
+ {
+ try
+ {
+ if (!m_pSeekCursor->absolute(nPos + 1))
+ {
+ AdjustRows();
+ return;
+ }
+ else
+ {
+ m_nSeekPos = m_pSeekCursor->getRow() - 1;
+ AdjustRows();
+ }
+ }
+ catch(Exception&)
+ {
+ return;
+ }
+ }
+ EditBrowseBox::GoToRow(nPos);
+ m_aBar->InvalidateAll(m_nCurrentPos);
+}
+
+void DbGridControl::AppendNew()
+{
+ if (!m_pSeekCursor || !(m_nOptions & DbGridControlOptions::Insert))
+ return;
+
+ if (m_nTotalCount < 0) // no RecordCount, yet
+ {
+ try
+ {
+ bool bRes = m_pSeekCursor->last();
+
+ if (bRes)
+ {
+ m_nSeekPos = m_pSeekCursor->getRow() - 1;
+ AdjustRows();
+ }
+ }
+ catch(Exception&)
+ {
+ return;
+ }
+ }
+
+ long nNewRow = m_nTotalCount + 1;
+ if (nNewRow > 0 && GetCurRow() != nNewRow)
+ MoveToPosition(nNewRow - 1);
+}
+
+void DbGridControl::SetDesignMode(bool bMode)
+{
+ if (IsDesignMode() != bMode)
+ {
+ // adjust Enable/Disable for design mode so that the headerbar remains configurable
+ if (bMode)
+ {
+ if (!IsEnabled())
+ {
+ Enable();
+ GetDataWindow().Disable();
+ }
+ }
+ else
+ {
+ // disable completely
+ if (!GetDataWindow().IsEnabled())
+ Disable();
+ }
+
+ m_bDesignMode = bMode;
+ GetDataWindow().SetMouseTransparent(bMode);
+ SetMouseTransparent(bMode);
+
+ m_aBar->InvalidateAll(m_nCurrentPos, true);
+ }
+}
+
+void DbGridControl::SetFilterMode(bool bMode)
+{
+ if (IsFilterMode() != bMode)
+ {
+ m_bFilterMode = bMode;
+
+ if (bMode)
+ {
+ SetUpdateMode(false);
+
+ // there is no cursor anymore
+ if (IsEditing())
+ DeactivateCell();
+ RemoveRows(false);
+
+ m_xEmptyRow = new DbGridRow();
+
+ // setting the new filter controls
+ for (auto const & pCurCol : m_aColumns)
+ {
+ if (!pCurCol->IsHidden())
+ pCurCol->UpdateControl();
+ }
+
+ // one row for filtering
+ RowInserted(0);
+ SetUpdateMode(true);
+ }
+ else
+ setDataSource(Reference< XRowSet > ());
+ }
+}
+
+OUString DbGridControl::GetCellText(long _nRow, sal_uInt16 _nColId) const
+{
+ size_t Location = GetModelColumnPos( _nColId );
+ DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
+ OUString sRet;
+ if ( const_cast<DbGridControl*>(this)->SeekRow(_nRow) )
+ sRet = GetCurrentRowCellText(pColumn, m_xPaintRow);
+ return sRet;
+}
+
+OUString DbGridControl::GetCurrentRowCellText(DbGridColumn const * pColumn,const DbGridRowRef& _rRow) const
+{
+ // text output for a single row
+ OUString aText;
+ if ( pColumn && IsValid(_rRow) )
+ aText = pColumn->GetCellText(_rRow.get(), m_xFormatter);
+ return aText;
+}
+
+sal_uInt32 DbGridControl::GetTotalCellWidth(long nRow, sal_uInt16 nColId)
+{
+ if (SeekRow(nRow))
+ {
+ size_t Location = GetModelColumnPos( nColId );
+ DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
+ return GetDataWindow().GetTextWidth(GetCurrentRowCellText(pColumn,m_xPaintRow));
+ }
+ else
+ return 30; // FIXME magic number for default cell width
+}
+
+void DbGridControl::PreExecuteRowContextMenu(sal_uInt16 /*nRow*/, PopupMenu& rMenu)
+{
+ bool bDelete = (m_nOptions & DbGridControlOptions::Delete) && GetSelectRowCount() && !IsCurrentAppending();
+ // if only a blank row is selected then do not delete
+ bDelete = bDelete && !((m_nOptions & DbGridControlOptions::Insert) && GetSelectRowCount() == 1 && IsRowSelected(GetRowCount() - 1));
+
+ rMenu.EnableItem(rMenu.GetItemId("delete"), bDelete);
+ rMenu.EnableItem(rMenu.GetItemId("save"), IsModified());
+
+ // the undo is more difficult
+ bool bCanUndo = IsModified();
+ int nState = -1;
+ if (m_aMasterStateProvider.IsSet())
+ nState = m_aMasterStateProvider.Call(DbGridControlNavigationBarState::Undo);
+ bCanUndo &= ( 0 != nState );
+
+ rMenu.EnableItem(rMenu.GetItemId("undo"), bCanUndo);
+}
+
+void DbGridControl::PostExecuteRowContextMenu(sal_uInt16 /*nRow*/, const PopupMenu& rMenu, sal_uInt16 nExecutionResult)
+{
+ if (nExecutionResult == rMenu.GetItemId("delete"))
+ {
+ // delete asynchronously
+ if (m_nDeleteEvent)
+ Application::RemoveUserEvent(m_nDeleteEvent);
+ m_nDeleteEvent = Application::PostUserEvent(LINK(this,DbGridControl,OnDelete), nullptr, true);
+ }
+ else if (nExecutionResult == rMenu.GetItemId("undo"))
+ Undo();
+ else if (nExecutionResult == rMenu.GetItemId("save"))
+ SaveRow();
+}
+
+void DbGridControl::DataSourcePropertyChanged(const PropertyChangeEvent& evt)
+{
+ SAL_INFO("svx.fmcomp", "DbGridControl::DataSourcePropertyChanged");
+ SolarMutexGuard aGuard;
+ // prop "IsModified" changed ?
+ // during update don't care about the modified state
+ if (!IsUpdating() && evt.PropertyName == FM_PROP_ISMODIFIED )
+ {
+ Reference< XPropertySet > xSource(evt.Source, UNO_QUERY);
+ DBG_ASSERT( xSource.is(), "DbGridControl::DataSourcePropertyChanged: invalid event source!" );
+ bool bIsNew = false;
+ if (xSource.is())
+ bIsNew = ::comphelper::getBOOL(xSource->getPropertyValue(FM_PROP_ISNEW));
+
+ if (bIsNew && m_xCurrentRow.is())
+ {
+ DBG_ASSERT(::comphelper::getBOOL(xSource->getPropertyValue(FM_PROP_ROWCOUNTFINAL)), "DbGridControl::DataSourcePropertyChanged : somebody moved the form to a new record before the row count was final !");
+ sal_Int32 nRecordCount = 0;
+ xSource->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount;
+ if (::comphelper::getBOOL(evt.NewValue))
+ { // modified state changed from sal_False to sal_True and we're on an insert row
+ // -> we've to add a new grid row
+ if ((nRecordCount == GetRowCount() - 1) && m_xCurrentRow->IsNew())
+ {
+ RowInserted(GetRowCount());
+ InvalidateStatusCell(m_nCurrentPos);
+ m_aBar->InvalidateAll(m_nCurrentPos);
+ }
+ }
+ else
+ { // modified state changed from sal_True to sal_False and we're on an insert row
+ // we have two "new row"s at the moment : the one we're editing currently (where the current
+ // column is the only dirty element) and a "new new" row which is completely clean. As the first
+ // one is about to be cleaned, too, the second one is obsolete now.
+ if (m_xCurrentRow->IsNew() && nRecordCount == (GetRowCount() - 2))
+ {
+ RowRemoved(GetRowCount() - 1);
+ InvalidateStatusCell(m_nCurrentPos);
+ m_aBar->InvalidateAll(m_nCurrentPos);
+ }
+ }
+ }
+ if (m_xCurrentRow.is())
+ {
+ m_xCurrentRow->SetStatus(::comphelper::getBOOL(evt.NewValue) ? GridRowStatus::Modified : GridRowStatus::Clean);
+ m_xCurrentRow->SetNew( bIsNew );
+ InvalidateStatusCell(m_nCurrentPos);
+ SAL_INFO("svx.fmcomp", "modified flag changed, new state: " << ROWSTATUS(m_xCurrentRow));
+ }
+ }
+}
+
+void DbGridControl::StartDrag( sal_Int8 /*nAction*/, const Point& rPosPixel )
+{
+ if (!m_pSeekCursor || IsResizing())
+ return;
+
+ sal_uInt16 nColId = GetColumnId(GetColumnAtXPosPixel(rPosPixel.X()));
+ long nRow = GetRowAtYPosPixel(rPosPixel.Y());
+ if (nColId != HandleColumnId && nRow >= 0)
+ {
+ if (GetDataWindow().IsMouseCaptured())
+ GetDataWindow().ReleaseMouse();
+
+ size_t Location = GetModelColumnPos( nColId );
+ DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
+ rtl::Reference<OStringTransferable> pTransferable = new OStringTransferable(GetCurrentRowCellText(pColumn,m_xPaintRow));
+ pTransferable->StartDrag(this, DND_ACTION_COPY);
+ }
+}
+
+bool DbGridControl::canCopyCellText(sal_Int32 _nRow, sal_uInt16 _nColId)
+{
+ return (_nRow >= 0)
+ && (_nRow < GetRowCount())
+ && (_nColId != HandleColumnId)
+ && (GetModelColumnPos(_nColId) != GRID_COLUMN_NOT_FOUND);
+}
+
+void DbGridControl::copyCellText(sal_Int32 _nRow, sal_uInt16 _nColId)
+{
+ DBG_ASSERT(canCopyCellText(_nRow, _nColId), "DbGridControl::copyCellText: invalid call!");
+ DbGridColumn* pColumn = m_aColumns[ GetModelColumnPos(_nColId) ].get();
+ SeekRow(_nRow);
+ OStringTransfer::CopyString( GetCurrentRowCellText( pColumn,m_xPaintRow ), this );
+}
+
+void DbGridControl::executeRowContextMenu( long _nRow, const Point& _rPreferredPos )
+{
+ VclBuilder aBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "svx/ui/rowsmenu.ui", "");
+ VclPtr<PopupMenu> aContextMenu(aBuilder.get_menu("menu"));
+
+ PreExecuteRowContextMenu( static_cast<sal_uInt16>(_nRow), *aContextMenu );
+ aContextMenu->RemoveDisabledEntries( true, true );
+ PostExecuteRowContextMenu( static_cast<sal_uInt16>(_nRow), *aContextMenu, aContextMenu->Execute( this, _rPreferredPos ) );
+}
+
+void DbGridControl::Command(const CommandEvent& rEvt)
+{
+ switch (rEvt.GetCommand())
+ {
+ case CommandEventId::ContextMenu:
+ {
+ if ( !m_pSeekCursor )
+ {
+ EditBrowseBox::Command(rEvt);
+ return;
+ }
+
+ if ( !rEvt.IsMouseEvent() )
+ { // context menu requested by keyboard
+ if ( GetSelectRowCount() )
+ {
+ long nRow = FirstSelectedRow( );
+
+ ::tools::Rectangle aRowRect( GetRowRectPixel( nRow ) );
+ executeRowContextMenu( nRow, aRowRect.LeftCenter() );
+
+ // handled
+ return;
+ }
+ }
+
+ sal_uInt16 nColId = GetColumnId(GetColumnAtXPosPixel(rEvt.GetMousePosPixel().X()));
+ long nRow = GetRowAtYPosPixel(rEvt.GetMousePosPixel().Y());
+
+ if (nColId == HandleColumnId)
+ {
+ executeRowContextMenu( nRow, rEvt.GetMousePosPixel() );
+ }
+ else if (canCopyCellText(nRow, nColId))
+ {
+ VclBuilder aBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "svx/ui/cellmenu.ui", "");
+ VclPtr<PopupMenu> aContextMenu(aBuilder.get_menu("menu"));
+ if (aContextMenu->Execute(this, rEvt.GetMousePosPixel()))
+ copyCellText(nRow, nColId);
+ }
+ else
+ {
+ EditBrowseBox::Command(rEvt);
+ return;
+ }
+
+ [[fallthrough]];
+ }
+ default:
+ EditBrowseBox::Command(rEvt);
+ }
+}
+
+IMPL_LINK_NOARG(DbGridControl, OnDelete, void*, void)
+{
+ m_nDeleteEvent = nullptr;
+ DeleteSelectedRows();
+}
+
+void DbGridControl::DeleteSelectedRows()
+{
+ DBG_ASSERT(GetSelection(), "no selection!!!");
+
+ if (!m_pSeekCursor)
+ return;
+}
+
+CellController* DbGridControl::GetController(long /*nRow*/, sal_uInt16 nColumnId)
+{
+ if (!IsValid(m_xCurrentRow) || !IsEnabled())
+ return nullptr;
+
+ size_t Location = GetModelColumnPos(nColumnId);
+ DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
+ if (!pColumn)
+ return nullptr;
+
+ CellController* pReturn = nullptr;
+ if (IsFilterMode())
+ pReturn = pColumn->GetController().get();
+ else
+ {
+ if (::comphelper::hasProperty(FM_PROP_ENABLED, pColumn->getModel()))
+ {
+ if (!::comphelper::getBOOL(pColumn->getModel()->getPropertyValue(FM_PROP_ENABLED)))
+ return nullptr;
+ }
+
+ bool bInsert = (m_xCurrentRow->IsNew() && (m_nOptions & DbGridControlOptions::Insert));
+ bool bUpdate = (!m_xCurrentRow->IsNew() && (m_nOptions & DbGridControlOptions::Update));
+
+ if ((bInsert && !pColumn->IsAutoValue()) || bUpdate)
+ {
+ pReturn = pColumn->GetController().get();
+ }
+ }
+ return pReturn;
+}
+
+void DbGridControl::CellModified()
+{
+ SAL_INFO("svx.fmcomp", "DbGridControl::CellModified");
+
+ {
+ ::osl::MutexGuard aGuard(m_aAdjustSafety);
+ if (m_nAsynAdjustEvent)
+ {
+ SAL_INFO("svx.fmcomp", "forcing a synchron call to " << (m_bPendingAdjustRows ? "AdjustRows" : "AdustDataSource"));
+ RemoveUserEvent(m_nAsynAdjustEvent);
+ m_nAsynAdjustEvent = nullptr;
+
+ // force the call : this should be no problem as we're probably running in the solar thread here
+ // (cell modified is triggered by user actions)
+ if (m_bPendingAdjustRows)
+ AdjustRows();
+ else
+ AdjustDataSource();
+ }
+ }
+
+ if (!IsFilterMode() && IsValid(m_xCurrentRow) && !m_xCurrentRow->IsModified())
+ {
+ // enable edit mode
+ // a data set should be inserted
+ if (m_xCurrentRow->IsNew())
+ {
+ m_xCurrentRow->SetStatus(GridRowStatus::Modified);
+ SAL_INFO("svx.fmcomp", "current row is new, new state: MODIFIED");
+ // if no row was added yet, do it now
+ if (m_nCurrentPos == GetRowCount() - 1)
+ {
+ // increment RowCount
+ RowInserted(GetRowCount());
+ InvalidateStatusCell(m_nCurrentPos);
+ m_aBar->InvalidateAll(m_nCurrentPos);
+ }
+ }
+ else if (m_xCurrentRow->GetStatus() != GridRowStatus::Modified)
+ {
+ m_xCurrentRow->SetState(m_pDataCursor.get(), false);
+ SAL_INFO("svx.fmcomp", "current row is not new, after SetState, new state: " << ROWSTATUS(m_xCurrentRow));
+ m_xCurrentRow->SetStatus(GridRowStatus::Modified);
+ SAL_INFO("svx.fmcomp", "current row is not new, new state: MODIFIED");
+ InvalidateStatusCell(m_nCurrentPos);
+ }
+ }
+}
+
+void DbGridControl::Dispatch(sal_uInt16 nId)
+{
+ if (nId == BROWSER_CURSORENDOFFILE)
+ {
+ if (m_nOptions & DbGridControlOptions::Insert)
+ AppendNew();
+ else
+ MoveToLast();
+ }
+ else
+ EditBrowseBox::Dispatch(nId);
+}
+
+void DbGridControl::Undo()
+{
+ if (IsFilterMode() || !IsValid(m_xCurrentRow) || !IsModified())
+ return;
+
+ // check if we have somebody doin' the UNDO for us
+ int nState = -1;
+ if (m_aMasterStateProvider.IsSet())
+ nState = m_aMasterStateProvider.Call(DbGridControlNavigationBarState::Undo);
+ if (nState>0)
+ { // yes, we have, and the slot is enabled
+ DBG_ASSERT(m_aMasterSlotExecutor.IsSet(), "DbGridControl::Undo : a state, but no execute link ?");
+ bool lResult = m_aMasterSlotExecutor.Call(DbGridControlNavigationBarState::Undo);
+ if (lResult)
+ // handled
+ return;
+ }
+ else if (nState == 0)
+ // yes, we have, and the slot is disabled
+ return;
+
+ BeginCursorAction();
+
+ bool bAppending = m_xCurrentRow->IsNew();
+ bool bDirty = m_xCurrentRow->IsModified();
+
+ try
+ {
+ // cancel editing
+ Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*m_pDataCursor), UNO_QUERY);
+ // no effects if we're not updating currently
+ if (bAppending)
+ // just refresh the row
+ xUpdateCursor->moveToInsertRow();
+ else
+ xUpdateCursor->cancelRowUpdates();
+
+ }
+ catch(Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ EndCursorAction();
+
+ m_xDataRow->SetState(m_pDataCursor.get(), false);
+ if (m_xPaintRow == m_xCurrentRow)
+ m_xPaintRow = m_xCurrentRow = m_xDataRow;
+ else
+ m_xCurrentRow = m_xDataRow;
+
+ if (bAppending && (EditBrowseBox::IsModified() || bDirty))
+ // remove the row
+ if (m_nCurrentPos == GetRowCount() - 2)
+ { // maybe we already removed it (in resetCurrentRow, called if the above moveToInsertRow
+ // caused our data source form to be reset - which should be the usual case...)
+ RowRemoved(GetRowCount() - 1);
+ m_aBar->InvalidateAll(m_nCurrentPos);
+ }
+
+ RowModified(m_nCurrentPos);
+}
+
+void DbGridControl::resetCurrentRow()
+{
+ if (IsModified())
+ {
+ // scenario : we're on the insert row, the row is dirty, and thus there exists a "second" insert row (which
+ // is clean). Normally in DataSourcePropertyChanged we would remove this second row if the modified state of
+ // the insert row changes from sal_True to sal_False. But if our current cell is the only modified element (means the
+ // data source isn't modified) and we're reset this DataSourcePropertyChanged would never be called, so we
+ // would never delete the obsolete "second insert row". Thus in this special case this method here
+ // is the only possibility to determine the redundance of the row (resetCurrentRow is called when the
+ // "first insert row" is about to be cleaned, so of course the "second insert row" is redundant now)
+ Reference< XPropertySet > xDataSource = getDataSource()->getPropertySet();
+ if (xDataSource.is() && !::comphelper::getBOOL(xDataSource->getPropertyValue(FM_PROP_ISMODIFIED)))
+ {
+ // are we on a new row currently ?
+ if (m_xCurrentRow->IsNew())
+ {
+ if (m_nCurrentPos == GetRowCount() - 2)
+ {
+ RowRemoved(GetRowCount() - 1);
+ m_aBar->InvalidateAll(m_nCurrentPos);
+ }
+ }
+ }
+
+ // update the rows
+ m_xDataRow->SetState(m_pDataCursor.get(), false);
+ if (m_xPaintRow == m_xCurrentRow)
+ m_xPaintRow = m_xCurrentRow = m_xDataRow;
+ else
+ m_xCurrentRow = m_xDataRow;
+ }
+
+ RowModified(GetCurRow()); // will update the current controller if affected
+}
+
+void DbGridControl::RowModified( long nRow )
+{
+ if (nRow == m_nCurrentPos && IsEditing())
+ {
+ CellControllerRef aTmpRef = Controller();
+ aTmpRef->ClearModified();
+ InitController(aTmpRef, m_nCurrentPos, GetCurColumnId());
+ }
+ EditBrowseBox::RowModified(nRow);
+}
+
+bool DbGridControl::IsModified() const
+{
+ return !IsFilterMode() && IsValid(m_xCurrentRow) && (m_xCurrentRow->IsModified() || EditBrowseBox::IsModified());
+}
+
+bool DbGridControl::IsCurrentAppending() const
+{
+ return m_xCurrentRow.is() && m_xCurrentRow->IsNew();
+}
+
+bool DbGridControl::IsInsertionRow(long nRow) const
+{
+ return (m_nOptions & DbGridControlOptions::Insert) && m_nTotalCount >= 0 && (nRow == GetRowCount() - 1);
+}
+
+bool DbGridControl::SaveModified()
+{
+ SAL_INFO("svx.fmcomp", "DbGridControl::SaveModified");
+ DBG_ASSERT(IsValid(m_xCurrentRow), "GridControl:: Invalid row");
+ if (!IsValid(m_xCurrentRow))
+ return true;
+
+ // accept input for this field
+ // Where there changes at the current input field?
+ if (!EditBrowseBox::IsModified())
+ return true;
+
+ size_t Location = GetModelColumnPos( GetCurColumnId() );
+ DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
+ bool bOK = pColumn && pColumn->Commit();
+ DBG_ASSERT( Controller().is(), "DbGridControl::SaveModified: was modified, by have no controller?!" );
+ if ( !Controller().is() )
+ // this might happen if the callbacks implicitly triggered by Commit
+ // fiddled with the form or the control ...
+ // (Note that this here is a workaround, at most. We need a general concept how
+ // to treat this, you can imagine an arbitrary number of scenarios where a callback
+ // triggers something which leaves us in an expected state.)
+ // #i67147# / 2006-07-17 / frank.schoenheit@sun.com
+ return bOK;
+
+ if (bOK)
+ {
+ Controller()->ClearModified();
+
+ if ( IsValid(m_xCurrentRow) )
+ {
+ m_xCurrentRow->SetState(m_pDataCursor.get(), false);
+ SAL_INFO("svx.fmcomp", "explicit SetState, new state: " << ROWSTATUS(m_xCurrentRow));
+ InvalidateStatusCell( m_nCurrentPos );
+ }
+ else
+ {
+ SAL_INFO("svx.fmcomp", "no SetState, new state: " << ROWSTATUS(m_xCurrentRow));
+ }
+ }
+ else
+ {
+ // reset the modified flag...
+ Controller()->SetModified();
+ }
+
+ return bOK;
+}
+
+bool DbGridControl::SaveRow()
+{
+ SAL_INFO("svx.fmcomp", "DbGridControl::SaveRow");
+ // valid row
+ if (!IsValid(m_xCurrentRow) || !IsModified())
+ return true;
+ // value of the controller was not saved, yet
+ else if (Controller().is() && Controller()->IsModified())
+ {
+ if (!SaveModified())
+ return false;
+ }
+ m_bUpdating = true;
+
+ BeginCursorAction();
+ bool bAppending = m_xCurrentRow->IsNew();
+ bool bSuccess = false;
+ try
+ {
+ Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*m_pDataCursor), UNO_QUERY);
+ if (bAppending)
+ xUpdateCursor->insertRow();
+ else
+ xUpdateCursor->updateRow();
+ bSuccess = true;
+ }
+ catch(SQLException&)
+ {
+ EndCursorAction();
+ m_bUpdating = false;
+ return false;
+ }
+
+ try
+ {
+ if (bSuccess)
+ {
+ // if we are appending we still sit on the insert row
+ // we don't move just clear the flags not to move on the current row
+ m_xCurrentRow->SetState(m_pDataCursor.get(), false);
+ SAL_INFO("svx.fmcomp", "explicit SetState after a successful update, new state: " << ROWSTATUS(m_xCurrentRow));
+ m_xCurrentRow->SetNew(false);
+
+ // adjust the seekcursor if it is on the same position as the datacursor
+ if (m_nSeekPos == m_nCurrentPos || bAppending)
+ {
+ // get the bookmark to refetch the data
+ // in insert mode we take the new bookmark of the data cursor
+ Any aBookmark = bAppending ? m_pDataCursor->getBookmark() : m_pSeekCursor->getBookmark();
+ m_pSeekCursor->moveToBookmark(aBookmark);
+ // get the data
+ m_xSeekRow->SetState(m_pSeekCursor.get(), true);
+ m_nSeekPos = m_pSeekCursor->getRow() - 1;
+ }
+ }
+ // and repaint the row
+ RowModified(m_nCurrentPos);
+ }
+ catch(Exception&)
+ {
+ }
+
+ m_bUpdating = false;
+ EndCursorAction();
+
+ // The old code returned (nRecords != 0) here.
+ // Me thinks this is wrong : If something goes wrong while update the record, an exception will be thrown,
+ // which results in a "return sal_False" (see above). If no exception is thrown, everything is fine. If nRecords
+ // is zero, this simply means all fields had their original values.
+ // FS - 06.12.99 - 70502
+ return true;
+}
+
+bool DbGridControl::PreNotify(NotifyEvent& rEvt)
+{
+ // do not handle events of the Navbar
+ if (m_aBar->IsWindowOrChild(rEvt.GetWindow()))
+ return BrowseBox::PreNotify(rEvt);
+
+ switch (rEvt.GetType())
+ {
+ case MouseNotifyEvent::KEYINPUT:
+ {
+ const KeyEvent* pKeyEvent = rEvt.GetKeyEvent();
+
+ sal_uInt16 nCode = pKeyEvent->GetKeyCode().GetCode();
+ bool bShift = pKeyEvent->GetKeyCode().IsShift();
+ bool bCtrl = pKeyEvent->GetKeyCode().IsMod1();
+ bool bAlt = pKeyEvent->GetKeyCode().IsMod2();
+ if ( ( KEY_TAB == nCode ) && bCtrl && !bAlt )
+ {
+ // Ctrl-Tab is used to step out of the control, without traveling to the
+ // remaining cells first
+ // -> build a new key event without the Ctrl-key, and let the very base class handle it
+ vcl::KeyCode aNewCode( KEY_TAB, bShift, false, false, false );
+ KeyEvent aNewEvent( pKeyEvent->GetCharCode(), aNewCode );
+
+ // call the Control - our direct base class will interpret this in a way we do not want (and do
+ // a cell traveling)
+ Control::KeyInput( aNewEvent );
+ return true;
+ }
+
+ if ( !bShift && !bCtrl && ( KEY_ESCAPE == nCode ) )
+ {
+ if (IsModified())
+ {
+ Undo();
+ return true;
+ }
+ }
+ else if ( ( KEY_DELETE == nCode ) && !bShift && !bCtrl ) // delete rows
+ {
+ if ((m_nOptions & DbGridControlOptions::Delete) && GetSelectRowCount())
+ {
+ // delete asynchronously
+ if (m_nDeleteEvent)
+ Application::RemoveUserEvent(m_nDeleteEvent);
+ m_nDeleteEvent = Application::PostUserEvent(LINK(this,DbGridControl,OnDelete), nullptr, true);
+ return true;
+ }
+ }
+
+ [[fallthrough]];
+ }
+ default:
+ return EditBrowseBox::PreNotify(rEvt);
+ }
+}
+
+bool DbGridControl::IsTabAllowed(bool bRight) const
+{
+ if (bRight)
+ // Tab only if not on the _last_ row
+ return GetCurRow() < (GetRowCount() - 1) || !m_bRecordCountFinal ||
+ GetViewColumnPos(GetCurColumnId()) < (GetViewColCount() - 1);
+ else
+ {
+ // Tab only if not on the _first_ row
+ return GetCurRow() > 0 || (GetCurColumnId() && GetViewColumnPos(GetCurColumnId()) > 0);
+ }
+}
+
+void DbGridControl::KeyInput( const KeyEvent& rEvt )
+{
+ if (rEvt.GetKeyCode().GetFunction() == KeyFuncType::COPY)
+ {
+ long nRow = GetCurRow();
+ sal_uInt16 nColId = GetCurColumnId();
+ if (nRow >= 0 && nRow < GetRowCount() && nColId < ColCount())
+ {
+ size_t Location = GetModelColumnPos( nColId );
+ DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
+ OStringTransfer::CopyString( GetCurrentRowCellText( pColumn, m_xCurrentRow ), this );
+ return;
+ }
+ }
+ EditBrowseBox::KeyInput(rEvt);
+}
+
+void DbGridControl::HideColumn(sal_uInt16 nId)
+{
+ DeactivateCell();
+
+ // determine the col for the focus to set to after removal
+ sal_uInt16 nPos = GetViewColumnPos(nId);
+ sal_uInt16 nNewColId = nPos == (ColCount()-1)
+ ? GetColumnIdFromViewPos(nPos-1) // last col is to be removed -> take the previous
+ : GetColumnIdFromViewPos(nPos+1); // take the next
+
+ long lCurrentWidth = GetColumnWidth(nId);
+ EditBrowseBox::RemoveColumn(nId);
+ // don't use my own RemoveColumn, this would remove it from m_aColumns, too
+
+ // update my model
+ size_t Location = GetModelColumnPos( nId );
+ DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
+ DBG_ASSERT(pColumn, "DbGridControl::HideColumn : somebody did hide a nonexistent column !");
+ if (pColumn)
+ {
+ pColumn->m_bHidden = true;
+ pColumn->m_nLastVisibleWidth = CalcReverseZoom(lCurrentWidth);
+ }
+
+ // and reset the focus
+ if ( nId == GetCurColumnId() )
+ GoToColumnId( nNewColId );
+}
+
+void DbGridControl::ShowColumn(sal_uInt16 nId)
+{
+ sal_uInt16 nPos = GetModelColumnPos(nId);
+ DBG_ASSERT(nPos != GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : invalid argument !");
+ if (nPos == GRID_COLUMN_NOT_FOUND)
+ return;
+
+ DbGridColumn* pColumn = m_aColumns[ nPos ].get();
+ if (!pColumn->IsHidden())
+ {
+ DBG_ASSERT(GetViewColumnPos(nId) != GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : inconsistent internal state !");
+ // if the column isn't marked as hidden, it should be visible, shouldn't it ?
+ return;
+ }
+ DBG_ASSERT(GetViewColumnPos(nId) == GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : inconsistent internal state !");
+ // the opposite situation ...
+
+ // to determine the new view position we need an adjacent non-hidden column
+ sal_uInt16 nNextNonHidden = BROWSER_INVALIDID;
+ // first search the cols to the right
+ for ( size_t i = nPos + 1; i < m_aColumns.size(); ++i )
+ {
+ DbGridColumn* pCurCol = m_aColumns[ i ].get();
+ if (!pCurCol->IsHidden())
+ {
+ nNextNonHidden = i;
+ break;
+ }
+ }
+ if ((nNextNonHidden == BROWSER_INVALIDID) && (nPos > 0))
+ {
+ // then to the left
+ for ( size_t i = nPos; i > 0; --i )
+ {
+ DbGridColumn* pCurCol = m_aColumns[ i-1 ].get();
+ if (!pCurCol->IsHidden())
+ {
+ nNextNonHidden = i-1;
+ break;
+ }
+ }
+ }
+ sal_uInt16 nNewViewPos = (nNextNonHidden == BROWSER_INVALIDID)
+ ? 1 // there is no visible column -> insert behind the handle col
+ : GetViewColumnPos( m_aColumns[ nNextNonHidden ]->GetId() ) + 1;
+ // the first non-handle col has "view pos" 0, but the pos arg for InsertDataColumn expects
+ // a position 1 for the first non-handle col -> +1
+ DBG_ASSERT(nNewViewPos != GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : inconsistent internal state !");
+ // we found a col marked as visible but got no view pos for it ...
+
+ if ((nNextNonHidden<nPos) && (nNextNonHidden != BROWSER_INVALIDID))
+ // nNextNonHidden is a column to the left, so we want to insert the new col _right_ beside it's pos
+ ++nNewViewPos;
+
+ DeactivateCell();
+
+ OUString aName;
+ pColumn->getModel()->getPropertyValue(FM_PROP_LABEL) >>= aName;
+ InsertDataColumn(nId, aName, CalcZoom(pColumn->m_nLastVisibleWidth), HeaderBarItemBits::CENTER | HeaderBarItemBits::CLICKABLE, nNewViewPos);
+ pColumn->m_bHidden = false;
+
+ ActivateCell();
+ Invalidate();
+}
+
+sal_uInt16 DbGridControl::GetColumnIdFromModelPos( sal_uInt16 nPos ) const
+{
+ if (nPos >= m_aColumns.size())
+ {
+ OSL_FAIL("DbGridControl::GetColumnIdFromModelPos : invalid argument !");
+ return GRID_COLUMN_NOT_FOUND;
+ }
+
+ DbGridColumn* pCol = m_aColumns[ nPos ].get();
+#if (OSL_DEBUG_LEVEL > 0) || defined DBG_UTIL
+ // in the debug version, we convert the ModelPos into a ViewPos and compare this with the
+ // value we will return (nId at the corresponding Col in m_aColumns)
+
+ if (!pCol->IsHidden())
+ { // makes sense only if the column is visible
+ sal_uInt16 nViewPos = nPos;
+ for ( size_t i = 0; i < m_aColumns.size() && i < nPos; ++i)
+ if ( m_aColumns[ i ]->IsHidden())
+ --nViewPos;
+
+ DBG_ASSERT(pCol && GetColumnIdFromViewPos(nViewPos) == pCol->GetId(),
+ "DbGridControl::GetColumnIdFromModelPos : this isn't consistent... did I misunderstand something ?");
+ }
+#endif
+ return pCol->GetId();
+}
+
+sal_uInt16 DbGridControl::GetModelColumnPos( sal_uInt16 nId ) const
+{
+ for ( size_t i = 0; i < m_aColumns.size(); ++i )
+ if ( m_aColumns[ i ]->GetId() == nId )
+ return i;
+
+ return GRID_COLUMN_NOT_FOUND;
+}
+
+void DbGridControl::implAdjustInSolarThread(bool _bRows)
+{
+ SAL_INFO("svx.fmcomp", "DbGridControl::implAdjustInSolarThread");
+ ::osl::MutexGuard aGuard(m_aAdjustSafety);
+ if (!Application::IsMainThread())
+ {
+ m_nAsynAdjustEvent = PostUserEvent(LINK(this, DbGridControl, OnAsyncAdjust), reinterpret_cast< void* >( _bRows ), true);
+ m_bPendingAdjustRows = _bRows;
+ if (_bRows)
+ SAL_INFO("svx.fmcomp", "posting an AdjustRows");
+ else
+ SAL_INFO("svx.fmcomp", "posting an AdjustDataSource");
+ }
+ else
+ {
+ if (_bRows)
+ SAL_INFO("svx.fmcomp", "doing an AdjustRows");
+ else
+ SAL_INFO("svx.fmcomp", "doing an AdjustDataSource");
+ // always adjust the rows before adjusting the data source
+ // If this is not necessary (because the row count did not change), nothing is done
+ // The problem is that we can't rely on the order of which the calls come in: If the cursor was moved
+ // to a position behind row count know 'til now, the cursorMoved notification may come before the
+ // RowCountChanged notification
+ // 94093 - 02.11.2001 - frank.schoenheit@sun.com
+ AdjustRows();
+
+ if ( !_bRows )
+ AdjustDataSource();
+ }
+}
+
+IMPL_LINK(DbGridControl, OnAsyncAdjust, void*, pAdjustWhat, void)
+{
+ m_nAsynAdjustEvent = nullptr;
+
+ AdjustRows();
+ // see implAdjustInSolarThread for a comment why we do this every time
+
+ if ( !pAdjustWhat )
+ AdjustDataSource();
+}
+
+void DbGridControl::BeginCursorAction()
+{
+ if (m_pFieldListeners)
+ {
+ ColumnFieldValueListeners* pListeners = static_cast<ColumnFieldValueListeners*>(m_pFieldListeners);
+ for (const auto& rListener : *pListeners)
+ {
+ GridFieldValueListener* pCurrent = rListener.second;
+ if (pCurrent)
+ pCurrent->suspend();
+ }
+ }
+
+ if (m_pDataSourcePropListener)
+ m_pDataSourcePropListener->suspend();
+}
+
+void DbGridControl::EndCursorAction()
+{
+ if (m_pFieldListeners)
+ {
+ ColumnFieldValueListeners* pListeners = static_cast<ColumnFieldValueListeners*>(m_pFieldListeners);
+ for (const auto& rListener : *pListeners)
+ {
+ GridFieldValueListener* pCurrent = rListener.second;
+ if (pCurrent)
+ pCurrent->resume();
+ }
+ }
+
+ if (m_pDataSourcePropListener)
+ m_pDataSourcePropListener->resume();
+}
+
+void DbGridControl::ConnectToFields()
+{
+ ColumnFieldValueListeners* pListeners = static_cast<ColumnFieldValueListeners*>(m_pFieldListeners);
+ DBG_ASSERT(!pListeners || pListeners->empty(), "DbGridControl::ConnectToFields : please call DisconnectFromFields first !");
+
+ if (!pListeners)
+ {
+ pListeners = new ColumnFieldValueListeners;
+ m_pFieldListeners = pListeners;
+ }
+
+ for (auto const & pCurrent : m_aColumns)
+ {
+ sal_uInt16 nViewPos = pCurrent ? GetViewColumnPos(pCurrent->GetId()) : GRID_COLUMN_NOT_FOUND;
+ if (GRID_COLUMN_NOT_FOUND == nViewPos)
+ continue;
+
+ Reference< XPropertySet > xField = pCurrent->GetField();
+ if (!xField.is())
+ continue;
+
+ // column is visible and bound here
+ GridFieldValueListener*& rpListener = (*pListeners)[pCurrent->GetId()];
+ DBG_ASSERT(!rpListener, "DbGridControl::ConnectToFields : already a listener for this column ?!");
+ rpListener = new GridFieldValueListener(*this, xField, pCurrent->GetId());
+ }
+}
+
+void DbGridControl::DisconnectFromFields()
+{
+ if (!m_pFieldListeners)
+ return;
+
+ ColumnFieldValueListeners* pListeners = static_cast<ColumnFieldValueListeners*>(m_pFieldListeners);
+ while (!pListeners->empty())
+ {
+ sal_Int32 nOldSize = pListeners->size();
+ pListeners->begin()->second->dispose();
+ DBG_ASSERT(nOldSize > static_cast<sal_Int32>(pListeners->size()), "DbGridControl::DisconnectFromFields : dispose on a listener should result in a removal from my list !");
+ }
+
+ delete pListeners;
+ m_pFieldListeners = nullptr;
+}
+
+void DbGridControl::FieldValueChanged(sal_uInt16 _nId)
+{
+ osl::MutexGuard aPreventDestruction(m_aDestructionSafety);
+ // needed as this may run in a thread other than the main one
+ if (GetRowStatus(GetCurRow()) != EditBrowseBox::MODIFIED)
+ // all other cases are handled elsewhere
+ return;
+
+ size_t Location = GetModelColumnPos( _nId );
+ DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
+ if (pColumn)
+ {
+ std::unique_ptr<vcl::SolarMutexTryAndBuyGuard> pGuard;
+ while (!m_bWantDestruction && (!pGuard || !pGuard->isAcquired()))
+ pGuard.reset(new vcl::SolarMutexTryAndBuyGuard);
+
+ if (m_bWantDestruction)
+ { // at this moment, within another thread, our destructor tries to destroy the listener which called this method
+ // => don't do anything
+ // 73365 - 23.02.00 - FS
+ return;
+ }
+
+ // and finally do the update ...
+ pColumn->UpdateFromField(m_xCurrentRow.get(), m_xFormatter);
+ RowModified(GetCurRow());
+ }
+}
+
+void DbGridControl::FieldListenerDisposing(sal_uInt16 _nId)
+{
+ ColumnFieldValueListeners* pListeners = static_cast<ColumnFieldValueListeners*>(m_pFieldListeners);
+ if (!pListeners)
+ {
+ OSL_FAIL("DbGridControl::FieldListenerDisposing : invalid call (have no listener array) !");
+ return;
+ }
+
+ ColumnFieldValueListeners::const_iterator aPos = pListeners->find(_nId);
+ if (aPos == pListeners->end())
+ {
+ OSL_FAIL("DbGridControl::FieldListenerDisposing : invalid call (did not find the listener) !");
+ return;
+ }
+
+ delete aPos->second;
+
+ pListeners->erase(aPos);
+}
+
+void DbGridControl::disposing(sal_uInt16 _nId)
+{
+ if (_nId == 0)
+ { // the seek cursor is being disposed
+ ::osl::MutexGuard aGuard(m_aAdjustSafety);
+ setDataSource(nullptr, DbGridControlOptions::Readonly); // our clone was disposed so we set our datasource to null to avoid later access to it
+ if (m_nAsynAdjustEvent)
+ {
+ RemoveUserEvent(m_nAsynAdjustEvent);
+ m_nAsynAdjustEvent = nullptr;
+ }
+ }
+}
+
+sal_Int32 DbGridControl::GetAccessibleControlCount() const
+{
+ return EditBrowseBox::GetAccessibleControlCount() + 1; // the navigation control
+}
+
+Reference<XAccessible > DbGridControl::CreateAccessibleControl( sal_Int32 _nIndex )
+{
+ Reference<XAccessible > xRet;
+ if ( _nIndex == EditBrowseBox::GetAccessibleControlCount() )
+ {
+ xRet = m_aBar->GetAccessible();
+ }
+ else
+ xRet = EditBrowseBox::CreateAccessibleControl( _nIndex );
+ return xRet;
+}
+
+Reference< XAccessible > DbGridControl::CreateAccessibleCell( sal_Int32 _nRow, sal_uInt16 _nColumnPos )
+{
+ sal_uInt16 nColumnId = GetColumnId( _nColumnPos );
+ size_t Location = GetModelColumnPos(nColumnId);
+ DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
+ if ( pColumn )
+ {
+ Reference< css::awt::XControl> xInt(pColumn->GetCell());
+ Reference< css::awt::XCheckBox> xBox(xInt,UNO_QUERY);
+ if ( xBox.is() )
+ {
+ TriState eValue = TRISTATE_FALSE;
+ switch( xBox->getState() )
+ {
+ case 0:
+ eValue = TRISTATE_FALSE;
+ break;
+ case 1:
+ eValue = TRISTATE_TRUE;
+ break;
+ case 2:
+ eValue = TRISTATE_INDET;
+ break;
+ }
+ return EditBrowseBox::CreateAccessibleCheckBoxCell( _nRow, _nColumnPos,eValue );
+ }
+ }
+ return EditBrowseBox::CreateAccessibleCell( _nRow, _nColumnPos );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/fmcomp/xmlexchg.cxx b/svx/source/fmcomp/xmlexchg.cxx
new file mode 100644
index 000000000..7fe8e4b88
--- /dev/null
+++ b/svx/source/fmcomp/xmlexchg.cxx
@@ -0,0 +1,61 @@
+/* -*- 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/xmlexchg.hxx>
+#include <sot/formats.hxx>
+#include <sot/exchange.hxx>
+
+namespace svx
+{
+
+
+ using namespace ::com::sun::star::datatransfer;
+
+ OXFormsTransferable::OXFormsTransferable( const OXFormsDescriptor &rhs ) :
+ m_aDescriptor(rhs)
+ {
+ }
+
+ void OXFormsTransferable::AddSupportedFormats()
+ {
+ AddFormat( SotClipboardFormatId::XFORMS );
+ }
+
+ bool OXFormsTransferable::GetData( const DataFlavor& _rFlavor, const OUString& /*rDestDoc*/ )
+ {
+ const SotClipboardFormatId nFormatId = SotExchange::GetFormat( _rFlavor );
+ if ( SotClipboardFormatId::XFORMS == nFormatId )
+ {
+ return SetString("XForms-Transferable", _rFlavor);
+ }
+ return false;
+ }
+
+ const OXFormsDescriptor &OXFormsTransferable::extractDescriptor( const TransferableDataHelper &_rData ) {
+
+ using namespace ::com::sun::star::uno;
+ Reference<XTransferable> &transfer = const_cast<Reference<XTransferable> &>(_rData.GetTransferable());
+ XTransferable *pInterface = transfer.get();
+ OXFormsTransferable& rThis = dynamic_cast<OXFormsTransferable&>(*pInterface);
+ return rThis.m_aDescriptor;
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */