1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <config_folders.h>
21 
22 #include <i18nutil/unicode.hxx>
23 #include <tools/stream.hxx>
24 #include <vcl/builder.hxx>
25 #include <vcl/customweld.hxx>
26 #include <vcl/event.hxx>
27 #include <vcl/svapp.hxx>
28 #include <vcl/field.hxx>
29 #include <vcl/settings.hxx>
30 #include <vcl/image.hxx>
31 #include <vcl/virdev.hxx>
32 #include <sal/macros.h>
33 #include <sal/log.hxx>
34 #include <comphelper/processfactory.hxx>
35 #include <comphelper/string.hxx>
36 #include <unotools/charclass.hxx>
37 #include <unotools/fontoptions.hxx>
38 #include <unotools/localedatawrapper.hxx>
39 
40 #include <svtools/borderline.hxx>
41 #include <svtools/sampletext.hxx>
42 #include <svtools/svtresid.hxx>
43 #include <svtools/strings.hrc>
44 #include <svtools/ctrlbox.hxx>
45 #include <svtools/ctrltool.hxx>
46 #include <svtools/borderhelper.hxx>
47 #include <svtools/valueset.hxx>
48 
49 #include <basegfx/polygon/b2dpolygon.hxx>
50 #include <basegfx/polygon/b2dpolygontools.hxx>
51 #include <editeng/borderline.hxx>
52 
53 #include <rtl/bootstrap.hxx>
54 
55 #include <borderline.hrc>
56 
57 #include <stdio.h>
58 
59 #define IMGOUTERTEXTSPACE 5
60 #define EXTRAFONTSIZE 5
61 #define GAPTOEXTRAPREVIEW 10
62 #define MAXPREVIEWWIDTH 120
63 #define MINGAPWIDTH 2
64 
65 #define FONTNAMEBOXMRUENTRIESFILE "/user/config/fontnameboxmruentries"
66 
67 
BorderWidthImpl(BorderWidthImplFlags nFlags,double nRate1,double nRate2,double nRateGap)68 BorderWidthImpl::BorderWidthImpl( BorderWidthImplFlags nFlags, double nRate1, double nRate2, double nRateGap ):
69     m_nFlags( nFlags ),
70     m_nRate1( nRate1 ),
71     m_nRate2( nRate2 ),
72     m_nRateGap( nRateGap )
73 {
74 }
75 
operator ==(const BorderWidthImpl & r) const76 bool BorderWidthImpl::operator== ( const BorderWidthImpl& r ) const
77 {
78     return ( m_nFlags == r.m_nFlags ) &&
79            ( m_nRate1 == r.m_nRate1 ) &&
80            ( m_nRate2 == r.m_nRate2 ) &&
81            ( m_nRateGap == r.m_nRateGap );
82 }
83 
GetLine1(long nWidth) const84 long BorderWidthImpl::GetLine1( long nWidth ) const
85 {
86     long result = static_cast<long>(m_nRate1);
87     if ( m_nFlags & BorderWidthImplFlags::CHANGE_LINE1 )
88     {
89         long const nConstant2 = (m_nFlags & BorderWidthImplFlags::CHANGE_LINE2) ? 0 : m_nRate2;
90         long const nConstantD = (m_nFlags & BorderWidthImplFlags::CHANGE_DIST ) ? 0 : m_nRateGap;
91         result = std::max<long>(0,
92                     static_cast<long>((m_nRate1 * nWidth) + 0.5)
93                         - (nConstant2 + nConstantD));
94         if (result == 0 && m_nRate1 > 0.0 && nWidth > 0)
95         {   // fdo#51777: hack to essentially treat 1 twip DOUBLE border
96             result = 1;  // as 1 twip SINGLE border
97         }
98     }
99     return result;
100 }
101 
GetLine2(long nWidth) const102 long BorderWidthImpl::GetLine2( long nWidth ) const
103 {
104     long result = static_cast<long>(m_nRate2);
105     if ( m_nFlags & BorderWidthImplFlags::CHANGE_LINE2)
106     {
107         long const nConstant1 = (m_nFlags & BorderWidthImplFlags::CHANGE_LINE1) ? 0 : m_nRate1;
108         long const nConstantD = (m_nFlags & BorderWidthImplFlags::CHANGE_DIST ) ? 0 : m_nRateGap;
109         result = std::max<long>(0,
110                     static_cast<long>((m_nRate2 * nWidth) + 0.5)
111                         - (nConstant1 + nConstantD));
112     }
113     return result;
114 }
115 
GetGap(long nWidth) const116 long BorderWidthImpl::GetGap( long nWidth ) const
117 {
118     long result = static_cast<long>(m_nRateGap);
119     if ( m_nFlags & BorderWidthImplFlags::CHANGE_DIST )
120     {
121         long const nConstant1 = (m_nFlags & BorderWidthImplFlags::CHANGE_LINE1) ? 0 : m_nRate1;
122         long const nConstant2 = (m_nFlags & BorderWidthImplFlags::CHANGE_LINE2) ? 0 : m_nRate2;
123         result = std::max<long>(0,
124                     static_cast<long>((m_nRateGap * nWidth) + 0.5)
125                         - (nConstant1 + nConstant2));
126     }
127 
128     // Avoid having too small distances (less than 0.1pt)
129     if ( result < MINGAPWIDTH && m_nRate1 > 0 && m_nRate2 > 0 )
130         result = MINGAPWIDTH;
131 
132     return result;
133 }
134 
lcl_getGuessedWidth(long nTested,double nRate,bool bChanging)135 static double lcl_getGuessedWidth( long nTested, double nRate, bool bChanging )
136 {
137     double nWidth = -1.0;
138     if ( bChanging )
139         nWidth = double( nTested ) / nRate;
140     else
141     {
142         if ( rtl::math::approxEqual(double( nTested ), nRate) )
143             nWidth = nRate;
144     }
145 
146     return nWidth;
147 }
148 
GuessWidth(long nLine1,long nLine2,long nGap)149 long BorderWidthImpl::GuessWidth( long nLine1, long nLine2, long nGap )
150 {
151     std::vector< double > aToCompare;
152     bool bInvalid = false;
153 
154     bool bLine1Change = bool( m_nFlags & BorderWidthImplFlags::CHANGE_LINE1 );
155     double nWidth1 = lcl_getGuessedWidth( nLine1, m_nRate1, bLine1Change );
156     if ( bLine1Change )
157         aToCompare.push_back( nWidth1 );
158     else if (nWidth1 < 0)
159         bInvalid = true;
160 
161     bool bLine2Change = bool( m_nFlags & BorderWidthImplFlags::CHANGE_LINE2 );
162     double nWidth2 = lcl_getGuessedWidth( nLine2, m_nRate2, bLine2Change );
163     if ( bLine2Change )
164         aToCompare.push_back( nWidth2 );
165     else if (nWidth2 < 0)
166         bInvalid = true;
167 
168     bool bGapChange = bool( m_nFlags & BorderWidthImplFlags::CHANGE_DIST );
169     double nWidthGap = lcl_getGuessedWidth( nGap, m_nRateGap, bGapChange );
170     if ( bGapChange && nGap >= MINGAPWIDTH )
171         aToCompare.push_back( nWidthGap );
172     else if ( !bGapChange && nWidthGap < 0 )
173         bInvalid = true;
174 
175     // non-constant line width factors must sum to 1
176     assert((((bLine1Change) ? m_nRate1 : 0) +
177             ((bLine2Change) ? m_nRate2 : 0) +
178             ((bGapChange) ? m_nRateGap : 0)) - 1.0 < 0.00001 );
179 
180     double nWidth = 0.0;
181     if ( (!bInvalid) && (!aToCompare.empty()) )
182     {
183         nWidth = *aToCompare.begin();
184         for (auto const& elem : aToCompare)
185         {
186             bInvalid = ( nWidth != elem );
187             if (bInvalid)
188                 break;
189         }
190         nWidth = bInvalid ?  0.0 : nLine1 + nLine2 + nGap;
191     }
192 
193     return nWidth;
194 }
195 
lclDrawPolygon(OutputDevice & rDev,const basegfx::B2DPolygon & rPolygon,long nWidth,SvxBorderLineStyle nDashing)196 static void lclDrawPolygon( OutputDevice& rDev, const basegfx::B2DPolygon& rPolygon, long nWidth, SvxBorderLineStyle nDashing )
197 {
198     AntialiasingFlags nOldAA = rDev.GetAntialiasing();
199     rDev.SetAntialiasing( nOldAA & ~AntialiasingFlags::EnableB2dDraw );
200 
201     long nPix = rDev.PixelToLogic(Size(1, 1)).Width();
202     basegfx::B2DPolyPolygon aPolygons = svtools::ApplyLineDashing(rPolygon, nDashing, nPix);
203 
204     // Handle problems of width 1px in Pixel mode: 0.5px gives a 1px line
205     if (rDev.GetMapMode().GetMapUnit() == MapUnit::MapPixel && nWidth == nPix)
206         nWidth = 0;
207 
208     for ( sal_uInt32 i = 0; i < aPolygons.count( ); i++ )
209     {
210         const basegfx::B2DPolygon& aDash = aPolygons.getB2DPolygon( i );
211         basegfx::B2DPoint aStart = aDash.getB2DPoint( 0 );
212         basegfx::B2DPoint aEnd = aDash.getB2DPoint( aDash.count() - 1 );
213 
214         basegfx::B2DVector aVector( aEnd - aStart );
215         aVector.normalize( );
216         const basegfx::B2DVector aPerpendicular(basegfx::getPerpendicular(aVector));
217 
218         const basegfx::B2DVector aWidthOffset( double( nWidth ) / 2 * aPerpendicular);
219         basegfx::B2DPolygon aDashPolygon;
220         aDashPolygon.append( aStart + aWidthOffset );
221         aDashPolygon.append( aEnd + aWidthOffset );
222         aDashPolygon.append( aEnd - aWidthOffset );
223         aDashPolygon.append( aStart - aWidthOffset );
224         aDashPolygon.setClosed( true );
225 
226         rDev.DrawPolygon( aDashPolygon );
227     }
228 
229     rDev.SetAntialiasing( nOldAA );
230 }
231 
232 namespace svtools {
233 
234 /**
235  * Dashing array must start with a line width and end with a blank width.
236  */
GetDashing(SvxBorderLineStyle nDashing)237 static std::vector<double> GetDashing( SvxBorderLineStyle nDashing )
238 {
239     std::vector<double> aPattern;
240     switch (nDashing)
241     {
242         case SvxBorderLineStyle::DOTTED:
243             aPattern.push_back( 1.0 ); // line
244             aPattern.push_back( 2.0 ); // blank
245         break;
246         case SvxBorderLineStyle::DASHED:
247             aPattern.push_back( 16.0 ); // line
248             aPattern.push_back( 5.0 );  // blank
249         break;
250         case SvxBorderLineStyle::FINE_DASHED:
251             aPattern.push_back( 6.0 ); // line
252             aPattern.push_back( 2.0 ); // blank
253         break;
254         case SvxBorderLineStyle::DASH_DOT:
255             aPattern.push_back( 16.0 ); // line
256             aPattern.push_back( 5.0 );  // blank
257             aPattern.push_back( 5.0 );  // line
258             aPattern.push_back( 5.0 );  // blank
259         break;
260         case SvxBorderLineStyle::DASH_DOT_DOT:
261             aPattern.push_back( 16.0 ); // line
262             aPattern.push_back( 5.0 );  // blank
263             aPattern.push_back( 5.0 );  // line
264             aPattern.push_back( 5.0 );  // blank
265             aPattern.push_back( 5.0 );  // line
266             aPattern.push_back( 5.0 );  // blank
267         break;
268         default:
269             ;
270     }
271 
272     return aPattern;
273 }
274 
275 namespace {
276 
277 class ApplyScale
278 {
279     double const mfScale;
280 public:
ApplyScale(double fScale)281     explicit ApplyScale( double fScale ) : mfScale(fScale) {}
operator ()(double & rVal)282     void operator() ( double& rVal )
283     {
284         rVal *= mfScale;
285     }
286 };
287 
288 }
289 
GetLineDashing(SvxBorderLineStyle nDashing,double fScale)290 std::vector<double> GetLineDashing( SvxBorderLineStyle nDashing, double fScale )
291 {
292     std::vector<double> aPattern = GetDashing(nDashing);
293     std::for_each(aPattern.begin(), aPattern.end(), ApplyScale(fScale));
294     return aPattern;
295 }
296 
ApplyLineDashing(const basegfx::B2DPolygon & rPolygon,SvxBorderLineStyle nDashing,double fScale)297 basegfx::B2DPolyPolygon ApplyLineDashing( const basegfx::B2DPolygon& rPolygon, SvxBorderLineStyle nDashing, double fScale )
298 {
299     std::vector<double> aPattern = GetDashing(nDashing);
300     std::for_each(aPattern.begin(), aPattern.end(), ApplyScale(fScale));
301 
302     basegfx::B2DPolyPolygon aPolygons;
303 
304     if (aPattern.empty())
305         aPolygons.append(rPolygon);
306     else
307         basegfx::utils::applyLineDashing(rPolygon, aPattern, &aPolygons);
308 
309     return aPolygons;
310 }
311 
DrawLine(OutputDevice & rDev,const Point & rP1,const Point & rP2,sal_uInt32 nWidth,SvxBorderLineStyle nDashing)312 void DrawLine( OutputDevice& rDev, const Point& rP1, const Point& rP2,
313     sal_uInt32 nWidth, SvxBorderLineStyle nDashing )
314 {
315     DrawLine( rDev, basegfx::B2DPoint( rP1.X(), rP1.Y() ),
316             basegfx::B2DPoint( rP2.X(), rP2.Y( ) ), nWidth, nDashing );
317 }
318 
DrawLine(OutputDevice & rDev,const basegfx::B2DPoint & rP1,const basegfx::B2DPoint & rP2,sal_uInt32 nWidth,SvxBorderLineStyle nDashing)319 void DrawLine( OutputDevice& rDev, const basegfx::B2DPoint& rP1, const basegfx::B2DPoint& rP2,
320     sal_uInt32 nWidth, SvxBorderLineStyle nDashing )
321 {
322     basegfx::B2DPolygon aPolygon;
323     aPolygon.append( rP1 );
324     aPolygon.append( rP2 );
325     lclDrawPolygon( rDev, aPolygon, nWidth, nDashing );
326 }
327 
328 }
329 
FontNameBox(vcl::Window * pParent,WinBits nWinStyle)330 FontNameBox::FontNameBox( vcl::Window* pParent, WinBits nWinStyle ) :
331     ComboBox( pParent, nWinStyle )
332 {
333     EnableSelectAll();
334     mbWYSIWYG = false;
335     InitFontMRUEntriesFile();
336 }
337 
makeFontNameBox(VclPtr<vcl::Window> & rRet,const VclPtr<vcl::Window> & pParent,VclBuilder::stringmap & rMap)338 extern "C" SAL_DLLPUBLIC_EXPORT void makeFontNameBox(VclPtr<vcl::Window> & rRet, const VclPtr<vcl::Window> & pParent, VclBuilder::stringmap & rMap)
339 {
340     static_assert(std::is_same_v<std::remove_pointer_t<VclBuilder::customMakeWidget>,
341                                  decltype(makeFontNameBox)>);
342     bool bDropdown = BuilderUtils::extractDropdown(rMap);
343     WinBits nWinBits = WB_LEFT|WB_VCENTER|WB_3DLOOK|WB_TABSTOP;
344     if (bDropdown)
345         nWinBits |= WB_DROPDOWN;
346     VclPtrInstance<FontNameBox> pListBox(pParent, nWinBits);
347     if (bDropdown)
348         pListBox->EnableAutoSize(true);
349     rRet = pListBox;
350 }
351 
~FontNameBox()352 FontNameBox::~FontNameBox()
353 {
354     disposeOnce();
355 }
356 
dispose()357 void FontNameBox::dispose()
358 {
359     if (mpFontList)
360     {
361         SaveMRUEntries (maFontMRUEntriesFile);
362         ImplDestroyFontList();
363     }
364     ComboBox::dispose();
365 }
366 
SaveMRUEntries(const OUString & aFontMRUEntriesFile) const367 void FontNameBox::SaveMRUEntries( const OUString& aFontMRUEntriesFile ) const
368 {
369     OString aEntries(OUStringToOString(GetMRUEntries(),
370         RTL_TEXTENCODING_UTF8));
371 
372     if (aEntries.isEmpty() || aFontMRUEntriesFile.isEmpty())
373         return;
374 
375     SvFileStream aStream;
376     aStream.Open( aFontMRUEntriesFile, StreamMode::WRITE | StreamMode::TRUNC );
377     if( ! (aStream.IsOpen() && aStream.IsWritable()) )
378     {
379         SAL_INFO("svtools.control", "FontNameBox::SaveMRUEntries: opening mru entries file " << aFontMRUEntriesFile << " failed");
380         return;
381     }
382 
383     aStream.SetLineDelimiter( LINEEND_LF );
384     aStream.WriteLine( aEntries );
385     aStream.WriteLine( OString() );
386 }
387 
LoadMRUEntries(const OUString & aFontMRUEntriesFile)388 void FontNameBox::LoadMRUEntries( const OUString& aFontMRUEntriesFile )
389 {
390     if (aFontMRUEntriesFile.isEmpty())
391         return;
392 
393     SvtFontOptions aFontOpt;
394     if (!aFontOpt.IsFontHistoryEnabled())
395         return;
396 
397     SvFileStream aStream( aFontMRUEntriesFile, StreamMode::READ );
398     if( ! aStream.IsOpen() )
399     {
400         SAL_INFO("svtools.control", "FontNameBox::LoadMRUEntries: opening mru entries file " << aFontMRUEntriesFile << " failed");
401         return;
402     }
403 
404     OString aLine;
405     aStream.ReadLine( aLine );
406     OUString aEntries = OStringToOUString(aLine,
407         RTL_TEXTENCODING_UTF8);
408     SetMRUEntries( aEntries );
409 }
410 
InitFontMRUEntriesFile()411 void FontNameBox::InitFontMRUEntriesFile()
412 {
413     OUString sUserConfigDir("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE( "bootstrap") "::UserInstallation}");
414     rtl::Bootstrap::expandMacros(sUserConfigDir);
415 
416     maFontMRUEntriesFile = sUserConfigDir;
417     if( !maFontMRUEntriesFile.isEmpty() )
418     {
419         maFontMRUEntriesFile += FONTNAMEBOXMRUENTRIESFILE;
420     }
421 }
422 
ImplDestroyFontList()423 void FontNameBox::ImplDestroyFontList()
424 {
425     mpFontList.reset();
426 }
427 
Fill(const FontList * pList)428 void FontNameBox::Fill( const FontList* pList )
429 {
430     // store old text and clear box
431     OUString aOldText = GetText();
432     OUString rEntries = GetMRUEntries();
433     bool bLoadFromFile = rEntries.isEmpty();
434     Clear();
435 
436     ImplDestroyFontList();
437     mpFontList.reset(new ImplFontList);
438 
439     // insert fonts
440     sal_uInt16 nFontCount = pList->GetFontNameCount();
441     for ( sal_uInt16 i = 0; i < nFontCount; i++ )
442     {
443         const FontMetric& rFontMetric = pList->GetFontName( i );
444         sal_Int32 nIndex = InsertEntry( rFontMetric.GetFamilyName() );
445         if ( nIndex < static_cast<sal_Int32>(mpFontList->size()) ) {
446             ImplFontList::iterator it = mpFontList->begin();
447             ::std::advance( it, nIndex );
448             mpFontList->insert( it, rFontMetric );
449         } else {
450             mpFontList->push_back( rFontMetric );
451         }
452     }
453 
454     if ( bLoadFromFile )
455         LoadMRUEntries (maFontMRUEntriesFile);
456     else
457         SetMRUEntries( rEntries );
458 
459     ImplCalcUserItemSize();
460 
461     // restore text
462     if (!aOldText.isEmpty())
463         SetText( aOldText );
464 }
465 
EnableWYSIWYG(bool bEnable)466 void FontNameBox::EnableWYSIWYG( bool bEnable )
467 {
468     if ( bEnable != mbWYSIWYG )
469     {
470         mbWYSIWYG = bEnable;
471         EnableUserDraw( mbWYSIWYG );
472         ImplCalcUserItemSize();
473     }
474 }
475 
ImplCalcUserItemSize()476 void FontNameBox::ImplCalcUserItemSize()
477 {
478     Size aUserItemSz;
479     if ( mbWYSIWYG && mpFontList )
480     {
481         aUserItemSz = Size(MAXPREVIEWWIDTH, GetTextHeight() );
482         aUserItemSz.setHeight( aUserItemSz.Height() * 16 );
483         aUserItemSz.setHeight( aUserItemSz.Height() / 10 );
484     }
485     SetUserItemSize( aUserItemSz );
486 }
487 
488 namespace
489 {
shrinkFontToFit(OUString const & rSampleText,long nH,vcl::Font & rFont,OutputDevice & rDevice,tools::Rectangle & rTextRect)490     long shrinkFontToFit(OUString const &rSampleText, long nH, vcl::Font &rFont, OutputDevice &rDevice, tools::Rectangle &rTextRect)
491     {
492         long nWidth = 0;
493 
494         Size aSize( rFont.GetFontSize() );
495 
496         //Make sure it fits in the available height
497         while (aSize.Height() > 0)
498         {
499             if (!rDevice.GetTextBoundRect(rTextRect, rSampleText))
500                 break;
501             if (rTextRect.GetHeight() <= nH)
502             {
503                 nWidth = rTextRect.GetWidth();
504                 break;
505             }
506 
507             aSize.AdjustHeight( -(EXTRAFONTSIZE) );
508             rFont.SetFontSize(aSize);
509             rDevice.SetFont(rFont);
510         }
511 
512         return nWidth;
513     }
514 }
515 
UserDraw(const UserDrawEvent & rUDEvt)516 void FontNameBox::UserDraw( const UserDrawEvent& rUDEvt )
517 {
518     assert( mpFontList );
519 
520     FontMetric& rFontMetric = (*mpFontList)[ rUDEvt.GetItemId() ];
521     Point aTopLeft = rUDEvt.GetRect().TopLeft();
522     long nX = aTopLeft.X();
523     long nH = rUDEvt.GetRect().GetHeight();
524 
525     if ( mbWYSIWYG )
526     {
527         nX += IMGOUTERTEXTSPACE;
528 
529         const bool bSymbolFont = isSymbolFont(rFontMetric);
530         vcl::RenderContext* pRenderContext = rUDEvt.GetRenderContext();
531 
532         Color aTextColor = pRenderContext->GetTextColor();
533         vcl::Font aOldFont(pRenderContext->GetFont());
534         Size aSize( aOldFont.GetFontSize() );
535         aSize.AdjustHeight(EXTRAFONTSIZE );
536         vcl::Font aFont( rFontMetric );
537         aFont.SetFontSize( aSize );
538         pRenderContext->SetFont(aFont);
539         pRenderContext->SetTextColor(aTextColor);
540 
541         bool bUsingCorrectFont = true;
542         tools::Rectangle aTextRect;
543 
544         // Preview the font name
545         const OUString& sFontName = rFontMetric.GetFamilyName();
546 
547         //If it shouldn't or can't draw its own name because it doesn't have the glyphs
548         if (!canRenderNameOfSelectedFont(*pRenderContext))
549             bUsingCorrectFont = false;
550         else
551         {
552             //Make sure it fits in the available height, shrinking the font if necessary
553             bUsingCorrectFont = shrinkFontToFit(sFontName, nH, aFont, *pRenderContext, aTextRect) != 0;
554         }
555 
556         if (!bUsingCorrectFont)
557         {
558             pRenderContext->SetFont(aOldFont);
559             pRenderContext->GetTextBoundRect(aTextRect, sFontName);
560         }
561 
562         long nTextHeight = aTextRect.GetHeight();
563         long nDesiredGap = (nH-nTextHeight)/2;
564         long nVertAdjust = nDesiredGap - aTextRect.Top();
565         Point aPos( nX, aTopLeft.Y() + nVertAdjust );
566         pRenderContext->DrawText(aPos, sFontName);
567         long nTextX = aPos.X() + aTextRect.GetWidth() + GAPTOEXTRAPREVIEW;
568 
569         if (!bUsingCorrectFont)
570             pRenderContext->SetFont(aFont);
571 
572         OUString sSampleText;
573 
574         if (!bSymbolFont)
575         {
576             const bool bNameBeginsWithLatinText = rFontMetric.GetFamilyName()[0] <= 'z';
577 
578             if (bNameBeginsWithLatinText || !bUsingCorrectFont)
579                 sSampleText = makeShortRepresentativeTextForSelectedFont(*pRenderContext);
580         }
581 
582         //If we're not a symbol font, but could neither render our own name and
583         //we can't determine what script it would like to render, then try a
584         //few well known scripts
585         if (sSampleText.isEmpty() && !bUsingCorrectFont)
586         {
587             static const UScriptCode aScripts[] =
588             {
589                 USCRIPT_ARABIC,
590                 USCRIPT_HEBREW,
591 
592                 USCRIPT_BENGALI,
593                 USCRIPT_GURMUKHI,
594                 USCRIPT_GUJARATI,
595                 USCRIPT_ORIYA,
596                 USCRIPT_TAMIL,
597                 USCRIPT_TELUGU,
598                 USCRIPT_KANNADA,
599                 USCRIPT_MALAYALAM,
600                 USCRIPT_SINHALA,
601                 USCRIPT_DEVANAGARI,
602 
603                 USCRIPT_THAI,
604                 USCRIPT_LAO,
605                 USCRIPT_GEORGIAN,
606                 USCRIPT_TIBETAN,
607                 USCRIPT_SYRIAC,
608                 USCRIPT_MYANMAR,
609                 USCRIPT_ETHIOPIC,
610                 USCRIPT_KHMER,
611                 USCRIPT_MONGOLIAN,
612 
613                 USCRIPT_KOREAN,
614                 USCRIPT_JAPANESE,
615                 USCRIPT_HAN,
616                 USCRIPT_SIMPLIFIED_HAN,
617                 USCRIPT_TRADITIONAL_HAN,
618 
619                 USCRIPT_GREEK
620             };
621 
622             for (const UScriptCode& rScript : aScripts)
623             {
624                 OUString sText = makeShortRepresentativeTextForScript(rScript);
625                 if (!sText.isEmpty())
626                 {
627                     bool bHasSampleTextGlyphs = (-1 == pRenderContext->HasGlyphs(aFont, sText));
628                     if (bHasSampleTextGlyphs)
629                     {
630                         sSampleText = sText;
631                         break;
632                     }
633                 }
634             }
635 
636             static const UScriptCode aMinimalScripts[] =
637             {
638                 USCRIPT_HEBREW, //e.g. biblical hebrew
639                 USCRIPT_GREEK
640             };
641 
642             for (const UScriptCode& rMinimalScript : aMinimalScripts)
643             {
644                 OUString sText = makeShortMinimalTextForScript(rMinimalScript);
645                 if (!sText.isEmpty())
646                 {
647                     bool bHasSampleTextGlyphs = (-1 == pRenderContext->HasGlyphs(aFont, sText));
648                     if (bHasSampleTextGlyphs)
649                     {
650                         sSampleText = sText;
651                         break;
652                     }
653                 }
654             }
655         }
656 
657         //If we're a symbol font, or for some reason the font still couldn't
658         //render something representative of what it would like to render then
659         //make up some semi-random text that it *can* display
660         if (bSymbolFont || (!bUsingCorrectFont && sSampleText.isEmpty()))
661             sSampleText = makeShortRepresentativeSymbolTextForSelectedFont(*pRenderContext);
662 
663         if (!sSampleText.isEmpty())
664         {
665             const Size &rItemSize = rUDEvt.GetWindow()->GetOutputSize();
666 
667             //leave a little border at the edge
668             long nSpace = rItemSize.Width() - nTextX - IMGOUTERTEXTSPACE;
669             if (nSpace >= 0)
670             {
671                 //Make sure it fits in the available height, and get how wide that would be
672                 long nWidth = shrinkFontToFit(sSampleText, nH, aFont, *pRenderContext, aTextRect);
673                 //Chop letters off until it fits in the available width
674                 while (nWidth > nSpace || nWidth > MAXPREVIEWWIDTH)
675                 {
676                     sSampleText = sSampleText.copy(0, sSampleText.getLength()-1);
677                     nWidth = pRenderContext->GetTextBoundRect(aTextRect, sSampleText) ?
678                              aTextRect.GetWidth() : 0;
679                 }
680 
681                 //center the text on the line
682                 if (!sSampleText.isEmpty() && nWidth)
683                 {
684                     nTextHeight = aTextRect.GetHeight();
685                     nDesiredGap = (nH-nTextHeight)/2;
686                     nVertAdjust = nDesiredGap - aTextRect.Top();
687                     aPos = Point(nTextX + nSpace - nWidth, aTopLeft.Y() + nVertAdjust);
688                     pRenderContext->DrawText(aPos, sSampleText);
689                 }
690             }
691         }
692 
693         pRenderContext->SetFont(aOldFont);
694         DrawEntry( rUDEvt, false, false);   // draw separator
695     }
696     else
697     {
698         DrawEntry( rUDEvt, true, true );
699     }
700 }
701 
FontStyleBox(vcl::Window * pParent,WinBits nBits)702 FontStyleBox::FontStyleBox(vcl::Window* pParent, WinBits nBits)
703     : ComboBox(pParent, nBits)
704 {
705     //Use the standard texts to get an optimal size and stick to that size.
706     //That should stop the character dialog dancing around.
707     InsertEntry(SvtResId(STR_SVT_STYLE_LIGHT));
708     InsertEntry(SvtResId(STR_SVT_STYLE_LIGHT_ITALIC));
709     InsertEntry(SvtResId(STR_SVT_STYLE_NORMAL));
710     InsertEntry(SvtResId(STR_SVT_STYLE_NORMAL_ITALIC));
711     InsertEntry(SvtResId(STR_SVT_STYLE_BOLD));
712     InsertEntry(SvtResId(STR_SVT_STYLE_BOLD_ITALIC));
713     InsertEntry(SvtResId(STR_SVT_STYLE_BLACK));
714     InsertEntry(SvtResId(STR_SVT_STYLE_BLACK_ITALIC));
715     aOptimalSize = GetOptimalSize();
716     Clear();
717 }
718 
GetOptimalSize() const719 Size FontStyleBox::GetOptimalSize() const
720 {
721     if (aOptimalSize.Width() || aOptimalSize.Height())
722         return aOptimalSize;
723     return ComboBox::GetOptimalSize();
724 }
725 
makeFontStyleBox(VclPtr<vcl::Window> & rRet,const VclPtr<vcl::Window> & pParent,VclBuilder::stringmap & rMap)726 extern "C" SAL_DLLPUBLIC_EXPORT void makeFontStyleBox(VclPtr<vcl::Window> & rRet, const VclPtr<vcl::Window> & pParent, VclBuilder::stringmap & rMap)
727 {
728     static_assert(std::is_same_v<std::remove_pointer_t<VclBuilder::customMakeWidget>,
729                                  decltype(makeFontStyleBox)>);
730     bool bDropdown = BuilderUtils::extractDropdown(rMap);
731     WinBits nWinBits = WB_LEFT|WB_VCENTER|WB_3DLOOK|WB_TABSTOP;
732     if (bDropdown)
733         nWinBits |= WB_DROPDOWN;
734     VclPtrInstance<FontStyleBox> pListBox(pParent, nWinBits);
735     if (bDropdown)
736         pListBox->EnableAutoSize(true);
737     rRet = pListBox;
738 }
739 
Modify()740 void FontStyleBox::Modify()
741 {
742     CharClass   aChrCls( ::comphelper::getProcessComponentContext(),
743                         GetSettings().GetLanguageTag() );
744     OUString   aStr = GetText();
745     sal_Int32      nEntryCount = GetEntryCount();
746 
747     if ( GetEntryPos( aStr ) == COMBOBOX_ENTRY_NOTFOUND )
748     {
749         aStr = aChrCls.uppercase(aStr);
750         for ( sal_Int32 i = 0; i < nEntryCount; i++ )
751         {
752             OUString aEntryText = aChrCls.uppercase(GetEntry(i));
753 
754             if ( aStr == aEntryText )
755             {
756                 SetText( GetEntry( i ) );
757                 break;
758             }
759         }
760     }
761 
762     ComboBox::Modify();
763 }
764 
765 
SvtFontStyleBox(std::unique_ptr<weld::ComboBox> p)766 SvtFontStyleBox::SvtFontStyleBox(std::unique_ptr<weld::ComboBox> p)
767     : m_xComboBox(std::move(p))
768 {
769     //Use the standard texts to get an optimal size and stick to that size.
770     //That should stop the character dialog dancing around.
771     auto nMaxLen = m_xComboBox->get_pixel_size(SvtResId(STR_SVT_STYLE_LIGHT)).Width();
772     nMaxLen = std::max(nMaxLen, m_xComboBox->get_pixel_size(SvtResId(STR_SVT_STYLE_LIGHT_ITALIC)).Width());
773     nMaxLen = std::max(nMaxLen, m_xComboBox->get_pixel_size(SvtResId(STR_SVT_STYLE_NORMAL)).Width());
774     nMaxLen = std::max(nMaxLen, m_xComboBox->get_pixel_size(SvtResId(STR_SVT_STYLE_NORMAL_ITALIC)).Width());
775     nMaxLen = std::max(nMaxLen, m_xComboBox->get_pixel_size(SvtResId(STR_SVT_STYLE_BOLD)).Width());
776     nMaxLen = std::max(nMaxLen, m_xComboBox->get_pixel_size(SvtResId(STR_SVT_STYLE_BOLD_ITALIC)).Width());
777     nMaxLen = std::max(nMaxLen, m_xComboBox->get_pixel_size(SvtResId(STR_SVT_STYLE_BLACK)).Width());
778     nMaxLen = std::max(nMaxLen, m_xComboBox->get_pixel_size(SvtResId(STR_SVT_STYLE_BLACK_ITALIC)).Width());
779     m_xComboBox->set_entry_width_chars(std::ceil(nMaxLen / m_xComboBox->get_approximate_digit_width()));
780 }
781 
Fill(const OUString & rName,const FontList * pList)782 void SvtFontStyleBox::Fill( const OUString& rName, const FontList* pList )
783 {
784     m_xComboBox->freeze();
785     OUString aOldText = m_xComboBox->get_active_text();
786     int nPos = m_xComboBox->get_active();
787     m_xComboBox->clear();
788 
789     // does a font with this name already exist?
790     sal_Handle hFontMetric = pList->GetFirstFontMetric( rName );
791     if ( hFontMetric )
792     {
793         OUString aStyleText;
794         FontWeight  eLastWeight = WEIGHT_DONTKNOW;
795         FontItalic  eLastItalic = ITALIC_NONE;
796         FontWidth   eLastWidth = WIDTH_DONTKNOW;
797         bool        bNormal = false;
798         bool        bItalic = false;
799         bool        bBold = false;
800         bool        bBoldItalic = false;
801         bool        bInsert = false;
802         FontMetric    aFontMetric;
803         while ( hFontMetric )
804         {
805             aFontMetric = FontList::GetFontMetric( hFontMetric );
806 
807             FontWeight  eWeight = aFontMetric.GetWeight();
808             FontItalic  eItalic = aFontMetric.GetItalic();
809             FontWidth   eWidth = aFontMetric.GetWidthType();
810             // Only if the attributes are different, we insert the
811             // Font to avoid double Entries in different languages
812             if ( (eWeight != eLastWeight) || (eItalic != eLastItalic) ||
813                  (eWidth != eLastWidth) )
814             {
815                 if ( bInsert )
816                     m_xComboBox->append_text(aStyleText);
817 
818                 if ( eWeight <= WEIGHT_NORMAL )
819                 {
820                     if ( eItalic != ITALIC_NONE )
821                         bItalic = true;
822                     else
823                         bNormal = true;
824                 }
825                 else
826                 {
827                     if ( eItalic != ITALIC_NONE )
828                         bBoldItalic = true;
829                     else
830                         bBold = true;
831                 }
832 
833                 // For wrong StyleNames we replace this with the correct once
834                 aStyleText = pList->GetStyleName( aFontMetric );
835                 bInsert = m_xComboBox->find_text(aStyleText) == -1;
836                 if ( !bInsert )
837                 {
838                     aStyleText = pList->GetStyleName( eWeight, eItalic );
839                     bInsert = m_xComboBox->find_text(aStyleText) == -1;
840                 }
841 
842                 eLastWeight = eWeight;
843                 eLastItalic = eItalic;
844                 eLastWidth = eWidth;
845             }
846             else
847             {
848                 if ( bInsert )
849                 {
850                     // If we have two names for the same attributes
851                     // we prefer the translated standard names
852                     const OUString& rAttrStyleText = pList->GetStyleName( eWeight, eItalic );
853                     if (rAttrStyleText != aStyleText)
854                     {
855                         OUString aTempStyleText = pList->GetStyleName( aFontMetric );
856                         if (rAttrStyleText == aTempStyleText)
857                             aStyleText = rAttrStyleText;
858                         bInsert = m_xComboBox->find_text(aStyleText) == -1;
859                     }
860                 }
861             }
862 
863             if ( !bItalic && (aStyleText == pList->GetItalicStr()) )
864                 bItalic = true;
865             else if ( !bBold && (aStyleText == pList->GetBoldStr()) )
866                 bBold = true;
867             else if ( !bBoldItalic && (aStyleText == pList->GetBoldItalicStr()) )
868                 bBoldItalic = true;
869 
870             hFontMetric = FontList::GetNextFontMetric( hFontMetric );
871         }
872 
873         if ( bInsert )
874             m_xComboBox->append_text(aStyleText);
875 
876         // certain style as copy
877         if ( bNormal )
878         {
879             if ( !bItalic )
880                 m_xComboBox->append_text(pList->GetItalicStr());
881             if ( !bBold )
882                 m_xComboBox->append_text(pList->GetBoldStr());
883         }
884         if ( !bBoldItalic )
885         {
886             if ( bNormal || bItalic || bBold )
887                 m_xComboBox->append_text(pList->GetBoldItalicStr());
888         }
889         if (!aOldText.isEmpty())
890         {
891             int nFound = m_xComboBox->find_text(aOldText);
892             if (nFound != -1)
893                 m_xComboBox->set_active(nFound);
894             else
895             {
896                 if (nPos >= m_xComboBox->get_count())
897                     m_xComboBox->set_active(0);
898                 else
899                     m_xComboBox->set_active(nPos);
900             }
901         }
902     }
903     else
904     {
905         // insert standard styles if no font
906         m_xComboBox->append_text(pList->GetNormalStr());
907         m_xComboBox->append_text(pList->GetItalicStr());
908         m_xComboBox->append_text(pList->GetBoldStr());
909         m_xComboBox->append_text(pList->GetBoldItalicStr());
910         if (!aOldText.isEmpty())
911         {
912             if (nPos >= m_xComboBox->get_count())
913                 m_xComboBox->set_active(0);
914             else
915                 m_xComboBox->set_active(nPos);
916         }
917     }
918     m_xComboBox->thaw();
919 }
920 
FontSizeBox(vcl::Window * pParent,WinBits nWinSize)921 FontSizeBox::FontSizeBox( vcl::Window* pParent, WinBits nWinSize ) :
922     MetricBox( pParent, nWinSize )
923 {
924     EnableSelectAll();
925     ImplInit();
926 }
927 
makeFontSizeBox(VclPtr<vcl::Window> & rRet,const VclPtr<vcl::Window> & pParent,VclBuilder::stringmap & rMap)928 extern "C" SAL_DLLPUBLIC_EXPORT void makeFontSizeBox(VclPtr<vcl::Window> & rRet, const VclPtr<vcl::Window> & pParent, VclBuilder::stringmap & rMap)
929 {
930     static_assert(std::is_same_v<std::remove_pointer_t<VclBuilder::customMakeWidget>,
931                                  decltype(makeFontSizeBox)>);
932     bool bDropdown = BuilderUtils::extractDropdown(rMap);
933     WinBits nWinBits = WB_LEFT|WB_VCENTER|WB_3DLOOK|WB_TABSTOP;
934     if (bDropdown)
935         nWinBits |= WB_DROPDOWN;
936     VclPtrInstance<FontSizeBox> pListBox(pParent, nWinBits);
937     if (bDropdown)
938         pListBox->EnableAutoSize(true);
939     rRet = pListBox;
940 }
941 
ImplInit()942 void FontSizeBox::ImplInit()
943 {
944     EnableAutocomplete( false );
945 
946     bStdSize        = false;
947 
948     SetShowTrailingZeros( false );
949     SetDecimalDigits( 1 );
950     SetMin( 20 );
951     SetMax( 9999 );
952     SetProminentEntryType( ProminentEntry::MIDDLE );
953 }
954 
Reformat()955 void FontSizeBox::Reformat()
956 {
957     FontSizeNames aFontSizeNames( GetSettings().GetUILanguageTag().getLanguageType() );
958     long nNewValue = aFontSizeNames.Name2Size( GetText() );
959     if ( nNewValue)
960     {
961         mnLastValue = nNewValue;
962         return;
963     }
964 
965     MetricBox::Reformat();
966 }
967 
Fill(const FontMetric * pFontMetric,const FontList * pList)968 void FontSizeBox::Fill( const FontMetric* pFontMetric, const FontList* pList )
969 {
970     // query font sizes
971     const sal_IntPtr* pTempAry;
972     const sal_IntPtr* pAry = nullptr;
973 
974     if( pFontMetric )
975     {
976         pAry = pList->GetSizeAry( *pFontMetric );
977     }
978     else
979     {
980         pAry = FontList::GetStdSizeAry();
981     }
982 
983     // first insert font size names (for simplified/traditional chinese)
984     FontSizeNames aFontSizeNames( GetSettings().GetUILanguageTag().getLanguageType() );
985     if ( pAry == FontList::GetStdSizeAry() )
986     {
987         // for standard sizes we don't need to bother
988         if ( bStdSize && GetEntryCount() && aFontSizeNames.IsEmpty() )
989             return;
990         bStdSize = true;
991     }
992     else
993         bStdSize = false;
994 
995     Selection aSelection = GetSelection();
996     OUString aStr = GetText();
997 
998     Clear();
999     sal_Int32 nPos = 0;
1000 
1001     if ( !aFontSizeNames.IsEmpty() )
1002     {
1003         if ( pAry == FontList::GetStdSizeAry() )
1004         {
1005             // for scalable fonts all font size names
1006             sal_Int32 nCount = aFontSizeNames.Count();
1007             for( sal_Int32 i = 0; i < nCount; i++ )
1008             {
1009                 OUString    aSizeName = aFontSizeNames.GetIndexName( i );
1010                 sal_Int32   nSize = aFontSizeNames.GetIndexSize( i );
1011                 ComboBox::InsertEntry( aSizeName, nPos );
1012                 ComboBox::SetEntryData( nPos, reinterpret_cast<void*>(-nSize) ); // mark as special
1013                 nPos++;
1014             }
1015         }
1016         else
1017         {
1018             // for fixed size fonts only selectable font size names
1019             pTempAry = pAry;
1020             while ( *pTempAry )
1021             {
1022                 OUString aSizeName = aFontSizeNames.Size2Name( *pTempAry );
1023                 if ( !aSizeName.isEmpty() )
1024                 {
1025                     ComboBox::InsertEntry( aSizeName, nPos );
1026                     ComboBox::SetEntryData( nPos, reinterpret_cast<void*>(-(*pTempAry)) ); // mark as special
1027                     nPos++;
1028                 }
1029                 pTempAry++;
1030             }
1031         }
1032     }
1033 
1034     // then insert numerical font size values
1035     pTempAry = pAry;
1036     while ( *pTempAry )
1037     {
1038         InsertValue( *pTempAry, FieldUnit::NONE, nPos );
1039         ComboBox::SetEntryData( nPos, reinterpret_cast<void*>(*pTempAry) );
1040         nPos++;
1041         pTempAry++;
1042     }
1043 
1044     SetText( aStr );
1045     SetSelection( aSelection );
1046 }
1047 
SetValue(sal_Int64 nNewValue,FieldUnit eInUnit)1048 void FontSizeBox::SetValue( sal_Int64 nNewValue, FieldUnit eInUnit )
1049 {
1050     sal_Int64 nTempValue = MetricField::ConvertValue( nNewValue, GetBaseValue(), GetDecimalDigits(), eInUnit, GetUnit() );
1051     FontSizeNames aFontSizeNames( GetSettings().GetUILanguageTag().getLanguageType() );
1052     // conversion loses precision; however font sizes should
1053     // never have a problem with that
1054     OUString aName = aFontSizeNames.Size2Name( static_cast<long>(nTempValue) );
1055     if ( !aName.isEmpty() && (GetEntryPos( aName ) != LISTBOX_ENTRY_NOTFOUND) )
1056     {
1057         mnLastValue = nTempValue;
1058         SetText( aName );
1059         mnFieldValue = mnLastValue;
1060         SetEmptyFieldValueData( false );
1061         return;
1062     }
1063 
1064     MetricBox::SetValue( nNewValue, eInUnit );
1065 }
1066 
SetValue(sal_Int64 nNewValue)1067 void FontSizeBox::SetValue( sal_Int64 nNewValue )
1068 {
1069     SetValue( nNewValue, FieldUnit::NONE );
1070 }
1071 
GetValueFromStringUnit(const OUString & rStr,FieldUnit eOutUnit) const1072 sal_Int64 FontSizeBox::GetValueFromStringUnit(const OUString& rStr, FieldUnit eOutUnit) const
1073 {
1074     FontSizeNames aFontSizeNames( GetSettings().GetUILanguageTag().getLanguageType() );
1075     sal_Int64 nValue = aFontSizeNames.Name2Size( rStr );
1076     if ( nValue )
1077         return MetricField::ConvertValue( nValue, GetBaseValue(), GetDecimalDigits(), GetUnit(), eOutUnit );
1078 
1079     return MetricBox::GetValueFromStringUnit( rStr, eOutUnit );
1080 }
1081 
SvtFontSizeBox(std::unique_ptr<weld::ComboBox> p)1082 SvtFontSizeBox::SvtFontSizeBox(std::unique_ptr<weld::ComboBox> p)
1083     : pFontList(nullptr)
1084     , nSavedValue(0)
1085     , nMin(20)
1086     , nMax(9999)
1087     , eUnit(FieldUnit::POINT)
1088     , nDecimalDigits(1)
1089     , nRelMin(0)
1090     , nRelMax(0)
1091     , nRelStep(0)
1092     , nPtRelMin(0)
1093     , nPtRelMax(0)
1094     , nPtRelStep(0)
1095     , bRelativeMode(false)
1096     , bRelative(false)
1097     , bPtRelative(false)
1098     , bStdSize(false)
1099     , m_xComboBox(std::move(p))
1100 {
1101     m_xComboBox->set_entry_width_chars(std::ceil(m_xComboBox->get_pixel_size(format_number(105)).Width() /
1102                                                  m_xComboBox->get_approximate_digit_width()));
1103     m_xComboBox->connect_focus_out(LINK(this, SvtFontSizeBox, ReformatHdl));
1104     m_xComboBox->connect_changed(LINK(this, SvtFontSizeBox, ModifyHdl));
1105 }
1106 
IMPL_LINK_NOARG(SvtFontSizeBox,ReformatHdl,weld::Widget &,void)1107 IMPL_LINK_NOARG(SvtFontSizeBox, ReformatHdl, weld::Widget&, void)
1108 {
1109     FontSizeNames aFontSizeNames(Application::GetSettings().GetUILanguageTag().getLanguageType());
1110     if (!bRelativeMode || !aFontSizeNames.IsEmpty())
1111     {
1112         if (aFontSizeNames.Name2Size(m_xComboBox->get_active_text()) != 0)
1113             return;
1114     }
1115 
1116     set_value(get_value());
1117 }
1118 
IMPL_LINK(SvtFontSizeBox,ModifyHdl,weld::ComboBox &,rBox,void)1119 IMPL_LINK(SvtFontSizeBox, ModifyHdl, weld::ComboBox&, rBox, void)
1120 {
1121     if (bRelativeMode)
1122     {
1123         OUString aStr = comphelper::string::stripStart(rBox.get_active_text(), ' ');
1124 
1125         bool bNewMode = bRelative;
1126         bool bOldPtRelMode = bPtRelative;
1127 
1128         if ( bRelative )
1129         {
1130             bPtRelative = false;
1131             const sal_Unicode* pStr = aStr.getStr();
1132             while ( *pStr )
1133             {
1134                 if ( ((*pStr < '0') || (*pStr > '9')) && (*pStr != '%') && !unicode::isSpace(*pStr) )
1135                 {
1136                     if ( ('-' == *pStr || '+' == *pStr) && !bPtRelative )
1137                         bPtRelative = true;
1138                     else if ( bPtRelative && 'p' == *pStr && 't' == *++pStr )
1139                         ;
1140                     else
1141                     {
1142                         bNewMode = false;
1143                         break;
1144                     }
1145                 }
1146                 pStr++;
1147             }
1148         }
1149         else if (!aStr.isEmpty())
1150         {
1151             if ( -1 != aStr.indexOf('%') )
1152             {
1153                 bNewMode = true;
1154                 bPtRelative = false;
1155             }
1156 
1157             if ( '-' == aStr[0] || '+' == aStr[0] )
1158             {
1159                 bNewMode = true;
1160                 bPtRelative = true;
1161             }
1162         }
1163 
1164         if ( bNewMode != bRelative || bPtRelative != bOldPtRelMode )
1165             SetRelative( bNewMode );
1166     }
1167     m_aChangeHdl.Call(rBox);
1168 }
1169 
Fill(const FontMetric * pFontMetric,const FontList * pList)1170 void SvtFontSizeBox::Fill( const FontMetric* pFontMetric, const FontList* pList )
1171 {
1172     // remember for relative mode
1173     pFontList = pList;
1174 
1175     // no font sizes need to be set for relative mode
1176     if ( bRelative )
1177         return;
1178 
1179     // query font sizes
1180     const sal_IntPtr* pTempAry;
1181     const sal_IntPtr* pAry = nullptr;
1182 
1183     if( pFontMetric )
1184     {
1185         aFontMetric = *pFontMetric;
1186         pAry = pList->GetSizeAry( *pFontMetric );
1187     }
1188     else
1189     {
1190         pAry = FontList::GetStdSizeAry();
1191     }
1192 
1193     // first insert font size names (for simplified/traditional chinese)
1194     FontSizeNames aFontSizeNames( Application::GetSettings().GetUILanguageTag().getLanguageType() );
1195     if ( pAry == FontList::GetStdSizeAry() )
1196     {
1197         // for standard sizes we don't need to bother
1198         if (bStdSize && m_xComboBox->get_count() && aFontSizeNames.IsEmpty())
1199             return;
1200         bStdSize = true;
1201     }
1202     else
1203         bStdSize = false;
1204 
1205     int nSelectionStart, nSelectionEnd;
1206     m_xComboBox->get_entry_selection_bounds(nSelectionStart, nSelectionEnd);
1207     OUString aStr = m_xComboBox->get_active_text();
1208 
1209     m_xComboBox->freeze();
1210     m_xComboBox->clear();
1211     int nPos = 0;
1212 
1213     if ( !aFontSizeNames.IsEmpty() )
1214     {
1215         if ( pAry == FontList::GetStdSizeAry() )
1216         {
1217             // for scalable fonts all font size names
1218             sal_uLong nCount = aFontSizeNames.Count();
1219             for( sal_uLong i = 0; i < nCount; i++ )
1220             {
1221                 OUString    aSizeName = aFontSizeNames.GetIndexName( i );
1222                 sal_IntPtr  nSize = aFontSizeNames.GetIndexSize( i );
1223                 OUString sId(OUString::number(-nSize)); // mark as special
1224                 m_xComboBox->insert(nPos, aSizeName, &sId, nullptr, nullptr);
1225                 nPos++;
1226             }
1227         }
1228         else
1229         {
1230             // for fixed size fonts only selectable font size names
1231             pTempAry = pAry;
1232             while ( *pTempAry )
1233             {
1234                 OUString aSizeName = aFontSizeNames.Size2Name( *pTempAry );
1235                 if ( !aSizeName.isEmpty() )
1236                 {
1237                     OUString sId(OUString::number(-(*pTempAry))); // mark as special
1238                     m_xComboBox->insert(nPos, aSizeName, &sId, nullptr, nullptr);
1239                     nPos++;
1240                 }
1241                 pTempAry++;
1242             }
1243         }
1244     }
1245 
1246     // then insert numerical font size values
1247     pTempAry = pAry;
1248     while (*pTempAry)
1249     {
1250         InsertValue(*pTempAry);
1251         ++pTempAry;
1252     }
1253 
1254     m_xComboBox->set_entry_text(aStr);
1255     m_xComboBox->select_entry_region(nSelectionStart, nSelectionEnd);
1256     m_xComboBox->thaw();
1257 }
1258 
EnableRelativeMode(sal_uInt16 nNewMin,sal_uInt16 nNewMax,sal_uInt16 nStep)1259 void SvtFontSizeBox::EnableRelativeMode( sal_uInt16 nNewMin, sal_uInt16 nNewMax, sal_uInt16 nStep )
1260 {
1261     bRelativeMode = true;
1262     nRelMin       = nNewMin;
1263     nRelMax       = nNewMax;
1264     nRelStep      = nStep;
1265     SetUnit(FieldUnit::POINT);
1266 }
1267 
EnablePtRelativeMode(short nNewMin,short nNewMax,short nStep)1268 void SvtFontSizeBox::EnablePtRelativeMode( short nNewMin, short nNewMax, short nStep )
1269 {
1270     bRelativeMode = true;
1271     nPtRelMin     = nNewMin;
1272     nPtRelMax     = nNewMax;
1273     nPtRelStep    = nStep;
1274     SetUnit(FieldUnit::POINT);
1275 }
1276 
InsertValue(int i)1277 void SvtFontSizeBox::InsertValue(int i)
1278 {
1279     OUString sNumber(OUString::number(i));
1280     m_xComboBox->append(sNumber, format_number(i));
1281 }
1282 
SetRelative(bool bNewRelative)1283 void SvtFontSizeBox::SetRelative( bool bNewRelative )
1284 {
1285     if ( !bRelativeMode )
1286         return;
1287 
1288     int nSelectionStart, nSelectionEnd;
1289     m_xComboBox->get_entry_selection_bounds(nSelectionStart, nSelectionEnd);
1290     OUString aStr = comphelper::string::stripStart(m_xComboBox->get_active_text(), ' ');
1291 
1292     if (bNewRelative)
1293     {
1294         bRelative = true;
1295         bStdSize = false;
1296 
1297         m_xComboBox->clear();
1298 
1299         if (bPtRelative)
1300         {
1301             SetDecimalDigits( 1 );
1302             SetRange(nPtRelMin, nPtRelMax);
1303             SetUnit(FieldUnit::POINT);
1304 
1305             short i = nPtRelMin, n = 0;
1306             // JP 30.06.98: more than 100 values are not useful
1307             while ( i <= nPtRelMax && n++ < 100 )
1308             {
1309                 InsertValue( i );
1310                 i = i + nPtRelStep;
1311             }
1312         }
1313         else
1314         {
1315             SetDecimalDigits(0);
1316             SetRange(nRelMin, nRelMax);
1317             SetUnit(FieldUnit::PERCENT);
1318 
1319             sal_uInt16 i = nRelMin;
1320             while ( i <= nRelMax )
1321             {
1322                 InsertValue( i );
1323                 i = i + nRelStep;
1324             }
1325         }
1326     }
1327     else
1328     {
1329         if (pFontList)
1330             m_xComboBox->clear();
1331         bRelative = bPtRelative = false;
1332         SetDecimalDigits(1);
1333         SetRange(20, 9999);
1334         SetUnit(FieldUnit::POINT);
1335         if ( pFontList)
1336             Fill( &aFontMetric, pFontList );
1337     }
1338 
1339     m_xComboBox->set_entry_text(aStr);
1340     m_xComboBox->select_entry_region(nSelectionStart, nSelectionEnd);
1341 }
1342 
format_number(int nValue) const1343 OUString SvtFontSizeBox::format_number(int nValue) const
1344 {
1345     OUString sRet;
1346 
1347     //pawn percent off to icu to decide whether percent is separated from its number for this locale
1348     if (eUnit == FieldUnit::PERCENT)
1349     {
1350         double fValue = nValue;
1351         fValue /= weld::SpinButton::Power10(nDecimalDigits);
1352         sRet = unicode::formatPercent(fValue, Application::GetSettings().GetUILanguageTag());
1353     }
1354     else
1355     {
1356         const SvtSysLocale aSysLocale;
1357         const LocaleDataWrapper& rLocaleData = aSysLocale.GetLocaleData();
1358         sRet = rLocaleData.getNum(nValue, nDecimalDigits, true, false);
1359         if (eUnit != FieldUnit::NONE && eUnit != FieldUnit::DEGREE)
1360             sRet += " ";
1361         assert(eUnit != FieldUnit::PERCENT);
1362         sRet += weld::MetricSpinButton::MetricToString(eUnit);
1363     }
1364 
1365     if (bRelativeMode && bPtRelative && (0 <= nValue) && !sRet.isEmpty())
1366         sRet = "+" + sRet;
1367 
1368     return sRet;
1369 }
1370 
SetValue(int nNewValue,FieldUnit eInUnit)1371 void SvtFontSizeBox::SetValue(int nNewValue, FieldUnit eInUnit)
1372 {
1373     auto nTempValue = MetricField::ConvertValue(nNewValue, 0, GetDecimalDigits(), eInUnit, GetUnit());
1374     if (nTempValue < nMin)
1375         nTempValue = nMin;
1376     else if (nTempValue > nMax)
1377         nTempValue = nMax;
1378     if (!bRelative)
1379     {
1380         FontSizeNames aFontSizeNames(Application::GetSettings().GetUILanguageTag().getLanguageType());
1381         // conversion loses precision; however font sizes should
1382         // never have a problem with that
1383         OUString aName = aFontSizeNames.Size2Name(nTempValue);
1384         if (!aName.isEmpty() && m_xComboBox->find_text(aName) != -1)
1385         {
1386             m_xComboBox->set_active_text(aName);
1387             return;
1388         }
1389     }
1390     OUString aResult = format_number(nTempValue);
1391     const int nFound = m_xComboBox->find_text(aResult);
1392     if (nFound != -1)
1393         m_xComboBox->set_active(nFound);
1394     else
1395         m_xComboBox->set_entry_text(aResult);
1396 }
1397 
set_value(int nNewValue)1398 void SvtFontSizeBox::set_value(int nNewValue)
1399 {
1400     SetValue(nNewValue, eUnit);
1401 }
1402 
get_value() const1403 int SvtFontSizeBox::get_value() const
1404 {
1405     OUString aStr = m_xComboBox->get_active_text();
1406     if (!bRelative)
1407     {
1408         FontSizeNames aFontSizeNames(Application::GetSettings().GetUILanguageTag().getLanguageType());
1409         auto nValue = aFontSizeNames.Name2Size(aStr);
1410         if (nValue)
1411             return MetricField::ConvertValue(nValue, 0, GetDecimalDigits(), GetUnit(), GetUnit());
1412     }
1413 
1414     const SvtSysLocale aSysLocale;
1415     const LocaleDataWrapper& rLocaleData = aSysLocale.GetLocaleData();
1416     double fResult(0.0);
1417     MetricFormatter::TextToValue(aStr, fResult, 0, GetDecimalDigits(), rLocaleData, GetUnit());
1418     if (!aStr.isEmpty())
1419     {
1420         if (fResult < nMin)
1421             fResult = nMin;
1422         else if (fResult > nMax)
1423             fResult = nMax;
1424     }
1425     return fResult;
1426 }
1427 
GetSelectEntryStyle() const1428 SvxBorderLineStyle SvtLineListBox::GetSelectEntryStyle() const
1429 {
1430     if (m_xLineSet->IsNoSelection())
1431         return SvxBorderLineStyle::NONE;
1432     auto nId = m_xLineSet->GetSelectedItemId();
1433     return static_cast<SvxBorderLineStyle>(nId - 1);
1434 }
1435 
1436 namespace
1437 {
getPreviewSize(const weld::Widget & rControl)1438     Size getPreviewSize(const weld::Widget& rControl)
1439     {
1440         return Size(rControl.get_approximate_digit_width() * 15, rControl.get_text_height());
1441     }
1442 }
1443 
ImpGetLine(long nLine1,long nLine2,long nDistance,Color aColor1,Color aColor2,Color aColorDist,SvxBorderLineStyle nStyle,BitmapEx & rBmp)1444 void SvtLineListBox::ImpGetLine( long nLine1, long nLine2, long nDistance,
1445                             Color aColor1, Color aColor2, Color aColorDist,
1446                             SvxBorderLineStyle nStyle, BitmapEx& rBmp )
1447 {
1448     Size aSize(getPreviewSize(*m_xControl));
1449 
1450     // SourceUnit to Twips
1451     if ( eSourceUnit == FieldUnit::POINT )
1452     {
1453         nLine1      /= 5;
1454         nLine2      /= 5;
1455         nDistance   /= 5;
1456     }
1457 
1458     // Paint the lines
1459     aSize = aVirDev->PixelToLogic( aSize );
1460     long nPix = aVirDev->PixelToLogic( Size( 0, 1 ) ).Height();
1461     sal_uInt32 n1 = nLine1;
1462     sal_uInt32 n2 = nLine2;
1463     long nDist  = nDistance;
1464     n1 += nPix-1;
1465     n1 -= n1%nPix;
1466     if ( n2 )
1467     {
1468         nDist += nPix-1;
1469         nDist -= nDist%nPix;
1470         n2    += nPix-1;
1471         n2    -= n2%nPix;
1472     }
1473     long nVirHeight = n1+nDist+n2;
1474     if ( nVirHeight > aSize.Height() )
1475         aSize.setHeight( nVirHeight );
1476     // negative width should not be drawn
1477     if ( aSize.Width() <= 0 )
1478         return;
1479 
1480     Size aVirSize = aVirDev->LogicToPixel( aSize );
1481     if ( aVirDev->GetOutputSizePixel() != aVirSize )
1482         aVirDev->SetOutputSizePixel( aVirSize );
1483     aVirDev->SetFillColor( aColorDist );
1484     aVirDev->DrawRect( tools::Rectangle( Point(), aSize ) );
1485 
1486     aVirDev->SetFillColor( aColor1 );
1487 
1488     double y1 = double( n1 ) / 2;
1489     svtools::DrawLine( *aVirDev, basegfx::B2DPoint( 0, y1 ), basegfx::B2DPoint( aSize.Width( ), y1 ), n1, nStyle );
1490 
1491     if ( n2 )
1492     {
1493         double y2 =  n1 + nDist + double( n2 ) / 2;
1494         aVirDev->SetFillColor( aColor2 );
1495         svtools::DrawLine( *aVirDev, basegfx::B2DPoint( 0, y2 ), basegfx::B2DPoint( aSize.Width(), y2 ), n2, SvxBorderLineStyle::SOLID );
1496     }
1497     rBmp = aVirDev->GetBitmapEx( Point(), Size( aSize.Width(), n1+nDist+n2 ) );
1498 }
1499 
1500 namespace
1501 {
GetLineStyleName(SvxBorderLineStyle eStyle)1502     OUString GetLineStyleName(SvxBorderLineStyle eStyle)
1503     {
1504         OUString sRet;
1505         for (sal_uInt32 i = 0; i < SAL_N_ELEMENTS(RID_SVXSTR_BORDERLINE); ++i)
1506         {
1507             if (eStyle == RID_SVXSTR_BORDERLINE[i].second)
1508             {
1509                 sRet = SvtResId(RID_SVXSTR_BORDERLINE[i].first);
1510                 break;
1511             }
1512         }
1513         return sRet;
1514     }
1515 }
1516 
SvtLineListBox(std::unique_ptr<weld::MenuButton> pControl)1517 SvtLineListBox::SvtLineListBox(std::unique_ptr<weld::MenuButton> pControl)
1518     : m_xControl(std::move(pControl))
1519     , m_xBuilder(Application::CreateBuilder(m_xControl.get(), "svt/ui/linewindow.ui"))
1520     , m_xTopLevel(m_xBuilder->weld_widget("line_popup_window"))
1521     , m_xNoneButton(m_xBuilder->weld_button("none_line_button"))
1522     , m_xLineSet(new SvtValueSet(nullptr))
1523     , m_xLineSetWin(new weld::CustomWeld(*m_xBuilder, "lineset", *m_xLineSet))
1524     , m_nWidth( 5 )
1525     , aVirDev(VclPtr<VirtualDevice>::Create())
1526     , aColor(COL_BLACK)
1527     , maPaintCol(COL_BLACK)
1528 {
1529     const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
1530     m_xLineSet->SetStyle(WinBits(WB_FLATVALUESET | WB_NO_DIRECTSELECT | WB_TABSTOP));
1531     m_xLineSet->SetItemHeight(rStyleSettings.GetListBoxPreviewDefaultPixelSize().Height() + 1);
1532     m_xLineSet->SetColCount(1);
1533     m_xLineSet->SetSelectHdl(LINK(this, SvtLineListBox, ValueSelectHdl));
1534 
1535     m_xNoneButton->connect_clicked(LINK(this, SvtLineListBox, NoneHdl));
1536 
1537     m_xTopLevel->connect_focus_in(LINK(this, SvtLineListBox, FocusHdl));
1538     m_xControl->set_popover(m_xTopLevel.get());
1539     m_xControl->connect_toggled(LINK(this, SvtLineListBox, ToggleHdl));
1540 
1541     // lock size to these maxes height/width so it doesn't jump around in size
1542     m_xControl->set_label(GetLineStyleName(SvxBorderLineStyle::NONE));
1543     Size aNonePrefSize = m_xControl->get_preferred_size();
1544     m_xControl->set_label("");
1545     aVirDev->SetOutputSizePixel(getPreviewSize(*m_xControl));
1546     m_xControl->set_image(aVirDev);
1547     Size aSolidPrefSize = m_xControl->get_preferred_size();
1548     m_xControl->set_size_request(std::max(aNonePrefSize.Width(), aSolidPrefSize.Width()),
1549                                  std::max(aNonePrefSize.Height(), aSolidPrefSize.Height()));
1550 
1551     eSourceUnit = FieldUnit::POINT;
1552 
1553     aVirDev->SetLineColor();
1554     aVirDev->SetMapMode(MapMode(MapUnit::MapTwip));
1555 
1556     UpdatePaintLineColor();
1557 }
1558 
IMPL_LINK_NOARG(SvtLineListBox,FocusHdl,weld::Widget &,void)1559 IMPL_LINK_NOARG(SvtLineListBox, FocusHdl, weld::Widget&, void)
1560 {
1561     if (GetSelectEntryStyle() == SvxBorderLineStyle::NONE)
1562         m_xNoneButton->grab_focus();
1563     else
1564         m_xLineSet->GrabFocus();
1565 }
1566 
IMPL_LINK(SvtLineListBox,ToggleHdl,weld::ToggleButton &,rButton,void)1567 IMPL_LINK(SvtLineListBox, ToggleHdl, weld::ToggleButton&, rButton, void)
1568 {
1569     if (rButton.get_active())
1570         FocusHdl(*m_xTopLevel);
1571 }
1572 
IMPL_LINK_NOARG(SvtLineListBox,NoneHdl,weld::Button &,void)1573 IMPL_LINK_NOARG(SvtLineListBox, NoneHdl, weld::Button&, void)
1574 {
1575     SelectEntry(SvxBorderLineStyle::NONE);
1576     ValueSelectHdl(nullptr);
1577 }
1578 
~SvtLineListBox()1579 SvtLineListBox::~SvtLineListBox()
1580 {
1581 }
1582 
GetStylePos(sal_Int32 nListPos)1583 sal_Int32 SvtLineListBox::GetStylePos( sal_Int32 nListPos )
1584 {
1585     sal_Int32 nPos = LISTBOX_ENTRY_NOTFOUND;
1586     --nListPos;
1587 
1588     sal_Int32 n = 0;
1589     size_t i = 0;
1590     size_t nCount = m_vLineList.size();
1591     while ( nPos == LISTBOX_ENTRY_NOTFOUND && i < nCount )
1592     {
1593         if ( nListPos == n )
1594             nPos = static_cast<sal_Int32>(i);
1595         n++;
1596         i++;
1597     }
1598 
1599     return nPos;
1600 }
1601 
SelectEntry(SvxBorderLineStyle nStyle)1602 void SvtLineListBox::SelectEntry(SvxBorderLineStyle nStyle)
1603 {
1604     if (nStyle == SvxBorderLineStyle::NONE)
1605         m_xLineSet->SetNoSelection();
1606     else
1607         m_xLineSet->SelectItem(static_cast<sal_Int16>(nStyle) + 1);
1608     UpdatePreview();
1609 }
1610 
InsertEntry(const BorderWidthImpl & rWidthImpl,SvxBorderLineStyle nStyle,long nMinWidth,ColorFunc pColor1Fn,ColorFunc pColor2Fn,ColorDistFunc pColorDistFn)1611 void SvtLineListBox::InsertEntry(
1612     const BorderWidthImpl& rWidthImpl, SvxBorderLineStyle nStyle, long nMinWidth,
1613     ColorFunc pColor1Fn, ColorFunc pColor2Fn, ColorDistFunc pColorDistFn )
1614 {
1615     m_vLineList.emplace_back(new ImpLineListData(
1616         rWidthImpl, nStyle, nMinWidth, pColor1Fn, pColor2Fn, pColorDistFn));
1617 }
1618 
UpdatePaintLineColor()1619 void SvtLineListBox::UpdatePaintLineColor()
1620 {
1621     const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
1622     Color aNewCol(rSettings.GetWindowColor().IsDark() ? rSettings.GetLabelTextColor() : aColor);
1623 
1624     bool bRet = aNewCol != maPaintCol;
1625 
1626     if( bRet )
1627         maPaintCol = aNewCol;
1628 }
1629 
UpdateEntries()1630 void SvtLineListBox::UpdateEntries()
1631 {
1632     UpdatePaintLineColor( );
1633 
1634     SvxBorderLineStyle eSelected = GetSelectEntryStyle();
1635 
1636     // Remove the old entries
1637     m_xLineSet->Clear();
1638 
1639     // Add the new entries based on the defined width
1640 
1641     sal_uInt16 n = 0;
1642     sal_uInt16 nCount = m_vLineList.size( );
1643     while ( n < nCount )
1644     {
1645         auto& pData = m_vLineList[ n ];
1646         BitmapEx aBmp;
1647         ImpGetLine( pData->GetLine1ForWidth( m_nWidth ),
1648                 pData->GetLine2ForWidth( m_nWidth ),
1649                 pData->GetDistForWidth( m_nWidth ),
1650                 GetColorLine1(m_xLineSet->GetItemCount()),
1651                 GetColorLine2(m_xLineSet->GetItemCount()),
1652                 GetColorDist(m_xLineSet->GetItemCount()),
1653                 pData->GetStyle(), aBmp );
1654         sal_Int16 nItemId = static_cast<sal_Int16>(pData->GetStyle()) + 1;
1655         m_xLineSet->InsertItem(nItemId, Image(aBmp), GetLineStyleName(pData->GetStyle()));
1656         if (pData->GetStyle() == eSelected)
1657             m_xLineSet->SelectItem(nItemId);
1658         n++;
1659     }
1660 
1661     m_xLineSet->SetOptimalSize();
1662 }
1663 
GetColorLine1(sal_Int32 nPos)1664 Color SvtLineListBox::GetColorLine1( sal_Int32 nPos )
1665 {
1666     sal_Int32 nStyle = GetStylePos( nPos );
1667     if (nStyle == LISTBOX_ENTRY_NOTFOUND)
1668         return GetPaintColor( );
1669     auto& pData = m_vLineList[ nStyle ];
1670     return pData->GetColorLine1( GetColor( ) );
1671 }
1672 
GetColorLine2(sal_Int32 nPos)1673 Color SvtLineListBox::GetColorLine2( sal_Int32 nPos )
1674 {
1675     sal_Int32 nStyle = GetStylePos(nPos);
1676     if (nStyle == LISTBOX_ENTRY_NOTFOUND)
1677         return GetPaintColor( );
1678     auto& pData = m_vLineList[ nStyle ];
1679     return pData->GetColorLine2( GetColor( ) );
1680 }
1681 
GetColorDist(sal_Int32 nPos)1682 Color SvtLineListBox::GetColorDist( sal_Int32 nPos )
1683 {
1684     const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
1685     Color rResult = rSettings.GetFieldColor();
1686 
1687     sal_Int32 nStyle = GetStylePos( nPos );
1688     if (nStyle == LISTBOX_ENTRY_NOTFOUND)
1689         return rResult;
1690     auto& pData = m_vLineList[ nStyle ];
1691     return pData->GetColorDist( GetColor( ), rResult );
1692 }
1693 
IMPL_LINK_NOARG(SvtLineListBox,StyleUpdated,weld::Widget &,void)1694 IMPL_LINK_NOARG(SvtLineListBox, StyleUpdated, weld::Widget&, void)
1695 {
1696     UpdateEntries();
1697 }
1698 
IMPL_LINK_NOARG(SvtLineListBox,ValueSelectHdl,SvtValueSet *,void)1699 IMPL_LINK_NOARG(SvtLineListBox, ValueSelectHdl, SvtValueSet*, void)
1700 {
1701     maSelectHdl.Call(*this);
1702     UpdatePreview();
1703     if (m_xControl->get_active())
1704         m_xControl->set_active(false);
1705 }
1706 
UpdatePreview()1707 void SvtLineListBox::UpdatePreview()
1708 {
1709     SvxBorderLineStyle eStyle = GetSelectEntryStyle();
1710     for (sal_uInt32 i = 0; i < SAL_N_ELEMENTS(RID_SVXSTR_BORDERLINE); ++i)
1711     {
1712         if (eStyle == RID_SVXSTR_BORDERLINE[i].second)
1713         {
1714             m_xControl->set_label(SvtResId(RID_SVXSTR_BORDERLINE[i].first));
1715             break;
1716         }
1717     }
1718 
1719     if (eStyle == SvxBorderLineStyle::NONE)
1720     {
1721         m_xControl->set_image(nullptr);
1722         m_xControl->set_label(GetLineStyleName(SvxBorderLineStyle::NONE));
1723     }
1724     else
1725     {
1726         Image aImage(m_xLineSet->GetItemImage(m_xLineSet->GetSelectedItemId()));
1727         m_xControl->set_label("");
1728         const auto nPos = (aVirDev->GetOutputSizePixel().Height() - aImage.GetSizePixel().Height()) / 2;
1729         aVirDev->Push(PushFlags::MAPMODE);
1730         aVirDev->SetMapMode(MapMode(MapUnit::MapPixel));
1731         aVirDev->Erase();
1732         aVirDev->DrawImage(Point(0, nPos), aImage);
1733         m_xControl->set_image(aVirDev.get());
1734         aVirDev->Pop();
1735     }
1736 }
1737 
SvtCalendarBox(std::unique_ptr<weld::MenuButton> pControl)1738 SvtCalendarBox::SvtCalendarBox(std::unique_ptr<weld::MenuButton> pControl)
1739     : m_xControl(std::move(pControl))
1740     , m_xBuilder(Application::CreateBuilder(m_xControl.get(), "svt/ui/datewindow.ui"))
1741     , m_xTopLevel(m_xBuilder->weld_widget("date_popup_window"))
1742     , m_xCalendar(m_xBuilder->weld_calendar("date"))
1743 {
1744     m_xControl->set_popover(m_xTopLevel.get());
1745     m_xCalendar->connect_selected(LINK(this, SvtCalendarBox, SelectHdl));
1746     m_xCalendar->connect_activated(LINK(this, SvtCalendarBox, ActivateHdl));
1747 }
1748 
set_date(const Date & rDate)1749 void SvtCalendarBox::set_date(const Date& rDate)
1750 {
1751     m_xCalendar->set_date(rDate);
1752     set_label_from_date();
1753 }
1754 
set_label_from_date()1755 void SvtCalendarBox::set_label_from_date()
1756 {
1757     const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
1758     m_xControl->set_label(rLocaleData.getDate(m_xCalendar->get_date()));
1759 }
1760 
IMPL_LINK_NOARG(SvtCalendarBox,SelectHdl,weld::Calendar &,void)1761 IMPL_LINK_NOARG(SvtCalendarBox, SelectHdl, weld::Calendar&, void)
1762 {
1763     set_label_from_date();
1764 }
1765 
IMPL_LINK_NOARG(SvtCalendarBox,ActivateHdl,weld::Calendar &,void)1766 IMPL_LINK_NOARG(SvtCalendarBox, ActivateHdl, weld::Calendar&, void)
1767 {
1768     if (m_xControl->get_active())
1769         m_xControl->set_active(false);
1770     m_aActivatedHdl.Call(*this);
1771 }
1772 
~SvtCalendarBox()1773 SvtCalendarBox::~SvtCalendarBox()
1774 {
1775 }
1776 
1777 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1778