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 "bmpmask.hrc"
22 #include <svx/dialmgr.hxx>
23 #include <tools/rcid.h>
25 #include <vcl/virdev.hxx>
26 #include <vcl/svapp.hxx>
27 #include <vcl/bitmap.hxx>
28 #include <vcl/field.hxx>
29 #include <vcl/settings.hxx>
30 #include <svtools/colorcfg.hxx>
31 #include <vcl/builder.hxx>
37 const long DIAL_OUTER_WIDTH
= 8;
41 DialControlBmp::DialControlBmp( Window
& rParent
) :
42 VirtualDevice( rParent
, 0, 0 ),
51 void DialControlBmp::InitBitmap(const Font
& rFont
)
57 void DialControlBmp::CopyBackground( const DialControlBmp
& rSrc
)
60 SetSize(rSrc
.maRect
.GetSize());
61 mbEnabled
= rSrc
.mbEnabled
;
63 DrawBitmapEx( aPos
, rSrc
.GetBitmapEx( aPos
, maRect
.GetSize() ) );
66 void DialControlBmp::DrawBackground( const Size
& rSize
, bool bEnabled
)
74 void DialControlBmp::DrawElements( const OUString
& rText
, sal_Int32 nAngle
)
76 double fAngle
= nAngle
* F_PI180
/ 100.0;
77 double fSin
= sin( fAngle
);
78 double fCos
= cos( fAngle
);
79 double fWidth
= GetTextWidth( rText
) / 2.0;
80 double fHeight
= GetTextHeight() / 2.0;
82 if ( !rText
.isEmpty() )
85 Font
aFont( GetFont() );
86 aFont
.SetColor( GetTextColor() );
87 aFont
.SetOrientation( static_cast< short >( (nAngle
+ 5) / 10 ) ); // Font uses 1/10 degrees
88 aFont
.SetWeight( WEIGHT_BOLD
);
91 long nX
= static_cast< long >( mnCenterX
- fWidth
* fCos
- fHeight
* fSin
);
92 long nY
= static_cast< long >( mnCenterY
+ fWidth
* fSin
- fHeight
* fCos
);
93 Rectangle
aRect( nX
, nY
, 2 * mnCenterX
- nX
, 2 * mnCenterY
- nY
);
94 DrawText( aRect
, rText
, mbEnabled
? 0 : TEXT_DRAW_DISABLE
);
99 const sal_Int32
nDx (fCos
* (maRect
.GetWidth()-4) / 2);
100 const sal_Int32
nDy (-fSin
* (maRect
.GetHeight()-4) / 2);
101 Point
pt1( maRect
.Center() );
102 Point
pt2( pt1
.X() + nDx
, pt1
.Y() + nDy
);
104 SetLineColor( GetTextColor() );
105 DrawLine( pt1
, pt2
);
108 // *** drag button ***
110 bool bMain
= (nAngle
% 4500) != 0;
111 SetLineColor( GetButtonLineColor() );
112 SetFillColor( GetButtonFillColor( bMain
) );
114 long nX
= mnCenterX
- static_cast< long >( (DIAL_OUTER_WIDTH
/ 2 - mnCenterX
) * fCos
);
115 long nY
= mnCenterY
- static_cast< long >( (mnCenterY
- DIAL_OUTER_WIDTH
/ 2) * fSin
);
116 long nSize
= bMain
? (DIAL_OUTER_WIDTH
/ 4) : (DIAL_OUTER_WIDTH
/ 2 - 1);
117 DrawEllipse( Rectangle( nX
- nSize
, nY
- nSize
, nX
+ nSize
, nY
+ nSize
) );
120 // private --------------------------------------------------------------------
122 const Color
& DialControlBmp::GetBackgroundColor() const
124 return GetSettings().GetStyleSettings().GetDialogColor();
127 const Color
& DialControlBmp::GetTextColor() const
129 return GetSettings().GetStyleSettings().GetLabelTextColor();
132 const Color
& DialControlBmp::GetScaleLineColor() const
134 const StyleSettings
& rSett
= GetSettings().GetStyleSettings();
135 return mbEnabled
? rSett
.GetButtonTextColor() : rSett
.GetDisableColor();
138 const Color
& DialControlBmp::GetButtonLineColor() const
140 const StyleSettings
& rSett
= GetSettings().GetStyleSettings();
141 return mbEnabled
? rSett
.GetButtonTextColor() : rSett
.GetDisableColor();
144 const Color
& DialControlBmp::GetButtonFillColor( bool bMain
) const
146 const StyleSettings
& rSett
= GetSettings().GetStyleSettings();
147 return mbEnabled
? (bMain
? rSett
.GetMenuColor() : rSett
.GetHighlightColor()) : rSett
.GetDisableColor();
150 void DialControlBmp::Init()
152 SetSettings(mrParent
.GetSettings());
156 void DialControlBmp::SetSize( const Size
& rSize
)
158 maRect
.SetPos( Point( 0, 0 ) );
159 maRect
.SetSize( rSize
);
160 mnCenterX
= rSize
.Width() / 2;
161 mnCenterY
= rSize
.Height() / 2;
162 SetOutputSize( rSize
);
165 void DialControlBmp::DrawBackground()
167 // *** background with 3D effect ***
173 EnableRTL( true ); // draw 3D effect in correct direction
175 sal_uInt8 nDiff
= mbEnabled
? 0x18 : 0x10;
178 aColor
= GetBackgroundColor();
179 SetFillColor( aColor
);
180 DrawPie( maRect
, maRect
.TopRight(), maRect
.TopCenter() );
181 DrawPie( maRect
, maRect
.BottomLeft(), maRect
.BottomCenter() );
183 aColor
.DecreaseLuminance( nDiff
);
184 SetFillColor( aColor
);
185 DrawPie( maRect
, maRect
.BottomCenter(), maRect
.TopRight() );
187 aColor
.DecreaseLuminance( nDiff
);
188 SetFillColor( aColor
);
189 DrawPie( maRect
, maRect
.BottomRight(), maRect
.RightCenter() );
191 aColor
= GetBackgroundColor();
192 aColor
.IncreaseLuminance( nDiff
);
193 SetFillColor( aColor
);
194 DrawPie( maRect
, maRect
.TopCenter(), maRect
.BottomLeft() );
196 aColor
.IncreaseLuminance( nDiff
);
197 SetFillColor( aColor
);
198 DrawPie( maRect
, maRect
.TopLeft(), maRect
.LeftCenter() );
202 // *** calibration ***
204 Point
aStartPos( mnCenterX
, mnCenterY
);
205 Color
aFullColor( GetScaleLineColor() );
206 Color
aLightColor( GetBackgroundColor() );
207 aLightColor
.Merge( aFullColor
, 128 );
209 for( int nAngle
= 0; nAngle
< 360; nAngle
+= 15 )
211 SetLineColor( (nAngle
% 45) ? aLightColor
: aFullColor
);
212 double fAngle
= nAngle
* F_PI180
;
213 long nX
= static_cast< long >( -mnCenterX
* cos( fAngle
) );
214 long nY
= static_cast< long >( mnCenterY
* sin( fAngle
) );
215 DrawLine( aStartPos
, Point( mnCenterX
- nX
, mnCenterY
- nY
) );
218 // *** clear inner area ***
221 SetFillColor( GetBackgroundColor() );
222 DrawEllipse( Rectangle( maRect
.Left() + DIAL_OUTER_WIDTH
, maRect
.Top() + DIAL_OUTER_WIDTH
,
223 maRect
.Right() - DIAL_OUTER_WIDTH
, maRect
.Bottom() - DIAL_OUTER_WIDTH
) );
228 DialControl::DialControl_Impl::DialControl_Impl ( Window
& rParent
) :
229 mpBmpEnabled(new DialControlBmp(rParent
)),
230 mpBmpDisabled(new DialControlBmp(rParent
)),
231 mpBmpBuffered(new DialControlBmp(rParent
)),
233 mnLinkedFieldValueMultiplyer( 0 ),
243 void DialControl::DialControl_Impl::Init( const Size
& rWinSize
, const Font
& rWinFont
)
245 maWinFont
= rWinFont
;
246 maWinFont
.SetTransparent(true);
247 mpBmpBuffered
->InitBitmap(maWinFont
);
251 void DialControl::DialControl_Impl::SetSize( const Size
& rWinSize
)
253 // make the control squared, and adjusted so that we have a well-defined
254 // center ["(x - 1) | 1" creates odd value <= x]
255 long nMin
= (std::min(rWinSize
.Width(), rWinSize
.Height()) - 1) | 1;
257 maWinSize
= Size( nMin
, nMin
);
259 mnCenterX
= maWinSize
.Width() / 2;
260 mnCenterY
= maWinSize
.Height() / 2;
262 mpBmpEnabled
->DrawBackground( maWinSize
, true );
263 mpBmpDisabled
->DrawBackground( maWinSize
, false );
264 mpBmpBuffered
->SetSize( maWinSize
);
269 DialControl::DialControl( Window
* pParent
, WinBits nBits
) :
270 Control( pParent
, nBits
),
271 mpImpl( new DialControl_Impl( *this ) )
273 Init( GetOutputSizePixel() );
276 DialControl::~DialControl()
280 extern "C" SAL_DLLPUBLIC_EXPORT Window
* SAL_CALL
makeDialControl(Window
*pParent
, VclBuilder::stringmap
&)
282 return new DialControl(pParent
, WB_TABSTOP
);
285 void DialControl::Resize()
287 mpImpl
->SetSize(GetOutputSizePixel());
291 void DialControl::Paint( const Rectangle
& )
294 DrawBitmapEx( aPos
, mpImpl
->mpBmpBuffered
->GetBitmapEx( aPos
, mpImpl
->maWinSize
) );
297 void DialControl::StateChanged( StateChangedType nStateChange
)
299 if( nStateChange
== STATE_CHANGE_ENABLE
)
302 // update the linked edit field
303 if( mpImpl
->mpLinkField
)
305 NumericField
& rField
= *mpImpl
->mpLinkField
;
306 switch( nStateChange
)
308 case STATE_CHANGE_VISIBLE
: rField
.Show( IsVisible() ); break;
309 case STATE_CHANGE_ENABLE
: rField
.Enable( IsEnabled() ); break;
313 Control::StateChanged( nStateChange
);
316 void DialControl::DataChanged( const DataChangedEvent
& rDCEvt
)
318 if( (rDCEvt
.GetType() == DATACHANGED_SETTINGS
) && (rDCEvt
.GetFlags() & SETTINGS_STYLE
) )
320 Init( mpImpl
->maWinSize
, mpImpl
->maWinFont
);
323 Control::DataChanged( rDCEvt
);
326 void DialControl::MouseButtonDown( const MouseEvent
& rMEvt
)
332 mpImpl
->mnOldAngle
= mpImpl
->mnAngle
;
333 HandleMouseEvent( rMEvt
.GetPosPixel(), true );
335 Control::MouseButtonDown( rMEvt
);
338 void DialControl::MouseMove( const MouseEvent
& rMEvt
)
340 if( IsMouseCaptured() && rMEvt
.IsLeft() )
341 HandleMouseEvent( rMEvt
.GetPosPixel(), false );
342 Control::MouseMove(rMEvt
);
345 void DialControl::MouseButtonUp( const MouseEvent
& rMEvt
)
347 if( IsMouseCaptured() )
350 if( mpImpl
->mpLinkField
)
351 mpImpl
->mpLinkField
->GrabFocus();
353 Control::MouseButtonUp( rMEvt
);
356 void DialControl::KeyInput( const KeyEvent
& rKEvt
)
358 const KeyCode
& rKCode
= rKEvt
.GetKeyCode();
359 if( !rKCode
.GetModifier() && (rKCode
.GetCode() == KEY_ESCAPE
) )
362 Control::KeyInput( rKEvt
);
365 void DialControl::LoseFocus()
367 // release captured mouse
369 Control::LoseFocus();
372 bool DialControl::HasRotation() const
374 return !mpImpl
->mbNoRot
;
377 void DialControl::SetNoRotation()
379 if( !mpImpl
->mbNoRot
)
381 mpImpl
->mbNoRot
= true;
383 if( mpImpl
->mpLinkField
)
384 mpImpl
->mpLinkField
->SetText( "" );
388 sal_Int32
DialControl::GetRotation() const
390 return mpImpl
->mnAngle
;
393 Size
DialControl::GetOptimalSize() const
395 return LogicToPixel(Size(42 , 43), MAP_APPFONT
);
398 void DialControl::SetRotation( sal_Int32 nAngle
)
400 SetRotation( nAngle
, false );
403 void DialControl::SetLinkedField( NumericField
* pField
, sal_Int32 nDecimalPlaces
)
405 mpImpl
->mnLinkedFieldValueMultiplyer
= 100 / std::pow(10.0, double(nDecimalPlaces
));
407 // remove modify handler from old linked field
408 ImplSetFieldLink( Link() );
409 // remember the new linked field
410 mpImpl
->mpLinkField
= pField
;
411 // set modify handler at new linked field
412 ImplSetFieldLink( LINK( this, DialControl
, LinkedFieldModifyHdl
) );
415 void DialControl::SaveValue()
417 mpImpl
->mnInitialAngle
= mpImpl
->mnAngle
;
420 bool DialControl::IsValueModified()
422 return mpImpl
->mnInitialAngle
!= mpImpl
->mnAngle
;
425 void DialControl::SetModifyHdl( const Link
& rLink
)
427 mpImpl
->maModifyHdl
= rLink
;
430 // private --------------------------------------------------------------------
432 void DialControl::Init( const Size
& rWinSize
, const Font
& rWinFont
)
434 mpImpl
->Init( rWinSize
, rWinFont
);
435 EnableRTL( false ); // don't mirror mouse handling
436 SetOutputSizePixel( mpImpl
->maWinSize
);
440 void DialControl::Init( const Size
& rWinSize
)
442 //hidpi TODO: GetDefaultFont() picks a font size too small, so fix it here.
443 Font aDefaultSize
= GetFont();
445 Font
aFont( OutputDevice::GetDefaultFont(
446 DEFAULTFONT_UI_SANS
, Application::GetSettings().GetUILanguageTag().getLanguageType(), DEFAULTFONT_FLAGS_ONLYONE
) );
448 aFont
.SetHeight(aDefaultSize
.GetHeight());
449 Init( rWinSize
, aFont
);
452 void DialControl::InvalidateControl()
454 mpImpl
->mpBmpBuffered
->CopyBackground( IsEnabled() ? *mpImpl
->mpBmpEnabled
: *mpImpl
->mpBmpDisabled
);
455 if( !mpImpl
->mbNoRot
)
456 mpImpl
->mpBmpBuffered
->DrawElements( GetText(), mpImpl
->mnAngle
);
460 void DialControl::SetRotation( sal_Int32 nAngle
, bool bBroadcast
)
462 bool bOldSel
= mpImpl
->mbNoRot
;
463 mpImpl
->mbNoRot
= false;
468 if( !bOldSel
|| (mpImpl
->mnAngle
!= nAngle
) )
470 mpImpl
->mnAngle
= nAngle
;
472 if( mpImpl
->mpLinkField
)
473 mpImpl
->mpLinkField
->SetValue( static_cast< long >( GetRotation() / mpImpl
->mnLinkedFieldValueMultiplyer
) );
475 mpImpl
->maModifyHdl
.Call( this );
479 void DialControl::ImplSetFieldLink( const Link
& rLink
)
481 if( mpImpl
->mpLinkField
)
483 NumericField
& rField
= *mpImpl
->mpLinkField
;
484 rField
.SetModifyHdl( rLink
);
485 rField
.SetUpHdl( rLink
);
486 rField
.SetDownHdl( rLink
);
487 rField
.SetFirstHdl( rLink
);
488 rField
.SetLastHdl( rLink
);
489 rField
.SetLoseFocusHdl( rLink
);
493 void DialControl::HandleMouseEvent( const Point
& rPos
, bool bInitial
)
495 long nX
= rPos
.X() - mpImpl
->mnCenterX
;
496 long nY
= mpImpl
->mnCenterY
- rPos
.Y();
497 double fH
= sqrt( static_cast< double >( nX
) * nX
+ static_cast< double >( nY
) * nY
);
500 double fAngle
= acos( nX
/ fH
);
501 sal_Int32 nAngle
= static_cast< sal_Int32
>( fAngle
/ F_PI180
* 100.0 );
503 nAngle
= 36000 - nAngle
;
504 if( bInitial
) // round to entire 15 degrees
505 nAngle
= ((nAngle
+ 750) / 1500) * 1500;
506 // Round up to 1 degree
507 nAngle
= (((nAngle
+ 50) / 100) * 100) % 36000;
508 SetRotation( nAngle
, true );
512 void DialControl::HandleEscapeEvent()
514 if( IsMouseCaptured() )
517 SetRotation( mpImpl
->mnOldAngle
, true );
518 if( mpImpl
->mpLinkField
)
519 mpImpl
->mpLinkField
->GrabFocus();
523 IMPL_LINK( DialControl
, LinkedFieldModifyHdl
, NumericField
*, pField
)
526 SetRotation( static_cast< sal_Int32
>( pField
->GetValue() * mpImpl
->mnLinkedFieldValueMultiplyer
), false );
532 DialControlWrapper::DialControlWrapper( DialControl
& rDial
) :
533 SingleControlWrapperType( rDial
)
537 bool DialControlWrapper::IsControlDontKnow() const
539 return !GetControl().HasRotation();
542 void DialControlWrapper::SetControlDontKnow( bool bSet
)
545 GetControl().SetNoRotation();
548 sal_Int32
DialControlWrapper::GetControlValue() const
550 return GetControl().GetRotation();
553 void DialControlWrapper::SetControlValue( sal_Int32 nValue
)
555 GetControl().SetRotation( nValue
);
562 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */