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
30namespace 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
59void DialControlBmp::DrawBackground( const Size& rSize, bool bEnabled )
60{
61 Init();
62 SetSize(rSize);
63 mbEnabled = bEnabled;
65}
66
67void 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( to<Degree10>(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;
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
136{
137 const StyleSettings& rSett = GetSettings().GetStyleSettings();
138 return mbEnabled ? (bMain ? rSett.GetMenuColor() : rSett.GetHighlightColor()) : rSett.GetDisableColor();
139}
140
142{
145}
146
147void 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
233void 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
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
319bool 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
362void 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
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
397void 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
404void 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
424void 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
447void 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 = std::hypot( nX, nY );
452 if( fH != 0.0 )
453 {
454 double fAngle = acos( nX / fH );
455 sal_Int32 nAngle = basegfx::rad2deg<100>(fAngle);
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: */
BaseContainerNodeSharedPtr & mrParent
const StyleSettings & GetStyleSettings() const
static const AllSettings & GetSettings()
void DecreaseLuminance(sal_uInt8 cLumDec)
void Merge(const Color &rMergeColor, sal_uInt8 cTransparency)
void IncreaseLuminance(sal_uInt8 cLumInc)
const vcl::KeyCode & GetKeyCode() const
const Point & GetPosPixel() const
bool IsLeft() const
virtual void SetSettings(const AllSettings &rSettings)
const vcl::Font & GetFont() const
void DrawBitmapEx(const Point &rDestPt, const BitmapEx &rBitmapEx)
void SetFont(const vcl::Font &rNewFont)
void DrawEllipse(const tools::Rectangle &rRect)
void DrawLine(const Point &rStartPt, const Point &rEndPt)
void SetLineColor()
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 SetFillColor()
void DrawPie(const tools::Rectangle &rRect, const Point &rStartPt, const Point &rEndPt)
tools::Long GetTextHeight() const
static vcl::Font GetDefaultFont(DefaultFontType nType, LanguageType eLang, GetDefaultFontFlags nFlags, const OutputDevice *pOutDev=nullptr)
void SetBackground()
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)
const AllSettings & GetSettings() const
BitmapEx GetBitmapEx(const Point &rSrcPt, const Size &rSize) const
constexpr tools::Long Y() const
constexpr tools::Long X() const
constexpr tools::Long Height() const
constexpr tools::Long Width() const
const Color & GetMenuColor() const
const Color & GetLabelTextColor() const
const vcl::Font & GetLabelFont() const
const Color & GetDialogColor() const
const Color & GetHighlightColor() const
const Color & GetDisableColor() const
const Color & GetButtonTextColor() const
virtual void EnableRTL(bool bEnable=true) override
bool SetOutputSize(const Size &rNewSize)
tools::Long mnCenterY
Definition: dialcontrol.hxx:58
OutputDevice & mrParent
Definition: dialcontrol.hxx:56
void DrawElements(const OUString &rText, Degree100 nAngle)
Definition: dialcontrol.cxx:67
tools::Rectangle maRect
Definition: dialcontrol.hxx:54
const Color & GetScaleLineColor() const
const Color & GetTextColor() const
const Color & GetButtonFillColor(bool bMain) const
void InitBitmap(const vcl::Font &rFont)
Definition: dialcontrol.cxx:44
DialControlBmp(OutputDevice &rReference)
Definition: dialcontrol.cxx:34
void CopyBackground(const DialControlBmp &rSrc)
Definition: dialcontrol.cxx:50
void SetSize(const Size &rSize)
tools::Long mnCenterX
Definition: dialcontrol.hxx:57
const Color & GetButtonLineColor() const
Color GetBackgroundColor() const override
This control allows to input a rotation angle, visualized by a dial.
Definition: dialcontrol.hxx:76
virtual bool KeyInput(const KeyEvent &rKEvt) override
void HandleMouseEvent(const Point &rPos, bool bInitial)
void SaveValue()
Save value for later comparison.
void SetModifyHdl(const Link< DialControl &, void > &rLink)
virtual void LoseFocus() override
void Init(const Size &rWinSize)
virtual bool MouseButtonDown(const MouseEvent &rMEvt) override
virtual bool MouseButtonUp(const MouseEvent &rMEvt) override
bool IsValueModified() const
Compare value with the saved value.
const OUString & GetText() const
bool HasRotation() const
Returns true, if the control is not in "don't care" state.
Degree100 GetRotation() const
Returns the current rotation angle in 1/100 degrees.
void HandleEscapeEvent()
void SetLinkedField(weld::MetricSpinButton *pField, sal_Int32 nDecimalPlaces=0)
Links the passed numeric edit field to the control (bi-directional).
std::unique_ptr< DialControl_Impl > mpImpl
virtual void Paint(vcl::RenderContext &rRenderContext, const tools::Rectangle &rRect) override
virtual void Resize() override
virtual void SetDrawingArea(weld::DrawingArea *pDrawingArea) override
void InvalidateControl()
void SetNoRotation()
Sets the control to "don't care" state.
virtual void StyleUpdated() override
void SetRotation(Degree100 nAngle)
Sets the rotation to the passed value (in 1/100 degrees).
virtual bool MouseMove(const MouseEvent &rMEvt) override
constexpr Point Center() const
constexpr tools::Long GetWidth() const
void SetSize(const Size &)
constexpr Point TopLeft() const
void SetPos(const Point &rPoint)
constexpr Size GetSize() const
constexpr Point RightCenter() const
void shrink(tools::Long nShrinkBy)
constexpr Point BottomCenter() const
constexpr Point BottomRight() const
constexpr Point TopRight() const
constexpr tools::Long GetHeight() const
constexpr Point LeftCenter() const
constexpr Point TopCenter() const
constexpr Point BottomLeft() const
tools::Long GetFontHeight() const
void SetOrientation(Degree10 nLineOrientation)
void SetTransparent(bool bTransparent)
void SetColor(const Color &)
void SetWeight(FontWeight)
void SetFontHeight(tools::Long nHeight)
sal_uInt16 GetCode() const
sal_uInt16 GetModifier() const
void EnableRTL(bool bEnable)
void SetOutputSizePixel(const Size &rSize)
Size const & GetOutputSizePixel() const
virtual OutputDevice & get_ref_device()=0
void connect_value_changed(const Link< MetricSpinButton &, void > &rLink)
virtual int get_text_height() const=0
virtual void set_size_request(int nWidth, int nHeight)=0
virtual float get_approximate_digit_width() const=0
double toRadians(D x)
WEIGHT_BOLD
constexpr sal_uInt16 KEY_ESCAPE
constexpr double deg2rad(double v)
void Create(SwFormatVertOrient &rItem, SvStream &rStrm, sal_uInt16 nVersionAbusedAsSize)
const tools::Long DIAL_OUTER_WIDTH
Definition: dialcontrol.cxx:32
IMPL_LINK_NOARG(SuggestionDisplay, SelectSuggestionValueSetHdl, ValueSet *, void)
css::uno::Reference< css::linguistic2::XProofreadingIterator > get(css::uno::Reference< css::uno::XComponentContext > const &context)
long Long
DeviceFormat
void Init(const Size &rWinSize, const vcl::Font &rWinFont)
void SetSize(const Size &rWinSize)
DialControl_Impl(OutputDevice &rReference)
Degree100 NormAngle36000(Degree100 a)
Normalize angle to -180.00..179.99.
Definition: svdtrans.cxx:408
sal_Int32 mnAngle
unsigned char sal_uInt8