LibreOffice Module svx (master)  1
dialcontrol.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <svx/dialcontrol.hxx>
21 #include <cmath>
22 #include <vcl/virdev.hxx>
23 #include <vcl/svapp.hxx>
24 #include <vcl/bitmapex.hxx>
25 #include <vcl/event.hxx>
26 #include <vcl/settings.hxx>
28 
29 namespace svx {
30 
31 const long DIAL_OUTER_WIDTH = 8;
32 
35  , mbEnabled(true)
36  , mrParent(rReference)
37  , mnCenterX(0)
38  , mnCenterY(0)
39 {
40  EnableRTL(false);
41 }
42 
44 {
45  Init();
46  SetFont(rFont);
47 }
48 
50 {
51  Init();
52  SetSize(rSrc.maRect.GetSize());
53  mbEnabled = rSrc.mbEnabled;
54  Point aPos;
55  DrawBitmapEx( aPos, rSrc.GetBitmapEx( aPos, maRect.GetSize() ) );
56 }
57 
58 void DialControlBmp::DrawBackground( const Size& rSize, bool bEnabled )
59 {
60  Init();
61  SetSize(rSize);
62  mbEnabled = bEnabled;
64 }
65 
66 void DialControlBmp::DrawElements( const OUString& rText, sal_Int32 nAngle )
67 {
68  double fAngle = basegfx::deg2rad(nAngle) / 100.0;
69  double fSin = sin( fAngle );
70  double fCos = cos( fAngle );
71  double fWidth = GetTextWidth( rText ) / 2.0;
72  double fHeight = GetTextHeight() / 2.0;
73 
74  if ( !rText.isEmpty() )
75  {
76  // rotated text
77  vcl::Font aFont( GetFont() );
78  aFont.SetColor( GetTextColor() );
79  aFont.SetOrientation( static_cast< short >( (nAngle + 5) / 10 ) ); // Font uses 1/10 degrees
80  aFont.SetWeight( WEIGHT_BOLD );
81  SetFont( aFont );
82 
83  long nX = static_cast< long >( mnCenterX - fWidth * fCos - fHeight * fSin );
84  long nY = static_cast< long >( mnCenterY + fWidth * fSin - fHeight * fCos );
85  tools::Rectangle aRect( nX, nY, 2 * mnCenterX - nX, 2 * mnCenterY - nY );
86  DrawText( aRect, rText, mbEnabled ? DrawTextFlags::NONE : DrawTextFlags::Disable );
87  }
88  else
89  {
90  // only a line
91  const sal_Int32 nDx (fCos * (maRect.GetWidth()-4) / 2);
92  const sal_Int32 nDy (-fSin * (maRect.GetHeight()-4) / 2);
93  Point pt1( maRect.Center() );
94  Point pt2( pt1.X() + nDx, pt1.Y() + nDy);
95 
97  DrawLine( pt1, pt2 );
98  }
99 
100  // *** drag button ***
101 
102  bool bMain = (nAngle % 4500) != 0;
104  SetFillColor( GetButtonFillColor( bMain ) );
105 
106  long nX = mnCenterX - static_cast< long >( (DIAL_OUTER_WIDTH / 2 - mnCenterX) * fCos );
107  long nY = mnCenterY - static_cast< long >( (mnCenterY - DIAL_OUTER_WIDTH / 2) * fSin );
108  long nSize = bMain ? (DIAL_OUTER_WIDTH / 4) : (DIAL_OUTER_WIDTH / 2 - 1);
109  DrawEllipse( tools::Rectangle( nX - nSize, nY - nSize, nX + nSize, nY + nSize ) );
110 }
111 
113 {
115 }
116 
118 {
120 }
121 
123 {
124  const StyleSettings& rSett = GetSettings().GetStyleSettings();
125  return mbEnabled ? rSett.GetButtonTextColor() : rSett.GetDisableColor();
126 }
127 
129 {
130  const StyleSettings& rSett = GetSettings().GetStyleSettings();
131  return mbEnabled ? rSett.GetButtonTextColor() : rSett.GetDisableColor();
132 }
133 
134 const Color& DialControlBmp::GetButtonFillColor( bool bMain ) const
135 {
136  const StyleSettings& rSett = GetSettings().GetStyleSettings();
137  return mbEnabled ? (bMain ? rSett.GetMenuColor() : rSett.GetHighlightColor()) : rSett.GetDisableColor();
138 }
139 
141 {
144 }
145 
146 void DialControlBmp::SetSize( const Size& rSize )
147 {
148  maRect.SetPos( Point( 0, 0 ) );
149  maRect.SetSize( rSize );
150  mnCenterX = rSize.Width() / 2;
151  mnCenterY = rSize.Height() / 2;
152  SetOutputSize( rSize );
153 }
154 
156 {
157  // *** background with 3D effect ***
158 
159  SetLineColor();
160  SetFillColor();
161  Erase();
162 
163  EnableRTL(); // draw 3D effect in correct direction
164 
165  sal_uInt8 nDiff = mbEnabled ? 0x18 : 0x10;
166  Color aColor;
167 
168  aColor = GetBackgroundColor();
169  SetFillColor( aColor );
172 
173  aColor.DecreaseLuminance( nDiff );
174  SetFillColor( aColor );
176 
177  aColor.DecreaseLuminance( nDiff );
178  SetFillColor( aColor );
180 
181  aColor = GetBackgroundColor();
182  aColor.IncreaseLuminance( nDiff );
183  SetFillColor( aColor );
185 
186  aColor.IncreaseLuminance( nDiff );
187  SetFillColor( aColor );
189 
190  EnableRTL( false );
191 
192  // *** calibration ***
193 
194  Point aStartPos( mnCenterX, mnCenterY );
195  Color aFullColor( GetScaleLineColor() );
196  Color aLightColor( GetBackgroundColor() );
197  aLightColor.Merge( aFullColor, 128 );
198 
199  for( int nAngle = 0; nAngle < 360; nAngle += 15 )
200  {
201  SetLineColor( (nAngle % 45) ? aLightColor : aFullColor );
202  double fAngle = basegfx::deg2rad(nAngle);
203  long nX = static_cast< long >( -mnCenterX * cos( fAngle ) );
204  long nY = static_cast< long >( mnCenterY * sin( fAngle ) );
205  DrawLine( aStartPos, Point( mnCenterX - nX, mnCenterY - nY ) );
206  }
207 
208  // *** clear inner area ***
209 
210  SetLineColor();
212  tools::Rectangle aEllipseRect = maRect;
213  aEllipseRect.shrink(DIAL_OUTER_WIDTH);
214  DrawEllipse( aEllipseRect );
215 }
216 
218  mxBmpEnabled(VclPtr<DialControlBmp>::Create(rReference)),
219  mxBmpDisabled(VclPtr<DialControlBmp>::Create(rReference)),
220  mxBmpBuffered(VclPtr<DialControlBmp>::Create(rReference)),
221  mpLinkField( nullptr ),
222  mnLinkedFieldValueMultiplyer( 0 ),
223  mnAngle( 0 ),
224  mnInitialAngle( 0 ),
225  mnOldAngle( 0 ),
226  mnCenterX( 0 ),
227  mnCenterY( 0 ),
228  mbNoRot( false )
229 {
230 }
231 
232 void DialControl::DialControl_Impl::Init( const Size& rWinSize, const vcl::Font& rWinFont )
233 {
234  maWinFont = rWinFont;
235  maWinFont.SetTransparent(true);
236  mxBmpBuffered->InitBitmap(maWinFont);
237  SetSize(rWinSize);
238 }
239 
241 {
242  // make the control squared, and adjusted so that we have a well-defined
243  // center ["(x - 1) | 1" creates odd value <= x]
244  long nMin = (std::min(rWinSize.Width(), rWinSize.Height()) - 1) | 1;
245 
246  maWinSize = Size( nMin, nMin );
247 
248  mnCenterX = maWinSize.Width() / 2;
249  mnCenterY = maWinSize.Height() / 2;
250 
251  mxBmpEnabled->DrawBackground( maWinSize, true );
252  mxBmpDisabled->DrawBackground( maWinSize, false );
253  mxBmpBuffered->SetSize( maWinSize );
254 }
255 
257 {
258  CustomWidgetController::SetDrawingArea(pDrawingArea);
259  //use same logic as DialControl_Impl::SetSize
260  int nDim = (std::min<int>(pDrawingArea->get_approximate_digit_width() * 12,
261  pDrawingArea->get_text_height() * 6) - 1) | 1;
262  Size aSize(nDim, nDim);
263  pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
264  mpImpl.reset(new DialControl_Impl(pDrawingArea->get_ref_device()));
265  //set size and use that
266  Init(aSize);
267 }
268 
270 {
271  mpImpl->SetSize(GetOutputSizePixel());
273 }
274 
276 {
277  Point aPos;
278  rRenderContext.DrawBitmapEx(aPos, mpImpl->mxBmpBuffered->GetBitmapEx(aPos, mpImpl->maWinSize));
279 }
280 
282 {
283  CustomWidgetController::StyleUpdated();
284  Init( mpImpl->maWinSize, mpImpl->maWinFont );
286 }
287 
289 {
290  if( rMEvt.IsLeft() )
291  {
292  GrabFocus();
293  CaptureMouse();
294  mpImpl->mnOldAngle = mpImpl->mnAngle;
295  HandleMouseEvent( rMEvt.GetPosPixel(), true );
296  }
297  return true;
298 }
299 
300 bool DialControl::MouseMove( const MouseEvent& rMEvt )
301 {
302  if( IsMouseCaptured() && rMEvt.IsLeft() )
303  HandleMouseEvent( rMEvt.GetPosPixel(), false );
304  return true;
305 }
306 
308 {
309  if( IsMouseCaptured() )
310  {
311  ReleaseMouse();
312  if( mpImpl->mpLinkField )
313  mpImpl->mpLinkField->grab_focus();
314  }
315  return true;
316 }
317 
318 bool DialControl::KeyInput( const KeyEvent& rKEvt )
319 {
320  const vcl::KeyCode& rKCode = rKEvt.GetKeyCode();
321  if( !rKCode.GetModifier() && (rKCode.GetCode() == KEY_ESCAPE) )
322  {
324  return true;
325  }
326  return CustomWidgetController::KeyInput(rKEvt);
327 }
328 
330 {
331  // release captured mouse
333 }
334 
336 {
337  return !mpImpl->mbNoRot;
338 }
339 
341 {
342  if( !mpImpl->mbNoRot )
343  {
344  mpImpl->mbNoRot = true;
346  if (mpImpl->mpLinkField)
347  mpImpl->mpLinkField->set_text("");
348  }
349 }
350 
351 sal_Int32 DialControl::GetRotation() const
352 {
353  return mpImpl->mnAngle;
354 }
355 
356 void DialControl::SetRotation(sal_Int32 nAngle)
357 {
358  SetRotation(nAngle, false);
359 }
360 
361 void DialControl::SetLinkedField(weld::MetricSpinButton* pField, sal_Int32 nDecimalPlaces)
362 {
363  mpImpl->mnLinkedFieldValueMultiplyer = 100 / std::pow(10.0, double(nDecimalPlaces));
364 
365  // remove modify handler from old linked field
366  if( mpImpl->mpLinkField )
367  {
368  weld::MetricSpinButton& rField = *mpImpl->mpLinkField;
370  }
371  // remember the new linked field
372  mpImpl->mpLinkField = pField;
373  // set modify handler at new linked field
374  if( mpImpl->mpLinkField )
375  {
376  weld::MetricSpinButton& rField = *mpImpl->mpLinkField;
377  rField.connect_value_changed(LINK(this, DialControl, LinkedFieldModifyHdl));
378  }
379 }
380 
381 IMPL_LINK_NOARG(DialControl, LinkedFieldModifyHdl, weld::MetricSpinButton&, void)
382 {
383  SetRotation(mpImpl->mpLinkField->get_value(FieldUnit::DEGREE) * mpImpl->mnLinkedFieldValueMultiplyer, true);
384 }
385 
387 {
388  mpImpl->mnInitialAngle = mpImpl->mnAngle;
389 }
390 
392 {
393  return mpImpl->mnInitialAngle != mpImpl->mnAngle;
394 }
395 
396 void DialControl::Init( const Size& rWinSize, const vcl::Font& rWinFont )
397 {
398  mpImpl->Init( rWinSize, rWinFont );
399  EnableRTL( false ); // don't mirror mouse handling
400  SetOutputSizePixel( mpImpl->maWinSize );
401 }
402 
403 void DialControl::Init( const Size& rWinSize )
404 {
405  //hidpi TODO: GetDefaultFont() picks a font size too small, so fix it here.
407 
409  DefaultFontType::UI_SANS, Application::GetSettings().GetUILanguageTag().getLanguageType(), GetDefaultFontFlags::OnlyOne ) );
410 
411  aFont.SetFontHeight(aDefaultSize.GetFontHeight());
412  Init( rWinSize, aFont );
413 }
414 
416 {
417  mpImpl->mxBmpBuffered->CopyBackground( IsEnabled() ? *mpImpl->mxBmpEnabled : *mpImpl->mxBmpDisabled );
418  if( !mpImpl->mbNoRot )
419  mpImpl->mxBmpBuffered->DrawElements(GetText(), mpImpl->mnAngle);
420  Invalidate();
421 }
422 
423 void DialControl::SetRotation(sal_Int32 nAngle, bool bBroadcast)
424 {
425  bool bOldSel = mpImpl->mbNoRot;
426  mpImpl->mbNoRot = false;
427 
428  while (nAngle < 0)
429  nAngle += 36000;
430 
431  if (!bOldSel || (mpImpl->mnAngle != nAngle))
432  {
433  mpImpl->mnAngle = nAngle;
435  if( mpImpl->mpLinkField )
436  mpImpl->mpLinkField->set_value(GetRotation() / mpImpl->mnLinkedFieldValueMultiplyer, FieldUnit::DEGREE);
437  if( bBroadcast )
438  mpImpl->maModifyHdl.Call(*this);
439  }
440 }
441 
443 {
444  mpImpl->maModifyHdl = rLink;
445 }
446 
447 void DialControl::HandleMouseEvent( const Point& rPos, bool bInitial )
448 {
449  long nX = rPos.X() - mpImpl->mnCenterX;
450  long nY = mpImpl->mnCenterY - rPos.Y();
451  double fH = sqrt( static_cast< double >( nX ) * nX + static_cast< double >( nY ) * nY );
452  if( fH != 0.0 )
453  {
454  double fAngle = acos( nX / fH );
455  sal_Int32 nAngle = static_cast<sal_Int32>(basegfx::rad2deg(fAngle) * 100.0);
456  if( nY < 0 )
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(nAngle, true);
463  }
464 }
465 
467 {
468  if( IsMouseCaptured() )
469  {
470  ReleaseMouse();
471  SetRotation(mpImpl->mnOldAngle, true);
472  if( mpImpl->mpLinkField )
473  mpImpl->mpLinkField->grab_focus();
474  }
475 }
476 
477 }
478 
479 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Point TopLeft() const
long Width() const
void DrawText(const Point &rStartPt, const OUString &rStr, sal_Int32 nIndex=0, sal_Int32 nLen=-1, MetricVector *pVector=nullptr, OUString *pDisplayText=nullptr, const SalLayoutGlyphs *pLayoutCache=nullptr)
void SetPos(const Point &rPoint)
const Color & GetButtonLineColor() const
long GetWidth() const
void Init(const Size &rWinSize, const vcl::Font &rWinFont)
static vcl::Font GetDefaultFont(DefaultFontType nType, LanguageType eLang, GetDefaultFontFlags nFlags, const OutputDevice *pOutDev=nullptr)
bool HasRotation() const
Returns true, if the control is not in "don't care" state.
const Color & GetDialogColor() const
long GetHeight() const
virtual void LoseFocus() override
void SetSize(const Size &rSize)
virtual bool MouseButtonUp(const MouseEvent &rMEvt) override
virtual bool MouseButtonDown(const MouseEvent &rMEvt) override
long GetFontHeight() const
void Merge(const Color &rMergeColor, sal_uInt8 cTransparency)
long Height() const
void DrawBitmapEx(const Point &rDestPt, const BitmapEx &rBitmapEx)
Point BottomLeft() const
This control allows to input a rotation angle, visualized by a dial.
Definition: dialcontrol.hxx:76
void Create(SwFormatVertOrient &rItem, SvStream &rStrm, sal_uInt16 nVersionAbusedAsSize)
void Init(const Size &rWinSize)
const StyleSettings & GetStyleSettings() const
static const AllSettings & GetSettings()
void SetSize(const Size &rWinSize)
void SetLinkedField(weld::MetricSpinButton *pField, sal_Int32 nDecimalPlaces=0)
Links the passed numeric edit field to the control (bi-directional).
void SetWeight(FontWeight)
const Color & GetScaleLineColor() const
sal_uInt16 GetCode() const
virtual bool KeyInput(const KeyEvent &rKEvt) override
const Color & GetLabelTextColor() const
virtual void SetSettings(const AllSettings &rSettings)
tools::Rectangle maRect
Definition: dialcontrol.hxx:55
bool IsValueModified() const
Compare value with the saved value.
void SetOrientation(short nLineOrientation)
constexpr double rad2deg(double v)
const Color & GetHighlightColor() const
Size const & GetOutputSizePixel() const
void InitBitmap(const vcl::Font &rFont)
Definition: dialcontrol.cxx:43
Point RightCenter() const
WEIGHT_BOLD
void connect_value_changed(const Link< MetricSpinButton &, void > &rLink)
void SetBackground()
void CopyBackground(const DialControlBmp &rSrc)
Definition: dialcontrol.cxx:49
const vcl::Font & GetLabelFont() const
const vcl::Font & GetFont() const
Point BottomCenter() const
std::unique_ptr< DialControl_Impl > mpImpl
OutputDevice & mrParent
Definition: dialcontrol.hxx:57
const Color & GetTextColor() const
sal_uInt16 GetModifier() const
virtual OutputDevice & get_ref_device()=0
Point LeftCenter() const
void DrawLine(const Point &rStartPt, const Point &rEndPt)
virtual void StyleUpdated() override
const Color & GetMenuColor() const
IMPL_LINK_NOARG(SuggestionDisplay, SelectSuggestionValueSetHdl, ValueSet *, void)
void DrawElements(const OUString &rText, sal_Int32 nAngle)
Definition: dialcontrol.cxx:66
Point BottomRight() const
void SetLineColor()
const Color & GetDisableColor() const
void HandleMouseEvent(const Point &rPos, bool bInitial)
void DecreaseLuminance(sal_uInt8 cLumDec)
virtual void EnableRTL(bool bEnable=true) override
void SetSize(const Size &rSize)
void InvalidateControl()
bool SetOutputSize(const Size &rNewSize)
void SetFillColor()
virtual bool MouseMove(const MouseEvent &rMEvt) override
constexpr double deg2rad(double v)
const long DIAL_OUTER_WIDTH
Definition: dialcontrol.cxx:31
const AllSettings & GetSettings() const
void DrawPie(const tools::Rectangle &rRect, const Point &rStartPt, const Point &rEndPt)
Color GetBackgroundColor() const override
long GetTextHeight() const
void SetOutputSizePixel(const Size &rSize)
void SetModifyHdl(const Link< DialControl &, void > &rLink)
Size GetSize() const
void shrink(long nShrinkBy)
void SetColor(const Color &)
virtual void Paint(vcl::RenderContext &rRenderContext, const tools::Rectangle &rRect) override
DeviceFormat
const vcl::KeyCode & GetKeyCode() const
virtual void Resize() override
void HandleEscapeEvent()
const Color & GetButtonTextColor() const
unsigned char sal_uInt8
constexpr sal_uInt16 KEY_ESCAPE
void SetFont(const vcl::Font &rNewFont)
virtual void SetDrawingArea(weld::DrawingArea *pDrawingArea) override
void SetTransparent(bool bTransparent)
bool IsLeft() const
virtual int get_text_height() const =0
void SetFontHeight(long nHeight)
const Point & GetPosPixel() const
long GetTextWidth(const OUString &rStr, sal_Int32 nIndex=0, sal_Int32 nLen=-1, vcl::TextLayoutCache const *=nullptr, SalLayoutGlyphs const *const pLayoutCache=nullptr) const
void SaveValue()
Save value for later comparison.
void DrawEllipse(const tools::Rectangle &rRect)
BitmapEx GetBitmapEx(const Point &rSrcPt, const Size &rSize) const
sal_Int32 GetRotation() const
Returns the current rotation angle in 1/100 degrees.
DialControlBmp(OutputDevice &rReference)
Definition: dialcontrol.cxx:33
void SetNoRotation()
Sets the control to "don't care" state.
const OUString & GetText() const
DialControl_Impl(OutputDevice &rReference)
const Color & GetButtonFillColor(bool bMain) const
virtual void set_size_request(int nWidth, int nHeight)=0
BaseContainerNodeSharedPtr & mrParent
Point TopRight() const
virtual float get_approximate_digit_width() const =0
sal_Int32 mnAngle
Point Center() const
Point TopCenter() const
void EnableRTL(bool bEnable)
void SetRotation(sal_Int32 nAngle)
Sets the rotation to the passed value (in 1/100 degrees).