1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
21 #include <config_folders.h>
23 #include <comphelper/lok.hxx>
24 #include <i18nutil/unicode.hxx>
25 #include <o3tl/test_info.hxx>
26 #include <officecfg/Office/Common.hxx>
27 #include <tools/stream.hxx>
28 #include <vcl/customweld.hxx>
29 #include <vcl/event.hxx>
30 #include <vcl/svapp.hxx>
31 #include <vcl/fieldvalues.hxx>
32 #include <vcl/settings.hxx>
33 #include <vcl/image.hxx>
34 #include <vcl/virdev.hxx>
35 #include <vcl/weldutils.hxx>
36 #include <rtl/math.hxx>
37 #include <sal/macros.h>
38 #include <sal/log.hxx>
39 #include <comphelper/string.hxx>
40 #include <unotools/localedatawrapper.hxx>
41 #include <unotools/syslocale.hxx>
43 #include <svtools/borderline.hxx>
44 #include <svtools/sampletext.hxx>
45 #include <svtools/svtresid.hxx>
46 #include <svtools/strings.hrc>
47 #include <svtools/ctrlbox.hxx>
48 #include <svtools/ctrltool.hxx>
49 #include <svtools/borderhelper.hxx>
50 #include <svtools/valueset.hxx>
52 #include <basegfx/polygon/b2dpolygon.hxx>
53 #include <basegfx/polygon/b2dpolygontools.hxx>
54 #include <editeng/borderline.hxx>
56 #include <rtl/bootstrap.hxx>
58 #include <borderline.hrc>
62 #define IMGOUTERTEXTSPACE 5
63 #define EXTRAFONTSIZE 5
64 #define GAPTOEXTRAPREVIEW 10
67 constexpr OUStringLiteral FONTNAMEBOXMRUENTRIESFILE
= u
"/user/config/fontnameboxmruentries";
70 BorderWidthImpl::BorderWidthImpl( BorderWidthImplFlags nFlags
, double nRate1
, double nRate2
, double nRateGap
):
74 m_nRateGap( nRateGap
)
78 bool BorderWidthImpl::operator== ( const BorderWidthImpl
& r
) const
80 return ( m_nFlags
== r
.m_nFlags
) &&
81 ( m_nRate1
== r
.m_nRate1
) &&
82 ( m_nRate2
== r
.m_nRate2
) &&
83 ( m_nRateGap
== r
.m_nRateGap
);
86 tools::Long
BorderWidthImpl::GetLine1( tools::Long nWidth
) const
88 tools::Long result
= static_cast<tools::Long
>(m_nRate1
);
89 if ( m_nFlags
& BorderWidthImplFlags::CHANGE_LINE1
)
91 tools::Long
const nConstant2
= (m_nFlags
& BorderWidthImplFlags::CHANGE_LINE2
) ? 0 : m_nRate2
;
92 tools::Long
const nConstantD
= (m_nFlags
& BorderWidthImplFlags::CHANGE_DIST
) ? 0 : m_nRateGap
;
93 result
= std::max
<tools::Long
>(0,
94 static_cast<tools::Long
>((m_nRate1
* nWidth
) + 0.5)
95 - (nConstant2
+ nConstantD
));
96 if (result
== 0 && m_nRate1
> 0.0 && nWidth
> 0)
97 { // fdo#51777: hack to essentially treat 1 twip DOUBLE border
98 result
= 1; // as 1 twip SINGLE border
104 tools::Long
BorderWidthImpl::GetLine2( tools::Long nWidth
) const
106 tools::Long result
= static_cast<tools::Long
>(m_nRate2
);
107 if ( m_nFlags
& BorderWidthImplFlags::CHANGE_LINE2
)
109 tools::Long
const nConstant1
= (m_nFlags
& BorderWidthImplFlags::CHANGE_LINE1
) ? 0 : m_nRate1
;
110 tools::Long
const nConstantD
= (m_nFlags
& BorderWidthImplFlags::CHANGE_DIST
) ? 0 : m_nRateGap
;
111 result
= std::max
<tools::Long
>(0,
112 static_cast<tools::Long
>((m_nRate2
* nWidth
) + 0.5)
113 - (nConstant1
+ nConstantD
));
118 tools::Long
BorderWidthImpl::GetGap( tools::Long nWidth
) const
120 tools::Long result
= static_cast<tools::Long
>(m_nRateGap
);
121 if ( m_nFlags
& BorderWidthImplFlags::CHANGE_DIST
)
123 tools::Long
const nConstant1
= (m_nFlags
& BorderWidthImplFlags::CHANGE_LINE1
) ? 0 : m_nRate1
;
124 tools::Long
const nConstant2
= (m_nFlags
& BorderWidthImplFlags::CHANGE_LINE2
) ? 0 : m_nRate2
;
125 result
= std::max
<tools::Long
>(0,
126 static_cast<tools::Long
>((m_nRateGap
* nWidth
) + 0.5)
127 - (nConstant1
+ nConstant2
));
130 // Avoid having too small distances (less than 0.1pt)
131 if ( result
< MINGAPWIDTH
&& m_nRate1
> 0 && m_nRate2
> 0 )
132 result
= MINGAPWIDTH
;
137 static double lcl_getGuessedWidth( tools::Long nTested
, double nRate
, bool bChanging
)
139 double nWidth
= -1.0;
141 nWidth
= double( nTested
) / nRate
;
144 if ( rtl::math::approxEqual(double( nTested
), nRate
) )
151 tools::Long
BorderWidthImpl::GuessWidth( tools::Long nLine1
, tools::Long nLine2
, tools::Long nGap
)
153 std::vector
< double > aToCompare
;
154 bool bInvalid
= false;
156 bool bLine1Change
= bool( m_nFlags
& BorderWidthImplFlags::CHANGE_LINE1
);
157 double nWidth1
= lcl_getGuessedWidth( nLine1
, m_nRate1
, bLine1Change
);
159 aToCompare
.push_back( nWidth1
);
160 else if (nWidth1
< 0)
163 bool bLine2Change
= bool( m_nFlags
& BorderWidthImplFlags::CHANGE_LINE2
);
164 double nWidth2
= lcl_getGuessedWidth( nLine2
, m_nRate2
, bLine2Change
);
166 aToCompare
.push_back( nWidth2
);
167 else if (nWidth2
< 0)
170 bool bGapChange
= bool( m_nFlags
& BorderWidthImplFlags::CHANGE_DIST
);
171 double nWidthGap
= lcl_getGuessedWidth( nGap
, m_nRateGap
, bGapChange
);
172 if ( bGapChange
&& nGap
>= MINGAPWIDTH
)
173 aToCompare
.push_back( nWidthGap
);
174 else if ( !bGapChange
&& nWidthGap
< 0 )
177 // non-constant line width factors must sum to 1
178 assert((((bLine1Change
) ? m_nRate1
: 0) +
179 ((bLine2Change
) ? m_nRate2
: 0) +
180 ((bGapChange
) ? m_nRateGap
: 0)) - 1.0 < 0.00001 );
183 if ( (!bInvalid
) && (!aToCompare
.empty()) )
185 nWidth
= *aToCompare
.begin();
186 for (auto const& elem
: aToCompare
)
188 bInvalid
= ( nWidth
!= elem
);
192 nWidth
= bInvalid
? 0.0 : nLine1
+ nLine2
+ nGap
;
198 static void lclDrawPolygon( OutputDevice
& rDev
, const basegfx::B2DPolygon
& rPolygon
, tools::Long nWidth
, SvxBorderLineStyle nDashing
)
200 AntialiasingFlags nOldAA
= rDev
.GetAntialiasing();
201 rDev
.SetAntialiasing( nOldAA
& ~AntialiasingFlags::Enable
);
203 tools::Long nPix
= rDev
.PixelToLogic(Size(1, 1)).Width();
204 basegfx::B2DPolyPolygon aPolygons
= svtools::ApplyLineDashing(rPolygon
, nDashing
, nPix
);
206 // Handle problems of width 1px in Pixel mode: 0.5px gives a 1px line
207 if (rDev
.GetMapMode().GetMapUnit() == MapUnit::MapPixel
&& nWidth
== nPix
)
210 for ( sal_uInt32 i
= 0; i
< aPolygons
.count( ); i
++ )
212 const basegfx::B2DPolygon
& aDash
= aPolygons
.getB2DPolygon( i
);
213 basegfx::B2DPoint aStart
= aDash
.getB2DPoint( 0 );
214 basegfx::B2DPoint aEnd
= aDash
.getB2DPoint( aDash
.count() - 1 );
216 basegfx::B2DVector
aVector( aEnd
- aStart
);
217 aVector
.normalize( );
218 const basegfx::B2DVector
aPerpendicular(basegfx::getPerpendicular(aVector
));
220 const basegfx::B2DVector
aWidthOffset( double( nWidth
) / 2 * aPerpendicular
);
221 basegfx::B2DPolygon aDashPolygon
;
222 aDashPolygon
.append( aStart
+ aWidthOffset
);
223 aDashPolygon
.append( aEnd
+ aWidthOffset
);
224 aDashPolygon
.append( aEnd
- aWidthOffset
);
225 aDashPolygon
.append( aStart
- aWidthOffset
);
226 aDashPolygon
.setClosed( true );
228 rDev
.DrawPolygon( aDashPolygon
);
231 rDev
.SetAntialiasing( nOldAA
);
237 * Dashing array must start with a line width and end with a blank width.
239 static std::vector
<double> GetDashing( SvxBorderLineStyle nDashing
)
241 std::vector
<double> aPattern
;
244 case SvxBorderLineStyle::DOTTED
:
245 aPattern
.push_back( 1.0 ); // line
246 aPattern
.push_back( 2.0 ); // blank
248 case SvxBorderLineStyle::DASHED
:
249 aPattern
.push_back( 16.0 ); // line
250 aPattern
.push_back( 5.0 ); // blank
252 case SvxBorderLineStyle::FINE_DASHED
:
253 aPattern
.push_back( 6.0 ); // line
254 aPattern
.push_back( 2.0 ); // blank
256 case SvxBorderLineStyle::DASH_DOT
:
257 aPattern
.push_back( 16.0 ); // line
258 aPattern
.push_back( 5.0 ); // blank
259 aPattern
.push_back( 5.0 ); // line
260 aPattern
.push_back( 5.0 ); // blank
262 case SvxBorderLineStyle::DASH_DOT_DOT
:
263 aPattern
.push_back( 16.0 ); // line
264 aPattern
.push_back( 5.0 ); // blank
265 aPattern
.push_back( 5.0 ); // line
266 aPattern
.push_back( 5.0 ); // blank
267 aPattern
.push_back( 5.0 ); // line
268 aPattern
.push_back( 5.0 ); // blank
283 explicit ApplyScale( double fScale
) : mfScale(fScale
) {}
284 void operator() ( double& rVal
)
292 std::vector
<double> GetLineDashing( SvxBorderLineStyle nDashing
, double fScale
)
294 std::vector
<double> aPattern
= GetDashing(nDashing
);
295 std::for_each(aPattern
.begin(), aPattern
.end(), ApplyScale(fScale
));
299 basegfx::B2DPolyPolygon
ApplyLineDashing( const basegfx::B2DPolygon
& rPolygon
, SvxBorderLineStyle nDashing
, double fScale
)
301 std::vector
<double> aPattern
= GetDashing(nDashing
);
302 std::for_each(aPattern
.begin(), aPattern
.end(), ApplyScale(fScale
));
304 basegfx::B2DPolyPolygon aPolygons
;
306 if (aPattern
.empty())
307 aPolygons
.append(rPolygon
);
309 basegfx::utils::applyLineDashing(rPolygon
, aPattern
, &aPolygons
);
314 void DrawLine( OutputDevice
& rDev
, const Point
& rP1
, const Point
& rP2
,
315 sal_uInt32 nWidth
, SvxBorderLineStyle nDashing
)
317 DrawLine( rDev
, basegfx::B2DPoint( rP1
.X(), rP1
.Y() ),
318 basegfx::B2DPoint( rP2
.X(), rP2
.Y( ) ), nWidth
, nDashing
);
321 void DrawLine( OutputDevice
& rDev
, const basegfx::B2DPoint
& rP1
, const basegfx::B2DPoint
& rP2
,
322 sal_uInt32 nWidth
, SvxBorderLineStyle nDashing
)
324 basegfx::B2DPolygon aPolygon
;
325 aPolygon
.append( rP1
);
326 aPolygon
.append( rP2
);
327 lclDrawPolygon( rDev
, aPolygon
, nWidth
, nDashing
);
332 static Size gUserItemSz
;
333 static int gFontNameBoxes
;
334 static size_t gPreviewsPerDevice
;
335 static std::vector
<VclPtr
<VirtualDevice
>> gFontPreviewVirDevs
;
336 static std::vector
<OUString
> gRenderedFontNames
;
337 static int gHighestDPI
= 0;
341 std::vector
<VclPtr
<VirtualDevice
>>& getFontPreviewVirDevs()
343 return gFontPreviewVirDevs
;
346 void clearFontPreviewVirDevs()
348 for (auto &rDev
: gFontPreviewVirDevs
)
349 rDev
.disposeAndClear();
350 gFontPreviewVirDevs
.clear();
353 std::vector
<OUString
>& getRenderedFontNames()
355 return gRenderedFontNames
;
358 void clearRenderedFontNames()
360 gRenderedFontNames
.clear();
363 void calcCustomItemSize(const weld::ComboBox
& rComboBox
)
365 gUserItemSz
= Size(rComboBox
.get_approximate_digit_width() * 52, rComboBox
.get_text_height());
366 gUserItemSz
.setHeight(gUserItemSz
.Height() * 16);
367 gUserItemSz
.setHeight(gUserItemSz
.Height() / 10);
369 size_t nMaxDeviceHeight
= SAL_MAX_INT16
/ 16; // see limitXCreatePixmap and be generous wrt up to x16 hidpi
370 assert(gUserItemSz
.Height() != 0);
371 gPreviewsPerDevice
= gUserItemSz
.Height() == 0 ? 16 : nMaxDeviceHeight
/ gUserItemSz
.Height();
372 if (comphelper::LibreOfficeKit::isActive())
373 gPreviewsPerDevice
= 1;
377 IMPL_LINK(FontNameBox
, SettingsChangedHdl
, VclSimpleEvent
&, rEvent
, void)
379 if (rEvent
.GetId() != VclEventId::ApplicationDataChanged
)
382 if (comphelper::LibreOfficeKit::isActive())
385 DataChangedEvent
* pData
= static_cast<DataChangedEvent
*>(static_cast<VclWindowEvent
&>(rEvent
).GetData());
386 if (pData
->GetType() == DataChangedEventType::SETTINGS
)
388 clearFontPreviewVirDevs();
389 clearRenderedFontNames();
390 calcCustomItemSize(*m_xComboBox
);
391 if (mbWYSIWYG
&& mpFontList
&& !mpFontList
->empty())
393 mnPreviewProgress
= 0;
394 maUpdateIdle
.Start();
399 FontNameBox::FontNameBox(std::unique_ptr
<weld::ComboBox
> p
)
400 : m_xComboBox(std::move(p
))
401 , mnPreviewProgress(0)
403 , maUpdateIdle("FontNameBox Preview Update")
406 InitFontMRUEntriesFile();
408 maUpdateIdle
.SetPriority(TaskPriority::LOWEST
);
409 maUpdateIdle
.SetInvokeHandler(LINK(this, FontNameBox
, UpdateHdl
));
411 Application::AddEventListener(LINK(this, FontNameBox
, SettingsChangedHdl
));
414 FontNameBox::~FontNameBox()
416 Application::RemoveEventListener(LINK(this, FontNameBox
, SettingsChangedHdl
));
420 SaveMRUEntries (maFontMRUEntriesFile
);
421 ImplDestroyFontList();
426 clearFontPreviewVirDevs();
427 clearRenderedFontNames();
431 void FontNameBox::SaveMRUEntries(const OUString
& aFontMRUEntriesFile
) const
433 OString
aEntries(OUStringToOString(m_xComboBox
->get_mru_entries(),
434 RTL_TEXTENCODING_UTF8
));
436 if (aEntries
.isEmpty() || aFontMRUEntriesFile
.isEmpty())
439 SvFileStream aStream
;
440 aStream
.Open( aFontMRUEntriesFile
, StreamMode::WRITE
| StreamMode::TRUNC
);
441 if( ! (aStream
.IsOpen() && aStream
.IsWritable()) )
443 SAL_INFO("svtools.control", "FontNameBox::SaveMRUEntries: opening mru entries file " << aFontMRUEntriesFile
<< " failed");
447 aStream
.SetLineDelimiter( LINEEND_LF
);
448 aStream
.WriteLine( aEntries
);
449 aStream
.WriteLine( "" );
452 void FontNameBox::LoadMRUEntries( const OUString
& aFontMRUEntriesFile
)
454 if (aFontMRUEntriesFile
.isEmpty())
457 if (!officecfg::Office::Common::Font::View::ShowFontBoxWYSIWYG::get())
460 SvFileStream
aStream( aFontMRUEntriesFile
, StreamMode::READ
);
461 if( ! aStream
.IsOpen() )
463 SAL_INFO("svtools.control", "FontNameBox::LoadMRUEntries: opening mru entries file " << aFontMRUEntriesFile
<< " failed");
468 aStream
.ReadLine( aLine
);
469 OUString aEntries
= OStringToOUString(aLine
,
470 RTL_TEXTENCODING_UTF8
);
471 m_xComboBox
->set_mru_entries(aEntries
);
474 void FontNameBox::InitFontMRUEntriesFile()
476 OUString
sUserConfigDir(u
"${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER
"/" SAL_CONFIGFILE( "bootstrap") "::UserInstallation}"_ustr
);
477 rtl::Bootstrap::expandMacros(sUserConfigDir
);
479 maFontMRUEntriesFile
= sUserConfigDir
;
480 if( !maFontMRUEntriesFile
.isEmpty() )
482 maFontMRUEntriesFile
+= FONTNAMEBOXMRUENTRIESFILE
;
486 void FontNameBox::ImplDestroyFontList()
489 mnPreviewProgress
= 0;
493 void FontNameBox::Fill( const FontList
* pList
)
495 // store old text and clear box
496 OUString aOldText
= m_xComboBox
->get_active_text();
497 OUString rEntries
= m_xComboBox
->get_mru_entries();
498 bool bLoadFromFile
= rEntries
.isEmpty();
499 m_xComboBox
->freeze();
500 m_xComboBox
->clear();
502 ImplDestroyFontList();
503 mpFontList
.reset(new ImplFontList
);
506 size_t nFontCount
= pList
->GetFontNameCount();
507 for (size_t i
= 0; i
< nFontCount
; ++i
)
509 const FontMetric
& rFontMetric
= pList
->GetFontName(i
);
510 m_xComboBox
->append(OUString::number(i
), rFontMetric
.GetFamilyName());
511 mpFontList
->push_back(rFontMetric
);
515 LoadMRUEntries(maFontMRUEntriesFile
);
517 m_xComboBox
->set_mru_entries(rEntries
);
521 if (mbWYSIWYG
&& nFontCount
)
523 assert(mnPreviewProgress
== 0 && "ImplDestroyFontList wasn't called");
524 maUpdateIdle
.Start();
528 if (!aOldText
.isEmpty())
529 set_active_or_entry_text(aOldText
);
532 void FontNameBox::EnableWYSIWYG(bool bEnable
)
534 if (o3tl::IsRunningUnitTest())
536 if (mbWYSIWYG
== bEnable
)
542 calcCustomItemSize(*m_xComboBox
);
543 m_xComboBox
->connect_custom_get_size(LINK(this, FontNameBox
, CustomGetSizeHdl
));
544 m_xComboBox
->connect_custom_render(LINK(this, FontNameBox
, CustomRenderHdl
));
548 m_xComboBox
->connect_custom_get_size(Link
<OutputDevice
&, Size
>());
549 m_xComboBox
->connect_custom_render(Link
<weld::ComboBox::render_args
, void>());
551 m_xComboBox
->set_custom_renderer(mbWYSIWYG
);
554 IMPL_LINK(FontNameBox
, CustomGetSizeHdl
, OutputDevice
&, rDevice
, Size
)
556 if (comphelper::LibreOfficeKit::isActive())
558 calcCustomItemSize(*m_xComboBox
);
559 gUserItemSz
.setWidth(1.0 * rDevice
.GetDPIX() / 96.0 * gUserItemSz
.getWidth());
560 gUserItemSz
.setHeight(1.0 * rDevice
.GetDPIY() / 96.0 * gUserItemSz
.getHeight());
562 return mbWYSIWYG
? gUserItemSz
: Size();
567 tools::Long
shrinkFontToFit(OUString
const &rSampleText
, tools::Long nH
, vcl::Font
&rFont
, OutputDevice
&rDevice
, tools::Rectangle
&rTextRect
)
569 tools::Long nWidth
= 0;
571 Size
aSize( rFont
.GetFontSize() );
573 //Make sure it fits in the available height
574 while (aSize
.Height() > 0)
576 if (!rDevice
.GetTextBoundRect(rTextRect
, rSampleText
))
578 if (rTextRect
.GetHeight() <= nH
)
580 nWidth
= rTextRect
.GetWidth();
584 aSize
.AdjustHeight( -(EXTRAFONTSIZE
) );
585 rFont
.SetFontSize(aSize
);
586 rDevice
.SetFont(rFont
);
593 IMPL_LINK_NOARG(FontNameBox
, UpdateHdl
, Timer
*, void)
595 if (comphelper::LibreOfficeKit::isActive())
598 CachePreview(mnPreviewProgress
++, nullptr);
599 // tdf#132536 limit to ~25 pre-rendered for now. The font caches look
600 // b0rked, the massive charmaps are ~never swapped out, and don't count
601 // towards the size of a font in the font cache.
602 if (mnPreviewProgress
< std::min
<size_t>(25, mpFontList
->size()))
603 maUpdateIdle
.Start();
606 static void DrawPreview(const FontMetric
& rFontMetric
, const Point
& rTopLeft
, OutputDevice
& rDevice
, bool bSelected
)
608 rDevice
.Push(vcl::PushFlags::TEXTCOLOR
);
610 const StyleSettings
& rStyleSettings
= Application::GetSettings().GetStyleSettings();
612 rDevice
.SetTextColor(rStyleSettings
.GetHighlightTextColor());
614 rDevice
.SetTextColor(rStyleSettings
.GetDialogTextColor());
616 tools::Long nX
= rTopLeft
.X();
617 tools::Long nH
= gUserItemSz
.Height();
619 nX
+= IMGOUTERTEXTSPACE
;
621 const bool bSymbolFont
= isSymbolFont(rFontMetric
);
623 vcl::Font
aOldFont(rDevice
.GetFont());
624 Size
aSize( aOldFont
.GetFontSize() );
625 aSize
.AdjustHeight(EXTRAFONTSIZE
);
626 vcl::Font
aFont( rFontMetric
);
627 aFont
.SetFontSize( aSize
);
628 rDevice
.SetFont(aFont
);
630 bool bUsingCorrectFont
= true;
631 tools::Rectangle aTextRect
;
633 // Preview the font name
634 const OUString
& sFontName
= rFontMetric
.GetFamilyName();
636 //If it shouldn't or can't draw its own name because it doesn't have the glyphs
637 if (!canRenderNameOfSelectedFont(rDevice
))
638 bUsingCorrectFont
= false;
641 //Make sure it fits in the available height, shrinking the font if necessary
642 bUsingCorrectFont
= shrinkFontToFit(sFontName
, nH
, aFont
, rDevice
, aTextRect
) != 0;
645 if (!bUsingCorrectFont
)
647 rDevice
.SetFont(aOldFont
);
648 rDevice
.GetTextBoundRect(aTextRect
, sFontName
);
651 tools::Long nTextHeight
= aTextRect
.GetHeight();
652 tools::Long nDesiredGap
= (nH
-nTextHeight
)/2;
653 tools::Long nVertAdjust
= nDesiredGap
- aTextRect
.Top();
654 Point
aPos( nX
, rTopLeft
.Y() + nVertAdjust
);
655 rDevice
.DrawText(aPos
, sFontName
);
656 tools::Long nTextX
= aPos
.X() + aTextRect
.GetWidth() + GAPTOEXTRAPREVIEW
;
658 if (!bUsingCorrectFont
)
659 rDevice
.SetFont(aFont
);
661 OUString sSampleText
;
665 const bool bNameBeginsWithLatinText
= rFontMetric
.GetFamilyName()[0] <= 'z';
667 if (bNameBeginsWithLatinText
|| !bUsingCorrectFont
)
668 sSampleText
= makeShortRepresentativeTextForSelectedFont(rDevice
);
671 //If we're not a symbol font, but could neither render our own name and
672 //we can't determine what script it would like to render, then try a
673 //few well known scripts
674 if (sSampleText
.isEmpty() && !bUsingCorrectFont
)
676 static const UScriptCode aScripts
[] =
705 USCRIPT_SIMPLIFIED_HAN
,
706 USCRIPT_TRADITIONAL_HAN
,
711 for (const UScriptCode
& rScript
: aScripts
)
713 OUString sText
= makeShortRepresentativeTextForScript(rScript
);
714 if (!sText
.isEmpty())
716 bool bHasSampleTextGlyphs
= (-1 == rDevice
.HasGlyphs(aFont
, sText
));
717 if (bHasSampleTextGlyphs
)
725 static const UScriptCode aMinimalScripts
[] =
727 USCRIPT_HEBREW
, //e.g. biblical hebrew
731 for (const UScriptCode
& rMinimalScript
: aMinimalScripts
)
733 OUString sText
= makeShortMinimalTextForScript(rMinimalScript
);
734 if (!sText
.isEmpty())
736 bool bHasSampleTextGlyphs
= (-1 == rDevice
.HasGlyphs(aFont
, sText
));
737 if (bHasSampleTextGlyphs
)
746 //If we're a symbol font, or for some reason the font still couldn't
747 //render something representative of what it would like to render then
748 //make up some semi-random text that it *can* display
749 if (bSymbolFont
|| (!bUsingCorrectFont
&& sSampleText
.isEmpty()))
750 sSampleText
= makeShortRepresentativeSymbolTextForSelectedFont(rDevice
);
752 if (!sSampleText
.isEmpty())
754 const Size
&rItemSize
= gUserItemSz
;
756 //leave a little border at the edge
757 tools::Long nSpace
= rItemSize
.Width() - nTextX
- IMGOUTERTEXTSPACE
;
760 //Make sure it fits in the available height, and get how wide that would be
761 tools::Long nWidth
= shrinkFontToFit(sSampleText
, nH
, aFont
, rDevice
, aTextRect
);
762 //Chop letters off until it fits in the available width
763 while (nWidth
> nSpace
|| nWidth
> gUserItemSz
.Width())
765 sSampleText
= sSampleText
.copy(0, sSampleText
.getLength()-1);
766 nWidth
= rDevice
.GetTextBoundRect(aTextRect
, sSampleText
) ?
767 aTextRect
.GetWidth() : 0;
770 //center the text on the line
771 if (!sSampleText
.isEmpty() && nWidth
)
773 nTextHeight
= aTextRect
.GetHeight();
774 nDesiredGap
= (nH
-nTextHeight
)/2;
775 nVertAdjust
= nDesiredGap
- aTextRect
.Top();
776 aPos
= Point(nTextX
+ nSpace
- nWidth
, rTopLeft
.Y() + nVertAdjust
);
777 rDevice
.DrawText(aPos
, sSampleText
);
782 rDevice
.SetFont(aOldFont
);
786 OutputDevice
& FontNameBox::CachePreview(size_t nIndex
, Point
* pTopLeft
,
787 sal_Int32 nDPIX
, sal_Int32 nDPIY
)
789 SolarMutexGuard aGuard
;
790 const FontMetric
& rFontMetric
= (*mpFontList
)[nIndex
];
791 const OUString
& rFontName
= rFontMetric
.GetFamilyName();
793 if (comphelper::LibreOfficeKit::isActive())
795 // we want to cache only best quality previews
796 if (gHighestDPI
< nDPIX
|| gHighestDPI
< nDPIY
)
798 clearRenderedFontNames();
799 clearFontPreviewVirDevs();
800 gHighestDPI
= std::max(nDPIX
, nDPIY
);
802 else if (gHighestDPI
> nDPIX
|| gHighestDPI
> nDPIY
)
809 size_t nPreviewIndex
;
810 auto& rFontNames
= getRenderedFontNames();
811 auto& rVirtualDevs
= getFontPreviewVirDevs();
812 auto xFind
= std::find(rFontNames
.begin(), rFontNames
.end(), rFontName
);
813 bool bPreviewAvailable
= xFind
!= rFontNames
.end();
814 if (!bPreviewAvailable
)
816 nPreviewIndex
= rFontNames
.size();
817 rFontNames
.push_back(rFontName
);
820 nPreviewIndex
= std::distance(rFontNames
.begin(), xFind
);
822 size_t nPage
= nPreviewIndex
/ gPreviewsPerDevice
;
823 size_t nIndexInPage
= nPreviewIndex
- (nPage
* gPreviewsPerDevice
);
825 Point
aTopLeft(0, gUserItemSz
.Height() * nIndexInPage
);
827 if (!bPreviewAvailable
)
829 if (nPage
>= rVirtualDevs
.size())
831 bool bIsLOK
= comphelper::LibreOfficeKit::isActive();
832 if (bIsLOK
) // allow transparent background in LOK case
833 rVirtualDevs
.emplace_back(VclPtr
<VirtualDevice
>::Create(DeviceFormat::WITH_ALPHA
));
835 rVirtualDevs
.emplace_back(m_xComboBox
->create_render_virtual_device());
837 VirtualDevice
& rDevice
= *rVirtualDevs
.back();
838 rDevice
.SetOutputSizePixel(Size(gUserItemSz
.Width(), gUserItemSz
.Height() * gPreviewsPerDevice
));
841 rDevice
.SetDPIX(nDPIX
);
842 rDevice
.SetDPIY(nDPIY
);
845 weld::SetPointFont(rDevice
, m_xComboBox
->get_font(), bIsLOK
);
846 assert(rVirtualDevs
.size() == nPage
+ 1);
849 DrawPreview(rFontMetric
, aTopLeft
, *rVirtualDevs
.back(), false);
853 *pTopLeft
= aTopLeft
;
855 return *rVirtualDevs
[nPage
];
858 IMPL_LINK(FontNameBox
, CustomRenderHdl
, weld::ComboBox::render_args
, aPayload
, void)
860 vcl::RenderContext
& rRenderContext
= std::get
<0>(aPayload
);
861 const ::tools::Rectangle
& rRect
= std::get
<1>(aPayload
);
862 bool bSelected
= std::get
<2>(aPayload
);
863 const OUString
& rId
= std::get
<3>(aPayload
);
865 sal_uInt32 nIndex
= rId
.toUInt32();
867 Point
aDestPoint(rRect
.TopLeft());
868 auto nMargin
= (rRect
.GetHeight() - gUserItemSz
.Height()) / 2;
869 aDestPoint
.AdjustY(nMargin
);
873 const FontMetric
& rFontMetric
= (*mpFontList
)[nIndex
];
874 DrawPreview(rFontMetric
, aDestPoint
, rRenderContext
, true);
875 m_aLivePreviewHdl
.Call(rFontMetric
);
879 // use cache of unselected entries
881 OutputDevice
& rDevice
= CachePreview(nIndex
, &aTopLeft
,
882 rRenderContext
.GetDPIX(),
883 rRenderContext
.GetDPIY());
885 Size aSourceSize
= comphelper::LibreOfficeKit::isActive() ? rDevice
.GetOutputSizePixel() : gUserItemSz
;
886 rRenderContext
.DrawOutDev(aDestPoint
, gUserItemSz
,
887 aTopLeft
, aSourceSize
,
892 void FontNameBox::set_active_or_entry_text(const OUString
& rText
)
894 const int nFound
= m_xComboBox
->find_text(rText
);
896 m_xComboBox
->set_active(nFound
);
897 m_xComboBox
->set_entry_text(rText
);
900 FontStyleBox::FontStyleBox(std::unique_ptr
<weld::ComboBox
> p
)
901 : m_xComboBox(std::move(p
))
902 , m_aLastStyle(m_xComboBox
->get_active_text())
904 //Use the standard texts to get an optimal size and stick to that size.
905 //That should stop the character dialog dancing around.
906 auto nMaxLen
= m_xComboBox
->get_pixel_size(SvtResId(STR_SVT_STYLE_LIGHT
)).Width();
907 nMaxLen
= std::max(nMaxLen
, m_xComboBox
->get_pixel_size(SvtResId(STR_SVT_STYLE_LIGHT_ITALIC
)).Width());
908 nMaxLen
= std::max(nMaxLen
, m_xComboBox
->get_pixel_size(SvtResId(STR_SVT_STYLE_NORMAL
)).Width());
909 nMaxLen
= std::max(nMaxLen
, m_xComboBox
->get_pixel_size(SvtResId(STR_SVT_STYLE_NORMAL_ITALIC
)).Width());
910 nMaxLen
= std::max(nMaxLen
, m_xComboBox
->get_pixel_size(SvtResId(STR_SVT_STYLE_BOLD
)).Width());
911 nMaxLen
= std::max(nMaxLen
, m_xComboBox
->get_pixel_size(SvtResId(STR_SVT_STYLE_BOLD_ITALIC
)).Width());
912 nMaxLen
= std::max(nMaxLen
, m_xComboBox
->get_pixel_size(SvtResId(STR_SVT_STYLE_BLACK
)).Width());
913 nMaxLen
= std::max(nMaxLen
, m_xComboBox
->get_pixel_size(SvtResId(STR_SVT_STYLE_BLACK_ITALIC
)).Width());
914 m_xComboBox
->set_entry_width_chars(std::ceil(nMaxLen
/ m_xComboBox
->get_approximate_digit_width()));
915 m_xComboBox
->connect_changed(LINK(this, FontStyleBox
, ChangeHdl
));
918 IMPL_LINK(FontStyleBox
, ChangeHdl
, weld::ComboBox
&, rComboBox
, void)
920 // update m_aLastStyle to whatever is explicitly selected by the user
921 m_aLastStyle
= rComboBox
.get_active_text();
922 m_aChangedLink
.Call(rComboBox
);
925 void FontStyleBox::Fill( std::u16string_view rName
, const FontList
* pList
)
927 OUString aOldText
= m_xComboBox
->get_active_text();
929 m_xComboBox
->freeze();
930 m_xComboBox
->clear();
932 // does a font with this name already exist?
933 sal_Handle hFontMetric
= pList
->GetFirstFontMetric( rName
);
937 FontWeight eLastWeight
= WEIGHT_DONTKNOW
;
938 FontItalic eLastItalic
= ITALIC_NONE
;
939 FontWidth eLastWidth
= WIDTH_DONTKNOW
;
940 bool bNormal
= false;
941 bool bItalic
= false;
943 bool bBoldItalic
= false;
944 bool bInsert
= false;
945 FontMetric aFontMetric
;
946 while ( hFontMetric
)
948 aFontMetric
= FontList::GetFontMetric( hFontMetric
);
950 FontWeight eWeight
= aFontMetric
.GetWeight();
951 FontItalic eItalic
= aFontMetric
.GetItalic();
952 FontWidth eWidth
= aFontMetric
.GetWidthType();
953 // Only if the attributes are different, we insert the
954 // Font to avoid double Entries in different languages
955 if ( (eWeight
!= eLastWeight
) || (eItalic
!= eLastItalic
) ||
956 (eWidth
!= eLastWidth
) )
959 m_xComboBox
->append_text(aStyleText
);
961 if ( eWeight
<= WEIGHT_NORMAL
)
963 if ( eItalic
!= ITALIC_NONE
)
970 if ( eItalic
!= ITALIC_NONE
)
976 // For wrong StyleNames we replace this with the correct once
977 aStyleText
= pList
->GetStyleName( aFontMetric
);
978 bInsert
= m_xComboBox
->find_text(aStyleText
) == -1;
981 aStyleText
= pList
->GetStyleName( eWeight
, eItalic
);
982 bInsert
= m_xComboBox
->find_text(aStyleText
) == -1;
985 eLastWeight
= eWeight
;
986 eLastItalic
= eItalic
;
993 // If we have two names for the same attributes
994 // we prefer the translated standard names
995 const OUString
& rAttrStyleText
= pList
->GetStyleName( eWeight
, eItalic
);
996 if (rAttrStyleText
!= aStyleText
)
998 OUString aTempStyleText
= pList
->GetStyleName( aFontMetric
);
999 if (rAttrStyleText
== aTempStyleText
)
1000 aStyleText
= rAttrStyleText
;
1001 bInsert
= m_xComboBox
->find_text(aStyleText
) == -1;
1006 if ( !bItalic
&& (aStyleText
== pList
->GetItalicStr()) )
1008 else if ( !bBold
&& (aStyleText
== pList
->GetBoldStr()) )
1010 else if ( !bBoldItalic
&& (aStyleText
== pList
->GetBoldItalicStr()) )
1013 hFontMetric
= FontList::GetNextFontMetric( hFontMetric
);
1017 m_xComboBox
->append_text(aStyleText
);
1019 // certain style as copy
1023 m_xComboBox
->append_text(pList
->GetItalicStr());
1025 m_xComboBox
->append_text(pList
->GetBoldStr());
1029 if ( bNormal
|| bItalic
|| bBold
)
1030 m_xComboBox
->append_text(pList
->GetBoldItalicStr());
1035 // insert standard styles if no font
1036 m_xComboBox
->append_text(pList
->GetNormalStr());
1037 m_xComboBox
->append_text(pList
->GetItalicStr());
1038 m_xComboBox
->append_text(pList
->GetBoldStr());
1039 m_xComboBox
->append_text(pList
->GetBoldItalicStr());
1042 m_xComboBox
->thaw();
1044 // tdf#162113 prefer restoring the last explicitly set
1045 // style if that is possible
1046 if (!m_aLastStyle
.isEmpty())
1048 int nFound
= m_xComboBox
->find_text(m_aLastStyle
);
1051 m_xComboBox
->set_active(nFound
);
1056 // otherwise, restore the style that was last selected
1057 // if that is possible
1058 if (!aOldText
.isEmpty())
1060 int nFound
= m_xComboBox
->find_text(aOldText
);
1063 m_xComboBox
->set_active(nFound
);
1068 // otherwise, just pick something
1069 m_xComboBox
->set_active(0);
1072 FontSizeBox::FontSizeBox(std::unique_ptr
<weld::ComboBox
> p
)
1073 : pFontList(nullptr)
1077 , eUnit(FieldUnit::POINT
)
1085 , bRelativeMode(false)
1087 , bPtRelative(false)
1089 , m_xComboBox(std::move(p
))
1091 m_xComboBox
->set_entry_width_chars(std::ceil(m_xComboBox
->get_pixel_size(format_number(105)).Width() /
1092 m_xComboBox
->get_approximate_digit_width()));
1093 m_xComboBox
->connect_focus_out(LINK(this, FontSizeBox
, ReformatHdl
));
1094 m_xComboBox
->connect_changed(LINK(this, FontSizeBox
, ModifyHdl
));
1097 void FontSizeBox::set_active_or_entry_text(const OUString
& rText
)
1099 const int nFound
= m_xComboBox
->find_text(rText
);
1101 m_xComboBox
->set_active(nFound
);
1102 m_xComboBox
->set_entry_text(rText
);
1105 IMPL_LINK(FontSizeBox
, ReformatHdl
, weld::Widget
&, rWidget
, void)
1107 FontSizeNames
aFontSizeNames(Application::GetSettings().GetUILanguageTag().getLanguageType());
1108 if (!bRelativeMode
|| !aFontSizeNames
.IsEmpty())
1110 if (aFontSizeNames
.Name2Size(m_xComboBox
->get_active_text()) != 0)
1114 set_value(get_value());
1116 m_aFocusOutHdl
.Call(rWidget
);
1119 IMPL_LINK(FontSizeBox
, ModifyHdl
, weld::ComboBox
&, rBox
, void)
1123 OUString aStr
= comphelper::string::stripStart(rBox
.get_active_text(), ' ');
1125 bool bNewMode
= bRelative
;
1126 bool bOldPtRelMode
= bPtRelative
;
1130 bPtRelative
= false;
1131 const sal_Unicode
* pStr
= aStr
.getStr();
1134 if ( ((*pStr
< '0') || (*pStr
> '9')) && (*pStr
!= '%') && !unicode::isSpace(*pStr
) )
1136 if ( ('-' == *pStr
|| '+' == *pStr
) && !bPtRelative
)
1138 else if ( bPtRelative
&& 'p' == *pStr
&& 't' == *++pStr
)
1149 else if (!aStr
.isEmpty())
1151 if ( -1 != aStr
.indexOf('%') )
1154 bPtRelative
= false;
1157 if ( '-' == aStr
[0] || '+' == aStr
[0] )
1164 if ( bNewMode
!= bRelative
|| bPtRelative
!= bOldPtRelMode
)
1165 SetRelative( bNewMode
);
1167 m_aChangeHdl
.Call(rBox
);
1170 void FontSizeBox::Fill( const FontList
* pList
)
1172 // remember for relative mode
1175 // no font sizes need to be set for relative mode
1180 const int* pTempAry
;
1181 const int* pAry
= nullptr;
1183 pAry
= FontList::GetStdSizeAry();
1185 // first insert font size names (for simplified/traditional chinese)
1186 FontSizeNames
aFontSizeNames( Application::GetSettings().GetUILanguageTag().getLanguageType() );
1187 if ( pAry
== FontList::GetStdSizeAry() )
1189 // for standard sizes we don't need to bother
1190 if (bStdSize
&& m_xComboBox
->get_count() && aFontSizeNames
.IsEmpty())
1197 int nSelectionStart
, nSelectionEnd
;
1198 m_xComboBox
->get_entry_selection_bounds(nSelectionStart
, nSelectionEnd
);
1199 OUString aStr
= m_xComboBox
->get_active_text();
1201 m_xComboBox
->freeze();
1202 m_xComboBox
->clear();
1205 if ( !aFontSizeNames
.IsEmpty() )
1207 if ( pAry
== FontList::GetStdSizeAry() )
1209 // for scalable fonts all font size names
1210 sal_uInt32 nCount
= aFontSizeNames
.Count();
1211 for( sal_uInt32 i
= 0; i
< nCount
; i
++ )
1213 OUString aSizeName
= aFontSizeNames
.GetIndexName( i
);
1214 int nSize
= aFontSizeNames
.GetIndexSize( i
);
1215 OUString
sId(OUString::number(-nSize
)); // mark as special
1216 m_xComboBox
->insert(nPos
, aSizeName
, &sId
, nullptr, nullptr);
1222 // for fixed size fonts only selectable font size names
1226 OUString aSizeName
= aFontSizeNames
.Size2Name( *pTempAry
);
1227 if ( !aSizeName
.isEmpty() )
1229 OUString
sId(OUString::number(-(*pTempAry
))); // mark as special
1230 m_xComboBox
->insert(nPos
, aSizeName
, &sId
, nullptr, nullptr);
1238 // then insert numerical font size values
1242 InsertValue(*pTempAry
);
1246 m_xComboBox
->thaw();
1247 set_active_or_entry_text(aStr
);
1248 m_xComboBox
->select_entry_region(nSelectionStart
, nSelectionEnd
);
1251 void FontSizeBox::EnableRelativeMode( sal_uInt16 nNewMin
, sal_uInt16 nNewMax
, sal_uInt16 nStep
)
1253 bRelativeMode
= true;
1257 SetUnit(FieldUnit::POINT
);
1260 void FontSizeBox::EnablePtRelativeMode( short nNewMin
, short nNewMax
, short nStep
)
1262 bRelativeMode
= true;
1263 nPtRelMin
= nNewMin
;
1264 nPtRelMax
= nNewMax
;
1266 SetUnit(FieldUnit::POINT
);
1269 void FontSizeBox::InsertValue(int i
)
1271 OUString
sNumber(OUString::number(i
));
1272 m_xComboBox
->append(sNumber
, format_number(i
));
1275 void FontSizeBox::SetRelative( bool bNewRelative
)
1277 if ( !bRelativeMode
)
1280 int nSelectionStart
, nSelectionEnd
;
1281 m_xComboBox
->get_entry_selection_bounds(nSelectionStart
, nSelectionEnd
);
1282 OUString aStr
= comphelper::string::stripStart(m_xComboBox
->get_active_text(), ' ');
1289 m_xComboBox
->clear();
1293 SetDecimalDigits( 1 );
1294 SetRange(nPtRelMin
, nPtRelMax
);
1295 SetUnit(FieldUnit::POINT
);
1297 short i
= nPtRelMin
, n
= 0;
1298 // JP 30.06.98: more than 100 values are not useful
1299 while ( i
<= nPtRelMax
&& n
++ < 100 )
1307 SetDecimalDigits(0);
1308 SetRange(nRelMin
, nRelMax
);
1309 SetUnit(FieldUnit::PERCENT
);
1311 sal_uInt16 i
= nRelMin
;
1312 while ( i
<= nRelMax
)
1322 m_xComboBox
->clear();
1323 bRelative
= bPtRelative
= false;
1324 SetDecimalDigits(1);
1326 SetUnit(FieldUnit::POINT
);
1331 set_active_or_entry_text(aStr
);
1332 m_xComboBox
->select_entry_region(nSelectionStart
, nSelectionEnd
);
1335 OUString
FontSizeBox::format_number(int nValue
) const
1339 //pawn percent off to icu to decide whether percent is separated from its number for this locale
1340 if (eUnit
== FieldUnit::PERCENT
)
1342 double fValue
= nValue
;
1343 fValue
/= weld::SpinButton::Power10(nDecimalDigits
);
1344 sRet
= unicode::formatPercent(fValue
, Application::GetSettings().GetUILanguageTag());
1348 const SvtSysLocale aSysLocale
;
1349 const LocaleDataWrapper
& rLocaleData
= aSysLocale
.GetLocaleData();
1350 sRet
= rLocaleData
.getNum(nValue
, nDecimalDigits
, true, false);
1351 if (eUnit
!= FieldUnit::NONE
&& eUnit
!= FieldUnit::DEGREE
)
1353 assert(eUnit
!= FieldUnit::PERCENT
);
1354 sRet
+= weld::MetricSpinButton::MetricToString(eUnit
);
1357 if (bRelativeMode
&& bPtRelative
&& (0 <= nValue
) && !sRet
.isEmpty())
1363 void FontSizeBox::SetValue(int nNewValue
, FieldUnit eInUnit
)
1365 auto nTempValue
= vcl::ConvertValue(nNewValue
, 0, GetDecimalDigits(), eInUnit
, GetUnit());
1366 if (nTempValue
< nMin
)
1368 else if (nTempValue
> nMax
)
1372 FontSizeNames
aFontSizeNames(Application::GetSettings().GetUILanguageTag().getLanguageType());
1373 // conversion loses precision; however font sizes should
1374 // never have a problem with that
1375 OUString aName
= aFontSizeNames
.Size2Name(nTempValue
);
1376 if (!aName
.isEmpty() && m_xComboBox
->find_text(aName
) != -1)
1378 m_xComboBox
->set_active_text(aName
);
1382 OUString aResult
= format_number(nTempValue
);
1383 set_active_or_entry_text(aResult
);
1386 void FontSizeBox::set_value(int nNewValue
)
1388 SetValue(nNewValue
, eUnit
);
1391 int FontSizeBox::get_value() const
1393 OUString aStr
= m_xComboBox
->get_active_text();
1396 FontSizeNames
aFontSizeNames(Application::GetSettings().GetUILanguageTag().getLanguageType());
1397 auto nValue
= aFontSizeNames
.Name2Size(aStr
);
1399 return vcl::ConvertValue(nValue
, 0, GetDecimalDigits(), GetUnit(), GetUnit());
1402 const SvtSysLocale aSysLocale
;
1403 const LocaleDataWrapper
& rLocaleData
= aSysLocale
.GetLocaleData();
1404 double fResult(0.0);
1405 (void)vcl::TextToValue(aStr
, fResult
, 0, GetDecimalDigits(), rLocaleData
, GetUnit());
1406 if (!aStr
.isEmpty())
1410 else if (fResult
> nMax
)
1416 SvxBorderLineStyle
SvtLineListBox::GetSelectEntryStyle() const
1418 if (m_xLineSet
->IsNoSelection())
1419 return SvxBorderLineStyle::NONE
;
1420 auto nId
= m_xLineSet
->GetSelectedItemId();
1421 return static_cast<SvxBorderLineStyle
>(nId
- 1);
1426 Size
getPreviewSize(const weld::Widget
& rControl
)
1428 return Size(rControl
.get_approximate_digit_width() * 15, rControl
.get_text_height());
1432 void SvtLineListBox::ImpGetLine( tools::Long nLine1
, tools::Long nLine2
, tools::Long nDistance
,
1433 Color aColor1
, Color aColor2
, Color aColorDist
,
1434 SvxBorderLineStyle nStyle
, BitmapEx
& rBmp
)
1436 Size
aSize(getPreviewSize(*m_xControl
));
1438 // SourceUnit to Twips
1439 if ( eSourceUnit
== FieldUnit::POINT
)
1447 aSize
= aVirDev
->PixelToLogic( aSize
);
1448 tools::Long nPix
= aVirDev
->PixelToLogic( Size( 0, 1 ) ).Height();
1449 sal_uInt32 n1
= nLine1
;
1450 sal_uInt32 n2
= nLine2
;
1451 tools::Long nDist
= nDistance
;
1457 nDist
-= nDist
%nPix
;
1461 tools::Long nVirHeight
= n1
+nDist
+n2
;
1462 if ( nVirHeight
> aSize
.Height() )
1463 aSize
.setHeight( nVirHeight
);
1464 // negative width should not be drawn
1465 if ( aSize
.Width() <= 0 )
1468 Size aVirSize
= aVirDev
->LogicToPixel( aSize
);
1469 if ( aVirDev
->GetOutputSizePixel() != aVirSize
)
1470 aVirDev
->SetOutputSizePixel( aVirSize
);
1471 aVirDev
->SetFillColor( aColorDist
);
1472 aVirDev
->DrawRect( tools::Rectangle( Point(), aSize
) );
1474 aVirDev
->SetFillColor( aColor1
);
1476 double y1
= double( n1
) / 2;
1477 svtools::DrawLine( *aVirDev
, basegfx::B2DPoint( 0, y1
), basegfx::B2DPoint( aSize
.Width( ), y1
), n1
, nStyle
);
1481 double y2
= n1
+ nDist
+ double( n2
) / 2;
1482 aVirDev
->SetFillColor( aColor2
);
1483 svtools::DrawLine( *aVirDev
, basegfx::B2DPoint( 0, y2
), basegfx::B2DPoint( aSize
.Width(), y2
), n2
, SvxBorderLineStyle::SOLID
);
1485 rBmp
= aVirDev
->GetBitmapEx( Point(), Size( aSize
.Width(), n1
+nDist
+n2
) );
1488 SvtLineListBox::SvtLineListBox(std::unique_ptr
<weld::MenuButton
> pControl
)
1489 : WeldToolbarPopup(css::uno::Reference
<css::frame::XFrame
>(), pControl
.get(), u
"svt/ui/linewindow.ui"_ustr
, u
"line_popup_window"_ustr
)
1490 , m_xControl(std::move(pControl
))
1491 , m_xNoneButton(m_xBuilder
->weld_button(u
"none_line_button"_ustr
))
1492 , m_xLineSet(new ValueSet(nullptr))
1493 , m_xLineSetWin(new weld::CustomWeld(*m_xBuilder
, u
"lineset"_ustr
, *m_xLineSet
))
1495 , aVirDev(VclPtr
<VirtualDevice
>::Create())
1498 const StyleSettings
& rStyleSettings
= Application::GetSettings().GetStyleSettings();
1499 m_xLineSet
->SetStyle(WinBits(WB_FLATVALUESET
| WB_NO_DIRECTSELECT
| WB_TABSTOP
));
1500 m_xLineSet
->SetItemHeight(rStyleSettings
.GetListBoxPreviewDefaultPixelSize().Height() + 1);
1501 m_xLineSet
->SetColCount(1);
1502 m_xLineSet
->SetSelectHdl(LINK(this, SvtLineListBox
, ValueSelectHdl
));
1504 m_xNoneButton
->connect_clicked(LINK(this, SvtLineListBox
, NoneHdl
));
1506 m_xControl
->set_popover(m_xTopLevel
.get());
1507 m_xControl
->connect_toggled(LINK(this, SvtLineListBox
, ToggleHdl
));
1508 m_xControl
->connect_style_updated(LINK(this, SvtLineListBox
, StyleUpdatedHdl
));
1510 // lock size to these maxes height/width so it doesn't jump around in size
1511 m_xControl
->set_label(GetLineStyleName(SvxBorderLineStyle::NONE
));
1512 Size aNonePrefSize
= m_xControl
->get_preferred_size();
1513 m_xControl
->set_label(u
""_ustr
);
1514 aVirDev
->SetOutputSizePixel(getPreviewSize(*m_xControl
));
1515 m_xControl
->set_image(aVirDev
);
1516 Size aSolidPrefSize
= m_xControl
->get_preferred_size();
1517 m_xControl
->set_size_request(std::max(aNonePrefSize
.Width(), aSolidPrefSize
.Width()),
1518 std::max(aNonePrefSize
.Height(), aSolidPrefSize
.Height()));
1520 eSourceUnit
= FieldUnit::POINT
;
1522 aVirDev
->SetLineColor();
1523 aVirDev
->SetMapMode(MapMode(MapUnit::MapTwip
));
1526 void SvtLineListBox::GrabFocus()
1528 if (GetSelectEntryStyle() == SvxBorderLineStyle::NONE
)
1529 m_xNoneButton
->grab_focus();
1531 m_xLineSet
->GrabFocus();
1534 IMPL_LINK(SvtLineListBox
, ToggleHdl
, weld::Toggleable
&, rButton
, void)
1536 if (rButton
.get_active())
1540 IMPL_LINK_NOARG(SvtLineListBox
, StyleUpdatedHdl
, weld::Widget
&, void)
1546 IMPL_LINK_NOARG(SvtLineListBox
, NoneHdl
, weld::Button
&, void)
1548 SelectEntry(SvxBorderLineStyle::NONE
);
1549 ValueSelectHdl(nullptr);
1552 SvtLineListBox::~SvtLineListBox()
1556 OUString
SvtLineListBox::GetLineStyleName(SvxBorderLineStyle eStyle
)
1559 for (sal_uInt32 i
= 0; i
< SAL_N_ELEMENTS(RID_SVXSTR_BORDERLINE
); ++i
)
1561 if (eStyle
== RID_SVXSTR_BORDERLINE
[i
].second
)
1563 sRet
= SvtResId(RID_SVXSTR_BORDERLINE
[i
].first
);
1570 void SvtLineListBox::SelectEntry(SvxBorderLineStyle nStyle
)
1572 if (nStyle
== SvxBorderLineStyle::NONE
)
1573 m_xLineSet
->SetNoSelection();
1575 m_xLineSet
->SelectItem(static_cast<sal_Int16
>(nStyle
) + 1);
1579 void SvtLineListBox::InsertEntry(
1580 const BorderWidthImpl
& rWidthImpl
, SvxBorderLineStyle nStyle
, tools::Long nMinWidth
,
1581 ColorFunc pColor1Fn
, ColorFunc pColor2Fn
, ColorDistFunc pColorDistFn
)
1583 m_vLineList
.emplace_back(new ImpLineListData(
1584 rWidthImpl
, nStyle
, nMinWidth
, pColor1Fn
, pColor2Fn
, pColorDistFn
));
1587 void SvtLineListBox::UpdateEntries()
1589 SvxBorderLineStyle eSelected
= GetSelectEntryStyle();
1591 // Remove the old entries
1592 m_xLineSet
->Clear();
1594 const StyleSettings
& rSettings
= Application::GetSettings().GetStyleSettings();
1595 Color aFieldColor
= rSettings
.GetFieldColor();
1597 // Add the new entries based on the defined width
1599 sal_uInt16 nCount
= m_vLineList
.size( );
1600 while ( n
< nCount
)
1602 auto& pData
= m_vLineList
[ n
];
1604 ImpGetLine( pData
->GetLine1ForWidth( m_nWidth
),
1605 pData
->GetLine2ForWidth( m_nWidth
),
1606 pData
->GetDistForWidth( m_nWidth
),
1607 pData
->GetColorLine1(aColor
),
1608 pData
->GetColorLine2(aColor
),
1609 pData
->GetColorDist(aColor
, aFieldColor
),
1610 pData
->GetStyle(), aBmp
);
1611 sal_Int16 nItemId
= static_cast<sal_Int16
>(pData
->GetStyle()) + 1;
1612 m_xLineSet
->InsertItem(nItemId
, Image(aBmp
), GetLineStyleName(pData
->GetStyle()));
1613 if (pData
->GetStyle() == eSelected
)
1614 m_xLineSet
->SelectItem(nItemId
);
1618 m_xLineSet
->SetOptimalSize();
1621 IMPL_LINK_NOARG(SvtLineListBox
, ValueSelectHdl
, ValueSet
*, void)
1623 maSelectHdl
.Call(*this);
1625 if (m_xControl
->get_active())
1626 m_xControl
->set_active(false);
1629 void SvtLineListBox::UpdatePreview()
1631 SvxBorderLineStyle eStyle
= GetSelectEntryStyle();
1632 for (sal_uInt32 i
= 0; i
< SAL_N_ELEMENTS(RID_SVXSTR_BORDERLINE
); ++i
)
1634 if (eStyle
== RID_SVXSTR_BORDERLINE
[i
].second
)
1636 m_xControl
->set_label(SvtResId(RID_SVXSTR_BORDERLINE
[i
].first
));
1641 if (eStyle
== SvxBorderLineStyle::NONE
)
1643 m_xControl
->set_image(nullptr);
1644 m_xControl
->set_label(GetLineStyleName(SvxBorderLineStyle::NONE
));
1648 Image
aImage(m_xLineSet
->GetItemImage(m_xLineSet
->GetSelectedItemId()));
1649 m_xControl
->set_label(u
""_ustr
);
1650 const auto nPos
= (aVirDev
->GetOutputSizePixel().Height() - aImage
.GetSizePixel().Height()) / 2;
1651 aVirDev
->Push(vcl::PushFlags::MAPMODE
);
1652 aVirDev
->SetMapMode(MapMode(MapUnit::MapPixel
));
1653 const StyleSettings
& rSettings
= Application::GetSettings().GetStyleSettings();
1654 aVirDev
->SetBackground(rSettings
.GetFieldColor());
1656 aVirDev
->DrawImage(Point(0, nPos
), aImage
);
1657 m_xControl
->set_image(aVirDev
.get());
1662 SvtCalendarBox::SvtCalendarBox(std::unique_ptr
<weld::MenuButton
> pControl
, bool bUseLabel
)
1663 : m_bUseLabel(bUseLabel
)
1664 , m_xControl(std::move(pControl
))
1665 , m_xBuilder(Application::CreateBuilder(m_xControl
.get(), u
"svt/ui/datewindow.ui"_ustr
))
1666 , m_xTopLevel(m_xBuilder
->weld_popover(u
"date_popup_window"_ustr
))
1667 , m_xCalendar(m_xBuilder
->weld_calendar(u
"date_picker"_ustr
))
1669 m_xControl
->set_popover(m_xTopLevel
.get());
1670 m_xCalendar
->connect_selected(LINK(this, SvtCalendarBox
, SelectHdl
));
1671 m_xCalendar
->connect_activated(LINK(this, SvtCalendarBox
, ActivateHdl
));
1674 void SvtCalendarBox::set_date(const Date
& rDate
)
1676 m_xCalendar
->set_date(rDate
);
1677 set_label_from_date();
1680 void SvtCalendarBox::set_label_from_date()
1684 const LocaleDataWrapper
& rLocaleData
= Application::GetSettings().GetLocaleDataWrapper();
1685 m_xControl
->set_label(rLocaleData
.getDate(m_xCalendar
->get_date()));
1688 IMPL_LINK_NOARG(SvtCalendarBox
, SelectHdl
, weld::Calendar
&, void)
1690 set_label_from_date();
1691 m_aSelectHdl
.Call(*this);
1694 IMPL_LINK_NOARG(SvtCalendarBox
, ActivateHdl
, weld::Calendar
&, void)
1696 if (m_xControl
->get_active())
1697 m_xControl
->set_active(false);
1698 m_aActivatedHdl
.Call(*this);
1701 SvtCalendarBox::~SvtCalendarBox()
1705 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */