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