Resolves: tdf#141441 get and set selected entry when "unfrozen"
[LibreOffice.git] / svtools / source / control / ctrlbox.cxx
blob0b1c67e31980b7994b303e235e2a098186695f8c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
21 #include <config_folders.h>
23 #include <comphelper/lok.hxx>
24 #include <i18nutil/unicode.hxx>
25 #include <officecfg/Office/Common.hxx>
26 #include <tools/stream.hxx>
27 #include <vcl/customweld.hxx>
28 #include <vcl/event.hxx>
29 #include <vcl/svapp.hxx>
30 #include <vcl/fieldvalues.hxx>
31 #include <vcl/settings.hxx>
32 #include <vcl/image.hxx>
33 #include <vcl/virdev.hxx>
34 #include <vcl/weldutils.hxx>
35 #include <rtl/math.hxx>
36 #include <sal/macros.h>
37 #include <sal/log.hxx>
38 #include <comphelper/string.hxx>
39 #include <unotools/localedatawrapper.hxx>
40 #include <unotools/syslocale.hxx>
42 #include <svtools/borderline.hxx>
43 #include <svtools/sampletext.hxx>
44 #include <svtools/svtresid.hxx>
45 #include <svtools/strings.hrc>
46 #include <svtools/ctrlbox.hxx>
47 #include <svtools/ctrltool.hxx>
48 #include <svtools/borderhelper.hxx>
49 #include <svtools/valueset.hxx>
51 #include <basegfx/polygon/b2dpolygon.hxx>
52 #include <basegfx/polygon/b2dpolygontools.hxx>
53 #include <editeng/borderline.hxx>
55 #include <rtl/bootstrap.hxx>
57 #include <borderline.hrc>
59 #include <stdio.h>
61 #define IMGOUTERTEXTSPACE 5
62 #define EXTRAFONTSIZE 5
63 #define GAPTOEXTRAPREVIEW 10
64 #define MINGAPWIDTH 2
66 constexpr OUStringLiteral FONTNAMEBOXMRUENTRIESFILE = u"/user/config/fontnameboxmruentries";
69 BorderWidthImpl::BorderWidthImpl( BorderWidthImplFlags nFlags, double nRate1, double nRate2, double nRateGap ):
70 m_nFlags( nFlags ),
71 m_nRate1( nRate1 ),
72 m_nRate2( nRate2 ),
73 m_nRateGap( nRateGap )
77 bool BorderWidthImpl::operator== ( const BorderWidthImpl& r ) const
79 return ( m_nFlags == r.m_nFlags ) &&
80 ( m_nRate1 == r.m_nRate1 ) &&
81 ( m_nRate2 == r.m_nRate2 ) &&
82 ( m_nRateGap == r.m_nRateGap );
85 tools::Long BorderWidthImpl::GetLine1( tools::Long nWidth ) const
87 tools::Long result = static_cast<tools::Long>(m_nRate1);
88 if ( m_nFlags & BorderWidthImplFlags::CHANGE_LINE1 )
90 tools::Long const nConstant2 = (m_nFlags & BorderWidthImplFlags::CHANGE_LINE2) ? 0 : m_nRate2;
91 tools::Long const nConstantD = (m_nFlags & BorderWidthImplFlags::CHANGE_DIST ) ? 0 : m_nRateGap;
92 result = std::max<tools::Long>(0,
93 static_cast<tools::Long>((m_nRate1 * nWidth) + 0.5)
94 - (nConstant2 + nConstantD));
95 if (result == 0 && m_nRate1 > 0.0 && nWidth > 0)
96 { // fdo#51777: hack to essentially treat 1 twip DOUBLE border
97 result = 1; // as 1 twip SINGLE border
100 return result;
103 tools::Long BorderWidthImpl::GetLine2( tools::Long nWidth ) const
105 tools::Long result = static_cast<tools::Long>(m_nRate2);
106 if ( m_nFlags & BorderWidthImplFlags::CHANGE_LINE2)
108 tools::Long const nConstant1 = (m_nFlags & BorderWidthImplFlags::CHANGE_LINE1) ? 0 : m_nRate1;
109 tools::Long const nConstantD = (m_nFlags & BorderWidthImplFlags::CHANGE_DIST ) ? 0 : m_nRateGap;
110 result = std::max<tools::Long>(0,
111 static_cast<tools::Long>((m_nRate2 * nWidth) + 0.5)
112 - (nConstant1 + nConstantD));
114 return result;
117 tools::Long BorderWidthImpl::GetGap( tools::Long nWidth ) const
119 tools::Long result = static_cast<tools::Long>(m_nRateGap);
120 if ( m_nFlags & BorderWidthImplFlags::CHANGE_DIST )
122 tools::Long const nConstant1 = (m_nFlags & BorderWidthImplFlags::CHANGE_LINE1) ? 0 : m_nRate1;
123 tools::Long const nConstant2 = (m_nFlags & BorderWidthImplFlags::CHANGE_LINE2) ? 0 : m_nRate2;
124 result = std::max<tools::Long>(0,
125 static_cast<tools::Long>((m_nRateGap * nWidth) + 0.5)
126 - (nConstant1 + nConstant2));
129 // Avoid having too small distances (less than 0.1pt)
130 if ( result < MINGAPWIDTH && m_nRate1 > 0 && m_nRate2 > 0 )
131 result = MINGAPWIDTH;
133 return result;
136 static double lcl_getGuessedWidth( tools::Long nTested, double nRate, bool bChanging )
138 double nWidth = -1.0;
139 if ( bChanging )
140 nWidth = double( nTested ) / nRate;
141 else
143 if ( rtl::math::approxEqual(double( nTested ), nRate) )
144 nWidth = nRate;
147 return nWidth;
150 tools::Long BorderWidthImpl::GuessWidth( tools::Long nLine1, tools::Long nLine2, tools::Long nGap )
152 std::vector< double > aToCompare;
153 bool bInvalid = false;
155 bool bLine1Change = bool( m_nFlags & BorderWidthImplFlags::CHANGE_LINE1 );
156 double nWidth1 = lcl_getGuessedWidth( nLine1, m_nRate1, bLine1Change );
157 if ( bLine1Change )
158 aToCompare.push_back( nWidth1 );
159 else if (nWidth1 < 0)
160 bInvalid = true;
162 bool bLine2Change = bool( m_nFlags & BorderWidthImplFlags::CHANGE_LINE2 );
163 double nWidth2 = lcl_getGuessedWidth( nLine2, m_nRate2, bLine2Change );
164 if ( bLine2Change )
165 aToCompare.push_back( nWidth2 );
166 else if (nWidth2 < 0)
167 bInvalid = true;
169 bool bGapChange = bool( m_nFlags & BorderWidthImplFlags::CHANGE_DIST );
170 double nWidthGap = lcl_getGuessedWidth( nGap, m_nRateGap, bGapChange );
171 if ( bGapChange && nGap >= MINGAPWIDTH )
172 aToCompare.push_back( nWidthGap );
173 else if ( !bGapChange && nWidthGap < 0 )
174 bInvalid = true;
176 // non-constant line width factors must sum to 1
177 assert((((bLine1Change) ? m_nRate1 : 0) +
178 ((bLine2Change) ? m_nRate2 : 0) +
179 ((bGapChange) ? m_nRateGap : 0)) - 1.0 < 0.00001 );
181 double nWidth = 0.0;
182 if ( (!bInvalid) && (!aToCompare.empty()) )
184 nWidth = *aToCompare.begin();
185 for (auto const& elem : aToCompare)
187 bInvalid = ( nWidth != elem );
188 if (bInvalid)
189 break;
191 nWidth = bInvalid ? 0.0 : nLine1 + nLine2 + nGap;
194 return nWidth;
197 static void lclDrawPolygon( OutputDevice& rDev, const basegfx::B2DPolygon& rPolygon, tools::Long nWidth, SvxBorderLineStyle nDashing )
199 AntialiasingFlags nOldAA = rDev.GetAntialiasing();
200 rDev.SetAntialiasing( nOldAA & ~AntialiasingFlags::Enable );
202 tools::Long nPix = rDev.PixelToLogic(Size(1, 1)).Width();
203 basegfx::B2DPolyPolygon aPolygons = svtools::ApplyLineDashing(rPolygon, nDashing, nPix);
205 // Handle problems of width 1px in Pixel mode: 0.5px gives a 1px line
206 if (rDev.GetMapMode().GetMapUnit() == MapUnit::MapPixel && nWidth == nPix)
207 nWidth = 0;
209 for ( sal_uInt32 i = 0; i < aPolygons.count( ); i++ )
211 const basegfx::B2DPolygon& aDash = aPolygons.getB2DPolygon( i );
212 basegfx::B2DPoint aStart = aDash.getB2DPoint( 0 );
213 basegfx::B2DPoint aEnd = aDash.getB2DPoint( aDash.count() - 1 );
215 basegfx::B2DVector aVector( aEnd - aStart );
216 aVector.normalize( );
217 const basegfx::B2DVector aPerpendicular(basegfx::getPerpendicular(aVector));
219 const basegfx::B2DVector aWidthOffset( double( nWidth ) / 2 * aPerpendicular);
220 basegfx::B2DPolygon aDashPolygon;
221 aDashPolygon.append( aStart + aWidthOffset );
222 aDashPolygon.append( aEnd + aWidthOffset );
223 aDashPolygon.append( aEnd - aWidthOffset );
224 aDashPolygon.append( aStart - aWidthOffset );
225 aDashPolygon.setClosed( true );
227 rDev.DrawPolygon( aDashPolygon );
230 rDev.SetAntialiasing( nOldAA );
233 namespace svtools {
236 * Dashing array must start with a line width and end with a blank width.
238 static std::vector<double> GetDashing( SvxBorderLineStyle nDashing )
240 std::vector<double> aPattern;
241 switch (nDashing)
243 case SvxBorderLineStyle::DOTTED:
244 aPattern.push_back( 1.0 ); // line
245 aPattern.push_back( 2.0 ); // blank
246 break;
247 case SvxBorderLineStyle::DASHED:
248 aPattern.push_back( 16.0 ); // line
249 aPattern.push_back( 5.0 ); // blank
250 break;
251 case SvxBorderLineStyle::FINE_DASHED:
252 aPattern.push_back( 6.0 ); // line
253 aPattern.push_back( 2.0 ); // blank
254 break;
255 case SvxBorderLineStyle::DASH_DOT:
256 aPattern.push_back( 16.0 ); // line
257 aPattern.push_back( 5.0 ); // blank
258 aPattern.push_back( 5.0 ); // line
259 aPattern.push_back( 5.0 ); // blank
260 break;
261 case SvxBorderLineStyle::DASH_DOT_DOT:
262 aPattern.push_back( 16.0 ); // line
263 aPattern.push_back( 5.0 ); // blank
264 aPattern.push_back( 5.0 ); // line
265 aPattern.push_back( 5.0 ); // blank
266 aPattern.push_back( 5.0 ); // line
267 aPattern.push_back( 5.0 ); // blank
268 break;
269 default:
273 return aPattern;
276 namespace {
278 class ApplyScale
280 double mfScale;
281 public:
282 explicit ApplyScale( double fScale ) : mfScale(fScale) {}
283 void operator() ( double& rVal )
285 rVal *= mfScale;
291 std::vector<double> GetLineDashing( SvxBorderLineStyle nDashing, double fScale )
293 std::vector<double> aPattern = GetDashing(nDashing);
294 std::for_each(aPattern.begin(), aPattern.end(), ApplyScale(fScale));
295 return aPattern;
298 basegfx::B2DPolyPolygon ApplyLineDashing( const basegfx::B2DPolygon& rPolygon, SvxBorderLineStyle nDashing, double fScale )
300 std::vector<double> aPattern = GetDashing(nDashing);
301 std::for_each(aPattern.begin(), aPattern.end(), ApplyScale(fScale));
303 basegfx::B2DPolyPolygon aPolygons;
305 if (aPattern.empty())
306 aPolygons.append(rPolygon);
307 else
308 basegfx::utils::applyLineDashing(rPolygon, aPattern, &aPolygons);
310 return aPolygons;
313 void DrawLine( OutputDevice& rDev, const Point& rP1, const Point& rP2,
314 sal_uInt32 nWidth, SvxBorderLineStyle nDashing )
316 DrawLine( rDev, basegfx::B2DPoint( rP1.X(), rP1.Y() ),
317 basegfx::B2DPoint( rP2.X(), rP2.Y( ) ), nWidth, nDashing );
320 void DrawLine( OutputDevice& rDev, const basegfx::B2DPoint& rP1, const basegfx::B2DPoint& rP2,
321 sal_uInt32 nWidth, SvxBorderLineStyle nDashing )
323 basegfx::B2DPolygon aPolygon;
324 aPolygon.append( rP1 );
325 aPolygon.append( rP2 );
326 lclDrawPolygon( rDev, aPolygon, nWidth, nDashing );
331 static Size gUserItemSz;
332 static int gFontNameBoxes;
333 static size_t gPreviewsPerDevice;
334 static std::vector<VclPtr<VirtualDevice>> gFontPreviewVirDevs;
335 static std::vector<OUString> gRenderedFontNames;
337 namespace
339 void calcCustomItemSize(const weld::ComboBox& rComboBox)
341 gUserItemSz = Size(rComboBox.get_approximate_digit_width() * 52, rComboBox.get_text_height());
342 gUserItemSz.setHeight(gUserItemSz.Height() * 16);
343 gUserItemSz.setHeight(gUserItemSz.Height() / 10);
345 size_t nMaxDeviceHeight = SAL_MAX_INT16 / 2; // see limitXCreatePixmap
346 gPreviewsPerDevice = nMaxDeviceHeight / gUserItemSz.Height();
350 IMPL_LINK(FontNameBox, SettingsChangedHdl, VclSimpleEvent&, rEvent, void)
352 if (rEvent.GetId() != VclEventId::ApplicationDataChanged)
353 return;
355 DataChangedEvent* pData = static_cast<DataChangedEvent*>(static_cast<VclWindowEvent&>(rEvent).GetData());
356 if (pData->GetType() == DataChangedEventType::SETTINGS)
358 gFontPreviewVirDevs.clear();
359 gRenderedFontNames.clear();
360 calcCustomItemSize(*m_xComboBox);
361 if (mbWYSIWYG && mpFontList && !mpFontList->empty())
363 mnPreviewProgress = 0;
364 maUpdateIdle.Start();
369 FontNameBox::FontNameBox(std::unique_ptr<weld::ComboBox> p)
370 : m_xComboBox(std::move(p))
371 , mnPreviewProgress(0)
372 , mbWYSIWYG(false)
373 , maUpdateIdle("FontNameBox Preview Update")
375 ++gFontNameBoxes;
376 InitFontMRUEntriesFile();
378 maUpdateIdle.SetPriority(TaskPriority::LOWEST);
379 maUpdateIdle.SetInvokeHandler(LINK(this, FontNameBox, UpdateHdl));
381 Application::AddEventListener(LINK(this, FontNameBox, SettingsChangedHdl));
384 FontNameBox::~FontNameBox()
386 Application::RemoveEventListener(LINK(this, FontNameBox, SettingsChangedHdl));
388 if (mpFontList)
390 SaveMRUEntries (maFontMRUEntriesFile);
391 ImplDestroyFontList();
393 --gFontNameBoxes;
394 if (!gFontNameBoxes)
396 for (auto &rDev : gFontPreviewVirDevs)
397 rDev.disposeAndClear();
398 gFontPreviewVirDevs.clear();
399 gRenderedFontNames.clear();
403 void FontNameBox::SaveMRUEntries(const OUString& aFontMRUEntriesFile) const
405 OString aEntries(OUStringToOString(m_xComboBox->get_mru_entries(),
406 RTL_TEXTENCODING_UTF8));
408 if (aEntries.isEmpty() || aFontMRUEntriesFile.isEmpty())
409 return;
411 SvFileStream aStream;
412 aStream.Open( aFontMRUEntriesFile, StreamMode::WRITE | StreamMode::TRUNC );
413 if( ! (aStream.IsOpen() && aStream.IsWritable()) )
415 SAL_INFO("svtools.control", "FontNameBox::SaveMRUEntries: opening mru entries file " << aFontMRUEntriesFile << " failed");
416 return;
419 aStream.SetLineDelimiter( LINEEND_LF );
420 aStream.WriteLine( aEntries );
421 aStream.WriteLine( "" );
424 void FontNameBox::LoadMRUEntries( const OUString& aFontMRUEntriesFile )
426 if (aFontMRUEntriesFile.isEmpty())
427 return;
429 if (!officecfg::Office::Common::Font::View::ShowFontBoxWYSIWYG::get())
430 return;
432 SvFileStream aStream( aFontMRUEntriesFile, StreamMode::READ );
433 if( ! aStream.IsOpen() )
435 SAL_INFO("svtools.control", "FontNameBox::LoadMRUEntries: opening mru entries file " << aFontMRUEntriesFile << " failed");
436 return;
439 OString aLine;
440 aStream.ReadLine( aLine );
441 OUString aEntries = OStringToOUString(aLine,
442 RTL_TEXTENCODING_UTF8);
443 m_xComboBox->set_mru_entries(aEntries);
446 void FontNameBox::InitFontMRUEntriesFile()
448 OUString sUserConfigDir("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE( "bootstrap") "::UserInstallation}");
449 rtl::Bootstrap::expandMacros(sUserConfigDir);
451 maFontMRUEntriesFile = sUserConfigDir;
452 if( !maFontMRUEntriesFile.isEmpty() )
454 maFontMRUEntriesFile += FONTNAMEBOXMRUENTRIESFILE;
458 void FontNameBox::ImplDestroyFontList()
460 mpFontList.reset();
461 mnPreviewProgress = 0;
462 maUpdateIdle.Stop();
465 void FontNameBox::Fill( const FontList* pList )
467 // store old text and clear box
468 OUString aOldText = m_xComboBox->get_active_text();
469 OUString rEntries = m_xComboBox->get_mru_entries();
470 bool bLoadFromFile = rEntries.isEmpty();
471 m_xComboBox->freeze();
472 m_xComboBox->clear();
474 ImplDestroyFontList();
475 mpFontList.reset(new ImplFontList);
477 // insert fonts
478 size_t nFontCount = pList->GetFontNameCount();
479 for (size_t i = 0; i < nFontCount; ++i)
481 const FontMetric& rFontMetric = pList->GetFontName(i);
482 m_xComboBox->append(OUString::number(i), rFontMetric.GetFamilyName());
483 mpFontList->push_back(rFontMetric);
486 if (bLoadFromFile)
487 LoadMRUEntries(maFontMRUEntriesFile);
488 else
489 m_xComboBox->set_mru_entries(rEntries);
491 m_xComboBox->thaw();
493 if (mbWYSIWYG && nFontCount)
495 assert(mnPreviewProgress == 0 && "ImplDestroyFontList wasn't called");
496 maUpdateIdle.Start();
499 // restore text
500 if (!aOldText.isEmpty())
501 set_active_or_entry_text(aOldText);
504 void FontNameBox::EnableWYSIWYG(bool bEnable)
506 if (comphelper::LibreOfficeKit::isActive())
507 return;
508 if (mbWYSIWYG == bEnable)
509 return;
510 mbWYSIWYG = bEnable;
512 if (mbWYSIWYG)
514 calcCustomItemSize(*m_xComboBox);
515 m_xComboBox->connect_custom_get_size(LINK(this, FontNameBox, CustomGetSizeHdl));
516 m_xComboBox->connect_custom_render(LINK(this, FontNameBox, CustomRenderHdl));
518 else
520 m_xComboBox->connect_custom_get_size(Link<OutputDevice&, Size>());
521 m_xComboBox->connect_custom_render(Link<weld::ComboBox::render_args, void>());
523 m_xComboBox->set_custom_renderer(mbWYSIWYG);
526 IMPL_LINK_NOARG(FontNameBox, CustomGetSizeHdl, OutputDevice&, Size)
528 return mbWYSIWYG ? gUserItemSz : Size();
531 namespace
533 tools::Long shrinkFontToFit(OUString const &rSampleText, tools::Long nH, vcl::Font &rFont, OutputDevice &rDevice, tools::Rectangle &rTextRect)
535 tools::Long nWidth = 0;
537 Size aSize( rFont.GetFontSize() );
539 //Make sure it fits in the available height
540 while (aSize.Height() > 0)
542 if (!rDevice.GetTextBoundRect(rTextRect, rSampleText))
543 break;
544 if (rTextRect.GetHeight() <= nH)
546 nWidth = rTextRect.GetWidth();
547 break;
550 aSize.AdjustHeight( -(EXTRAFONTSIZE) );
551 rFont.SetFontSize(aSize);
552 rDevice.SetFont(rFont);
555 return nWidth;
559 IMPL_LINK_NOARG(FontNameBox, UpdateHdl, Timer*, void)
561 CachePreview(mnPreviewProgress++, nullptr);
562 // tdf#132536 limit to ~25 pre-rendered for now. The font caches look
563 // b0rked, the massive charmaps are ~never swapped out, and don't count
564 // towards the size of a font in the font cache.
565 if (mnPreviewProgress < std::min<size_t>(25, mpFontList->size()))
566 maUpdateIdle.Start();
569 static void DrawPreview(const FontMetric& rFontMetric, const Point& rTopLeft, OutputDevice& rDevice, bool bSelected)
571 rDevice.Push(vcl::PushFlags::TEXTCOLOR);
573 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
574 if (bSelected)
575 rDevice.SetTextColor(rStyleSettings.GetHighlightTextColor());
576 else
577 rDevice.SetTextColor(rStyleSettings.GetDialogTextColor());
579 tools::Long nX = rTopLeft.X();
580 tools::Long nH = gUserItemSz.Height();
582 nX += IMGOUTERTEXTSPACE;
584 const bool bSymbolFont = isSymbolFont(rFontMetric);
586 vcl::Font aOldFont(rDevice.GetFont());
587 Size aSize( aOldFont.GetFontSize() );
588 aSize.AdjustHeight(EXTRAFONTSIZE );
589 vcl::Font aFont( rFontMetric );
590 aFont.SetFontSize( aSize );
591 rDevice.SetFont(aFont);
593 bool bUsingCorrectFont = true;
594 tools::Rectangle aTextRect;
596 // Preview the font name
597 const OUString& sFontName = rFontMetric.GetFamilyName();
599 //If it shouldn't or can't draw its own name because it doesn't have the glyphs
600 if (!canRenderNameOfSelectedFont(rDevice))
601 bUsingCorrectFont = false;
602 else
604 //Make sure it fits in the available height, shrinking the font if necessary
605 bUsingCorrectFont = shrinkFontToFit(sFontName, nH, aFont, rDevice, aTextRect) != 0;
608 if (!bUsingCorrectFont)
610 rDevice.SetFont(aOldFont);
611 rDevice.GetTextBoundRect(aTextRect, sFontName);
614 tools::Long nTextHeight = aTextRect.GetHeight();
615 tools::Long nDesiredGap = (nH-nTextHeight)/2;
616 tools::Long nVertAdjust = nDesiredGap - aTextRect.Top();
617 Point aPos( nX, rTopLeft.Y() + nVertAdjust );
618 rDevice.DrawText(aPos, sFontName);
619 tools::Long nTextX = aPos.X() + aTextRect.GetWidth() + GAPTOEXTRAPREVIEW;
621 if (!bUsingCorrectFont)
622 rDevice.SetFont(aFont);
624 OUString sSampleText;
626 if (!bSymbolFont)
628 const bool bNameBeginsWithLatinText = rFontMetric.GetFamilyName()[0] <= 'z';
630 if (bNameBeginsWithLatinText || !bUsingCorrectFont)
631 sSampleText = makeShortRepresentativeTextForSelectedFont(rDevice);
634 //If we're not a symbol font, but could neither render our own name and
635 //we can't determine what script it would like to render, then try a
636 //few well known scripts
637 if (sSampleText.isEmpty() && !bUsingCorrectFont)
639 static const UScriptCode aScripts[] =
641 USCRIPT_ARABIC,
642 USCRIPT_HEBREW,
644 USCRIPT_BENGALI,
645 USCRIPT_GURMUKHI,
646 USCRIPT_GUJARATI,
647 USCRIPT_ORIYA,
648 USCRIPT_TAMIL,
649 USCRIPT_TELUGU,
650 USCRIPT_KANNADA,
651 USCRIPT_MALAYALAM,
652 USCRIPT_SINHALA,
653 USCRIPT_DEVANAGARI,
655 USCRIPT_THAI,
656 USCRIPT_LAO,
657 USCRIPT_GEORGIAN,
658 USCRIPT_TIBETAN,
659 USCRIPT_SYRIAC,
660 USCRIPT_MYANMAR,
661 USCRIPT_ETHIOPIC,
662 USCRIPT_KHMER,
663 USCRIPT_MONGOLIAN,
665 USCRIPT_KOREAN,
666 USCRIPT_JAPANESE,
667 USCRIPT_HAN,
668 USCRIPT_SIMPLIFIED_HAN,
669 USCRIPT_TRADITIONAL_HAN,
671 USCRIPT_GREEK
674 for (const UScriptCode& rScript : aScripts)
676 OUString sText = makeShortRepresentativeTextForScript(rScript);
677 if (!sText.isEmpty())
679 bool bHasSampleTextGlyphs = (-1 == rDevice.HasGlyphs(aFont, sText));
680 if (bHasSampleTextGlyphs)
682 sSampleText = sText;
683 break;
688 static const UScriptCode aMinimalScripts[] =
690 USCRIPT_HEBREW, //e.g. biblical hebrew
691 USCRIPT_GREEK
694 for (const UScriptCode& rMinimalScript : aMinimalScripts)
696 OUString sText = makeShortMinimalTextForScript(rMinimalScript);
697 if (!sText.isEmpty())
699 bool bHasSampleTextGlyphs = (-1 == rDevice.HasGlyphs(aFont, sText));
700 if (bHasSampleTextGlyphs)
702 sSampleText = sText;
703 break;
709 //If we're a symbol font, or for some reason the font still couldn't
710 //render something representative of what it would like to render then
711 //make up some semi-random text that it *can* display
712 if (bSymbolFont || (!bUsingCorrectFont && sSampleText.isEmpty()))
713 sSampleText = makeShortRepresentativeSymbolTextForSelectedFont(rDevice);
715 if (!sSampleText.isEmpty())
717 const Size &rItemSize = gUserItemSz;
719 //leave a little border at the edge
720 tools::Long nSpace = rItemSize.Width() - nTextX - IMGOUTERTEXTSPACE;
721 if (nSpace >= 0)
723 //Make sure it fits in the available height, and get how wide that would be
724 tools::Long nWidth = shrinkFontToFit(sSampleText, nH, aFont, rDevice, aTextRect);
725 //Chop letters off until it fits in the available width
726 while (nWidth > nSpace || nWidth > gUserItemSz.Width())
728 sSampleText = sSampleText.copy(0, sSampleText.getLength()-1);
729 nWidth = rDevice.GetTextBoundRect(aTextRect, sSampleText) ?
730 aTextRect.GetWidth() : 0;
733 //center the text on the line
734 if (!sSampleText.isEmpty() && nWidth)
736 nTextHeight = aTextRect.GetHeight();
737 nDesiredGap = (nH-nTextHeight)/2;
738 nVertAdjust = nDesiredGap - aTextRect.Top();
739 aPos = Point(nTextX + nSpace - nWidth, rTopLeft.Y() + nVertAdjust);
740 rDevice.DrawText(aPos, sSampleText);
745 rDevice.SetFont(aOldFont);
746 rDevice.Pop();
749 OutputDevice& FontNameBox::CachePreview(size_t nIndex, Point* pTopLeft)
751 SolarMutexGuard aGuard;
752 const FontMetric& rFontMetric = (*mpFontList)[nIndex];
753 const OUString& rFontName = rFontMetric.GetFamilyName();
755 size_t nPreviewIndex;
756 auto xFind = std::find(gRenderedFontNames.begin(), gRenderedFontNames.end(), rFontName);
757 bool bPreviewAvailable = xFind != gRenderedFontNames.end();
758 if (!bPreviewAvailable)
760 nPreviewIndex = gRenderedFontNames.size();
761 gRenderedFontNames.push_back(rFontName);
763 else
764 nPreviewIndex = std::distance(gRenderedFontNames.begin(), xFind);
766 size_t nPage = nPreviewIndex / gPreviewsPerDevice;
767 size_t nIndexInPage = nPreviewIndex - (nPage * gPreviewsPerDevice);
769 Point aTopLeft(0, gUserItemSz.Height() * nIndexInPage);
771 if (!bPreviewAvailable)
773 if (nPage >= gFontPreviewVirDevs.size())
775 gFontPreviewVirDevs.emplace_back(m_xComboBox->create_render_virtual_device());
776 VirtualDevice& rDevice = *gFontPreviewVirDevs.back();
777 rDevice.SetOutputSizePixel(Size(gUserItemSz.Width(), gUserItemSz.Height() * gPreviewsPerDevice));
778 weld::SetPointFont(rDevice, m_xComboBox->get_font());
779 assert(gFontPreviewVirDevs.size() == nPage + 1);
782 DrawPreview(rFontMetric, aTopLeft, *gFontPreviewVirDevs.back(), false);
785 if (pTopLeft)
786 *pTopLeft = aTopLeft;
788 return *gFontPreviewVirDevs[nPage];
791 IMPL_LINK(FontNameBox, CustomRenderHdl, weld::ComboBox::render_args, aPayload, void)
793 vcl::RenderContext& rRenderContext = std::get<0>(aPayload);
794 const ::tools::Rectangle& rRect = std::get<1>(aPayload);
795 bool bSelected = std::get<2>(aPayload);
796 const OUString& rId = std::get<3>(aPayload);
798 sal_uInt32 nIndex = rId.toUInt32();
800 Point aDestPoint(rRect.TopLeft());
801 auto nMargin = (rRect.GetHeight() - gUserItemSz.Height()) / 2;
802 aDestPoint.AdjustY(nMargin);
804 if (bSelected)
806 const FontMetric& rFontMetric = (*mpFontList)[nIndex];
807 DrawPreview(rFontMetric, aDestPoint, rRenderContext, true);
809 else
811 // use cache of unselected entries
812 Point aTopLeft;
813 OutputDevice& rDevice = CachePreview(nIndex, &aTopLeft);
815 rRenderContext.DrawOutDev(aDestPoint, gUserItemSz,
816 aTopLeft, gUserItemSz,
817 rDevice);
821 void FontNameBox::set_active_or_entry_text(const OUString& rText)
823 const int nFound = m_xComboBox->find_text(rText);
824 if (nFound != -1)
825 m_xComboBox->set_active(nFound);
826 m_xComboBox->set_entry_text(rText);
829 FontStyleBox::FontStyleBox(std::unique_ptr<weld::ComboBox> p)
830 : m_xComboBox(std::move(p))
832 //Use the standard texts to get an optimal size and stick to that size.
833 //That should stop the character dialog dancing around.
834 auto nMaxLen = m_xComboBox->get_pixel_size(SvtResId(STR_SVT_STYLE_LIGHT)).Width();
835 nMaxLen = std::max(nMaxLen, m_xComboBox->get_pixel_size(SvtResId(STR_SVT_STYLE_LIGHT_ITALIC)).Width());
836 nMaxLen = std::max(nMaxLen, m_xComboBox->get_pixel_size(SvtResId(STR_SVT_STYLE_NORMAL)).Width());
837 nMaxLen = std::max(nMaxLen, m_xComboBox->get_pixel_size(SvtResId(STR_SVT_STYLE_NORMAL_ITALIC)).Width());
838 nMaxLen = std::max(nMaxLen, m_xComboBox->get_pixel_size(SvtResId(STR_SVT_STYLE_BOLD)).Width());
839 nMaxLen = std::max(nMaxLen, m_xComboBox->get_pixel_size(SvtResId(STR_SVT_STYLE_BOLD_ITALIC)).Width());
840 nMaxLen = std::max(nMaxLen, m_xComboBox->get_pixel_size(SvtResId(STR_SVT_STYLE_BLACK)).Width());
841 nMaxLen = std::max(nMaxLen, m_xComboBox->get_pixel_size(SvtResId(STR_SVT_STYLE_BLACK_ITALIC)).Width());
842 m_xComboBox->set_entry_width_chars(std::ceil(nMaxLen / m_xComboBox->get_approximate_digit_width()));
845 void FontStyleBox::Fill( const OUString& rName, const FontList* pList )
847 OUString aOldText = m_xComboBox->get_active_text();
848 int nPos = m_xComboBox->get_active();
850 m_xComboBox->freeze();
851 m_xComboBox->clear();
853 // does a font with this name already exist?
854 sal_Handle hFontMetric = pList->GetFirstFontMetric( rName );
855 if ( hFontMetric )
857 OUString aStyleText;
858 FontWeight eLastWeight = WEIGHT_DONTKNOW;
859 FontItalic eLastItalic = ITALIC_NONE;
860 FontWidth eLastWidth = WIDTH_DONTKNOW;
861 bool bNormal = false;
862 bool bItalic = false;
863 bool bBold = false;
864 bool bBoldItalic = false;
865 bool bInsert = false;
866 FontMetric aFontMetric;
867 while ( hFontMetric )
869 aFontMetric = FontList::GetFontMetric( hFontMetric );
871 FontWeight eWeight = aFontMetric.GetWeight();
872 FontItalic eItalic = aFontMetric.GetItalic();
873 FontWidth eWidth = aFontMetric.GetWidthType();
874 // Only if the attributes are different, we insert the
875 // Font to avoid double Entries in different languages
876 if ( (eWeight != eLastWeight) || (eItalic != eLastItalic) ||
877 (eWidth != eLastWidth) )
879 if ( bInsert )
880 m_xComboBox->append_text(aStyleText);
882 if ( eWeight <= WEIGHT_NORMAL )
884 if ( eItalic != ITALIC_NONE )
885 bItalic = true;
886 else
887 bNormal = true;
889 else
891 if ( eItalic != ITALIC_NONE )
892 bBoldItalic = true;
893 else
894 bBold = true;
897 // For wrong StyleNames we replace this with the correct once
898 aStyleText = pList->GetStyleName( aFontMetric );
899 bInsert = m_xComboBox->find_text(aStyleText) == -1;
900 if ( !bInsert )
902 aStyleText = pList->GetStyleName( eWeight, eItalic );
903 bInsert = m_xComboBox->find_text(aStyleText) == -1;
906 eLastWeight = eWeight;
907 eLastItalic = eItalic;
908 eLastWidth = eWidth;
910 else
912 if ( bInsert )
914 // If we have two names for the same attributes
915 // we prefer the translated standard names
916 const OUString& rAttrStyleText = pList->GetStyleName( eWeight, eItalic );
917 if (rAttrStyleText != aStyleText)
919 OUString aTempStyleText = pList->GetStyleName( aFontMetric );
920 if (rAttrStyleText == aTempStyleText)
921 aStyleText = rAttrStyleText;
922 bInsert = m_xComboBox->find_text(aStyleText) == -1;
927 if ( !bItalic && (aStyleText == pList->GetItalicStr()) )
928 bItalic = true;
929 else if ( !bBold && (aStyleText == pList->GetBoldStr()) )
930 bBold = true;
931 else if ( !bBoldItalic && (aStyleText == pList->GetBoldItalicStr()) )
932 bBoldItalic = true;
934 hFontMetric = FontList::GetNextFontMetric( hFontMetric );
937 if ( bInsert )
938 m_xComboBox->append_text(aStyleText);
940 // certain style as copy
941 if ( bNormal )
943 if ( !bItalic )
944 m_xComboBox->append_text(pList->GetItalicStr());
945 if ( !bBold )
946 m_xComboBox->append_text(pList->GetBoldStr());
948 if ( !bBoldItalic )
950 if ( bNormal || bItalic || bBold )
951 m_xComboBox->append_text(pList->GetBoldItalicStr());
954 else
956 // insert standard styles if no font
957 m_xComboBox->append_text(pList->GetNormalStr());
958 m_xComboBox->append_text(pList->GetItalicStr());
959 m_xComboBox->append_text(pList->GetBoldStr());
960 m_xComboBox->append_text(pList->GetBoldItalicStr());
963 m_xComboBox->thaw();
965 if (!aOldText.isEmpty())
967 int nFound = m_xComboBox->find_text(aOldText);
968 if (nFound != -1)
969 m_xComboBox->set_active(nFound);
970 else
972 if (nPos >= m_xComboBox->get_count())
973 m_xComboBox->set_active(0);
974 else
975 m_xComboBox->set_active(nPos);
980 FontSizeBox::FontSizeBox(std::unique_ptr<weld::ComboBox> p)
981 : pFontList(nullptr)
982 , nSavedValue(0)
983 , nMin(20)
984 , nMax(9999)
985 , eUnit(FieldUnit::POINT)
986 , nDecimalDigits(1)
987 , nRelMin(0)
988 , nRelMax(0)
989 , nRelStep(0)
990 , nPtRelMin(0)
991 , nPtRelMax(0)
992 , nPtRelStep(0)
993 , bRelativeMode(false)
994 , bRelative(false)
995 , bPtRelative(false)
996 , bStdSize(false)
997 , m_xComboBox(std::move(p))
999 m_xComboBox->set_entry_width_chars(std::ceil(m_xComboBox->get_pixel_size(format_number(105)).Width() /
1000 m_xComboBox->get_approximate_digit_width()));
1001 m_xComboBox->connect_focus_out(LINK(this, FontSizeBox, ReformatHdl));
1002 m_xComboBox->connect_changed(LINK(this, FontSizeBox, ModifyHdl));
1005 void FontSizeBox::set_active_or_entry_text(const OUString& rText)
1007 const int nFound = m_xComboBox->find_text(rText);
1008 if (nFound != -1)
1009 m_xComboBox->set_active(nFound);
1010 m_xComboBox->set_entry_text(rText);
1013 IMPL_LINK(FontSizeBox, ReformatHdl, weld::Widget&, rWidget, void)
1015 FontSizeNames aFontSizeNames(Application::GetSettings().GetUILanguageTag().getLanguageType());
1016 if (!bRelativeMode || !aFontSizeNames.IsEmpty())
1018 if (aFontSizeNames.Name2Size(m_xComboBox->get_active_text()) != 0)
1019 return;
1022 set_value(get_value());
1024 m_aFocusOutHdl.Call(rWidget);
1027 IMPL_LINK(FontSizeBox, ModifyHdl, weld::ComboBox&, rBox, void)
1029 if (bRelativeMode)
1031 OUString aStr = comphelper::string::stripStart(rBox.get_active_text(), ' ');
1033 bool bNewMode = bRelative;
1034 bool bOldPtRelMode = bPtRelative;
1036 if ( bRelative )
1038 bPtRelative = false;
1039 const sal_Unicode* pStr = aStr.getStr();
1040 while ( *pStr )
1042 if ( ((*pStr < '0') || (*pStr > '9')) && (*pStr != '%') && !unicode::isSpace(*pStr) )
1044 if ( ('-' == *pStr || '+' == *pStr) && !bPtRelative )
1045 bPtRelative = true;
1046 else if ( bPtRelative && 'p' == *pStr && 't' == *++pStr )
1048 else
1050 bNewMode = false;
1051 break;
1054 pStr++;
1057 else if (!aStr.isEmpty())
1059 if ( -1 != aStr.indexOf('%') )
1061 bNewMode = true;
1062 bPtRelative = false;
1065 if ( '-' == aStr[0] || '+' == aStr[0] )
1067 bNewMode = true;
1068 bPtRelative = true;
1072 if ( bNewMode != bRelative || bPtRelative != bOldPtRelMode )
1073 SetRelative( bNewMode );
1075 m_aChangeHdl.Call(rBox);
1078 void FontSizeBox::Fill( const FontList* pList )
1080 // remember for relative mode
1081 pFontList = pList;
1083 // no font sizes need to be set for relative mode
1084 if ( bRelative )
1085 return;
1087 // query font sizes
1088 const int* pTempAry;
1089 const int* pAry = nullptr;
1091 pAry = FontList::GetStdSizeAry();
1093 // first insert font size names (for simplified/traditional chinese)
1094 FontSizeNames aFontSizeNames( Application::GetSettings().GetUILanguageTag().getLanguageType() );
1095 if ( pAry == FontList::GetStdSizeAry() )
1097 // for standard sizes we don't need to bother
1098 if (bStdSize && m_xComboBox->get_count() && aFontSizeNames.IsEmpty())
1099 return;
1100 bStdSize = true;
1102 else
1103 bStdSize = false;
1105 int nSelectionStart, nSelectionEnd;
1106 m_xComboBox->get_entry_selection_bounds(nSelectionStart, nSelectionEnd);
1107 OUString aStr = m_xComboBox->get_active_text();
1109 m_xComboBox->freeze();
1110 m_xComboBox->clear();
1111 int nPos = 0;
1113 if ( !aFontSizeNames.IsEmpty() )
1115 if ( pAry == FontList::GetStdSizeAry() )
1117 // for scalable fonts all font size names
1118 sal_uLong nCount = aFontSizeNames.Count();
1119 for( sal_uLong i = 0; i < nCount; i++ )
1121 OUString aSizeName = aFontSizeNames.GetIndexName( i );
1122 int nSize = aFontSizeNames.GetIndexSize( i );
1123 OUString sId(OUString::number(-nSize)); // mark as special
1124 m_xComboBox->insert(nPos, aSizeName, &sId, nullptr, nullptr);
1125 nPos++;
1128 else
1130 // for fixed size fonts only selectable font size names
1131 pTempAry = pAry;
1132 while ( *pTempAry )
1134 OUString aSizeName = aFontSizeNames.Size2Name( *pTempAry );
1135 if ( !aSizeName.isEmpty() )
1137 OUString sId(OUString::number(-(*pTempAry))); // mark as special
1138 m_xComboBox->insert(nPos, aSizeName, &sId, nullptr, nullptr);
1139 nPos++;
1141 pTempAry++;
1146 // then insert numerical font size values
1147 pTempAry = pAry;
1148 while (*pTempAry)
1150 InsertValue(*pTempAry);
1151 ++pTempAry;
1154 set_active_or_entry_text(aStr);
1155 m_xComboBox->select_entry_region(nSelectionStart, nSelectionEnd);
1156 m_xComboBox->thaw();
1159 void FontSizeBox::EnableRelativeMode( sal_uInt16 nNewMin, sal_uInt16 nNewMax, sal_uInt16 nStep )
1161 bRelativeMode = true;
1162 nRelMin = nNewMin;
1163 nRelMax = nNewMax;
1164 nRelStep = nStep;
1165 SetUnit(FieldUnit::POINT);
1168 void FontSizeBox::EnablePtRelativeMode( short nNewMin, short nNewMax, short nStep )
1170 bRelativeMode = true;
1171 nPtRelMin = nNewMin;
1172 nPtRelMax = nNewMax;
1173 nPtRelStep = nStep;
1174 SetUnit(FieldUnit::POINT);
1177 void FontSizeBox::InsertValue(int i)
1179 OUString sNumber(OUString::number(i));
1180 m_xComboBox->append(sNumber, format_number(i));
1183 void FontSizeBox::SetRelative( bool bNewRelative )
1185 if ( !bRelativeMode )
1186 return;
1188 int nSelectionStart, nSelectionEnd;
1189 m_xComboBox->get_entry_selection_bounds(nSelectionStart, nSelectionEnd);
1190 OUString aStr = comphelper::string::stripStart(m_xComboBox->get_active_text(), ' ');
1192 if (bNewRelative)
1194 bRelative = true;
1195 bStdSize = false;
1197 m_xComboBox->clear();
1199 if (bPtRelative)
1201 SetDecimalDigits( 1 );
1202 SetRange(nPtRelMin, nPtRelMax);
1203 SetUnit(FieldUnit::POINT);
1205 short i = nPtRelMin, n = 0;
1206 // JP 30.06.98: more than 100 values are not useful
1207 while ( i <= nPtRelMax && n++ < 100 )
1209 InsertValue( i );
1210 i = i + nPtRelStep;
1213 else
1215 SetDecimalDigits(0);
1216 SetRange(nRelMin, nRelMax);
1217 SetUnit(FieldUnit::PERCENT);
1219 sal_uInt16 i = nRelMin;
1220 while ( i <= nRelMax )
1222 InsertValue( i );
1223 i = i + nRelStep;
1227 else
1229 if (pFontList)
1230 m_xComboBox->clear();
1231 bRelative = bPtRelative = false;
1232 SetDecimalDigits(1);
1233 SetRange(20, 9999);
1234 SetUnit(FieldUnit::POINT);
1235 if ( pFontList)
1236 Fill( pFontList );
1239 set_active_or_entry_text(aStr);
1240 m_xComboBox->select_entry_region(nSelectionStart, nSelectionEnd);
1243 OUString FontSizeBox::format_number(int nValue) const
1245 OUString sRet;
1247 //pawn percent off to icu to decide whether percent is separated from its number for this locale
1248 if (eUnit == FieldUnit::PERCENT)
1250 double fValue = nValue;
1251 fValue /= weld::SpinButton::Power10(nDecimalDigits);
1252 sRet = unicode::formatPercent(fValue, Application::GetSettings().GetUILanguageTag());
1254 else
1256 const SvtSysLocale aSysLocale;
1257 const LocaleDataWrapper& rLocaleData = aSysLocale.GetLocaleData();
1258 sRet = rLocaleData.getNum(nValue, nDecimalDigits, true, false);
1259 if (eUnit != FieldUnit::NONE && eUnit != FieldUnit::DEGREE)
1260 sRet += " ";
1261 assert(eUnit != FieldUnit::PERCENT);
1262 sRet += weld::MetricSpinButton::MetricToString(eUnit);
1265 if (bRelativeMode && bPtRelative && (0 <= nValue) && !sRet.isEmpty())
1266 sRet = "+" + sRet;
1268 return sRet;
1271 void FontSizeBox::SetValue(int nNewValue, FieldUnit eInUnit)
1273 auto nTempValue = vcl::ConvertValue(nNewValue, 0, GetDecimalDigits(), eInUnit, GetUnit());
1274 if (nTempValue < nMin)
1275 nTempValue = nMin;
1276 else if (nTempValue > nMax)
1277 nTempValue = nMax;
1278 if (!bRelative)
1280 FontSizeNames aFontSizeNames(Application::GetSettings().GetUILanguageTag().getLanguageType());
1281 // conversion loses precision; however font sizes should
1282 // never have a problem with that
1283 OUString aName = aFontSizeNames.Size2Name(nTempValue);
1284 if (!aName.isEmpty() && m_xComboBox->find_text(aName) != -1)
1286 m_xComboBox->set_active_text(aName);
1287 return;
1290 OUString aResult = format_number(nTempValue);
1291 set_active_or_entry_text(aResult);
1294 void FontSizeBox::set_value(int nNewValue)
1296 SetValue(nNewValue, eUnit);
1299 int FontSizeBox::get_value() const
1301 OUString aStr = m_xComboBox->get_active_text();
1302 if (!bRelative)
1304 FontSizeNames aFontSizeNames(Application::GetSettings().GetUILanguageTag().getLanguageType());
1305 auto nValue = aFontSizeNames.Name2Size(aStr);
1306 if (nValue)
1307 return vcl::ConvertValue(nValue, 0, GetDecimalDigits(), GetUnit(), GetUnit());
1310 const SvtSysLocale aSysLocale;
1311 const LocaleDataWrapper& rLocaleData = aSysLocale.GetLocaleData();
1312 double fResult(0.0);
1313 (void)vcl::TextToValue(aStr, fResult, 0, GetDecimalDigits(), rLocaleData, GetUnit());
1314 if (!aStr.isEmpty())
1316 if (fResult < nMin)
1317 fResult = nMin;
1318 else if (fResult > nMax)
1319 fResult = nMax;
1321 return fResult;
1324 SvxBorderLineStyle SvtLineListBox::GetSelectEntryStyle() const
1326 if (m_xLineSet->IsNoSelection())
1327 return SvxBorderLineStyle::NONE;
1328 auto nId = m_xLineSet->GetSelectedItemId();
1329 return static_cast<SvxBorderLineStyle>(nId - 1);
1332 namespace
1334 Size getPreviewSize(const weld::Widget& rControl)
1336 return Size(rControl.get_approximate_digit_width() * 15, rControl.get_text_height());
1340 void SvtLineListBox::ImpGetLine( tools::Long nLine1, tools::Long nLine2, tools::Long nDistance,
1341 Color aColor1, Color aColor2, Color aColorDist,
1342 SvxBorderLineStyle nStyle, BitmapEx& rBmp )
1344 Size aSize(getPreviewSize(*m_xControl));
1346 // SourceUnit to Twips
1347 if ( eSourceUnit == FieldUnit::POINT )
1349 nLine1 /= 5;
1350 nLine2 /= 5;
1351 nDistance /= 5;
1354 // Paint the lines
1355 aSize = aVirDev->PixelToLogic( aSize );
1356 tools::Long nPix = aVirDev->PixelToLogic( Size( 0, 1 ) ).Height();
1357 sal_uInt32 n1 = nLine1;
1358 sal_uInt32 n2 = nLine2;
1359 tools::Long nDist = nDistance;
1360 n1 += nPix-1;
1361 n1 -= n1%nPix;
1362 if ( n2 )
1364 nDist += nPix-1;
1365 nDist -= nDist%nPix;
1366 n2 += nPix-1;
1367 n2 -= n2%nPix;
1369 tools::Long nVirHeight = n1+nDist+n2;
1370 if ( nVirHeight > aSize.Height() )
1371 aSize.setHeight( nVirHeight );
1372 // negative width should not be drawn
1373 if ( aSize.Width() <= 0 )
1374 return;
1376 Size aVirSize = aVirDev->LogicToPixel( aSize );
1377 if ( aVirDev->GetOutputSizePixel() != aVirSize )
1378 aVirDev->SetOutputSizePixel( aVirSize );
1379 aVirDev->SetFillColor( aColorDist );
1380 aVirDev->DrawRect( tools::Rectangle( Point(), aSize ) );
1382 aVirDev->SetFillColor( aColor1 );
1384 double y1 = double( n1 ) / 2;
1385 svtools::DrawLine( *aVirDev, basegfx::B2DPoint( 0, y1 ), basegfx::B2DPoint( aSize.Width( ), y1 ), n1, nStyle );
1387 if ( n2 )
1389 double y2 = n1 + nDist + double( n2 ) / 2;
1390 aVirDev->SetFillColor( aColor2 );
1391 svtools::DrawLine( *aVirDev, basegfx::B2DPoint( 0, y2 ), basegfx::B2DPoint( aSize.Width(), y2 ), n2, SvxBorderLineStyle::SOLID );
1393 rBmp = aVirDev->GetBitmapEx( Point(), Size( aSize.Width(), n1+nDist+n2 ) );
1396 SvtLineListBox::SvtLineListBox(std::unique_ptr<weld::MenuButton> pControl)
1397 : m_xControl(std::move(pControl))
1398 , m_xBuilder(Application::CreateBuilder(m_xControl.get(), "svt/ui/linewindow.ui"))
1399 , m_xTopLevel(m_xBuilder->weld_widget("line_popup_window"))
1400 , m_xNoneButton(m_xBuilder->weld_button("none_line_button"))
1401 , m_xLineSet(new ValueSet(nullptr))
1402 , m_xLineSetWin(new weld::CustomWeld(*m_xBuilder, "lineset", *m_xLineSet))
1403 , m_nWidth( 5 )
1404 , aVirDev(VclPtr<VirtualDevice>::Create())
1405 , aColor(COL_BLACK)
1406 , maPaintCol(COL_BLACK)
1408 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
1409 m_xLineSet->SetStyle(WinBits(WB_FLATVALUESET | WB_NO_DIRECTSELECT | WB_TABSTOP));
1410 m_xLineSet->SetItemHeight(rStyleSettings.GetListBoxPreviewDefaultPixelSize().Height() + 1);
1411 m_xLineSet->SetColCount(1);
1412 m_xLineSet->SetSelectHdl(LINK(this, SvtLineListBox, ValueSelectHdl));
1414 m_xNoneButton->connect_clicked(LINK(this, SvtLineListBox, NoneHdl));
1416 m_xTopLevel->connect_focus_in(LINK(this, SvtLineListBox, FocusHdl));
1417 m_xControl->set_popover(m_xTopLevel.get());
1418 m_xControl->connect_toggled(LINK(this, SvtLineListBox, ToggleHdl));
1420 // lock size to these maxes height/width so it doesn't jump around in size
1421 m_xControl->set_label(GetLineStyleName(SvxBorderLineStyle::NONE));
1422 Size aNonePrefSize = m_xControl->get_preferred_size();
1423 m_xControl->set_label("");
1424 aVirDev->SetOutputSizePixel(getPreviewSize(*m_xControl));
1425 m_xControl->set_image(aVirDev);
1426 Size aSolidPrefSize = m_xControl->get_preferred_size();
1427 m_xControl->set_size_request(std::max(aNonePrefSize.Width(), aSolidPrefSize.Width()),
1428 std::max(aNonePrefSize.Height(), aSolidPrefSize.Height()));
1430 eSourceUnit = FieldUnit::POINT;
1432 aVirDev->SetLineColor();
1433 aVirDev->SetMapMode(MapMode(MapUnit::MapTwip));
1435 UpdatePaintLineColor();
1438 IMPL_LINK_NOARG(SvtLineListBox, FocusHdl, weld::Widget&, void)
1440 if (GetSelectEntryStyle() == SvxBorderLineStyle::NONE)
1441 m_xNoneButton->grab_focus();
1442 else
1443 m_xLineSet->GrabFocus();
1446 IMPL_LINK(SvtLineListBox, ToggleHdl, weld::Toggleable&, rButton, void)
1448 if (rButton.get_active())
1449 FocusHdl(*m_xTopLevel);
1452 IMPL_LINK_NOARG(SvtLineListBox, NoneHdl, weld::Button&, void)
1454 SelectEntry(SvxBorderLineStyle::NONE);
1455 ValueSelectHdl(nullptr);
1458 SvtLineListBox::~SvtLineListBox()
1462 OUString SvtLineListBox::GetLineStyleName(SvxBorderLineStyle eStyle)
1464 OUString sRet;
1465 for (sal_uInt32 i = 0; i < SAL_N_ELEMENTS(RID_SVXSTR_BORDERLINE); ++i)
1467 if (eStyle == RID_SVXSTR_BORDERLINE[i].second)
1469 sRet = SvtResId(RID_SVXSTR_BORDERLINE[i].first);
1470 break;
1473 return sRet;
1476 sal_Int32 SvtLineListBox::GetStylePos( sal_Int32 nListPos ) const
1478 sal_Int32 nPos = -1;
1479 --nListPos;
1481 sal_Int32 n = 0;
1482 size_t i = 0;
1483 size_t nCount = m_vLineList.size();
1484 while ( nPos == -1 && i < nCount )
1486 if ( nListPos == n )
1487 nPos = static_cast<sal_Int32>(i);
1488 n++;
1489 i++;
1492 return nPos;
1495 void SvtLineListBox::SelectEntry(SvxBorderLineStyle nStyle)
1497 if (nStyle == SvxBorderLineStyle::NONE)
1498 m_xLineSet->SetNoSelection();
1499 else
1500 m_xLineSet->SelectItem(static_cast<sal_Int16>(nStyle) + 1);
1501 UpdatePreview();
1504 void SvtLineListBox::InsertEntry(
1505 const BorderWidthImpl& rWidthImpl, SvxBorderLineStyle nStyle, tools::Long nMinWidth,
1506 ColorFunc pColor1Fn, ColorFunc pColor2Fn, ColorDistFunc pColorDistFn )
1508 m_vLineList.emplace_back(new ImpLineListData(
1509 rWidthImpl, nStyle, nMinWidth, pColor1Fn, pColor2Fn, pColorDistFn));
1512 void SvtLineListBox::UpdatePaintLineColor()
1514 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
1515 Color aNewCol(rSettings.GetWindowColor().IsDark() ? rSettings.GetLabelTextColor() : aColor);
1517 bool bRet = aNewCol != maPaintCol;
1519 if( bRet )
1520 maPaintCol = aNewCol;
1523 void SvtLineListBox::UpdateEntries()
1525 UpdatePaintLineColor( );
1527 SvxBorderLineStyle eSelected = GetSelectEntryStyle();
1529 // Remove the old entries
1530 m_xLineSet->Clear();
1532 // Add the new entries based on the defined width
1534 sal_uInt16 n = 0;
1535 sal_uInt16 nCount = m_vLineList.size( );
1536 while ( n < nCount )
1538 auto& pData = m_vLineList[ n ];
1539 BitmapEx aBmp;
1540 ImpGetLine( pData->GetLine1ForWidth( m_nWidth ),
1541 pData->GetLine2ForWidth( m_nWidth ),
1542 pData->GetDistForWidth( m_nWidth ),
1543 GetColorLine1(m_xLineSet->GetItemCount()),
1544 GetColorLine2(m_xLineSet->GetItemCount()),
1545 GetColorDist(m_xLineSet->GetItemCount()),
1546 pData->GetStyle(), aBmp );
1547 sal_Int16 nItemId = static_cast<sal_Int16>(pData->GetStyle()) + 1;
1548 m_xLineSet->InsertItem(nItemId, Image(aBmp), GetLineStyleName(pData->GetStyle()));
1549 if (pData->GetStyle() == eSelected)
1550 m_xLineSet->SelectItem(nItemId);
1551 n++;
1554 m_xLineSet->SetOptimalSize();
1557 Color SvtLineListBox::GetColorLine1( sal_Int32 nPos )
1559 sal_Int32 nStyle = GetStylePos( nPos );
1560 if (nStyle == -1)
1561 return GetPaintColor( );
1562 auto& pData = m_vLineList[ nStyle ];
1563 return pData->GetColorLine1( GetColor( ) );
1566 Color SvtLineListBox::GetColorLine2( sal_Int32 nPos )
1568 sal_Int32 nStyle = GetStylePos(nPos);
1569 if (nStyle == -1)
1570 return GetPaintColor( );
1571 auto& pData = m_vLineList[ nStyle ];
1572 return pData->GetColorLine2( GetColor( ) );
1575 Color SvtLineListBox::GetColorDist( sal_Int32 nPos )
1577 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
1578 Color rResult = rSettings.GetFieldColor();
1580 sal_Int32 nStyle = GetStylePos( nPos );
1581 if (nStyle == -1)
1582 return rResult;
1583 auto& pData = m_vLineList[ nStyle ];
1584 return pData->GetColorDist( GetColor( ), rResult );
1587 IMPL_LINK_NOARG(SvtLineListBox, ValueSelectHdl, ValueSet*, void)
1589 maSelectHdl.Call(*this);
1590 UpdatePreview();
1591 if (m_xControl->get_active())
1592 m_xControl->set_active(false);
1595 void SvtLineListBox::UpdatePreview()
1597 SvxBorderLineStyle eStyle = GetSelectEntryStyle();
1598 for (sal_uInt32 i = 0; i < SAL_N_ELEMENTS(RID_SVXSTR_BORDERLINE); ++i)
1600 if (eStyle == RID_SVXSTR_BORDERLINE[i].second)
1602 m_xControl->set_label(SvtResId(RID_SVXSTR_BORDERLINE[i].first));
1603 break;
1607 if (eStyle == SvxBorderLineStyle::NONE)
1609 m_xControl->set_image(nullptr);
1610 m_xControl->set_label(GetLineStyleName(SvxBorderLineStyle::NONE));
1612 else
1614 Image aImage(m_xLineSet->GetItemImage(m_xLineSet->GetSelectedItemId()));
1615 m_xControl->set_label("");
1616 const auto nPos = (aVirDev->GetOutputSizePixel().Height() - aImage.GetSizePixel().Height()) / 2;
1617 aVirDev->Push(vcl::PushFlags::MAPMODE);
1618 aVirDev->SetMapMode(MapMode(MapUnit::MapPixel));
1619 aVirDev->Erase();
1620 aVirDev->DrawImage(Point(0, nPos), aImage);
1621 m_xControl->set_image(aVirDev.get());
1622 aVirDev->Pop();
1626 SvtCalendarBox::SvtCalendarBox(std::unique_ptr<weld::MenuButton> pControl, bool bUseLabel)
1627 : m_bUseLabel(bUseLabel)
1628 , m_xControl(std::move(pControl))
1629 , m_xBuilder(Application::CreateBuilder(m_xControl.get(), "svt/ui/datewindow.ui"))
1630 , m_xTopLevel(m_xBuilder->weld_widget("date_popup_window"))
1631 , m_xCalendar(m_xBuilder->weld_calendar("date"))
1633 m_xControl->set_popover(m_xTopLevel.get());
1634 m_xCalendar->connect_selected(LINK(this, SvtCalendarBox, SelectHdl));
1635 m_xCalendar->connect_activated(LINK(this, SvtCalendarBox, ActivateHdl));
1638 void SvtCalendarBox::set_date(const Date& rDate)
1640 m_xCalendar->set_date(rDate);
1641 set_label_from_date();
1644 void SvtCalendarBox::set_label_from_date()
1646 if (!m_bUseLabel)
1647 return;
1648 const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
1649 m_xControl->set_label(rLocaleData.getDate(m_xCalendar->get_date()));
1652 IMPL_LINK_NOARG(SvtCalendarBox, SelectHdl, weld::Calendar&, void)
1654 set_label_from_date();
1655 m_aSelectHdl.Call(*this);
1658 IMPL_LINK_NOARG(SvtCalendarBox, ActivateHdl, weld::Calendar&, void)
1660 if (m_xControl->get_active())
1661 m_xControl->set_active(false);
1662 m_aActivatedHdl.Call(*this);
1665 SvtCalendarBox::~SvtCalendarBox()
1669 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */