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 <officecfg/Office/Common.hxx>
26 #include <tools/stream.hxx>
27 #include <vcl/customweld.hxx>
28 #include <vcl/event.hxx>
29 #include <vcl/svapp.hxx>
30 #include <vcl/fieldvalues.hxx>
31 #include <vcl/settings.hxx>
32 #include <vcl/image.hxx>
33 #include <vcl/virdev.hxx>
34 #include <vcl/weldutils.hxx>
35 #include <rtl/math.hxx>
36 #include <sal/macros.h>
37 #include <sal/log.hxx>
38 #include <comphelper/string.hxx>
39 #include <unotools/localedatawrapper.hxx>
40 #include <unotools/syslocale.hxx>
42 #include <svtools/borderline.hxx>
43 #include <svtools/sampletext.hxx>
44 #include <svtools/svtresid.hxx>
45 #include <svtools/strings.hrc>
46 #include <svtools/ctrlbox.hxx>
47 #include <svtools/ctrltool.hxx>
48 #include <svtools/borderhelper.hxx>
49 #include <svtools/valueset.hxx>
51 #include <basegfx/polygon/b2dpolygon.hxx>
52 #include <basegfx/polygon/b2dpolygontools.hxx>
53 #include <editeng/borderline.hxx>
55 #include <rtl/bootstrap.hxx>
57 #include <borderline.hrc>
61 #define IMGOUTERTEXTSPACE 5
62 #define EXTRAFONTSIZE 5
63 #define GAPTOEXTRAPREVIEW 10
66 constexpr OUStringLiteral FONTNAMEBOXMRUENTRIESFILE
= u
"/user/config/fontnameboxmruentries";
69 BorderWidthImpl::BorderWidthImpl( BorderWidthImplFlags nFlags
, double nRate1
, double nRate2
, double nRateGap
):
73 m_nRateGap( nRateGap
)
77 bool BorderWidthImpl::operator== ( const BorderWidthImpl
& r
) const
79 return ( m_nFlags
== r
.m_nFlags
) &&
80 ( m_nRate1
== r
.m_nRate1
) &&
81 ( m_nRate2
== r
.m_nRate2
) &&
82 ( m_nRateGap
== r
.m_nRateGap
);
85 tools::Long
BorderWidthImpl::GetLine1( tools::Long nWidth
) const
87 tools::Long result
= static_cast<tools::Long
>(m_nRate1
);
88 if ( m_nFlags
& BorderWidthImplFlags::CHANGE_LINE1
)
90 tools::Long
const nConstant2
= (m_nFlags
& BorderWidthImplFlags::CHANGE_LINE2
) ? 0 : m_nRate2
;
91 tools::Long
const nConstantD
= (m_nFlags
& BorderWidthImplFlags::CHANGE_DIST
) ? 0 : m_nRateGap
;
92 result
= std::max
<tools::Long
>(0,
93 static_cast<tools::Long
>((m_nRate1
* nWidth
) + 0.5)
94 - (nConstant2
+ nConstantD
));
95 if (result
== 0 && m_nRate1
> 0.0 && nWidth
> 0)
96 { // fdo#51777: hack to essentially treat 1 twip DOUBLE border
97 result
= 1; // as 1 twip SINGLE border
103 tools::Long
BorderWidthImpl::GetLine2( tools::Long nWidth
) const
105 tools::Long result
= static_cast<tools::Long
>(m_nRate2
);
106 if ( m_nFlags
& BorderWidthImplFlags::CHANGE_LINE2
)
108 tools::Long
const nConstant1
= (m_nFlags
& BorderWidthImplFlags::CHANGE_LINE1
) ? 0 : m_nRate1
;
109 tools::Long
const nConstantD
= (m_nFlags
& BorderWidthImplFlags::CHANGE_DIST
) ? 0 : m_nRateGap
;
110 result
= std::max
<tools::Long
>(0,
111 static_cast<tools::Long
>((m_nRate2
* nWidth
) + 0.5)
112 - (nConstant1
+ nConstantD
));
117 tools::Long
BorderWidthImpl::GetGap( tools::Long nWidth
) const
119 tools::Long result
= static_cast<tools::Long
>(m_nRateGap
);
120 if ( m_nFlags
& BorderWidthImplFlags::CHANGE_DIST
)
122 tools::Long
const nConstant1
= (m_nFlags
& BorderWidthImplFlags::CHANGE_LINE1
) ? 0 : m_nRate1
;
123 tools::Long
const nConstant2
= (m_nFlags
& BorderWidthImplFlags::CHANGE_LINE2
) ? 0 : m_nRate2
;
124 result
= std::max
<tools::Long
>(0,
125 static_cast<tools::Long
>((m_nRateGap
* nWidth
) + 0.5)
126 - (nConstant1
+ nConstant2
));
129 // Avoid having too small distances (less than 0.1pt)
130 if ( result
< MINGAPWIDTH
&& m_nRate1
> 0 && m_nRate2
> 0 )
131 result
= MINGAPWIDTH
;
136 static double lcl_getGuessedWidth( tools::Long nTested
, double nRate
, bool bChanging
)
138 double nWidth
= -1.0;
140 nWidth
= double( nTested
) / nRate
;
143 if ( rtl::math::approxEqual(double( nTested
), nRate
) )
150 tools::Long
BorderWidthImpl::GuessWidth( tools::Long nLine1
, tools::Long nLine2
, tools::Long nGap
)
152 std::vector
< double > aToCompare
;
153 bool bInvalid
= false;
155 bool bLine1Change
= bool( m_nFlags
& BorderWidthImplFlags::CHANGE_LINE1
);
156 double nWidth1
= lcl_getGuessedWidth( nLine1
, m_nRate1
, bLine1Change
);
158 aToCompare
.push_back( nWidth1
);
159 else if (nWidth1
< 0)
162 bool bLine2Change
= bool( m_nFlags
& BorderWidthImplFlags::CHANGE_LINE2
);
163 double nWidth2
= lcl_getGuessedWidth( nLine2
, m_nRate2
, bLine2Change
);
165 aToCompare
.push_back( nWidth2
);
166 else if (nWidth2
< 0)
169 bool bGapChange
= bool( m_nFlags
& BorderWidthImplFlags::CHANGE_DIST
);
170 double nWidthGap
= lcl_getGuessedWidth( nGap
, m_nRateGap
, bGapChange
);
171 if ( bGapChange
&& nGap
>= MINGAPWIDTH
)
172 aToCompare
.push_back( nWidthGap
);
173 else if ( !bGapChange
&& nWidthGap
< 0 )
176 // non-constant line width factors must sum to 1
177 assert((((bLine1Change
) ? m_nRate1
: 0) +
178 ((bLine2Change
) ? m_nRate2
: 0) +
179 ((bGapChange
) ? m_nRateGap
: 0)) - 1.0 < 0.00001 );
182 if ( (!bInvalid
) && (!aToCompare
.empty()) )
184 nWidth
= *aToCompare
.begin();
185 for (auto const& elem
: aToCompare
)
187 bInvalid
= ( nWidth
!= elem
);
191 nWidth
= bInvalid
? 0.0 : nLine1
+ nLine2
+ nGap
;
197 static void lclDrawPolygon( OutputDevice
& rDev
, const basegfx::B2DPolygon
& rPolygon
, tools::Long nWidth
, SvxBorderLineStyle nDashing
)
199 AntialiasingFlags nOldAA
= rDev
.GetAntialiasing();
200 rDev
.SetAntialiasing( nOldAA
& ~AntialiasingFlags::Enable
);
202 tools::Long nPix
= rDev
.PixelToLogic(Size(1, 1)).Width();
203 basegfx::B2DPolyPolygon aPolygons
= svtools::ApplyLineDashing(rPolygon
, nDashing
, nPix
);
205 // Handle problems of width 1px in Pixel mode: 0.5px gives a 1px line
206 if (rDev
.GetMapMode().GetMapUnit() == MapUnit::MapPixel
&& nWidth
== nPix
)
209 for ( sal_uInt32 i
= 0; i
< aPolygons
.count( ); i
++ )
211 const basegfx::B2DPolygon
& aDash
= aPolygons
.getB2DPolygon( i
);
212 basegfx::B2DPoint aStart
= aDash
.getB2DPoint( 0 );
213 basegfx::B2DPoint aEnd
= aDash
.getB2DPoint( aDash
.count() - 1 );
215 basegfx::B2DVector
aVector( aEnd
- aStart
);
216 aVector
.normalize( );
217 const basegfx::B2DVector
aPerpendicular(basegfx::getPerpendicular(aVector
));
219 const basegfx::B2DVector
aWidthOffset( double( nWidth
) / 2 * aPerpendicular
);
220 basegfx::B2DPolygon aDashPolygon
;
221 aDashPolygon
.append( aStart
+ aWidthOffset
);
222 aDashPolygon
.append( aEnd
+ aWidthOffset
);
223 aDashPolygon
.append( aEnd
- aWidthOffset
);
224 aDashPolygon
.append( aStart
- aWidthOffset
);
225 aDashPolygon
.setClosed( true );
227 rDev
.DrawPolygon( aDashPolygon
);
230 rDev
.SetAntialiasing( nOldAA
);
236 * Dashing array must start with a line width and end with a blank width.
238 static std::vector
<double> GetDashing( SvxBorderLineStyle nDashing
)
240 std::vector
<double> aPattern
;
243 case SvxBorderLineStyle::DOTTED
:
244 aPattern
.push_back( 1.0 ); // line
245 aPattern
.push_back( 2.0 ); // blank
247 case SvxBorderLineStyle::DASHED
:
248 aPattern
.push_back( 16.0 ); // line
249 aPattern
.push_back( 5.0 ); // blank
251 case SvxBorderLineStyle::FINE_DASHED
:
252 aPattern
.push_back( 6.0 ); // line
253 aPattern
.push_back( 2.0 ); // blank
255 case SvxBorderLineStyle::DASH_DOT
:
256 aPattern
.push_back( 16.0 ); // line
257 aPattern
.push_back( 5.0 ); // blank
258 aPattern
.push_back( 5.0 ); // line
259 aPattern
.push_back( 5.0 ); // blank
261 case SvxBorderLineStyle::DASH_DOT_DOT
:
262 aPattern
.push_back( 16.0 ); // line
263 aPattern
.push_back( 5.0 ); // blank
264 aPattern
.push_back( 5.0 ); // line
265 aPattern
.push_back( 5.0 ); // blank
266 aPattern
.push_back( 5.0 ); // line
267 aPattern
.push_back( 5.0 ); // blank
282 explicit ApplyScale( double fScale
) : mfScale(fScale
) {}
283 void operator() ( double& rVal
)
291 std::vector
<double> GetLineDashing( SvxBorderLineStyle nDashing
, double fScale
)
293 std::vector
<double> aPattern
= GetDashing(nDashing
);
294 std::for_each(aPattern
.begin(), aPattern
.end(), ApplyScale(fScale
));
298 basegfx::B2DPolyPolygon
ApplyLineDashing( const basegfx::B2DPolygon
& rPolygon
, SvxBorderLineStyle nDashing
, double fScale
)
300 std::vector
<double> aPattern
= GetDashing(nDashing
);
301 std::for_each(aPattern
.begin(), aPattern
.end(), ApplyScale(fScale
));
303 basegfx::B2DPolyPolygon aPolygons
;
305 if (aPattern
.empty())
306 aPolygons
.append(rPolygon
);
308 basegfx::utils::applyLineDashing(rPolygon
, aPattern
, &aPolygons
);
313 void DrawLine( OutputDevice
& rDev
, const Point
& rP1
, const Point
& rP2
,
314 sal_uInt32 nWidth
, SvxBorderLineStyle nDashing
)
316 DrawLine( rDev
, basegfx::B2DPoint( rP1
.X(), rP1
.Y() ),
317 basegfx::B2DPoint( rP2
.X(), rP2
.Y( ) ), nWidth
, nDashing
);
320 void DrawLine( OutputDevice
& rDev
, const basegfx::B2DPoint
& rP1
, const basegfx::B2DPoint
& rP2
,
321 sal_uInt32 nWidth
, SvxBorderLineStyle nDashing
)
323 basegfx::B2DPolygon aPolygon
;
324 aPolygon
.append( rP1
);
325 aPolygon
.append( rP2
);
326 lclDrawPolygon( rDev
, aPolygon
, nWidth
, nDashing
);
331 static Size gUserItemSz
;
332 static int gFontNameBoxes
;
333 static size_t gPreviewsPerDevice
;
334 static std::vector
<VclPtr
<VirtualDevice
>> gFontPreviewVirDevs
;
335 static std::vector
<OUString
> gRenderedFontNames
;
339 void calcCustomItemSize(const weld::ComboBox
& rComboBox
)
341 gUserItemSz
= Size(rComboBox
.get_approximate_digit_width() * 52, rComboBox
.get_text_height());
342 gUserItemSz
.setHeight(gUserItemSz
.Height() * 16);
343 gUserItemSz
.setHeight(gUserItemSz
.Height() / 10);
345 size_t nMaxDeviceHeight
= SAL_MAX_INT16
/ 16; // see limitXCreatePixmap and be generous wrt up to x16 hidpi
346 assert(gUserItemSz
.Height() != 0);
347 gPreviewsPerDevice
= gUserItemSz
.Height() == 0 ? 16 : nMaxDeviceHeight
/ gUserItemSz
.Height();
351 IMPL_LINK(FontNameBox
, SettingsChangedHdl
, VclSimpleEvent
&, rEvent
, void)
353 if (rEvent
.GetId() != VclEventId::ApplicationDataChanged
)
356 DataChangedEvent
* pData
= static_cast<DataChangedEvent
*>(static_cast<VclWindowEvent
&>(rEvent
).GetData());
357 if (pData
->GetType() == DataChangedEventType::SETTINGS
)
359 for (auto &rDev
: gFontPreviewVirDevs
)
360 rDev
.disposeAndClear();
361 gFontPreviewVirDevs
.clear();
362 gRenderedFontNames
.clear();
363 calcCustomItemSize(*m_xComboBox
);
364 if (mbWYSIWYG
&& mpFontList
&& !mpFontList
->empty())
366 mnPreviewProgress
= 0;
367 maUpdateIdle
.Start();
372 FontNameBox::FontNameBox(std::unique_ptr
<weld::ComboBox
> p
)
373 : m_xComboBox(std::move(p
))
374 , mnPreviewProgress(0)
376 , maUpdateIdle("FontNameBox Preview Update")
379 InitFontMRUEntriesFile();
381 maUpdateIdle
.SetPriority(TaskPriority::LOWEST
);
382 maUpdateIdle
.SetInvokeHandler(LINK(this, FontNameBox
, UpdateHdl
));
384 Application::AddEventListener(LINK(this, FontNameBox
, SettingsChangedHdl
));
387 FontNameBox::~FontNameBox()
389 Application::RemoveEventListener(LINK(this, FontNameBox
, SettingsChangedHdl
));
393 SaveMRUEntries (maFontMRUEntriesFile
);
394 ImplDestroyFontList();
399 for (auto &rDev
: gFontPreviewVirDevs
)
400 rDev
.disposeAndClear();
401 gFontPreviewVirDevs
.clear();
402 gRenderedFontNames
.clear();
406 void FontNameBox::SaveMRUEntries(const OUString
& aFontMRUEntriesFile
) const
408 OString
aEntries(OUStringToOString(m_xComboBox
->get_mru_entries(),
409 RTL_TEXTENCODING_UTF8
));
411 if (aEntries
.isEmpty() || aFontMRUEntriesFile
.isEmpty())
414 SvFileStream aStream
;
415 aStream
.Open( aFontMRUEntriesFile
, StreamMode::WRITE
| StreamMode::TRUNC
);
416 if( ! (aStream
.IsOpen() && aStream
.IsWritable()) )
418 SAL_INFO("svtools.control", "FontNameBox::SaveMRUEntries: opening mru entries file " << aFontMRUEntriesFile
<< " failed");
422 aStream
.SetLineDelimiter( LINEEND_LF
);
423 aStream
.WriteLine( aEntries
);
424 aStream
.WriteLine( "" );
427 void FontNameBox::LoadMRUEntries( const OUString
& aFontMRUEntriesFile
)
429 if (aFontMRUEntriesFile
.isEmpty())
432 if (!officecfg::Office::Common::Font::View::ShowFontBoxWYSIWYG::get())
435 SvFileStream
aStream( aFontMRUEntriesFile
, StreamMode::READ
);
436 if( ! aStream
.IsOpen() )
438 SAL_INFO("svtools.control", "FontNameBox::LoadMRUEntries: opening mru entries file " << aFontMRUEntriesFile
<< " failed");
443 aStream
.ReadLine( aLine
);
444 OUString aEntries
= OStringToOUString(aLine
,
445 RTL_TEXTENCODING_UTF8
);
446 m_xComboBox
->set_mru_entries(aEntries
);
449 void FontNameBox::InitFontMRUEntriesFile()
451 OUString
sUserConfigDir("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER
"/" SAL_CONFIGFILE( "bootstrap") "::UserInstallation}");
452 rtl::Bootstrap::expandMacros(sUserConfigDir
);
454 maFontMRUEntriesFile
= sUserConfigDir
;
455 if( !maFontMRUEntriesFile
.isEmpty() )
457 maFontMRUEntriesFile
+= FONTNAMEBOXMRUENTRIESFILE
;
461 void FontNameBox::ImplDestroyFontList()
464 mnPreviewProgress
= 0;
468 void FontNameBox::Fill( const FontList
* pList
)
470 // store old text and clear box
471 OUString aOldText
= m_xComboBox
->get_active_text();
472 OUString rEntries
= m_xComboBox
->get_mru_entries();
473 bool bLoadFromFile
= rEntries
.isEmpty();
474 m_xComboBox
->freeze();
475 m_xComboBox
->clear();
477 ImplDestroyFontList();
478 mpFontList
.reset(new ImplFontList
);
481 size_t nFontCount
= pList
->GetFontNameCount();
482 for (size_t i
= 0; i
< nFontCount
; ++i
)
484 const FontMetric
& rFontMetric
= pList
->GetFontName(i
);
485 m_xComboBox
->append(OUString::number(i
), rFontMetric
.GetFamilyName());
486 mpFontList
->push_back(rFontMetric
);
490 LoadMRUEntries(maFontMRUEntriesFile
);
492 m_xComboBox
->set_mru_entries(rEntries
);
496 if (mbWYSIWYG
&& nFontCount
)
498 assert(mnPreviewProgress
== 0 && "ImplDestroyFontList wasn't called");
499 maUpdateIdle
.Start();
503 if (!aOldText
.isEmpty())
504 set_active_or_entry_text(aOldText
);
507 static bool IsRunningUnitTest() { return getenv("LO_TESTNAME") != nullptr; }
509 void FontNameBox::EnableWYSIWYG(bool bEnable
)
511 if (comphelper::LibreOfficeKit::isActive() || IsRunningUnitTest())
513 if (mbWYSIWYG
== bEnable
)
519 calcCustomItemSize(*m_xComboBox
);
520 m_xComboBox
->connect_custom_get_size(LINK(this, FontNameBox
, CustomGetSizeHdl
));
521 m_xComboBox
->connect_custom_render(LINK(this, FontNameBox
, CustomRenderHdl
));
525 m_xComboBox
->connect_custom_get_size(Link
<OutputDevice
&, Size
>());
526 m_xComboBox
->connect_custom_render(Link
<weld::ComboBox::render_args
, void>());
528 m_xComboBox
->set_custom_renderer(mbWYSIWYG
);
531 IMPL_LINK_NOARG(FontNameBox
, CustomGetSizeHdl
, OutputDevice
&, Size
)
533 return mbWYSIWYG
? gUserItemSz
: Size();
538 tools::Long
shrinkFontToFit(OUString
const &rSampleText
, tools::Long nH
, vcl::Font
&rFont
, OutputDevice
&rDevice
, tools::Rectangle
&rTextRect
)
540 tools::Long nWidth
= 0;
542 Size
aSize( rFont
.GetFontSize() );
544 //Make sure it fits in the available height
545 while (aSize
.Height() > 0)
547 if (!rDevice
.GetTextBoundRect(rTextRect
, rSampleText
))
549 if (rTextRect
.GetHeight() <= nH
)
551 nWidth
= rTextRect
.GetWidth();
555 aSize
.AdjustHeight( -(EXTRAFONTSIZE
) );
556 rFont
.SetFontSize(aSize
);
557 rDevice
.SetFont(rFont
);
564 IMPL_LINK_NOARG(FontNameBox
, UpdateHdl
, Timer
*, void)
566 CachePreview(mnPreviewProgress
++, nullptr);
567 // tdf#132536 limit to ~25 pre-rendered for now. The font caches look
568 // b0rked, the massive charmaps are ~never swapped out, and don't count
569 // towards the size of a font in the font cache.
570 if (mnPreviewProgress
< std::min
<size_t>(25, mpFontList
->size()))
571 maUpdateIdle
.Start();
574 static void DrawPreview(const FontMetric
& rFontMetric
, const Point
& rTopLeft
, OutputDevice
& rDevice
, bool bSelected
)
576 rDevice
.Push(vcl::PushFlags::TEXTCOLOR
);
578 const StyleSettings
& rStyleSettings
= Application::GetSettings().GetStyleSettings();
580 rDevice
.SetTextColor(rStyleSettings
.GetHighlightTextColor());
582 rDevice
.SetTextColor(rStyleSettings
.GetDialogTextColor());
584 tools::Long nX
= rTopLeft
.X();
585 tools::Long nH
= gUserItemSz
.Height();
587 nX
+= IMGOUTERTEXTSPACE
;
589 const bool bSymbolFont
= isSymbolFont(rFontMetric
);
591 vcl::Font
aOldFont(rDevice
.GetFont());
592 Size
aSize( aOldFont
.GetFontSize() );
593 aSize
.AdjustHeight(EXTRAFONTSIZE
);
594 vcl::Font
aFont( rFontMetric
);
595 aFont
.SetFontSize( aSize
);
596 rDevice
.SetFont(aFont
);
598 bool bUsingCorrectFont
= true;
599 tools::Rectangle aTextRect
;
601 // Preview the font name
602 const OUString
& sFontName
= rFontMetric
.GetFamilyName();
604 //If it shouldn't or can't draw its own name because it doesn't have the glyphs
605 if (!canRenderNameOfSelectedFont(rDevice
))
606 bUsingCorrectFont
= false;
609 //Make sure it fits in the available height, shrinking the font if necessary
610 bUsingCorrectFont
= shrinkFontToFit(sFontName
, nH
, aFont
, rDevice
, aTextRect
) != 0;
613 if (!bUsingCorrectFont
)
615 rDevice
.SetFont(aOldFont
);
616 rDevice
.GetTextBoundRect(aTextRect
, sFontName
);
619 tools::Long nTextHeight
= aTextRect
.GetHeight();
620 tools::Long nDesiredGap
= (nH
-nTextHeight
)/2;
621 tools::Long nVertAdjust
= nDesiredGap
- aTextRect
.Top();
622 Point
aPos( nX
, rTopLeft
.Y() + nVertAdjust
);
623 rDevice
.DrawText(aPos
, sFontName
);
624 tools::Long nTextX
= aPos
.X() + aTextRect
.GetWidth() + GAPTOEXTRAPREVIEW
;
626 if (!bUsingCorrectFont
)
627 rDevice
.SetFont(aFont
);
629 OUString sSampleText
;
633 const bool bNameBeginsWithLatinText
= rFontMetric
.GetFamilyName()[0] <= 'z';
635 if (bNameBeginsWithLatinText
|| !bUsingCorrectFont
)
636 sSampleText
= makeShortRepresentativeTextForSelectedFont(rDevice
);
639 //If we're not a symbol font, but could neither render our own name and
640 //we can't determine what script it would like to render, then try a
641 //few well known scripts
642 if (sSampleText
.isEmpty() && !bUsingCorrectFont
)
644 static const UScriptCode aScripts
[] =
673 USCRIPT_SIMPLIFIED_HAN
,
674 USCRIPT_TRADITIONAL_HAN
,
679 for (const UScriptCode
& rScript
: aScripts
)
681 OUString sText
= makeShortRepresentativeTextForScript(rScript
);
682 if (!sText
.isEmpty())
684 bool bHasSampleTextGlyphs
= (-1 == rDevice
.HasGlyphs(aFont
, sText
));
685 if (bHasSampleTextGlyphs
)
693 static const UScriptCode aMinimalScripts
[] =
695 USCRIPT_HEBREW
, //e.g. biblical hebrew
699 for (const UScriptCode
& rMinimalScript
: aMinimalScripts
)
701 OUString sText
= makeShortMinimalTextForScript(rMinimalScript
);
702 if (!sText
.isEmpty())
704 bool bHasSampleTextGlyphs
= (-1 == rDevice
.HasGlyphs(aFont
, sText
));
705 if (bHasSampleTextGlyphs
)
714 //If we're a symbol font, or for some reason the font still couldn't
715 //render something representative of what it would like to render then
716 //make up some semi-random text that it *can* display
717 if (bSymbolFont
|| (!bUsingCorrectFont
&& sSampleText
.isEmpty()))
718 sSampleText
= makeShortRepresentativeSymbolTextForSelectedFont(rDevice
);
720 if (!sSampleText
.isEmpty())
722 const Size
&rItemSize
= gUserItemSz
;
724 //leave a little border at the edge
725 tools::Long nSpace
= rItemSize
.Width() - nTextX
- IMGOUTERTEXTSPACE
;
728 //Make sure it fits in the available height, and get how wide that would be
729 tools::Long nWidth
= shrinkFontToFit(sSampleText
, nH
, aFont
, rDevice
, aTextRect
);
730 //Chop letters off until it fits in the available width
731 while (nWidth
> nSpace
|| nWidth
> gUserItemSz
.Width())
733 sSampleText
= sSampleText
.copy(0, sSampleText
.getLength()-1);
734 nWidth
= rDevice
.GetTextBoundRect(aTextRect
, sSampleText
) ?
735 aTextRect
.GetWidth() : 0;
738 //center the text on the line
739 if (!sSampleText
.isEmpty() && nWidth
)
741 nTextHeight
= aTextRect
.GetHeight();
742 nDesiredGap
= (nH
-nTextHeight
)/2;
743 nVertAdjust
= nDesiredGap
- aTextRect
.Top();
744 aPos
= Point(nTextX
+ nSpace
- nWidth
, rTopLeft
.Y() + nVertAdjust
);
745 rDevice
.DrawText(aPos
, sSampleText
);
750 rDevice
.SetFont(aOldFont
);
754 OutputDevice
& FontNameBox::CachePreview(size_t nIndex
, Point
* pTopLeft
)
756 SolarMutexGuard aGuard
;
757 const FontMetric
& rFontMetric
= (*mpFontList
)[nIndex
];
758 const OUString
& rFontName
= rFontMetric
.GetFamilyName();
760 size_t nPreviewIndex
;
761 auto xFind
= std::find(gRenderedFontNames
.begin(), gRenderedFontNames
.end(), rFontName
);
762 bool bPreviewAvailable
= xFind
!= gRenderedFontNames
.end();
763 if (!bPreviewAvailable
)
765 nPreviewIndex
= gRenderedFontNames
.size();
766 gRenderedFontNames
.push_back(rFontName
);
769 nPreviewIndex
= std::distance(gRenderedFontNames
.begin(), xFind
);
771 size_t nPage
= nPreviewIndex
/ gPreviewsPerDevice
;
772 size_t nIndexInPage
= nPreviewIndex
- (nPage
* gPreviewsPerDevice
);
774 Point
aTopLeft(0, gUserItemSz
.Height() * nIndexInPage
);
776 if (!bPreviewAvailable
)
778 if (nPage
>= gFontPreviewVirDevs
.size())
780 gFontPreviewVirDevs
.emplace_back(m_xComboBox
->create_render_virtual_device());
781 VirtualDevice
& rDevice
= *gFontPreviewVirDevs
.back();
782 rDevice
.SetOutputSizePixel(Size(gUserItemSz
.Width(), gUserItemSz
.Height() * gPreviewsPerDevice
));
783 weld::SetPointFont(rDevice
, m_xComboBox
->get_font());
784 assert(gFontPreviewVirDevs
.size() == nPage
+ 1);
787 DrawPreview(rFontMetric
, aTopLeft
, *gFontPreviewVirDevs
.back(), false);
791 *pTopLeft
= aTopLeft
;
793 return *gFontPreviewVirDevs
[nPage
];
796 IMPL_LINK(FontNameBox
, CustomRenderHdl
, weld::ComboBox::render_args
, aPayload
, void)
798 vcl::RenderContext
& rRenderContext
= std::get
<0>(aPayload
);
799 const ::tools::Rectangle
& rRect
= std::get
<1>(aPayload
);
800 bool bSelected
= std::get
<2>(aPayload
);
801 const OUString
& rId
= std::get
<3>(aPayload
);
803 sal_uInt32 nIndex
= rId
.toUInt32();
805 Point
aDestPoint(rRect
.TopLeft());
806 auto nMargin
= (rRect
.GetHeight() - gUserItemSz
.Height()) / 2;
807 aDestPoint
.AdjustY(nMargin
);
811 const FontMetric
& rFontMetric
= (*mpFontList
)[nIndex
];
812 DrawPreview(rFontMetric
, aDestPoint
, rRenderContext
, true);
816 // use cache of unselected entries
818 OutputDevice
& rDevice
= CachePreview(nIndex
, &aTopLeft
);
820 rRenderContext
.DrawOutDev(aDestPoint
, gUserItemSz
,
821 aTopLeft
, gUserItemSz
,
826 void FontNameBox::set_active_or_entry_text(const OUString
& rText
)
828 const int nFound
= m_xComboBox
->find_text(rText
);
830 m_xComboBox
->set_active(nFound
);
831 m_xComboBox
->set_entry_text(rText
);
834 FontStyleBox::FontStyleBox(std::unique_ptr
<weld::ComboBox
> p
)
835 : m_xComboBox(std::move(p
))
837 //Use the standard texts to get an optimal size and stick to that size.
838 //That should stop the character dialog dancing around.
839 auto nMaxLen
= m_xComboBox
->get_pixel_size(SvtResId(STR_SVT_STYLE_LIGHT
)).Width();
840 nMaxLen
= std::max(nMaxLen
, m_xComboBox
->get_pixel_size(SvtResId(STR_SVT_STYLE_LIGHT_ITALIC
)).Width());
841 nMaxLen
= std::max(nMaxLen
, m_xComboBox
->get_pixel_size(SvtResId(STR_SVT_STYLE_NORMAL
)).Width());
842 nMaxLen
= std::max(nMaxLen
, m_xComboBox
->get_pixel_size(SvtResId(STR_SVT_STYLE_NORMAL_ITALIC
)).Width());
843 nMaxLen
= std::max(nMaxLen
, m_xComboBox
->get_pixel_size(SvtResId(STR_SVT_STYLE_BOLD
)).Width());
844 nMaxLen
= std::max(nMaxLen
, m_xComboBox
->get_pixel_size(SvtResId(STR_SVT_STYLE_BOLD_ITALIC
)).Width());
845 nMaxLen
= std::max(nMaxLen
, m_xComboBox
->get_pixel_size(SvtResId(STR_SVT_STYLE_BLACK
)).Width());
846 nMaxLen
= std::max(nMaxLen
, m_xComboBox
->get_pixel_size(SvtResId(STR_SVT_STYLE_BLACK_ITALIC
)).Width());
847 m_xComboBox
->set_entry_width_chars(std::ceil(nMaxLen
/ m_xComboBox
->get_approximate_digit_width()));
850 void FontStyleBox::Fill( std::u16string_view rName
, const FontList
* pList
)
852 OUString aOldText
= m_xComboBox
->get_active_text();
853 int nPos
= m_xComboBox
->get_active();
855 m_xComboBox
->freeze();
856 m_xComboBox
->clear();
858 // does a font with this name already exist?
859 sal_Handle hFontMetric
= pList
->GetFirstFontMetric( rName
);
863 FontWeight eLastWeight
= WEIGHT_DONTKNOW
;
864 FontItalic eLastItalic
= ITALIC_NONE
;
865 FontWidth eLastWidth
= WIDTH_DONTKNOW
;
866 bool bNormal
= false;
867 bool bItalic
= false;
869 bool bBoldItalic
= false;
870 bool bInsert
= false;
871 FontMetric aFontMetric
;
872 while ( hFontMetric
)
874 aFontMetric
= FontList::GetFontMetric( hFontMetric
);
876 FontWeight eWeight
= aFontMetric
.GetWeight();
877 FontItalic eItalic
= aFontMetric
.GetItalic();
878 FontWidth eWidth
= aFontMetric
.GetWidthType();
879 // Only if the attributes are different, we insert the
880 // Font to avoid double Entries in different languages
881 if ( (eWeight
!= eLastWeight
) || (eItalic
!= eLastItalic
) ||
882 (eWidth
!= eLastWidth
) )
885 m_xComboBox
->append_text(aStyleText
);
887 if ( eWeight
<= WEIGHT_NORMAL
)
889 if ( eItalic
!= ITALIC_NONE
)
896 if ( eItalic
!= ITALIC_NONE
)
902 // For wrong StyleNames we replace this with the correct once
903 aStyleText
= pList
->GetStyleName( aFontMetric
);
904 bInsert
= m_xComboBox
->find_text(aStyleText
) == -1;
907 aStyleText
= pList
->GetStyleName( eWeight
, eItalic
);
908 bInsert
= m_xComboBox
->find_text(aStyleText
) == -1;
911 eLastWeight
= eWeight
;
912 eLastItalic
= eItalic
;
919 // If we have two names for the same attributes
920 // we prefer the translated standard names
921 const OUString
& rAttrStyleText
= pList
->GetStyleName( eWeight
, eItalic
);
922 if (rAttrStyleText
!= aStyleText
)
924 OUString aTempStyleText
= pList
->GetStyleName( aFontMetric
);
925 if (rAttrStyleText
== aTempStyleText
)
926 aStyleText
= rAttrStyleText
;
927 bInsert
= m_xComboBox
->find_text(aStyleText
) == -1;
932 if ( !bItalic
&& (aStyleText
== pList
->GetItalicStr()) )
934 else if ( !bBold
&& (aStyleText
== pList
->GetBoldStr()) )
936 else if ( !bBoldItalic
&& (aStyleText
== pList
->GetBoldItalicStr()) )
939 hFontMetric
= FontList::GetNextFontMetric( hFontMetric
);
943 m_xComboBox
->append_text(aStyleText
);
945 // certain style as copy
949 m_xComboBox
->append_text(pList
->GetItalicStr());
951 m_xComboBox
->append_text(pList
->GetBoldStr());
955 if ( bNormal
|| bItalic
|| bBold
)
956 m_xComboBox
->append_text(pList
->GetBoldItalicStr());
961 // insert standard styles if no font
962 m_xComboBox
->append_text(pList
->GetNormalStr());
963 m_xComboBox
->append_text(pList
->GetItalicStr());
964 m_xComboBox
->append_text(pList
->GetBoldStr());
965 m_xComboBox
->append_text(pList
->GetBoldItalicStr());
970 if (!aOldText
.isEmpty())
972 int nFound
= m_xComboBox
->find_text(aOldText
);
974 m_xComboBox
->set_active(nFound
);
977 if (nPos
>= m_xComboBox
->get_count())
978 m_xComboBox
->set_active(0);
980 m_xComboBox
->set_active(nPos
);
985 FontSizeBox::FontSizeBox(std::unique_ptr
<weld::ComboBox
> p
)
990 , eUnit(FieldUnit::POINT
)
998 , bRelativeMode(false)
1000 , bPtRelative(false)
1002 , m_xComboBox(std::move(p
))
1004 m_xComboBox
->set_entry_width_chars(std::ceil(m_xComboBox
->get_pixel_size(format_number(105)).Width() /
1005 m_xComboBox
->get_approximate_digit_width()));
1006 m_xComboBox
->connect_focus_out(LINK(this, FontSizeBox
, ReformatHdl
));
1007 m_xComboBox
->connect_changed(LINK(this, FontSizeBox
, ModifyHdl
));
1010 void FontSizeBox::set_active_or_entry_text(const OUString
& rText
)
1012 const int nFound
= m_xComboBox
->find_text(rText
);
1014 m_xComboBox
->set_active(nFound
);
1015 m_xComboBox
->set_entry_text(rText
);
1018 IMPL_LINK(FontSizeBox
, ReformatHdl
, weld::Widget
&, rWidget
, void)
1020 FontSizeNames
aFontSizeNames(Application::GetSettings().GetUILanguageTag().getLanguageType());
1021 if (!bRelativeMode
|| !aFontSizeNames
.IsEmpty())
1023 if (aFontSizeNames
.Name2Size(m_xComboBox
->get_active_text()) != 0)
1027 set_value(get_value());
1029 m_aFocusOutHdl
.Call(rWidget
);
1032 IMPL_LINK(FontSizeBox
, ModifyHdl
, weld::ComboBox
&, rBox
, void)
1036 OUString aStr
= comphelper::string::stripStart(rBox
.get_active_text(), ' ');
1038 bool bNewMode
= bRelative
;
1039 bool bOldPtRelMode
= bPtRelative
;
1043 bPtRelative
= false;
1044 const sal_Unicode
* pStr
= aStr
.getStr();
1047 if ( ((*pStr
< '0') || (*pStr
> '9')) && (*pStr
!= '%') && !unicode::isSpace(*pStr
) )
1049 if ( ('-' == *pStr
|| '+' == *pStr
) && !bPtRelative
)
1051 else if ( bPtRelative
&& 'p' == *pStr
&& 't' == *++pStr
)
1062 else if (!aStr
.isEmpty())
1064 if ( -1 != aStr
.indexOf('%') )
1067 bPtRelative
= false;
1070 if ( '-' == aStr
[0] || '+' == aStr
[0] )
1077 if ( bNewMode
!= bRelative
|| bPtRelative
!= bOldPtRelMode
)
1078 SetRelative( bNewMode
);
1080 m_aChangeHdl
.Call(rBox
);
1083 void FontSizeBox::Fill( const FontList
* pList
)
1085 // remember for relative mode
1088 // no font sizes need to be set for relative mode
1093 const int* pTempAry
;
1094 const int* pAry
= nullptr;
1096 pAry
= FontList::GetStdSizeAry();
1098 // first insert font size names (for simplified/traditional chinese)
1099 FontSizeNames
aFontSizeNames( Application::GetSettings().GetUILanguageTag().getLanguageType() );
1100 if ( pAry
== FontList::GetStdSizeAry() )
1102 // for standard sizes we don't need to bother
1103 if (bStdSize
&& m_xComboBox
->get_count() && aFontSizeNames
.IsEmpty())
1110 int nSelectionStart
, nSelectionEnd
;
1111 m_xComboBox
->get_entry_selection_bounds(nSelectionStart
, nSelectionEnd
);
1112 OUString aStr
= m_xComboBox
->get_active_text();
1114 m_xComboBox
->freeze();
1115 m_xComboBox
->clear();
1118 if ( !aFontSizeNames
.IsEmpty() )
1120 if ( pAry
== FontList::GetStdSizeAry() )
1122 // for scalable fonts all font size names
1123 sal_uLong nCount
= aFontSizeNames
.Count();
1124 for( sal_uLong i
= 0; i
< nCount
; i
++ )
1126 OUString aSizeName
= aFontSizeNames
.GetIndexName( i
);
1127 int nSize
= aFontSizeNames
.GetIndexSize( i
);
1128 OUString
sId(OUString::number(-nSize
)); // mark as special
1129 m_xComboBox
->insert(nPos
, aSizeName
, &sId
, nullptr, nullptr);
1135 // for fixed size fonts only selectable font size names
1139 OUString aSizeName
= aFontSizeNames
.Size2Name( *pTempAry
);
1140 if ( !aSizeName
.isEmpty() )
1142 OUString
sId(OUString::number(-(*pTempAry
))); // mark as special
1143 m_xComboBox
->insert(nPos
, aSizeName
, &sId
, nullptr, nullptr);
1151 // then insert numerical font size values
1155 InsertValue(*pTempAry
);
1159 m_xComboBox
->thaw();
1160 set_active_or_entry_text(aStr
);
1161 m_xComboBox
->select_entry_region(nSelectionStart
, nSelectionEnd
);
1164 void FontSizeBox::EnableRelativeMode( sal_uInt16 nNewMin
, sal_uInt16 nNewMax
, sal_uInt16 nStep
)
1166 bRelativeMode
= true;
1170 SetUnit(FieldUnit::POINT
);
1173 void FontSizeBox::EnablePtRelativeMode( short nNewMin
, short nNewMax
, short nStep
)
1175 bRelativeMode
= true;
1176 nPtRelMin
= nNewMin
;
1177 nPtRelMax
= nNewMax
;
1179 SetUnit(FieldUnit::POINT
);
1182 void FontSizeBox::InsertValue(int i
)
1184 OUString
sNumber(OUString::number(i
));
1185 m_xComboBox
->append(sNumber
, format_number(i
));
1188 void FontSizeBox::SetRelative( bool bNewRelative
)
1190 if ( !bRelativeMode
)
1193 int nSelectionStart
, nSelectionEnd
;
1194 m_xComboBox
->get_entry_selection_bounds(nSelectionStart
, nSelectionEnd
);
1195 OUString aStr
= comphelper::string::stripStart(m_xComboBox
->get_active_text(), ' ');
1202 m_xComboBox
->clear();
1206 SetDecimalDigits( 1 );
1207 SetRange(nPtRelMin
, nPtRelMax
);
1208 SetUnit(FieldUnit::POINT
);
1210 short i
= nPtRelMin
, n
= 0;
1211 // JP 30.06.98: more than 100 values are not useful
1212 while ( i
<= nPtRelMax
&& n
++ < 100 )
1220 SetDecimalDigits(0);
1221 SetRange(nRelMin
, nRelMax
);
1222 SetUnit(FieldUnit::PERCENT
);
1224 sal_uInt16 i
= nRelMin
;
1225 while ( i
<= nRelMax
)
1235 m_xComboBox
->clear();
1236 bRelative
= bPtRelative
= false;
1237 SetDecimalDigits(1);
1239 SetUnit(FieldUnit::POINT
);
1244 set_active_or_entry_text(aStr
);
1245 m_xComboBox
->select_entry_region(nSelectionStart
, nSelectionEnd
);
1248 OUString
FontSizeBox::format_number(int nValue
) const
1252 //pawn percent off to icu to decide whether percent is separated from its number for this locale
1253 if (eUnit
== FieldUnit::PERCENT
)
1255 double fValue
= nValue
;
1256 fValue
/= weld::SpinButton::Power10(nDecimalDigits
);
1257 sRet
= unicode::formatPercent(fValue
, Application::GetSettings().GetUILanguageTag());
1261 const SvtSysLocale aSysLocale
;
1262 const LocaleDataWrapper
& rLocaleData
= aSysLocale
.GetLocaleData();
1263 sRet
= rLocaleData
.getNum(nValue
, nDecimalDigits
, true, false);
1264 if (eUnit
!= FieldUnit::NONE
&& eUnit
!= FieldUnit::DEGREE
)
1266 assert(eUnit
!= FieldUnit::PERCENT
);
1267 sRet
+= weld::MetricSpinButton::MetricToString(eUnit
);
1270 if (bRelativeMode
&& bPtRelative
&& (0 <= nValue
) && !sRet
.isEmpty())
1276 void FontSizeBox::SetValue(int nNewValue
, FieldUnit eInUnit
)
1278 auto nTempValue
= vcl::ConvertValue(nNewValue
, 0, GetDecimalDigits(), eInUnit
, GetUnit());
1279 if (nTempValue
< nMin
)
1281 else if (nTempValue
> nMax
)
1285 FontSizeNames
aFontSizeNames(Application::GetSettings().GetUILanguageTag().getLanguageType());
1286 // conversion loses precision; however font sizes should
1287 // never have a problem with that
1288 OUString aName
= aFontSizeNames
.Size2Name(nTempValue
);
1289 if (!aName
.isEmpty() && m_xComboBox
->find_text(aName
) != -1)
1291 m_xComboBox
->set_active_text(aName
);
1295 OUString aResult
= format_number(nTempValue
);
1296 set_active_or_entry_text(aResult
);
1299 void FontSizeBox::set_value(int nNewValue
)
1301 SetValue(nNewValue
, eUnit
);
1304 int FontSizeBox::get_value() const
1306 OUString aStr
= m_xComboBox
->get_active_text();
1309 FontSizeNames
aFontSizeNames(Application::GetSettings().GetUILanguageTag().getLanguageType());
1310 auto nValue
= aFontSizeNames
.Name2Size(aStr
);
1312 return vcl::ConvertValue(nValue
, 0, GetDecimalDigits(), GetUnit(), GetUnit());
1315 const SvtSysLocale aSysLocale
;
1316 const LocaleDataWrapper
& rLocaleData
= aSysLocale
.GetLocaleData();
1317 double fResult(0.0);
1318 (void)vcl::TextToValue(aStr
, fResult
, 0, GetDecimalDigits(), rLocaleData
, GetUnit());
1319 if (!aStr
.isEmpty())
1323 else if (fResult
> nMax
)
1329 SvxBorderLineStyle
SvtLineListBox::GetSelectEntryStyle() const
1331 if (m_xLineSet
->IsNoSelection())
1332 return SvxBorderLineStyle::NONE
;
1333 auto nId
= m_xLineSet
->GetSelectedItemId();
1334 return static_cast<SvxBorderLineStyle
>(nId
- 1);
1339 Size
getPreviewSize(const weld::Widget
& rControl
)
1341 return Size(rControl
.get_approximate_digit_width() * 15, rControl
.get_text_height());
1345 void SvtLineListBox::ImpGetLine( tools::Long nLine1
, tools::Long nLine2
, tools::Long nDistance
,
1346 Color aColor1
, Color aColor2
, Color aColorDist
,
1347 SvxBorderLineStyle nStyle
, BitmapEx
& rBmp
)
1349 Size
aSize(getPreviewSize(*m_xControl
));
1351 // SourceUnit to Twips
1352 if ( eSourceUnit
== FieldUnit::POINT
)
1360 aSize
= aVirDev
->PixelToLogic( aSize
);
1361 tools::Long nPix
= aVirDev
->PixelToLogic( Size( 0, 1 ) ).Height();
1362 sal_uInt32 n1
= nLine1
;
1363 sal_uInt32 n2
= nLine2
;
1364 tools::Long nDist
= nDistance
;
1370 nDist
-= nDist
%nPix
;
1374 tools::Long nVirHeight
= n1
+nDist
+n2
;
1375 if ( nVirHeight
> aSize
.Height() )
1376 aSize
.setHeight( nVirHeight
);
1377 // negative width should not be drawn
1378 if ( aSize
.Width() <= 0 )
1381 Size aVirSize
= aVirDev
->LogicToPixel( aSize
);
1382 if ( aVirDev
->GetOutputSizePixel() != aVirSize
)
1383 aVirDev
->SetOutputSizePixel( aVirSize
);
1384 aVirDev
->SetFillColor( aColorDist
);
1385 aVirDev
->DrawRect( tools::Rectangle( Point(), aSize
) );
1387 aVirDev
->SetFillColor( aColor1
);
1389 double y1
= double( n1
) / 2;
1390 svtools::DrawLine( *aVirDev
, basegfx::B2DPoint( 0, y1
), basegfx::B2DPoint( aSize
.Width( ), y1
), n1
, nStyle
);
1394 double y2
= n1
+ nDist
+ double( n2
) / 2;
1395 aVirDev
->SetFillColor( aColor2
);
1396 svtools::DrawLine( *aVirDev
, basegfx::B2DPoint( 0, y2
), basegfx::B2DPoint( aSize
.Width(), y2
), n2
, SvxBorderLineStyle::SOLID
);
1398 rBmp
= aVirDev
->GetBitmapEx( Point(), Size( aSize
.Width(), n1
+nDist
+n2
) );
1401 SvtLineListBox::SvtLineListBox(std::unique_ptr
<weld::MenuButton
> pControl
)
1402 : WeldToolbarPopup(css::uno::Reference
<css::frame::XFrame
>(), pControl
.get(), "svt/ui/linewindow.ui", "line_popup_window")
1403 , m_xControl(std::move(pControl
))
1404 , m_xNoneButton(m_xBuilder
->weld_button("none_line_button"))
1405 , m_xLineSet(new ValueSet(nullptr))
1406 , m_xLineSetWin(new weld::CustomWeld(*m_xBuilder
, "lineset", *m_xLineSet
))
1408 , aVirDev(VclPtr
<VirtualDevice
>::Create())
1411 const StyleSettings
& rStyleSettings
= Application::GetSettings().GetStyleSettings();
1412 m_xLineSet
->SetStyle(WinBits(WB_FLATVALUESET
| WB_NO_DIRECTSELECT
| WB_TABSTOP
));
1413 m_xLineSet
->SetItemHeight(rStyleSettings
.GetListBoxPreviewDefaultPixelSize().Height() + 1);
1414 m_xLineSet
->SetColCount(1);
1415 m_xLineSet
->SetSelectHdl(LINK(this, SvtLineListBox
, ValueSelectHdl
));
1417 m_xNoneButton
->connect_clicked(LINK(this, SvtLineListBox
, NoneHdl
));
1419 m_xControl
->set_popover(m_xTopLevel
.get());
1420 m_xControl
->connect_toggled(LINK(this, SvtLineListBox
, ToggleHdl
));
1421 m_xControl
->connect_style_updated(LINK(this, SvtLineListBox
, StyleUpdatedHdl
));
1423 // lock size to these maxes height/width so it doesn't jump around in size
1424 m_xControl
->set_label(GetLineStyleName(SvxBorderLineStyle::NONE
));
1425 Size aNonePrefSize
= m_xControl
->get_preferred_size();
1426 m_xControl
->set_label("");
1427 aVirDev
->SetOutputSizePixel(getPreviewSize(*m_xControl
));
1428 m_xControl
->set_image(aVirDev
);
1429 Size aSolidPrefSize
= m_xControl
->get_preferred_size();
1430 m_xControl
->set_size_request(std::max(aNonePrefSize
.Width(), aSolidPrefSize
.Width()),
1431 std::max(aNonePrefSize
.Height(), aSolidPrefSize
.Height()));
1433 eSourceUnit
= FieldUnit::POINT
;
1435 aVirDev
->SetLineColor();
1436 aVirDev
->SetMapMode(MapMode(MapUnit::MapTwip
));
1439 void SvtLineListBox::GrabFocus()
1441 if (GetSelectEntryStyle() == SvxBorderLineStyle::NONE
)
1442 m_xNoneButton
->grab_focus();
1444 m_xLineSet
->GrabFocus();
1447 IMPL_LINK(SvtLineListBox
, ToggleHdl
, weld::Toggleable
&, rButton
, void)
1449 if (rButton
.get_active())
1453 IMPL_LINK_NOARG(SvtLineListBox
, StyleUpdatedHdl
, weld::Widget
&, void)
1459 IMPL_LINK_NOARG(SvtLineListBox
, NoneHdl
, weld::Button
&, void)
1461 SelectEntry(SvxBorderLineStyle::NONE
);
1462 ValueSelectHdl(nullptr);
1465 SvtLineListBox::~SvtLineListBox()
1469 OUString
SvtLineListBox::GetLineStyleName(SvxBorderLineStyle eStyle
)
1472 for (sal_uInt32 i
= 0; i
< SAL_N_ELEMENTS(RID_SVXSTR_BORDERLINE
); ++i
)
1474 if (eStyle
== RID_SVXSTR_BORDERLINE
[i
].second
)
1476 sRet
= SvtResId(RID_SVXSTR_BORDERLINE
[i
].first
);
1483 void SvtLineListBox::SelectEntry(SvxBorderLineStyle nStyle
)
1485 if (nStyle
== SvxBorderLineStyle::NONE
)
1486 m_xLineSet
->SetNoSelection();
1488 m_xLineSet
->SelectItem(static_cast<sal_Int16
>(nStyle
) + 1);
1492 void SvtLineListBox::InsertEntry(
1493 const BorderWidthImpl
& rWidthImpl
, SvxBorderLineStyle nStyle
, tools::Long nMinWidth
,
1494 ColorFunc pColor1Fn
, ColorFunc pColor2Fn
, ColorDistFunc pColorDistFn
)
1496 m_vLineList
.emplace_back(new ImpLineListData(
1497 rWidthImpl
, nStyle
, nMinWidth
, pColor1Fn
, pColor2Fn
, pColorDistFn
));
1500 void SvtLineListBox::UpdateEntries()
1502 SvxBorderLineStyle eSelected
= GetSelectEntryStyle();
1504 // Remove the old entries
1505 m_xLineSet
->Clear();
1507 const StyleSettings
& rSettings
= Application::GetSettings().GetStyleSettings();
1508 Color aFieldColor
= rSettings
.GetFieldColor();
1510 // Add the new entries based on the defined width
1512 sal_uInt16 nCount
= m_vLineList
.size( );
1513 while ( n
< nCount
)
1515 auto& pData
= m_vLineList
[ n
];
1517 ImpGetLine( pData
->GetLine1ForWidth( m_nWidth
),
1518 pData
->GetLine2ForWidth( m_nWidth
),
1519 pData
->GetDistForWidth( m_nWidth
),
1520 pData
->GetColorLine1(aColor
),
1521 pData
->GetColorLine2(aColor
),
1522 pData
->GetColorDist(aColor
, aFieldColor
),
1523 pData
->GetStyle(), aBmp
);
1524 sal_Int16 nItemId
= static_cast<sal_Int16
>(pData
->GetStyle()) + 1;
1525 m_xLineSet
->InsertItem(nItemId
, Image(aBmp
), GetLineStyleName(pData
->GetStyle()));
1526 if (pData
->GetStyle() == eSelected
)
1527 m_xLineSet
->SelectItem(nItemId
);
1531 m_xLineSet
->SetOptimalSize();
1534 IMPL_LINK_NOARG(SvtLineListBox
, ValueSelectHdl
, ValueSet
*, void)
1536 maSelectHdl
.Call(*this);
1538 if (m_xControl
->get_active())
1539 m_xControl
->set_active(false);
1542 void SvtLineListBox::UpdatePreview()
1544 SvxBorderLineStyle eStyle
= GetSelectEntryStyle();
1545 for (sal_uInt32 i
= 0; i
< SAL_N_ELEMENTS(RID_SVXSTR_BORDERLINE
); ++i
)
1547 if (eStyle
== RID_SVXSTR_BORDERLINE
[i
].second
)
1549 m_xControl
->set_label(SvtResId(RID_SVXSTR_BORDERLINE
[i
].first
));
1554 if (eStyle
== SvxBorderLineStyle::NONE
)
1556 m_xControl
->set_image(nullptr);
1557 m_xControl
->set_label(GetLineStyleName(SvxBorderLineStyle::NONE
));
1561 Image
aImage(m_xLineSet
->GetItemImage(m_xLineSet
->GetSelectedItemId()));
1562 m_xControl
->set_label("");
1563 const auto nPos
= (aVirDev
->GetOutputSizePixel().Height() - aImage
.GetSizePixel().Height()) / 2;
1564 aVirDev
->Push(vcl::PushFlags::MAPMODE
);
1565 aVirDev
->SetMapMode(MapMode(MapUnit::MapPixel
));
1566 const StyleSettings
& rSettings
= Application::GetSettings().GetStyleSettings();
1567 aVirDev
->SetBackground(rSettings
.GetFieldColor());
1569 aVirDev
->DrawImage(Point(0, nPos
), aImage
);
1570 m_xControl
->set_image(aVirDev
.get());
1575 SvtCalendarBox::SvtCalendarBox(std::unique_ptr
<weld::MenuButton
> pControl
, bool bUseLabel
)
1576 : m_bUseLabel(bUseLabel
)
1577 , m_xControl(std::move(pControl
))
1578 , m_xBuilder(Application::CreateBuilder(m_xControl
.get(), "svt/ui/datewindow.ui"))
1579 , m_xTopLevel(m_xBuilder
->weld_widget("date_popup_window"))
1580 , m_xCalendar(m_xBuilder
->weld_calendar("date"))
1582 m_xControl
->set_popover(m_xTopLevel
.get());
1583 m_xCalendar
->connect_selected(LINK(this, SvtCalendarBox
, SelectHdl
));
1584 m_xCalendar
->connect_activated(LINK(this, SvtCalendarBox
, ActivateHdl
));
1587 void SvtCalendarBox::set_date(const Date
& rDate
)
1589 m_xCalendar
->set_date(rDate
);
1590 set_label_from_date();
1593 void SvtCalendarBox::set_label_from_date()
1597 const LocaleDataWrapper
& rLocaleData
= Application::GetSettings().GetLocaleDataWrapper();
1598 m_xControl
->set_label(rLocaleData
.getDate(m_xCalendar
->get_date()));
1601 IMPL_LINK_NOARG(SvtCalendarBox
, SelectHdl
, weld::Calendar
&, void)
1603 set_label_from_date();
1604 m_aSelectHdl
.Call(*this);
1607 IMPL_LINK_NOARG(SvtCalendarBox
, ActivateHdl
, weld::Calendar
&, void)
1609 if (m_xControl
->get_active())
1610 m_xControl
->set_active(false);
1611 m_aActivatedHdl
.Call(*this);
1614 SvtCalendarBox::~SvtCalendarBox()
1618 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */