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 <svx/svdtrans.hxx>
22 #include <cmath>
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>
29 
30 namespace svx {
31 
33 
36  , mbEnabled(true)
37  , mrParent(rReference)
38  , mnCenterX(0)
39  , mnCenterY(0)
40 {
41  EnableRTL(false);
42 }
43 
45 {
46  Init();
47  SetFont(rFont);
48 }
49 
51 {
52  Init();
53  SetSize(rSrc.maRect.GetSize());
54  mbEnabled = rSrc.mbEnabled;
55  Point aPos;
56  DrawBitmapEx( aPos, rSrc.GetBitmapEx( aPos, maRect.GetSize() ) );
57 }
58 
59 void DialControlBmp::DrawBackground( const Size& rSize, bool bEnabled )
60 {
61  Init();
62  SetSize(rSize);
63  mbEnabled = bEnabled;
65 }
66 
67 void DialControlBmp::DrawElements( const OUString& rText, Degree100 nAngle )
68 {
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;
74 
75  if ( !rText.isEmpty() )
76  {
77  // rotated text
78  vcl::Font aFont( GetFont() );
79  aFont.SetColor( GetTextColor() );
80  aFont.SetOrientation( toDegree10(nAngle) ); // Font uses 1/10 degrees
81  aFont.SetWeight( WEIGHT_BOLD );
82  SetFont( aFont );
83 
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 );
88  }
89  else
90  {
91  // only a line
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);
96 
98  DrawLine( pt1, pt2 );
99  }
100 
101  // *** drag button ***
102 
103  bool bMain = (nAngle % 4500_deg100) != 0_deg100;
105  SetFillColor( GetButtonFillColor( bMain ) );
106 
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 ) );
111 }
112 
114 {
116 }
117 
119 {
121 }
122 
124 {
125  const StyleSettings& rSett = GetSettings().GetStyleSettings();
126  return mbEnabled ? rSett.GetButtonTextColor() : rSett.GetDisableColor();
127 }
128 
130 {
131  const StyleSettings& rSett = GetSettings().GetStyleSettings();
132  return mbEnabled ? rSett.GetButtonTextColor() : rSett.GetDisableColor();
133 }
134 
135 const Color& DialControlBmp::GetButtonFillColor( bool bMain ) const
136 {
137  const StyleSettings& rSett = GetSettings().GetStyleSettings();
138  return mbEnabled ? (bMain ? rSett.GetMenuColor() : rSett.GetHighlightColor()) : rSett.GetDisableColor();
139 }
140 
142 {
145 }
146 
147 void DialControlBmp::SetSize( const Size& rSize )
148 {
149  maRect.SetPos( Point( 0, 0 ) );
150  maRect.SetSize( rSize );
151  mnCenterX = rSize.Width() / 2;
152  mnCenterY = rSize.Height() / 2;
153  SetOutputSize( rSize );
154 }
155 
157 {
158  // *** background with 3D effect ***
159 
160  SetLineColor();
161  SetFillColor();
162  Erase();
163 
164  EnableRTL(); // draw 3D effect in correct direction
165 
166  sal_uInt8 nDiff = mbEnabled ? 0x18 : 0x10;
167  Color aColor;
168 
169  aColor = GetBackgroundColor();
170  SetFillColor( aColor );
173 
174  aColor.DecreaseLuminance( nDiff );
175  SetFillColor( aColor );
177 
178  aColor.DecreaseLuminance( nDiff );
179  SetFillColor( aColor );
181 
182  aColor = GetBackgroundColor();
183  aColor.IncreaseLuminance( nDiff );
184  SetFillColor( aColor );
186 
187  aColor.IncreaseLuminance( nDiff );
188  SetFillColor( aColor );
190 
191  EnableRTL( false );
192 
193  // *** calibration ***
194 
195  Point aStartPos( mnCenterX, mnCenterY );
196  Color aFullColor( GetScaleLineColor() );
197  Color aLightColor( GetBackgroundColor() );
198  aLightColor.Merge( aFullColor, 128 );
199 
200  for( int nAngle = 0; nAngle < 360; nAngle += 15 )
201  {
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 ) );
207  }
208 
209  // *** clear inner area ***
210 
211  SetLineColor();
213  tools::Rectangle aEllipseRect = maRect;
214  aEllipseRect.shrink(DIAL_OUTER_WIDTH);
215  DrawEllipse( aEllipseRect );
216 }
217 
219  mxBmpEnabled(VclPtr<DialControlBmp>::Create(rReference)),
220  mxBmpDisabled(VclPtr<DialControlBmp>::Create(rReference)),
221  mxBmpBuffered(VclPtr<DialControlBmp>::Create(rReference)),
222  mpLinkField( nullptr ),
223  mnLinkedFieldValueMultiplyer( 0 ),
224  mnAngle( 0 ),
225  mnInitialAngle( 0 ),
226  mnOldAngle( 0 ),
227  mnCenterX( 0 ),
228  mnCenterY( 0 ),
229  mbNoRot( false )
230 {
231 }
232 
233 void DialControl::DialControl_Impl::Init( const Size& rWinSize, const vcl::Font& rWinFont )
234 {
235  maWinFont = rWinFont;
236  maWinFont.SetTransparent(true);
237  mxBmpBuffered->InitBitmap(maWinFont);
238  SetSize(rWinSize);
239 }
240 
242 {
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;
246 
247  maWinSize = Size( nMin, nMin );
248 
249  mnCenterX = maWinSize.Width() / 2;
250  mnCenterY = maWinSize.Height() / 2;
251 
252  mxBmpEnabled->DrawBackground( maWinSize, true );
253  mxBmpDisabled->DrawBackground( maWinSize, false );
254  mxBmpBuffered->SetSize( maWinSize );
255 }
256 
258 {
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
267  Init(aSize);
268 }
269 
271 {
272  mpImpl->SetSize(GetOutputSizePixel());
274 }
275 
277 {
278  Point aPos;
279  rRenderContext.DrawBitmapEx(aPos, mpImpl->mxBmpBuffered->GetBitmapEx(aPos, mpImpl->maWinSize));
280 }
281 
283 {
284  CustomWidgetController::StyleUpdated();
285  Init( mpImpl->maWinSize, mpImpl->maWinFont );
287 }
288 
290 {
291  if( rMEvt.IsLeft() )
292  {
293  GrabFocus();
294  CaptureMouse();
295  mpImpl->mnOldAngle = mpImpl->mnAngle;
296  HandleMouseEvent( rMEvt.GetPosPixel(), true );
297  }
298  return true;
299 }
300 
301 bool DialControl::MouseMove( const MouseEvent& rMEvt )
302 {
303  if( IsMouseCaptured() && rMEvt.IsLeft() )
304  HandleMouseEvent( rMEvt.GetPosPixel(), false );
305  return true;
306 }
307 
309 {
310  if( IsMouseCaptured() )
311  {
312  ReleaseMouse();
313  if( mpImpl->mpLinkField )
314  mpImpl->mpLinkField->grab_focus();
315  }
316  return true;
317 }
318 
319 bool DialControl::KeyInput( const KeyEvent& rKEvt )
320 {
321  const vcl::KeyCode& rKCode = rKEvt.GetKeyCode();
322  if( !rKCode.GetModifier() && (rKCode.GetCode() == KEY_ESCAPE) )
323  {
325  return true;
326  }
327  return CustomWidgetController::KeyInput(rKEvt);
328 }
329 
331 {
332  // release captured mouse
334 }
335 
337 {
338  return !mpImpl->mbNoRot;
339 }
340 
342 {
343  if( !mpImpl->mbNoRot )
344  {
345  mpImpl->mbNoRot = true;
347  if (mpImpl->mpLinkField)
348  mpImpl->mpLinkField->set_text("");
349  }
350 }
351 
353 {
354  return mpImpl->mnAngle;
355 }
356 
358 {
359  SetRotation(nAngle, false);
360 }
361 
362 void DialControl::SetLinkedField(weld::MetricSpinButton* pField, sal_Int32 nDecimalPlaces)
363 {
364  mpImpl->mnLinkedFieldValueMultiplyer = 100 / std::pow(10.0, double(nDecimalPlaces));
365 
366  // remove modify handler from old linked field
367  if( mpImpl->mpLinkField )
368  {
369  weld::MetricSpinButton& rField = *mpImpl->mpLinkField;
371  }
372  // remember the new linked field
373  mpImpl->mpLinkField = pField;
374  // set modify handler at new linked field
375  if( mpImpl->mpLinkField )
376  {
377  weld::MetricSpinButton& rField = *mpImpl->mpLinkField;
378  rField.connect_value_changed(LINK(this, DialControl, LinkedFieldModifyHdl));
379  }
380 }
381 
382 IMPL_LINK_NOARG(DialControl, LinkedFieldModifyHdl, weld::MetricSpinButton&, void)
383 {
384  SetRotation(Degree100(mpImpl->mpLinkField->get_value(FieldUnit::DEGREE) * mpImpl->mnLinkedFieldValueMultiplyer), true);
385 }
386 
388 {
389  mpImpl->mnInitialAngle = mpImpl->mnAngle;
390 }
391 
393 {
394  return mpImpl->mnInitialAngle != mpImpl->mnAngle;
395 }
396 
397 void DialControl::Init( const Size& rWinSize, const vcl::Font& rWinFont )
398 {
399  mpImpl->Init( rWinSize, rWinFont );
400  EnableRTL( false ); // don't mirror mouse handling
401  SetOutputSizePixel( mpImpl->maWinSize );
402 }
403 
404 void DialControl::Init( const Size& rWinSize )
405 {
406  //hidpi TODO: GetDefaultFont() picks a font size too small, so fix it here.
408 
410  DefaultFontType::UI_SANS, Application::GetSettings().GetUILanguageTag().getLanguageType(), GetDefaultFontFlags::OnlyOne ) );
411 
412  aFont.SetFontHeight(aDefaultSize.GetFontHeight());
413  Init( rWinSize, aFont );
414 }
415 
417 {
418  mpImpl->mxBmpBuffered->CopyBackground( IsEnabled() ? *mpImpl->mxBmpEnabled : *mpImpl->mxBmpDisabled );
419  if( !mpImpl->mbNoRot )
420  mpImpl->mxBmpBuffered->DrawElements(GetText(), mpImpl->mnAngle);
421  Invalidate();
422 }
423 
424 void DialControl::SetRotation(Degree100 nAngle, bool bBroadcast)
425 {
426  bool bOldSel = mpImpl->mbNoRot;
427  mpImpl->mbNoRot = false;
428 
429  nAngle = NormAngle36000(nAngle);
430 
431  if (!bOldSel || (mpImpl->mnAngle != nAngle))
432  {
433  mpImpl->mnAngle = nAngle;
435  if( mpImpl->mpLinkField )
436  mpImpl->mpLinkField->set_value(GetRotation().get() / 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  tools::Long nX = rPos.X() - mpImpl->mnCenterX;
450  tools::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(Degree100(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: */
void SetPos(const Point &rPoint)
const Color & GetButtonLineColor() 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.
tools::Long mnCenterY
Definition: dialcontrol.hxx:58
const tools::Long DIAL_OUTER_WIDTH
Definition: dialcontrol.cxx:32
const Color & GetDialogColor() const
virtual void LoseFocus() override
void SetSize(const Size &rSize)
virtual bool MouseButtonUp(const MouseEvent &rMEvt) override
virtual bool MouseButtonDown(const MouseEvent &rMEvt) override
tools::Long mnCenterX
Definition: dialcontrol.hxx:57
void Merge(const Color &rMergeColor, sal_uInt8 cTransparency)
void DrawBitmapEx(const Point &rDestPt, const BitmapEx &rBitmapEx)
This control allows to input a rotation angle, visualized by a dial.
Definition: dialcontrol.hxx:75
constexpr Point RightCenter() const
void Create(SwFormatVertOrient &rItem, SvStream &rStrm, sal_uInt16 nVersionAbusedAsSize)
void Init(const Size &rWinSize)
Degree100 GetRotation() const
Returns the current rotation angle in 1/100 degrees.
long Long
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
double toRadians(Degree10 x)
constexpr Point BottomCenter() const
virtual bool KeyInput(const KeyEvent &rKEvt) override
const Color & GetLabelTextColor() const
virtual void SetSettings(const AllSettings &rSettings)
tools::Rectangle maRect
Definition: dialcontrol.hxx:54
bool IsValueModified() const
Compare value with the saved value.
constexpr double rad2deg(double v)
const Color & GetHighlightColor() const
Size const & GetOutputSizePixel() const
void InitBitmap(const vcl::Font &rFont)
Definition: dialcontrol.cxx:44
tools::Long GetFontHeight() const
WEIGHT_BOLD
constexpr tools::Long Width() const
void connect_value_changed(const Link< MetricSpinButton &, void > &rLink)
void SetBackground()
void CopyBackground(const DialControlBmp &rSrc)
Definition: dialcontrol.cxx:50
void SetRotation(Degree100 nAngle)
Sets the rotation to the passed value (in 1/100 degrees).
const vcl::Font & GetLabelFont() const
const vcl::Font & GetFont() const
Degree10 toDegree10(Degree100 x)
constexpr Point BottomLeft() const
std::unique_ptr< DialControl_Impl > mpImpl
constexpr Point TopCenter() const
constexpr tools::Long GetWidth() const
OutputDevice & mrParent
Definition: dialcontrol.hxx:56
const Color & GetTextColor() const
Degree100 NormAngle36000(Degree100 deg100)
Normalize angle to -180.00..179.99.
Definition: svdtrans.cxx:409
sal_uInt16 GetModifier() const
virtual OutputDevice & get_ref_device()=0
void DrawLine(const Point &rStartPt, const Point &rEndPt)
virtual void StyleUpdated() override
const Color & GetMenuColor() const
IMPL_LINK_NOARG(SuggestionDisplay, SelectSuggestionValueSetHdl, ValueSet *, void)
void SetLineColor()
const Color & GetDisableColor() const
void HandleMouseEvent(const Point &rPos, bool bInitial)
void DecreaseLuminance(sal_uInt8 cLumDec)
tools::Long GetTextWidth(const OUString &rStr, sal_Int32 nIndex=0, sal_Int32 nLen=-1, vcl::text::TextLayoutCache const *=nullptr, SalLayoutGlyphs const *const pLayoutCache=nullptr) const
void SetOrientation(Degree10 nLineOrientation)
virtual void EnableRTL(bool bEnable=true) override
void SetSize(const Size &rSize)
void InvalidateControl()
void shrink(tools::Long nShrinkBy)
bool SetOutputSize(const Size &rNewSize)
void SetFillColor()
void DrawElements(const OUString &rText, Degree100 nAngle)
Definition: dialcontrol.cxx:67
virtual bool MouseMove(const MouseEvent &rMEvt) override
constexpr double deg2rad(double v)
const AllSettings & GetSettings() const
void DrawPie(const tools::Rectangle &rRect, const Point &rStartPt, const Point &rEndPt)
Color GetBackgroundColor() const override
constexpr Point LeftCenter() const
void SetOutputSizePixel(const Size &rSize)
void SetModifyHdl(const Link< DialControl &, void > &rLink)
constexpr Point Center() const
void SetColor(const Color &)
constexpr Point TopLeft() const
virtual void Paint(vcl::RenderContext &rRenderContext, const tools::Rectangle &rRect) override
DeviceFormat
const vcl::KeyCode & GetKeyCode() const
constexpr Size GetSize() const
virtual void Resize() override
void HandleEscapeEvent()
const Color & GetButtonTextColor() const
constexpr tools::Long Height() 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)
tools::Long GetTextHeight() const
constexpr Point TopRight() const
bool IsLeft() const
virtual int get_text_height() const =0
const Point & GetPosPixel() const
void SaveValue()
Save value for later comparison.
void DrawEllipse(const tools::Rectangle &rRect)
void SetFontHeight(tools::Long nHeight)
constexpr Point BottomRight() const
BitmapEx GetBitmapEx(const Point &rSrcPt, const Size &rSize) const
DialControlBmp(OutputDevice &rReference)
Definition: dialcontrol.cxx:34
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
void DrawText(const Point &rStartPt, const OUString &rStr, sal_Int32 nIndex=0, sal_Int32 nLen=-1, std::vector< tools::Rectangle > *pVector=nullptr, OUString *pDisplayText=nullptr, const SalLayoutGlyphs *pLayoutCache=nullptr)
BaseContainerNodeSharedPtr & mrParent
virtual float get_approximate_digit_width() const =0
sal_Int32 mnAngle
void EnableRTL(bool bEnable)
constexpr tools::Long GetHeight() const