forked from amazingfate/loongoffice
so when scrolling through fonts that might not have the original or explicitly picked style, then on moving to the next font that does have that style, then the original bold/italic/etc gets picked again. Change-Id: If818a116ed4ab248cb67f60845acad2cd4eeb8ed Reviewed-on: https://gerrit.libreoffice.org/c/core/+/173071 Reviewed-by: Caolán McNamara <caolan.mcnamara@collabora.com> Tested-by: Jenkins
1706 lines
54 KiB
C++
1706 lines
54 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 <sal/config.h>
|
|
#include <config_folders.h>
|
|
|
|
#include <comphelper/lok.hxx>
|
|
#include <i18nutil/unicode.hxx>
|
|
#include <o3tl/test_info.hxx>
|
|
#include <officecfg/Office/Common.hxx>
|
|
#include <tools/stream.hxx>
|
|
#include <vcl/customweld.hxx>
|
|
#include <vcl/event.hxx>
|
|
#include <vcl/svapp.hxx>
|
|
#include <vcl/fieldvalues.hxx>
|
|
#include <vcl/settings.hxx>
|
|
#include <vcl/image.hxx>
|
|
#include <vcl/virdev.hxx>
|
|
#include <vcl/weldutils.hxx>
|
|
#include <rtl/math.hxx>
|
|
#include <sal/macros.h>
|
|
#include <sal/log.hxx>
|
|
#include <comphelper/string.hxx>
|
|
#include <unotools/localedatawrapper.hxx>
|
|
#include <unotools/syslocale.hxx>
|
|
|
|
#include <svtools/borderline.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 <basegfx/polygon/b2dpolygon.hxx>
|
|
#include <basegfx/polygon/b2dpolygontools.hxx>
|
|
#include <editeng/borderline.hxx>
|
|
|
|
#include <rtl/bootstrap.hxx>
|
|
|
|
#include <borderline.hrc>
|
|
|
|
#include <stdio.h>
|
|
|
|
#define IMGOUTERTEXTSPACE 5
|
|
#define EXTRAFONTSIZE 5
|
|
#define GAPTOEXTRAPREVIEW 10
|
|
#define MINGAPWIDTH 2
|
|
|
|
constexpr OUStringLiteral FONTNAMEBOXMRUENTRIESFILE = u"/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 );
|
|
}
|
|
|
|
tools::Long BorderWidthImpl::GetLine1( tools::Long nWidth ) const
|
|
{
|
|
tools::Long result = static_cast<tools::Long>(m_nRate1);
|
|
if ( m_nFlags & BorderWidthImplFlags::CHANGE_LINE1 )
|
|
{
|
|
tools::Long const nConstant2 = (m_nFlags & BorderWidthImplFlags::CHANGE_LINE2) ? 0 : m_nRate2;
|
|
tools::Long const nConstantD = (m_nFlags & BorderWidthImplFlags::CHANGE_DIST ) ? 0 : m_nRateGap;
|
|
result = std::max<tools::Long>(0,
|
|
static_cast<tools::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;
|
|
}
|
|
|
|
tools::Long BorderWidthImpl::GetLine2( tools::Long nWidth ) const
|
|
{
|
|
tools::Long result = static_cast<tools::Long>(m_nRate2);
|
|
if ( m_nFlags & BorderWidthImplFlags::CHANGE_LINE2)
|
|
{
|
|
tools::Long const nConstant1 = (m_nFlags & BorderWidthImplFlags::CHANGE_LINE1) ? 0 : m_nRate1;
|
|
tools::Long const nConstantD = (m_nFlags & BorderWidthImplFlags::CHANGE_DIST ) ? 0 : m_nRateGap;
|
|
result = std::max<tools::Long>(0,
|
|
static_cast<tools::Long>((m_nRate2 * nWidth) + 0.5)
|
|
- (nConstant1 + nConstantD));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
tools::Long BorderWidthImpl::GetGap( tools::Long nWidth ) const
|
|
{
|
|
tools::Long result = static_cast<tools::Long>(m_nRateGap);
|
|
if ( m_nFlags & BorderWidthImplFlags::CHANGE_DIST )
|
|
{
|
|
tools::Long const nConstant1 = (m_nFlags & BorderWidthImplFlags::CHANGE_LINE1) ? 0 : m_nRate1;
|
|
tools::Long const nConstant2 = (m_nFlags & BorderWidthImplFlags::CHANGE_LINE2) ? 0 : m_nRate2;
|
|
result = std::max<tools::Long>(0,
|
|
static_cast<tools::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( tools::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;
|
|
}
|
|
|
|
tools::Long BorderWidthImpl::GuessWidth( tools::Long nLine1, tools::Long nLine2, tools::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 (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 (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;
|
|
}
|
|
|
|
static void lclDrawPolygon( OutputDevice& rDev, const basegfx::B2DPolygon& rPolygon, tools::Long nWidth, SvxBorderLineStyle nDashing )
|
|
{
|
|
AntialiasingFlags nOldAA = rDev.GetAntialiasing();
|
|
rDev.SetAntialiasing( nOldAA & ~AntialiasingFlags::Enable );
|
|
|
|
tools::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++ )
|
|
{
|
|
const 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.
|
|
*/
|
|
static 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 );
|
|
}
|
|
|
|
}
|
|
|
|
static Size gUserItemSz;
|
|
static int gFontNameBoxes;
|
|
static size_t gPreviewsPerDevice;
|
|
static std::vector<VclPtr<VirtualDevice>> gFontPreviewVirDevs;
|
|
static std::vector<OUString> gRenderedFontNames;
|
|
static int gHighestDPI = 0;
|
|
|
|
namespace
|
|
{
|
|
std::vector<VclPtr<VirtualDevice>>& getFontPreviewVirDevs()
|
|
{
|
|
return gFontPreviewVirDevs;
|
|
}
|
|
|
|
void clearFontPreviewVirDevs()
|
|
{
|
|
for (auto &rDev : gFontPreviewVirDevs)
|
|
rDev.disposeAndClear();
|
|
gFontPreviewVirDevs.clear();
|
|
}
|
|
|
|
std::vector<OUString>& getRenderedFontNames()
|
|
{
|
|
return gRenderedFontNames;
|
|
}
|
|
|
|
void clearRenderedFontNames()
|
|
{
|
|
gRenderedFontNames.clear();
|
|
}
|
|
|
|
void calcCustomItemSize(const weld::ComboBox& rComboBox)
|
|
{
|
|
gUserItemSz = Size(rComboBox.get_approximate_digit_width() * 52, rComboBox.get_text_height());
|
|
gUserItemSz.setHeight(gUserItemSz.Height() * 16);
|
|
gUserItemSz.setHeight(gUserItemSz.Height() / 10);
|
|
|
|
size_t nMaxDeviceHeight = SAL_MAX_INT16 / 16; // see limitXCreatePixmap and be generous wrt up to x16 hidpi
|
|
assert(gUserItemSz.Height() != 0);
|
|
gPreviewsPerDevice = gUserItemSz.Height() == 0 ? 16 : nMaxDeviceHeight / gUserItemSz.Height();
|
|
if (comphelper::LibreOfficeKit::isActive())
|
|
gPreviewsPerDevice = 1;
|
|
}
|
|
}
|
|
|
|
IMPL_LINK(FontNameBox, SettingsChangedHdl, VclSimpleEvent&, rEvent, void)
|
|
{
|
|
if (rEvent.GetId() != VclEventId::ApplicationDataChanged)
|
|
return;
|
|
|
|
if (comphelper::LibreOfficeKit::isActive())
|
|
return;
|
|
|
|
DataChangedEvent* pData = static_cast<DataChangedEvent*>(static_cast<VclWindowEvent&>(rEvent).GetData());
|
|
if (pData->GetType() == DataChangedEventType::SETTINGS)
|
|
{
|
|
clearFontPreviewVirDevs();
|
|
clearRenderedFontNames();
|
|
calcCustomItemSize(*m_xComboBox);
|
|
if (mbWYSIWYG && mpFontList && !mpFontList->empty())
|
|
{
|
|
mnPreviewProgress = 0;
|
|
maUpdateIdle.Start();
|
|
}
|
|
}
|
|
}
|
|
|
|
FontNameBox::FontNameBox(std::unique_ptr<weld::ComboBox> p)
|
|
: m_xComboBox(std::move(p))
|
|
, mnPreviewProgress(0)
|
|
, mbWYSIWYG(false)
|
|
, maUpdateIdle("FontNameBox Preview Update")
|
|
{
|
|
++gFontNameBoxes;
|
|
InitFontMRUEntriesFile();
|
|
|
|
maUpdateIdle.SetPriority(TaskPriority::LOWEST);
|
|
maUpdateIdle.SetInvokeHandler(LINK(this, FontNameBox, UpdateHdl));
|
|
|
|
Application::AddEventListener(LINK(this, FontNameBox, SettingsChangedHdl));
|
|
}
|
|
|
|
FontNameBox::~FontNameBox()
|
|
{
|
|
Application::RemoveEventListener(LINK(this, FontNameBox, SettingsChangedHdl));
|
|
|
|
if (mpFontList)
|
|
{
|
|
SaveMRUEntries (maFontMRUEntriesFile);
|
|
ImplDestroyFontList();
|
|
}
|
|
--gFontNameBoxes;
|
|
if (!gFontNameBoxes)
|
|
{
|
|
clearFontPreviewVirDevs();
|
|
clearRenderedFontNames();
|
|
}
|
|
}
|
|
|
|
void FontNameBox::SaveMRUEntries(const OUString& aFontMRUEntriesFile) const
|
|
{
|
|
OString aEntries(OUStringToOString(m_xComboBox->get_mru_entries(),
|
|
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( "" );
|
|
}
|
|
|
|
void FontNameBox::LoadMRUEntries( const OUString& aFontMRUEntriesFile )
|
|
{
|
|
if (aFontMRUEntriesFile.isEmpty())
|
|
return;
|
|
|
|
if (!officecfg::Office::Common::Font::View::ShowFontBoxWYSIWYG::get())
|
|
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);
|
|
m_xComboBox->set_mru_entries(aEntries);
|
|
}
|
|
|
|
void FontNameBox::InitFontMRUEntriesFile()
|
|
{
|
|
OUString sUserConfigDir(u"${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE( "bootstrap") "::UserInstallation}"_ustr);
|
|
rtl::Bootstrap::expandMacros(sUserConfigDir);
|
|
|
|
maFontMRUEntriesFile = sUserConfigDir;
|
|
if( !maFontMRUEntriesFile.isEmpty() )
|
|
{
|
|
maFontMRUEntriesFile += FONTNAMEBOXMRUENTRIESFILE;
|
|
}
|
|
}
|
|
|
|
void FontNameBox::ImplDestroyFontList()
|
|
{
|
|
mpFontList.reset();
|
|
mnPreviewProgress = 0;
|
|
maUpdateIdle.Stop();
|
|
}
|
|
|
|
void FontNameBox::Fill( const FontList* pList )
|
|
{
|
|
// store old text and clear box
|
|
OUString aOldText = m_xComboBox->get_active_text();
|
|
OUString rEntries = m_xComboBox->get_mru_entries();
|
|
bool bLoadFromFile = rEntries.isEmpty();
|
|
m_xComboBox->freeze();
|
|
m_xComboBox->clear();
|
|
|
|
ImplDestroyFontList();
|
|
mpFontList.reset(new ImplFontList);
|
|
|
|
// insert fonts
|
|
size_t nFontCount = pList->GetFontNameCount();
|
|
for (size_t i = 0; i < nFontCount; ++i)
|
|
{
|
|
const FontMetric& rFontMetric = pList->GetFontName(i);
|
|
m_xComboBox->append(OUString::number(i), rFontMetric.GetFamilyName());
|
|
mpFontList->push_back(rFontMetric);
|
|
}
|
|
|
|
if (bLoadFromFile)
|
|
LoadMRUEntries(maFontMRUEntriesFile);
|
|
else
|
|
m_xComboBox->set_mru_entries(rEntries);
|
|
|
|
m_xComboBox->thaw();
|
|
|
|
if (mbWYSIWYG && nFontCount)
|
|
{
|
|
assert(mnPreviewProgress == 0 && "ImplDestroyFontList wasn't called");
|
|
maUpdateIdle.Start();
|
|
}
|
|
|
|
// restore text
|
|
if (!aOldText.isEmpty())
|
|
set_active_or_entry_text(aOldText);
|
|
}
|
|
|
|
void FontNameBox::EnableWYSIWYG(bool bEnable)
|
|
{
|
|
if (o3tl::IsRunningUnitTest())
|
|
return;
|
|
if (mbWYSIWYG == bEnable)
|
|
return;
|
|
mbWYSIWYG = bEnable;
|
|
|
|
if (mbWYSIWYG)
|
|
{
|
|
calcCustomItemSize(*m_xComboBox);
|
|
m_xComboBox->connect_custom_get_size(LINK(this, FontNameBox, CustomGetSizeHdl));
|
|
m_xComboBox->connect_custom_render(LINK(this, FontNameBox, CustomRenderHdl));
|
|
}
|
|
else
|
|
{
|
|
m_xComboBox->connect_custom_get_size(Link<OutputDevice&, Size>());
|
|
m_xComboBox->connect_custom_render(Link<weld::ComboBox::render_args, void>());
|
|
}
|
|
m_xComboBox->set_custom_renderer(mbWYSIWYG);
|
|
}
|
|
|
|
IMPL_LINK(FontNameBox, CustomGetSizeHdl, OutputDevice&, rDevice, Size)
|
|
{
|
|
if (comphelper::LibreOfficeKit::isActive())
|
|
{
|
|
calcCustomItemSize(*m_xComboBox);
|
|
gUserItemSz.setWidth(1.0 * rDevice.GetDPIX() / 96.0 * gUserItemSz.getWidth());
|
|
gUserItemSz.setHeight(1.0 * rDevice.GetDPIY() / 96.0 * gUserItemSz.getHeight());
|
|
}
|
|
return mbWYSIWYG ? gUserItemSz : Size();
|
|
}
|
|
|
|
namespace
|
|
{
|
|
tools::Long shrinkFontToFit(OUString const &rSampleText, tools::Long nH, vcl::Font &rFont, OutputDevice &rDevice, tools::Rectangle &rTextRect)
|
|
{
|
|
tools::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;
|
|
}
|
|
}
|
|
|
|
IMPL_LINK_NOARG(FontNameBox, UpdateHdl, Timer*, void)
|
|
{
|
|
if (comphelper::LibreOfficeKit::isActive())
|
|
return;
|
|
|
|
CachePreview(mnPreviewProgress++, nullptr);
|
|
// tdf#132536 limit to ~25 pre-rendered for now. The font caches look
|
|
// b0rked, the massive charmaps are ~never swapped out, and don't count
|
|
// towards the size of a font in the font cache.
|
|
if (mnPreviewProgress < std::min<size_t>(25, mpFontList->size()))
|
|
maUpdateIdle.Start();
|
|
}
|
|
|
|
static void DrawPreview(const FontMetric& rFontMetric, const Point& rTopLeft, OutputDevice& rDevice, bool bSelected)
|
|
{
|
|
rDevice.Push(vcl::PushFlags::TEXTCOLOR);
|
|
|
|
const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
|
|
if (bSelected)
|
|
rDevice.SetTextColor(rStyleSettings.GetHighlightTextColor());
|
|
else
|
|
rDevice.SetTextColor(rStyleSettings.GetDialogTextColor());
|
|
|
|
tools::Long nX = rTopLeft.X();
|
|
tools::Long nH = gUserItemSz.Height();
|
|
|
|
nX += IMGOUTERTEXTSPACE;
|
|
|
|
const bool bSymbolFont = isSymbolFont(rFontMetric);
|
|
|
|
vcl::Font aOldFont(rDevice.GetFont());
|
|
Size aSize( aOldFont.GetFontSize() );
|
|
aSize.AdjustHeight(EXTRAFONTSIZE );
|
|
vcl::Font aFont( rFontMetric );
|
|
aFont.SetFontSize( aSize );
|
|
rDevice.SetFont(aFont);
|
|
|
|
bool bUsingCorrectFont = true;
|
|
tools::Rectangle aTextRect;
|
|
|
|
// Preview the font name
|
|
const OUString& sFontName = rFontMetric.GetFamilyName();
|
|
|
|
//If it shouldn't or can't draw its own name because it doesn't have the glyphs
|
|
if (!canRenderNameOfSelectedFont(rDevice))
|
|
bUsingCorrectFont = false;
|
|
else
|
|
{
|
|
//Make sure it fits in the available height, shrinking the font if necessary
|
|
bUsingCorrectFont = shrinkFontToFit(sFontName, nH, aFont, rDevice, aTextRect) != 0;
|
|
}
|
|
|
|
if (!bUsingCorrectFont)
|
|
{
|
|
rDevice.SetFont(aOldFont);
|
|
rDevice.GetTextBoundRect(aTextRect, sFontName);
|
|
}
|
|
|
|
tools::Long nTextHeight = aTextRect.GetHeight();
|
|
tools::Long nDesiredGap = (nH-nTextHeight)/2;
|
|
tools::Long nVertAdjust = nDesiredGap - aTextRect.Top();
|
|
Point aPos( nX, rTopLeft.Y() + nVertAdjust );
|
|
rDevice.DrawText(aPos, sFontName);
|
|
tools::Long nTextX = aPos.X() + aTextRect.GetWidth() + GAPTOEXTRAPREVIEW;
|
|
|
|
if (!bUsingCorrectFont)
|
|
rDevice.SetFont(aFont);
|
|
|
|
OUString sSampleText;
|
|
|
|
if (!bSymbolFont)
|
|
{
|
|
const bool bNameBeginsWithLatinText = rFontMetric.GetFamilyName()[0] <= 'z';
|
|
|
|
if (bNameBeginsWithLatinText || !bUsingCorrectFont)
|
|
sSampleText = makeShortRepresentativeTextForSelectedFont(rDevice);
|
|
}
|
|
|
|
//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 == rDevice.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 == rDevice.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(rDevice);
|
|
|
|
if (!sSampleText.isEmpty())
|
|
{
|
|
const Size &rItemSize = gUserItemSz;
|
|
|
|
//leave a little border at the edge
|
|
tools::Long nSpace = rItemSize.Width() - nTextX - IMGOUTERTEXTSPACE;
|
|
if (nSpace >= 0)
|
|
{
|
|
//Make sure it fits in the available height, and get how wide that would be
|
|
tools::Long nWidth = shrinkFontToFit(sSampleText, nH, aFont, rDevice, aTextRect);
|
|
//Chop letters off until it fits in the available width
|
|
while (nWidth > nSpace || nWidth > gUserItemSz.Width())
|
|
{
|
|
sSampleText = sSampleText.copy(0, sSampleText.getLength()-1);
|
|
nWidth = rDevice.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, rTopLeft.Y() + nVertAdjust);
|
|
rDevice.DrawText(aPos, sSampleText);
|
|
}
|
|
}
|
|
}
|
|
|
|
rDevice.SetFont(aOldFont);
|
|
rDevice.Pop();
|
|
}
|
|
|
|
OutputDevice& FontNameBox::CachePreview(size_t nIndex, Point* pTopLeft,
|
|
sal_Int32 nDPIX, sal_Int32 nDPIY)
|
|
{
|
|
SolarMutexGuard aGuard;
|
|
const FontMetric& rFontMetric = (*mpFontList)[nIndex];
|
|
const OUString& rFontName = rFontMetric.GetFamilyName();
|
|
|
|
if (comphelper::LibreOfficeKit::isActive())
|
|
{
|
|
// we want to cache only best quality previews
|
|
if (gHighestDPI < nDPIX || gHighestDPI < nDPIY)
|
|
{
|
|
clearRenderedFontNames();
|
|
clearFontPreviewVirDevs();
|
|
gHighestDPI = std::max(nDPIX, nDPIY);
|
|
}
|
|
else if (gHighestDPI > nDPIX || gHighestDPI > nDPIY)
|
|
{
|
|
nDPIX = gHighestDPI;
|
|
nDPIY = gHighestDPI;
|
|
}
|
|
}
|
|
|
|
size_t nPreviewIndex;
|
|
auto& rFontNames = getRenderedFontNames();
|
|
auto& rVirtualDevs = getFontPreviewVirDevs();
|
|
auto xFind = std::find(rFontNames.begin(), rFontNames.end(), rFontName);
|
|
bool bPreviewAvailable = xFind != rFontNames.end();
|
|
if (!bPreviewAvailable)
|
|
{
|
|
nPreviewIndex = rFontNames.size();
|
|
rFontNames.push_back(rFontName);
|
|
}
|
|
else
|
|
nPreviewIndex = std::distance(rFontNames.begin(), xFind);
|
|
|
|
size_t nPage = nPreviewIndex / gPreviewsPerDevice;
|
|
size_t nIndexInPage = nPreviewIndex - (nPage * gPreviewsPerDevice);
|
|
|
|
Point aTopLeft(0, gUserItemSz.Height() * nIndexInPage);
|
|
|
|
if (!bPreviewAvailable)
|
|
{
|
|
if (nPage >= rVirtualDevs.size())
|
|
{
|
|
bool bIsLOK = comphelper::LibreOfficeKit::isActive();
|
|
if (bIsLOK) // allow transparent background in LOK case
|
|
rVirtualDevs.emplace_back(VclPtr<VirtualDevice>::Create(DeviceFormat::WITH_ALPHA));
|
|
else
|
|
rVirtualDevs.emplace_back(m_xComboBox->create_render_virtual_device());
|
|
|
|
VirtualDevice& rDevice = *rVirtualDevs.back();
|
|
rDevice.SetOutputSizePixel(Size(gUserItemSz.Width(), gUserItemSz.Height() * gPreviewsPerDevice));
|
|
if (bIsLOK)
|
|
{
|
|
rDevice.SetDPIX(nDPIX);
|
|
rDevice.SetDPIY(nDPIY);
|
|
}
|
|
|
|
weld::SetPointFont(rDevice, m_xComboBox->get_font(), bIsLOK);
|
|
assert(rVirtualDevs.size() == nPage + 1);
|
|
}
|
|
|
|
DrawPreview(rFontMetric, aTopLeft, *rVirtualDevs.back(), false);
|
|
}
|
|
|
|
if (pTopLeft)
|
|
*pTopLeft = aTopLeft;
|
|
|
|
return *rVirtualDevs[nPage];
|
|
}
|
|
|
|
IMPL_LINK(FontNameBox, CustomRenderHdl, weld::ComboBox::render_args, aPayload, void)
|
|
{
|
|
vcl::RenderContext& rRenderContext = std::get<0>(aPayload);
|
|
const ::tools::Rectangle& rRect = std::get<1>(aPayload);
|
|
bool bSelected = std::get<2>(aPayload);
|
|
const OUString& rId = std::get<3>(aPayload);
|
|
|
|
sal_uInt32 nIndex = rId.toUInt32();
|
|
|
|
Point aDestPoint(rRect.TopLeft());
|
|
auto nMargin = (rRect.GetHeight() - gUserItemSz.Height()) / 2;
|
|
aDestPoint.AdjustY(nMargin);
|
|
|
|
if (bSelected)
|
|
{
|
|
const FontMetric& rFontMetric = (*mpFontList)[nIndex];
|
|
DrawPreview(rFontMetric, aDestPoint, rRenderContext, true);
|
|
m_aLivePreviewHdl.Call(rFontMetric);
|
|
}
|
|
else
|
|
{
|
|
// use cache of unselected entries
|
|
Point aTopLeft;
|
|
OutputDevice& rDevice = CachePreview(nIndex, &aTopLeft,
|
|
rRenderContext.GetDPIX(),
|
|
rRenderContext.GetDPIY());
|
|
|
|
Size aSourceSize = comphelper::LibreOfficeKit::isActive() ? rDevice.GetOutputSizePixel() : gUserItemSz;
|
|
rRenderContext.DrawOutDev(aDestPoint, gUserItemSz,
|
|
aTopLeft, aSourceSize,
|
|
rDevice);
|
|
}
|
|
}
|
|
|
|
void FontNameBox::set_active_or_entry_text(const OUString& rText)
|
|
{
|
|
const int nFound = m_xComboBox->find_text(rText);
|
|
if (nFound != -1)
|
|
m_xComboBox->set_active(nFound);
|
|
m_xComboBox->set_entry_text(rText);
|
|
}
|
|
|
|
FontStyleBox::FontStyleBox(std::unique_ptr<weld::ComboBox> p)
|
|
: m_xComboBox(std::move(p))
|
|
, m_aLastStyle(m_xComboBox->get_active_text())
|
|
{
|
|
//Use the standard texts to get an optimal size and stick to that size.
|
|
//That should stop the character dialog dancing around.
|
|
auto nMaxLen = m_xComboBox->get_pixel_size(SvtResId(STR_SVT_STYLE_LIGHT)).Width();
|
|
nMaxLen = std::max(nMaxLen, m_xComboBox->get_pixel_size(SvtResId(STR_SVT_STYLE_LIGHT_ITALIC)).Width());
|
|
nMaxLen = std::max(nMaxLen, m_xComboBox->get_pixel_size(SvtResId(STR_SVT_STYLE_NORMAL)).Width());
|
|
nMaxLen = std::max(nMaxLen, m_xComboBox->get_pixel_size(SvtResId(STR_SVT_STYLE_NORMAL_ITALIC)).Width());
|
|
nMaxLen = std::max(nMaxLen, m_xComboBox->get_pixel_size(SvtResId(STR_SVT_STYLE_BOLD)).Width());
|
|
nMaxLen = std::max(nMaxLen, m_xComboBox->get_pixel_size(SvtResId(STR_SVT_STYLE_BOLD_ITALIC)).Width());
|
|
nMaxLen = std::max(nMaxLen, m_xComboBox->get_pixel_size(SvtResId(STR_SVT_STYLE_BLACK)).Width());
|
|
nMaxLen = std::max(nMaxLen, m_xComboBox->get_pixel_size(SvtResId(STR_SVT_STYLE_BLACK_ITALIC)).Width());
|
|
m_xComboBox->set_entry_width_chars(std::ceil(nMaxLen / m_xComboBox->get_approximate_digit_width()));
|
|
m_xComboBox->connect_changed(LINK(this, FontStyleBox, ChangeHdl));
|
|
}
|
|
|
|
IMPL_LINK(FontStyleBox, ChangeHdl, weld::ComboBox&, rComboBox, void)
|
|
{
|
|
// update m_aLastStyle to whatever is explicitly selected by the user
|
|
m_aLastStyle = rComboBox.get_active_text();
|
|
m_aChangedLink.Call(rComboBox);
|
|
}
|
|
|
|
void FontStyleBox::Fill( std::u16string_view rName, const FontList* pList )
|
|
{
|
|
OUString aOldText = m_xComboBox->get_active_text();
|
|
|
|
m_xComboBox->freeze();
|
|
m_xComboBox->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 )
|
|
m_xComboBox->append_text(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 = m_xComboBox->find_text(aStyleText) == -1;
|
|
if ( !bInsert )
|
|
{
|
|
aStyleText = pList->GetStyleName( eWeight, eItalic );
|
|
bInsert = m_xComboBox->find_text(aStyleText) == -1;
|
|
}
|
|
|
|
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 = m_xComboBox->find_text(aStyleText) == -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
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 )
|
|
m_xComboBox->append_text(aStyleText);
|
|
|
|
// certain style as copy
|
|
if ( bNormal )
|
|
{
|
|
if ( !bItalic )
|
|
m_xComboBox->append_text(pList->GetItalicStr());
|
|
if ( !bBold )
|
|
m_xComboBox->append_text(pList->GetBoldStr());
|
|
}
|
|
if ( !bBoldItalic )
|
|
{
|
|
if ( bNormal || bItalic || bBold )
|
|
m_xComboBox->append_text(pList->GetBoldItalicStr());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// insert standard styles if no font
|
|
m_xComboBox->append_text(pList->GetNormalStr());
|
|
m_xComboBox->append_text(pList->GetItalicStr());
|
|
m_xComboBox->append_text(pList->GetBoldStr());
|
|
m_xComboBox->append_text(pList->GetBoldItalicStr());
|
|
}
|
|
|
|
m_xComboBox->thaw();
|
|
|
|
// tdf#162113 prefer restoring the last explicitly set
|
|
// style if that is possible
|
|
if (!m_aLastStyle.isEmpty())
|
|
{
|
|
int nFound = m_xComboBox->find_text(m_aLastStyle);
|
|
if (nFound != -1)
|
|
{
|
|
m_xComboBox->set_active(nFound);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// otherwise, restore the style that was last selected
|
|
// if that is possible
|
|
if (!aOldText.isEmpty())
|
|
{
|
|
int nFound = m_xComboBox->find_text(aOldText);
|
|
if (nFound != -1)
|
|
{
|
|
m_xComboBox->set_active(nFound);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// otherwise, just pick something
|
|
m_xComboBox->set_active(0);
|
|
}
|
|
|
|
FontSizeBox::FontSizeBox(std::unique_ptr<weld::ComboBox> p)
|
|
: pFontList(nullptr)
|
|
, nSavedValue(0)
|
|
, nMin(20)
|
|
, nMax(9999)
|
|
, eUnit(FieldUnit::POINT)
|
|
, nDecimalDigits(1)
|
|
, nRelMin(0)
|
|
, nRelMax(0)
|
|
, nRelStep(0)
|
|
, nPtRelMin(0)
|
|
, nPtRelMax(0)
|
|
, nPtRelStep(0)
|
|
, bRelativeMode(false)
|
|
, bRelative(false)
|
|
, bPtRelative(false)
|
|
, bStdSize(false)
|
|
, m_xComboBox(std::move(p))
|
|
{
|
|
m_xComboBox->set_entry_width_chars(std::ceil(m_xComboBox->get_pixel_size(format_number(105)).Width() /
|
|
m_xComboBox->get_approximate_digit_width()));
|
|
m_xComboBox->connect_focus_out(LINK(this, FontSizeBox, ReformatHdl));
|
|
m_xComboBox->connect_changed(LINK(this, FontSizeBox, ModifyHdl));
|
|
}
|
|
|
|
void FontSizeBox::set_active_or_entry_text(const OUString& rText)
|
|
{
|
|
const int nFound = m_xComboBox->find_text(rText);
|
|
if (nFound != -1)
|
|
m_xComboBox->set_active(nFound);
|
|
m_xComboBox->set_entry_text(rText);
|
|
}
|
|
|
|
IMPL_LINK(FontSizeBox, ReformatHdl, weld::Widget&, rWidget, void)
|
|
{
|
|
FontSizeNames aFontSizeNames(Application::GetSettings().GetUILanguageTag().getLanguageType());
|
|
if (!bRelativeMode || !aFontSizeNames.IsEmpty())
|
|
{
|
|
if (aFontSizeNames.Name2Size(m_xComboBox->get_active_text()) != 0)
|
|
return;
|
|
}
|
|
|
|
set_value(get_value());
|
|
|
|
m_aFocusOutHdl.Call(rWidget);
|
|
}
|
|
|
|
IMPL_LINK(FontSizeBox, ModifyHdl, weld::ComboBox&, rBox, void)
|
|
{
|
|
if (bRelativeMode)
|
|
{
|
|
OUString aStr = comphelper::string::stripStart(rBox.get_active_text(), ' ');
|
|
|
|
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 );
|
|
}
|
|
m_aChangeHdl.Call(rBox);
|
|
}
|
|
|
|
void FontSizeBox::Fill( 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 int* pTempAry;
|
|
const int* pAry = nullptr;
|
|
|
|
pAry = FontList::GetStdSizeAry();
|
|
|
|
// first insert font size names (for simplified/traditional chinese)
|
|
FontSizeNames aFontSizeNames( Application::GetSettings().GetUILanguageTag().getLanguageType() );
|
|
if ( pAry == FontList::GetStdSizeAry() )
|
|
{
|
|
// for standard sizes we don't need to bother
|
|
if (bStdSize && m_xComboBox->get_count() && aFontSizeNames.IsEmpty())
|
|
return;
|
|
bStdSize = true;
|
|
}
|
|
else
|
|
bStdSize = false;
|
|
|
|
int nSelectionStart, nSelectionEnd;
|
|
m_xComboBox->get_entry_selection_bounds(nSelectionStart, nSelectionEnd);
|
|
OUString aStr = m_xComboBox->get_active_text();
|
|
|
|
m_xComboBox->freeze();
|
|
m_xComboBox->clear();
|
|
int nPos = 0;
|
|
|
|
if ( !aFontSizeNames.IsEmpty() )
|
|
{
|
|
if ( pAry == FontList::GetStdSizeAry() )
|
|
{
|
|
// for scalable fonts all font size names
|
|
sal_uInt32 nCount = aFontSizeNames.Count();
|
|
for( sal_uInt32 i = 0; i < nCount; i++ )
|
|
{
|
|
OUString aSizeName = aFontSizeNames.GetIndexName( i );
|
|
int nSize = aFontSizeNames.GetIndexSize( i );
|
|
OUString sId(OUString::number(-nSize)); // mark as special
|
|
m_xComboBox->insert(nPos, aSizeName, &sId, nullptr, nullptr);
|
|
nPos++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// for fixed size fonts only selectable font size names
|
|
pTempAry = pAry;
|
|
while ( *pTempAry )
|
|
{
|
|
OUString aSizeName = aFontSizeNames.Size2Name( *pTempAry );
|
|
if ( !aSizeName.isEmpty() )
|
|
{
|
|
OUString sId(OUString::number(-(*pTempAry))); // mark as special
|
|
m_xComboBox->insert(nPos, aSizeName, &sId, nullptr, nullptr);
|
|
nPos++;
|
|
}
|
|
pTempAry++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// then insert numerical font size values
|
|
pTempAry = pAry;
|
|
while (*pTempAry)
|
|
{
|
|
InsertValue(*pTempAry);
|
|
++pTempAry;
|
|
}
|
|
|
|
m_xComboBox->thaw();
|
|
set_active_or_entry_text(aStr);
|
|
m_xComboBox->select_entry_region(nSelectionStart, nSelectionEnd);
|
|
}
|
|
|
|
void FontSizeBox::EnableRelativeMode( sal_uInt16 nNewMin, sal_uInt16 nNewMax, sal_uInt16 nStep )
|
|
{
|
|
bRelativeMode = true;
|
|
nRelMin = nNewMin;
|
|
nRelMax = nNewMax;
|
|
nRelStep = nStep;
|
|
SetUnit(FieldUnit::POINT);
|
|
}
|
|
|
|
void FontSizeBox::EnablePtRelativeMode( short nNewMin, short nNewMax, short nStep )
|
|
{
|
|
bRelativeMode = true;
|
|
nPtRelMin = nNewMin;
|
|
nPtRelMax = nNewMax;
|
|
nPtRelStep = nStep;
|
|
SetUnit(FieldUnit::POINT);
|
|
}
|
|
|
|
void FontSizeBox::InsertValue(int i)
|
|
{
|
|
OUString sNumber(OUString::number(i));
|
|
m_xComboBox->append(sNumber, format_number(i));
|
|
}
|
|
|
|
void FontSizeBox::SetRelative( bool bNewRelative )
|
|
{
|
|
if ( !bRelativeMode )
|
|
return;
|
|
|
|
int nSelectionStart, nSelectionEnd;
|
|
m_xComboBox->get_entry_selection_bounds(nSelectionStart, nSelectionEnd);
|
|
OUString aStr = comphelper::string::stripStart(m_xComboBox->get_active_text(), ' ');
|
|
|
|
if (bNewRelative)
|
|
{
|
|
bRelative = true;
|
|
bStdSize = false;
|
|
|
|
m_xComboBox->clear();
|
|
|
|
if (bPtRelative)
|
|
{
|
|
SetDecimalDigits( 1 );
|
|
SetRange(nPtRelMin, nPtRelMax);
|
|
SetUnit(FieldUnit::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
|
|
{
|
|
SetDecimalDigits(0);
|
|
SetRange(nRelMin, nRelMax);
|
|
SetUnit(FieldUnit::PERCENT);
|
|
|
|
sal_uInt16 i = nRelMin;
|
|
while ( i <= nRelMax )
|
|
{
|
|
InsertValue( i );
|
|
i = i + nRelStep;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pFontList)
|
|
m_xComboBox->clear();
|
|
bRelative = bPtRelative = false;
|
|
SetDecimalDigits(1);
|
|
SetRange(20, 9999);
|
|
SetUnit(FieldUnit::POINT);
|
|
if ( pFontList)
|
|
Fill( pFontList );
|
|
}
|
|
|
|
set_active_or_entry_text(aStr);
|
|
m_xComboBox->select_entry_region(nSelectionStart, nSelectionEnd);
|
|
}
|
|
|
|
OUString FontSizeBox::format_number(int nValue) const
|
|
{
|
|
OUString sRet;
|
|
|
|
//pawn percent off to icu to decide whether percent is separated from its number for this locale
|
|
if (eUnit == FieldUnit::PERCENT)
|
|
{
|
|
double fValue = nValue;
|
|
fValue /= weld::SpinButton::Power10(nDecimalDigits);
|
|
sRet = unicode::formatPercent(fValue, Application::GetSettings().GetUILanguageTag());
|
|
}
|
|
else
|
|
{
|
|
const SvtSysLocale aSysLocale;
|
|
const LocaleDataWrapper& rLocaleData = aSysLocale.GetLocaleData();
|
|
sRet = rLocaleData.getNum(nValue, nDecimalDigits, true, false);
|
|
if (eUnit != FieldUnit::NONE && eUnit != FieldUnit::DEGREE)
|
|
sRet += " ";
|
|
assert(eUnit != FieldUnit::PERCENT);
|
|
sRet += weld::MetricSpinButton::MetricToString(eUnit);
|
|
}
|
|
|
|
if (bRelativeMode && bPtRelative && (0 <= nValue) && !sRet.isEmpty())
|
|
sRet = "+" + sRet;
|
|
|
|
return sRet;
|
|
}
|
|
|
|
void FontSizeBox::SetValue(int nNewValue, FieldUnit eInUnit)
|
|
{
|
|
auto nTempValue = vcl::ConvertValue(nNewValue, 0, GetDecimalDigits(), eInUnit, GetUnit());
|
|
if (nTempValue < nMin)
|
|
nTempValue = nMin;
|
|
else if (nTempValue > nMax)
|
|
nTempValue = nMax;
|
|
if (!bRelative)
|
|
{
|
|
FontSizeNames aFontSizeNames(Application::GetSettings().GetUILanguageTag().getLanguageType());
|
|
// conversion loses precision; however font sizes should
|
|
// never have a problem with that
|
|
OUString aName = aFontSizeNames.Size2Name(nTempValue);
|
|
if (!aName.isEmpty() && m_xComboBox->find_text(aName) != -1)
|
|
{
|
|
m_xComboBox->set_active_text(aName);
|
|
return;
|
|
}
|
|
}
|
|
OUString aResult = format_number(nTempValue);
|
|
set_active_or_entry_text(aResult);
|
|
}
|
|
|
|
void FontSizeBox::set_value(int nNewValue)
|
|
{
|
|
SetValue(nNewValue, eUnit);
|
|
}
|
|
|
|
int FontSizeBox::get_value() const
|
|
{
|
|
OUString aStr = m_xComboBox->get_active_text();
|
|
if (!bRelative)
|
|
{
|
|
FontSizeNames aFontSizeNames(Application::GetSettings().GetUILanguageTag().getLanguageType());
|
|
auto nValue = aFontSizeNames.Name2Size(aStr);
|
|
if (nValue)
|
|
return vcl::ConvertValue(nValue, 0, GetDecimalDigits(), GetUnit(), GetUnit());
|
|
}
|
|
|
|
const SvtSysLocale aSysLocale;
|
|
const LocaleDataWrapper& rLocaleData = aSysLocale.GetLocaleData();
|
|
double fResult(0.0);
|
|
(void)vcl::TextToValue(aStr, fResult, 0, GetDecimalDigits(), rLocaleData, GetUnit());
|
|
if (!aStr.isEmpty())
|
|
{
|
|
if (fResult < nMin)
|
|
fResult = nMin;
|
|
else if (fResult > nMax)
|
|
fResult = nMax;
|
|
}
|
|
return fResult;
|
|
}
|
|
|
|
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( tools::Long nLine1, tools::Long nLine2, tools::Long nDistance,
|
|
Color aColor1, Color aColor2, Color aColorDist,
|
|
SvxBorderLineStyle nStyle, BitmapEx& rBmp )
|
|
{
|
|
Size aSize(getPreviewSize(*m_xControl));
|
|
|
|
// SourceUnit to Twips
|
|
if ( eSourceUnit == FieldUnit::POINT )
|
|
{
|
|
nLine1 /= 5;
|
|
nLine2 /= 5;
|
|
nDistance /= 5;
|
|
}
|
|
|
|
// Paint the lines
|
|
aSize = aVirDev->PixelToLogic( aSize );
|
|
tools::Long nPix = aVirDev->PixelToLogic( Size( 0, 1 ) ).Height();
|
|
sal_uInt32 n1 = nLine1;
|
|
sal_uInt32 n2 = nLine2;
|
|
tools::Long nDist = nDistance;
|
|
n1 += nPix-1;
|
|
n1 -= n1%nPix;
|
|
if ( n2 )
|
|
{
|
|
nDist += nPix-1;
|
|
nDist -= nDist%nPix;
|
|
n2 += nPix-1;
|
|
n2 -= n2%nPix;
|
|
}
|
|
tools::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, 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, SvxBorderLineStyle::SOLID );
|
|
}
|
|
rBmp = aVirDev->GetBitmapEx( Point(), Size( aSize.Width(), n1+nDist+n2 ) );
|
|
}
|
|
|
|
SvtLineListBox::SvtLineListBox(std::unique_ptr<weld::MenuButton> pControl)
|
|
: WeldToolbarPopup(css::uno::Reference<css::frame::XFrame>(), pControl.get(), u"svt/ui/linewindow.ui"_ustr, u"line_popup_window"_ustr)
|
|
, m_xControl(std::move(pControl))
|
|
, m_xNoneButton(m_xBuilder->weld_button(u"none_line_button"_ustr))
|
|
, m_xLineSet(new ValueSet(nullptr))
|
|
, m_xLineSetWin(new weld::CustomWeld(*m_xBuilder, u"lineset"_ustr, *m_xLineSet))
|
|
, m_nWidth( 5 )
|
|
, aVirDev(VclPtr<VirtualDevice>::Create())
|
|
, aColor(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_xControl->set_popover(m_xTopLevel.get());
|
|
m_xControl->connect_toggled(LINK(this, SvtLineListBox, ToggleHdl));
|
|
m_xControl->connect_style_updated(LINK(this, SvtLineListBox, StyleUpdatedHdl));
|
|
|
|
// 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(u""_ustr);
|
|
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 = FieldUnit::POINT;
|
|
|
|
aVirDev->SetLineColor();
|
|
aVirDev->SetMapMode(MapMode(MapUnit::MapTwip));
|
|
}
|
|
|
|
void SvtLineListBox::GrabFocus()
|
|
{
|
|
if (GetSelectEntryStyle() == SvxBorderLineStyle::NONE)
|
|
m_xNoneButton->grab_focus();
|
|
else
|
|
m_xLineSet->GrabFocus();
|
|
}
|
|
|
|
IMPL_LINK(SvtLineListBox, ToggleHdl, weld::Toggleable&, rButton, void)
|
|
{
|
|
if (rButton.get_active())
|
|
GrabFocus();
|
|
}
|
|
|
|
IMPL_LINK_NOARG(SvtLineListBox, StyleUpdatedHdl, weld::Widget&, void)
|
|
{
|
|
UpdateEntries();
|
|
UpdatePreview();
|
|
}
|
|
|
|
IMPL_LINK_NOARG(SvtLineListBox, NoneHdl, weld::Button&, void)
|
|
{
|
|
SelectEntry(SvxBorderLineStyle::NONE);
|
|
ValueSelectHdl(nullptr);
|
|
}
|
|
|
|
SvtLineListBox::~SvtLineListBox()
|
|
{
|
|
}
|
|
|
|
OUString SvtLineListBox::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;
|
|
}
|
|
|
|
void SvtLineListBox::SelectEntry(SvxBorderLineStyle nStyle)
|
|
{
|
|
if (nStyle == SvxBorderLineStyle::NONE)
|
|
m_xLineSet->SetNoSelection();
|
|
else
|
|
m_xLineSet->SelectItem(static_cast<sal_Int16>(nStyle) + 1);
|
|
UpdatePreview();
|
|
}
|
|
|
|
void SvtLineListBox::InsertEntry(
|
|
const BorderWidthImpl& rWidthImpl, SvxBorderLineStyle nStyle, tools::Long nMinWidth,
|
|
ColorFunc pColor1Fn, ColorFunc pColor2Fn, ColorDistFunc pColorDistFn )
|
|
{
|
|
m_vLineList.emplace_back(new ImpLineListData(
|
|
rWidthImpl, nStyle, nMinWidth, pColor1Fn, pColor2Fn, pColorDistFn));
|
|
}
|
|
|
|
void SvtLineListBox::UpdateEntries()
|
|
{
|
|
SvxBorderLineStyle eSelected = GetSelectEntryStyle();
|
|
|
|
// Remove the old entries
|
|
m_xLineSet->Clear();
|
|
|
|
const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
|
|
Color aFieldColor = rSettings.GetFieldColor();
|
|
|
|
// 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 ),
|
|
pData->GetColorLine1(aColor),
|
|
pData->GetColorLine2(aColor),
|
|
pData->GetColorDist(aColor, aFieldColor),
|
|
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();
|
|
}
|
|
|
|
IMPL_LINK_NOARG(SvtLineListBox, ValueSelectHdl, ValueSet*, 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(u""_ustr);
|
|
const auto nPos = (aVirDev->GetOutputSizePixel().Height() - aImage.GetSizePixel().Height()) / 2;
|
|
aVirDev->Push(vcl::PushFlags::MAPMODE);
|
|
aVirDev->SetMapMode(MapMode(MapUnit::MapPixel));
|
|
const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
|
|
aVirDev->SetBackground(rSettings.GetFieldColor());
|
|
aVirDev->Erase();
|
|
aVirDev->DrawImage(Point(0, nPos), aImage);
|
|
m_xControl->set_image(aVirDev.get());
|
|
aVirDev->Pop();
|
|
}
|
|
}
|
|
|
|
SvtCalendarBox::SvtCalendarBox(std::unique_ptr<weld::MenuButton> pControl, bool bUseLabel)
|
|
: m_bUseLabel(bUseLabel)
|
|
, m_xControl(std::move(pControl))
|
|
, m_xBuilder(Application::CreateBuilder(m_xControl.get(), u"svt/ui/datewindow.ui"_ustr))
|
|
, m_xTopLevel(m_xBuilder->weld_popover(u"date_popup_window"_ustr))
|
|
, m_xCalendar(m_xBuilder->weld_calendar(u"date_picker"_ustr))
|
|
{
|
|
m_xControl->set_popover(m_xTopLevel.get());
|
|
m_xCalendar->connect_selected(LINK(this, SvtCalendarBox, SelectHdl));
|
|
m_xCalendar->connect_activated(LINK(this, SvtCalendarBox, ActivateHdl));
|
|
}
|
|
|
|
void SvtCalendarBox::set_date(const Date& rDate)
|
|
{
|
|
m_xCalendar->set_date(rDate);
|
|
set_label_from_date();
|
|
}
|
|
|
|
void SvtCalendarBox::set_label_from_date()
|
|
{
|
|
if (!m_bUseLabel)
|
|
return;
|
|
const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
|
|
m_xControl->set_label(rLocaleData.getDate(m_xCalendar->get_date()));
|
|
}
|
|
|
|
IMPL_LINK_NOARG(SvtCalendarBox, SelectHdl, weld::Calendar&, void)
|
|
{
|
|
set_label_from_date();
|
|
m_aSelectHdl.Call(*this);
|
|
}
|
|
|
|
IMPL_LINK_NOARG(SvtCalendarBox, ActivateHdl, weld::Calendar&, void)
|
|
{
|
|
if (m_xControl->get_active())
|
|
m_xControl->set_active(false);
|
|
m_aActivatedHdl.Call(*this);
|
|
}
|
|
|
|
SvtCalendarBox::~SvtCalendarBox()
|
|
{
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|