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