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