1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_svx.hxx" 30 #include "svx/dialcontrol.hxx" 31 32 #include <math.h> 33 #include <vcl/virdev.hxx> 34 #include <vcl/svapp.hxx> 35 #include <vcl/bitmap.hxx> 36 #include <vcl/field.hxx> 37 #include <svtools/colorcfg.hxx> 38 39 namespace svx { 40 41 // ============================================================================ 42 43 const long DIAL_OUTER_WIDTH = 8; 44 45 // ============================================================================ 46 47 class DialControlBmp : public VirtualDevice 48 { 49 public: 50 explicit DialControlBmp( Window& rParent ); 51 52 void InitBitmap( const Size& rSize, const Font& rFont ); 53 void CopyBackground( const DialControlBmp& rSrc ); 54 void DrawBackground( const Size& rSize, bool bEnabled ); 55 void DrawElements( const String& rText, sal_Int32 nAngle ); 56 57 private: 58 const Color& GetBackgroundColor() const; 59 const Color& GetTextColor() const; 60 const Color& GetScaleLineColor() const; 61 const Color& GetButtonLineColor() const; 62 const Color& GetButtonFillColor( bool bMain ) const; 63 64 void Init( const Size& rSize ); 65 void DrawBackground(); 66 67 Window& mrParent; 68 Rectangle maRect; 69 long mnCenterX; 70 long mnCenterY; 71 bool mbEnabled; 72 }; 73 74 // ---------------------------------------------------------------------------- 75 76 DialControlBmp::DialControlBmp( Window& rParent ) : 77 VirtualDevice( rParent, 0, 0 ), 78 mrParent( rParent ), 79 mbEnabled( true ) 80 { 81 EnableRTL( sal_False ); 82 } 83 84 void DialControlBmp::InitBitmap( const Size& rSize, const Font& rFont ) 85 { 86 Init( rSize ); 87 SetFont( rFont ); 88 } 89 90 void DialControlBmp::CopyBackground( const DialControlBmp& rSrc ) 91 { 92 Init( rSrc.maRect.GetSize() ); 93 mbEnabled = rSrc.mbEnabled; 94 Point aPos; 95 DrawBitmapEx( aPos, rSrc.GetBitmapEx( aPos, maRect.GetSize() ) ); 96 } 97 98 void DialControlBmp::DrawBackground( const Size& rSize, bool bEnabled ) 99 { 100 Init( rSize ); 101 mbEnabled = bEnabled; 102 DrawBackground(); 103 } 104 105 void DialControlBmp::DrawElements( const String& rText, sal_Int32 nAngle ) 106 { 107 // *** rotated text *** 108 109 Font aFont( GetFont() ); 110 aFont.SetColor( GetTextColor() ); 111 aFont.SetOrientation( static_cast< short >( (nAngle + 5) / 10 ) ); // Font uses 1/10 degrees 112 aFont.SetWeight( WEIGHT_BOLD ); 113 SetFont( aFont ); 114 115 double fAngle = nAngle * F_PI180 / 100.0; 116 double fSin = sin( fAngle ); 117 double fCos = cos( fAngle ); 118 double fWidth = GetTextWidth( rText ) / 2.0; 119 double fHeight = GetTextHeight() / 2.0; 120 long nX = static_cast< long >( mnCenterX - fWidth * fCos - fHeight * fSin ); 121 long nY = static_cast< long >( mnCenterY + fWidth * fSin - fHeight * fCos ); 122 Rectangle aRect( nX, nY, 2 * mnCenterX - nX, 2 * mnCenterY - nY ); 123 DrawText( aRect, rText, mbEnabled ? 0 : TEXT_DRAW_DISABLE ); 124 125 // *** drag button *** 126 127 bool bMain = (nAngle % 4500) != 0; 128 SetLineColor( GetButtonLineColor() ); 129 SetFillColor( GetButtonFillColor( bMain ) ); 130 131 nX = mnCenterX - static_cast< long >( (DIAL_OUTER_WIDTH / 2 - mnCenterX) * fCos ); 132 nY = mnCenterY - static_cast< long >( (mnCenterY - DIAL_OUTER_WIDTH / 2) * fSin ); 133 long nSize = bMain ? (DIAL_OUTER_WIDTH / 4) : (DIAL_OUTER_WIDTH / 2 - 1); 134 DrawEllipse( Rectangle( nX - nSize, nY - nSize, nX + nSize, nY + nSize ) ); 135 } 136 137 // private -------------------------------------------------------------------- 138 139 const Color& DialControlBmp::GetBackgroundColor() const 140 { 141 return GetSettings().GetStyleSettings().GetDialogColor(); 142 } 143 144 const Color& DialControlBmp::GetTextColor() const 145 { 146 return GetSettings().GetStyleSettings().GetLabelTextColor(); 147 } 148 149 const Color& DialControlBmp::GetScaleLineColor() const 150 { 151 const StyleSettings& rSett = GetSettings().GetStyleSettings(); 152 return mbEnabled ? rSett.GetButtonTextColor() : rSett.GetDisableColor(); 153 } 154 155 const Color& DialControlBmp::GetButtonLineColor() const 156 { 157 const StyleSettings& rSett = GetSettings().GetStyleSettings(); 158 return mbEnabled ? rSett.GetButtonTextColor() : rSett.GetDisableColor(); 159 } 160 161 const Color& DialControlBmp::GetButtonFillColor( bool bMain ) const 162 { 163 const StyleSettings& rSett = GetSettings().GetStyleSettings(); 164 return mbEnabled ? (bMain ? rSett.GetMenuColor() : rSett.GetHighlightColor()) : rSett.GetDisableColor(); 165 } 166 167 void DialControlBmp::Init( const Size& rSize ) 168 { 169 SetSettings( mrParent.GetSettings() ); 170 maRect.SetPos( Point( 0, 0 ) ); 171 maRect.SetSize( rSize ); 172 mnCenterX = rSize.Width() / 2; 173 mnCenterY = rSize.Height() / 2; 174 SetOutputSize( rSize ); 175 SetBackground(); 176 } 177 178 void DialControlBmp::DrawBackground() 179 { 180 // *** background with 3D effect *** 181 182 SetLineColor(); 183 SetFillColor(); 184 Erase(); 185 186 EnableRTL( sal_True ); // #107807# draw 3D effect in correct direction 187 188 sal_uInt8 nDiff = mbEnabled ? 0x18 : 0x10; 189 Color aColor; 190 191 aColor = GetBackgroundColor(); 192 SetFillColor( aColor ); 193 DrawPie( maRect, maRect.TopRight(), maRect.TopCenter() ); 194 DrawPie( maRect, maRect.BottomLeft(), maRect.BottomCenter() ); 195 196 aColor.DecreaseLuminance( nDiff ); 197 SetFillColor( aColor ); 198 DrawPie( maRect, maRect.BottomCenter(), maRect.TopRight() ); 199 200 aColor.DecreaseLuminance( nDiff ); 201 SetFillColor( aColor ); 202 DrawPie( maRect, maRect.BottomRight(), maRect.RightCenter() ); 203 204 aColor = GetBackgroundColor(); 205 aColor.IncreaseLuminance( nDiff ); 206 SetFillColor( aColor ); 207 DrawPie( maRect, maRect.TopCenter(), maRect.BottomLeft() ); 208 209 aColor.IncreaseLuminance( nDiff ); 210 SetFillColor( aColor ); 211 DrawPie( maRect, maRect.TopLeft(), maRect.LeftCenter() ); 212 213 EnableRTL( sal_False ); 214 215 // *** calibration *** 216 217 Point aStartPos( mnCenterX, mnCenterY ); 218 Color aFullColor( GetScaleLineColor() ); 219 Color aLightColor( GetBackgroundColor() ); 220 aLightColor.Merge( aFullColor, 128 ); 221 222 for( int nAngle = 0; nAngle < 360; nAngle += 15 ) 223 { 224 SetLineColor( (nAngle % 45) ? aLightColor : aFullColor ); 225 double fAngle = nAngle * F_PI180; 226 long nX = static_cast< long >( -mnCenterX * cos( fAngle ) ); 227 long nY = static_cast< long >( mnCenterY * sin( fAngle ) ); 228 DrawLine( aStartPos, Point( mnCenterX - nX, mnCenterY - nY ) ); 229 } 230 231 // *** clear inner area *** 232 233 SetLineColor(); 234 SetFillColor( GetBackgroundColor() ); 235 DrawEllipse( Rectangle( maRect.Left() + DIAL_OUTER_WIDTH, maRect.Top() + DIAL_OUTER_WIDTH, 236 maRect.Right() - DIAL_OUTER_WIDTH, maRect.Bottom() - DIAL_OUTER_WIDTH ) ); 237 } 238 239 // ============================================================================ 240 241 struct DialControl_Impl 242 { 243 DialControlBmp maBmpEnabled; 244 DialControlBmp maBmpDisabled; 245 DialControlBmp maBmpBuffered; 246 Link maModifyHdl; 247 NumericField* mpLinkField; 248 Size maWinSize; 249 Font maWinFont; 250 sal_Int32 mnAngle; 251 sal_Int32 mnOldAngle; 252 long mnCenterX; 253 long mnCenterY; 254 bool mbNoRot; 255 256 explicit DialControl_Impl( Window& rParent ); 257 void Init( const Size& rWinSize, const Font& rWinFont ); 258 }; 259 260 // ---------------------------------------------------------------------------- 261 262 DialControl_Impl::DialControl_Impl( Window& rParent ) : 263 maBmpEnabled( rParent ), 264 maBmpDisabled( rParent ), 265 maBmpBuffered( rParent ), 266 mpLinkField( 0 ), 267 mnAngle( 0 ), 268 mbNoRot( false ) 269 { 270 } 271 272 void DialControl_Impl::Init( const Size& rWinSize, const Font& rWinFont ) 273 { 274 // "(x - 1) | 1" creates odd value <= x, to have a well-defined center pixel position 275 maWinSize = Size( (rWinSize.Width() - 1) | 1, (rWinSize.Height() - 1) | 1 ); 276 maWinFont = rWinFont; 277 278 mnCenterX = maWinSize.Width() / 2; 279 mnCenterY = maWinSize.Height() / 2; 280 maWinFont.SetTransparent( sal_True ); 281 282 maBmpEnabled.DrawBackground( maWinSize, true ); 283 maBmpDisabled.DrawBackground( maWinSize, false ); 284 maBmpBuffered.InitBitmap( maWinSize, maWinFont ); 285 } 286 287 // ============================================================================ 288 289 DialControl::DialControl( Window* pParent, const Size& rSize, const Font& rFont, WinBits nWinStyle ) : 290 Control( pParent, nWinStyle ), 291 mpImpl( new DialControl_Impl( *this ) ) 292 { 293 Init( rSize, rFont ); 294 } 295 296 DialControl::DialControl( Window* pParent, const Size& rSize, WinBits nWinStyle ) : 297 Control( pParent, nWinStyle ), 298 mpImpl( new DialControl_Impl( *this ) ) 299 { 300 if( pParent ) 301 Init( rSize, pParent->GetFont() ); 302 else 303 Init( rSize ); 304 } 305 306 DialControl::DialControl( Window* pParent, const ResId& rResId ) : 307 Control( pParent, rResId ), 308 mpImpl( new DialControl_Impl( *this ) ) 309 { 310 Init( GetOutputSizePixel() ); 311 } 312 313 DialControl::~DialControl() 314 { 315 } 316 317 void DialControl::Paint( const Rectangle& ) 318 { 319 Point aPos; 320 DrawBitmapEx( aPos, mpImpl->maBmpBuffered.GetBitmapEx( aPos, mpImpl->maWinSize ) ); 321 } 322 323 void DialControl::StateChanged( StateChangedType nStateChange ) 324 { 325 if( nStateChange == STATE_CHANGE_ENABLE ) 326 InvalidateControl(); 327 328 // update the linked edit field 329 if( mpImpl->mpLinkField ) 330 { 331 NumericField& rField = *mpImpl->mpLinkField; 332 switch( nStateChange ) 333 { 334 case STATE_CHANGE_VISIBLE: rField.Show( IsVisible() ); break; 335 case STATE_CHANGE_ENABLE: rField.Enable( IsEnabled() ); break; 336 } 337 } 338 339 Control::StateChanged( nStateChange ); 340 } 341 342 void DialControl::DataChanged( const DataChangedEvent& rDCEvt ) 343 { 344 if( (rDCEvt.GetType() == DATACHANGED_SETTINGS) && (rDCEvt.GetFlags() & SETTINGS_STYLE) ) 345 { 346 Init( mpImpl->maWinSize, mpImpl->maWinFont ); 347 InvalidateControl(); 348 } 349 Control::DataChanged( rDCEvt ); 350 } 351 352 void DialControl::MouseButtonDown( const MouseEvent& rMEvt ) 353 { 354 if( rMEvt.IsLeft() ) 355 { 356 GrabFocus(); 357 CaptureMouse(); 358 mpImpl->mnOldAngle = mpImpl->mnAngle; 359 HandleMouseEvent( rMEvt.GetPosPixel(), true ); 360 } 361 Control::MouseButtonDown( rMEvt ); 362 } 363 364 void DialControl::MouseMove( const MouseEvent& rMEvt ) 365 { 366 if( IsMouseCaptured() && rMEvt.IsLeft() ) 367 HandleMouseEvent( rMEvt.GetPosPixel(), false ); 368 Control::MouseMove(rMEvt ); 369 } 370 371 void DialControl::MouseButtonUp( const MouseEvent& rMEvt ) 372 { 373 if( IsMouseCaptured() ) 374 { 375 ReleaseMouse(); 376 if( mpImpl->mpLinkField ) 377 mpImpl->mpLinkField->GrabFocus(); 378 } 379 Control::MouseButtonUp( rMEvt ); 380 } 381 382 void DialControl::KeyInput( const KeyEvent& rKEvt ) 383 { 384 const KeyCode& rKCode = rKEvt.GetKeyCode(); 385 if( !rKCode.GetModifier() && (rKCode.GetCode() == KEY_ESCAPE) ) 386 HandleEscapeEvent(); 387 else 388 Control::KeyInput( rKEvt ); 389 } 390 391 void DialControl::LoseFocus() 392 { 393 // release captured mouse 394 HandleEscapeEvent(); 395 Control::LoseFocus(); 396 } 397 398 bool DialControl::HasRotation() const 399 { 400 return !mpImpl->mbNoRot; 401 } 402 403 void DialControl::SetNoRotation() 404 { 405 if( !mpImpl->mbNoRot ) 406 { 407 mpImpl->mbNoRot = true; 408 InvalidateControl(); 409 if( mpImpl->mpLinkField ) 410 mpImpl->mpLinkField->SetText( String() ); 411 } 412 } 413 414 sal_Int32 DialControl::GetRotation() const 415 { 416 return mpImpl->mnAngle; 417 } 418 419 void DialControl::SetRotation( sal_Int32 nAngle ) 420 { 421 ImplSetRotation( nAngle, false ); 422 } 423 424 void DialControl::SetLinkedField( NumericField* pField ) 425 { 426 // remove modify handler from old linked field 427 ImplSetFieldLink( Link() ); 428 // remember the new linked field 429 mpImpl->mpLinkField = pField; 430 // set modify handler at new linked field 431 ImplSetFieldLink( LINK( this, DialControl, LinkedFieldModifyHdl ) ); 432 } 433 434 NumericField* DialControl::GetLinkedField() const 435 { 436 return mpImpl->mpLinkField; 437 } 438 439 void DialControl::SetModifyHdl( const Link& rLink ) 440 { 441 mpImpl->maModifyHdl = rLink; 442 } 443 444 const Link& DialControl::GetModifyHdl() const 445 { 446 return mpImpl->maModifyHdl; 447 } 448 449 // private -------------------------------------------------------------------- 450 451 void DialControl::Init( const Size& rWinSize, const Font& rWinFont ) 452 { 453 mpImpl->Init( rWinSize, rWinFont ); 454 EnableRTL( sal_False ); // #107807# don't mirror mouse handling 455 SetOutputSizePixel( mpImpl->maWinSize ); 456 SetBackground(); 457 } 458 459 void DialControl::Init( const Size& rWinSize ) 460 { 461 Font aFont( OutputDevice::GetDefaultFont( 462 DEFAULTFONT_UI_SANS, Application::GetSettings().GetUILanguage(), DEFAULTFONT_FLAGS_ONLYONE ) ); 463 Init( rWinSize, aFont ); 464 } 465 466 void DialControl::InvalidateControl() 467 { 468 mpImpl->maBmpBuffered.CopyBackground( IsEnabled() ? mpImpl->maBmpEnabled : mpImpl->maBmpDisabled ); 469 if( !mpImpl->mbNoRot ) 470 mpImpl->maBmpBuffered.DrawElements( GetText(), mpImpl->mnAngle ); 471 Invalidate(); 472 } 473 474 void DialControl::ImplSetRotation( sal_Int32 nAngle, bool bBroadcast ) 475 { 476 bool bOldSel = mpImpl->mbNoRot; 477 mpImpl->mbNoRot = false; 478 479 while( nAngle < 0 ) nAngle += 36000; 480 nAngle = (((nAngle + 50) / 100) * 100) % 36000; 481 if( !bOldSel || (mpImpl->mnAngle != nAngle) ) 482 { 483 mpImpl->mnAngle = nAngle; 484 InvalidateControl(); 485 if( mpImpl->mpLinkField ) 486 mpImpl->mpLinkField->SetValue( static_cast< long >( GetRotation() / 100 ) ); 487 if( bBroadcast ) 488 mpImpl->maModifyHdl.Call( this ); 489 } 490 } 491 492 void DialControl::ImplSetFieldLink( const Link& rLink ) 493 { 494 if( mpImpl->mpLinkField ) 495 { 496 NumericField& rField = *mpImpl->mpLinkField; 497 rField.SetModifyHdl( rLink ); 498 rField.SetUpHdl( rLink ); 499 rField.SetDownHdl( rLink ); 500 rField.SetFirstHdl( rLink ); 501 rField.SetLastHdl( rLink ); 502 rField.SetLoseFocusHdl( rLink ); 503 } 504 } 505 506 void DialControl::HandleMouseEvent( const Point& rPos, bool bInitial ) 507 { 508 long nX = rPos.X() - mpImpl->mnCenterX; 509 long nY = mpImpl->mnCenterY - rPos.Y(); 510 double fH = sqrt( static_cast< double >( nX ) * nX + static_cast< double >( nY ) * nY ); 511 if( fH != 0.0 ) 512 { 513 double fAngle = acos( nX / fH ); 514 sal_Int32 nAngle = static_cast< sal_Int32 >( fAngle / F_PI180 * 100.0 ); 515 if( nY < 0 ) 516 nAngle = 36000 - nAngle; 517 if( bInitial ) // round to entire 15 degrees 518 nAngle = ((nAngle + 750) / 1500) * 1500; 519 ImplSetRotation( nAngle, true ); 520 } 521 } 522 523 void DialControl::HandleEscapeEvent() 524 { 525 if( IsMouseCaptured() ) 526 { 527 ReleaseMouse(); 528 ImplSetRotation( mpImpl->mnOldAngle, true ); 529 if( mpImpl->mpLinkField ) 530 mpImpl->mpLinkField->GrabFocus(); 531 } 532 } 533 534 IMPL_LINK( DialControl, LinkedFieldModifyHdl, NumericField*, pField ) 535 { 536 if( pField ) 537 ImplSetRotation( static_cast< sal_Int32 >( pField->GetValue() * 100 ), false ); 538 return 0; 539 } 540 541 // ============================================================================ 542 543 DialControlWrapper::DialControlWrapper( DialControl& rDial ) : 544 SingleControlWrapperType( rDial ) 545 { 546 } 547 548 bool DialControlWrapper::IsControlDontKnow() const 549 { 550 return !GetControl().HasRotation(); 551 } 552 553 void DialControlWrapper::SetControlDontKnow( bool bSet ) 554 { 555 if( bSet ) 556 GetControl().SetNoRotation(); 557 } 558 559 sal_Int32 DialControlWrapper::GetControlValue() const 560 { 561 return GetControl().GetRotation(); 562 } 563 564 void DialControlWrapper::SetControlValue( sal_Int32 nValue ) 565 { 566 GetControl().SetRotation( nValue ); 567 } 568 569 // ============================================================================ 570 571 } // namespace svx 572 573