/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <config_folders.h>
#include <i18nutil/unicode.hxx>
#include <tools/stream.hxx>
#include <vcl/builderfactory.hxx>
#include <vcl/customweld.hxx>
#include <vcl/svapp.hxx>
#include <vcl/field.hxx>
#include <vcl/settings.hxx>
#include <sal/macros.h>
#include <sal/log.hxx>
#include <comphelper/processfactory.hxx>
#include <comphelper/string.hxx>
#include <unotools/charclass.hxx>
#include <unotools/fontoptions.hxx>
#include <svtools/sampletext.hxx>
#include <svtools/svtresid.hxx>
#include <svtools/strings.hrc>
#include <svtools/ctrlbox.hxx>
#include <svtools/ctrltool.hxx>
#include <svtools/borderhelper.hxx>
#include <svtools/valueset.hxx>
#include <vcl/i18nhelp.hxx>
#include <vcl/fontcapabilities.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <editeng/borderline.hxx>
#include <com/sun/star/table/BorderLineStyle.hpp>
#include <rtl/bootstrap.hxx>
#include <borderline.hrc>
#include <stdio.h>
#define IMGOUTERTEXTSPACE 5
#define EXTRAFONTSIZE 5
#define GAPTOEXTRAPREVIEW 10
#define MAXPREVIEWWIDTH 120
#define MINGAPWIDTH 2
#define FONTNAMEBOXMRUENTRIESFILE "/user/config/fontnameboxmruentries"
BorderWidthImpl::BorderWidthImpl( BorderWidthImplFlags nFlags, double nRate1, double nRate2, double nRateGap ):
m_nFlags( nFlags ),
m_nRate1( nRate1 ),
m_nRate2( nRate2 ),
m_nRateGap( nRateGap )
{
}
bool BorderWidthImpl::operator== ( const BorderWidthImpl& r ) const
{
return ( m_nFlags == r.m_nFlags ) &&
( m_nRate1 == r.m_nRate1 ) &&
( m_nRate2 == r.m_nRate2 ) &&
( m_nRateGap == r.m_nRateGap );
}
long BorderWidthImpl::GetLine1( long nWidth ) const
{
long result = static_cast<long>(m_nRate1);
if ( m_nFlags & BorderWidthImplFlags::CHANGE_LINE1 )
{
long const nConstant2 = (m_nFlags & BorderWidthImplFlags::CHANGE_LINE2) ? 0 : m_nRate2;
long const nConstantD = (m_nFlags & BorderWidthImplFlags::CHANGE_DIST ) ? 0 : m_nRateGap;
result = std::max<long>(0,
static_cast<long>((m_nRate1 * nWidth) + 0.5)
- (nConstant2 + nConstantD));
if (result == 0 && m_nRate1 > 0.0 && nWidth > 0)
{ // fdo#51777: hack to essentially treat 1 twip DOUBLE border
result = 1; // as 1 twip SINGLE border
}
}
return result;
}
long BorderWidthImpl::GetLine2( long nWidth ) const
{
long result = static_cast<long>(m_nRate2);
if ( m_nFlags & BorderWidthImplFlags::CHANGE_LINE2)
{
long const nConstant1 = (m_nFlags & BorderWidthImplFlags::CHANGE_LINE1) ? 0 : m_nRate1;
long const nConstantD = (m_nFlags & BorderWidthImplFlags::CHANGE_DIST ) ? 0 : m_nRateGap;
result = std::max<long>(0,
static_cast<long>((m_nRate2 * nWidth) + 0.5)
- (nConstant1 + nConstantD));
}
return result;
}
long BorderWidthImpl::GetGap( long nWidth ) const
{
long result = static_cast<long>(m_nRateGap);
if ( m_nFlags & BorderWidthImplFlags::CHANGE_DIST )
{
long const nConstant1 = (m_nFlags & BorderWidthImplFlags::CHANGE_LINE1) ? 0 : m_nRate1;
long const nConstant2 = (m_nFlags & BorderWidthImplFlags::CHANGE_LINE2) ? 0 : m_nRate2;
result = std::max<long>(0,
static_cast<long>((m_nRateGap * nWidth) + 0.5)
- (nConstant1 + nConstant2));
}
// Avoid having too small distances (less than 0.1pt)
if ( result < MINGAPWIDTH && m_nRate1 > 0 && m_nRate2 > 0 )
result = MINGAPWIDTH;
return result;
}
static double lcl_getGuessedWidth( long nTested, double nRate, bool bChanging )
{
double nWidth = -1.0;
if ( bChanging )
nWidth = double( nTested ) / nRate;
else
{
if ( rtl::math::approxEqual(double( nTested ), nRate) )
nWidth = nRate;
}
return nWidth;
}
long BorderWidthImpl::GuessWidth( long nLine1, long nLine2, long nGap )
{
std::vector< double > aToCompare;
bool bInvalid = false;
bool bLine1Change = bool( m_nFlags & BorderWidthImplFlags::CHANGE_LINE1 );
double nWidth1 = lcl_getGuessedWidth( nLine1, m_nRate1, bLine1Change );
if ( bLine1Change )
aToCompare.push_back( nWidth1 );
else if ( !bLine1Change && nWidth1 < 0 )
bInvalid = true;
bool bLine2Change = bool( m_nFlags & BorderWidthImplFlags::CHANGE_LINE2 );
double nWidth2 = lcl_getGuessedWidth( nLine2, m_nRate2, bLine2Change );
if ( bLine2Change )
aToCompare.push_back( nWidth2 );
else if ( !bLine2Change && nWidth2 < 0 )
bInvalid = true;
bool bGapChange = bool( m_nFlags & BorderWidthImplFlags::CHANGE_DIST );
double nWidthGap = lcl_getGuessedWidth( nGap, m_nRateGap, bGapChange );
if ( bGapChange && nGap >= MINGAPWIDTH )
aToCompare.push_back( nWidthGap );
else if ( !bGapChange && nWidthGap < 0 )
bInvalid = true;
// non-constant line width factors must sum to 1
assert((((bLine1Change) ? m_nRate1 : 0) +
((bLine2Change) ? m_nRate2 : 0) +
((bGapChange) ? m_nRateGap : 0)) - 1.0 < 0.00001 );
double nWidth = 0.0;
if ( (!bInvalid) && (!aToCompare.empty()) )
{
nWidth = *aToCompare.begin();
for (auto const& elem : aToCompare)
{
bInvalid = ( nWidth != elem );
if (bInvalid)
break;
}
nWidth = bInvalid ? 0.0 : nLine1 + nLine2 + nGap;
}
return nWidth;
}
/** Utility class storing the border line width, style and colors. The widths
are defined in Twips.
*/
class ImpLineListData
{
private:
BorderWidthImpl m_aWidthImpl;
Color ( *m_pColor1Fn )( Color );
Color ( *m_pColor2Fn )( Color );
Color ( *m_pColorDistFn )( Color, Color );
long m_nMinWidth;
SvxBorderLineStyle m_nStyle;
public:
ImpLineListData( BorderWidthImpl aWidthImpl, SvxBorderLineStyle nStyle,
long nMinWidth,
Color ( *pColor1Fn ) ( Color ),
Color ( *pColor2Fn ) ( Color ),
Color ( *pColorDistFn ) ( Color, Color ) );
/** Returns the computed width of the line 1 in twips. */
long GetLine1ForWidth( long nWidth ) { return m_aWidthImpl.GetLine1( nWidth ); }
/** Returns the computed width of the line 2 in twips. */
long GetLine2ForWidth( long nWidth ) { return m_aWidthImpl.GetLine2( nWidth ); }
/** Returns the computed width of the gap in twips. */
long GetDistForWidth( long nWidth ) { return m_aWidthImpl.GetGap( nWidth ); }
Color GetColorLine1( const Color& aMain );
Color GetColorLine2( const Color& aMain );
Color GetColorDist( const Color& aMain, const Color& rDefault );
/** Returns the minimum width in twips */
long GetMinWidth( ) { return m_nMinWidth;}
SvxBorderLineStyle GetStyle( ) { return m_nStyle;}
};
ImpLineListData::ImpLineListData( BorderWidthImpl aWidthImpl,
SvxBorderLineStyle nStyle, long nMinWidth, Color ( *pColor1Fn )( Color ),
Color ( *pColor2Fn )( Color ), Color ( *pColorDistFn )( Color, Color ) ) :
m_aWidthImpl( aWidthImpl ),
m_pColor1Fn( pColor1Fn ),
m_pColor2Fn( pColor2Fn ),
m_pColorDistFn( pColorDistFn ),
m_nMinWidth( nMinWidth ),
m_nStyle( nStyle )
{
}
Color ImpLineListData::GetColorLine1( const Color& rMain )
{
return ( *m_pColor1Fn )( rMain );
}
Color ImpLineListData::GetColorLine2( const Color& rMain )
{
return ( *m_pColor2Fn )( rMain );
}
Color ImpLineListData::GetColorDist( const Color& rMain, const Color& rDefault )
{
return ( *m_pColorDistFn )( rMain, rDefault );
}
SvxBorderLineStyle LineListBox::GetSelectEntryStyle() const
{
SvxBorderLineStyle nStyle = SvxBorderLineStyle::SOLID;
sal_Int32 nPos = GetSelectedEntryPos();
if ( nPos != LISTBOX_ENTRY_NOTFOUND )
{
if (!m_sNone.isEmpty())
nPos--;
nStyle = GetEntryStyle( nPos );
}
return nStyle;
}
void lclDrawPolygon( OutputDevice& rDev, const basegfx::B2DPolygon& rPolygon, long nWidth, SvxBorderLineStyle nDashing )
{
AntialiasingFlags nOldAA = rDev.GetAntialiasing();
rDev.SetAntialiasing( nOldAA & ~AntialiasingFlags::EnableB2dDraw );
long nPix = rDev.PixelToLogic(Size(1, 1)).Width();
basegfx::B2DPolyPolygon aPolygons = svtools::ApplyLineDashing(rPolygon, nDashing, nPix);
// Handle problems of width 1px in Pixel mode: 0.5px gives a 1px line
if (rDev.GetMapMode().GetMapUnit() == MapUnit::MapPixel && nWidth == nPix)
nWidth = 0;
for ( sal_uInt32 i = 0; i < aPolygons.count( ); i++ )
{
basegfx::B2DPolygon aDash = aPolygons.getB2DPolygon( i );
basegfx::B2DPoint aStart = aDash.getB2DPoint( 0 );
basegfx::B2DPoint aEnd = aDash.getB2DPoint( aDash.count() - 1 );
basegfx::B2DVector aVector( aEnd - aStart );
aVector.normalize( );
const basegfx::B2DVector aPerpendicular(basegfx::getPerpendicular(aVector));
const basegfx::B2DVector aWidthOffset( double( nWidth ) / 2 * aPerpendicular);
basegfx::B2DPolygon aDashPolygon;
aDashPolygon.append( aStart + aWidthOffset );
aDashPolygon.append( aEnd + aWidthOffset );
aDashPolygon.append( aEnd - aWidthOffset );
aDashPolygon.append( aStart - aWidthOffset );
aDashPolygon.setClosed( true );
rDev.DrawPolygon( aDashPolygon );
}
rDev.SetAntialiasing( nOldAA );
}
namespace svtools {
/**
* Dashing array must start with a line width and end with a blank width.
*/
std::vector<double> GetDashing( SvxBorderLineStyle nDashing )
{
std::vector<double> aPattern;
switch (nDashing)
{
case SvxBorderLineStyle::DOTTED:
aPattern.push_back( 1.0 ); // line
aPattern.push_back( 2.0 ); // blank
break;
case SvxBorderLineStyle::DASHED:
aPattern.push_back( 16.0 ); // line
aPattern.push_back( 5.0 ); // blank
break;
case SvxBorderLineStyle::FINE_DASHED:
aPattern.push_back( 6.0 ); // line
aPattern.push_back( 2.0 ); // blank
break;
case SvxBorderLineStyle::DASH_DOT:
aPattern.push_back( 16.0 ); // line
aPattern.push_back( 5.0 ); // blank
aPattern.push_back( 5.0 ); // line
aPattern.push_back( 5.0 ); // blank
break;
case SvxBorderLineStyle::DASH_DOT_DOT:
aPattern.push_back( 16.0 ); // line
aPattern.push_back( 5.0 ); // blank
aPattern.push_back( 5.0 ); // line
aPattern.push_back( 5.0 ); // blank
aPattern.push_back( 5.0 ); // line
aPattern.push_back( 5.0 ); // blank
break;
default:
;
}
return aPattern;
}
namespace {
class ApplyScale
{
double mfScale;
public:
explicit ApplyScale( double fScale ) : mfScale(fScale) {}
void operator() ( double& rVal )
{
rVal *= mfScale;
}
};
}
std::vector<double> GetLineDashing( SvxBorderLineStyle nDashing, double fScale )
{
std::vector<double> aPattern = GetDashing(nDashing);
std::for_each(aPattern.begin(), aPattern.end(), ApplyScale(fScale));
return aPattern;
}
basegfx::B2DPolyPolygon ApplyLineDashing( const basegfx::B2DPolygon& rPolygon, SvxBorderLineStyle nDashing, double fScale )
{
std::vector<double> aPattern = GetDashing(nDashing);
std::for_each(aPattern.begin(), aPattern.end(), ApplyScale(fScale));
basegfx::B2DPolyPolygon aPolygons;
if (aPattern.empty())
aPolygons.append(rPolygon);
else
basegfx::utils::applyLineDashing(rPolygon, aPattern, &aPolygons);
return aPolygons;
}
void DrawLine( OutputDevice& rDev, const Point& rP1, const Point& rP2,
sal_uInt32 nWidth, SvxBorderLineStyle nDashing )
{
DrawLine( rDev, basegfx::B2DPoint( rP1.X(), rP1.Y() ),
basegfx::B2DPoint( rP2.X(), rP2.Y( ) ), nWidth, nDashing );
}
void DrawLine( OutputDevice& rDev, const basegfx::B2DPoint& rP1, const basegfx::B2DPoint& rP2,
sal_uInt32 nWidth, SvxBorderLineStyle nDashing )
{
basegfx::B2DPolygon aPolygon;
aPolygon.append( rP1 );
aPolygon.append( rP2 );
lclDrawPolygon( rDev, aPolygon, nWidth, nDashing );
}
}
void LineListBox::ImpGetLine( long nLine1, long nLine2, long nDistance,
Color aColor1, Color aColor2, Color aColorDist,
SvxBorderLineStyle nStyle, BitmapEx& rBmp )
{
//TODO, rather than including the " " text to force
//the line height, better would be do drop
//this calculation and draw a bitmap of height
//equal to normal text line and center the
//line within that
long nMinWidth = GetTextWidth("----------");
Size aSize = CalcSubEditSize();
aSize.setWidth( std::max(nMinWidth, aSize.Width()) );
aSize.AdjustWidth( -(aTxtSize.Width()) );
aSize.AdjustWidth( -6 );
aSize.setHeight( aTxtSize.Height() );
// SourceUnit to Twips
if ( eSourceUnit == FUNIT_POINT )
{
nLine1 /= 5;
nLine2 /= 5;
nDistance /= 5;
}
// Paint the lines
aSize = aVirDev->PixelToLogic( aSize );
long nPix = aVirDev->PixelToLogic( Size( 0, 1 ) ).Height();
sal_uInt32 n1 = nLine1;
sal_uInt32 n2 = nLine2;
long nDist = nDistance;
n1 += nPix-1;
n1 -= n1%nPix;
if ( n2 )
{
nDist += nPix-1;
nDist -= nDist%nPix;
n2 += nPix-1;
n2 -= n2%nPix;
}
long nVirHeight = n1+nDist+n2;
if ( nVirHeight > aSize.Height() )
aSize.setHeight( nVirHeight );
// negative width should not be drawn
if ( aSize.Width() <= 0 )
return;
Size aVirSize = aVirDev->LogicToPixel( aSize );
if ( aVirDev->GetOutputSizePixel() != aVirSize )
aVirDev->SetOutputSizePixel( aVirSize );
aVirDev->SetFillColor( aColorDist );
aVirDev->DrawRect( tools::Rectangle( Point(), aSize ) );
aVirDev->SetFillColor( aColor1 );
double y1 = double( n1 ) / 2;
svtools::DrawLine( *aVirDev.get(), basegfx::B2DPoint( 0, y1 ), basegfx::B2DPoint( aSize.Width( ), y1 ), n1, nStyle );
if ( n2 )
{
double y2 = n1 + nDist + double( n2 ) / 2;
aVirDev->SetFillColor( aColor2 );
svtools::DrawLine( *aVirDev.get(), basegfx::B2DPoint( 0, y2 ), basegfx::B2DPoint( aSize.Width(), y2 ), n2, SvxBorderLineStyle::SOLID );
}
rBmp = aVirDev->GetBitmapEx( Point(), Size( aSize.Width(), n1+nDist+n2 ) );
}
LineListBox::LineListBox( vcl::Window* pParent, WinBits nWinStyle ) :
ListBox( pParent, nWinStyle ),
m_nWidth( 5 ),
m_sNone( ),
aVirDev( VclPtr<VirtualDevice>::Create() ),
aColor( COL_BLACK ),
maPaintCol( COL_BLACK )
{
aTxtSize.setWidth( GetTextWidth( " " ) );
aTxtSize.setHeight( GetTextHeight() );
eSourceUnit = FUNIT_POINT;
aVirDev->SetLineColor();
aVirDev->SetMapMode( MapMode( MapUnit::MapTwip ) );
UpdatePaintLineColor();
}
LineListBox::~LineListBox()
{
disposeOnce();
}
void LineListBox::dispose()
{
m_vLineList.clear();
ListBox::dispose();
}
sal_Int32 LineListBox::GetStylePos( sal_Int32 nListPos, long nWidth )
{
sal_Int32 nPos = LISTBOX_ENTRY_NOTFOUND;
if (!m_sNone.isEmpty())
nListPos--;
sal_Int32 n = 0;
size_t i = 0;
size_t nCount = m_vLineList.size();
while ( nPos == LISTBOX_ENTRY_NOTFOUND && i < nCount )
{
auto& pData = m_vLineList[ i ];
if ( pData->GetMinWidth() <= nWidth )
{
if ( nListPos == n )
nPos = static_cast<sal_Int32>(i);
n++;
}
i++;
}
return nPos;
}
void LineListBox::InsertEntry(
const BorderWidthImpl& rWidthImpl, SvxBorderLineStyle nStyle, long nMinWidth,
ColorFunc pColor1Fn, ColorFunc pColor2Fn, ColorDistFunc pColorDistFn )
{
m_vLineList.emplace_back(new ImpLineListData(
rWidthImpl, nStyle, nMinWidth, pColor1Fn, pColor2Fn, pColorDistFn));
}
SvxBorderLineStyle LineListBox::GetEntryStyle( sal_Int32 nPos ) const
{
ImpLineListData* pData = (0 <= nPos && static_cast<size_t>(nPos) < m_vLineList.size()) ? m_vLineList[ nPos ].get() : nullptr;
return pData ? pData->GetStyle() : SvxBorderLineStyle::NONE;
}
void LineListBox::UpdatePaintLineColor()
{
const StyleSettings& rSettings = GetSettings().GetStyleSettings();
Color aNewCol( rSettings.GetWindowColor().IsDark()? rSettings.GetLabelTextColor() : aColor );
bool bRet = aNewCol != maPaintCol;
if( bRet )
maPaintCol = aNewCol;
}
void LineListBox::UpdateEntries( long nOldWidth )
{
SetUpdateMode( false );
UpdatePaintLineColor( );
sal_Int32 nSelEntry = GetSelectedEntryPos();
sal_Int32 nTypePos = GetStylePos( nSelEntry, nOldWidth );
// Remove the old entries
while ( GetEntryCount( ) > 0 )
ListBox::RemoveEntry( 0 );
// Add the new entries based on the defined width
if (!m_sNone.isEmpty())
ListBox::InsertEntry( m_sNone );
sal_uInt16 n = 0;
sal_uInt16 nCount = m_vLineList.size( );
while ( n < nCount )
{
auto& pData = m_vLineList[ n ];
if ( pData->GetMinWidth() <= m_nWidth )
{
BitmapEx aBmp;
ImpGetLine( pData->GetLine1ForWidth( m_nWidth ),
pData->GetLine2ForWidth( m_nWidth ),
pData->GetDistForWidth( m_nWidth ),
GetColorLine1( GetEntryCount( ) ),
GetColorLine2( GetEntryCount( ) ),
GetColorDist( GetEntryCount( ) ),
pData->GetStyle(), aBmp );
ListBox::InsertEntry(" ", Image(aBmp));
if ( n == nTypePos )
SelectEntryPos( GetEntryCount() - 1 );
}
else if ( n == nTypePos )
SetNoSelection();
n++;
}
SetUpdateMode( true );
Invalidate();
}
Color LineListBox::GetColorLine1( sal_Int32 nPos )
{
sal_Int32 nStyle = GetStylePos( nPos, m_nWidth );
if (nStyle == LISTBOX_ENTRY_NOTFOUND)
return GetPaintColor( );
auto& pData = m_vLineList[ nStyle ];
return pData->GetColorLine1( GetColor( ) );
}
Color LineListBox::GetColorLine2( sal_Int32 nPos )
{
sal_Int32 nStyle = GetStylePos( nPos, m_nWidth );
if (nStyle == LISTBOX_ENTRY_NOTFOUND)
return GetPaintColor( );
auto& pData = m_vLineList[ nStyle ];
return pData->GetColorLine2( GetColor( ) );
}
Color LineListBox::GetColorDist( sal_Int32 nPos )
{
Color rResult = GetSettings().GetStyleSettings().GetFieldColor();
sal_Int32 nStyle = GetStylePos( nPos, m_nWidth );
if (nStyle == LISTBOX_ENTRY_NOTFOUND)
return rResult;
auto& pData = m_vLineList[ nStyle ];
return pData->GetColorDist( GetColor( ), rResult );
}
void LineListBox::DataChanged( const DataChangedEvent& rDCEvt )
{
ListBox::DataChanged( rDCEvt );
if( ( rDCEvt.GetType() == DataChangedEventType::SETTINGS ) && ( rDCEvt.GetFlags() & AllSettingsFlags::STYLE ) )
UpdateEntries( m_nWidth );
}
FontNameBox::FontNameBox( vcl::Window* pParent, WinBits nWinStyle ) :
ComboBox( pParent, nWinStyle )
{
EnableSelectAll();
mbWYSIWYG = false;
InitFontMRUEntriesFile();
}
extern "C" SAL_DLLPUBLIC_EXPORT void makeFontNameBox(VclPtr<vcl::Window> & rRet, VclPtr<vcl::Window> & pParent, VclBuilder::stringmap & rMap)
{
bool bDropdown = BuilderUtils::extractDropdown(rMap);
WinBits nWinBits = WB_LEFT|WB_VCENTER|WB_3DLOOK|WB_TABSTOP;
if (bDropdown)
nWinBits |= WB_DROPDOWN;
VclPtrInstance<FontNameBox> pListBox(pParent, nWinBits);
if (bDropdown)
pListBox->EnableAutoSize(true);
rRet = pListBox;
}
FontNameBox::~FontNameBox()
{
disposeOnce();
}
void FontNameBox::dispose()
{
if (mpFontList)
{
SaveMRUEntries (maFontMRUEntriesFile);
ImplDestroyFontList();
}
ComboBox::dispose();
}
void FontNameBox::SaveMRUEntries( const OUString& aFontMRUEntriesFile ) const
{
OString aEntries(OUStringToOString(GetMRUEntries(),
RTL_TEXTENCODING_UTF8));
if (aEntries.isEmpty() || aFontMRUEntriesFile.isEmpty())
return;
SvFileStream aStream;
aStream.Open( aFontMRUEntriesFile, StreamMode::WRITE | StreamMode::TRUNC );
if( ! (aStream.IsOpen() && aStream.IsWritable()) )
{
SAL_INFO("svtools.control", "FontNameBox::SaveMRUEntries: opening mru entries file " << aFontMRUEntriesFile << " failed");
return;
}
aStream.SetLineDelimiter( LINEEND_LF );
aStream.WriteLine( aEntries );
aStream.WriteLine( OString() );
}
void FontNameBox::LoadMRUEntries( const OUString& aFontMRUEntriesFile )
{
if (aFontMRUEntriesFile.isEmpty())
return;
SvtFontOptions aFontOpt;
if (!aFontOpt.IsFontHistoryEnabled())
return;
SvFileStream aStream( aFontMRUEntriesFile, StreamMode::READ );
if( ! aStream.IsOpen() )
{
SAL_INFO("svtools.control", "FontNameBox::LoadMRUEntries: opening mru entries file " << aFontMRUEntriesFile << " failed");
return;
}
OString aLine;
aStream.ReadLine( aLine );
OUString aEntries = OStringToOUString(aLine,
RTL_TEXTENCODING_UTF8);
SetMRUEntries( aEntries );
}
void FontNameBox::InitFontMRUEntriesFile()
{
OUString sUserConfigDir("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE( "bootstrap") "::UserInstallation}");
rtl::Bootstrap::expandMacros(sUserConfigDir);
maFontMRUEntriesFile = sUserConfigDir;
if( !maFontMRUEntriesFile.isEmpty() )
{
maFontMRUEntriesFile += FONTNAMEBOXMRUENTRIESFILE;
}
}
void FontNameBox::ImplDestroyFontList()
{
mpFontList.reset();
}
void FontNameBox::Fill( const FontList* pList )
{
// store old text and clear box
OUString aOldText = GetText();
OUString rEntries = GetMRUEntries();
bool bLoadFromFile = rEntries.isEmpty();
Clear();
ImplDestroyFontList();
mpFontList.reset(new ImplFontList);
// insert fonts
sal_uInt16 nFontCount = pList->GetFontNameCount();
for ( sal_uInt16 i = 0; i < nFontCount; i++ )
{
const FontMetric& rFontMetric = pList->GetFontName( i );
sal_uLong nIndex = InsertEntry( rFontMetric.GetFamilyName() );
if ( nIndex != LISTBOX_ERROR )
{
if ( nIndex < mpFontList->size() ) {
ImplFontList::iterator it = mpFontList->begin();
::std::advance( it, nIndex );
mpFontList->insert( it, rFontMetric );
} else {
mpFontList->push_back( rFontMetric );
}
}
}
if ( bLoadFromFile )
LoadMRUEntries (maFontMRUEntriesFile);
else
SetMRUEntries( rEntries );
ImplCalcUserItemSize();
// restore text
if (!aOldText.isEmpty())
SetText( aOldText );
}
void FontNameBox::EnableWYSIWYG( bool bEnable )
{
if ( bEnable != mbWYSIWYG )
{
mbWYSIWYG = bEnable;
EnableUserDraw( mbWYSIWYG );
ImplCalcUserItemSize();
}
}
void FontNameBox::ImplCalcUserItemSize()
{
Size aUserItemSz;
if ( mbWYSIWYG && mpFontList )
{
aUserItemSz = Size(MAXPREVIEWWIDTH, GetTextHeight() );
aUserItemSz.setHeight( aUserItemSz.Height() * 16 );
aUserItemSz.setHeight( aUserItemSz.Height() / 10 );
}
SetUserItemSize( aUserItemSz );
}
namespace
{
long shrinkFontToFit(OUString const &rSampleText, long nH, vcl::Font &rFont, OutputDevice &rDevice, tools::Rectangle &rTextRect)
{
long nWidth = 0;
Size aSize( rFont.GetFontSize() );
//Make sure it fits in the available height
while (aSize.Height() > 0)
{
if (!rDevice.GetTextBoundRect(rTextRect, rSampleText))
break;
if (rTextRect.GetHeight() <= nH)
{
nWidth = rTextRect.GetWidth();
break;
}
aSize.AdjustHeight( -(EXTRAFONTSIZE) );
rFont.SetFontSize(aSize);
rDevice.SetFont(rFont);
}
return nWidth;
}
}
void FontNameBox::UserDraw( const UserDrawEvent& rUDEvt )
{
assert( mpFontList );
FontMetric& rFontMetric = (*mpFontList)[ rUDEvt.GetItemId() ];
Point aTopLeft = rUDEvt.GetRect().TopLeft();
long nX = aTopLeft.X();
long nH = rUDEvt.GetRect().GetHeight();
if ( mbWYSIWYG )
{
nX += IMGOUTERTEXTSPACE;
const bool bSymbolFont = isSymbolFont(rFontMetric);
vcl::RenderContext* pRenderContext = rUDEvt.GetRenderContext();
Color aTextColor = pRenderContext->GetTextColor();
vcl::Font aOldFont(pRenderContext->GetFont());
Size aSize( aOldFont.GetFontSize() );
aSize.AdjustHeight(EXTRAFONTSIZE );
vcl::Font aFont( rFontMetric );
aFont.SetFontSize( aSize );
pRenderContext->SetFont(aFont);
pRenderContext->SetTextColor(aTextColor);
bool bUsingCorrectFont = true;
tools::Rectangle aTextRect;
// Preview the font name
OUString sFontName = rFontMetric.GetFamilyName();
//If it shouldn't or can't draw its own name because it doesn't have the glyphs
if (!canRenderNameOfSelectedFont(*pRenderContext))
bUsingCorrectFont = false;
else
{
//Make sure it fits in the available height, shrinking the font if necessary
bUsingCorrectFont = shrinkFontToFit(sFontName, nH, aFont, *pRenderContext, aTextRect) != 0;
}
if (!bUsingCorrectFont)
{
pRenderContext->SetFont(aOldFont);
pRenderContext->GetTextBoundRect(aTextRect, sFontName);
}
long nTextHeight = aTextRect.GetHeight();
long nDesiredGap = (nH-nTextHeight)/2;
long nVertAdjust = nDesiredGap - aTextRect.Top();
Point aPos( nX, aTopLeft.Y() + nVertAdjust );
pRenderContext->DrawText(aPos, sFontName);
long nTextX = aPos.X() + aTextRect.GetWidth() + GAPTOEXTRAPREVIEW;
if (!bUsingCorrectFont)
pRenderContext->SetFont(aFont);
OUString sSampleText;
if (!bSymbolFont)
{
const bool bNameBeginsWithLatinText = rFontMetric.GetFamilyName()[0] <= 'z';
if (bNameBeginsWithLatinText || !bUsingCorrectFont)
sSampleText = makeShortRepresentativeTextForSelectedFont(*pRenderContext);
}
//If we're not a symbol font, but could neither render our own name and
//we can't determine what script it would like to render, then try a
//few well known scripts
if (sSampleText.isEmpty() && !bUsingCorrectFont)
{
static const UScriptCode aScripts[] =
{
USCRIPT_ARABIC,
USCRIPT_HEBREW,
USCRIPT_BENGALI,
USCRIPT_GURMUKHI,
USCRIPT_GUJARATI,
USCRIPT_ORIYA,
USCRIPT_TAMIL,
USCRIPT_TELUGU,
USCRIPT_KANNADA,
USCRIPT_MALAYALAM,
USCRIPT_SINHALA,
USCRIPT_DEVANAGARI,
USCRIPT_THAI,
USCRIPT_LAO,
USCRIPT_GEORGIAN,
USCRIPT_TIBETAN,
USCRIPT_SYRIAC,
USCRIPT_MYANMAR,
USCRIPT_ETHIOPIC,
USCRIPT_KHMER,
USCRIPT_MONGOLIAN,
USCRIPT_KOREAN,
USCRIPT_JAPANESE,
USCRIPT_HAN,
USCRIPT_SIMPLIFIED_HAN,
USCRIPT_TRADITIONAL_HAN,
USCRIPT_GREEK
};
for (const UScriptCode& rScript : aScripts)
{
OUString sText = makeShortRepresentativeTextForScript(rScript);
if (!sText.isEmpty())
{
bool bHasSampleTextGlyphs = (-1 == pRenderContext->HasGlyphs(aFont, sText));
if (bHasSampleTextGlyphs)
{
sSampleText = sText;
break;
}
}
}
static const UScriptCode aMinimalScripts[] =
{
USCRIPT_HEBREW, //e.g. biblical hebrew
USCRIPT_GREEK
};
for (const UScriptCode& rMinimalScript : aMinimalScripts)
{
OUString sText = makeShortMinimalTextForScript(rMinimalScript);
if (!sText.isEmpty())
{
bool bHasSampleTextGlyphs = (-1 == pRenderContext->HasGlyphs(aFont, sText));
if (bHasSampleTextGlyphs)
{
sSampleText = sText;
break;
}
}
}
}
//If we're a symbol font, or for some reason the font still couldn't
//render something representative of what it would like to render then
//make up some semi-random text that it *can* display
if (bSymbolFont || (!bUsingCorrectFont && sSampleText.isEmpty()))
sSampleText = makeShortRepresentativeSymbolTextForSelectedFont(*pRenderContext);
if (!sSampleText.isEmpty())
{
const Size &rItemSize = rUDEvt.GetWindow()->GetOutputSize();
//leave a little border at the edge
long nSpace = rItemSize.Width() - nTextX - IMGOUTERTEXTSPACE;
if (nSpace >= 0)
{
//Make sure it fits in the available height, and get how wide that would be
long nWidth = shrinkFontToFit(sSampleText, nH, aFont, *pRenderContext, aTextRect);
//Chop letters off until it fits in the available width
while (nWidth > nSpace || nWidth > MAXPREVIEWWIDTH)
{
sSampleText = sSampleText.copy(0, sSampleText.getLength()-1);
nWidth = pRenderContext->GetTextBoundRect(aTextRect, sSampleText) ?
aTextRect.GetWidth() : 0;
}
//center the text on the line
if (!sSampleText.isEmpty() && nWidth)
{
nTextHeight = aTextRect.GetHeight();
nDesiredGap = (nH-nTextHeight)/2;
nVertAdjust = nDesiredGap - aTextRect.Top();
aPos = Point(nTextX + nSpace - nWidth, aTopLeft.Y() + nVertAdjust);
pRenderContext->DrawText(aPos, sSampleText);
}
}
}
pRenderContext->SetFont(aOldFont);
DrawEntry( rUDEvt, false, false); // draw separator
}
else
{
DrawEntry( rUDEvt, true, true );
}
}
FontStyleBox::FontStyleBox(vcl::Window* pParent, WinBits nBits)
: ComboBox(pParent, nBits)
{
//Use the standard texts to get an optimal size and stick to that size.
//That should stop the character dialog dancing around.
InsertEntry(SvtResId(STR_SVT_STYLE_LIGHT));
InsertEntry(SvtResId(STR_SVT_STYLE_LIGHT_ITALIC));
InsertEntry(SvtResId(STR_SVT_STYLE_NORMAL));
InsertEntry(SvtResId(STR_SVT_STYLE_NORMAL_ITALIC));
InsertEntry(SvtResId(STR_SVT_STYLE_BOLD));
InsertEntry(SvtResId(STR_SVT_STYLE_BOLD_ITALIC));
InsertEntry(SvtResId(STR_SVT_STYLE_BLACK));
InsertEntry(SvtResId(STR_SVT_STYLE_BLACK_ITALIC));
aOptimalSize = GetOptimalSize();
Clear();
}
Size FontStyleBox::GetOptimalSize() const
{
if (aOptimalSize.Width() || aOptimalSize.Height())
return aOptimalSize;
return ComboBox::GetOptimalSize();
}
extern "C" SAL_DLLPUBLIC_EXPORT void makeFontStyleBox(VclPtr<vcl::Window> & rRet, VclPtr<vcl::Window> & pParent, VclBuilder::stringmap & rMap)
{
bool bDropdown = BuilderUtils::extractDropdown(rMap);
WinBits nWinBits = WB_LEFT|WB_VCENTER|WB_3DLOOK|WB_TABSTOP;
if (bDropdown)
nWinBits |= WB_DROPDOWN;
VclPtrInstance<FontStyleBox> pListBox(pParent, nWinBits);
if (bDropdown)
pListBox->EnableAutoSize(true);
rRet = pListBox;
}
void FontStyleBox::Select()
{
// keep text over fill operation
aLastStyle = GetText();
ComboBox::Select();
}
void FontStyleBox::LoseFocus()
{
// keep text over fill operation
aLastStyle = GetText();
ComboBox::LoseFocus();
}
void FontStyleBox::Modify()
{
CharClass aChrCls( ::comphelper::getProcessComponentContext(),
GetSettings().GetLanguageTag() );
OUString aStr = GetText();
sal_Int32 nEntryCount = GetEntryCount();
if ( GetEntryPos( aStr ) == COMBOBOX_ENTRY_NOTFOUND )
{
aStr = aChrCls.uppercase(aStr);
for ( sal_Int32 i = 0; i < nEntryCount; i++ )
{
OUString aEntryText = aChrCls.uppercase(GetEntry(i));
if ( aStr == aEntryText )
{
SetText( GetEntry( i ) );
break;
}
}
}
ComboBox::Modify();
}
void FontStyleBox::Fill( const OUString& rName, const FontList* pList )
{
// note: this method must call ComboBox::SetText(),
// else aLastStyle will overwritten
// store prior selection position and clear box
OUString aOldText = GetText();
sal_Int32 nPos = GetEntryPos( aOldText );
Clear();
// does a font with this name already exist?
sal_Handle hFontMetric = pList->GetFirstFontMetric( rName );
if ( hFontMetric )
{
OUString aStyleText;
FontWeight eLastWeight = WEIGHT_DONTKNOW;
FontItalic eLastItalic = ITALIC_NONE;
FontWidth eLastWidth = WIDTH_DONTKNOW;
bool bNormal = false;
bool bItalic = false;
bool bBold = false;
bool bBoldItalic = false;
bool bInsert = false;
FontMetric aFontMetric;
while ( hFontMetric )
{
aFontMetric = FontList::GetFontMetric( hFontMetric );
FontWeight eWeight = aFontMetric.GetWeight();
FontItalic eItalic = aFontMetric.GetItalic();
FontWidth eWidth = aFontMetric.GetWidthType();
// Only if the attributes are different, we insert the
// Font to avoid double Entries in different languages
if ( (eWeight != eLastWeight) || (eItalic != eLastItalic) ||
(eWidth != eLastWidth) )
{
if ( bInsert )
InsertEntry( aStyleText );
if ( eWeight <= WEIGHT_NORMAL )
{
if ( eItalic != ITALIC_NONE )
bItalic = true;
else
bNormal = true;
}
else
{
if ( eItalic != ITALIC_NONE )
bBoldItalic = true;
else
bBold = true;
}
// For wrong StyleNames we replace this with the correct once
aStyleText = pList->GetStyleName( aFontMetric );
bInsert = GetEntryPos( aStyleText ) == LISTBOX_ENTRY_NOTFOUND;
if ( !bInsert )
{
aStyleText = pList->GetStyleName( eWeight, eItalic );
bInsert = GetEntryPos( aStyleText ) == LISTBOX_ENTRY_NOTFOUND;
}
eLastWeight = eWeight;
eLastItalic = eItalic;
eLastWidth = eWidth;
}
else
{
if ( bInsert )
{
// If we have two names for the same attributes
// we prefer the translated standard names
const OUString& rAttrStyleText = pList->GetStyleName( eWeight, eItalic );
if (rAttrStyleText != aStyleText)
{
OUString aTempStyleText = pList->GetStyleName( aFontMetric );
if (rAttrStyleText == aTempStyleText)
aStyleText = rAttrStyleText;
bInsert = GetEntryPos( aStyleText ) == LISTBOX_ENTRY_NOTFOUND;
}
}
}
if ( !bItalic && (aStyleText == pList->GetItalicStr()) )
bItalic = true;
else if ( !bBold && (aStyleText == pList->GetBoldStr()) )
bBold = true;
else if ( !bBoldItalic && (aStyleText == pList->GetBoldItalicStr()) )
bBoldItalic = true;
hFontMetric = FontList::GetNextFontMetric( hFontMetric );
}
if ( bInsert )
InsertEntry( aStyleText );
// certain style as copy
if ( bNormal )
{
if ( !bItalic )
InsertEntry( pList->GetItalicStr() );
if ( !bBold )
InsertEntry( pList->GetBoldStr() );
}
if ( !bBoldItalic )
{
if ( bNormal || bItalic || bBold )
InsertEntry( pList->GetBoldItalicStr() );
}
if (!aOldText.isEmpty())
{
if ( GetEntryPos( aLastStyle ) != LISTBOX_ENTRY_NOTFOUND )
ComboBox::SetText( aLastStyle );
else
{
if ( nPos >= GetEntryCount() )
ComboBox::SetText( GetEntry( 0 ) );
else
ComboBox::SetText( GetEntry( nPos ) );
}
}
}
else
{
// insert standard styles if no font
InsertEntry( pList->GetNormalStr() );
InsertEntry( pList->GetItalicStr() );
InsertEntry( pList->GetBoldStr() );
InsertEntry( pList->GetBoldItalicStr() );
if (!aOldText.isEmpty())
{
if ( nPos > GetEntryCount() )
ComboBox::SetText( GetEntry( 0 ) );
else
ComboBox::SetText( GetEntry( nPos ) );
}
}
}
FontSizeBox::FontSizeBox( vcl::Window* pParent, WinBits nWinSize ) :
MetricBox( pParent, nWinSize )
{
ImplInit();
}
extern "C" SAL_DLLPUBLIC_EXPORT void makeFontSizeBox(VclPtr<vcl::Window> & rRet, VclPtr<vcl::Window> & pParent, VclBuilder::stringmap & rMap)
{
bool bDropdown = BuilderUtils::extractDropdown(rMap);
WinBits nWinBits = WB_LEFT|WB_VCENTER|WB_3DLOOK|WB_TABSTOP;
if (bDropdown)
nWinBits |= WB_DROPDOWN;
VclPtrInstance<FontSizeBox> pListBox(pParent, nWinBits);
if (bDropdown)
pListBox->EnableAutoSize(true);
rRet = pListBox;
}
void FontSizeBox::ImplInit()
{
EnableAutocomplete( false );
bRelativeMode = false;
bPtRelative = false;
bRelative = false;
bStdSize = false;
pFontList = nullptr;
SetShowTrailingZeros( false );
SetDecimalDigits( 1 );
SetMin( 20 );
SetMax( 9999 );
SetProminentEntryType( ProminentEntry::MIDDLE );
}
void FontSizeBox::Reformat()
{
FontSizeNames aFontSizeNames( GetSettings().GetUILanguageTag().getLanguageType() );
if ( !bRelativeMode || !aFontSizeNames.IsEmpty() )
{
long nNewValue = aFontSizeNames.Name2Size( GetText() );
if ( nNewValue)
{
mnLastValue = nNewValue;
return;
}
}
MetricBox::Reformat();
}
void FontSizeBox::Modify()
{
MetricBox::Modify();
if ( !bRelativeMode )
return;
OUString aStr = comphelper::string::stripStart(GetText(), ' ');
bool bNewMode = bRelative;
bool bOldPtRelMode = bPtRelative;
if ( bRelative )
{
bPtRelative = false;
const sal_Unicode* pStr = aStr.getStr();
while ( *pStr )
{
if ( ((*pStr < '0') || (*pStr > '9')) && (*pStr != '%') && !unicode::isSpace(*pStr) )
{
if ( ('-' == *pStr || '+' == *pStr) && !bPtRelative )
bPtRelative = true;
else if ( bPtRelative && 'p' == *pStr && 't' == *++pStr )
;
else
{
bNewMode = false;
break;
}
}
pStr++;
}
}
else if (!aStr.isEmpty())
{
if ( -1 != aStr.indexOf('%') )
{
bNewMode = true;
bPtRelative = false;
}
if ( '-' == aStr[0] || '+' == aStr[0] )
{
bNewMode = true;
bPtRelative = true;
}
}
if ( bNewMode != bRelative || bPtRelative != bOldPtRelMode )
SetRelative( bNewMode );
}
void FontSizeBox::Fill( const FontMetric* pFontMetric, const FontList* pList )
{
// remember for relative mode
pFontList = pList;
// no font sizes need to be set for relative mode
if ( bRelative )
return;
// query font sizes
const sal_IntPtr* pTempAry;
const sal_IntPtr* pAry = nullptr;
if( pFontMetric )
{
aFontMetric = *pFontMetric;
pAry = pList->GetSizeAry( *pFontMetric );
}
else
{
pAry = FontList::GetStdSizeAry();
}
// first insert font size names (for simplified/traditional chinese)
FontSizeNames aFontSizeNames( GetSettings().GetUILanguageTag().getLanguageType() );
if ( pAry == FontList::GetStdSizeAry() )
{
// for standard sizes we don't need to bother
if ( bStdSize && GetEntryCount() && aFontSizeNames.IsEmpty() )
return;
bStdSize = true;
}
else
bStdSize = false;
Selection aSelection = GetSelection();
OUString aStr = GetText();
Clear();
sal_Int32 nPos = 0;
if ( !aFontSizeNames.IsEmpty() )
{
if ( pAry == FontList::GetStdSizeAry() )
{
// for scalable fonts all font size names
sal_uLong nCount = aFontSizeNames.Count();
for( sal_uLong i = 0; i < nCount; i++ )
{
OUString aSizeName = aFontSizeNames.GetIndexName( i );
sal_IntPtr nSize = aFontSizeNames.GetIndexSize( i );
ComboBox::InsertEntry( aSizeName, nPos );
ComboBox::SetEntryData( nPos, reinterpret_cast<void*>(-nSize) ); // mark as special
nPos++;
}
}
else
{
// for fixed size fonts only selectable font size names
pTempAry = pAry;
while ( *pTempAry )
{
OUString aSizeName = aFontSizeNames.Size2Name( *pTempAry );
if ( !aSizeName.isEmpty() )
{
ComboBox::InsertEntry( aSizeName, nPos );
ComboBox::SetEntryData( nPos, reinterpret_cast<void*>(-(*pTempAry)) ); // mark as special
nPos++;
}
pTempAry++;
}
}
}
// then insert numerical font size values
pTempAry = pAry;
while ( *pTempAry )
{
InsertValue( *pTempAry, FUNIT_NONE, nPos );
ComboBox::SetEntryData( nPos, reinterpret_cast<void*>(*pTempAry) );
nPos++;
pTempAry++;
}
SetText( aStr );
SetSelection( aSelection );
}
void FontSizeBox::EnableRelativeMode( sal_uInt16 nMin, sal_uInt16 nMax, sal_uInt16 nStep )
{
bRelativeMode = true;
nRelMin = nMin;
nRelMax = nMax;
nRelStep = nStep;
SetUnit( FUNIT_POINT );
}
void FontSizeBox::EnablePtRelativeMode( short nMin, short nMax, short nStep )
{
bRelativeMode = true;
nPtRelMin = nMin;
nPtRelMax = nMax;
nPtRelStep = nStep;
SetUnit( FUNIT_POINT );
}
void FontSizeBox::SetRelative( bool bNewRelative )
{
if ( !bRelativeMode )
return;
Selection aSelection = GetSelection();
OUString aStr = comphelper::string::stripStart(GetText(), ' ');
if ( bNewRelative )
{
bRelative = true;
bStdSize = false;
if ( bPtRelative )
{
Clear(); //clear early because SetDecimalDigits is a slow recalc
SetDecimalDigits( 1 );
SetMin( nPtRelMin );
SetMax( nPtRelMax );
SetUnit( FUNIT_POINT );
short i = nPtRelMin, n = 0;
// JP 30.06.98: more than 100 values are not useful
while ( i <= nPtRelMax && n++ < 100 )
{
InsertValue( i );
i = i + nPtRelStep;
}
}
else
{
Clear(); //clear early because SetDecimalDigits is a slow recalc
SetDecimalDigits( 0 );
SetMin( nRelMin );
SetMax( nRelMax );
SetUnit( FUNIT_PERCENT );
sal_uInt16 i = nRelMin;
while ( i <= nRelMax )
{
InsertValue( i );
i = i + nRelStep;
}
}
}
else
{
if (pFontList)
Clear(); //clear early because SetDecimalDigits is a slow recalc
bRelative = bPtRelative = false;
SetDecimalDigits( 1 );
SetMin( 20 );
SetMax( 9999 );
SetUnit( FUNIT_POINT );
if ( pFontList )
Fill( &aFontMetric, pFontList );
}
SetText( aStr );
SetSelection( aSelection );
}
OUString FontSizeBox::CreateFieldText( sal_Int64 nValue ) const
{
OUString sRet( MetricBox::CreateFieldText( nValue ) );
if ( bRelativeMode && bPtRelative && (0 <= nValue) && !sRet.isEmpty() )
sRet = "+" + sRet;
return sRet;
}
void FontSizeBox::SetValue( sal_Int64 nNewValue, FieldUnit eInUnit )
{
if ( !bRelative )
{
sal_Int64 nTempValue = MetricField::ConvertValue( nNewValue, GetBaseValue(), GetDecimalDigits(), eInUnit, GetUnit() );
FontSizeNames aFontSizeNames( GetSettings().GetUILanguageTag().getLanguageType() );
// conversion loses precision; however font sizes should
// never have a problem with that
OUString aName = aFontSizeNames.Size2Name( static_cast<long>(nTempValue) );
if ( !aName.isEmpty() && (GetEntryPos( aName ) != LISTBOX_ENTRY_NOTFOUND) )
{
mnLastValue = nTempValue;
SetText( aName );
mnFieldValue = mnLastValue;
SetEmptyFieldValueData( false );
return;
}
}
MetricBox::SetValue( nNewValue, eInUnit );
}
void FontSizeBox::SetValue( sal_Int64 nNewValue )
{
SetValue( nNewValue, FUNIT_NONE );
}
sal_Int64 FontSizeBox::GetValueFromStringUnit(const OUString& rStr, FieldUnit eOutUnit) const
{
if ( !bRelative )
{
FontSizeNames aFontSizeNames( GetSettings().GetUILanguageTag().getLanguageType() );
sal_Int64 nValue = aFontSizeNames.Name2Size( rStr );
if ( nValue )
return MetricField::ConvertValue( nValue, GetBaseValue(), GetDecimalDigits(), GetUnit(), eOutUnit );
}
return MetricBox::GetValueFromStringUnit( rStr, eOutUnit );
}
SvxBorderLineStyle SvtLineListBox::GetSelectEntryStyle() const
{
if (m_xLineSet->IsNoSelection())
return SvxBorderLineStyle::NONE;
auto nId = m_xLineSet->GetSelectedItemId();
return static_cast<SvxBorderLineStyle>(nId - 1);
}
namespace
{
Size getPreviewSize(const weld::Widget& rControl)
{
return Size(rControl.get_approximate_digit_width() * 15, rControl.get_text_height());
}
}
void SvtLineListBox::ImpGetLine( long nLine1, long nLine2, long nDistance,
Color aColor1, Color aColor2, Color aColorDist,
SvxBorderLineStyle nStyle, BitmapEx& rBmp )
{
Size aSize(getPreviewSize(*m_xControl));
// SourceUnit to Twips
if ( eSourceUnit == FUNIT_POINT )
{
nLine1 /= 5;
nLine2 /= 5;
nDistance /= 5;
}
// Paint the lines
aSize = aVirDev->PixelToLogic( aSize );
long nPix = aVirDev->PixelToLogic( Size( 0, 1 ) ).Height();
sal_uInt32 n1 = nLine1;
sal_uInt32 n2 = nLine2;
long nDist = nDistance;
n1 += nPix-1;
n1 -= n1%nPix;
if ( n2 )
{
nDist += nPix-1;
nDist -= nDist%nPix;
n2 += nPix-1;
n2 -= n2%nPix;
}
long nVirHeight = n1+nDist+n2;
if ( nVirHeight > aSize.Height() )
aSize.setHeight( nVirHeight );
// negative width should not be drawn
if ( aSize.Width() <= 0 )
return;
Size aVirSize = aVirDev->LogicToPixel( aSize );
if ( aVirDev->GetOutputSizePixel() != aVirSize )
aVirDev->SetOutputSizePixel( aVirSize );
aVirDev->SetFillColor( aColorDist );
aVirDev->DrawRect( tools::Rectangle( Point(), aSize ) );
aVirDev->SetFillColor( aColor1 );
double y1 = double( n1 ) / 2;
svtools::DrawLine( *aVirDev.get(), basegfx::B2DPoint( 0, y1 ), basegfx::B2DPoint( aSize.Width( ), y1 ), n1, nStyle );
if ( n2 )
{
double y2 = n1 + nDist + double( n2 ) / 2;
aVirDev->SetFillColor( aColor2 );
svtools::DrawLine( *aVirDev.get(), basegfx::B2DPoint( 0, y2 ), basegfx::B2DPoint( aSize.Width(), y2 ), n2, SvxBorderLineStyle::SOLID );
}
rBmp = aVirDev->GetBitmapEx( Point(), Size( aSize.Width(), n1+nDist+n2 ) );
}
namespace
{
OUString GetLineStyleName(SvxBorderLineStyle eStyle)
{
OUString sRet;
for (sal_uInt32 i = 0; i < SAL_N_ELEMENTS(RID_SVXSTR_BORDERLINE); ++i)
{
if (eStyle == RID_SVXSTR_BORDERLINE[i].second)
{
sRet = SvtResId(RID_SVXSTR_BORDERLINE[i].first);
break;
}
}
return sRet;
}
}
SvtLineListBox::SvtLineListBox(std::unique_ptr<weld::MenuButton> pControl)
: m_xControl(std::move(pControl))
, m_xBuilder(Application::CreateBuilder(m_xControl.get(), "svt/ui/linewindow.ui"))
, m_xTopLevel(m_xBuilder->weld_widget("line_popup_window"))
, m_xNoneButton(m_xBuilder->weld_button("none_line_button"))
, m_xLineSet(new SvtValueSet(nullptr))
, m_xLineSetWin(new weld::CustomWeld(*m_xBuilder, "lineset", *m_xLineSet))
, m_nWidth( 5 )
, aVirDev(VclPtr<VirtualDevice>::Create())
, aColor(COL_BLACK)
, maPaintCol(COL_BLACK)
{
const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
m_xLineSet->SetStyle(WinBits(WB_FLATVALUESET | WB_NO_DIRECTSELECT | WB_TABSTOP));
m_xLineSet->SetItemHeight(rStyleSettings.GetListBoxPreviewDefaultPixelSize().Height() + 1);
m_xLineSet->SetColCount(1);
m_xLineSet->SetSelectHdl(LINK(this, SvtLineListBox, ValueSelectHdl));
m_xNoneButton->connect_clicked(LINK(this, SvtLineListBox, NoneHdl));
m_xTopLevel->connect_focus_in(LINK(this, SvtLineListBox, FocusHdl));
m_xControl->set_popover(m_xTopLevel.get());
// lock size to these maxes height/width so it doesn't jump around in size
m_xControl->set_label(GetLineStyleName(SvxBorderLineStyle::NONE));
Size aNonePrefSize = m_xControl->get_preferred_size();
m_xControl->set_label("");
aVirDev->SetOutputSizePixel(getPreviewSize(*m_xControl));
m_xControl->set_image(aVirDev);
Size aSolidPrefSize = m_xControl->get_preferred_size();
m_xControl->set_size_request(std::max(aNonePrefSize.Width(), aSolidPrefSize.Width()),
std::max(aNonePrefSize.Height(), aSolidPrefSize.Height()));
eSourceUnit = FUNIT_POINT;
aVirDev->SetLineColor();
aVirDev->SetMapMode(MapMode(MapUnit::MapTwip));
UpdatePaintLineColor();
}
IMPL_LINK_NOARG(SvtLineListBox, FocusHdl, weld::Widget&, void)
{
if (GetSelectEntryStyle() == SvxBorderLineStyle::NONE)
m_xNoneButton->grab_focus();
else
m_xLineSet->GrabFocus();
}
IMPL_LINK_NOARG(SvtLineListBox, NoneHdl, weld::Button&, void)
{
SelectEntry(SvxBorderLineStyle::NONE);
ValueSelectHdl(nullptr);
}
SvtLineListBox::~SvtLineListBox()
{
}
sal_Int32 SvtLineListBox::GetStylePos( sal_Int32 nListPos )
{
sal_Int32 nPos = LISTBOX_ENTRY_NOTFOUND;
--nListPos;
sal_Int32 n = 0;
size_t i = 0;
size_t nCount = m_vLineList.size();
while ( nPos == LISTBOX_ENTRY_NOTFOUND && i < nCount )
{
if ( nListPos == n )
nPos = static_cast<sal_Int32>(i);
n++;
i++;
}
return nPos;
}
void SvtLineListBox::SelectEntry(SvxBorderLineStyle nStyle)
{
if (nStyle == SvxBorderLineStyle::NONE)
{
m_xLineSet->SetNoSelection();
m_xNoneButton->set_has_default(true);
}
else
{
m_xLineSet->SelectItem(static_cast<sal_Int16>(nStyle) + 1);
m_xNoneButton->set_has_default(false);
}
UpdatePreview();
}
void SvtLineListBox::InsertEntry(
const BorderWidthImpl& rWidthImpl, SvxBorderLineStyle nStyle, long nMinWidth,
ColorFunc pColor1Fn, ColorFunc pColor2Fn, ColorDistFunc pColorDistFn )
{
m_vLineList.emplace_back(new ImpLineListData(
rWidthImpl, nStyle, nMinWidth, pColor1Fn, pColor2Fn, pColorDistFn));
}
void SvtLineListBox::UpdatePaintLineColor()
{
const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
Color aNewCol(rSettings.GetWindowColor().IsDark() ? rSettings.GetLabelTextColor() : aColor);
bool bRet = aNewCol != maPaintCol;
if( bRet )
maPaintCol = aNewCol;
}
void SvtLineListBox::UpdateEntries()
{
UpdatePaintLineColor( );
SvxBorderLineStyle eSelected = GetSelectEntryStyle();
// Remove the old entries
m_xLineSet->Clear();
// Add the new entries based on the defined width
sal_uInt16 n = 0;
sal_uInt16 nCount = m_vLineList.size( );
while ( n < nCount )
{
auto& pData = m_vLineList[ n ];
BitmapEx aBmp;
ImpGetLine( pData->GetLine1ForWidth( m_nWidth ),
pData->GetLine2ForWidth( m_nWidth ),
pData->GetDistForWidth( m_nWidth ),
GetColorLine1(m_xLineSet->GetItemCount()),
GetColorLine2(m_xLineSet->GetItemCount()),
GetColorDist(m_xLineSet->GetItemCount()),
pData->GetStyle(), aBmp );
sal_Int16 nItemId = static_cast<sal_Int16>(pData->GetStyle()) + 1;
m_xLineSet->InsertItem(nItemId, Image(aBmp), GetLineStyleName(pData->GetStyle()));
if (pData->GetStyle() == eSelected)
m_xLineSet->SelectItem(nItemId);
n++;
}
m_xLineSet->SetOptimalSize();
}
Color SvtLineListBox::GetColorLine1( sal_Int32 nPos )
{
sal_Int32 nStyle = GetStylePos( nPos );
if (nStyle == LISTBOX_ENTRY_NOTFOUND)
return GetPaintColor( );
auto& pData = m_vLineList[ nStyle ];
return pData->GetColorLine1( GetColor( ) );
}
Color SvtLineListBox::GetColorLine2( sal_Int32 nPos )
{
sal_Int32 nStyle = GetStylePos(nPos);
if (nStyle == LISTBOX_ENTRY_NOTFOUND)
return GetPaintColor( );
auto& pData = m_vLineList[ nStyle ];
return pData->GetColorLine2( GetColor( ) );
}
Color SvtLineListBox::GetColorDist( sal_Int32 nPos )
{
const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
Color rResult = rSettings.GetFieldColor();
sal_Int32 nStyle = GetStylePos( nPos );
if (nStyle == LISTBOX_ENTRY_NOTFOUND)
return rResult;
auto& pData = m_vLineList[ nStyle ];
return pData->GetColorDist( GetColor( ), rResult );
}
IMPL_LINK_NOARG(SvtLineListBox, StyleUpdated, weld::Widget&, void)
{
UpdateEntries();
}
IMPL_LINK_NOARG(SvtLineListBox, ValueSelectHdl, SvtValueSet*, void)
{
maSelectHdl.Call(*this);
UpdatePreview();
if (m_xControl->get_active())
m_xControl->set_active(false);
}
void SvtLineListBox::UpdatePreview()
{
SvxBorderLineStyle eStyle = GetSelectEntryStyle();
for (sal_uInt32 i = 0; i < SAL_N_ELEMENTS(RID_SVXSTR_BORDERLINE); ++i)
{
if (eStyle == RID_SVXSTR_BORDERLINE[i].second)
{
m_xControl->set_label(SvtResId(RID_SVXSTR_BORDERLINE[i].first));
break;
}
}
if (eStyle == SvxBorderLineStyle::NONE)
{
m_xControl->set_image(nullptr);
m_xControl->set_label(GetLineStyleName(SvxBorderLineStyle::NONE));
}
else
{
Image aImage(m_xLineSet->GetItemImage(m_xLineSet->GetSelectedItemId()));
m_xControl->set_label("");
const auto nPos = (aVirDev->GetOutputSizePixel().Height() - aImage.GetSizePixel().Height()) / 2;
aVirDev->Push(PushFlags::MAPMODE);
aVirDev->SetMapMode(MapMode(MapUnit::MapPixel));
aVirDev->Erase();
aVirDev->DrawImage(Point(0, nPos), aImage);
m_xControl->set_image(aVirDev.get());
aVirDev->Pop();
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V1029 Numeric Truncation Error. Result of the 'size' function is written to the 16-bit variable.
↑ V1023 A pointer without owner is added to the 'm_vLineList' container by the 'emplace_back' method. A memory leak will occur in case of an exception.
↑ V1029 Numeric Truncation Error. Result of the 'size' function is written to the 16-bit variable.
↑ V1023 A pointer without owner is added to the 'm_vLineList' container by the 'emplace_back' method. A memory leak will occur in case of an exception.
↑ V560 A part of conditional expression is always true: !bLine2Change.
↑ V560 A part of conditional expression is always true: !bLine1Change.
↑ V730 It is possible that not all members of a class are initialized inside the constructor. Consider inspecting: nRelMin, nRelMax, nRelStep, nPtRelMin, nPtRelMax, nPtRelStep.