LibreOffice Module sc (master) 1
tbzoomsliderctrl.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#include <tbzoomsliderctrl.hxx>
20
22#include <utility>
24#include <vcl/event.hxx>
25#include <vcl/image.hxx>
26#include <vcl/toolbox.hxx>
27#include <vcl/virdev.hxx>
29#include <iterator>
30#include <set>
31#include <bitmaps.hlst>
32
33#include <com/sun/star/frame/XFrame.hpp>
34#include <com/sun/star/frame/XDispatchProvider.hpp>
35
36// class ScZoomSliderControl ---------------------------------------
37
39
41 sal_uInt16 nSlotId,
42 ToolBoxItemId nId,
43 ToolBox& rTbx )
44 :SfxToolBoxControl( nSlotId, nId, rTbx )
45{
46 rTbx.Invalidate();
47}
48
50{
51
52}
53
55 const SfxPoolItem* pState )
56{
58 ToolBox& rTbx = GetToolBox();
59 ScZoomSliderWnd* pBox = static_cast<ScZoomSliderWnd*>(rTbx.GetItemWindow( nId ));
60 OSL_ENSURE( pBox ,"Control not found!" );
61
62 if ( SfxItemState::DEFAULT != eState || pState->IsVoidItem() )
63 {
64 SvxZoomSliderItem aZoomSliderItem( 100 );
65 pBox->Disable();
66 pBox->UpdateFromItem( &aZoomSliderItem );
67 }
68 else
69 {
70 pBox->Enable();
71 OSL_ENSURE( dynamic_cast<const SvxZoomSliderItem*>( pState) != nullptr, "invalid item type" );
72 const SvxZoomSliderItem* pZoomSliderItem = dynamic_cast< const SvxZoomSliderItem* >( pState );
73
74 OSL_ENSURE( pZoomSliderItem, "Sc::ScZoomSliderControl::StateChanged(), wrong item type!" );
75 if( pZoomSliderItem )
76 pBox->UpdateFromItem( pZoomSliderItem );
77 }
78}
79
81{
82 // #i98000# Don't try to get a value via SfxViewFrame::Current here.
83 // The view's value is always notified via StateChanged later.
84 VclPtrInstance<ScZoomSliderWnd> xSlider( pParent,
85 css::uno::Reference< css::frame::XDispatchProvider >( m_xFrame->getController(),
86 css::uno::UNO_QUERY ), 100 );
87 return xSlider;
88}
89
90constexpr sal_uInt16 gnSliderCenter(100);
91
100const tools::Long nSnappingEpsilon = 5; // snapping epsilon in pixels
101const tools::Long nSnappingPointsMinDist = nSnappingEpsilon; // minimum distance of two adjacent snapping points
102
103sal_uInt16 ScZoomSlider::Offset2Zoom( tools::Long nOffset ) const
104{
105 Size aSliderWindowSize = GetOutputSizePixel();
106 const tools::Long nControlWidth = aSliderWindowSize.Width();
107 sal_uInt16 nRet = 0;
108
109 if( nOffset < nSliderXOffset )
110 return mnMinZoom;
111 if( nOffset > nControlWidth - nSliderXOffset )
112 return mnMaxZoom;
113
114 // check for snapping points:
115 auto aSnappingPointIter = std::find_if(maSnappingPointOffsets.begin(), maSnappingPointOffsets.end(),
116 [nOffset](const tools::Long nCurrent) { return std::abs(nCurrent - nOffset) < nSnappingEpsilon; });
117 if (aSnappingPointIter != maSnappingPointOffsets.end())
118 {
119 nOffset = *aSnappingPointIter;
120 auto nCount = static_cast<sal_uInt16>(std::distance(maSnappingPointOffsets.begin(), aSnappingPointIter));
122 }
123
124 if( 0 == nRet )
125 {
126 if( nOffset < nControlWidth / 2 )
127 {
128 // first half of slider
129 const tools::Long nFirstHalfRange = gnSliderCenter - mnMinZoom;
130 const tools::Long nHalfSliderWidth = nControlWidth/2 - nSliderXOffset;
131 const tools::Long nZoomPerSliderPixel = (1000 * nFirstHalfRange) / nHalfSliderWidth;
132 const tools::Long nOffsetToSliderLeft = nOffset - nSliderXOffset;
133 nRet = mnMinZoom + sal_uInt16( nOffsetToSliderLeft * nZoomPerSliderPixel / 1000 );
134 }
135 else
136 {
137 // second half of slider
138 const tools::Long nSecondHalfRange = mnMaxZoom - gnSliderCenter;
139 const tools::Long nHalfSliderWidth = nControlWidth/2 - nSliderXOffset;
140 const tools::Long nZoomPerSliderPixel = 1000 * nSecondHalfRange / nHalfSliderWidth;
141 const tools::Long nOffsetToSliderCenter = nOffset - nControlWidth/2;
142 nRet = gnSliderCenter + sal_uInt16( nOffsetToSliderCenter * nZoomPerSliderPixel / 1000 );
143 }
144 }
145
146 if( nRet < mnMinZoom )
147 return mnMinZoom;
148
149 else if( nRet > mnMaxZoom )
150 return mnMaxZoom;
151
152 return nRet;
153}
154
155tools::Long ScZoomSlider::Zoom2Offset( sal_uInt16 nCurrentZoom ) const
156{
157 Size aSliderWindowSize = GetOutputSizePixel();
158 const tools::Long nControlWidth = aSliderWindowSize.Width();
160
161 const tools::Long nHalfSliderWidth = nControlWidth/2 - nSliderXOffset;
162 if( nCurrentZoom <= gnSliderCenter )
163 {
164 nCurrentZoom = nCurrentZoom - mnMinZoom;
165 const tools::Long nFirstHalfRange = gnSliderCenter - mnMinZoom;
166 const tools::Long nSliderPixelPerZoomPercent = 1000 * nHalfSliderWidth / nFirstHalfRange;
167 const tools::Long nOffset = (nSliderPixelPerZoomPercent * nCurrentZoom) / 1000;
168 nRect += nOffset;
169 }
170 else
171 {
172 nCurrentZoom = nCurrentZoom - gnSliderCenter;
173 const tools::Long nSecondHalfRange = mnMaxZoom - gnSliderCenter;
174 const tools::Long nSliderPixelPerZoomPercent = 1000 * nHalfSliderWidth / nSecondHalfRange;
175 const tools::Long nOffset = (nSliderPixelPerZoomPercent * nCurrentZoom) / 1000;
176 nRect += nHalfSliderWidth + nOffset;
177 }
178 return nRect;
179}
180
182 const css::uno::Reference< css::frame::XDispatchProvider >& rDispatchProvider,
183 sal_uInt16 nCurrentZoom ):
184 InterimItemWindow(pParent, "modules/scalc/ui/zoombox.ui", "ZoomBox"),
185 mxWidget(new ScZoomSlider(rDispatchProvider, nCurrentZoom)),
186 mxWeld(new weld::CustomWeld(*m_xBuilder, "zoom", *mxWidget))
187{
188 Size aLogicalSize( 115, 40 );
189 Size aSliderSize = LogicToPixel(aLogicalSize, MapMode(MapUnit::Map10thMM));
190 Size aPreferredSize(aSliderSize.Width() * nSliderWidth-1, aSliderSize.Height() + nSliderHeight);
191 mxWidget->GetDrawingArea()->set_size_request(aPreferredSize.Width(), aPreferredSize.Height());
192 mxWidget->SetOutputSizePixel(aPreferredSize);
193 SetSizePixel(aPreferredSize);
194}
195
197{
198 disposeOnce();
199}
200
202{
203 mxWeld.reset();
204 mxWidget.reset();
206}
207
208ScZoomSlider::ScZoomSlider(css::uno::Reference< css::frame::XDispatchProvider> xDispatchProvider,
209 sal_uInt16 nCurrentZoom)
210 : mnCurrentZoom( nCurrentZoom ),
211 mnMinZoom( 10 ),
212 mnMaxZoom( 400 ),
213 mbOmitPaint( false ),
214 m_xDispatchProvider(std::move(xDispatchProvider))
215{
216 maSliderButton = Image(StockImage::Yes, RID_SVXBMP_SLIDERBUTTON);
217 maIncreaseButton = Image(StockImage::Yes, RID_SVXBMP_SLIDERINCREASE);
218 maDecreaseButton = Image(StockImage::Yes, RID_SVXBMP_SLIDERDECREASE);
219}
220
221
223{
224 Size aSliderWindowSize = GetOutputSizePixel();
225
226 const Point aPoint = rMEvt.GetPosPixel();
227
228 const tools::Long nButtonLeftOffset = ( nSliderXOffset - nIncDecWidth )/2;
229 const tools::Long nButtonRightOffset = ( nSliderXOffset + nIncDecWidth )/2;
230
231 const tools::Long nOldZoom = mnCurrentZoom;
232
233 // click to - button
234 if ( aPoint.X() >= nButtonLeftOffset && aPoint.X() <= nButtonRightOffset )
235 {
237 }
238 // click to + button
239 else if ( aPoint.X() >= aSliderWindowSize.Width() - nSliderXOffset + nButtonLeftOffset &&
240 aPoint.X() <= aSliderWindowSize.Width() - nSliderXOffset + nButtonRightOffset )
241 {
243 }
244 else if( aPoint.X() >= nSliderXOffset && aPoint.X() <= aSliderWindowSize.Width() - nSliderXOffset )
245 {
246 mnCurrentZoom = Offset2Zoom( aPoint.X() );
247 }
248
251 else if( mnCurrentZoom > mnMaxZoom )
253
254 if( nOldZoom == mnCurrentZoom )
255 return true;
256
257 tools::Rectangle aRect( Point( 0, 0 ), aSliderWindowSize );
258
259 Invalidate(aRect);
260 mbOmitPaint = true;
261
262 SvxZoomSliderItem aZoomSliderItem( mnCurrentZoom );
263
264 css::uno::Any a;
265 aZoomSliderItem.QueryValue( a );
266
267 css::uno::Sequence aArgs{ comphelper::makePropertyValue("ScalingFactor", a) };
268
269 SfxToolBoxControl::Dispatch( m_xDispatchProvider, ".uno:ScalingFactor", aArgs );
270
271 mbOmitPaint = false;
272
273 return true;
274}
275
277{
278 Size aSliderWindowSize = GetOutputSizePixel();
279 const tools::Long nControlWidth = aSliderWindowSize.Width();
280 const short nButtons = rMEvt.GetButtons();
281
282 // check mouse move with button pressed
283 if ( 1 == nButtons )
284 {
285 const Point aPoint = rMEvt.GetPosPixel();
286
287 if ( aPoint.X() >= nSliderXOffset && aPoint.X() <= nControlWidth - nSliderXOffset )
288 {
289 mnCurrentZoom = Offset2Zoom( aPoint.X() );
290
291 tools::Rectangle aRect(Point(0, 0), aSliderWindowSize);
292 Invalidate(aRect);
293
294 mbOmitPaint = true; // optimization: paint before executing command,
295
296 // commit state change
297 SvxZoomSliderItem aZoomSliderItem( mnCurrentZoom );
298
299 css::uno::Any a;
300 aZoomSliderItem.QueryValue( a );
301
302 css::uno::Sequence aArgs{ comphelper::makePropertyValue("ScalingFactor", a) };
303
304 SfxToolBoxControl::Dispatch( m_xDispatchProvider, ".uno:ScalingFactor", aArgs );
305
306 mbOmitPaint = false;
307 }
308 }
309
310 return false;
311}
312
314{
315 mxWidget->UpdateFromItem(pZoomSliderItem);
316}
317
319{
320 if( pZoomSliderItem )
321 {
322 mnCurrentZoom = pZoomSliderItem->GetValue();
323 mnMinZoom = pZoomSliderItem->GetMinZoom();
324 mnMaxZoom = pZoomSliderItem->GetMaxZoom();
325
326 OSL_ENSURE( mnMinZoom <= mnCurrentZoom &&
330 "Looks like the zoom slider item is corrupted" );
331 const css::uno::Sequence < sal_Int32 >& rSnappingPoints = pZoomSliderItem->GetSnappingPoints();
333 maSnappingPointZooms.clear();
334
335 // get all snapping points:
336 std::set< sal_uInt16 > aTmpSnappingPoints;
337 std::transform(rSnappingPoints.begin(), rSnappingPoints.end(), std::inserter(aTmpSnappingPoints, aTmpSnappingPoints.end()),
338 [](const sal_Int32 nSnappingPoint) -> sal_uInt16 { return static_cast<sal_uInt16>(nSnappingPoint); });
339
340 // remove snapping points that are too close to each other:
341 tools::Long nLastOffset = 0;
342
343 for ( const sal_uInt16 nCurrent : aTmpSnappingPoints )
344 {
345 const tools::Long nCurrentOffset = Zoom2Offset( nCurrent );
346
347 if ( nCurrentOffset - nLastOffset >= nSnappingPointsMinDist )
348 {
349 maSnappingPointOffsets.push_back( nCurrentOffset );
350 maSnappingPointZooms.push_back( nCurrent );
351 nLastOffset = nCurrentOffset;
352 }
353 }
354 }
355
356 Size aSliderWindowSize = GetOutputSizePixel();
357 tools::Rectangle aRect(Point(0, 0), aSliderWindowSize);
358
359 if ( !mbOmitPaint )
360 Invalidate(aRect);
361}
362
363void ScZoomSlider::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*rRect*/)
364{
365 DoPaint(rRenderContext);
366}
367
369{
370 if (mbOmitPaint)
371 return;
372
373 Size aSliderWindowSize(GetOutputSizePixel());
374 tools::Rectangle aRect(Point(0, 0), aSliderWindowSize);
375
376 ScopedVclPtrInstance< VirtualDevice > pVDev(rRenderContext);
377 pVDev->SetOutputSizePixel(aSliderWindowSize);
378
379 tools::Rectangle aSlider = aRect;
380
381 aSlider.AdjustTop((aSliderWindowSize.Height() - nSliderHeight) / 2 - 1 );
382 aSlider.SetBottom( aSlider.Top() + nSliderHeight );
383 aSlider.AdjustLeft(nSliderXOffset );
384 aSlider.AdjustRight( -nSliderXOffset );
385
386 tools::Rectangle aFirstLine(aSlider);
387 aFirstLine.SetBottom( aFirstLine.Top() );
388
389 tools::Rectangle aSecondLine(aSlider);
390 aSecondLine.SetTop( aSecondLine.Bottom() );
391
392 tools::Rectangle aLeft(aSlider);
393 aLeft.SetRight( aLeft.Left() );
394
395 tools::Rectangle aRight(aSlider);
396 aRight.SetLeft( aRight.Right() );
397
398 // draw VirtualDevice's background color
399 Color aStartColor = rRenderContext.GetSettings().GetStyleSettings().GetFaceColor();
400 Color aEndColor = rRenderContext.GetSettings().GetStyleSettings().GetFaceColor();
401
402 if (aEndColor.IsDark())
403 aStartColor = aEndColor;
404
405 Gradient aGradient;
406 aGradient.SetAngle(0_deg10);
407 aGradient.SetStyle(css::awt::GradientStyle_LINEAR);
408
409 aGradient.SetStartColor(aStartColor);
410 aGradient.SetEndColor(aEndColor);
411 pVDev->DrawGradient(aRect, aGradient);
412
413 // draw slider
414 pVDev->SetLineColor(COL_WHITE);
415 pVDev->DrawRect(aSecondLine);
416 pVDev->DrawRect(aRight);
417
418 pVDev->SetLineColor(COL_GRAY);
419 pVDev->DrawRect(aFirstLine);
420 pVDev->DrawRect(aLeft);
421
422 // draw snapping points:
423 for (const auto& rSnappingPointOffset : maSnappingPointOffsets)
424 {
425 pVDev->SetLineColor(COL_GRAY);
426 tools::Rectangle aSnapping(aRect);
427 aSnapping.SetBottom( aSlider.Top() );
428 aSnapping.SetTop( aSnapping.Bottom() - nSnappingHeight );
429 aSnapping.AdjustLeft(rSnappingPointOffset );
430 aSnapping.SetRight( aSnapping.Left() );
431 pVDev->DrawRect(aSnapping);
432
435 pVDev->DrawRect(aSnapping);
436 }
437
438 // draw slider button
439 Point aImagePoint = aRect.TopLeft();
440 aImagePoint.AdjustX(Zoom2Offset(mnCurrentZoom) );
441 aImagePoint.AdjustX( -(nButtonWidth / 2) );
442 aImagePoint.AdjustY( (aSliderWindowSize.Height() - nButtonHeight) / 2 );
443 pVDev->DrawImage(aImagePoint, maSliderButton);
444
445 // draw decrease button
446 aImagePoint = aRect.TopLeft();
447 aImagePoint.AdjustX((nSliderXOffset - nIncDecWidth) / 2 );
448 aImagePoint.AdjustY((aSliderWindowSize.Height() - nIncDecHeight) / 2 );
449 pVDev->DrawImage(aImagePoint, maDecreaseButton);
450
451 // draw increase button
452 aImagePoint.setX( aRect.Left() + aSliderWindowSize.Width() - nIncDecWidth - (nSliderXOffset - nIncDecWidth) / 2 );
453 pVDev->DrawImage(aImagePoint, maIncreaseButton);
454
455 rRenderContext.DrawOutDev(Point(0, 0), aSliderWindowSize, Point(0, 0), aSliderWindowSize, *pVDev);
456}
457
458/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
css::uno::Reference< css::lang::XComponent > m_xFrame
const StyleSettings & GetStyleSettings() const
bool IsDark() const
void SetStyle(css::awt::GradientStyle eStyle)
void SetStartColor(const Color &rColor)
void SetAngle(Degree10 nAngle)
void SetEndColor(const Color &rColor)
virtual void dispose() override
sal_uInt16 GetButtons() const
const Point & GetPosPixel() const
SAL_DLLPRIVATE void DrawOutDev(const Point &, const Size &, const Point &, const Size &, const Printer &)=delete
const AllSettings & GetSettings() const
void setX(tools::Long nX)
tools::Long AdjustY(tools::Long nVertMove)
tools::Long AdjustX(tools::Long nHorzMove)
constexpr tools::Long X() const
virtual void StateChangedAtToolBoxControl(sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem *pState) override
virtual ~ScZoomSliderControl() override
ScZoomSliderControl(sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox &rTbx)
virtual VclPtr< InterimItemWindow > CreateItemWindow(vcl::Window *pParent) override
void UpdateFromItem(const SvxZoomSliderItem *pZoomSliderItem)
std::unique_ptr< weld::CustomWeld > mxWeld
virtual void dispose() override
std::unique_ptr< ScZoomSlider > mxWidget
ScZoomSliderWnd(vcl::Window *pParent, const css::uno::Reference< css::frame::XDispatchProvider > &rDispatchProvider, sal_uInt16 nCurrentZoom)
virtual ~ScZoomSliderWnd() override
void DoPaint(vcl::RenderContext &rRenderContext)
std::vector< sal_uInt16 > maSnappingPointZooms
virtual bool MouseMove(const MouseEvent &rMEvt) override
sal_uInt16 Offset2Zoom(tools::Long nOffset) const
ScZoomSlider(css::uno::Reference< css::frame::XDispatchProvider > xDispatchProvider, sal_uInt16 nCurrentZoom)
css::uno::Reference< css::frame::XDispatchProvider > m_xDispatchProvider
std::vector< tools::Long > maSnappingPointOffsets
void UpdateFromItem(const SvxZoomSliderItem *pZoomSliderItem)
sal_uInt16 mnMinZoom
tools::Long Zoom2Offset(sal_uInt16 nZoom) const
virtual void Paint(vcl::RenderContext &rRenderContext, const tools::Rectangle &rRect) override
virtual bool MouseButtonDown(const MouseEvent &rMEvt) override
sal_uInt16 mnMaxZoom
sal_uInt16 mnCurrentZoom
virtual bool IsVoidItem() const
void Dispatch(const OUString &aCommand, css::uno::Sequence< css::beans::PropertyValue > const &aArgs)
ToolBoxItemId GetId() const
ToolBox & GetToolBox() const
constexpr tools::Long Height() const
constexpr tools::Long Width() const
const Color & GetFaceColor() const
sal_uInt16 GetMaxZoom() const
const css::uno::Sequence< sal_Int32 > & GetSnappingPoints() const
sal_uInt16 GetMinZoom() const
virtual bool QueryValue(css::uno::Any &rVal, sal_uInt8 nMemberId=0) const override
vcl::Window * GetItemWindow(ToolBoxItemId nItemId) const
constexpr void SetLeft(tools::Long v)
constexpr void SetTop(tools::Long v)
constexpr tools::Long Top() const
constexpr Point TopLeft() const
constexpr void SetRight(tools::Long v)
constexpr tools::Long Right() const
tools::Long AdjustTop(tools::Long nVertMoveDelta)
tools::Long AdjustRight(tools::Long nHorzMoveDelta)
constexpr void SetBottom(tools::Long v)
tools::Long AdjustBottom(tools::Long nVertMoveDelta)
tools::Long AdjustLeft(tools::Long nHorzMoveDelta)
constexpr tools::Long Left() const
constexpr tools::Long Bottom() const
Point LogicToPixel(const Point &rLogicPt) const
virtual void SetSizePixel(const Size &rNewSize)
void Disable(bool bChild=true)
void Enable(bool bEnable=true, bool bChild=true)
void Invalidate(InvalidateFlags nFlags=InvalidateFlags::NONE)
Size const & GetOutputSizePixel() const
constexpr ::Color COL_GRAY(0x80, 0x80, 0x80)
constexpr ::Color COL_WHITE(0xFF, 0xFF, 0xFF)
int nCount
uno_Any a
css::beans::PropertyValue makePropertyValue(const OUString &rName, T &&rValue)
long Long
sal_Int16 nId
SfxItemState
const tools::Long nSnappingEpsilon
const tools::Long nSnappingPointsMinDist
const tools::Long nSliderXOffset
const tools::Long nSnappingHeight
const tools::Long nSliderHeight
constexpr sal_uInt16 gnSliderCenter(100)
const tools::Long nIncDecHeight
SFX_IMPL_TOOLBOX_CONTROL(ScZoomSliderControl, SvxZoomSliderItem)
const tools::Long nIncDecWidth
const tools::Long nButtonHeight
const tools::Long nButtonWidth
const tools::Long nSliderWidth