Files
libreoffice/svtools/source/control/ctrlbox.cxx
Kohei Yoshida ec1a3157c1 Better pixelization of dashed lines for screen rendering.
Now the dashed lines are evenly placed on screen.  For now, horizontal lines
only.  I'll work on vertical lines later.

Change-Id: I474e9c8214e5f079ea2cfca12b35381d8fcf2ae1
2014-01-20 14:37:02 -05:00

2020 lines
61 KiB
C++

/* -*- 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 <tools/stream.hxx>
#include <vcl/builder.hxx>
#include <vcl/svapp.hxx>
#include <vcl/field.hxx>
#include <vcl/helper.hxx>
#include <sal/macros.h>
#include <comphelper/processfactory.hxx>
#include <comphelper/string.hxx>
#include <unotools/charclass.hxx>
#include <svtools/sampletext.hxx>
#include <svtools/svtresid.hxx>
#include <svtools/svtools.hrc>
#include <svtools/ctrlbox.hxx>
#include <svtools/ctrltool.hxx>
#include <svtools/borderhelper.hxx>
#include <vcl/i18nhelp.hxx>
#include <vcl/fontcapabilities.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <com/sun/star/table/BorderLineStyle.hpp>
#include <rtl/bootstrap.hxx>
#if OSL_DEBUG_LEVEL > 1
#include <cstdio>
#endif
#include <stdio.h>
#define IMGOUTERTEXTSPACE 5
#define EXTRAFONTSIZE 5
#define GAPTOEXTRAPREVIEW 10
#define MAXPREVIEWWIDTH 120
#define MINGAPWIDTH 2
#define FONTNAMEBOXMRUENTRIESFILE "/user/config/fontnameboxmruentries"
using namespace ::com::sun::star;
// ========================================================================
// ColorListBox
// ========================================================================
// --------------------
// - ImplColorListData -
// --------------------
class ImplColorListData
{
public:
Color aColor;
sal_Bool bColor;
ImplColorListData() : aColor( COL_BLACK ) { bColor = sal_False; }
ImplColorListData( const Color& rColor ) : aColor( rColor ) { bColor = sal_True; }
};
// -----------------------------------------------------------------------
void ColorListBox::ImplInit()
{
pColorList = new ImpColorList();
const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
aImageSize = rStyleSettings.GetListBoxPreviewDefaultPixelSize();
EnableUserDraw( sal_True );
SetUserItemSize( aImageSize );
}
// -----------------------------------------------------------------------
void ColorListBox::ImplDestroyColorEntries()
{
for ( size_t n = pColorList->size(); n; )
delete (*pColorList)[ --n ];
pColorList->clear();
}
// -----------------------------------------------------------------------
ColorListBox::ColorListBox( Window* pParent, WinBits nWinStyle ) :
ListBox( pParent, nWinStyle )
{
ImplInit();
SetEdgeBlending(true);
}
// -----------------------------------------------------------------------
ColorListBox::ColorListBox( Window* pParent, const ResId& rResId ) :
ListBox( pParent, rResId )
{
ImplInit();
SetEdgeBlending(true);
}
extern "C" SAL_DLLPUBLIC_EXPORT Window* SAL_CALL makeColorListBox(Window *pParent, VclBuilder::stringmap &rMap)
{
bool bDropdown = VclBuilder::extractDropdown(rMap);
WinBits nWinBits = WB_LEFT|WB_VCENTER|WB_3DLOOK|WB_TABSTOP;
if (bDropdown)
nWinBits |= WB_DROPDOWN;
ColorListBox *pListBox = new ColorListBox(pParent, nWinBits);
if (bDropdown)
pListBox->EnableAutoSize(true);
return pListBox;
}
// -----------------------------------------------------------------------
ColorListBox::~ColorListBox()
{
ImplDestroyColorEntries();
delete pColorList;
}
// -----------------------------------------------------------------------
sal_uInt16 ColorListBox::InsertEntry( const OUString& rStr, sal_uInt16 nPos )
{
nPos = ListBox::InsertEntry( rStr, nPos );
if ( nPos != LISTBOX_ERROR )
{
ImplColorListData* pData = new ImplColorListData;
if ( nPos < pColorList->size() )
{
ImpColorList::iterator it = pColorList->begin();
::std::advance( it, nPos );
pColorList->insert( it, pData );
}
else
{
pColorList->push_back( pData );
nPos = pColorList->size() - 1;
}
}
return nPos;
}
// -----------------------------------------------------------------------
sal_uInt16 ColorListBox::InsertEntry( const Color& rColor, const OUString& rStr,
sal_uInt16 nPos )
{
nPos = ListBox::InsertEntry( rStr, nPos );
if ( nPos != LISTBOX_ERROR )
{
ImplColorListData* pData = new ImplColorListData( rColor );
if ( nPos < pColorList->size() )
{
ImpColorList::iterator it = pColorList->begin();
::std::advance( it, nPos );
pColorList->insert( it, pData );
}
else
{
pColorList->push_back( pData );
nPos = pColorList->size() - 1;
}
}
return nPos;
}
// -----------------------------------------------------------------------
void ColorListBox::InsertAutomaticEntryColor(const Color &rColor)
{
// insert the "Automatic"-entry always on the first position
InsertEntry( rColor, SVT_RESSTR(STR_SVT_AUTOMATIC_COLOR), 0 );
}
// -----------------------------------------------------------------------
void ColorListBox::RemoveEntry( sal_uInt16 nPos )
{
ListBox::RemoveEntry( nPos );
if ( nPos < pColorList->size() )
{
ImpColorList::iterator it = pColorList->begin();
::std::advance( it, nPos );
delete *it;
pColorList->erase( it );
}
}
// -----------------------------------------------------------------------
void ColorListBox::Clear()
{
ImplDestroyColorEntries();
ListBox::Clear();
}
// -----------------------------------------------------------------------
void ColorListBox::CopyEntries( const ColorListBox& rBox )
{
// Liste leeren
ImplDestroyColorEntries();
// Daten kopieren
size_t nCount = rBox.pColorList->size();
for ( size_t n = 0; n < nCount; n++ )
{
ImplColorListData* pData = (*rBox.pColorList)[ n ];
sal_uInt16 nPos = InsertEntry( rBox.GetEntry( n ), LISTBOX_APPEND );
if ( nPos != LISTBOX_ERROR )
{
if ( nPos < pColorList->size() )
{
ImpColorList::iterator it = pColorList->begin();
::std::advance( it, nPos );
pColorList->insert( it, new ImplColorListData( *pData ) );
}
else
{
pColorList->push_back( new ImplColorListData( *pData ) );
}
}
}
}
// -----------------------------------------------------------------------
sal_uInt16 ColorListBox::GetEntryPos( const Color& rColor ) const
{
for( sal_uInt16 n = (sal_uInt16) pColorList->size(); n; )
{
ImplColorListData* pData = (*pColorList)[ --n ];
if ( pData->bColor && ( pData->aColor == rColor ) )
return n;
}
return LISTBOX_ENTRY_NOTFOUND;
}
// -----------------------------------------------------------------------
Color ColorListBox::GetEntryColor( sal_uInt16 nPos ) const
{
Color aColor;
ImplColorListData* pData = ( nPos < pColorList->size() ) ? (*pColorList)[ nPos ] : NULL;
if ( pData && pData->bColor )
aColor = pData->aColor;
return aColor;
}
// -----------------------------------------------------------------------
void ColorListBox::UserDraw( const UserDrawEvent& rUDEvt )
{
size_t nPos = rUDEvt.GetItemId();
ImplColorListData* pData = ( nPos < pColorList->size() ) ? (*pColorList)[ nPos ] : NULL;
if ( pData )
{
if ( pData->bColor )
{
Point aPos( rUDEvt.GetRect().TopLeft() );
aPos.X() += 2;
aPos.Y() += ( rUDEvt.GetRect().GetHeight() - aImageSize.Height() ) / 2;
const Rectangle aRect(aPos, aImageSize);
rUDEvt.GetDevice()->Push();
rUDEvt.GetDevice()->SetFillColor( pData->aColor );
rUDEvt.GetDevice()->SetLineColor( rUDEvt.GetDevice()->GetTextColor() );
rUDEvt.GetDevice()->DrawRect(aRect);
rUDEvt.GetDevice()->Pop();
const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
const sal_uInt16 nEdgeBlendingPercent(GetEdgeBlending() ? rStyleSettings.GetEdgeBlending() : 0);
if(nEdgeBlendingPercent)
{
const Color& rTopLeft(rStyleSettings.GetEdgeBlendingTopLeftColor());
const Color& rBottomRight(rStyleSettings.GetEdgeBlendingBottomRightColor());
const sal_uInt8 nAlpha((nEdgeBlendingPercent * 255) / 100);
const BitmapEx aBlendFrame(createBlendFrame(aRect.GetSize(), nAlpha, rTopLeft, rBottomRight));
if(!aBlendFrame.IsEmpty())
{
rUDEvt.GetDevice()->DrawBitmapEx(aRect.TopLeft(), aBlendFrame);
}
}
ListBox::DrawEntry( rUDEvt, sal_False, sal_True, sal_False );
}
else
ListBox::DrawEntry( rUDEvt, sal_False, sal_True, sal_True );
}
else
ListBox::DrawEntry( rUDEvt, sal_True, sal_True, sal_False );
}
// =======================================================================
// LineListBox
// =======================================================================
BorderWidthImpl::BorderWidthImpl( sal_uInt16 nFlags, double nRate1, double nRate2, double nRateGap ):
m_nFlags( nFlags ),
m_nRate1( nRate1 ),
m_nRate2( nRate2 ),
m_nRateGap( nRateGap )
{
}
BorderWidthImpl& BorderWidthImpl::operator= ( const BorderWidthImpl& r )
{
m_nFlags = r.m_nFlags;
m_nRate1 = r.m_nRate1;
m_nRate2 = r.m_nRate2;
m_nRateGap = r.m_nRateGap;
return *this;
}
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 & CHANGE_LINE1 ) > 0 )
{
long const nConstant2 = (m_nFlags & CHANGE_LINE2) ? 0 : m_nRate2;
long const nConstantD = (m_nFlags & 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 & CHANGE_LINE2 ) > 0 )
{
long const nConstant1 = (m_nFlags & CHANGE_LINE1) ? 0 : m_nRate1;
long const nConstantD = (m_nFlags & 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 & CHANGE_DIST ) > 0 )
{
long const nConstant1 = (m_nFlags & CHANGE_LINE1) ? 0 : m_nRate1;
long const nConstant2 = (m_nFlags & 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 nChanging )
{
double nWidth = -1.0;
if ( nChanging )
nWidth = double( nTested ) / nRate;
else
{
if ( 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 = ( m_nFlags & CHANGE_LINE1 ) > 0;
double nWidth1 = lcl_getGuessedWidth( nLine1, m_nRate1, bLine1Change );
if ( bLine1Change )
aToCompare.push_back( nWidth1 );
else if ( !bLine1Change && nWidth1 < 0 )
bInvalid = true;
bool bLine2Change = ( m_nFlags & CHANGE_LINE2 ) > 0;
double nWidth2 = lcl_getGuessedWidth( nLine2, m_nRate2, bLine2Change );
if ( bLine2Change )
aToCompare.push_back( nWidth2 );
else if ( !bLine2Change && nWidth2 < 0 )
bInvalid = true;
bool bGapChange = ( m_nFlags & CHANGE_DIST ) > 0;
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();
std::vector< double >::iterator pIt = aToCompare.begin();
while ( pIt != aToCompare.end() && !bInvalid )
{
bInvalid = ( nWidth != *pIt );
++pIt;
}
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;
sal_uInt16 m_nStyle;
public:
ImpLineListData( BorderWidthImpl aWidthImpl, sal_uInt16 nStyle,
long nMinWidth=0, Color ( *pColor1Fn ) ( Color ) = &sameColor,
Color ( *pColor2Fn ) ( Color ) = &sameColor, Color ( *pColorDistFn ) ( Color, Color ) = &sameDistColor );
/** 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( );
sal_uInt16 GetStyle( );
};
ImpLineListData::ImpLineListData( BorderWidthImpl aWidthImpl,
sal_uInt16 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 )
{
}
long ImpLineListData::GetMinWidth( )
{
return m_nMinWidth;
}
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 );
}
sal_uInt16 LineListBox::GetSelectEntryStyle( sal_uInt16 nSelIndex ) const
{
sal_uInt16 nStyle = table::BorderLineStyle::SOLID;
sal_uInt16 nPos = GetSelectEntryPos( nSelIndex );
if ( nPos != LISTBOX_ENTRY_NOTFOUND )
{
if (!m_sNone.isEmpty())
nPos--;
nStyle = GetEntryStyle( nPos );
}
return nStyle;
}
sal_uInt16 ImpLineListData::GetStyle( )
{
return m_nStyle;
}
// -----------------------------------------------------------------------
void lclDrawPolygon( OutputDevice& rDev, const basegfx::B2DPolygon& rPolygon, long nWidth, sal_uInt16 nDashing )
{
sal_uInt16 nOldAA = rDev.GetAntialiasing();
rDev.SetAntialiasing( nOldAA & ~ANTIALIASING_ENABLE_B2DDRAW );
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() == MAP_PIXEL && 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( sal_uInt16 nDashing )
{
std::vector<double> aPattern;
switch (nDashing)
{
case table::BorderLineStyle::DOTTED:
aPattern.push_back( 1.0 ); // line
aPattern.push_back( 2.0 ); // blank
break;
case table::BorderLineStyle::DASHED:
aPattern.push_back( 16.0 ); // line
aPattern.push_back( 5.0 ); // blank
break;
case table::BorderLineStyle::FINE_DASHED:
aPattern.push_back( 6.0 ); // line
aPattern.push_back( 2.0 ); // blank
break;
default:
;
}
return aPattern;
}
namespace {
class ApplyScale : std::unary_function<double, void>
{
double mfScale;
public:
ApplyScale( double fScale ) : mfScale(fScale) {}
void operator() ( double& rVal )
{
rVal *= mfScale;
}
};
}
std::vector<double> GetLineDashing( sal_uInt16 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, sal_uInt16 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::tools::applyLineDashing(rPolygon, aPattern, &aPolygons);
return aPolygons;
}
void DrawLine( OutputDevice& rDev, const Point& rP1, const Point& rP2,
sal_uInt32 nWidth, sal_uInt16 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, sal_uInt16 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,
sal_uInt16 nStyle, Bitmap& 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(OUString("----------"));
Size aSize = CalcSubEditSize();
aSize.Width() = std::max(nMinWidth, aSize.Width());
aSize.Width() -= aTxtSize.Width();
aSize.Width() -= 6;
aSize.Height() = aTxtSize.Height();
// SourceUnit nach Twips
if ( eSourceUnit == FUNIT_POINT )
{
nLine1 /= 5;
nLine2 /= 5;
nDistance /= 5;
}
// Linien malen
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.Height() = nVirHeight;
// negative width should not be drawn
if ( aSize.Width() > 0 )
{
Size aVirSize = aVirDev.LogicToPixel( aSize );
if ( aVirDev.GetOutputSizePixel() != aVirSize )
aVirDev.SetOutputSizePixel( aVirSize );
aVirDev.SetFillColor( aColorDist );
aVirDev.DrawRect( Rectangle( Point(), aSize ) );
aVirDev.SetFillColor( aColor1 );
double y1 = double( n1 ) / 2;
svtools::DrawLine( aVirDev, 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, basegfx::B2DPoint( 0, y2 ), basegfx::B2DPoint( aSize.Width(), y2 ), n2, table::BorderLineStyle::SOLID );
}
rBmp = aVirDev.GetBitmap( Point(), Size( aSize.Width(), n1+nDist+n2 ) );
}
}
// -----------------------------------------------------------------------
void LineListBox::ImplInit()
{
aTxtSize.Width() = GetTextWidth( OUString( " " ) );
aTxtSize.Height() = GetTextHeight();
pLineList = new ImpLineList();
eUnit = FUNIT_POINT;
eSourceUnit = FUNIT_POINT;
aVirDev.SetLineColor();
aVirDev.SetMapMode( MapMode( MAP_TWIP ) );
UpdatePaintLineColor();
}
// -----------------------------------------------------------------------
LineListBox::LineListBox( Window* pParent, WinBits nWinStyle ) :
ListBox( pParent, nWinStyle ),
m_nWidth( 5 ),
m_sNone( ),
aColor( COL_BLACK ),
maPaintCol( COL_BLACK )
{
ImplInit();
}
extern "C" SAL_DLLPUBLIC_EXPORT Window* SAL_CALL makeLineListBox(Window *pParent, VclBuilder::stringmap &rMap)
{
bool bDropdown = VclBuilder::extractDropdown(rMap);
WinBits nWinBits = WB_LEFT|WB_VCENTER|WB_3DLOOK|WB_TABSTOP;
if (bDropdown)
nWinBits |= WB_DROPDOWN;
LineListBox *pListBox = new LineListBox(pParent, nWinBits);
if (bDropdown)
pListBox->EnableAutoSize(true);
return pListBox;
}
// -----------------------------------------------------------------------
LineListBox::~LineListBox()
{
for ( size_t i = 0, n = pLineList->size(); i < n; ++i ) {
if ( (*pLineList)[ i ] ) {
delete (*pLineList)[ i ];
}
}
pLineList->clear();
delete pLineList;
}
sal_uInt16 LineListBox::GetStylePos( sal_uInt16 nListPos, long nWidth )
{
sal_uInt16 nPos = LISTBOX_ENTRY_NOTFOUND;
if (!m_sNone.isEmpty())
nListPos--;
sal_uInt16 i = 0;
sal_uInt16 n = 0;
sal_uInt16 nCount = pLineList->size();
while ( nPos == LISTBOX_ENTRY_NOTFOUND && i < nCount )
{
ImpLineListData* pData = (*pLineList)[ i ];
if ( pData && pData->GetMinWidth() <= nWidth )
{
if ( nListPos == n )
nPos = i;
n++;
}
i++;
}
return nPos;
}
void LineListBox::SelectEntry( sal_uInt16 nStyle, sal_Bool bSelect )
{
sal_uInt16 nPos = GetEntryPos( nStyle );
if ( nPos != LISTBOX_ENTRY_NOTFOUND )
ListBox::SelectEntryPos( nPos, bSelect );
}
// -----------------------------------------------------------------------
sal_uInt16 LineListBox::InsertEntry( const OUString& rStr, sal_uInt16 nPos )
{
nPos = ListBox::InsertEntry( rStr, nPos );
if ( nPos != LISTBOX_ERROR ) {
if ( nPos < pLineList->size() ) {
ImpLineList::iterator it = pLineList->begin();
::std::advance( it, nPos );
pLineList->insert( it, reinterpret_cast<ImpLineListData *>(NULL) );
} else {
pLineList->push_back( NULL );
}
}
return nPos;
}
// -----------------------------------------------------------------------
void LineListBox::InsertEntry(
BorderWidthImpl aWidthImpl,
sal_uInt16 nStyle, long nMinWidth,
Color ( *pColor1Fn )( Color ), Color ( *pColor2Fn )( Color ),
Color ( *pColorDistFn )( Color, Color ) )
{
ImpLineListData* pData = new ImpLineListData(
aWidthImpl, nStyle, nMinWidth,
pColor1Fn, pColor2Fn, pColorDistFn );
pLineList->push_back( pData );
}
// -----------------------------------------------------------------------
void LineListBox::RemoveEntry( sal_uInt16 nPos )
{
ListBox::RemoveEntry( nPos );
if ( nPos < pLineList->size() ) {
ImpLineList::iterator it = pLineList->begin();
::std::advance( it, nPos );
if ( *it ) delete *it;
pLineList->erase( it );
}
}
// -----------------------------------------------------------------------
void LineListBox::Clear()
{
for ( size_t i = 0, n = pLineList->size(); i < n; ++i ) {
if ( (*pLineList)[ i ] ) {
delete (*pLineList)[ i ];
}
}
pLineList->clear();
ListBox::Clear();
}
// -----------------------------------------------------------------------
sal_uInt16 LineListBox::GetEntryPos( sal_uInt16 nStyle ) const
{
for ( size_t i = 0, n = pLineList->size(); i < n; ++i ) {
ImpLineListData* pData = (*pLineList)[ i ];
if ( pData )
{
if ( GetEntryStyle( i ) == nStyle )
{
size_t nPos = i;
if (!m_sNone.isEmpty())
nPos ++;
return (sal_uInt16)nPos;
}
}
}
return LISTBOX_ENTRY_NOTFOUND;
}
// -----------------------------------------------------------------------
sal_uInt16 LineListBox::GetEntryStyle( sal_uInt16 nPos ) const
{
ImpLineListData* pData = (nPos < pLineList->size()) ? (*pLineList)[ nPos ] : NULL;
return ( pData ) ? pData->GetStyle() : table::BorderLineStyle::NONE;
}
// -----------------------------------------------------------------------
sal_Bool LineListBox::UpdatePaintLineColor( void )
{
const StyleSettings& rSettings = GetSettings().GetStyleSettings();
Color aNewCol( rSettings.GetWindowColor().IsDark()? rSettings.GetLabelTextColor() : aColor );
sal_Bool bRet = aNewCol != maPaintCol;
if( bRet )
maPaintCol = aNewCol;
return bRet;
}
void LineListBox::UpdateEntries( long nOldWidth )
{
SetUpdateMode( sal_False );
UpdatePaintLineColor( );
sal_uInt16 nSelEntry = GetSelectEntryPos();
sal_uInt16 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, LISTBOX_APPEND );
sal_uInt16 n = 0;
sal_uInt16 nCount = pLineList->size( );
while ( n < nCount )
{
ImpLineListData* pData = (*pLineList)[ n ];
if ( pData && pData->GetMinWidth() <= m_nWidth )
{
Bitmap 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(OUString(" "), Image(aBmp), LISTBOX_APPEND);
if ( n == nTypePos )
SelectEntryPos( GetEntryCount() - 1 );
}
else if ( n == nTypePos )
SetNoSelection();
n++;
}
SetUpdateMode( sal_True );
Invalidate();
}
// -----------------------------------------------------------------------
Color LineListBox::GetColorLine1( sal_uInt16 nPos )
{
Color rResult = GetPaintColor( );
sal_uInt16 nStyle = GetStylePos( nPos, m_nWidth );
ImpLineListData* pData = (*pLineList)[ nStyle ];
if ( pData )
rResult = pData->GetColorLine1( GetColor( ) );
return rResult;
}
Color LineListBox::GetColorLine2( sal_uInt16 nPos )
{
Color rResult = GetPaintColor( );
sal_uInt16 nStyle = GetStylePos( nPos, m_nWidth );
ImpLineListData* pData = (*pLineList)[ nStyle ];
if ( pData )
rResult = pData->GetColorLine2( GetColor( ) );
return rResult;
}
Color LineListBox::GetColorDist( sal_uInt16 nPos )
{
Color rResult = GetSettings().GetStyleSettings().GetFieldColor();
sal_uInt16 nStyle = GetStylePos( nPos, m_nWidth );
ImpLineListData* pData = (*pLineList)[ nStyle ];
if ( pData )
rResult = pData->GetColorDist( GetColor( ), rResult );
return rResult;
}
// -----------------------------------------------------------------------
void LineListBox::DataChanged( const DataChangedEvent& rDCEvt )
{
ListBox::DataChanged( rDCEvt );
if( ( rDCEvt.GetType() == DATACHANGED_SETTINGS ) && ( rDCEvt.GetFlags() & SETTINGS_STYLE ) )
UpdateEntries( m_nWidth );
}
// ===================================================================
// FontNameBox
// ===================================================================
FontNameBox::FontNameBox( Window* pParent, WinBits nWinStyle ) :
ComboBox( pParent, nWinStyle )
{
mpFontList = NULL;
mbWYSIWYG = sal_False;
InitFontMRUEntriesFile();
}
// -------------------------------------------------------------------
extern "C" SAL_DLLPUBLIC_EXPORT Window* SAL_CALL makeFontNameBox(Window *pParent, VclBuilder::stringmap &rMap)
{
bool bDropdown = VclBuilder::extractDropdown(rMap);
WinBits nWinBits = WB_LEFT|WB_VCENTER|WB_3DLOOK|WB_TABSTOP;
if (bDropdown)
nWinBits |= WB_DROPDOWN;
FontNameBox *pListBox = new FontNameBox(pParent, nWinBits);
if (bDropdown)
pListBox->EnableAutoSize(true);
return pListBox;
}
// -------------------------------------------------------------------
FontNameBox::~FontNameBox()
{
SaveMRUEntries (maFontMRUEntriesFile);
ImplDestroyFontList();
}
// -------------------------------------------------------------------
void FontNameBox::SaveMRUEntries( const OUString& aFontMRUEntriesFile, sal_Unicode cSep ) const
{
OString aEntries(OUStringToOString(GetMRUEntries(cSep),
RTL_TEXTENCODING_UTF8));
if (aEntries.isEmpty() || aFontMRUEntriesFile.isEmpty())
return;
SvFileStream aStream;
aStream.Open( aFontMRUEntriesFile, STREAM_WRITE | STREAM_TRUNC );
if( ! (aStream.IsOpen() && aStream.IsWritable()) )
{
#if OSL_DEBUG_LEVEL > 1
fprintf( stderr, "FontNameBox::SaveMRUEntries: opening mru entries file %s failed\n", OUStringToOString(aFontMRUEntriesFile, RTL_TEXTENCODING_UTF8 ).getStr() );
#endif
return;
}
aStream.SetLineDelimiter( LINEEND_LF );
aStream.WriteLine( aEntries );
aStream.WriteLine( OString() );
}
// -------------------------------------------------------------------
void FontNameBox::LoadMRUEntries( const OUString& aFontMRUEntriesFile, sal_Unicode cSep )
{
if( aFontMRUEntriesFile.isEmpty() )
return;
SvFileStream aStream( aFontMRUEntriesFile, STREAM_READ );
if( ! aStream.IsOpen() )
{
#if OSL_DEBUG_LEVEL > 1
fprintf( stderr, "FontNameBox::LoadMRUEntries: opening mru entries file %s failed\n", OUStringToOString(aFontMRUEntriesFile, RTL_TEXTENCODING_UTF8).getStr() );
#endif
return;
}
OString aLine;
aStream.ReadLine( aLine );
OUString aEntries = OStringToOUString(aLine,
RTL_TEXTENCODING_UTF8);
SetMRUEntries( aEntries, cSep );
}
// ------------------------------------------------------------------
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()
{
delete mpFontList;
}
// -------------------------------------------------------------------
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 = new ImplFontList;
// insert fonts
sal_uInt16 nFontCount = pList->GetFontNameCount();
for ( sal_uInt16 i = 0; i < nFontCount; i++ )
{
const FontInfo& rFontInfo = pList->GetFontName( i );
sal_uLong nIndex = InsertEntry( rFontInfo.GetName() );
if ( nIndex != LISTBOX_ERROR )
{
if ( nIndex < mpFontList->size() ) {
ImplFontList::iterator it = mpFontList->begin();
::std::advance( it, nIndex );
mpFontList->insert( it, rFontInfo );
} else {
mpFontList->push_back( rFontInfo );
}
}
}
if ( bLoadFromFile )
LoadMRUEntries (maFontMRUEntriesFile);
else
SetMRUEntries( rEntries );
ImplCalcUserItemSize();
// restore text
if (!aOldText.isEmpty())
SetText( aOldText );
}
// -------------------------------------------------------------------
void FontNameBox::EnableWYSIWYG( sal_Bool bEnable )
{
if ( bEnable != mbWYSIWYG )
{
mbWYSIWYG = bEnable;
EnableUserDraw( mbWYSIWYG );
ImplCalcUserItemSize();
}
}
// -------------------------------------------------------------------
void FontNameBox::ImplCalcUserItemSize()
{
Size aUserItemSz;
if ( mbWYSIWYG && mpFontList )
{
aUserItemSz = Size(MAXPREVIEWWIDTH, GetTextHeight() );
aUserItemSz.Height() *= 16;
aUserItemSz.Height() /= 10;
}
SetUserItemSize( aUserItemSz );
}
namespace
{
long shrinkFontToFit(OUString &rSampleText, long nH, Font &rFont, OutputDevice &rDevice, Rectangle &rTextRect)
{
long nWidth = 0;
Size aSize( rFont.GetSize() );
//Make sure it fits in the available height
while (aSize.Height() > 0)
{
if (!rDevice.GetTextBoundRect(rTextRect, rSampleText, 0, 0))
break;
if (rTextRect.GetHeight() <= nH)
{
nWidth = rTextRect.GetWidth();
break;
}
aSize.Height() -= EXTRAFONTSIZE;
rFont.SetSize(aSize);
rDevice.SetFont(rFont);
}
return nWidth;
}
}
// -------------------------------------------------------------------
void FontNameBox::UserDraw( const UserDrawEvent& rUDEvt )
{
assert( mpFontList );
FontInfo& rInfo = (*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(rInfo);
Color aTextColor = rUDEvt.GetDevice()->GetTextColor();
Font aOldFont( rUDEvt.GetDevice()->GetFont() );
Size aSize( aOldFont.GetSize() );
aSize.Height() += EXTRAFONTSIZE;
Font aFont( rInfo );
aFont.SetSize( aSize );
rUDEvt.GetDevice()->SetFont( aFont );
rUDEvt.GetDevice()->SetTextColor( aTextColor );
bool bUsingCorrectFont = true;
Rectangle aTextRect;
// Preview the font name
OUString sFontName = rInfo.GetName();
//If it shouldn't or can't draw its own name because it doesn't have the glyphs
if (!canRenderNameOfSelectedFont(*rUDEvt.GetDevice()))
bUsingCorrectFont = false;
else
{
//Make sure it fits in the available height, shrinking the font if necessary
bUsingCorrectFont = shrinkFontToFit(sFontName, nH, aFont, *rUDEvt.GetDevice(), aTextRect) != 0;
}
if (!bUsingCorrectFont)
{
rUDEvt.GetDevice()->SetFont(aOldFont);
rUDEvt.GetDevice()->GetTextBoundRect(aTextRect, sFontName, 0, 0);
}
long nTextHeight = aTextRect.GetHeight();
long nDesiredGap = (nH-nTextHeight)/2;
long nVertAdjust = nDesiredGap - aTextRect.Top();
Point aPos( nX, aTopLeft.Y() + nVertAdjust );
rUDEvt.GetDevice()->DrawText( aPos, sFontName );
long nTextX = aPos.X() + aTextRect.GetWidth() + GAPTOEXTRAPREVIEW;
if (!bUsingCorrectFont)
rUDEvt.GetDevice()->SetFont( aFont );
OUString sSampleText;
if (!bSymbolFont)
{
const bool bNameBeginsWithLatinText = rInfo.GetName()[0] <= 'z';
if (bNameBeginsWithLatinText || !bUsingCorrectFont)
sSampleText = makeShortRepresentativeTextForSelectedFont(*rUDEvt.GetDevice());
}
//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 (size_t i = 0; i < SAL_N_ELEMENTS(aScripts); ++i)
{
OUString sText = makeShortRepresentativeTextForScript(aScripts[i]);
if (!sText.isEmpty())
{
bool bHasSampleTextGlyphs = (-1 == rUDEvt.GetDevice()->HasGlyphs(aFont, sText));
if (bHasSampleTextGlyphs)
{
sSampleText = sText;
break;
}
}
}
static const UScriptCode aMinimalScripts[] =
{
USCRIPT_HEBREW, //e.g. biblical hebrew
USCRIPT_GREEK
};
for (size_t i = 0; i < SAL_N_ELEMENTS(aMinimalScripts); ++i)
{
OUString sText = makeShortMinimalTextForScript(aMinimalScripts[i]);
if (!sText.isEmpty())
{
bool bHasSampleTextGlyphs = (-1 == rUDEvt.GetDevice()->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(*rUDEvt.GetDevice());
if (!sSampleText.isEmpty())
{
const Size &rItemSize = rUDEvt.GetDevice()->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, *rUDEvt.GetDevice(), aTextRect);
//Chop letters off until it fits in the available width
while (nWidth > nSpace || nWidth > MAXPREVIEWWIDTH)
{
sSampleText = sSampleText.copy(0, sSampleText.getLength()-1);
nWidth = rUDEvt.GetDevice()->GetTextBoundRect(aTextRect, sSampleText, 0, 0) ?
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);
rUDEvt.GetDevice()->DrawText( aPos, sSampleText );
}
}
}
rUDEvt.GetDevice()->SetFont( aOldFont );
DrawEntry( rUDEvt, sal_False, sal_False); // draw separator
}
else
{
DrawEntry( rUDEvt, sal_True, sal_True );
}
}
// ===================================================================
// FontStyleBox
// ===================================================================
FontStyleBox::FontStyleBox( Window* pParent, const ResId& rResId ) :
ComboBox( pParent, rResId )
{
aLastStyle = GetText();
}
FontStyleBox::FontStyleBox(Window* pParent, WinBits nBits)
: ComboBox(pParent, nBits)
{
aLastStyle = GetText();
//Use the standard texts to get an optimal size and stick to that size.
//That should stop the character dialog dancing around.
InsertEntry(SVT_RESSTR(STR_SVT_STYLE_LIGHT));
InsertEntry(SVT_RESSTR(STR_SVT_STYLE_LIGHT_ITALIC));
InsertEntry(SVT_RESSTR(STR_SVT_STYLE_NORMAL));
InsertEntry(SVT_RESSTR(STR_SVT_STYLE_NORMAL_ITALIC));
InsertEntry(SVT_RESSTR(STR_SVT_STYLE_BOLD));
InsertEntry(SVT_RESSTR(STR_SVT_STYLE_BOLD_ITALIC));
InsertEntry(SVT_RESSTR(STR_SVT_STYLE_BLACK));
InsertEntry(SVT_RESSTR(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 Window* SAL_CALL makeFontStyleBox(Window *pParent, VclBuilder::stringmap &rMap)
{
bool bDropdown = VclBuilder::extractDropdown(rMap);
WinBits nWinBits = WB_LEFT|WB_VCENTER|WB_3DLOOK|WB_TABSTOP;
if (bDropdown)
nWinBits |= WB_DROPDOWN;
FontStyleBox *pListBox = new FontStyleBox(pParent, nWinBits);
if (bDropdown)
pListBox->EnableAutoSize(true);
return pListBox;
}
FontStyleBox::~FontStyleBox()
{
}
// -------------------------------------------------------------------
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_uInt16 nEntryCount = GetEntryCount();
if ( GetEntryPos( aStr ) == COMBOBOX_ENTRY_NOTFOUND )
{
aStr = aChrCls.uppercase(aStr);
for ( sal_uInt16 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_uInt16 nPos = GetEntryPos( aOldText );
Clear();
// does a font with this name already exist?
sal_Handle hFontInfo = pList->GetFirstFontInfo( rName );
if ( hFontInfo )
{
OUString aStyleText;
FontWeight eLastWeight = WEIGHT_DONTKNOW;
FontItalic eLastItalic = ITALIC_NONE;
FontWidth eLastWidth = WIDTH_DONTKNOW;
sal_Bool bNormal = sal_False;
sal_Bool bItalic = sal_False;
sal_Bool bBold = sal_False;
sal_Bool bBoldItalic = sal_False;
sal_Bool bInsert = sal_False;
FontInfo aInfo;
while ( hFontInfo )
{
aInfo = pList->GetFontInfo( hFontInfo );
FontWeight eWeight = aInfo.GetWeight();
FontItalic eItalic = aInfo.GetItalic();
FontWidth eWidth = aInfo.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 = sal_True;
else
bNormal = sal_True;
}
else
{
if ( eItalic != ITALIC_NONE )
bBoldItalic = sal_True;
else
bBold = sal_True;
}
// For wrong StyleNames we replace this with the correct once
aStyleText = pList->GetStyleName( aInfo );
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( aInfo );
if (rAttrStyleText == aTempStyleText)
aStyleText = rAttrStyleText;
bInsert = GetEntryPos( aStyleText ) == LISTBOX_ENTRY_NOTFOUND;
}
}
}
if ( !bItalic && (aStyleText == pList->GetItalicStr()) )
bItalic = sal_True;
else if ( !bBold && (aStyleText == pList->GetBoldStr()) )
bBold = sal_True;
else if ( !bBoldItalic && (aStyleText == pList->GetBoldItalicStr()) )
bBoldItalic = sal_True;
hFontInfo = pList->GetNextFontInfo( hFontInfo );
}
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::FontSizeBox( Window* pParent, WinBits nWinSize ) :
MetricBox( pParent, nWinSize )
{
ImplInit();
}
extern "C" SAL_DLLPUBLIC_EXPORT Window* SAL_CALL makeFontSizeBox(Window *pParent, VclBuilder::stringmap &rMap)
{
bool bDropdown = VclBuilder::extractDropdown(rMap);
WinBits nWinBits = WB_LEFT|WB_VCENTER|WB_3DLOOK|WB_TABSTOP;
if (bDropdown)
nWinBits |= WB_DROPDOWN;
FontSizeBox* pListBox = new FontSizeBox(pParent, nWinBits);
if (bDropdown)
pListBox->EnableAutoSize(true);
return pListBox;
}
// -----------------------------------------------------------------------
FontSizeBox::~FontSizeBox()
{
}
// -----------------------------------------------------------------------
void FontSizeBox::ImplInit()
{
EnableAutocomplete( sal_False );
bRelativeMode = sal_False;
bPtRelative = sal_False;
bRelative = sal_False;
bStdSize = sal_False;
pFontList = NULL;
SetShowTrailingZeros( sal_False );
SetDecimalDigits( 1 );
SetMin( 20 );
SetMax( 9999 );
SetProminentEntryType( PROMINENT_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 )
{
OUString aStr = comphelper::string::stripStart(GetText(), ' ');
sal_Bool bNewMode = bRelative;
sal_Bool bOldPtRelMode = bPtRelative;
if ( bRelative )
{
bPtRelative = sal_False;
const sal_Unicode* pStr = aStr.getStr();
while ( *pStr )
{
if ( ((*pStr < '0') || (*pStr > '9')) && (*pStr != '%') )
{
if ( ('-' == *pStr || '+' == *pStr) && !bPtRelative )
bPtRelative = sal_True;
else if ( bPtRelative && 'p' == *pStr && 't' == *++pStr )
;
else
{
bNewMode = sal_False;
break;
}
}
pStr++;
}
}
else
{
if ( -1 != aStr.indexOf('%') )
{
bNewMode = sal_True;
bPtRelative = sal_False;
}
if ( '-' == aStr[0] || '+' == aStr[0] )
{
bNewMode = sal_True;
bPtRelative = sal_True;
}
}
if ( bNewMode != bRelative || bPtRelative != bOldPtRelMode )
SetRelative( bNewMode );
}
}
// -----------------------------------------------------------------------
void FontSizeBox::Fill( const FontInfo* pInfo, 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 = 0;
if( pInfo )
{
aFontInfo = *pInfo;
pAry = pList->GetSizeAry( *pInfo );
}
else
{
pAry = pList->GetStdSizeAry();
}
// first insert font size names (for simplified/traditional chinese)
FontSizeNames aFontSizeNames( GetSettings().GetUILanguageTag().getLanguageType() );
if ( pAry == pList->GetStdSizeAry() )
{
// for standard sizes we don't need to bother
if ( bStdSize && GetEntryCount() && aFontSizeNames.IsEmpty() )
return;
bStdSize = sal_True;
}
else
bStdSize = sal_False;
Selection aSelection = GetSelection();
OUString aStr = GetText();
Clear();
sal_uInt16 nPos = 0;
if ( !aFontSizeNames.IsEmpty() )
{
if ( pAry == pList->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, (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, (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, (void*)(*pTempAry) );
nPos++;
pTempAry++;
}
SetText( aStr );
SetSelection( aSelection );
}
// -----------------------------------------------------------------------
void FontSizeBox::EnableRelativeMode( sal_uInt16 nMin, sal_uInt16 nMax, sal_uInt16 nStep )
{
bRelativeMode = sal_True;
nRelMin = nMin;
nRelMax = nMax;
nRelStep = nStep;
SetUnit( FUNIT_POINT );
}
// -----------------------------------------------------------------------
void FontSizeBox::EnablePtRelativeMode( short nMin, short nMax, short nStep )
{
bRelativeMode = sal_True;
nPtRelMin = nMin;
nPtRelMax = nMax;
nPtRelStep = nStep;
SetUnit( FUNIT_POINT );
}
// -----------------------------------------------------------------------
void FontSizeBox::SetRelative( sal_Bool bNewRelative )
{
if ( bRelativeMode )
{
Selection aSelection = GetSelection();
OUString aStr = comphelper::string::stripStart(GetText(), ' ');
if ( bNewRelative )
{
bRelative = sal_True;
bStdSize = sal_False;
if ( bPtRelative )
{
SetDecimalDigits( 1 );
SetMin( nPtRelMin );
SetMax( nPtRelMax );
SetUnit( FUNIT_POINT );
Clear();
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
{
SetDecimalDigits( 0 );
SetMin( nRelMin );
SetMax( nRelMax );
SetUnit( FUNIT_PERCENT );
Clear();
sal_uInt16 i = nRelMin;
while ( i <= nRelMax )
{
InsertValue( i );
i = i + nRelStep;
}
}
}
else
{
bRelative = bPtRelative = sal_False;
SetDecimalDigits( 1 );
SetMin( 20 );
SetMax( 9999 );
SetUnit( FUNIT_POINT );
if ( pFontList )
Fill( &aFontInfo, 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( sal_False );
return;
}
}
MetricBox::SetValue( nNewValue, eInUnit );
}
// -----------------------------------------------------------------------
void FontSizeBox::SetValue( sal_Int64 nNewValue )
{
SetValue( nNewValue, FUNIT_NONE );
}
// -----------------------------------------------------------------------
sal_Int64 FontSizeBox::GetValue( sal_uInt16 nPos, FieldUnit eOutUnit ) const
{
if ( !bRelative )
{
sal_Int64 nComboVal = static_cast<sal_Int64>(reinterpret_cast<sal_IntPtr>(ComboBox::GetEntryData( nPos )));
if ( nComboVal < 0 ) // marked as special?
{
return MetricField::ConvertValue( -nComboVal, mnBaseValue, GetDecimalDigits(),
meUnit, eOutUnit );
}
}
// do normal font size processing
sal_Int64 nRetValue = MetricBox::GetValue( nPos, eOutUnit );
return nRetValue;
}
// -----------------------------------------------------------------------
sal_Int64 FontSizeBox::GetValue( FieldUnit eOutUnit ) const
{
if ( !bRelative )
{
FontSizeNames aFontSizeNames( GetSettings().GetUILanguageTag().getLanguageType() );
sal_Int64 nValue = aFontSizeNames.Name2Size( GetText() );
if ( nValue)
return MetricField::ConvertValue( nValue, GetBaseValue(), GetDecimalDigits(), GetUnit(), eOutUnit );
}
return MetricBox::GetValue( eOutUnit );
}
// -----------------------------------------------------------------------
sal_Int64 FontSizeBox::GetValue() const
{
// implementation not inline, because it is a virtual function
return GetValue( FUNIT_NONE );
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */