/* -*- 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 <utility>
#include <comphelper/configurationlistener.hxx>
#include <comphelper/propertysequence.hxx>
#include <comphelper/propertyvalue.hxx>
#include <tools/color.hxx>
#include <svl/numformat.hxx>
#include <svl/poolitem.hxx>
#include <svl/itemset.hxx>
#include <svl/itempool.hxx>
#include <vcl/commandinfoprovider.hxx>
#include <vcl/event.hxx>
#include <vcl/toolbox.hxx>
#include <vcl/customweld.hxx>
#include <vcl/vclptr.hxx>
#include <vcl/weldutils.hxx>
#include <svtools/valueset.hxx>
#include <svtools/ctrlbox.hxx>
#include <svl/style.hxx>
#include <svtools/ctrltool.hxx>
#include <svtools/borderhelper.hxx>
#include <vcl/InterimItemWindow.hxx>
#include <sfx2/tbxctrl.hxx>
#include <sfx2/tplpitem.hxx>
#include <sfx2/sfxstatuslistener.hxx>
#include <sfx2/viewsh.hxx>
#include <toolkit/helper/vclunohelper.hxx>
#include <sfx2/viewfrm.hxx>
#include <vcl/svapp.hxx>
#include <vcl/settings.hxx>
#include <vcl/virdev.hxx>
#include <com/sun/star/awt/FontDescriptor.hpp>
#include <com/sun/star/table/BorderLine2.hpp>
#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
#include <com/sun/star/frame/XDispatchProvider.hpp>
#include <com/sun/star/frame/XFrame.hpp>
#include <svx/strings.hrc>
#include <svx/svxids.hrc>
#include <helpids.h>
#include <sfx2/sidebar/Sidebar.hxx>
#include <svx/xtable.hxx>
#include <editeng/editids.hrc>
#include <editeng/fontitem.hxx>
#include <editeng/fhgtitem.hxx>
#include <editeng/boxitem.hxx>
#include <editeng/charreliefitem.hxx>
#include <editeng/contouritem.hxx>
#include <editeng/colritem.hxx>
#include <editeng/crossedoutitem.hxx>
#include <editeng/emphasismarkitem.hxx>
#include <editeng/flstitem.hxx>
#include <editeng/lineitem.hxx>
#include <editeng/postitem.hxx>
#include <editeng/shdditem.hxx>
#include <editeng/udlnitem.hxx>
#include <editeng/wghtitem.hxx>
#include <editeng/svxfont.hxx>
#include <editeng/cmapitem.hxx>
#include <svx/colorwindow.hxx>
#include <svx/colorbox.hxx>
#include <svx/tbcontrl.hxx>
#include <svx/dialmgr.hxx>
#include <svx/PaletteManager.hxx>
#include <memory>
#include <tbxcolorupdate.hxx>
#include <editeng/eerdll.hxx>
#include <editeng/editrids.hrc>
#include <svx/xdef.hxx>
#include <svx/xfillit0.hxx>
#include <svx/xflclit.hxx>
#include <svl/currencytable.hxx>
#include <svtools/langtab.hxx>
#include <cppuhelper/supportsservice.hxx>
#include <officecfg/Office/Common.hxx>
#include <o3tl/temporary.hxx>
#include <o3tl/safeint.hxx>
#include <o3tl/string_view.hxx>
#include <o3tl/typed_flags_set.hxx>
#include <bitmaps.hlst>
#include <sal/log.hxx>
#include <unotools/collatorwrapper.hxx>
#include <sfx2/IDocumentModelAccessor.hxx>
#include <comphelper/lok.hxx>
#include <tools/json_writer.hxx>
#include <editeng/editeng.hxx>
#define MAX_MRU_FONTNAME_ENTRIES 5
#define COMBO_WIDTH_IN_CHARS 18
// namespaces
using namespace ::editeng;
using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::frame;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::lang;
namespace
{
struct ScriptInfo
{
tools::
Long textWidth;
SvtScriptType scriptType;
sal_Int32 changePos;
ScriptInfo(SvtScriptType scrptType, sal_Int32 position)
: textWidth(0)
, scriptType(scrptType)
, changePos(position)
{
}
};
class SvxStyleBox_Base
{
public :
SvxStyleBox_Base(std::unique_ptr<weld::ComboBox> xWidget, OUString rCommand, SfxStyleF
amily eFamily,
const Reference<XFrame>& _xFrame, OUString aClearFormatKey,
OUString aMoreKey, bool bInSpecialMode, SvxStyleToolBoxControl& rCtrl);
virtual ~SvxStyleBox_Base()
{
}
void SetFamily( SfxStyleFamily eNewFamily );
void SetDefaultStyle( const OUString& rDefault ) { sDefaultStyle = rDefault; }
int get_count() const { return m_xWidget->get_count(); }
OUString get_text(int nIndex) const { return m_xWidget->get_text(nIndex); }
OUString get_active_text() const { return m_xWidget->get_active_text(); }
void append_text(const OUString& rStr)
{
OUString sId(OUString::number(m_xWidget->get_count()));
m_xWidget->append(sId, rStr);
}
void insert_separator(int pos, const OUString& rId)
{
m_xWidget->insert_separator(pos, rId);
}
void set_active_or_entry_text(const OUString& rText)
{
const int nFound = m_xWidget->find_text(rText);
if (nFound != -1)
m_xWidget->set_active(nFound);
else
m_xWidget->set_entry_text(rText);
}
int find_text(const OUString& rText)
{
return m_xWidget->find_text(rText);
}
void set_active(int nActive)
{
m_xWidget->set_active(nActive);
}
void freeze()
{
m_xWidget->freeze();
}
void save_value()
{
m_xWidget->save_value();
}
void clear()
{
m_xWidget->clear();
m_nMaxUserDrawFontWidth = 0;
}
void thaw()
{
m_xWidget->thaw();
}
virtual bool DoKeyInput(const KeyEvent& rKEvt);
private :
std::optional<SvxFont> m_oFont;
std::optional<SvxFont> m_oCJKFont;
std::optional<SvxFont> m_oCTLFont;
DECL_LINK(SelectHdl, weld::ComboBox&, void );
DECL_LINK(KeyInputHdl, const KeyEvent&, bool );
DECL_LINK(ActivateHdl, weld::ComboBox&, bool );
DECL_LINK(FocusOutHdl, weld::Widget&, void );
DECL_LINK(DumpAsPropertyTreeHdl, tools::JsonWriter&, void );
DECL_LINK(CustomRenderHdl, weld::ComboBox::render_args, void );
DECL_LINK(CustomGetSizeHdl, OutputDevice&, Size);
/// Calculate the optimal width of the dropdown. Very expensive operation, triggers lots of font measurement.
void CalcOptimalExtraUserWidth(vcl::RenderContext& rRenderContext);
void Select(bool bNonTravelSelect);
tools::Rectangle CalcBoundRect(vcl::RenderContext& rRenderContext, const OUString &rStyleName, std::vector<ScriptInfo>& rScriptChanges, double fRatio = 1);
protected :
SvxStyleToolBoxControl& m_rCtrl;
std::unique_ptr<weld::Builder> m_xMenuBuilder;
std::unique_ptr<weld::Menu> m_xMenu;
std::unique_ptr<weld::ComboBox> m_xWidget;
SfxStyleFamily eStyleFamily;
int m_nMaxUserDrawFontWidth;
int m_nLastItemWithMenu;
bool bRelease;
Reference< XFrame > m_xFrame;
OUString m_aCommand;
OUString aClearFormatKey;
OUString aMoreKey;
OUString sDefaultStyle;
bool bInSpecialMode;
void ReleaseFocus();
static Color TestColorsVisible(const Color &FontCol, const Color &BackCol);
void UserDrawEntry(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect, const tools::Rectangle& rTextRect, const OUString &rStyleName, const std::vector<ScriptInfo>& rScriptChanges);
void SetupEntry(vcl::RenderContext& rRenderContext, sal_Int32 nItem, const tools::Rectangle& rRect, std::u16string_view rStyleName, bool bIsNotSelected);
DECL_LINK(MenuSelectHdl, const OUString&, void );
DECL_STATIC_LINK(SvxStyleBox_Base, ShowMoreHdl, void *, void );
};
class SvxStyleBox_Impl final : public InterimItemWindow
, public SvxStyleBox_Base
{
public :
SvxStyleBox_Impl(vcl::Window* pParent, const OUString& rCommand, SfxStyleFamily eFamily,
const Reference< XFrame >& _xFrame,const OUString& rClearFormatKey, const OUString& rMoreKey, bool bInSpecialMode, SvxStyleToolBoxControl& rCtrl);
virtual ~SvxStyleBox_Impl() override
{
disposeOnce();
}
virtual void dispose() override
{
m_xWidget.reset();
m_xMenu.reset();
m_xMenuBuilder.reset();
InterimItemWindow::dispose();
}
virtual bool DoKeyInput(const KeyEvent& rKEvt) override;
private :
virtual void DataChanged(const DataChangedEvent& rDCEvt) override;
void SetOptimalSize();
};
class SvxFontNameBox_Impl;
class SvxFontNameBox_Base;
class SvxFontNameToolBoxControl final : public cppu::ImplInheritanceHelper<svt::ToolboxController,
css::lang::XServiceInfo>
{
public :
SvxFontNameToolBoxControl();
// XStatusListener
virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override;
// XToolbarController
virtual css::uno::Reference<css::awt::XWindow> SAL_CALL createItemWindow(const css::uno::Reference<css::awt::XWindow>& rParent) override;
// XComponent
virtual void SAL_CALL dispose() override;
// XServiceInfo
virtual OUString SAL_CALL getImplementationName() override;
virtual sal_Bool SAL_CALL supportsService( const OUString& rServiceName ) override;
virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
private :
VclPtr<SvxFontNameBox_Impl> m_xVclBox;
std::unique_ptr<SvxFontNameBox_Base> m_xWeldBox;
SvxFontNameBox_Base* m_pBox;
};
class FontOptionsListener final : public comphelper::ConfigurationListenerProperty<bool >
{
private :
SvxFontNameBox_Base& m_rBox;
virtual void setProperty(const css::uno::Any &rProperty) override;
public :
FontOptionsListener(const rtl::Reference<comphelper::ConfigurationListener>& rListener, const OUString& rProp, SvxFontNameBox_Base& rBox)
: comphelper::ConfigurationListenerProperty<bool >(rListener, rProp)
, m_rBox(rBox)
{
}
};
class SvxFontNameBox_Base
{
private :
rtl::Reference<comphelper::ConfigurationListener> m_xListener;
FontOptionsListener m_aWYSIWYG;
FontOptionsListener m_aHistory;
protected :
SvxFontNameToolBoxControl& m_rCtrl;
std::unique_ptr<FontNameBox> m_xWidget;
const FontList* pFontList;
::std::unique_ptr<FontList> m_aOwnFontList;
vcl::Font aCurFont;
sal_uInt16 nFtCount;
bool bRelease;
Reference< XFrame > m_xFrame;
bool mbCheckingUnknownFont;
bool mbDropDownActive;
void ReleaseFocus_Impl();
void Select(bool bNonTravelSelect);
void EndPreview()
{
Sequence< PropertyValue > aArgs;
const Reference<XDispatchProvider> xProvider(m_xFrame, UNO_QUERY);
SfxToolBoxControl::Dispatch(xProvider, u".uno:CharEndPreviewFontName" _ustr, aArgs);
}
bool CheckFontIsAvailable(std::u16string_view fontname);
void CheckAndMarkUnknownFont();
public :
SvxFontNameBox_Base(std::unique_ptr<weld::ComboBox> xWidget, const Reference<XFrame>& rFrame,
SvxFontNameToolBoxControl& rCtrl);
virtual ~SvxFontNameBox_Base()
{
m_xListener->dispose();
}
void FillList();
void Update( const css::awt::FontDescriptor* pFontDesc );
sal_uInt16 GetListCount() const { return nFtCount; }
void Clear() { m_xWidget->clear(); nFtCount = 0; }
void Fill( const FontList* pList )
{
m_xWidget->Fill(pList);
nFtCount = pList->GetFontNameCount();
}
void SetOwnFontList(::std::unique_ptr<FontList> && _aOwnFontList) { m_aOwnFontList = std::move(_aOwnFontList); }
virtual void set_sensitive(bool bSensitive)
{
m_xWidget->set_sensitive(bSensitive);
}
void set_active_or_entry_text(const OUString& rText);
void statusChanged_Impl(const css::frame::FeatureStateEvent& rEvent);
virtual bool DoKeyInput(const KeyEvent& rKEvt);
void EnableControls();
DECL_LINK(SelectHdl, weld::ComboBox&, void );
DECL_LINK(KeyInputHdl, const KeyEvent&, bool );
DECL_LINK(ActivateHdl, weld::ComboBox&, bool );
DECL_LINK(FocusInHdl, weld::Widget&, void );
DECL_LINK(FocusOutHdl, weld::Widget&, void );
DECL_LINK(PopupToggledHdl, weld::ComboBox&, void );
DECL_LINK(LivePreviewHdl, const FontMetric&, void );
DECL_LINK(DumpAsPropertyTreeHdl, tools::JsonWriter&, void );
};
void FontOptionsListener::setProperty(const css::uno::Any &rProperty)
{
comphelper::ConfigurationListenerProperty<bool >::setProperty(rProperty);
m_rBox.EnableControls();
}
class SvxFontNameBox_Impl final : public InterimItemWindow
, public SvxFontNameBox_Base
{
private :
virtual void DataChanged( const DataChangedEvent& rDCEvt ) override;
virtual void GetFocus() override
{
if (m_xWidget)
m_xWidget->grab_focus();
InterimItemWindow::GetFocus();
}
void SetOptimalSize();
virtual bool DoKeyInput(const KeyEvent& rKEvt) override;
public :
SvxFontNameBox_Impl(vcl::Window* pParent,
const Reference<XFrame>& rFrame, SvxFontNameToolBoxControl& rCtrl);
virtual void dispose() override
{
m_xWidget.reset();
InterimItemWindow::dispose();
}
virtual ~SvxFontNameBox_Impl() override
{
disposeOnce();
}
virtual Reference< css::accessibility::XAccessible > CreateAccessible() override;
virtual void StateChanged(StateChangedType nStateChange) override
{
if (nStateChange == StateChangedType::Enable)
m_xWidget->set_sensitive(IsEnabled());
InterimItemWindow::StateChanged(nStateChange);
}
virtual void set_sensitive(bool bSensitive) override
{
m_xWidget->set_sensitive(bSensitive);
if (bSensitive)
InterimItemWindow::Enable();
else
InterimItemWindow::Disable();
}
};
// SelectHdl needs the Modifiers, get them in MouseButtonUp
class SvxFrmValueSet_Impl final : public ValueSet
{
private :
sal_uInt16 nModifier;
virtual bool MouseButtonUp(const MouseEvent& rMEvt) override
{
nModifier = rMEvt.GetModifier();
return ValueSet::MouseButtonUp(rMEvt);
}
public :
SvxFrmValueSet_Impl()
: ValueSet(nullptr)
, nModifier(0)
{
}
sal_uInt16 GetModifier() const {return nModifier;}
};
}
namespace {
class SvxFrameToolBoxControl;
class SvxFrameWindow_Impl final : public WeldToolbarPopup
{
private :
rtl::Reference<SvxFrameToolBoxControl> mxControl;
std::unique_ptr<SvxFrmValueSet_Impl> mxFrameSet;
std::unique_ptr<weld::CustomWeld> mxFrameSetWin;
std::vector<std::pair<BitmapEx, OUString>> aImgVec;
bool bParagraphMode;
bool m_bIsWriter;
bool m_bIsCalc;
void InitImageList();
void CalcSizeValueSet();
DECL_LINK( SelectHdl, ValueSet*, void );
void SetDiagonalDownBorder(const SvxLineItem& dDownLineItem);
void SetDiagonalUpBorder(const SvxLineItem& dUpLineItem);
public :
SvxFrameWindow_Impl(SvxFrameToolBoxControl* pControl, weld::Widget* pParent);
virtual void GrabFocus() override
{
mxFrameSet->GrabFocus();
}
virtual void statusChanged( const css::frame::FeatureStateEvent& rEvent ) override;
};
class SvxFrameToolBoxControl : public svt::PopupWindowController
{
public :
explicit SvxFrameToolBoxControl( const css::uno::Reference< css::uno::XComponentContext >& rContext );
// XInitialization
virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& rArguments ) override;
// XServiceInfo
virtual OUString SAL_CALL getImplementationName() override;
virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
virtual void SAL_CALL execute(sal_Int16 nKeyModifier) override;
private :
virtual std::unique_ptr<WeldToolbarPopup> weldPopupWindow() override;
virtual VclPtr<vcl::Window> createVclPopupWindow( vcl::Window* pParent ) override;
};
class LineListBox final : public ValueSet
{
public :
typedef Color (*ColorFunc)(Color);
typedef Color (*ColorDistFunc)(Color, Color);
LineListBox();
/** Set the width in Twips */
Size SetWidth( tools::Long nWidth )
{
tools::Long nOldWidth = m_nWidth;
m_nWidth = nWidth;
return UpdateEntries( nOldWidth );
}
void SetNone( const OUString& sNone )
{
m_sNone = sNone;
}
/** Insert a listbox entry with all widths in Twips. */
void InsertEntry(const BorderWidthImpl& rWidthImpl,
SvxBorderLineStyle nStyle, tools::Long nMinWidth = 0,
ColorFunc pColor1Fn = &sameColor,
ColorFunc pColor2Fn = &sameColor,
ColorDistFunc pColorDistFn = &sameDistColor);
SvxBorderLineStyle GetEntryStyle( sal_Int32 nPos ) const ;
SvxBorderLineStyle GetSelectEntryStyle() const ;
void SetSourceUnit( FieldUnit eNewUnit ) { eSourceUnit = eNewUnit; }
const Color& GetColor() const { return aColor; }
virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override;
private :
void ImpGetLine(tools::Long nLine1, tools::Long nLine2, tools::Long nDistance,
Color nColor1, Color nColor2, Color nColorDist,
SvxBorderLineStyle nStyle, BitmapEx& rBmp);
void UpdatePaintLineColor(); // returns sal_True if maPaintCol has changed
Size UpdateEntries( tools::Long nOldWidth );
sal_Int32 GetStylePos( sal_Int32 nListPos, tools::Long nWidth );
const Color& GetPaintColor() const
{
return maPaintCol;
}
Color GetColorLine1( sal_Int32 nPos );
Color GetColorLine2( sal_Int32 nPos );
Color GetColorDist( sal_Int32 nPos );
LineListBox( const LineListBox& ) = delete ;
LineListBox& operator =( const LineListBox& ) = delete ;
std::vector<std::unique_ptr<ImpLineListData>> m_vLineList;
tools::Long m_nWidth;
OUString m_sNone;
ScopedVclPtr<VirtualDevice> aVirDev;
Size aTxtSize;
Color const aColor;
Color maPaintCol;
FieldUnit eSourceUnit;
};
SvxBorderLineStyle LineListBox::GetSelectEntryStyle() const
{
SvxBorderLineStyle nStyle = SvxBorderLineStyle::SOLID;
size_t nPos = GetSelectItemPos();
if (nPos != VALUESET_ITEM_NOTFOUND)
{
if (!m_sNone.isEmpty())
--nPos;
nStyle = GetEntryStyle( nPos );
}
return nStyle;
}
void LineListBox::ImpGetLine( tools::Long nLine1, tools::Long nLine2, tools::Long nDistance,
Color aColor1, Color aColor2, Color aColorDist,
SvxBorderLineStyle nStyle, BitmapEx& rBmp )
{
auto nMinWidth = GetDrawingArea()->get_ref_device().approximate_digit_width() * COMBO_WIDTH_IN_CHARS;
Size aSize(nMinWidth, aTxtSize.Height());
aSize.AdjustWidth( -(aTxtSize.Width()) );
aSize.AdjustWidth( -6 );
// 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 ) );
}
LineListBox::LineListBox()
: ValueSet(nullptr)
, m_nWidth( 5 )
, aVirDev(VclPtr<VirtualDevice>::Create())
, aColor(Application::GetSettings().GetStyleSettings().GetWindowTextColor())
, maPaintCol(COL_BLACK)
, eSourceUnit(FieldUnit::POINT)
{
aVirDev->SetLineColor();
aVirDev->SetMapMode( MapMode( MapUnit::MapTwip ) );
}
void LineListBox::SetDrawingArea(weld::DrawingArea* pDrawingArea)
{
ValueSet::SetDrawingArea(pDrawingArea);
OutputDevice& rDevice = pDrawingArea->get_ref_device();
aTxtSize.setWidth( rDevice.approximate_digit_width() );
aTxtSize.setHeight( rDevice.GetTextHeight() );
UpdatePaintLineColor();
}
sal_Int32 LineListBox::GetStylePos( sal_Int32 nListPos, tools::Long nWidth )
{
sal_Int32 nPos = -1;
if (!m_sNone.isEmpty())
nListPos--;
sal_Int32 n = 0;
size_t i = 0;
size_t nCount = m_vLineList.size();
while ( nPos == -1 && i < nCount )
{
auto & pData = m_vLineList[ i ];
if ( pData->GetMinWidth() <= nWidth )
{
if ( nListPos == n )
nPos = static_cast <sal_Int32>(i);
n++;
}
i++;
}
return nPos;
}
void LineListBox::InsertEntry(
const BorderWidthImpl& rWidthImpl, SvxBorderLineStyle nStyle, tools::Long nMinWidth,
ColorFunc pColor1Fn, ColorFunc pColor2Fn, ColorDistFunc pColorDistFn )
{
m_vLineList.emplace_back(new ImpLineListData(
rWidthImpl, nStyle, nMinWidth, pColor1Fn, pColor2Fn, pColorDistFn));
}
SvxBorderLineStyle LineListBox::GetEntryStyle( sal_Int32 nPos ) const
{
ImpLineListData* pData = (0 <= nPos && o3tl::make_unsigned(nPos) < m_vLineList.size()) ? m_vLineList[ nPos ].get() : nullptr;
return pData ? pData->GetStyle() : SvxBorderLineStyle::NONE;
}
void LineListBox::UpdatePaintLineColor()
{
const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
Color aNewCol( rSettings.GetWindowColor().IsDark()? rSettings.GetLabelTextColor() : aColor );
bool bRet = aNewCol != maPaintCol;
if ( bRet )
maPaintCol = aNewCol;
}
Size LineListBox::UpdateEntries( tools::Long nOldWidth )
{
Size aSize;
UpdatePaintLineColor( );
sal_Int32 nSelEntry = GetSelectItemPos();
sal_Int32 nTypePos = GetStylePos( nSelEntry, nOldWidth );
// Remove the old entries
Clear();
sal_uInt16 nId(1);
// Add the new entries based on the defined width
if (!m_sNone.isEmpty())
InsertItem(nId++, Image(), m_sNone);
sal_uInt16 n = 0;
sal_uInt16 nCount = m_vLineList.size( );
while ( n < nCount )
{
auto & pData = m_vLineList[ n ];
if ( pData->GetMinWidth() <= m_nWidth )
{
BitmapEx aBmp;
ImpGetLine( pData->GetLine1ForWidth( m_nWidth ),
pData->GetLine2ForWidth( m_nWidth ),
pData->GetDistForWidth( m_nWidth ),
GetColorLine1( GetItemCount( ) ),
GetColorLine2( GetItemCount( ) ),
GetColorDist( GetItemCount( ) ),
pData->GetStyle(), aBmp );
InsertItem(nId, Image(aBmp), SvtLineListBox::GetLineStyleName(pData->GetStyle()));
Size aBmpSize = aBmp.GetSizePixel();
if (aBmpSize.Width() > aSize.Width())
aSize.setWidth(aBmpSize.getWidth());
if (aBmpSize.Height() > aSize.Height())
aSize.setHeight(aBmpSize.getHeight());
if ( n == nTypePos )
SelectItem(nId);
}
else if ( n == nTypePos )
SetNoSelection();
n++;
++nId;
}
Invalidate();
return aSize;
}
Color LineListBox::GetColorLine1( sal_Int32 nPos )
{
sal_Int32 nStyle = GetStylePos( nPos, m_nWidth );
if (nStyle == -1)
return GetPaintColor( );
auto & pData = m_vLineList[ nStyle ];
return pData->GetColorLine1( GetColor( ) );
}
Color LineListBox::GetColorLine2( sal_Int32 nPos )
{
sal_Int32 nStyle = GetStylePos( nPos, m_nWidth );
if (nStyle == -1)
return GetPaintColor( );
auto & pData = m_vLineList[ nStyle ];
return pData->GetColorLine2( GetColor( ) );
}
Color LineListBox::GetColorDist( sal_Int32 nPos )
{
Color rResult = Application::GetSettings().GetStyleSettings().GetFieldColor();
sal_Int32 nStyle = GetStylePos( nPos, m_nWidth );
if (nStyle == -1)
return rResult;
auto & pData = m_vLineList[ nStyle ];
return pData->GetColorDist( GetColor( ), rResult );
}
}
namespace {
class SvxLineWindow_Impl final : public WeldToolbarPopup
{
private :
rtl::Reference<SvxFrameToolBoxControl> m_xControl;
std::unique_ptr<LineListBox> m_xLineStyleLb;
std::unique_ptr<weld::CustomWeld> m_xLineStyleLbWin;
bool m_bIsWriter;
DECL_LINK( SelectHdl, ValueSet*, void );
public :
SvxLineWindow_Impl(SvxFrameToolBoxControl* pControl, weld::Widget* pParent);
virtual void GrabFocus() override
{
m_xLineStyleLb->GrabFocus();
}
};
}
class SvxStyleToolBoxControl;
class SfxStyleControllerItem_Impl : public SfxStatusListener
{
public :
SfxStyleControllerItem_Impl( const Reference< XDispatchProvider >& rDispatchProvider,
sal_uInt16 nSlotId,
const OUString& rCommand,
SvxStyleToolBoxControl& rTbxCtl );
protected :
virtual void StateChangedAtStatusListener( SfxItemState eState, const SfxPoolItem* pState ) override;
private :
SvxStyleToolBoxControl& rControl;
};
#define BUTTON_PADDING 10
#define ITEM_HEIGHT 30
SvxStyleBox_Base::SvxStyleBox_Base(std::unique_ptr<weld::ComboBox> xWidget,
OUString aCommand,
SfxStyleFamily eFamily,
const Reference< XFrame >& _xFrame,
OUString _aClearFormatKey,
OUString _aMoreKey,
bool bInSpec, SvxStyleToolBoxControl& rCtrl)
: m_rCtrl(rCtrl)
, m_xMenuBuilder(Application::CreateBuilder(nullptr, u"svx/ui/stylemenu.ui" _ustr))
, m_xMenu(m_xMenuBuilder->weld_menu(u"menu" _ustr))
, m_xWidget(std::move(xWidget))
, eStyleFamily( eFamily )
, m_nMaxUserDrawFontWidth(0)
, m_nLastItemWithMenu(-1)
, bRelease( true )
, m_xFrame(_xFrame)
, m_aCommand(std::move( aCommand ))
, aClearFormatKey(std::move( _aClearFormatKey ))
, aMoreKey(std::move( _aMoreKey ))
, bInSpecialMode( bInSpec )
{
m_xWidget->connect_changed(LINK(this , SvxStyleBox_Base, SelectHdl));
m_xWidget->connect_key_press(LINK(this , SvxStyleBox_Base, KeyInputHdl));
m_xWidget->connect_entry_activate(LINK(this , SvxStyleBox_Base, ActivateHdl));
m_xWidget->connect_focus_out(LINK(this , SvxStyleBox_Base, FocusOutHdl));
m_xWidget->connect_get_property_tree(LINK(this , SvxStyleBox_Base, DumpAsPropertyTreeHdl));
m_xWidget->set_help_id(HID_STYLE_LISTBOX);
m_xWidget->set_entry_completion(true );
m_xMenu->connect_activate(LINK(this , SvxStyleBox_Base, MenuSelectHdl));
m_xWidget->connect_custom_get_size(LINK(this , SvxStyleBox_Base, CustomGetSizeHdl));
m_xWidget->connect_custom_render(LINK(this , SvxStyleBox_Base, CustomRenderHdl));
m_xWidget->set_custom_renderer(true );
m_xWidget->set_entry_width_chars(COMBO_WIDTH_IN_CHARS + 3);
}
IMPL_LINK(SvxStyleBox_Base, CustomGetSizeHdl, OutputDevice&, rArg, Size)
{
CalcOptimalExtraUserWidth(rArg);
if (comphelper::LibreOfficeKit::isActive())
return Size(m_nMaxUserDrawFontWidth * rArg.GetDPIX() / 96, ITEM_HEIGHT * rArg.GetDPIY() / 96);
return Size(m_nMaxUserDrawFontWidth, ITEM_HEIGHT);
}
SvxStyleBox_Impl::SvxStyleBox_Impl(vcl::Window* pParent,
const OUString& rCommand,
SfxStyleFamily eFamily,
const Reference< XFrame >& _xFrame,
const OUString& rClearFormatKey,
const OUString& rMoreKey,
bool bInSpec, SvxStyleToolBoxControl& rCtrl)
: InterimItemWindow(pParent, u"svx/ui/applystylebox.ui" _ustr, u"ApplyStyleBox" _ustr)
, SvxStyleBox_Base(m_xBuilder->weld_combo_box(u"applystyle" _ustr), rCommand, eFamily, _xFrame,
rClearFormatKey, rMoreKey, bInSpec, rCtrl)
{
InitControlBase(m_xWidget.get());
set_id(u"applystyle" _ustr);
SetOptimalSize();
}
void SvxStyleBox_Base::ReleaseFocus()
{
if ( !bRelease )
{
bRelease = true ;
return ;
}
if ( m_xFrame.is() && m_xFrame->getContainerWindow().is() )
m_xFrame->getContainerWindow()->setFocus();
}
IMPL_LINK(SvxStyleBox_Base, MenuSelectHdl, const OUString&, rMenuIdent, void )
{
if (m_nLastItemWithMenu < 0 || m_nLastItemWithMenu >= m_xWidget->get_count())
return ;
OUString sEntry = m_xWidget->get_text(m_nLastItemWithMenu);
ReleaseFocus(); // It must be after getting entry pos!
Sequence<PropertyValue> aArgs{ comphelper::makePropertyValue(u"Param" _ustr, sEntry),
comphelper::makePropertyValue(u"Family" _ustr,
sal_Int16( eStyleFamily )) };
const Reference<XDispatchProvider> xProvider(m_xFrame, UNO_QUERY);
if (rMenuIdent == "update" )
{
SfxToolBoxControl::Dispatch(xProvider, u".uno:StyleUpdateByExample" _ustr, aArgs);
}
else if (rMenuIdent == "edit" )
{
SfxToolBoxControl::Dispatch(xProvider, u".uno:EditStyle" _ustr, aArgs);
}
}
IMPL_STATIC_LINK_NOARG(SvxStyleBox_Base, ShowMoreHdl, void *, void )
{
SfxViewFrame* pViewFrm = SfxViewFrame::Current();
DBG_ASSERT( pViewFrm, "SvxStyleBox_Base::Select(): no viewframe" );
if (!pViewFrm)
return ;
pViewFrm->ShowChildWindow(SID_SIDEBAR);
::sfx2::sidebar::Sidebar::ShowPanel(u"StyleListPanel" , pViewFrm->GetFrame().GetFrameInterface(), true );
}
IMPL_LINK(SvxStyleBox_Base, SelectHdl, weld::ComboBox&, rCombo, void )
{
Select(rCombo.changed_by_direct_pick()); // only when picked from the list
}
IMPL_LINK_NOARG(SvxStyleBox_Base, ActivateHdl, weld::ComboBox&, bool )
{
Select(true );
return true ;
}
void SvxStyleBox_Base::Select(bool bNonTravelSelect)
{
if (!bNonTravelSelect)
return ;
OUString aSearchEntry(m_xWidget->get_active_text());
bool bDoIt = true , bClear = false ;
if ( bInSpecialMode )
{
if ( aSearchEntry == aClearFormatKey && m_xWidget->get_active() == 0 )
{
aSearchEntry = sDefaultStyle;
bClear = true ;
//not only apply default style but also call 'ClearFormatting'
Sequence< PropertyValue > aEmptyVals;
const Reference<XDispatchProvider> xProvider(m_xFrame, UNO_QUERY);
SfxToolBoxControl::Dispatch(xProvider, u".uno:ResetAttributes" _ustr, aEmptyVals);
}
else if (aSearchEntry == aMoreKey && m_xWidget->get_active() == (m_xWidget->get_count() - 1))
{
Application::PostUserEvent(LINK(nullptr, SvxStyleBox_Base, ShowMoreHdl));
//tdf#113214 change text back to previous entry
set_active_or_entry_text(m_xWidget->get_saved_value());
bDoIt = false ;
}
}
//Do we need to create a new style?
SfxObjectShell *pShell = SfxObjectShell::Current();
if (!pShell)
return ;
SfxStyleSheetBasePool* pPool = pShell->GetStyleSheetPool();
SfxStyleSheetBase* pStyle = nullptr;
bool bCreateNew = false ;
if ( pPool )
{
pStyle = pPool->First(eStyleFamily);
while ( pStyle && pStyle->GetName() != aSearchEntry )
pStyle = pPool->Next();
}
if ( !pStyle )
{
// cannot find the style for whatever reason
// therefore create a new style
bCreateNew = true ;
}
/* #i33380# DR 2004-09-03 Moved the following line above the Dispatch() call.
This instance may be deleted in the meantime (i.e. when a dialog is opened
while in Dispatch()), accessing members will crash in this case. */
ReleaseFocus();
if ( !bDoIt )
return ;
if ( bClear )
set_active_or_entry_text(aSearchEntry);
m_xWidget->save_value();
Sequence< PropertyValue > aArgs( 2 );
auto pArgs = aArgs.getArray();
pArgs[0].Value <<= aSearchEntry;
pArgs[1].Name = "Family" ;
pArgs[1].Value <<= sal_Int16( eStyleFamily );
const Reference<XDispatchProvider> xProvider(m_xFrame, UNO_QUERY);
if ( bCreateNew )
{
pArgs[0].Name = "Param" ;
SfxToolBoxControl::Dispatch(xProvider, u".uno:StyleNewByExample" _ustr, aArgs);
}
else
{
pArgs[0].Name = "Template" ;
SfxToolBoxControl::Dispatch(xProvider, m_aCommand, aArgs);
}
}
void SvxStyleBox_Base::SetFamily( SfxStyleFamily eNewFamily )
{
eStyleFamily = eNewFamily;
}
IMPL_LINK_NOARG(SvxStyleBox_Base, FocusOutHdl, weld::Widget&, void )
{
if (!m_xWidget->has_focus()) // a combobox can be comprised of different subwidget so double-check if none of those has focus
set_active_or_entry_text(m_xWidget->get_saved_value());
}
IMPL_LINK(SvxStyleBox_Base, KeyInputHdl, const KeyEvent&, rKEvt, bool )
{
return DoKeyInput(rKEvt);
}
bool SvxStyleBox_Base::DoKeyInput(const KeyEvent& rKEvt)
{
bool bHandled = false ;
sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
switch (nCode)
{
case KEY_TAB:
bRelease = false ;
Select(true );
break ;
case KEY_ESCAPE:
set_active_or_entry_text(m_xWidget->get_saved_value());
if (!m_rCtrl.IsInSidebar())
{
ReleaseFocus();
bHandled = true ;
}
break ;
}
return bHandled;
}
bool SvxStyleBox_Impl::DoKeyInput(const KeyEvent& rKEvt)
{
return SvxStyleBox_Base::DoKeyInput(rKEvt) || ChildKeyInput(rKEvt);
}
void SvxStyleBox_Impl::DataChanged( const DataChangedEvent& rDCEvt )
{
if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
(rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
{
SetOptimalSize();
}
InterimItemWindow::DataChanged( rDCEvt );
}
void SvxStyleBox_Impl::SetOptimalSize()
{
// set width in chars low so the size request will not be overridden
m_xWidget->set_entry_width_chars(1);
// tdf#132338 purely using this calculation to keep things their traditional width
Size aSize(LogicToPixel(Size((COMBO_WIDTH_IN_CHARS + 3) * 4, 0), MapMode(MapUnit::MapAppFont)));
m_xWidget->set_size_request(aSize.Width(), -1);
SetSizePixel(get_preferred_size());
}
namespace
{
std::vector<ScriptInfo> CheckScript(const OUString &rStyleName)
{
assert(!rStyleName.isEmpty()); // must have a preview text here!
std::vector<ScriptInfo> aScriptChanges;
auto aEditEngine = EditEngine(nullptr);
aEditEngine.SetText(rStyleName);
auto aScript = aEditEngine.GetScriptType({ 0, 0, 0, 0 });
for (sal_Int32 i = 1; i <= rStyleName.getLength(); i++)
{
auto aNextScript = aEditEngine.GetScriptType({ 0, i, 0, i });
if (aNextScript != aScript || i == rStyleName.getLength())
aScriptChanges.emplace_back(aScript, i);
aScript = aNextScript;
}
return aScriptChanges;
}
}
tools::Rectangle SvxStyleBox_Base::CalcBoundRect(vcl::RenderContext& rRenderContext, const OUString &rStyleName, std::vector<ScriptInfo>& rScriptChanges, double fRatio)
{
tools::Rectangle aTextRect;
SvtScriptType aScript;
sal_uInt16 nIdx = 0;
sal_Int32 nStart = 0;
sal_Int32 nEnd;
size_t nCnt = rScriptChanges.size();
if (nCnt)
{
nEnd = rScriptChanges[nIdx].changePos;
aScript = rScriptChanges[nIdx].scriptType;
}
else
{
nEnd = rStyleName.getLength();
aScript = SvtScriptType::LATIN;
}
do
{
auto oFont = (aScript == SvtScriptType::ASIAN) ?
m_oCJKFont :
((aScript == SvtScriptType::COMPLEX) ?
m_oCTLFont :
m_oFont);
rRenderContext.Push(vcl::PushFlags::FONT);
if (oFont)
rRenderContext.SetFont(*oFont);
if (fRatio != 1)
{
vcl::Font aFont(rRenderContext.GetFont());
Size aPixelSize(aFont.GetFontSize());
aPixelSize.setWidth(aPixelSize.Width() * fRatio);
aPixelSize.setHeight(aPixelSize.Height() * fRatio);
aFont.SetFontSize(aPixelSize);
rRenderContext.SetFont(aFont);
}
tools::Rectangle aRect;
rRenderContext.GetTextBoundRect(aRect, rStyleName, nStart, nStart, nEnd - nStart);
aTextRect = aTextRect.Union (aRect);
tools::Long nWidth = rRenderContext.GetTextWidth(rStyleName, nStart, nEnd - nStart);
rRenderContext.Pop();
if (nIdx >= rScriptChanges.size())
break ;
rScriptChanges[nIdx++].textWidth = nWidth;
if (nEnd < rStyleName.getLength() && nIdx < nCnt)
{
nStart = nEnd;
nEnd = rScriptChanges[nIdx].changePos;
aScript = rScriptChanges[nIdx].scriptType;
}
else
break ;
}
while (true );
return aTextRect;
}
void SvxStyleBox_Base::UserDrawEntry(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect, const tools::Rectangle& rTextRect, const OUString &rStyleName, const std::vector<ScriptInfo>& rScriptChanges)
{
// IMG_TXT_DISTANCE in ilstbox.hxx is 6, then 1 is added as
// nBorder, and we are adding 1 in order to look better when
// italics is present
const int nLeftDistance = 8;
Point aPos(rRect.TopLeft());
aPos.AdjustX(nLeftDistance );
double fRatio = 1;
if (rTextRect.Bottom() > rRect.GetHeight())
fRatio = static_cast <double >(rRect.GetHeight()) / rTextRect.Bottom();
else
aPos.AdjustY((rRect.GetHeight() - rTextRect.Bottom()) / 2);
SvtScriptType aScript;
sal_uInt16 nIdx = 0;
sal_Int32 nStart = 0;
sal_Int32 nEnd;
size_t nCnt = rScriptChanges.size();
if (nCnt)
{
nEnd = rScriptChanges[nIdx].changePos;
aScript = rScriptChanges[nIdx].scriptType;
}
else
{
nEnd = rStyleName.getLength();
aScript = SvtScriptType::LATIN;
}
do
{
auto oFont = (aScript == SvtScriptType::ASIAN) ?
m_oCJKFont :
((aScript == SvtScriptType::COMPLEX) ?
m_oCTLFont :
m_oFont);
rRenderContext.Push(vcl::PushFlags::FONT);
if (oFont)
rRenderContext.SetFont(*oFont);
if (fRatio != 1)
{
vcl::Font aFont(rRenderContext.GetFont());
Size aPixelSize(aFont.GetFontSize());
aPixelSize.setWidth(aPixelSize.Width() * fRatio);
aPixelSize.setHeight(aPixelSize.Height() * fRatio);
aFont.SetFontSize(aPixelSize);
rRenderContext.SetFont(aFont);
}
rRenderContext.DrawText(aPos, rStyleName, nStart, nEnd - nStart);
rRenderContext.Pop();
aPos.AdjustX(rScriptChanges[nIdx++].textWidth * fRatio);
if (nEnd < rStyleName.getLength() && nIdx < nCnt)
{
nStart = nEnd;
nEnd = rScriptChanges[nIdx].changePos;
aScript = rScriptChanges[nIdx].scriptType;
}
else
break ;
}
while (true );
}
static bool GetWhich(const SfxItemSet& rSet, sal_uInt16 nSlot, sal_uInt16& rWhich)
{
rWhich = rSet.GetPool()->GetWhichIDFromSlotID(nSlot);
return rSet.GetItemState(rWhich) >= SfxItemState::DEFAULT ;
}
static bool SetFont(const SfxItemSet& rSet, sal_uInt16 nSlot, SvxFont& rFont)
{
sal_uInt16 nWhich;
if (GetWhich(rSet, nSlot, nWhich))
{
const auto & rFontItem = static_cast <const SvxFontItem&>(rSet.Get(nWhich));
rFont.SetFamilyName(rFontItem.GetFamilyName());
rFont.SetStyleName(rFontItem.GetStyleName());
return true ;
}
return false ;
}
static bool SetFontSize(const vcl::RenderContext& rRenderContext, const SfxItemSet& rSet, sal_uInt16 nSlot, SvxFont& rFont)
{
sal_uInt16 nWhich;
if (GetWhich(rSet, nSlot, nWhich))
{
const auto & rFontHeightItem = static_cast <const SvxFontHeightItem&>(rSet.Get(nWhich));
if (SfxObjectShell *pShell = SfxObjectShell::Current())
{
Size aFontSize(0, rFontHeightItem.GetHeight());
Size aPixelSize(rRenderContext.LogicToPixel(aFontSize, MapMode(pShell->GetMapUnit())));
rFont.SetFontSize(aPixelSize);
return true ;
}
}
return false ;
}
static void SetFontStyle(const SfxItemSet& rSet, sal_uInt16 nPosture, sal_uInt16 nWeight, SvxFont& rFont)
{
sal_uInt16 nWhich;
if (GetWhich(rSet, nPosture, nWhich))
{
const auto & rItem = static_cast <const SvxPostureItem&>(rSet.Get(nWhich));
rFont.SetItalic(rItem.GetPosture());
}
if (GetWhich(rSet, nWeight, nWhich))
{
const auto & rItem = static_cast <const SvxWeightItem&>(rSet.Get(nWhich));
rFont.SetWeight(rItem.GetWeight());
}
}
void SvxStyleBox_Base::SetupEntry(vcl::RenderContext& rRenderContext, sal_Int32 nItem, const tools::Rectangle& rRect, std::u16string_view rStyleName, bool bIsNotSelected)
{
const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
if (!bIsNotSelected)
rRenderContext.SetTextColor(rStyleSettings.GetHighlightTextColor());
else
rRenderContext.SetTextColor(rStyleSettings.GetDialogTextColor());
// handle the push-button
if (!bIsNotSelected)
{
if (nItem == 0 || nItem == m_xWidget->get_count() - 1)
m_xWidget->set_item_menu(OUString::number(nItem), nullptr);
else
{
m_nLastItemWithMenu = nItem;
m_xWidget->set_item_menu(OUString::number(nItem), m_xMenu.get());
}
}
if (nItem <= 0 || nItem >= m_xWidget->get_count() - 1)
return ;
SfxObjectShell *pShell = SfxObjectShell::Current();
if (!pShell)
return ;
SfxStyleSheetBasePool* pPool = pShell->GetStyleSheetPool();
if (!pPool)
return ;
SfxStyleSheetBase* pStyle = pPool->First(eStyleFamily);
while (pStyle && pStyle->GetName() != rStyleName)
pStyle = pPool->Next();
if (!pStyle )
return ;
std::optional<SfxItemSet> const pItemSet(pStyle->GetItemSetForPreview());
if (!pItemSet) return ;
SvxFont aFont;
SvxFont aCJKFont;
SvxFont aCTLFont;
SetFontStyle(*pItemSet, SID_ATTR_CHAR_POSTURE, SID_ATTR_CHAR_WEIGHT, aFont);
SetFontStyle(*pItemSet, SID_ATTR_CHAR_CJK_POSTURE, SID_ATTR_CHAR_CJK_WEIGHT, aCJKFont);
SetFontStyle(*pItemSet, SID_ATTR_CHAR_CTL_POSTURE, SID_ATTR_CHAR_CTL_WEIGHT, aCTLFont);
const SfxPoolItem *pItem = pItemSet->GetItem( SID_ATTR_CHAR_CONTOUR );
if ( pItem )
{
auto aVal = static_cast < const SvxContourItem* >( pItem )->GetValue();
aFont.SetOutline(aVal);
aCJKFont.SetOutline(aVal);
aCTLFont.SetOutline(aVal);
}
pItem = pItemSet->GetItem( SID_ATTR_CHAR_SHADOWED );
if ( pItem )
{
auto aVal = static_cast < const SvxShadowedItem* >( pItem )->GetValue();
aFont.SetShadow(aVal);
aCJKFont.SetShadow(aVal);
aCTLFont.SetShadow(aVal);
}
pItem = pItemSet->GetItem( SID_ATTR_CHAR_RELIEF );
if ( pItem )
{
auto aVal = static_cast < const SvxCharReliefItem* >( pItem )->GetValue();
aFont.SetRelief(aVal);
aCJKFont.SetRelief(aVal);
aCTLFont.SetRelief(aVal);
}
pItem = pItemSet->GetItem( SID_ATTR_CHAR_UNDERLINE );
if ( pItem )
{
auto aVal = static_cast <const SvxUnderlineItem*>(pItem)->GetLineStyle();
aFont.SetUnderline(aVal);
aCJKFont.SetUnderline(aVal);
aCTLFont.SetUnderline(aVal);
}
pItem = pItemSet->GetItem( SID_ATTR_CHAR_OVERLINE );
if ( pItem )
{
auto aVal = static_cast < const SvxOverlineItem* >( pItem )->GetValue();
aFont.SetOverline(aVal);
aCJKFont.SetOverline(aVal);
aCTLFont.SetOverline(aVal);
}
pItem = pItemSet->GetItem( SID_ATTR_CHAR_STRIKEOUT );
if ( pItem )
{
auto aVal = static_cast < const SvxCrossedOutItem* >( pItem )->GetStrikeout();
aFont.SetStrikeout(aVal);
aCJKFont.SetStrikeout(aVal);
aCTLFont.SetStrikeout(aVal);
}
pItem = pItemSet->GetItem( SID_ATTR_CHAR_CASEMAP );
if ( pItem )
{
auto aVal = static_cast <const SvxCaseMapItem*>(pItem)->GetCaseMap();
aFont.SetCaseMap(aVal);
aCJKFont.SetCaseMap(aVal);
aCTLFont.SetCaseMap(aVal);
}
pItem = pItemSet->GetItem( SID_ATTR_CHAR_EMPHASISMARK );
if ( pItem )
{
auto aVal = static_cast < const SvxEmphasisMarkItem* >( pItem )->GetEmphasisMark();
aFont.SetEmphasisMark(aVal);
aCJKFont.SetEmphasisMark(aVal);
aCTLFont.SetEmphasisMark(aVal);
}
// setup the device & draw
Color aFontCol = COL_AUTO, aBackCol = COL_AUTO;
pItem = pItemSet->GetItem( SID_ATTR_CHAR_COLOR );
// text color, when nothing is selected
if ( (nullptr != pItem) && bIsNotSelected)
aFontCol = static_cast < const SvxColorItem* >( pItem )->GetValue();
drawing::FillStyle style = drawing::FillStyle_NONE;
// which kind of Fill style is selected
pItem = pItemSet->GetItem( XATTR_FILLSTYLE );
// only when ok and not selected
if ( (nullptr != pItem) && bIsNotSelected)
style = static_cast < const XFillStyleItem* >( pItem )->GetValue();
switch (style)
{
case drawing::FillStyle_SOLID:
{
// set background color
pItem = pItemSet->GetItem( XATTR_FILLCOLOR );
if ( nullptr != pItem )
aBackCol = static_cast < const XFillColorItem* >( pItem )->GetColorValue();
if ( aBackCol != COL_AUTO )
{
rRenderContext.SetFillColor(aBackCol);
rRenderContext.DrawRect(rRect);
}
}
break ;
default : break ;
//TODO Draw the other background styles: gradient, hatching and bitmap
}
// when the font and background color are too similar, adjust the Font-Color
if ( (aFontCol != COL_AUTO) || (aBackCol != COL_AUTO) )
aFontCol = TestColorsVisible(aFontCol, (aBackCol != COL_AUTO) ? aBackCol : rRenderContext.GetBackground().GetColor());
// set text color
if ( aFontCol != COL_AUTO )
rRenderContext.SetTextColor(aFontCol);
if (SetFont(*pItemSet, SID_ATTR_CHAR_FONT, aFont) &&
SetFontSize(rRenderContext, *pItemSet, SID_ATTR_CHAR_FONTHEIGHT, aFont))
m_oFont = aFont;
if (SetFont(*pItemSet, SID_ATTR_CHAR_CJK_FONT, aCJKFont) &&
SetFontSize(rRenderContext, *pItemSet, SID_ATTR_CHAR_CJK_FONTHEIGHT, aCJKFont))
m_oCJKFont = aCJKFont;
if (SetFont(*pItemSet, SID_ATTR_CHAR_CTL_FONT, aCTLFont) &&
SetFontSize(rRenderContext, *pItemSet, SID_ATTR_CHAR_CTL_FONTHEIGHT, aCTLFont))
m_oCTLFont = aCTLFont;
}
IMPL_LINK(SvxStyleBox_Base, 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();
OUString aStyleName(m_xWidget->get_text(nIndex));
rRenderContext.Push(vcl::PushFlags::FILLCOLOR | vcl::PushFlags::FONT | vcl::PushFlags::TEXTCOLOR);
SetupEntry(rRenderContext, nIndex, rRect, aStyleName, !bSelected);
auto aScriptChanges = CheckScript(aStyleName);
auto aTextRect = CalcBoundRect(rRenderContext, aStyleName, aScriptChanges);
UserDrawEntry(rRenderContext, rRect, aTextRect, aStyleName, aScriptChanges);
rRenderContext.Pop();
}
void SvxStyleBox_Base::CalcOptimalExtraUserWidth(vcl::RenderContext& rRenderContext)
{
if (m_nMaxUserDrawFontWidth)
return ;
tools::Long nMaxNormalFontWidth = 0;
sal_Int32 nEntryCount = m_xWidget->get_count();
for (sal_Int32 i = 0; i < nEntryCount; ++i)
{
OUString sStyleName(get_text(i));
tools::Rectangle aTextRectForDefaultFont;
rRenderContext.GetTextBoundRect(aTextRectForDefaultFont, sStyleName);
const tools::Long nWidth = aTextRectForDefaultFont.GetWidth();
nMaxNormalFontWidth = std::max(nWidth, nMaxNormalFontWidth);
}
m_nMaxUserDrawFontWidth = nMaxNormalFontWidth;
for (sal_Int32 i = 1; i < nEntryCount-1; ++i)
{
OUString sStyleName(get_text(i));
if (sStyleName.isEmpty())
continue ;
rRenderContext.Push(vcl::PushFlags::FILLCOLOR | vcl::PushFlags::FONT | vcl::PushFlags::TEXTCOLOR);
SetupEntry(rRenderContext, i, tools::Rectangle(0, 0, RECT_MAX, ITEM_HEIGHT), sStyleName, true );
auto aScriptChanges = CheckScript(sStyleName);
tools::Rectangle aTextRectForActualFont = CalcBoundRect(rRenderContext, sStyleName, aScriptChanges);
if (aTextRectForActualFont.Bottom() > ITEM_HEIGHT)
{
//Font didn't fit, re-calculate with adjustment ratio.
double fRatio = static_cast <double >(ITEM_HEIGHT) / aTextRectForActualFont.Bottom();
aTextRectForActualFont = CalcBoundRect(rRenderContext, sStyleName, aScriptChanges, fRatio);
}
rRenderContext.Pop();
const int nWidth = aTextRectForActualFont.GetWidth() + m_xWidget->get_menu_button_width() + BUTTON_PADDING;
m_nMaxUserDrawFontWidth = std::max(nWidth, m_nMaxUserDrawFontWidth);
}
}
// test is the color between Font- and background-color to be identify
// return is always the Font-Color
// when both light or dark, change the Contrast
// in other case do not change the origin color
// when the color is R=G=B=128 the DecreaseContrast make 128 the need an exception
Color SvxStyleBox_Base::TestColorsVisible(const Color &FontCol, const Color &BackCol)
{
constexpr sal_uInt8 ChgVal = 60; // increase/decrease the Contrast
Color retCol = FontCol;
if ((FontCol.IsDark() == BackCol.IsDark()) && (FontCol.IsBright() == BackCol.IsBright()))
{
sal_uInt8 lumi = retCol.GetLuminance();
if ((lumi > 120) && (lumi < 140))
retCol.DecreaseLuminance(ChgVal / 2);
else
retCol.DecreaseContrast(ChgVal);
}
return retCol;
}
IMPL_LINK(SvxStyleBox_Base, DumpAsPropertyTreeHdl, tools::JsonWriter&, rJsonWriter, void )
{
if (!m_xWidget)
return ;
{
auto entriesNode = rJsonWriter.startNode("entries" );
for (int i = 0, nEntryCount = m_xWidget->get_count(); i < nEntryCount; ++i)
{
auto entryNode = rJsonWriter.startNode("" );
rJsonWriter.put("" , m_xWidget->get_text(i));
}
}
int nActive = m_xWidget->get_active();
rJsonWriter.put("selectedCount" , static_cast <sal_Int32>(nActive == -1 ? 0 : 1));
{
auto selectedNode = rJsonWriter.startNode("selectedEntries" );
if (nActive != -1)
{
auto node = rJsonWriter.startNode("" );
rJsonWriter.put("" , static_cast <sal_Int32>(nActive));
}
}
rJsonWriter.put("command" , ".uno:StyleApply" );
}
static bool lcl_GetDocFontList(const FontList** ppFontList, SvxFontNameBox_Base& rBox)
{
bool bChanged = false ;
const SfxObjectShell* pDocSh = SfxObjectShell::Current();
const SvxFontListItem* pFontListItem = nullptr;
if ( pDocSh )
pFontListItem =
static_cast <const SvxFontListItem*>(pDocSh->GetItem( SID_ATTR_CHAR_FONTLIST ));
else
{
::std::unique_ptr<FontList> aFontList(new FontList(Application::GetDefaultDevice()));
*ppFontList = aFontList.get();
rBox.SetOwnFontList(std::move(aFontList));
bChanged = true ;
}
if ( pFontListItem )
{
const FontList* pNewFontList = pFontListItem->GetFontList();
DBG_ASSERT( pNewFontList, "Doc-FontList not available!" );
// No old list, but a new list
if ( !*ppFontList && pNewFontList )
{
// => take over
*ppFontList = pNewFontList;
bChanged = true ;
}
else
{
// Comparing the font lists is not perfect.
// When you change the font list in the Doc, you can track
// changes here only on the Listbox, because ppFontList
// has already been updated.
bChanged = !pNewFontList ||
*ppFontList != pNewFontList ||
rBox.GetListCount() != pNewFontList->GetFontNameCount();
// HACK: Comparing is incomplete
if ( bChanged )
*ppFontList = pNewFontList;
}
rBox.set_sensitive(true );
}
else if ( pDocSh || !ppFontList)
{
// Disable box only when we have a SfxObjectShell and didn't get a font list OR
// we don't have a SfxObjectShell and no current font list.
// It's possible that we currently have no SfxObjectShell, but a current font list.
// See #i58471: When a user set the focus into the font name combo box and opens
// the help window with F1. After closing the help window, we disable the font name
// combo box. The SfxObjectShell::Current() method returns in that case zero. But the
// font list hasn't changed and therefore the combo box shouldn't be disabled!
rBox.set_sensitive(false );
}
// Fill the FontBox, also the new list if necessary
if ( bChanged )
{
if (ppFontList && *ppFontList)
rBox.Fill( *ppFontList );
else
rBox.Clear();
}
return bChanged;
}
SvxFontNameBox_Base::SvxFontNameBox_Base(std::unique_ptr<weld::ComboBox> xWidget,
const Reference<XFrame>& rFrame,
SvxFontNameToolBoxControl& rCtrl)
: m_xListener(new comphelper::ConfigurationListener(u"/org.openoffice.Office.Common/Font/View" _ustr))
, m_aWYSIWYG(m_xListener, u"ShowFontBoxWYSIWYG" _ustr, *this )
, m_aHistory(m_xListener, u"History" _ustr, *this )
, m_rCtrl(rCtrl)
, m_xWidget(new FontNameBox(std::move(xWidget)))
, pFontList(nullptr)
, nFtCount(0)
, bRelease(true )
, m_xFrame(rFrame)
, mbCheckingUnknownFont(false )
, mbDropDownActive(false )
{
EnableControls();
m_xWidget->connect_changed(LINK(this , SvxFontNameBox_Base, SelectHdl));
m_xWidget->connect_key_press(LINK(this , SvxFontNameBox_Base, KeyInputHdl));
m_xWidget->connect_entry_activate(LINK(this , SvxFontNameBox_Base, ActivateHdl));
m_xWidget->connect_focus_in(LINK(this , SvxFontNameBox_Base, FocusInHdl));
m_xWidget->connect_focus_out(LINK(this , SvxFontNameBox_Base, FocusOutHdl));
m_xWidget->connect_popup_toggled(LINK(this , SvxFontNameBox_Base, PopupToggledHdl));
m_xWidget->connect_live_preview(LINK(this , SvxFontNameBox_Base, LivePreviewHdl));
m_xWidget->connect_get_property_tree(LINK(this , SvxFontNameBox_Base, DumpAsPropertyTreeHdl));
m_xWidget->set_entry_width_chars(COMBO_WIDTH_IN_CHARS + 5);
}
SvxFontNameBox_Impl::SvxFontNameBox_Impl(vcl::Window* pParent, const Reference<XFrame>& rFrame,
SvxFontNameToolBoxControl& rCtrl)
: InterimItemWindow(pParent, u"svx/ui/fontnamebox.ui" _ustr, u"FontNameBox" _ustr, true , reinterpret_cast <sal_uInt64>(SfxViewShell::Current()))
, SvxFontNameBox_Base(m_xBuilder->weld_combo_box(u"fontnamecombobox" _ustr), rFrame, rCtrl)
{
set_id(u"fontnamecombobox" _ustr);
SetOptimalSize();
}
void SvxFontNameBox_Base::FillList()
{
if (!m_xWidget) // e.g. disposed
return ;
// Save old Selection, set back in the end
int nStartPos, nEndPos;
m_xWidget->get_entry_selection_bounds(nStartPos, nEndPos);
// Did Doc-Fontlist change?
lcl_GetDocFontList(&pFontList, *this );
m_xWidget->select_entry_region(nStartPos, nEndPos);
}
bool SvxFontNameBox_Base::CheckFontIsAvailable(std::u16string_view fontname)
{
lcl_GetDocFontList(&pFontList, *this );
return pFontList && pFontList->IsAvailable(fontname);
}
void SvxFontNameBox_Base::CheckAndMarkUnknownFont()
{
if (mbCheckingUnknownFont) //tdf#117537 block rentry
return ;
mbCheckingUnknownFont = true ;
OUString fontname = m_xWidget->get_active_text();
// tdf#154680 If a font is set and that font is unknown, show it in italic.
vcl::Font font = m_xWidget->get_entry_font();
if (fontname.isEmpty() || CheckFontIsAvailable(fontname))
{
if ( font.GetItalicMaybeAskConfig() != ITALIC_NONE )
{
font.SetItalic( ITALIC_NONE );
m_xWidget->set_entry_font(font);
m_xWidget->set_tooltip_text(SvxResId(RID_SVXSTR_CHARFONTNAME));
}
}
else
{
if ( font.GetItalicMaybeAskConfig() != ITALIC_NORMAL )
{
font.SetItalic( ITALIC_NORMAL );
m_xWidget->set_entry_font(font);
m_xWidget->set_tooltip_text(SvxResId(RID_SVXSTR_CHARFONTNAME_NOTAVAILABLE));
}
}
mbCheckingUnknownFont = false ;
}
void SvxFontNameBox_Base::Update( const css::awt::FontDescriptor* pFontDesc )
{
if ( pFontDesc )
{
aCurFont.SetFamilyName ( pFontDesc->Name );
aCurFont.SetFamily ( FontFamily( pFontDesc->Family ) );
aCurFont.SetStyleName ( pFontDesc->StyleName );
aCurFont.SetPitch ( FontPitch( pFontDesc->Pitch ) );
aCurFont.SetCharSet ( rtl_TextEncoding( pFontDesc->CharSet ) );
}
OUString aCurName = aCurFont.GetFamilyName();
OUString aText = m_xWidget->get_active_text();
if (aText != aCurName)
set_active_or_entry_text(aCurName);
}
void SvxFontNameBox_Base::set_active_or_entry_text(const OUString& rText)
{
m_xWidget->set_active_or_entry_text(rText);
CheckAndMarkUnknownFont();
}
IMPL_LINK_NOARG(SvxFontNameBox_Base, FocusInHdl, weld::Widget&, void )
{
FillList();
}
IMPL_LINK(SvxFontNameBox_Base, KeyInputHdl, const KeyEvent&, rKEvt, bool )
{
return DoKeyInput(rKEvt);
}
bool SvxFontNameBox_Base::DoKeyInput(const KeyEvent& rKEvt)
{
bool bHandled = false ;
sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
switch (nCode)
{
case KEY_TAB:
bRelease = false ;
Select(true );
break ;
case KEY_ESCAPE:
set_active_or_entry_text(m_xWidget->get_saved_value());
if (!m_rCtrl.IsInSidebar())
{
ReleaseFocus_Impl();
bHandled = true ;
}
break ;
}
return bHandled;
}
bool SvxFontNameBox_Impl::DoKeyInput(const KeyEvent& rKEvt)
{
return SvxFontNameBox_Base::DoKeyInput(rKEvt) || ChildKeyInput(rKEvt);
}
IMPL_LINK_NOARG(SvxFontNameBox_Base, FocusOutHdl, weld::Widget&, void )
{
if (!m_xWidget->has_focus()) // a combobox can be comprised of different subwidget so double-check if none of those has focus
{
set_active_or_entry_text(m_xWidget->get_saved_value());
// send EndPreview
EndPreview();
}
}
IMPL_LINK(SvxFontNameBox_Base, LivePreviewHdl, const FontMetric&, rFontMetric, void )
{
Sequence<PropertyValue> aArgs(1);
SvxFontItem aFontItem(rFontMetric.GetFamilyType(),
rFontMetric.GetFamilyName(),
rFontMetric.GetStyleName(),
rFontMetric.GetPitch(),
rFontMetric.GetCharSet(),
SID_ATTR_CHAR_FONT);
PropertyValue* pArgs = aArgs.getArray();
aFontItem.QueryValue(pArgs[0].Value);
pArgs[0].Name = "CharPreviewFontName" ;
const Reference<XDispatchProvider> xProvider(m_xFrame, UNO_QUERY);
SfxToolBoxControl::Dispatch(xProvider, u".uno:CharPreviewFontName" _ustr, aArgs);
}
IMPL_LINK_NOARG(SvxFontNameBox_Base, PopupToggledHdl, weld::ComboBox&, void )
{
mbDropDownActive = !mbDropDownActive;
if (!mbDropDownActive)
EndPreview();
}
void SvxFontNameBox_Impl::SetOptimalSize()
{
// set width in chars low so the size request will not be overridden
m_xWidget->set_entry_width_chars(1);
// tdf#132338 purely using this calculation to keep things their traditional width
Size aSize(LogicToPixel(Size((COMBO_WIDTH_IN_CHARS +5) * 4, 0), MapMode(MapUnit::MapAppFont)));
m_xWidget->set_size_request(aSize.Width(), -1);
SetSizePixel(get_preferred_size());
}
void SvxFontNameBox_Impl::DataChanged( const DataChangedEvent& rDCEvt )
{
if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
(rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
{
SetOptimalSize();
}
else if ( ( rDCEvt.GetType() == DataChangedEventType::FONTS ) ||
( rDCEvt.GetType() == DataChangedEventType::DISPLAY ) )
{
// The old font list in shell has likely been destroyed at this point, so we need to get
// the new one before doing anything further.
lcl_GetDocFontList( &pFontList, *this );
}
}
void SvxFontNameBox_Base::ReleaseFocus_Impl()
{
if ( !bRelease )
{
bRelease = true ;
return ;
}
if ( m_xFrame.is() && m_xFrame->getContainerWindow().is() )
m_xFrame->getContainerWindow()->setFocus();
}
void SvxFontNameBox_Base::EnableControls()
{
bool bEnableMRU = m_aHistory.get();
sal_uInt16 nEntries = bEnableMRU ? MAX_MRU_FONTNAME_ENTRIES : 0;
bool bNewWYSIWYG = m_aWYSIWYG.get();
bool bOldWYSIWYG = m_xWidget->IsWYSIWYGEnabled();
if (m_xWidget->get_max_mru_count() != nEntries || bNewWYSIWYG != bOldWYSIWYG)
{
// refill in the next GetFocus-Handler
pFontList = nullptr;
Clear();
m_xWidget->set_max_mru_count(nEntries);
}
if (bNewWYSIWYG != bOldWYSIWYG)
m_xWidget->EnableWYSIWYG(bNewWYSIWYG);
}
IMPL_LINK(SvxFontNameBox_Base, SelectHdl, weld::ComboBox&, rCombo, void )
{
Select(rCombo.changed_by_direct_pick()); // only when picked from the list
}
IMPL_LINK_NOARG(SvxFontNameBox_Base, ActivateHdl, weld::ComboBox&, bool )
{
Select(true );
return true ;
}
void SvxFontNameBox_Base::Select(bool bNonTravelSelect)
{
Sequence< PropertyValue > aArgs( 1 );
auto pArgs = aArgs.getArray();
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5 C=95 H=97 G=95
¤ Dauer der Verarbeitung: 0.30 Sekunden
(vorverarbeitet)
¤
*© Formatika GbR, Deutschland