diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /svx/source/fmcomp/gridcell.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'svx/source/fmcomp/gridcell.cxx')
-rw-r--r-- | svx/source/fmcomp/gridcell.cxx | 4615 |
1 files changed, 4615 insertions, 0 deletions
diff --git a/svx/source/fmcomp/gridcell.cxx b/svx/source/fmcomp/gridcell.cxx new file mode 100644 index 000000000..67e5d0040 --- /dev/null +++ b/svx/source/fmcomp/gridcell.cxx @@ -0,0 +1,4615 @@ +/* -*- 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 <o3tl/safeint.hxx> +#include <svl/numformat.hxx> +#include <svl/numuno.hxx> +#include <svx/dialmgr.hxx> +#include <toolkit/helper/listenermultiplexer.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <tools/debug.hxx> +#include <tools/fract.hxx> +#include <tools/diagnose_ex.h> +#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; + +constexpr OUStringLiteral INVALIDTEXT = u"###"; +constexpr OUStringLiteral OBJECTTEXT = u"<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, Any( 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 ) + || ( rDev.GetOwnerWindow()->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 ( auto pFilterCell = dynamic_cast<FmXFilterCell*>( m_pCell.get() ) ) + pFilterCell->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 ) +{ + svt::ControlBase* pWindows[] = { m_pPainter, m_pWindow }; + + if (_eInitWhat & InitWindowFacet::WritingMode) + { + for (svt::ControlBase* pWindow : pWindows) + { + if (pWindow) + pWindow->EnableRTL(rParent.IsRTLEnabled()); + } + } + + if (_eInitWhat & InitWindowFacet::Font) + { + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + const Fraction& rZoom = rParent.GetZoom(); + + for (svt::ControlBase* pWindow : pWindows) + { + if (!pWindow) + continue; + + vcl::Font aFont = rStyleSettings.GetFieldFont(); + aFont.SetTransparent(isTransparent()); + + if (rParent.IsControlFont()) + aFont.Merge(rParent.GetControlFont()); + + if (rZoom.GetNumerator() != rZoom.GetDenominator()) + { + Size aSize = aFont.GetFontSize(); + aSize.setWidth(std::round(double(aSize.Width() * rZoom))); + aSize.setHeight(std::round(double(aSize.Height() * rZoom))); + aFont.SetFontSize(aSize); + } + + pWindow->SetPointFont(aFont); + } + } + + if ((_eInitWhat & InitWindowFacet::Font) || (_eInitWhat & InitWindowFacet::Foreground)) + { + Color aTextColor(rParent.IsControlForeground() ? rParent.GetControlForeground() : rParent.GetTextColor()); + + bool bTextLineColor = rParent.IsTextLineColor(); + Color aTextLineColor(rParent.GetTextLineColor()); + + for (svt::ControlBase* 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)) + return; + + if (rParent.IsControlBackground()) + { + Color aColor(rParent.GetControlBackground()); + for (svt::ControlBase* pWindow : pWindows) + { + if (pWindow) + { + if (isTransparent()) + pWindow->SetBackground(); + else + { + pWindow->SetBackground(aColor); + pWindow->SetControlBackground(aColor); + } + pWindow->GetOutDev()->SetFillColor(aColor); + } + } + } + else + { + if (m_pPainter) + { + if (isTransparent()) + m_pPainter->SetBackground(); + else + m_pPainter->SetBackground(rParent.GetBackground()); + m_pPainter->GetOutDev()->SetFillColor(rParent.GetOutDev()->GetFillColor()); + } + + if (m_pWindow) + { + if (isTransparent()) + m_pWindow->SetBackground(rParent.GetBackground()); + else + m_pWindow->GetOutDev()->SetFillColor(rParent.GetOutDev()->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()) ) + return; + + bool bReadOnly = m_rColumn.IsReadOnly(); + if ( !bReadOnly ) + { + _rxModel->getPropertyValue( i_bReadOnly ? OUString(FM_PROP_READONLY) : OUString(FM_PROP_ISREADONLY)) >>= bReadOnly; + } + m_pWindow->SetEditableReadOnly(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(BrowserDataWin& 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 ) + { + if (EditControlBase* pControl = dynamic_cast<EditControlBase*>(_pWindow)) + { + switch (_nAlignmentBit) + { + case WB_LEFT: + pControl->get_widget().set_alignment(TxtAlign::Left); + break; + case WB_CENTER: + pControl->get_widget().set_alignment(TxtAlign::Center); + break; + case WB_RIGHT: + pControl->get_widget().set_alignment(TxtAlign::Right); + break; + } + return; + } + + 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) +{ + m_pPainter->SetSizePixel(rRect.GetSize()); + m_pPainter->Draw(&rDev, rRect.TopLeft(), SystemTextColorFlags::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<EditControlBase&>(*m_pWindow).get_widget().set_max_length(nMaxLen); + if (m_pPainter) + dynamic_cast<EditControlBase&>(*m_pPainter).get_widget().set_max_length(nMaxLen); +} + +DbTextField::DbTextField(DbGridColumn& _rColumn) + :DbLimitedLengthField(_rColumn) + ,m_bIsMultiLineEdit(false) +{ +} + +DbTextField::~DbTextField( ) +{ + m_pPainterImplementation.reset(); + m_pEdit.reset(); +} + +void DbTextField::Init(BrowserDataWin& rParent, const Reference< XRowSet >& xCursor) +{ + sal_Int16 nAlignment = m_rColumn.SetAlignmentFromModel(-1); + + Reference< XPropertySet > xModel( m_rColumn.getModel() ); + + bool bLeftAlign = true; + + // is this a multi-line field? + bool bIsMultiLine = false; + try + { + if ( xModel.is() ) + { + OSL_VERIFY( xModel->getPropertyValue( FM_PROP_MULTILINE ) >>= bIsMultiLine ); + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION("svx", + "caught an exception while determining the multi-line capabilities!"); + } + + m_bIsMultiLineEdit = bIsMultiLine; + if ( bIsMultiLine ) + { + auto xEditControl = VclPtr<MultiLineTextCell>::Create(&rParent); + auto xEditPainter = VclPtr<MultiLineTextCell>::Create(&rParent); + + switch (nAlignment) + { + case awt::TextAlign::RIGHT: + xEditControl->get_widget().set_alignment(TxtAlign::Right); + xEditPainter->get_widget().set_alignment(TxtAlign::Right); + bLeftAlign = false; + break; + case awt::TextAlign::CENTER: + xEditControl->get_widget().set_alignment(TxtAlign::Center); + xEditPainter->get_widget().set_alignment(TxtAlign::Center); + bLeftAlign = false; + break; + } + + m_pWindow = xEditControl; + m_pEdit.reset(new MultiLineEditImplementation(*xEditControl)); + + m_pPainter = xEditPainter; + m_pPainterImplementation.reset(new MultiLineEditImplementation(*xEditPainter)); + } + else + { + auto xEditControl = VclPtr<EditControl>::Create(&rParent); + auto xEditPainter = VclPtr<EditControl>::Create(&rParent); + + switch (nAlignment) + { + case awt::TextAlign::RIGHT: + xEditControl->get_widget().set_alignment(TxtAlign::Right); + xEditPainter->get_widget().set_alignment(TxtAlign::Right); + bLeftAlign = false; + break; + case awt::TextAlign::CENTER: + xEditControl->get_widget().set_alignment(TxtAlign::Center); + xEditPainter->get_widget().set_alignment(TxtAlign::Center); + bLeftAlign = false; + break; + } + + m_pWindow = xEditControl; + m_pEdit.reset(new EntryImplementation(*xEditControl)); + + m_pPainter = xEditPainter; + m_pPainterImplementation.reset(new EntryImplementation(*xEditPainter)); + } + + if (bLeftAlign) + { + // 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, const 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 (nMaxTextLen > 0 && sText.getLength() > nMaxTextLen) + { + sal_Int32 nDiff = sText.getLength() - nMaxTextLen; + sText = sText.replaceAt(sText.getLength() - nDiff,nDiff, u""); + } + + 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 (nMaxTextLen > 0) + { + 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, Any( 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( BrowserDataWin& rParent, const Reference< XRowSet >& xCursor) +{ + sal_Int16 nAlignment = m_rColumn.SetAlignmentFromModel(-1); + + Reference< css::beans::XPropertySet > xUnoModel = m_rColumn.getModel(); + + auto xEditControl = VclPtr<FormattedControl>::Create(&rParent, false); + auto xEditPainter = VclPtr<FormattedControl>::Create(&rParent, false); + + weld::EntryFormatter& rControlFormatter = xEditControl->get_formatter(); + weld::EntryFormatter& rPainterFormatter = xEditPainter->get_formatter(); + + m_pWindow = xEditControl.get(); + m_pPainter = xEditPainter.get(); + + switch (nAlignment) + { + case awt::TextAlign::RIGHT: + xEditControl->get_widget().set_alignment(TxtAlign::Right); + xEditPainter->get_widget().set_alignment(TxtAlign::Right); + break; + case awt::TextAlign::CENTER: + xEditControl->get_widget().set_alignment(TxtAlign::Center); + xEditPainter->get_widget().set_alignment(TxtAlign::Center); + break; + default: + { + // Everything just so that the selection goes from right to left when getting focus + SelectionOptions eOptions = rControlFormatter.GetEntrySelectionOptions(); + rControlFormatter.SetEntrySelectionOptions(eOptions | SelectionOptions::ShowFirst); + break; + } + } + + implAdjustGenericFieldSetting( xUnoModel ); + + rControlFormatter.SetStrictFormat(false); + rPainterFormatter.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* pImplementation = comphelper::getFromUnoTunnel<SvNumberFormatsSupplierObj>(m_xSupplier); + if (pImplementation) + pFormatterUsed = pImplementation->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 = rControlFormatter.StandardFormatter(); + DBG_ASSERT(pFormatterUsed != nullptr, "DbFormattedField::Init : no standard formatter given by the numeric field !"); + } + // ... and a standard key + if (nFormatKey == -1) + nFormatKey = 0; + + rControlFormatter.SetFormatter(pFormatterUsed); + rPainterFormatter.SetFormatter(pFormatterUsed); + + rControlFormatter.SetFormatKey(nFormatKey); + rPainterFormatter.SetFormatKey(nFormatKey); + + rControlFormatter.TreatAsNumber(m_rColumn.IsNumeric()); + rPainterFormatter.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); + rControlFormatter.SetMinValue(dMin); + rPainterFormatter.SetMinValue(dMin); + bClearMin = false; + } + } + if (bClearMin) + { + rControlFormatter.ClearMinValue(); + rPainterFormatter.ClearMinValue(); + } + bool bClearMax = true; + if (::comphelper::hasProperty(FM_PROP_EFFECTIVE_MAX, xUnoModel)) + { + Any aMax(xUnoModel->getPropertyValue(FM_PROP_EFFECTIVE_MAX)); + if (aMax.getValueType().getTypeClass() != TypeClass_VOID) + { + DBG_ASSERT(aMax.getValueType().getTypeClass() == TypeClass_DOUBLE, "DbFormattedField::Init : the model has an invalid max value !"); + double dMax = ::comphelper::getDouble(aMax); + rControlFormatter.SetMaxValue(dMax); + rPainterFormatter.SetMaxValue(dMax); + bClearMax = false; + } + } + if (bClearMax) + { + rControlFormatter.ClearMaxValue(); + rPainterFormatter.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()) + { + rControlFormatter.SetDefaultValue(::comphelper::getDouble(aDefault)); + rPainterFormatter.SetDefaultValue(::comphelper::getDouble(aDefault)); + } + else + { + OUString sConverted; + const Color* pDummy; + pFormatterUsed->GetOutputString(::comphelper::getDouble(aDefault), 0, sConverted, &pDummy); + rControlFormatter.SetDefaultText(sConverted); + rPainterFormatter.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)) + { + rControlFormatter.SetDefaultValue(dVal); + rPainterFormatter.SetDefaultValue(dVal); + } + } + else + { + rControlFormatter.SetDefaultText(sDefault); + rPainterFormatter.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<FormattedControlBase*>(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<FormattedControlBase*>(m_pWindow.get())->get_formatter().SetFormatKey(nNewKey); + if (m_pPainter) + static_cast<FormattedControlBase*>(m_pPainter.get())->get_formatter().SetFormatKey(nNewKey); + } + else + { + DbLimitedLengthField::_propertyChanged( _rEvent ); + } +} + +OUString DbFormattedField::GetFormatText(const Reference< css::sdb::XColumn >& _rxField, const Reference< XNumberFormatter >& /*xFormatter*/, const Color** ppColor) +{ + // no color specification by default + if (ppColor != nullptr) + *ppColor = nullptr; + + // NULL value -> empty text + if (!_rxField.is()) + return OUString(); + + FormattedControlBase* pControl = static_cast<FormattedControlBase*>(m_pPainter.get()); + weld::EntryFormatter& rPainterFormatter = pControl->get_formatter(); + + 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; + rPainterFormatter.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; + rPainterFormatter.SetTextFormatted(aText); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svx"); + } + + aText = pControl->get_widget().get_text(); + if (ppColor != nullptr) + *ppColor = rPainterFormatter.GetLastOutputColor(); + + return aText; +} + +void DbFormattedField::UpdateFromField(const Reference< css::sdb::XColumn >& _rxField, const Reference< XNumberFormatter >& /*xFormatter*/) +{ + try + { + FormattedControlBase* pEditControl = static_cast<FormattedControlBase*>(m_pWindow.get()); + weld::Entry& rEntry = pEditControl->get_widget(); + weld::EntryFormatter& rEditFormatter = pEditControl->get_formatter(); + + if (!_rxField.is()) + { + // NULL value -> empty text + rEntry.set_text(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()) + rEntry.set_text(OUString()); + else + rEditFormatter.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()); + + rEditFormatter.SetTextFormatted( sText ); + rEntry.select_region(0, -1); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svx"); + } +} + +void DbFormattedField::updateFromModel( Reference< XPropertySet > _rxModel ) +{ + OSL_ENSURE( _rxModel.is() && m_pWindow, "DbFormattedField::updateFromModel: invalid call!" ); + + FormattedControlBase* pEditControl = static_cast<FormattedControlBase*>(m_pWindow.get()); + weld::Entry& rEntry = pEditControl->get_widget(); + weld::EntryFormatter& rEditFormatter = pEditControl->get_formatter(); + + OUString sText; + Any aValue = _rxModel->getPropertyValue( FM_PROP_EFFECTIVE_VALUE ); + if ( !aValue.hasValue() || (aValue >>= sText) ) + { + // our effective value is transferred as string + rEditFormatter.SetTextFormatted( sText ); + rEntry.select_region(0, -1); + } + else + { + double dValue = 0; + aValue >>= dValue; + rEditFormatter.SetValue(dValue); + } +} + +bool DbFormattedField::commitControl() +{ + Any aNewVal; + + FormattedControlBase* pEditControl = static_cast<FormattedControlBase*>(m_pWindow.get()); + weld::Entry& rEntry = pEditControl->get_widget(); + weld::EntryFormatter& rEditFormatter = pEditControl->get_formatter(); + + if (m_rColumn.IsNumeric()) + { + if (!rEntry.get_text().isEmpty()) + aNewVal <<= rEditFormatter.GetValue(); + // an empty string is passed on as void by default, to start with + } + else + aNewVal <<= rEditFormatter.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(BrowserDataWin& 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() )->EnableTriState( bTristate ); + static_cast< CheckBoxControl* >( m_pPainter.get() )->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->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) +{ + CheckBoxControl* pControl = static_cast<CheckBoxControl*>(m_pPainter.get()); + lcl_setCheckBoxState( _rxField, pControl ); + + Size aBoxSize; + + switch (rDev.GetOutDevType()) + { + case OUTDEV_WINDOW: + case OUTDEV_VIRDEV: + aBoxSize = pControl->GetBox().get_preferred_size(); + break; + case OUTDEV_PRINTER: + case OUTDEV_PDF: + { + auto nSize = std::min(rRect.GetWidth(), rRect.GetHeight()); + aBoxSize = Size(nSize, nSize); + break; + } + } + + tools::Rectangle aRect(Point(rRect.Left() + ((rRect.GetWidth() - aBoxSize.Width()) / 2), + rRect.Top() + ((rRect.GetHeight() - aBoxSize.Height()) / 2)), + aBoxSize); + + DbCellControl::PaintFieldToCell(rDev, aRect, _rxField, xFormatter); +} + +void DbCheckBox::PaintCell(OutputDevice& rDev, const tools::Rectangle& rRect) +{ + switch (rDev.GetOutDevType()) + { + case OUTDEV_WINDOW: + case OUTDEV_VIRDEV: + DbCellControl::PaintCell(rDev, rRect); + break; + case OUTDEV_PRINTER: + case OUTDEV_PDF: + { + TriState eState = static_cast<CheckBoxControl*>(m_pWindow.get())->GetState(); + + MapMode aResMapMode(MapUnit::Map100thMM); + Size aImageSize = rDev.LogicToPixel(Size(300, 300), aResMapMode); + Size aBrd1Size = rDev.LogicToPixel(Size(20, 20), aResMapMode); + Size aBrd2Size = rDev.LogicToPixel(Size(30, 30), aResMapMode); + int nCheckWidth = rDev.LogicToPixel(Size(20, 20), aResMapMode).Width(); + + tools::Rectangle aStateRect; + aStateRect.SetLeft(rRect.Left() + ((rRect.GetWidth() - aImageSize.Width()) / 2)); + aStateRect.SetTop(rRect.Top() + ((rRect.GetHeight() - aImageSize.Height()) / 2)); + aStateRect.SetRight(aStateRect.Left() + aImageSize.Width() - 1); + aStateRect.SetBottom(aStateRect.Top() + aImageSize.Height() - 1); + + rDev.Push(); + rDev.SetMapMode(); + + rDev.SetLineColor(); + rDev.SetFillColor(COL_BLACK); + rDev.DrawRect(aStateRect); + aStateRect.AdjustLeft(aBrd1Size.Width()); + aStateRect.AdjustTop(aBrd1Size.Height()); + aStateRect.AdjustRight(-aBrd1Size.Width()); + aStateRect.AdjustBottom(-aBrd1Size.Height()); + if (eState == TRISTATE_INDET) + rDev.SetFillColor(COL_LIGHTGRAY); + else + rDev.SetFillColor(COL_WHITE); + rDev.DrawRect(aStateRect); + + if (eState == TRISTATE_TRUE) + { + aStateRect.AdjustLeft(aBrd2Size.Width()); + aStateRect.AdjustTop(aBrd2Size.Height()); + aStateRect.AdjustRight(-aBrd2Size.Width()); + aStateRect.AdjustBottom(-aBrd2Size.Height()); + Point aPos11(aStateRect.TopLeft()); + Point aPos12(aStateRect.BottomRight()); + Point aPos21(aStateRect.TopRight()); + Point aPos22(aStateRect.BottomLeft()); + Point aTempPos11(aPos11); + Point aTempPos12(aPos12); + Point aTempPos21(aPos21); + Point aTempPos22(aPos22); + rDev.SetLineColor(COL_BLACK); + int nDX = 0; + for (int i = 0; i < nCheckWidth; i++) + { + if ( !(i % 2) ) + { + aTempPos11.setX(aPos11.X() + nDX); + aTempPos12.setX(aPos12.X() + nDX); + aTempPos21.setX(aPos21.X() + nDX); + aTempPos22.setX(aPos22.X() + nDX); + } + else + { + nDX++; + aTempPos11.setX(aPos11.X() - nDX); + aTempPos12.setX(aPos12.X() - nDX); + aTempPos21.setX(aPos21.X() - nDX); + aTempPos22.setX(aPos22.X() - nDX); + } + rDev.DrawLine(aTempPos11, aTempPos12); + rDev.DrawLine(aTempPos21, aTempPos22); + } + } + + rDev.Pop(); + break; + } + } +} + +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() )->SetState( static_cast< TriState >( nState ) ); +} + +bool DbCheckBox::commitControl() +{ + m_rColumn.getModel()->setPropertyValue( FM_PROP_STATE, + Any( static_cast<sal_Int16>( static_cast< CheckBoxControl* >( m_pWindow.get() )->GetState() ) ) ); + return true; +} + +OUString DbCheckBox::GetFormatText(const Reference< XColumn >& /*_rxField*/, const Reference< XNumberFormatter >& /*xFormatter*/, const 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)); + + weld::PatternFormatter& rEditFormatter = static_cast<PatternControl*>(m_pWindow.get())->get_formatter(); + rEditFormatter.SetMask(aAsciiEditMask, aLitMask); + rEditFormatter.SetStrictFormat(bStrict); + + weld::PatternFormatter& rPaintFormatter = static_cast<PatternControl*>(m_pPainter.get())->get_formatter(); + rPaintFormatter.SetMask(aAsciiEditMask, aLitMask); + rPaintFormatter.SetStrictFormat(bStrict); +} + +void DbPatternField::Init(BrowserDataWin& rParent, const Reference< XRowSet >& xCursor) +{ + m_rColumn.SetAlignmentFromModel(-1); + + m_pWindow = VclPtr<PatternControl>::Create(&rParent); + m_pPainter= VclPtr<PatternControl>::Create(&rParent); + + Reference< XPropertySet > xModel( m_rColumn.getModel() ); + implAdjustGenericFieldSetting( xModel ); + + DbCellControl::Init( rParent, xCursor ); +} + +CellControllerRef DbPatternField::CreateController() const +{ + return new EditCellController(static_cast<PatternControl*>(m_pWindow.get())); +} + +OUString DbPatternField::impl_formatText( const OUString& _rText ) +{ + weld::PatternFormatter& rPaintFormatter = static_cast<PatternControl*>(m_pPainter.get())->get_formatter(); + rPaintFormatter.get_widget().set_text(_rText); + rPaintFormatter.ReformatAll(); + return rPaintFormatter.get_widget().get_text(); +} + +OUString DbPatternField::GetFormatText(const Reference< css::sdb::XColumn >& _rxField, const Reference< XNumberFormatter >& /*xFormatter*/, const 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 ) +{ + weld::Entry& rEntry = static_cast<PatternControl*>(m_pWindow.get())->get_widget(); + rEntry.set_text(GetFormatText(_rxField, _rxFormatter)); + rEntry.select_region(-1, 0); +} + +void DbPatternField::updateFromModel( Reference< XPropertySet > _rxModel ) +{ + OSL_ENSURE( _rxModel.is() && m_pWindow, "DbPatternField::updateFromModel: invalid call!" ); + + OUString sText; + _rxModel->getPropertyValue( FM_PROP_TEXT ) >>= sText; + + weld::Entry& rEntry = static_cast<PatternControl*>(m_pWindow.get())->get_widget(); + rEntry.set_text(impl_formatText(sText)); + rEntry.select_region(-1, 0); +} + +bool DbPatternField::commitControl() +{ + weld::Entry& rEntry = static_cast<PatternControl*>(m_pWindow.get())->get_widget(); + m_rColumn.getModel()->setPropertyValue(FM_PROP_TEXT, Any(rEntry.get_text())); + return true; +} + +DbSpinField::DbSpinField( DbGridColumn& _rColumn, sal_Int16 _nStandardAlign ) + :DbCellControl( _rColumn ) + ,m_nStandardAlign( _nStandardAlign ) +{ +} + +void DbSpinField::Init(BrowserDataWin& _rParent, const Reference< XRowSet >& _rxCursor) +{ + m_rColumn.SetAlignmentFromModel( m_nStandardAlign ); + + Reference< XPropertySet > xModel( m_rColumn.getModel() ); + + // determine if we need a spinbutton version + bool bSpinButton(false); + if ( ::comphelper::getBOOL( xModel->getPropertyValue( FM_PROP_SPIN ) ) ) + bSpinButton = true; + // create the fields + m_pWindow = createField( &_rParent, bSpinButton, xModel ); + m_pPainter = createField( &_rParent, bSpinButton, 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 ::svt::FormattedFieldCellController(static_cast<FormattedControlBase*>(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 ) ); + + Formatter& rEditFormatter = static_cast<FormattedControlBase*>(m_pWindow.get())->get_formatter(); + rEditFormatter.SetMinValue(nMin); + rEditFormatter.SetMaxValue(nMax); + rEditFormatter.SetSpinSize(nStep); + rEditFormatter.SetStrictFormat(bStrict); + + Formatter& rPaintFormatter = static_cast<FormattedControlBase*>(m_pPainter.get())->get_formatter(); + rPaintFormatter.SetMinValue(nMin); + rPaintFormatter.SetMaxValue(nMax); + rPaintFormatter.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* pImplementation = comphelper::getFromUnoTunnel<SvNumberFormatsSupplierObj>( xSupplier ); + pFormatterUsed = pImplementation ? pImplementation->GetNumberFormatter() : nullptr; + } + if ( nullptr == pFormatterUsed ) + { // the cursor didn't lead to success -> standard + pFormatterUsed = rEditFormatter.StandardFormatter(); + DBG_ASSERT( pFormatterUsed != nullptr, "DbNumericField::implAdjustGenericFieldSetting: no standard formatter given by the numeric field !" ); + } + rEditFormatter.SetFormatter( pFormatterUsed ); + rPaintFormatter.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); + + rEditFormatter.SetFormat( sFormatString, aAppLanguage ); + rPaintFormatter.SetFormat( sFormatString, aAppLanguage ); +} + +VclPtr<svt::ControlBase> DbNumericField::createField(BrowserDataWin* pParent, bool bSpinButton, const Reference<XPropertySet>& /*rxModel*/) +{ + return VclPtr<DoubleNumericControl>::Create(pParent, bSpinButton); +} + +namespace +{ + OUString lcl_setFormattedNumeric_nothrow( FormattedControlBase& _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.get_formatter().SetValue(fValue); + sValue = _rField.get_widget().get_text(); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svx"); + } + } + return sValue; + } +} + +OUString DbNumericField::GetFormatText(const Reference< css::sdb::XColumn >& _rxField, const Reference< css::util::XNumberFormatter >& _rxFormatter, const Color** /*ppColor*/) +{ + return lcl_setFormattedNumeric_nothrow(dynamic_cast<FormattedControlBase&>(*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<FormattedControlBase&>(*m_pWindow), *this, _rxField, _rxFormatter); +} + +void DbNumericField::updateFromModel( Reference< XPropertySet > _rxModel ) +{ + OSL_ENSURE( _rxModel.is() && m_pWindow, "DbNumericField::updateFromModel: invalid call!" ); + + FormattedControlBase* pControl = static_cast<FormattedControlBase*>(m_pWindow.get()); + + double dValue = 0; + if ( _rxModel->getPropertyValue( FM_PROP_VALUE ) >>= dValue ) + { + Formatter& rFormatter = pControl->get_formatter(); + rFormatter.SetValue(dValue); + } + else + pControl->get_widget().set_text(OUString()); +} + +bool DbNumericField::commitControl() +{ + FormattedControlBase* pControl = static_cast<FormattedControlBase*>(m_pWindow.get()); + OUString aText(pControl->get_widget().get_text()); + Any aVal; + + if (!aText.isEmpty()) // not empty + { + Formatter& rFormatter = pControl->get_formatter(); + double fValue = rFormatter.GetValue(); + aVal <<= fValue; + } + m_rColumn.getModel()->setPropertyValue(FM_PROP_VALUE, aVal); + return true; +} + +DbCurrencyField::DbCurrencyField(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 ); + 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; + + sal_Int16 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 ) ) ); + + Formatter& rEditFormatter = static_cast<FormattedControlBase*>(m_pWindow.get())->get_formatter(); + rEditFormatter.SetDecimalDigits(nScale); + rEditFormatter.SetMinValue(nMin); + rEditFormatter.SetMaxValue(nMax); + rEditFormatter.SetSpinSize(nStep); + rEditFormatter.SetStrictFormat(bStrict); + weld::LongCurrencyFormatter& rCurrencyEditFormatter = static_cast<weld::LongCurrencyFormatter&>(rEditFormatter); + rCurrencyEditFormatter.SetUseThousandSep(bThousand); + rCurrencyEditFormatter.SetCurrencySymbol(aStr); + + Formatter& rPaintFormatter = static_cast<FormattedControlBase*>(m_pPainter.get())->get_formatter(); + rPaintFormatter.SetDecimalDigits(nScale); + rPaintFormatter.SetMinValue(nMin); + rPaintFormatter.SetMaxValue(nMax); + rPaintFormatter.SetStrictFormat(bStrict); + weld::LongCurrencyFormatter& rPaintCurrencyFormatter = static_cast<weld::LongCurrencyFormatter&>(rPaintFormatter); + rPaintCurrencyFormatter.SetUseThousandSep(bThousand); + rPaintCurrencyFormatter.SetCurrencySymbol(aStr); +} + +VclPtr<svt::ControlBase> DbCurrencyField::createField(BrowserDataWin* pParent, bool bSpinButton, const Reference< XPropertySet >& /*rxModel*/) +{ + return VclPtr<LongCurrencyControl>::Create(pParent, bSpinButton); +} + +namespace +{ + OUString lcl_setFormattedCurrency_nothrow( FormattedControlBase& _rField, const DbCurrencyField& _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.get_formatter().SetValue(fValue); + sValue = _rField.get_widget().get_text(); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svx"); + } + } + return sValue; + } +} + +OUString DbCurrencyField::GetFormatText(const Reference< css::sdb::XColumn >& _rxField, const Reference< css::util::XNumberFormatter >& _rxFormatter, const Color** /*ppColor*/) +{ + return lcl_setFormattedCurrency_nothrow(dynamic_cast<FormattedControlBase&>(*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<FormattedControlBase&>(*m_pWindow), *this, _rxField, _rxFormatter); +} + +void DbCurrencyField::updateFromModel( Reference< XPropertySet > _rxModel ) +{ + OSL_ENSURE( _rxModel.is() && m_pWindow, "DbCurrencyField::updateFromModel: invalid call!" ); + + FormattedControlBase* pControl = static_cast<FormattedControlBase*>(m_pWindow.get()); + + double dValue = 0; + if ( _rxModel->getPropertyValue( FM_PROP_VALUE ) >>= dValue ) + { + Formatter& rFormatter = pControl->get_formatter(); + rFormatter.SetValue(dValue); + } + else + pControl->get_widget().set_text(OUString()); +} + +bool DbCurrencyField::commitControl() +{ + FormattedControlBase* pControl = static_cast<FormattedControlBase*>(m_pWindow.get()); + OUString aText(pControl->get_widget().get_text()); + Any aVal; + + if (!aText.isEmpty()) // not empty + { + Formatter& rFormatter = pControl->get_formatter(); + double fValue = rFormatter.GetValue(); + 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<svt::ControlBase> DbDateField::createField(BrowserDataWin* pParent, bool bSpinButton, 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 ) ); + // given the apparent inability to set a custom up/down action for a gtk + // spinbutton to have different up/down dates depending on the zone the + // mouse is in, show the dropdown calendar for both the spin or dropdown case + return VclPtr<DateControl>::Create(pParent, bSpinButton || bDropDown); +} + +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 ) ); + + FormattedControlBase* pControl = static_cast<FormattedControlBase*>(m_pWindow.get()); + weld::DateFormatter& rControlFormatter = static_cast<weld::DateFormatter&>(pControl->get_formatter()); + + FormattedControlBase* pPainter = static_cast<FormattedControlBase*>(m_pPainter.get()); + weld::DateFormatter& rPainterFormatter = static_cast<weld::DateFormatter&>(pPainter->get_formatter()); + + Any aCentury = _rxModel->getPropertyValue( FM_PROP_DATE_SHOW_CENTURY ); + if ( aCentury.getValueType().getTypeClass() != TypeClass_VOID ) + { + bool bShowDateCentury = getBOOL( aCentury ); + + rControlFormatter.SetShowDateCentury(bShowDateCentury); + rPainterFormatter.SetShowDateCentury(bShowDateCentury); + } + + rControlFormatter.SetExtDateFormat( static_cast<ExtDateFieldFormat>(nFormat) ); + rControlFormatter.SetMin( aMin ); + rControlFormatter.SetMax( aMax ); + rControlFormatter.SetStrictFormat( bStrict ); + rControlFormatter.EnableEmptyField( true ); + + rPainterFormatter.SetExtDateFormat( static_cast<ExtDateFieldFormat>(nFormat) ); + rPainterFormatter.SetMin( aMin ); + rPainterFormatter.SetMax( aMax ); + rPainterFormatter.SetStrictFormat( bStrict ); + rPainterFormatter.EnableEmptyField( true ); +} + +namespace +{ + OUString lcl_setFormattedDate_nothrow(DateControl& _rField, const Reference<XColumn>& _rxField) + { + OUString sDate; + if ( _rxField.is() ) + { + try + { + css::util::Date aValue = _rxField->getDate(); + if (!_rxField->wasNull()) + { + _rField.SetDate(::Date(aValue.Day, aValue.Month, aValue.Year)); + sDate = _rField.get_widget().get_text(); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svx"); + } + } + return sDate; + } +} + +OUString DbDateField::GetFormatText(const Reference< css::sdb::XColumn >& _rxField, const Reference< css::util::XNumberFormatter >& /*xFormatter*/, const Color** /*ppColor*/) +{ + return lcl_setFormattedDate_nothrow(*static_cast<DateControl*>(m_pPainter.get()), _rxField); +} + +void DbDateField::UpdateFromField(const Reference< css::sdb::XColumn >& _rxField, const Reference< XNumberFormatter >& /*xFormatter*/) +{ + lcl_setFormattedDate_nothrow(*static_cast<DateControl*>(m_pWindow.get()), _rxField); +} + +void DbDateField::updateFromModel( Reference< XPropertySet > _rxModel ) +{ + OSL_ENSURE( _rxModel.is() && m_pWindow, "DbDateField::updateFromModel: invalid call!" ); + + DateControl* pControl = static_cast<DateControl*>(m_pWindow.get()); + + util::Date aDate; + if ( _rxModel->getPropertyValue( FM_PROP_DATE ) >>= aDate ) + pControl->SetDate(::Date(aDate)); + else + pControl->get_widget().set_text(OUString()); +} + +bool DbDateField::commitControl() +{ + FormattedControlBase* pControl = static_cast<FormattedControlBase*>(m_pWindow.get()); + OUString aText(pControl->get_widget().get_text()); + Any aVal; + + if (!aText.isEmpty()) // not empty + { + weld::DateFormatter& rControlFormatter = static_cast<weld::DateFormatter&>(pControl->get_formatter()); + aVal <<= rControlFormatter.GetDate().GetUNODate(); + } + + 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<svt::ControlBase> DbTimeField::createField(BrowserDataWin* pParent, bool bSpinButton, const Reference< XPropertySet >& /*rxModel*/ ) +{ + return VclPtr<TimeControl>::Create(pParent, bSpinButton); +} + +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 ) ); + + FormattedControlBase* pControl = static_cast<FormattedControlBase*>(m_pWindow.get()); + weld::TimeFormatter& rControlFormatter = static_cast<weld::TimeFormatter&>(pControl->get_formatter()); + + rControlFormatter.SetExtFormat(static_cast<ExtTimeFieldFormat>(nFormat)); + rControlFormatter.SetMin(aMin); + rControlFormatter.SetMax(aMax); + rControlFormatter.SetStrictFormat(bStrict); + rControlFormatter.EnableEmptyField(true); + + FormattedControlBase* pPainter = static_cast<FormattedControlBase*>(m_pPainter.get()); + weld::TimeFormatter& rPainterFormatter = static_cast<weld::TimeFormatter&>(pPainter->get_formatter()); + + rPainterFormatter.SetExtFormat(static_cast<ExtTimeFieldFormat>(nFormat)); + rPainterFormatter.SetMin(aMin); + rPainterFormatter.SetMax(aMax); + rPainterFormatter.SetStrictFormat(bStrict); + rPainterFormatter.EnableEmptyField(true); +} + +namespace +{ + OUString lcl_setFormattedTime_nothrow(TimeControl& _rField, const Reference<XColumn>& _rxField) + { + OUString sTime; + if ( _rxField.is() ) + { + try + { + css::util::Time aValue = _rxField->getTime(); + if (!_rxField->wasNull()) + { + static_cast<weld::TimeFormatter&>(_rField.get_formatter()).SetTime( ::tools::Time( aValue ) ); + sTime = _rField.get_widget().get_text(); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svx"); + } + } + return sTime; + } +} + +OUString DbTimeField::GetFormatText(const Reference< css::sdb::XColumn >& _rxField, const Reference< css::util::XNumberFormatter >& /*xFormatter*/, const Color** /*ppColor*/) +{ + return lcl_setFormattedTime_nothrow(*static_cast<TimeControl*>(m_pPainter.get()), _rxField); +} + +void DbTimeField::UpdateFromField(const Reference< css::sdb::XColumn >& _rxField, const Reference< XNumberFormatter >& /*xFormatter*/) +{ + lcl_setFormattedTime_nothrow(*static_cast<TimeControl*>(m_pWindow.get()), _rxField); +} + +void DbTimeField::updateFromModel( Reference< XPropertySet > _rxModel ) +{ + OSL_ENSURE( _rxModel.is() && m_pWindow, "DbTimeField::updateFromModel: invalid call!" ); + + FormattedControlBase* pControl = static_cast<FormattedControlBase*>(m_pWindow.get()); + weld::TimeFormatter& rControlFormatter = static_cast<weld::TimeFormatter&>(pControl->get_formatter()); + + util::Time aTime; + if ( _rxModel->getPropertyValue( FM_PROP_TIME ) >>= aTime ) + rControlFormatter.SetTime(::tools::Time(aTime)); + else + pControl->get_widget().set_text(OUString()); +} + +bool DbTimeField::commitControl() +{ + FormattedControlBase* pControl = static_cast<FormattedControlBase*>(m_pWindow.get()); + OUString aText(pControl->get_widget().get_text()); + Any aVal; + + if (!aText.isEmpty()) // not empty + { + weld::TimeFormatter& rControlFormatter = static_cast<weld::TimeFormatter&>(pControl->get_formatter()); + aVal <<= rControlFormatter.GetTime().GetUNOTime(); + } + + 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(BrowserDataWin& 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, const 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(); + + OUString sOldActive = rComboBox.get_active_text(); + rComboBox.set_entry_text(sText); + rComboBox.select_entry_region(0, -1); + + if (sOldActive != rComboBox.get_active_text()) + pControl->TriggerAuxModify(); +} + +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, Any(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)) + return; + + 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(BrowserDataWin& 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*/, const 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 ]; + + ListBoxControl* pControl = static_cast<ListBoxControl*>(m_pWindow.get()); + weld::ComboBox& rComboBox = pControl->get_widget(); + + int nOldActive = rComboBox.get_active(); + if (nSelection >= 0 && nSelection < rComboBox.get_count()) + rComboBox.set_active(nSelection); + else + rComboBox.set_active(-1); + + if (nOldActive != rComboBox.get_active()) + pControl->TriggerAuxModify(); +} + +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())->SetToggleHdl(Link<weld::CheckButton&,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: + { + // center the checkbox within the space available + CheckBoxControl* pControl = static_cast<CheckBoxControl*>(m_pPainter.get()); + Size aBoxSize = pControl->GetBox().get_preferred_size(); + tools::Rectangle aRect(Point(rRect.Left() + ((rRect.GetWidth() - aBoxSize.Width()) / 2), + rRect.Top() + ((rRect.GetHeight() - aBoxSize.Height()) / 2)), + aBoxSize); + + DbCellControl::PaintCell(rDev, aRect); + 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()) + return; + + 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(BrowserDataWin* 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())->SetToggleHdl(LINK(this, DbFilterField, OnToggle)); + + 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<EditControl>::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); + } + } +} + +void DbFilterField::Init(BrowserDataWin& 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 + m_pWindow->SetEditableReadOnly(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<EditControlBase*>(m_pWindow.get())); + } + return xController; +} + +void DbFilterField::updateFromModel( Reference< XPropertySet > _rxModel ) +{ + OSL_ENSURE( _rxModel.is() && m_pWindow, "DbFilterField::updateFromModel: invalid call!" ); + + OSL_FAIL( "DbFilterField::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; + } + case css::form::FormComponentType::COMBOBOX: + { + aText = static_cast<ComboBoxControl*>(m_pWindow.get())->get_widget().get_active_text(); + break; + } + default: + { + aText = static_cast<EditControlBase*>(m_pWindow.get())->get_widget().get_text(); + break; + } + } + + 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, VCLUnoHelper::GetInterface(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())->SetState(eState); + static_cast<CheckBoxControl*>(m_pPainter.get())->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; + case css::form::FormComponentType::COMBOBOX: + { + static_cast<ComboBoxControl*>(m_pWindow.get())->get_widget().set_entry_text(m_aText); + break; + } + default: + { + static_cast<EditControlBase*>(m_pWindow.get())->get_widget().set_text(m_aText); + break; + } + } + + // 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, Any(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*/, const 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, OnToggle, weld::CheckButton&, void) +{ + TriState eState = static_cast<CheckBoxControl*>(m_pWindow.get())->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(u"", true, nBooleanComparisonMode, aTextBuf); + break; + case TRISTATE_FALSE: + ::dbtools::getBooleanComparisonPredicate(u"", 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() +{ + svt::ControlBase* pEventWindow( getEventWindow() ); + if ( pEventWindow ) + { + pEventWindow->SetFocusInHdl(LINK( this, FmXGridCell, OnFocusGained)); + pEventWindow->SetFocusOutHdl(LINK( this, FmXGridCell, OnFocusLost)); + pEventWindow->SetMousePressHdl(LINK( this, FmXGridCell, OnMousePress)); + pEventWindow->SetMouseReleaseHdl(LINK( this, FmXGridCell, OnMouseRelease)); + pEventWindow->SetMouseMoveHdl(LINK( this, FmXGridCell, OnMouseMove)); + pEventWindow->SetKeyInputHdl( LINK( this, FmXGridCell, OnKeyInput) ); + pEventWindow->SetKeyReleaseHdl( LINK( this, FmXGridCell, OnKeyRelease) ); + } +} + +svt::ControlBase* 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" ); +} + +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 ); +} + +IMPL_LINK_NOARG(FmXGridCell, OnFocusGained, LinkParamNone*, void) +{ + if (!m_aFocusListeners.getLength()) + return; + + awt::FocusEvent aEvent; + aEvent.Source = *this; + aEvent.Temporary = false; + + onFocusGained(aEvent); +} + +IMPL_LINK_NOARG(FmXGridCell, OnFocusLost, LinkParamNone*, void) +{ + if (!m_aFocusListeners.getLength()) + return; + + awt::FocusEvent aEvent; + aEvent.Source = *this; + aEvent.Temporary = false; + + onFocusLost(aEvent); +} + +IMPL_LINK(FmXGridCell, OnMousePress, const MouseEvent&, rEventData, void) +{ + if (!m_aMouseListeners.getLength()) + return; + + awt::MouseEvent aEvent(VCLUnoHelper::createMouseEvent(rEventData, *this)); + m_aMouseListeners.notifyEach(&awt::XMouseListener::mousePressed, aEvent); +} + +IMPL_LINK(FmXGridCell, OnMouseRelease, const MouseEvent&, rEventData, void) +{ + if (!m_aMouseListeners.getLength()) + return; + + awt::MouseEvent aEvent(VCLUnoHelper::createMouseEvent(rEventData, *this)); + m_aMouseListeners.notifyEach(&awt::XMouseListener::mouseReleased, aEvent); +} + +IMPL_LINK(FmXGridCell, OnMouseMove, const MouseEvent&, rMouseEvent, void) +{ + 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 ); + } + } +} + +IMPL_LINK(FmXGridCell, OnKeyInput, const KeyEvent&, rEventData, void) +{ + if (!m_aKeyListeners.getLength()) + return; + + awt::KeyEvent aEvent(VCLUnoHelper::createKeyEvent(rEventData, *this)); + m_aKeyListeners.notifyEach(&awt::XKeyListener::keyPressed, aEvent); +} + +IMPL_LINK(FmXGridCell, OnKeyRelease, const KeyEvent&, rEventData, void) +{ + if (!m_aKeyListeners.getLength()) + return; + + awt::KeyEvent aEvent(VCLUnoHelper::createKeyEvent(rEventData, *this)); + m_aKeyListeners.notifyEach(&awt::XKeyListener::keyReleased, aEvent); +} + +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_bIsMultiLineText(false) +{ +} + +void FmXTextCell::PaintFieldToCell(OutputDevice& rDev, + const tools::Rectangle& rRect, + const Reference< css::sdb::XColumn >& _rxField, + const Reference< XNumberFormatter >& xFormatter) +{ + DrawTextFlags nStyle = DrawTextFlags::Clip; + if ( ( rDev.GetOutDevType() == OUTDEV_WINDOW ) && !rDev.GetOwnerWindow()->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; + } + + if (!m_bIsMultiLineText) + nStyle |= DrawTextFlags::VCenter; + else + nStyle |= DrawTextFlags::Top | DrawTextFlags::MultiLine | DrawTextFlags::WordBreak; + + try + { + const 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(); + m_bIsMultiLineText = pTextField->IsMultiLineEdit(); + } + else + { + m_pEditImplementation = new EntryImplementation(static_cast<EditControlBase&>(m_pCellControl->GetWindow())); + m_bOwnEditImplementation = true; + } + m_pEditImplementation->SetAuxModifyHdl(LINK(this, FmXEditCell, ModifyHdl)); +} + +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); + + 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 ); + } +} + +IMPL_LINK_NOARG(FmXEditCell, ModifyHdl, LinkParamNone*, void) +{ + if (m_aTextListeners.getLength()) + onTextChanged(); +} + +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() ) ) +{ + m_pBox->SetAuxModifyHdl(LINK(this, FmXCheckBoxCell, ModifyHdl)); +} + +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); + + m_pBox->SetToggleHdl(Link<weld::CheckButton&,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; +} + +IMPL_LINK_NOARG(FmXCheckBoxCell, ModifyHdl, LinkParamNone*, void) +{ + // 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 ); + } +} + +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<bool,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 ); + + if (m_pBox) + { + UpdateFromColumn(); + weld::ComboBox& rBox = m_pBox->get_widget(); + auto nActive = rBox.get_active(); + if (nActive != -1) + { + return { o3tl::narrowing<short>(nActive) }; + } + } + return {}; +} + +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 ); + + if (m_pBox) + { + UpdateFromColumn(); + weld::ComboBox& rBox = m_pBox->get_widget(); + auto nActive = rBox.get_active(); + if (nActive != -1) + { + return { rBox.get_text(nActive) }; + } + } + return {}; +} + +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(FmXListBoxCell, ChangedHdl, bool, bInteractive, void) +{ + if (!m_pBox) + return; + + weld::ComboBox& rBox = m_pBox->get_widget(); + + if (bInteractive && !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() +{ + css::awt::ActionEvent aEvent; + aEvent.Source = *this; + weld::ComboBox& rBox = m_pBox->get_widget(); + aEvent.ActionCommand = rBox.get_active_text(); + + m_aActionListeners.notifyEach( &css::awt::XActionListener::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<bool,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(FmXComboBoxCell, ChangedHdl, bool, bInteractive, void) +{ + if (!m_pComboBox) + return; + + weld::ComboBox& rComboBox = m_pComboBox->get_widget(); + + if (bInteractive && !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 ) +{ + return comphelper::getSomethingImpl(_rIdentifier, this); +} + +const Sequence<sal_Int8>& FmXFilterCell::getUnoTunnelId() +{ + static const comphelper::UnoIdInit theFmXFilterCellUnoTunnelId; + return theFmXFilterCellUnoTunnelId.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) +{ + css::awt::TextEvent aEvt; + aEvt.Source = *this; + m_aTextListeners.notifyEach( &css::awt::XTextListener::textChanged, aEvt ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |