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