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