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::lang
;
41 using namespace ::com::sun::star::frame
;
42 using namespace ::com::sun::star::util
;
47 // Wrapper class to notify controller about events from combobox.
48 // Unfortunaltly the events are notified through virtual methods instead
51 class SpinfieldControl final
: public InterimItemWindow
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);
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()
119 void SpinfieldControl::dispose()
121 m_pSpinfieldToolbarController
= nullptr;
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();
161 IMPL_LINK_NOARG(SpinfieldControl
, FormatOutputHdl
, LinkParamNone
*, bool)
163 OUString aText
= m_pSpinfieldToolbarController
->FormatOutputString(m_xWidget
->GetFormatter().GetValue());
164 m_xWidget
->set_text(aText
);
168 SpinfieldToolbarController::SpinfieldToolbarController(
169 const Reference
< XComponentContext
>& rxContext
,
170 const Reference
< XFrame
>& rFrame
,
174 const OUString
& aCommand
) :
175 ComplexToolbarController( rxContext
, rFrame
, pToolbar
, nID
, aCommand
)
181 , m_pSpinfieldControl( nullptr )
183 m_pSpinfieldControl
= VclPtr
<SpinfieldControl
>::Create(m_xToolbar
, this);
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()
229 void SpinfieldToolbarController::LoseFocus()
234 void SpinfieldToolbarController::Activate()
236 // Call execute only with non-empty text
237 if (!m_pSpinfieldControl
->get_entry_text().isEmpty())
241 void SpinfieldToolbarController::executeControlCommand( const css::frame::ControlCommand
& rControlCommand
)
247 bool bFloatValue( false );
249 if ( rControlCommand
.Command
== "SetStep" )
251 for ( auto const & arg
: rControlCommand
.Arguments
)
253 if ( arg
.Name
== "Step" )
257 bool bFloat( false );
258 if ( impl_getValue( arg
.Value
, nValue
, fValue
, bFloat
))
259 aStep
= bFloat
? OUString::number( fValue
) :
260 OUString( OUString::number( nValue
));
265 else if ( rControlCommand
.Command
== "SetValue" )
267 for ( auto const & arg
: rControlCommand
.Arguments
)
269 if ( arg
.Name
== "Value" )
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
;
285 else if ( rControlCommand
.Command
== "SetValues" )
287 for ( auto const & arg
: rControlCommand
.Arguments
)
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" )
324 bool bFloat( false );
325 if ( impl_getValue( arg
.Value
, nValue
, fValue
, bFloat
))
326 aMin
= bFloat
? OUString::number( fValue
) :
327 OUString( OUString::number( nValue
));
332 else if ( rControlCommand
.Command
== "SetUpperLimit" )
334 for ( auto const & arg
: rControlCommand
.Arguments
)
336 if ( arg
.Name
== "UpperLimit" )
340 bool bFloat( false );
341 if ( impl_getValue( arg
.Value
, nValue
, fValue
, bFloat
))
342 aMax
= bFloat
? OUString::number( fValue
) :
343 OUString( OUString::number( nValue
));
348 else if ( rControlCommand
.Command
== "SetOutputFormat" )
350 for ( auto const & arg
: rControlCommand
.Arguments
)
352 if ( arg
.Name
== "OutputFormat" )
354 arg
.Value
>>= m_aOutFormat
;
360 Formatter
& rFormatter
= m_pSpinfieldControl
->GetFormatter();
362 // Check values and set members
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 );
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: */