Avoid potential negative array index access to cached text.
[LibreOffice.git] / framework / source / uielement / spinfieldtoolbarcontroller.cxx
blob9ba295f309329a2904d9ed6ec2946f6360af1a94
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <stdio.h>
24 #include <uielement/spinfieldtoolbarcontroller.hxx>
26 #include <com/sun/star/beans/PropertyValue.hpp>
28 #include <comphelper/propertyvalue.hxx>
29 #include <svtools/toolboxcontroller.hxx>
30 #include <vcl/InterimItemWindow.hxx>
31 #include <vcl/event.hxx>
32 #include <vcl/formatter.hxx>
33 #include <vcl/svapp.hxx>
34 #include <vcl/toolbox.hxx>
35 #include <o3tl/char16_t2wchar_t.hxx>
37 using namespace ::com::sun::star;
38 using namespace ::com::sun::star::uno;
39 using namespace ::com::sun::star::beans;
40 using namespace ::com::sun::star::lang;
41 using namespace ::com::sun::star::frame;
42 using namespace ::com::sun::star::util;
44 namespace framework
47 // Wrapper class to notify controller about events from combobox.
48 // Unfortunaltly the events are notified through virtual methods instead
49 // of Listeners.
51 class SpinfieldControl final : public InterimItemWindow
53 public:
54 SpinfieldControl(vcl::Window* pParent, SpinfieldToolbarController* pSpinfieldToolbarController);
55 virtual ~SpinfieldControl() override;
56 virtual void dispose() override;
58 Formatter& GetFormatter()
60 return m_xWidget->GetFormatter();
63 OUString get_entry_text() const { return m_xWidget->get_text(); }
65 DECL_LINK(ValueChangedHdl, weld::FormattedSpinButton&, void);
66 DECL_LINK(FormatOutputHdl, LinkParamNone*, bool);
67 DECL_LINK(ParseInputHdl, sal_Int64*, TriState);
68 DECL_LINK(ModifyHdl, weld::Entry&, void);
69 DECL_LINK(ActivateHdl, weld::Entry&, bool);
70 DECL_LINK(FocusInHdl, weld::Widget&, void);
71 DECL_LINK(FocusOutHdl, weld::Widget&, void);
72 DECL_LINK(KeyInputHdl, const ::KeyEvent&, bool);
74 private:
75 std::unique_ptr<weld::FormattedSpinButton> m_xWidget;
76 SpinfieldToolbarController* m_pSpinfieldToolbarController;
79 SpinfieldControl::SpinfieldControl(vcl::Window* pParent, SpinfieldToolbarController* pSpinfieldToolbarController)
80 : InterimItemWindow(pParent, "svt/ui/spinfieldcontrol.ui", "SpinFieldControl")
81 , m_xWidget(m_xBuilder->weld_formatted_spin_button("spinbutton"))
82 , m_pSpinfieldToolbarController(pSpinfieldToolbarController)
84 InitControlBase(m_xWidget.get());
86 m_xWidget->connect_focus_in(LINK(this, SpinfieldControl, FocusInHdl));
87 m_xWidget->connect_focus_out(LINK(this, SpinfieldControl, FocusOutHdl));
88 Formatter& rFormatter = m_xWidget->GetFormatter();
89 rFormatter.SetOutputHdl(LINK(this, SpinfieldControl, FormatOutputHdl));
90 rFormatter.SetInputHdl(LINK(this, SpinfieldControl, ParseInputHdl));
91 m_xWidget->connect_value_changed(LINK(this, SpinfieldControl, ValueChangedHdl));
92 m_xWidget->connect_changed(LINK(this, SpinfieldControl, ModifyHdl));
93 m_xWidget->connect_activate(LINK(this, SpinfieldControl, ActivateHdl));
94 m_xWidget->connect_key_press(LINK(this, SpinfieldControl, KeyInputHdl));
96 // so a later narrow size request can stick
97 m_xWidget->set_width_chars(3);
98 m_xWidget->set_size_request(42, -1);
100 SetSizePixel(get_preferred_size());
103 IMPL_LINK(SpinfieldControl, KeyInputHdl, const ::KeyEvent&, rKEvt, bool)
105 return ChildKeyInput(rKEvt);
108 IMPL_LINK(SpinfieldControl, ParseInputHdl, sal_Int64*, result, TriState)
110 *result = m_xWidget->get_text().toDouble() * weld::SpinButton::Power10(m_xWidget->GetFormatter().GetDecimalDigits());
111 return TRISTATE_TRUE;
114 SpinfieldControl::~SpinfieldControl()
116 disposeOnce();
119 void SpinfieldControl::dispose()
121 m_pSpinfieldToolbarController = nullptr;
122 m_xWidget.reset();
123 InterimItemWindow::dispose();
126 IMPL_LINK_NOARG(SpinfieldControl, ValueChangedHdl, weld::FormattedSpinButton&, void)
128 if (m_pSpinfieldToolbarController)
129 m_pSpinfieldToolbarController->execute(0);
132 IMPL_LINK_NOARG(SpinfieldControl, ModifyHdl, weld::Entry&, void)
134 if (m_pSpinfieldToolbarController)
135 m_pSpinfieldToolbarController->Modify();
138 IMPL_LINK_NOARG(SpinfieldControl, FocusInHdl, weld::Widget&, void)
140 if (m_pSpinfieldToolbarController)
141 m_pSpinfieldToolbarController->GetFocus();
144 IMPL_LINK_NOARG(SpinfieldControl, FocusOutHdl, weld::Widget&, void)
146 if (m_pSpinfieldToolbarController)
147 m_pSpinfieldToolbarController->LoseFocus();
150 IMPL_LINK_NOARG(SpinfieldControl, ActivateHdl, weld::Entry&, bool)
152 bool bConsumed = false;
153 if (m_pSpinfieldToolbarController)
155 m_pSpinfieldToolbarController->Activate();
156 bConsumed = true;
158 return bConsumed;
161 IMPL_LINK_NOARG(SpinfieldControl, FormatOutputHdl, LinkParamNone*, bool)
163 OUString aText = m_pSpinfieldToolbarController->FormatOutputString(m_xWidget->GetFormatter().GetValue());
164 m_xWidget->set_text(aText);
165 return true;
168 SpinfieldToolbarController::SpinfieldToolbarController(
169 const Reference< XComponentContext >& rxContext,
170 const Reference< XFrame >& rFrame,
171 ToolBox* pToolbar,
172 ToolBoxItemId nID,
173 sal_Int32 nWidth,
174 const OUString& aCommand ) :
175 ComplexToolbarController( rxContext, rFrame, pToolbar, nID, aCommand )
176 , m_bFloat( false )
177 , m_nMax( 0.0 )
178 , m_nMin( 0.0 )
179 , m_nValue( 0.0 )
180 , m_nStep( 0.0 )
181 , m_pSpinfieldControl( nullptr )
183 m_pSpinfieldControl = VclPtr<SpinfieldControl>::Create(m_xToolbar, this);
184 if ( nWidth == 0 )
185 nWidth = 100;
187 // SpinFieldControl ctor has set a suitable height already
188 auto nHeight = m_pSpinfieldControl->GetSizePixel().Height();
190 m_pSpinfieldControl->SetSizePixel( ::Size( nWidth, nHeight ));
191 m_xToolbar->SetItemWindow( m_nID, m_pSpinfieldControl );
194 SpinfieldToolbarController::~SpinfieldToolbarController()
198 void SAL_CALL SpinfieldToolbarController::dispose()
200 SolarMutexGuard aSolarMutexGuard;
202 m_xToolbar->SetItemWindow( m_nID, nullptr );
203 m_pSpinfieldControl.disposeAndClear();
205 ComplexToolbarController::dispose();
208 Sequence<PropertyValue> SpinfieldToolbarController::getExecuteArgs(sal_Int16 KeyModifier) const
210 OUString aSpinfieldText = m_pSpinfieldControl->get_entry_text();
212 // Add key modifier to argument list
213 auto aArgs0 = comphelper::makePropertyValue("KeyModifier", KeyModifier);
214 auto aArgs1 = comphelper::makePropertyValue("Value", m_bFloat ? Any(aSpinfieldText.toDouble())
215 : Any(aSpinfieldText.toInt32()));
216 return { aArgs0, aArgs1 };
219 void SpinfieldToolbarController::Modify()
221 notifyTextChanged(m_pSpinfieldControl->get_entry_text());
224 void SpinfieldToolbarController::GetFocus()
226 notifyFocusGet();
229 void SpinfieldToolbarController::LoseFocus()
231 notifyFocusLost();
234 void SpinfieldToolbarController::Activate()
236 // Call execute only with non-empty text
237 if (!m_pSpinfieldControl->get_entry_text().isEmpty())
238 execute(0);
241 void SpinfieldToolbarController::executeControlCommand( const css::frame::ControlCommand& rControlCommand )
243 OUString aValue;
244 OUString aMax;
245 OUString aMin;
246 OUString aStep;
247 bool bFloatValue( false );
249 if ( rControlCommand.Command == "SetStep" )
251 for ( auto const & arg : rControlCommand.Arguments )
253 if ( arg.Name == "Step" )
255 sal_Int32 nValue;
256 double fValue;
257 bool bFloat( false );
258 if ( impl_getValue( arg.Value, nValue, fValue, bFloat ))
259 aStep = bFloat ? OUString::number( fValue ) :
260 OUString( OUString::number( nValue ));
261 break;
265 else if ( rControlCommand.Command == "SetValue" )
267 for ( auto const & arg : rControlCommand.Arguments )
269 if ( arg.Name == "Value" )
271 sal_Int32 nValue;
272 double fValue;
273 bool bFloat( false );
275 if ( impl_getValue( arg.Value, nValue, fValue, bFloat ))
277 aValue = bFloat ? OUString::number( fValue ) :
278 OUString( OUString::number( nValue ));
279 bFloatValue = bFloat;
281 break;
285 else if ( rControlCommand.Command == "SetValues" )
287 for ( auto const & arg : rControlCommand.Arguments )
289 sal_Int32 nValue;
290 double fValue;
291 bool bFloat( false );
293 OUString aName = arg.Name;
294 if ( impl_getValue( arg.Value, nValue, fValue, bFloat ))
296 if ( aName == "Value" )
298 aValue = bFloat ? OUString::number( fValue ) :
299 OUString( OUString::number( nValue ));
300 bFloatValue = bFloat;
302 else if ( aName == "Step" )
303 aStep = bFloat ? OUString::number( fValue ) :
304 OUString( OUString::number( nValue ));
305 else if ( aName == "LowerLimit" )
306 aMin = bFloat ? OUString::number( fValue ) :
307 OUString( OUString::number( nValue ));
308 else if ( aName == "UpperLimit" )
309 aMax = bFloat ? OUString::number( fValue ) :
310 OUString( OUString::number( nValue ));
312 else if ( aName == "OutputFormat" )
313 arg.Value >>= m_aOutFormat;
316 else if ( rControlCommand.Command == "SetLowerLimit" )
318 for ( auto const & arg : rControlCommand.Arguments )
320 if ( arg.Name == "LowerLimit" )
322 sal_Int32 nValue;
323 double fValue;
324 bool bFloat( false );
325 if ( impl_getValue( arg.Value, nValue, fValue, bFloat ))
326 aMin = bFloat ? OUString::number( fValue ) :
327 OUString( OUString::number( nValue ));
328 break;
332 else if ( rControlCommand.Command == "SetUpperLimit" )
334 for ( auto const & arg : rControlCommand.Arguments )
336 if ( arg.Name == "UpperLimit" )
338 sal_Int32 nValue;
339 double fValue;
340 bool bFloat( false );
341 if ( impl_getValue( arg.Value, nValue, fValue, bFloat ))
342 aMax = bFloat ? OUString::number( fValue ) :
343 OUString( OUString::number( nValue ));
344 break;
348 else if ( rControlCommand.Command == "SetOutputFormat" )
350 for ( auto const & arg : rControlCommand.Arguments )
352 if ( arg.Name == "OutputFormat" )
354 arg.Value >>= m_aOutFormat;
355 break;
360 Formatter& rFormatter = m_pSpinfieldControl->GetFormatter();
362 // Check values and set members
363 if (bFloatValue)
364 rFormatter.SetDecimalDigits(2);
365 if ( !aValue.isEmpty() )
367 m_bFloat = bFloatValue;
368 m_nValue = aValue.toDouble();
369 rFormatter.SetValue(m_nValue);
371 if ( !aMax.isEmpty() )
373 m_nMax = aMax.toDouble();
374 rFormatter.SetMaxValue(m_nMax);
376 if ( !aMin.isEmpty() )
378 m_nMin = aMin.toDouble();
379 rFormatter.SetMinValue(m_nMin);
381 if ( !aStep.isEmpty() )
383 m_nStep = aStep.toDouble();
384 rFormatter.SetSpinSize(m_nStep);
388 bool SpinfieldToolbarController::impl_getValue(
389 const Any& rAny, sal_Int32& nValue, double& fValue, bool& bFloat )
391 using ::com::sun::star::uno::TypeClass;
393 bool bValueValid( false );
395 bFloat = false;
396 TypeClass aTypeClass = rAny.getValueTypeClass();
397 if (( aTypeClass == TypeClass( typelib_TypeClass_LONG )) ||
398 ( aTypeClass == TypeClass( typelib_TypeClass_SHORT )) ||
399 ( aTypeClass == TypeClass( typelib_TypeClass_BYTE )))
400 bValueValid = rAny >>= nValue;
401 else if (( aTypeClass == TypeClass( typelib_TypeClass_FLOAT )) ||
402 ( aTypeClass == TypeClass( typelib_TypeClass_DOUBLE )))
404 bValueValid = rAny >>= fValue;
405 bFloat = true;
408 return bValueValid;
411 OUString SpinfieldToolbarController::FormatOutputString( double fValue )
413 if ( m_aOutFormat.isEmpty() )
415 if ( m_bFloat )
416 return OUString::number( fValue );
417 else
418 return OUString::number( sal_Int32( fValue ));
420 else
422 #ifdef _WIN32
423 sal_Unicode aBuffer[128];
425 aBuffer[0] = 0;
426 if ( m_bFloat )
427 _snwprintf( o3tl::toW(aBuffer), SAL_N_ELEMENTS(aBuffer), o3tl::toW(m_aOutFormat.getStr()), fValue );
428 else
429 _snwprintf( o3tl::toW(aBuffer), SAL_N_ELEMENTS(aBuffer), o3tl::toW(m_aOutFormat.getStr()), sal_Int32( fValue ));
431 return OUString(aBuffer);
432 #else
433 // Currently we have no support for a format string using sal_Unicode. wchar_t
434 // is 32 bit on Unix platform!
435 char aBuffer[128];
437 OString aFormat = OUStringToOString( m_aOutFormat, osl_getThreadTextEncoding() );
438 if ( m_bFloat )
439 snprintf( aBuffer, 128, aFormat.getStr(), fValue );
440 else
441 snprintf( aBuffer, 128, aFormat.getStr(), static_cast<tools::Long>( fValue ));
443 sal_Int32 nSize = strlen( aBuffer );
444 std::string_view aTmp( aBuffer, nSize );
445 return OStringToOUString( aTmp, osl_getThreadTextEncoding() );
446 #endif
450 } // namespace
452 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */