1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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::frame
;
45 // Wrapper class to notify controller about events from combobox.
46 // Unfortunaltly the events are notified through virtual methods instead
49 class SpinfieldControl final
: public InterimItemWindow
52 SpinfieldControl(vcl::Window
* pParent
, SpinfieldToolbarController
* pSpinfieldToolbarController
);
53 virtual ~SpinfieldControl() override
;
54 virtual void dispose() override
;
56 Formatter
& GetFormatter()
58 return m_xWidget
->GetFormatter();
61 OUString
get_entry_text() const { return m_xWidget
->get_text(); }
63 DECL_LINK(ValueChangedHdl
, weld::FormattedSpinButton
&, void);
64 DECL_LINK(FormatOutputHdl
, LinkParamNone
*, bool);
65 DECL_LINK(ParseInputHdl
, sal_Int64
*, TriState
);
66 DECL_LINK(ModifyHdl
, weld::Entry
&, void);
67 DECL_LINK(ActivateHdl
, weld::Entry
&, bool);
68 DECL_LINK(FocusInHdl
, weld::Widget
&, void);
69 DECL_LINK(FocusOutHdl
, weld::Widget
&, void);
70 DECL_LINK(KeyInputHdl
, const ::KeyEvent
&, bool);
73 std::unique_ptr
<weld::FormattedSpinButton
> m_xWidget
;
74 SpinfieldToolbarController
* m_pSpinfieldToolbarController
;
77 SpinfieldControl::SpinfieldControl(vcl::Window
* pParent
, SpinfieldToolbarController
* pSpinfieldToolbarController
)
78 : InterimItemWindow(pParent
, u
"svt/ui/spinfieldcontrol.ui"_ustr
, u
"SpinFieldControl"_ustr
)
79 , m_xWidget(m_xBuilder
->weld_formatted_spin_button(u
"spinbutton"_ustr
))
80 , m_pSpinfieldToolbarController(pSpinfieldToolbarController
)
82 InitControlBase(m_xWidget
.get());
84 m_xWidget
->connect_focus_in(LINK(this, SpinfieldControl
, FocusInHdl
));
85 m_xWidget
->connect_focus_out(LINK(this, SpinfieldControl
, FocusOutHdl
));
86 Formatter
& rFormatter
= m_xWidget
->GetFormatter();
87 rFormatter
.SetOutputHdl(LINK(this, SpinfieldControl
, FormatOutputHdl
));
88 rFormatter
.SetInputHdl(LINK(this, SpinfieldControl
, ParseInputHdl
));
89 m_xWidget
->connect_value_changed(LINK(this, SpinfieldControl
, ValueChangedHdl
));
90 m_xWidget
->connect_changed(LINK(this, SpinfieldControl
, ModifyHdl
));
91 m_xWidget
->connect_activate(LINK(this, SpinfieldControl
, ActivateHdl
));
92 m_xWidget
->connect_key_press(LINK(this, SpinfieldControl
, KeyInputHdl
));
94 // so a later narrow size request can stick
95 m_xWidget
->set_width_chars(3);
96 m_xWidget
->set_size_request(42, -1);
98 SetSizePixel(get_preferred_size());
101 IMPL_LINK(SpinfieldControl
, KeyInputHdl
, const ::KeyEvent
&, rKEvt
, bool)
103 return ChildKeyInput(rKEvt
);
106 IMPL_LINK(SpinfieldControl
, ParseInputHdl
, sal_Int64
*, result
, TriState
)
108 *result
= m_xWidget
->get_text().toDouble() * weld::SpinButton::Power10(m_xWidget
->GetFormatter().GetDecimalDigits());
109 return TRISTATE_TRUE
;
112 SpinfieldControl::~SpinfieldControl()
117 void SpinfieldControl::dispose()
119 m_pSpinfieldToolbarController
= nullptr;
121 InterimItemWindow::dispose();
124 IMPL_LINK_NOARG(SpinfieldControl
, ValueChangedHdl
, weld::FormattedSpinButton
&, void)
126 if (m_pSpinfieldToolbarController
)
127 m_pSpinfieldToolbarController
->execute(0);
130 IMPL_LINK_NOARG(SpinfieldControl
, ModifyHdl
, weld::Entry
&, void)
132 if (m_pSpinfieldToolbarController
)
133 m_pSpinfieldToolbarController
->Modify();
136 IMPL_LINK_NOARG(SpinfieldControl
, FocusInHdl
, weld::Widget
&, void)
138 if (m_pSpinfieldToolbarController
)
139 m_pSpinfieldToolbarController
->GetFocus();
142 IMPL_LINK_NOARG(SpinfieldControl
, FocusOutHdl
, weld::Widget
&, void)
144 if (m_pSpinfieldToolbarController
)
145 m_pSpinfieldToolbarController
->LoseFocus();
148 IMPL_LINK_NOARG(SpinfieldControl
, ActivateHdl
, weld::Entry
&, bool)
150 bool bConsumed
= false;
151 if (m_pSpinfieldToolbarController
)
153 m_pSpinfieldToolbarController
->Activate();
159 IMPL_LINK_NOARG(SpinfieldControl
, FormatOutputHdl
, LinkParamNone
*, bool)
161 OUString aText
= m_pSpinfieldToolbarController
->FormatOutputString(m_xWidget
->GetFormatter().GetValue());
162 m_xWidget
->set_text(aText
);
166 SpinfieldToolbarController::SpinfieldToolbarController(
167 const Reference
< XComponentContext
>& rxContext
,
168 const Reference
< XFrame
>& rFrame
,
172 const OUString
& aCommand
) :
173 ComplexToolbarController( rxContext
, rFrame
, pToolbar
, nID
, aCommand
)
179 , m_pSpinfieldControl( nullptr )
181 m_pSpinfieldControl
= VclPtr
<SpinfieldControl
>::Create(m_xToolbar
, this);
185 // SpinFieldControl ctor has set a suitable height already
186 auto nHeight
= m_pSpinfieldControl
->GetSizePixel().Height();
188 m_pSpinfieldControl
->SetSizePixel( ::Size( nWidth
, nHeight
));
189 m_xToolbar
->SetItemWindow( m_nID
, m_pSpinfieldControl
);
192 SpinfieldToolbarController::~SpinfieldToolbarController()
196 void SAL_CALL
SpinfieldToolbarController::dispose()
198 SolarMutexGuard aSolarMutexGuard
;
200 m_xToolbar
->SetItemWindow( m_nID
, nullptr );
201 m_pSpinfieldControl
.disposeAndClear();
203 ComplexToolbarController::dispose();
206 Sequence
<PropertyValue
> SpinfieldToolbarController::getExecuteArgs(sal_Int16 KeyModifier
) const
208 OUString aSpinfieldText
= m_pSpinfieldControl
->get_entry_text();
210 // Add key modifier to argument list
212 comphelper::makePropertyValue(u
"KeyModifier"_ustr
, KeyModifier
),
213 comphelper::makePropertyValue(u
"Value"_ustr
, m_bFloat
? Any(aSpinfieldText
.toDouble())
214 : Any(aSpinfieldText
.toInt32()))
218 void SpinfieldToolbarController::Modify()
220 notifyTextChanged(m_pSpinfieldControl
->get_entry_text());
223 void SpinfieldToolbarController::GetFocus()
228 void SpinfieldToolbarController::LoseFocus()
233 void SpinfieldToolbarController::Activate()
235 // Call execute only with non-empty text
236 if (!m_pSpinfieldControl
->get_entry_text().isEmpty())
240 void SpinfieldToolbarController::executeControlCommand( const css::frame::ControlCommand
& rControlCommand
)
246 bool bFloatValue( false );
248 if ( rControlCommand
.Command
== "SetStep" )
250 for ( auto const & arg
: rControlCommand
.Arguments
)
252 if ( arg
.Name
== "Step" )
256 bool bFloat( false );
257 if ( impl_getValue( arg
.Value
, nValue
, fValue
, bFloat
))
258 aStep
= bFloat
? OUString::number( fValue
) :
259 OUString( OUString::number( nValue
));
264 else if ( rControlCommand
.Command
== "SetValue" )
266 for ( auto const & arg
: rControlCommand
.Arguments
)
268 if ( arg
.Name
== "Value" )
272 bool bFloat( false );
274 if ( impl_getValue( arg
.Value
, nValue
, fValue
, bFloat
))
276 aValue
= bFloat
? OUString::number( fValue
) :
277 OUString( OUString::number( nValue
));
278 bFloatValue
= bFloat
;
284 else if ( rControlCommand
.Command
== "SetValues" )
286 for ( auto const & arg
: rControlCommand
.Arguments
)
290 bool bFloat( false );
292 OUString aName
= arg
.Name
;
293 if ( impl_getValue( arg
.Value
, nValue
, fValue
, bFloat
))
295 if ( aName
== "Value" )
297 aValue
= bFloat
? OUString::number( fValue
) :
298 OUString( OUString::number( nValue
));
299 bFloatValue
= bFloat
;
301 else if ( aName
== "Step" )
302 aStep
= bFloat
? OUString::number( fValue
) :
303 OUString( OUString::number( nValue
));
304 else if ( aName
== "LowerLimit" )
305 aMin
= bFloat
? OUString::number( fValue
) :
306 OUString( OUString::number( nValue
));
307 else if ( aName
== "UpperLimit" )
308 aMax
= bFloat
? OUString::number( fValue
) :
309 OUString( OUString::number( nValue
));
311 else if ( aName
== "OutputFormat" )
312 arg
.Value
>>= m_aOutFormat
;
315 else if ( rControlCommand
.Command
== "SetLowerLimit" )
317 for ( auto const & arg
: rControlCommand
.Arguments
)
319 if ( arg
.Name
== "LowerLimit" )
323 bool bFloat( false );
324 if ( impl_getValue( arg
.Value
, nValue
, fValue
, bFloat
))
325 aMin
= bFloat
? OUString::number( fValue
) :
326 OUString( OUString::number( nValue
));
331 else if ( rControlCommand
.Command
== "SetUpperLimit" )
333 for ( auto const & arg
: rControlCommand
.Arguments
)
335 if ( arg
.Name
== "UpperLimit" )
339 bool bFloat( false );
340 if ( impl_getValue( arg
.Value
, nValue
, fValue
, bFloat
))
341 aMax
= bFloat
? OUString::number( fValue
) :
342 OUString( OUString::number( nValue
));
347 else if ( rControlCommand
.Command
== "SetOutputFormat" )
349 for ( auto const & arg
: rControlCommand
.Arguments
)
351 if ( arg
.Name
== "OutputFormat" )
353 arg
.Value
>>= m_aOutFormat
;
359 Formatter
& rFormatter
= m_pSpinfieldControl
->GetFormatter();
361 // Check values and set members
363 rFormatter
.SetDecimalDigits(2);
364 if ( !aValue
.isEmpty() )
366 m_bFloat
= bFloatValue
;
367 m_nValue
= aValue
.toDouble();
368 rFormatter
.SetValue(m_nValue
);
370 if ( !aMax
.isEmpty() )
372 m_nMax
= aMax
.toDouble();
373 rFormatter
.SetMaxValue(m_nMax
);
375 if ( !aMin
.isEmpty() )
377 m_nMin
= aMin
.toDouble();
378 rFormatter
.SetMinValue(m_nMin
);
380 if ( !aStep
.isEmpty() )
382 m_nStep
= aStep
.toDouble();
383 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 );
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
;
411 OUString
SpinfieldToolbarController::FormatOutputString( double fValue
)
413 if ( m_aOutFormat
.isEmpty() )
416 return OUString::number( fValue
);
418 return OUString::number( sal_Int32( fValue
));
423 sal_Unicode aBuffer
[128];
427 _snwprintf( o3tl::toW(aBuffer
), SAL_N_ELEMENTS(aBuffer
), o3tl::toW(m_aOutFormat
.getStr()), fValue
);
429 _snwprintf( o3tl::toW(aBuffer
), SAL_N_ELEMENTS(aBuffer
), o3tl::toW(m_aOutFormat
.getStr()), sal_Int32( fValue
));
431 return OUString(aBuffer
);
433 // Currently we have no support for a format string using sal_Unicode. wchar_t
434 // is 32 bit on Unix platform!
437 OString aFormat
= OUStringToOString( m_aOutFormat
, osl_getThreadTextEncoding() );
439 snprintf( aBuffer
, 128, aFormat
.getStr(), fValue
);
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() );
452 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */