office-gobmx/framework/source/uielement/spinfieldtoolbarcontroller.cxx
Mike Kaganski 8b6d167608 Use of O(U)StringNumber for float/double is actually a pessimisation
They use rtl_(u)str_valueOf(Float|Double), to fill the buffer, and
the latter use doubleToString, which creates an rtl_(u)String, copies
to the buffer, and releases the rtl_(u)String.

So instead just use the rtl_(u)String from rtl_math_doubleTo(U)String
directly. Even when the end result is not needed as O(U)String, this
would avoid an extra copy step. Also, this avoids separate
LIBO_INTERNAL_ONLY implementations.

Change-Id: Ib1d9ecebd7876dfff7dc758f89ee4c1536647a50
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/150150
Tested-by: Jenkins
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
2023-04-10 16:36:09 +02:00

452 lines
14 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <sal/config.h>
#include <stdio.h>
#include <uielement/spinfieldtoolbarcontroller.hxx>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <comphelper/propertyvalue.hxx>
#include <svtools/toolboxcontroller.hxx>
#include <vcl/InterimItemWindow.hxx>
#include <vcl/event.hxx>
#include <vcl/formatter.hxx>
#include <vcl/svapp.hxx>
#include <vcl/toolbox.hxx>
#include <o3tl/char16_t2wchar_t.hxx>
using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::frame;
using namespace ::com::sun::star::util;
namespace framework
{
// Wrapper class to notify controller about events from combobox.
// Unfortunaltly the events are notified through virtual methods instead
// of Listeners.
class SpinfieldControl final : public InterimItemWindow
{
public:
SpinfieldControl(vcl::Window* pParent, SpinfieldToolbarController* pSpinfieldToolbarController);
virtual ~SpinfieldControl() override;
virtual void dispose() override;
Formatter& GetFormatter()
{
return m_xWidget->GetFormatter();
}
OUString get_entry_text() const { return m_xWidget->get_text(); }
DECL_LINK(ValueChangedHdl, weld::FormattedSpinButton&, void);
DECL_LINK(FormatOutputHdl, LinkParamNone*, bool);
DECL_LINK(ParseInputHdl, sal_Int64*, TriState);
DECL_LINK(ModifyHdl, weld::Entry&, void);
DECL_LINK(ActivateHdl, weld::Entry&, bool);
DECL_LINK(FocusInHdl, weld::Widget&, void);
DECL_LINK(FocusOutHdl, weld::Widget&, void);
DECL_LINK(KeyInputHdl, const ::KeyEvent&, bool);
private:
std::unique_ptr<weld::FormattedSpinButton> m_xWidget;
SpinfieldToolbarController* m_pSpinfieldToolbarController;
};
SpinfieldControl::SpinfieldControl(vcl::Window* pParent, SpinfieldToolbarController* pSpinfieldToolbarController)
: InterimItemWindow(pParent, "svt/ui/spinfieldcontrol.ui", "SpinFieldControl")
, m_xWidget(m_xBuilder->weld_formatted_spin_button("spinbutton"))
, m_pSpinfieldToolbarController(pSpinfieldToolbarController)
{
InitControlBase(m_xWidget.get());
m_xWidget->connect_focus_in(LINK(this, SpinfieldControl, FocusInHdl));
m_xWidget->connect_focus_out(LINK(this, SpinfieldControl, FocusOutHdl));
Formatter& rFormatter = m_xWidget->GetFormatter();
rFormatter.SetOutputHdl(LINK(this, SpinfieldControl, FormatOutputHdl));
rFormatter.SetInputHdl(LINK(this, SpinfieldControl, ParseInputHdl));
m_xWidget->connect_value_changed(LINK(this, SpinfieldControl, ValueChangedHdl));
m_xWidget->connect_changed(LINK(this, SpinfieldControl, ModifyHdl));
m_xWidget->connect_activate(LINK(this, SpinfieldControl, ActivateHdl));
m_xWidget->connect_key_press(LINK(this, SpinfieldControl, KeyInputHdl));
// so a later narrow size request can stick
m_xWidget->set_width_chars(3);
m_xWidget->set_size_request(42, -1);
SetSizePixel(get_preferred_size());
}
IMPL_LINK(SpinfieldControl, KeyInputHdl, const ::KeyEvent&, rKEvt, bool)
{
return ChildKeyInput(rKEvt);
}
IMPL_LINK(SpinfieldControl, ParseInputHdl, sal_Int64*, result, TriState)
{
*result = m_xWidget->get_text().toDouble() * weld::SpinButton::Power10(m_xWidget->GetFormatter().GetDecimalDigits());
return TRISTATE_TRUE;
}
SpinfieldControl::~SpinfieldControl()
{
disposeOnce();
}
void SpinfieldControl::dispose()
{
m_pSpinfieldToolbarController = nullptr;
m_xWidget.reset();
InterimItemWindow::dispose();
}
IMPL_LINK_NOARG(SpinfieldControl, ValueChangedHdl, weld::FormattedSpinButton&, void)
{
if (m_pSpinfieldToolbarController)
m_pSpinfieldToolbarController->execute(0);
}
IMPL_LINK_NOARG(SpinfieldControl, ModifyHdl, weld::Entry&, void)
{
if (m_pSpinfieldToolbarController)
m_pSpinfieldToolbarController->Modify();
}
IMPL_LINK_NOARG(SpinfieldControl, FocusInHdl, weld::Widget&, void)
{
if (m_pSpinfieldToolbarController)
m_pSpinfieldToolbarController->GetFocus();
}
IMPL_LINK_NOARG(SpinfieldControl, FocusOutHdl, weld::Widget&, void)
{
if (m_pSpinfieldToolbarController)
m_pSpinfieldToolbarController->LoseFocus();
}
IMPL_LINK_NOARG(SpinfieldControl, ActivateHdl, weld::Entry&, bool)
{
bool bConsumed = false;
if (m_pSpinfieldToolbarController)
{
m_pSpinfieldToolbarController->Activate();
bConsumed = true;
}
return bConsumed;
}
IMPL_LINK_NOARG(SpinfieldControl, FormatOutputHdl, LinkParamNone*, bool)
{
OUString aText = m_pSpinfieldToolbarController->FormatOutputString(m_xWidget->GetFormatter().GetValue());
m_xWidget->set_text(aText);
return true;
}
SpinfieldToolbarController::SpinfieldToolbarController(
const Reference< XComponentContext >& rxContext,
const Reference< XFrame >& rFrame,
ToolBox* pToolbar,
ToolBoxItemId nID,
sal_Int32 nWidth,
const OUString& aCommand ) :
ComplexToolbarController( rxContext, rFrame, pToolbar, nID, aCommand )
, m_bFloat( false )
, m_nMax( 0.0 )
, m_nMin( 0.0 )
, m_nValue( 0.0 )
, m_nStep( 0.0 )
, m_pSpinfieldControl( nullptr )
{
m_pSpinfieldControl = VclPtr<SpinfieldControl>::Create(m_xToolbar, this);
if ( nWidth == 0 )
nWidth = 100;
// SpinFieldControl ctor has set a suitable height already
auto nHeight = m_pSpinfieldControl->GetSizePixel().Height();
m_pSpinfieldControl->SetSizePixel( ::Size( nWidth, nHeight ));
m_xToolbar->SetItemWindow( m_nID, m_pSpinfieldControl );
}
SpinfieldToolbarController::~SpinfieldToolbarController()
{
}
void SAL_CALL SpinfieldToolbarController::dispose()
{
SolarMutexGuard aSolarMutexGuard;
m_xToolbar->SetItemWindow( m_nID, nullptr );
m_pSpinfieldControl.disposeAndClear();
ComplexToolbarController::dispose();
}
Sequence<PropertyValue> SpinfieldToolbarController::getExecuteArgs(sal_Int16 KeyModifier) const
{
OUString aSpinfieldText = m_pSpinfieldControl->get_entry_text();
// Add key modifier to argument list
auto aArgs0 = comphelper::makePropertyValue("KeyModifier", KeyModifier);
auto aArgs1 = comphelper::makePropertyValue("Value", m_bFloat ? Any(aSpinfieldText.toDouble())
: Any(aSpinfieldText.toInt32()));
return { aArgs0, aArgs1 };
}
void SpinfieldToolbarController::Modify()
{
notifyTextChanged(m_pSpinfieldControl->get_entry_text());
}
void SpinfieldToolbarController::GetFocus()
{
notifyFocusGet();
}
void SpinfieldToolbarController::LoseFocus()
{
notifyFocusLost();
}
void SpinfieldToolbarController::Activate()
{
// Call execute only with non-empty text
if (!m_pSpinfieldControl->get_entry_text().isEmpty())
execute(0);
}
void SpinfieldToolbarController::executeControlCommand( const css::frame::ControlCommand& rControlCommand )
{
OUString aValue;
OUString aMax;
OUString aMin;
OUString aStep;
bool bFloatValue( false );
if ( rControlCommand.Command == "SetStep" )
{
for ( auto const & arg : rControlCommand.Arguments )
{
if ( arg.Name == "Step" )
{
sal_Int32 nValue;
double fValue;
bool bFloat( false );
if ( impl_getValue( arg.Value, nValue, fValue, bFloat ))
aStep = bFloat ? OUString::number( fValue ) :
OUString( OUString::number( nValue ));
break;
}
}
}
else if ( rControlCommand.Command == "SetValue" )
{
for ( auto const & arg : rControlCommand.Arguments )
{
if ( arg.Name == "Value" )
{
sal_Int32 nValue;
double fValue;
bool bFloat( false );
if ( impl_getValue( arg.Value, nValue, fValue, bFloat ))
{
aValue = bFloat ? OUString::number( fValue ) :
OUString( OUString::number( nValue ));
bFloatValue = bFloat;
}
break;
}
}
}
else if ( rControlCommand.Command == "SetValues" )
{
for ( auto const & arg : rControlCommand.Arguments )
{
sal_Int32 nValue;
double fValue;
bool bFloat( false );
OUString aName = arg.Name;
if ( impl_getValue( arg.Value, nValue, fValue, bFloat ))
{
if ( aName == "Value" )
{
aValue = bFloat ? OUString::number( fValue ) :
OUString( OUString::number( nValue ));
bFloatValue = bFloat;
}
else if ( aName == "Step" )
aStep = bFloat ? OUString::number( fValue ) :
OUString( OUString::number( nValue ));
else if ( aName == "LowerLimit" )
aMin = bFloat ? OUString::number( fValue ) :
OUString( OUString::number( nValue ));
else if ( aName == "UpperLimit" )
aMax = bFloat ? OUString::number( fValue ) :
OUString( OUString::number( nValue ));
}
else if ( aName == "OutputFormat" )
arg.Value >>= m_aOutFormat;
}
}
else if ( rControlCommand.Command == "SetLowerLimit" )
{
for ( auto const & arg : rControlCommand.Arguments )
{
if ( arg.Name == "LowerLimit" )
{
sal_Int32 nValue;
double fValue;
bool bFloat( false );
if ( impl_getValue( arg.Value, nValue, fValue, bFloat ))
aMin = bFloat ? OUString::number( fValue ) :
OUString( OUString::number( nValue ));
break;
}
}
}
else if ( rControlCommand.Command == "SetUpperLimit" )
{
for ( auto const & arg : rControlCommand.Arguments )
{
if ( arg.Name == "UpperLimit" )
{
sal_Int32 nValue;
double fValue;
bool bFloat( false );
if ( impl_getValue( arg.Value, nValue, fValue, bFloat ))
aMax = bFloat ? OUString::number( fValue ) :
OUString( OUString::number( nValue ));
break;
}
}
}
else if ( rControlCommand.Command == "SetOutputFormat" )
{
for ( auto const & arg : rControlCommand.Arguments )
{
if ( arg.Name == "OutputFormat" )
{
arg.Value >>= m_aOutFormat;
break;
}
}
}
Formatter& rFormatter = m_pSpinfieldControl->GetFormatter();
// Check values and set members
if (bFloatValue)
rFormatter.SetDecimalDigits(2);
if ( !aValue.isEmpty() )
{
m_bFloat = bFloatValue;
m_nValue = aValue.toDouble();
rFormatter.SetValue(m_nValue);
}
if ( !aMax.isEmpty() )
{
m_nMax = aMax.toDouble();
rFormatter.SetMaxValue(m_nMax);
}
if ( !aMin.isEmpty() )
{
m_nMin = aMin.toDouble();
rFormatter.SetMinValue(m_nMin);
}
if ( !aStep.isEmpty() )
{
m_nStep = aStep.toDouble();
rFormatter.SetSpinSize(m_nStep);
}
}
bool SpinfieldToolbarController::impl_getValue(
const Any& rAny, sal_Int32& nValue, double& fValue, bool& bFloat )
{
using ::com::sun::star::uno::TypeClass;
bool bValueValid( false );
bFloat = false;
TypeClass aTypeClass = rAny.getValueTypeClass();
if (( aTypeClass == TypeClass( typelib_TypeClass_LONG )) ||
( aTypeClass == TypeClass( typelib_TypeClass_SHORT )) ||
( aTypeClass == TypeClass( typelib_TypeClass_BYTE )))
bValueValid = rAny >>= nValue;
else if (( aTypeClass == TypeClass( typelib_TypeClass_FLOAT )) ||
( aTypeClass == TypeClass( typelib_TypeClass_DOUBLE )))
{
bValueValid = rAny >>= fValue;
bFloat = true;
}
return bValueValid;
}
OUString SpinfieldToolbarController::FormatOutputString( double fValue )
{
if ( m_aOutFormat.isEmpty() )
{
if ( m_bFloat )
return OUString::number( fValue );
else
return OUString::number( sal_Int32( fValue ));
}
else
{
#ifdef _WIN32
sal_Unicode aBuffer[128];
aBuffer[0] = 0;
if ( m_bFloat )
_snwprintf( o3tl::toW(aBuffer), SAL_N_ELEMENTS(aBuffer), o3tl::toW(m_aOutFormat.getStr()), fValue );
else
_snwprintf( o3tl::toW(aBuffer), SAL_N_ELEMENTS(aBuffer), o3tl::toW(m_aOutFormat.getStr()), sal_Int32( fValue ));
return OUString(aBuffer);
#else
// Currently we have no support for a format string using sal_Unicode. wchar_t
// is 32 bit on Unix platform!
char aBuffer[128];
OString aFormat = OUStringToOString( m_aOutFormat, osl_getThreadTextEncoding() );
if ( m_bFloat )
snprintf( aBuffer, 128, aFormat.getStr(), fValue );
else
snprintf( aBuffer, 128, aFormat.getStr(), static_cast<tools::Long>( fValue ));
sal_Int32 nSize = strlen( aBuffer );
std::string_view aTmp( aBuffer, nSize );
return OStringToOUString( aTmp, osl_getThreadTextEncoding() );
#endif
}
}
} // namespace
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */