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 <svx/dialcontrol.hxx>
21 #include <svx/svdtrans.hxx>
23 #include <vcl/virdev.hxx>
24 #include <vcl/svapp.hxx>
25 #include <vcl/bitmapex.hxx>
26 #include <vcl/event.hxx>
27 #include <vcl/settings.hxx>
28 #include <i18nlangtag/languagetag.hxx>
32 const tools::Long DIAL_OUTER_WIDTH
= 8;
34 DialControlBmp::DialControlBmp(OutputDevice
& rReference
)
35 : VirtualDevice(rReference
, DeviceFormat::WITH_ALPHA
)
37 , mrParent(rReference
)
44 void DialControlBmp::InitBitmap(const vcl::Font
& rFont
)
50 void DialControlBmp::CopyBackground( const DialControlBmp
& rSrc
)
53 SetSize(rSrc
.maRect
.GetSize());
54 mbEnabled
= rSrc
.mbEnabled
;
56 DrawBitmapEx( aPos
, rSrc
.GetBitmapEx( aPos
, maRect
.GetSize() ) );
59 void DialControlBmp::DrawBackground( const Size
& rSize
, bool bEnabled
)
67 void DialControlBmp::DrawElements( const OUString
& rText
, Degree100 nAngle
)
69 double fAngle
= toRadians(nAngle
);
70 double fSin
= sin( fAngle
);
71 double fCos
= cos( fAngle
);
72 double fWidth
= GetTextWidth( rText
) / 2.0;
73 double fHeight
= GetTextHeight() / 2.0;
75 if ( !rText
.isEmpty() )
78 vcl::Font
aFont( GetFont() );
79 aFont
.SetColor( GetTextColor() );
80 aFont
.SetOrientation( to
<Degree10
>(nAngle
) ); // Font uses 1/10 degrees
81 aFont
.SetWeight( WEIGHT_BOLD
);
84 tools::Long nX
= static_cast< tools::Long
>( mnCenterX
- fWidth
* fCos
- fHeight
* fSin
);
85 tools::Long nY
= static_cast< tools::Long
>( mnCenterY
+ fWidth
* fSin
- fHeight
* fCos
);
86 tools::Rectangle
aRect( nX
, nY
, 2 * mnCenterX
- nX
, 2 * mnCenterY
- nY
);
87 DrawText( aRect
, rText
, mbEnabled
? DrawTextFlags::NONE
: DrawTextFlags::Disable
);
92 const sal_Int32
nDx (fCos
* (maRect
.GetWidth()-4) / 2);
93 const sal_Int32
nDy (-fSin
* (maRect
.GetHeight()-4) / 2);
94 Point
pt1( maRect
.Center() );
95 Point
pt2( pt1
.X() + nDx
, pt1
.Y() + nDy
);
97 SetLineColor( GetTextColor() );
101 // *** drag button ***
103 bool bMain
= (nAngle
% 4500_deg100
) != 0_deg100
;
104 SetLineColor( GetButtonLineColor() );
105 SetFillColor( GetButtonFillColor( bMain
) );
107 tools::Long nX
= mnCenterX
- static_cast< tools::Long
>( (DIAL_OUTER_WIDTH
/ 2 - mnCenterX
) * fCos
);
108 tools::Long nY
= mnCenterY
- static_cast< tools::Long
>( (mnCenterY
- DIAL_OUTER_WIDTH
/ 2) * fSin
);
109 tools::Long nSize
= bMain
? (DIAL_OUTER_WIDTH
/ 4) : (DIAL_OUTER_WIDTH
/ 2 - 1);
110 DrawEllipse( tools::Rectangle( nX
- nSize
, nY
- nSize
, nX
+ nSize
, nY
+ nSize
) );
113 Color
DialControlBmp::GetBackgroundColor() const
115 return GetSettings().GetStyleSettings().GetDialogColor();
118 const Color
& DialControlBmp::GetTextColor() const
120 return GetSettings().GetStyleSettings().GetLabelTextColor();
123 const Color
& DialControlBmp::GetScaleLineColor() const
125 const StyleSettings
& rSett
= GetSettings().GetStyleSettings();
126 return mbEnabled
? rSett
.GetButtonTextColor() : rSett
.GetDisableColor();
129 const Color
& DialControlBmp::GetButtonLineColor() const
131 const StyleSettings
& rSett
= GetSettings().GetStyleSettings();
132 return mbEnabled
? rSett
.GetButtonTextColor() : rSett
.GetDisableColor();
135 const Color
& DialControlBmp::GetButtonFillColor( bool bMain
) const
137 const StyleSettings
& rSett
= GetSettings().GetStyleSettings();
138 return mbEnabled
? (bMain
? rSett
.GetMenuColor() : rSett
.GetHighlightColor()) : rSett
.GetDisableColor();
141 void DialControlBmp::Init()
143 SetSettings(mrParent
.GetSettings());
144 SetBackground(GetBackgroundColor());
147 void DialControlBmp::SetSize( const Size
& rSize
)
149 maRect
.SetPos( Point( 0, 0 ) );
150 maRect
.SetSize( rSize
);
151 mnCenterX
= rSize
.Width() / 2;
152 mnCenterY
= rSize
.Height() / 2;
153 SetOutputSize( rSize
);
156 void DialControlBmp::DrawBackground()
158 // *** background with 3D effect ***
164 EnableRTL(); // draw 3D effect in correct direction
166 sal_uInt8 nDiff
= mbEnabled
? 0x18 : 0x10;
169 aColor
= GetBackgroundColor();
170 SetFillColor( aColor
);
171 DrawPie( maRect
, maRect
.TopRight(), maRect
.TopCenter() );
172 DrawPie( maRect
, maRect
.BottomLeft(), maRect
.BottomCenter() );
174 aColor
.DecreaseLuminance( nDiff
);
175 SetFillColor( aColor
);
176 DrawPie( maRect
, maRect
.BottomCenter(), maRect
.TopRight() );
178 aColor
.DecreaseLuminance( nDiff
);
179 SetFillColor( aColor
);
180 DrawPie( maRect
, maRect
.BottomRight(), maRect
.RightCenter() );
182 aColor
= GetBackgroundColor();
183 aColor
.IncreaseLuminance( nDiff
);
184 SetFillColor( aColor
);
185 DrawPie( maRect
, maRect
.TopCenter(), maRect
.BottomLeft() );
187 aColor
.IncreaseLuminance( nDiff
);
188 SetFillColor( aColor
);
189 DrawPie( maRect
, maRect
.TopLeft(), maRect
.LeftCenter() );
193 // *** calibration ***
195 Point
aStartPos( mnCenterX
, mnCenterY
);
196 Color
aFullColor( GetScaleLineColor() );
197 Color
aLightColor( GetBackgroundColor() );
198 aLightColor
.Merge( aFullColor
, 128 );
200 for( int nAngle
= 0; nAngle
< 360; nAngle
+= 15 )
202 SetLineColor( (nAngle
% 45) ? aLightColor
: aFullColor
);
203 double fAngle
= basegfx::deg2rad(nAngle
);
204 tools::Long nX
= static_cast< tools::Long
>( -mnCenterX
* cos( fAngle
) );
205 tools::Long nY
= static_cast< tools::Long
>( mnCenterY
* sin( fAngle
) );
206 DrawLine( aStartPos
, Point( mnCenterX
- nX
, mnCenterY
- nY
) );
209 // *** clear inner area ***
212 SetFillColor( GetBackgroundColor() );
213 tools::Rectangle aEllipseRect
= maRect
;
214 aEllipseRect
.shrink(DIAL_OUTER_WIDTH
);
215 DrawEllipse( aEllipseRect
);
218 DialControl::DialControl_Impl::DialControl_Impl(OutputDevice
& rReference
) :
219 mxBmpEnabled(VclPtr
<DialControlBmp
>::Create(rReference
)),
220 mxBmpDisabled(VclPtr
<DialControlBmp
>::Create(rReference
)),
221 mxBmpBuffered(VclPtr
<DialControlBmp
>::Create(rReference
)),
222 mpLinkField( nullptr ),
223 mnLinkedFieldValueMultiplyer( 0 ),
233 void DialControl::DialControl_Impl::Init( const Size
& rWinSize
, const vcl::Font
& rWinFont
)
235 maWinFont
= rWinFont
;
236 maWinFont
.SetTransparent(true);
237 mxBmpBuffered
->InitBitmap(maWinFont
);
241 void DialControl::DialControl_Impl::SetSize( const Size
& rWinSize
)
243 // make the control squared, and adjusted so that we have a well-defined
244 // center ["(x - 1) | 1" creates odd value <= x]
245 tools::Long nMin
= (std::min(rWinSize
.Width(), rWinSize
.Height()) - 1) | 1;
247 maWinSize
= Size( nMin
, nMin
);
249 mnCenterX
= maWinSize
.Width() / 2;
250 mnCenterY
= maWinSize
.Height() / 2;
252 mxBmpEnabled
->DrawBackground( maWinSize
, true );
253 mxBmpDisabled
->DrawBackground( maWinSize
, false );
254 mxBmpBuffered
->SetSize( maWinSize
);
257 void DialControl::SetDrawingArea(weld::DrawingArea
* pDrawingArea
)
259 CustomWidgetController::SetDrawingArea(pDrawingArea
);
260 //use same logic as DialControl_Impl::SetSize
261 int nDim
= (std::min
<int>(pDrawingArea
->get_approximate_digit_width() * 12,
262 pDrawingArea
->get_text_height() * 6) - 1) | 1;
263 Size
aSize(nDim
, nDim
);
264 pDrawingArea
->set_size_request(aSize
.Width(), aSize
.Height());
265 mpImpl
.reset(new DialControl_Impl(pDrawingArea
->get_ref_device()));
266 //set size and use that
270 void DialControl::Resize()
272 mpImpl
->SetSize(GetOutputSizePixel());
276 void DialControl::Paint(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
&)
279 rRenderContext
.DrawBitmapEx(aPos
, mpImpl
->mxBmpBuffered
->GetBitmapEx(aPos
, mpImpl
->maWinSize
));
282 void DialControl::StyleUpdated()
284 CustomWidgetController::StyleUpdated();
285 Init( mpImpl
->maWinSize
, mpImpl
->maWinFont
);
289 bool DialControl::MouseButtonDown(const MouseEvent
& rMEvt
)
295 mpImpl
->mnOldAngle
= mpImpl
->mnAngle
;
296 HandleMouseEvent( rMEvt
.GetPosPixel(), true );
301 bool DialControl::MouseMove( const MouseEvent
& rMEvt
)
303 if( IsMouseCaptured() && rMEvt
.IsLeft() )
304 HandleMouseEvent( rMEvt
.GetPosPixel(), false );
308 bool DialControl::MouseButtonUp(const MouseEvent
&)
310 if( IsMouseCaptured() )
313 if( mpImpl
->mpLinkField
)
314 mpImpl
->mpLinkField
->grab_focus();
319 bool DialControl::KeyInput( const KeyEvent
& rKEvt
)
321 const vcl::KeyCode
& rKCode
= rKEvt
.GetKeyCode();
322 if( !rKCode
.GetModifier() && (rKCode
.GetCode() == KEY_ESCAPE
) )
327 return CustomWidgetController::KeyInput(rKEvt
);
330 void DialControl::LoseFocus()
332 // release captured mouse
336 bool DialControl::HasRotation() const
338 return !mpImpl
->mbNoRot
;
341 void DialControl::SetNoRotation()
343 if( !mpImpl
->mbNoRot
)
345 mpImpl
->mbNoRot
= true;
347 if (mpImpl
->mpLinkField
)
348 mpImpl
->mpLinkField
->set_text(u
""_ustr
);
352 Degree100
DialControl::GetRotation() const
354 return mpImpl
->mnAngle
;
357 void DialControl::SetRotation(Degree100 nAngle
)
359 SetRotation(nAngle
, false);
362 void DialControl::SetLinkedField(weld::MetricSpinButton
* pField
, sal_Int32 nDecimalPlaces
)
364 mpImpl
->mnLinkedFieldValueMultiplyer
= 100 / std::pow(10.0, double(nDecimalPlaces
));
366 // remove modify handler from old linked field
367 if( mpImpl
->mpLinkField
)
369 weld::MetricSpinButton
& rField
= *mpImpl
->mpLinkField
;
370 rField
.connect_value_changed(Link
<weld::MetricSpinButton
&,void>());
372 // remember the new linked field
373 mpImpl
->mpLinkField
= pField
;
374 // set modify handler at new linked field
375 if( mpImpl
->mpLinkField
)
377 weld::MetricSpinButton
& rField
= *mpImpl
->mpLinkField
;
378 rField
.connect_value_changed(LINK(this, DialControl
, LinkedFieldModifyHdl
));
382 IMPL_LINK_NOARG(DialControl
, LinkedFieldModifyHdl
, weld::MetricSpinButton
&, void)
384 SetRotation(Degree100(mpImpl
->mpLinkField
->get_value(FieldUnit::DEGREE
) * mpImpl
->mnLinkedFieldValueMultiplyer
), true);
387 void DialControl::SaveValue()
389 mpImpl
->mnInitialAngle
= mpImpl
->mnAngle
;
392 bool DialControl::IsValueModified() const
394 return mpImpl
->mnInitialAngle
!= mpImpl
->mnAngle
;
397 void DialControl::Init( const Size
& rWinSize
, const vcl::Font
& rWinFont
)
399 mpImpl
->Init( rWinSize
, rWinFont
);
400 EnableRTL( false ); // don't mirror mouse handling
401 SetOutputSizePixel( mpImpl
->maWinSize
);
404 void DialControl::Init( const Size
& rWinSize
)
406 //hidpi TODO: GetDefaultFont() picks a font size too small, so fix it here.
407 vcl::Font aDefaultSize
= Application::GetSettings().GetStyleSettings().GetLabelFont();
409 vcl::Font
aFont( OutputDevice::GetDefaultFont(
410 DefaultFontType::UI_SANS
, Application::GetSettings().GetUILanguageTag().getLanguageType(), GetDefaultFontFlags::OnlyOne
) );
412 aFont
.SetFontHeight(aDefaultSize
.GetFontHeight());
413 Init( rWinSize
, aFont
);
416 void DialControl::InvalidateControl()
418 mpImpl
->mxBmpBuffered
->CopyBackground( IsEnabled() ? *mpImpl
->mxBmpEnabled
: *mpImpl
->mxBmpDisabled
);
419 if( !mpImpl
->mbNoRot
)
420 mpImpl
->mxBmpBuffered
->DrawElements(GetText(), mpImpl
->mnAngle
);
424 void DialControl::SetRotation(Degree100 nAngle
, bool bBroadcast
)
426 bool bOldSel
= mpImpl
->mbNoRot
;
427 mpImpl
->mbNoRot
= false;
429 nAngle
= NormAngle36000(nAngle
);
431 if (!bOldSel
|| (mpImpl
->mnAngle
!= nAngle
))
433 mpImpl
->mnAngle
= nAngle
;
435 if( mpImpl
->mpLinkField
)
436 mpImpl
->mpLinkField
->set_value(GetRotation().get() / mpImpl
->mnLinkedFieldValueMultiplyer
, FieldUnit::DEGREE
);
438 mpImpl
->maModifyHdl
.Call(*this);
442 void DialControl::SetModifyHdl( const Link
<DialControl
&,void>& rLink
)
444 mpImpl
->maModifyHdl
= rLink
;
447 void DialControl::HandleMouseEvent( const Point
& rPos
, bool bInitial
)
449 tools::Long nX
= rPos
.X() - mpImpl
->mnCenterX
;
450 tools::Long nY
= mpImpl
->mnCenterY
- rPos
.Y();
451 double fH
= std::hypot( nX
, nY
);
454 double fAngle
= acos( nX
/ fH
);
455 sal_Int32 nAngle
= basegfx::rad2deg
<100>(fAngle
);
457 nAngle
= 36000 - nAngle
;
458 if( bInitial
) // round to entire 15 degrees
459 nAngle
= ((nAngle
+ 750) / 1500) * 1500;
460 // Round up to 1 degree
461 nAngle
= (((nAngle
+ 50) / 100) * 100) % 36000;
462 SetRotation(Degree100(nAngle
), true);
466 void DialControl::HandleEscapeEvent()
468 if( IsMouseCaptured() )
471 SetRotation(mpImpl
->mnOldAngle
, true);
472 if( mpImpl
->mpLinkField
)
473 mpImpl
->mpLinkField
->grab_focus();
479 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */