/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <config_features.h>
#include <svl/numformat.hxx>
#include <svl/zforlist.hxx>
#include <svl/currencytable.hxx>
#include <svtools/langhelp.hxx>
#include <unotools/lingucfg.hxx>
#if defined(_WIN32)
#include <unotools/resmgr.hxx>
#endif
#include <sfx2/bindings.hxx>
#include <sfx2/viewfrm.hxx>
#include <i18nlangtag/mslangid.hxx>
#include <i18nlangtag/languagetag.hxx>
#include <unotools/compatibility.hxx>
#include <svl/languageoptions.hxx>
#include <svl/cjkoptions.hxx>
#include <svl/ctloptions.hxx>
#include <svtools/miscopt.hxx>
#include <unotools/syslocaleoptions.hxx>
#include <sfx2/objsh.hxx>
#include <comphelper/propertysequence.hxx>
#include <comphelper/propertyvalue.hxx>
#include <svtools/langtab.hxx>
#include <editeng/unolingu.hxx>
#include <editeng/langitem.hxx>
#include <comphelper/processfactory.hxx>
#include <comphelper/string.hxx>
#include <rtl/ustrbuf.hxx>
#include <editeng/editids.hrc>
#include <svx/svxids.hrc>
#include <svl/intitem.hxx>
#include <svl/voiditem.hxx>
#include <GraphicsTestsDialog.hxx>
#include <unotools/searchopt.hxx>
#include <sal/log.hxx>
#include <officecfg/Office/Common.hxx>
#include <officecfg/Setup.hxx>
#include <comphelper/configuration.hxx>
#include <comphelper/diagnose_ex.hxx>
#if HAVE_FEATURE_BREAKPAD
#include <desktop/crashreport.hxx>
#endif

#include <com/sun/star/configuration/theDefaultProvider.hpp>
#include <com/sun/star/container/XNameAccess.hpp>
#include <com/sun/star/container/XNameReplace.hpp>
#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
#include <com/sun/star/beans/NamedValue.hpp>
#include <com/sun/star/beans/PropertyAttribute.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/util/XChangesBatch.hpp>
#include <com/sun/star/uno/Any.hxx>
#include <com/sun/star/container/XContentEnumerationAccess.hpp>
#include <com/sun/star/container/XSet.hpp>
#include <com/sun/star/i18n/ScriptType.hpp>
#include <com/sun/star/office/Quickstart.hpp>
#include <com/sun/star/linguistic2/XLinguProperties.hpp>
#include <comphelper/dispatchcommand.hxx>

#include <vcl/vclenum.hxx>
#include <vcl/svapp.hxx>
#include <vcl/settings.hxx>
#include <vcl/window.hxx>
#include <vcl/IconThemeInfo.hxx>
#include <vcl/skia/SkiaHelper.hxx>
#include <bitmaps.hlst>

#include "optgdlg.hxx"
#include <svtools/apearcfg.hxx>
#include <svtools/optionsdrawinglayer.hxx>
#include <svtools/restartdialog.hxx>
#include <svtools/imgdef.hxx>
#include <com/sun/star/datatransfer/clipboard/SystemClipboard.hpp>
#include <vcl/unohelp2.hxx>

#if defined(_WIN32)
#include <systools/win32/winstoreutil.hxx>
#include <vcl/fileregistration.hxx>
#endif
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::util;
using namespace ::utl;

// class OfaMiscTabPage --------------------------------------------------

DeactivateRC OfaMiscTabPage::DeactivatePage( SfxItemSet* pSet_ )
{
    if ( pSet_ )
        FillItemSet( pSet_ );
    return DeactivateRC::LeavePage;
}

namespace
{
const OUString & impl_SystemFileOpenServiceName()
{
#if defined(_WIN32)
    static constexpr OUString gPicker = u"com.sun.star.ui.dialogs.SystemFilePicker"_ustr;
    return gPicker;
#elif defined MACOSX
    static constexpr OUString gPicker = u"com.sun.star.ui.dialogs.AquaFilePicker"_ustr;
    return gPicker;
#else
    return EMPTY_OUSTRING;
#endif
}

bool lcl_HasSystemFilePicker()
{
    if( Application::hasNativeFileSelection() )
        return true;

    // Otherwise fall-back on querying services
    bool bRet = false;
    Reference< XMultiServiceFactory > xFactory = comphelper::getProcessServiceFactory();

    Reference< XContentEnumerationAccess > xEnumAccess( xFactory, UNO_QUERY );
    Reference< XSet > xSet( xFactory, UNO_QUERY );

    if ( ! xEnumAccess.is() || ! xSet.is() )
        return bRet;

    try
    {
        const OUString& aFileService = impl_SystemFileOpenServiceName();
        Reference< XEnumeration > xEnum = xEnumAccess->createContentEnumeration( aFileService );
        if ( xEnum.is() && xEnum->hasMoreElements() )
            bRet = true;
    }
    catch (const IllegalArgumentException&)
    {
    }
    catch (const ElementExistException&)
    {
    }
    return bRet;
}
}

OfaMiscTabPage::OfaMiscTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet)
    : SfxTabPage(pPage, pController, u"cui/ui/optgeneralpage.ui"_ustr, u"OptGeneralPage"_ustr, &rSet)
    , m_xExtHelpCB(m_xBuilder->weld_check_button(u"exthelp"_ustr))
    , m_xExtHelpImg(m_xBuilder->weld_widget(u"lockexthelp"_ustr))
    , m_xPopUpNoHelpCB(m_xBuilder->weld_check_button(u"popupnohelp"_ustr))
    , m_xPopUpNoHelpImg(m_xBuilder->weld_widget(u"lockpopupnohelp"_ustr))
    , m_xShowTipOfTheDay(m_xBuilder->weld_check_button(u"cbShowTipOfTheDay"_ustr))
    , m_xShowTipOfTheDayImg(m_xBuilder->weld_widget(u"lockcbShowTipOfTheDay"_ustr))
    , m_xFileDlgFrame(m_xBuilder->weld_widget(u"filedlgframe"_ustr))
    , m_xFileDlgROImage(m_xBuilder->weld_widget(u"lockimage"_ustr))
    , m_xFileDlgCB(m_xBuilder->weld_check_button(u"filedlg"_ustr))
    , m_xDocStatusCB(m_xBuilder->weld_check_button(u"docstatus"_ustr))
    , m_xDocStatusImg(m_xBuilder->weld_widget(u"lockdocstatus"_ustr))
    , m_xYearFrame(m_xBuilder->weld_widget(u"yearframe"_ustr))
    , m_xYearLabel(m_xBuilder->weld_label(u"yearslabel"_ustr))
    , m_xYearValueField(m_xBuilder->weld_spin_button(u"year"_ustr))
    , m_xToYearFT(m_xBuilder->weld_label(u"toyear"_ustr))
    , m_xYearFrameImg(m_xBuilder->weld_widget(u"lockyears"_ustr))
#if HAVE_FEATURE_BREAKPAD
    , m_xPrivacyFrame(m_xBuilder->weld_widget("privacyframe"))
    , m_xCrashReport(m_xBuilder->weld_check_button("crashreport"))
    , m_xCrashReportImg(m_xBuilder->weld_widget("lockcrashreport"))
#endif
#if defined(_WIN32)
    , m_xQuickStarterFrame(m_xBuilder->weld_widget("quickstarter"))
    , m_xQuickLaunchCB(m_xBuilder->weld_check_button("quicklaunch"))
    , m_xQuickLaunchImg(m_xBuilder->weld_widget("lockquicklaunch"))
    , m_xFileAssocFrame(m_xBuilder->weld_widget("fileassoc"))
    , m_xFileAssocBtn(m_xBuilder->weld_button("assocfiles"))
    , m_xPerformFileExtCheck(m_xBuilder->weld_check_button("cbPerformFileExtCheck"))
    , m_xPerformFileExtImg(m_xBuilder->weld_widget("lockcbPerformFileExtCheck"))
#endif
{
#if HAVE_FEATURE_BREAKPAD
    m_xPrivacyFrame->show();
#endif

#if defined(_WIN32)
    // Store-packaged apps (located under the protected Program Files\WindowsApps) can't use normal
    // shell shortcuts to their exe. TODO: show a button to open "Startup Apps" system applet?
    if (!sal::systools::IsStorePackagedApp())
        m_xQuickStarterFrame->show();

    m_xFileAssocFrame->show();
    m_xFileAssocBtn->connect_clicked(LINK(this, OfaMiscTabPage, FileAssocClick));
#endif

    m_aStrDateInfo = m_xToYearFT->get_label();
    m_xYearValueField->connect_value_changed( LINK( this, OfaMiscTabPage, TwoFigureHdl ) );

    SetExchangeSupport();
}

OfaMiscTabPage::~OfaMiscTabPage()
{
}

std::unique_ptr<SfxTabPage> OfaMiscTabPage::Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet )
{
    return std::make_unique<OfaMiscTabPage>( pPage, pController, *rAttrSet );
}

OUString OfaMiscTabPage::GetAllStrings()
{
    OUString sAllStrings;
    OUString labels[] = { u"label1"_ustr, u"label2"_ustr, u"label4"_ustr, u"label5"_ustr, u"yearslabel"_ustr,
                          u"toyear"_ustr, u"label8"_ustr, u"label9"_ustr };

    for (const auto& label : labels)
    {
        if (const auto pString = m_xBuilder->weld_label(label))
            sAllStrings += pString->get_label() + " ";
    }

    OUString checkButton[]
        = { u"exthelp"_ustr,   u"popupnohelp"_ustr, u"cbShowTipOfTheDay"_ustr, u"filedlg"_ustr,
            u"docstatus"_ustr, u"crashreport"_ustr, u"quicklaunch"_ustr,       u"cbPerformFileExtCheck"_ustr };

    for (const auto& check : checkButton)
    {
        if (const auto pString = m_xBuilder->weld_check_button(check))
            sAllStrings += pString->get_label() + " ";
    }

    if (const auto pString = m_xBuilder->weld_button(u"assocfiles"_ustr))
        sAllStrings += pString->get_label() + " ";

    return sAllStrings.replaceAll("_", "");
}

bool OfaMiscTabPage::FillItemSet( SfxItemSet* rSet )
{
    bool bModified = false;
    std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());

    if ( m_xPopUpNoHelpCB->get_state_changed_from_saved() )
        officecfg::Office::Common::Help::BuiltInHelpNotInstalledPopUp::set(m_xPopUpNoHelpCB->get_active(), batch);

    if ( m_xExtHelpCB->get_state_changed_from_saved() )
        officecfg::Office::Common::Help::ExtendedTip::set(m_xExtHelpCB->get_active(), batch);

    if ( m_xShowTipOfTheDay->get_state_changed_from_saved() )
    {
        officecfg::Office::Common::Misc::ShowTipOfTheDay::set(m_xShowTipOfTheDay->get_active(), batch);
        bModified = true;
    }

    if ( m_xFileDlgCB->get_state_changed_from_saved() )
    {
        officecfg::Office::Common::Misc::UseSystemFileDialog::set( !m_xFileDlgCB->get_active(), batch );
        bModified = true;
    }

    if (m_xDocStatusCB->get_state_changed_from_saved())
    {
        officecfg::Office::Common::Print::PrintingModifiesDocument::set(m_xDocStatusCB->get_active(), batch);
        bModified = true;
    }

    const SfxUInt16Item* pUInt16Item = GetOldItem( *rSet, SID_ATTR_YEAR2000 );
    sal_uInt16 nNum = static_cast<sal_uInt16>(m_xYearValueField->get_text().toInt32());
    if ( pUInt16Item && pUInt16Item->GetValue() != nNum )
    {
        bModified = true;
        rSet->Put( SfxUInt16Item( SID_ATTR_YEAR2000, nNum ) );
    }

#if HAVE_FEATURE_BREAKPAD
    if (m_xCrashReport->get_state_changed_from_saved())
    {
        officecfg::Office::Common::Misc::CrashReport::set(m_xCrashReport->get_active(), batch);
        bModified = true;
    }
#endif

#if defined(_WIN32)
    if (m_xPerformFileExtCheck->get_state_changed_from_saved())
    {
        officecfg::Office::Common::Misc::PerformFileExtCheck::set(
            m_xPerformFileExtCheck->get_active(), batch);
        bModified = true;
    }

    if( m_xQuickLaunchCB->get_state_changed_from_saved())
    {
        rSet->Put(SfxBoolItem(SID_ATTR_QUICKLAUNCHER, m_xQuickLaunchCB->get_active()));
        bModified = true;
    }
#endif

    batch->commit();

    return bModified;
}

void OfaMiscTabPage::Reset( const SfxItemSet* rSet )
{
    bool bEnable = !officecfg::Office::Common::Help::ExtendedTip::isReadOnly();
    m_xExtHelpCB->set_active( officecfg::Office::Common::Help::Tip::get() &&
            officecfg::Office::Common::Help::ExtendedTip::get() );
    m_xExtHelpCB->set_sensitive(bEnable);
    m_xExtHelpImg->set_visible(!bEnable);
    m_xExtHelpCB->save_state();

    bEnable = !officecfg::Office::Common::Help::BuiltInHelpNotInstalledPopUp::isReadOnly();
    m_xPopUpNoHelpCB->set_active( officecfg::Office::Common::Help::BuiltInHelpNotInstalledPopUp::get() );
    m_xPopUpNoHelpCB->set_sensitive(bEnable);
    m_xPopUpNoHelpImg->set_visible(!bEnable);
    m_xPopUpNoHelpCB->save_state();

    bEnable = !officecfg::Office::Common::Misc::ShowTipOfTheDay::isReadOnly();
    m_xShowTipOfTheDay->set_active( officecfg::Office::Common::Misc::ShowTipOfTheDay::get() );
    m_xShowTipOfTheDay->set_sensitive(bEnable);
    m_xShowTipOfTheDayImg->set_visible(!bEnable);
    m_xShowTipOfTheDay->save_state();

    if (!lcl_HasSystemFilePicker())
        m_xFileDlgFrame->hide();
    else
    {
        bEnable = !officecfg::Office::Common::Misc::UseSystemFileDialog::isReadOnly();
        m_xFileDlgCB->set_sensitive(bEnable);
        m_xFileDlgROImage->set_visible(!bEnable);
    }
    m_xFileDlgCB->set_active(!officecfg::Office::Common::Misc::UseSystemFileDialog::get());
    m_xFileDlgCB->save_state();

    bEnable = !officecfg::Office::Common::Print::PrintingModifiesDocument::isReadOnly();
    m_xDocStatusCB->set_active(officecfg::Office::Common::Print::PrintingModifiesDocument::get());
    m_xDocStatusCB->set_sensitive(bEnable);
    m_xDocStatusImg->set_visible(!bEnable);
    m_xDocStatusCB->save_state();

    bEnable = !officecfg::Office::Common::DateFormat::TwoDigitYear::isReadOnly();
    m_xYearLabel->set_sensitive(bEnable);
    m_xYearValueField->set_sensitive(bEnable);
    m_xToYearFT->set_sensitive(bEnable);
    m_xYearFrameImg->set_visible(!bEnable);

    if ( const SfxUInt16Item* pYearItem = rSet->GetItemIfSet( SID_ATTR_YEAR2000, false ) )
    {
        m_xYearValueField->set_value( pYearItem->GetValue() );
        TwoFigureHdl(*m_xYearValueField);
    }
    else
        m_xYearFrame->set_sensitive(false);

#if HAVE_FEATURE_BREAKPAD
    m_xCrashReport->set_active(officecfg::Office::Common::Misc::CrashReport::get() && CrashReporter::IsDumpEnable());
    m_xCrashReport->set_sensitive(!officecfg::Office::Common::Misc::CrashReport::isReadOnly() && CrashReporter::IsDumpEnable());
    m_xCrashReportImg->set_visible(officecfg::Office::Common::Misc::CrashReport::isReadOnly() && CrashReporter::IsDumpEnable());
    m_xCrashReport->save_state();
#endif

#if defined(_WIN32)
    const SfxPoolItem* pItem = nullptr;
    SfxItemState eState = rSet->GetItemState( SID_ATTR_QUICKLAUNCHER, false, &pItem );
    if ( SfxItemState::SET == eState )
        m_xQuickLaunchCB->set_active( static_cast<const SfxBoolItem*>(pItem)->GetValue() );
    else if ( SfxItemState::DISABLED == eState )
    {
        // quickstart not installed
        m_xQuickStarterFrame->hide();
    }

    m_xQuickLaunchCB->save_state();

    m_xPerformFileExtCheck->set_active(
        officecfg::Office::Common::Misc::PerformFileExtCheck::get());
    m_xPerformFileExtCheck->save_state();
    m_xPerformFileExtCheck->set_sensitive(!officecfg::Office::Common::Misc::PerformFileExtCheck::isReadOnly());
    m_xPerformFileExtImg->set_visible(officecfg::Office::Common::Misc::PerformFileExtCheck::isReadOnly());
#endif
}

IMPL_LINK_NOARG( OfaMiscTabPage, TwoFigureHdl, weld::SpinButton&, void )
{
    OUString aOutput( m_aStrDateInfo );
    OUString aStr( m_xYearValueField->get_text() );
    sal_Int32 nNum = aStr.toInt32();
    if ( aStr.getLength() != 4 || nNum < m_xYearValueField->get_min() || nNum > m_xYearValueField->get_max() )
        aOutput += "????";
    else
    {
        nNum += 99;
        aOutput += OUString::number( nNum );
    }
    m_xToYearFT->set_label( aOutput );
}

#if defined(_WIN32)
IMPL_STATIC_LINK_NOARG(OfaMiscTabPage, FileAssocClick, weld::Button&, void)
{
    vcl::fileregistration::LaunchRegistrationUI();
}
#endif

class CanvasSettings
{
public:
    CanvasSettings();

    bool    IsHardwareAccelerationEnabled() const;
    bool    IsHardwareAccelerationAvailable() const;
    bool    IsHardwareAccelerationRO() const;
    void    EnabledHardwareAcceleration( bool _bEnabled ) const;

private:
    typedef std::vector< std::pair<OUString,Sequence<OUString> > > ServiceVector;

    Reference<XNameAccess> mxForceFlagNameAccess;
    ServiceVector          maAvailableImplementations;
    mutable bool           mbHWAccelAvailable;
    mutable bool           mbHWAccelChecked;
};

CanvasSettings::CanvasSettings() :
    mbHWAccelAvailable(false),
    mbHWAccelChecked(false)
{
    try
    {
        Reference<XMultiServiceFactory> xConfigProvider(
            css::configuration::theDefaultProvider::get(
                comphelper::getProcessComponentContext()));

        Sequence<Any> aArgs1(comphelper::InitAnyPropertySequence(
        {
            {"nodepath", Any(u"/org.openoffice.Office.Canvas"_ustr)}
        }));
        mxForceFlagNameAccess.set(
            xConfigProvider->createInstanceWithArguments(
                u"com.sun.star.configuration.ConfigurationUpdateAccess"_ustr,
                aArgs1 ),
            UNO_QUERY_THROW );

        Sequence<Any> aArgs2(comphelper::InitAnyPropertySequence(
        {
            {"nodepath", Any(u"/org.openoffice.Office.Canvas/CanvasServiceList"_ustr)}
        }));
        Reference<XNameAccess> xNameAccess(
            xConfigProvider->createInstanceWithArguments(
                u"com.sun.star.configuration.ConfigurationAccess"_ustr,
                aArgs2 ), UNO_QUERY_THROW );
        Reference<XHierarchicalNameAccess> xHierarchicalNameAccess(
            xNameAccess, UNO_QUERY_THROW);

        for (auto& serviceName : xNameAccess->getElementNames())
        {
            Reference<XNameAccess> xEntryNameAccess(
                xHierarchicalNameAccess->getByHierarchicalName(serviceName),
                UNO_QUERY );

            if( xEntryNameAccess.is() )
            {
                Sequence<OUString> preferredImplementations;
                if( xEntryNameAccess->getByName(u"PreferredImplementations"_ustr) >>= preferredImplementations )
                    maAvailableImplementations.emplace_back(serviceName, preferredImplementations);
            }
        }
    }
    catch (const Exception&)
    {
    }
}

bool CanvasSettings::IsHardwareAccelerationAvailable() const
{
    if( !mbHWAccelChecked )
    {
        mbHWAccelChecked = true;

        Reference< XMultiServiceFactory > xFactory = comphelper::getProcessServiceFactory();

        // check whether any of the service lists has an
        // implementation that presents the "HardwareAcceleration" property
        for (auto const& availableImpl : maAvailableImplementations)
        {
            for (auto& currImpl : availableImpl.second)
            {
                try
                {
                    Reference<XPropertySet> xPropSet( xFactory->createInstance(
                                                          currImpl.trim() ),
                                                      UNO_QUERY_THROW );
                    bool bHasAccel(false);
                    if( xPropSet->getPropertyValue(u"HardwareAcceleration"_ustr) >>= bHasAccel )
                        if( bHasAccel )
                        {
                            mbHWAccelAvailable = true;
                            return mbHWAccelAvailable;
                        }
                }
                catch (const Exception&)
                {
                }
            }
        }
    }

    return mbHWAccelAvailable;
}

bool CanvasSettings::IsHardwareAccelerationEnabled() const
{
    bool bForceLastEntry(false);
    if( !mxForceFlagNameAccess.is() )
        return true;

    if( !(mxForceFlagNameAccess->getByName(u"ForceSafeServiceImpl"_ustr) >>= bForceLastEntry) )
        return true;

    return !bForceLastEntry;
}

bool CanvasSettings::IsHardwareAccelerationRO() const
{
    Reference< XPropertySet > xSet(mxForceFlagNameAccess, UNO_QUERY);
    if (!xSet.is())
        return true;

    Reference< XPropertySetInfo > xInfo = xSet->getPropertySetInfo();
    Property aProp = xInfo->getPropertyByName(u"ForceSafeServiceImpl"_ustr);
    return ((aProp.Attributes & css::beans::PropertyAttribute::READONLY ) == css::beans::PropertyAttribute::READONLY);
}

void CanvasSettings::EnabledHardwareAcceleration( bool _bEnabled ) const
{
    Reference< XNameReplace > xNameReplace(
        mxForceFlagNameAccess, UNO_QUERY );

    if( !xNameReplace.is() )
        return;

    xNameReplace->replaceByName( u"ForceSafeServiceImpl"_ustr, Any(!_bEnabled) );

    Reference< XChangesBatch > xChangesBatch(
        mxForceFlagNameAccess, UNO_QUERY );

    if( !xChangesBatch.is() )
        return;

    xChangesBatch->commitChanges();
}

// class OfaViewTabPage --------------------------------------------------

static bool DisplayNameCompareLessThan(const vcl::IconThemeInfo& rInfo1, const vcl::IconThemeInfo& rInfo2)
{
    return rInfo1.GetDisplayName().compareTo(rInfo2.GetDisplayName()) < 0;
}

OfaViewTabPage::OfaViewTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet)
    : SfxTabPage(pPage, pController, u"cui/ui/optviewpage.ui"_ustr, u"OptViewPage"_ustr, &rSet)
    , nSizeLB_InitialSelection(0)
    , nSidebarSizeLB_InitialSelection(0)
    , nNotebookbarSizeLB_InitialSelection(0)
    , nStyleLB_InitialSelection(0)
    , pCanvasSettings(new CanvasSettings)
    , m_xIconSizeLabel(m_xBuilder->weld_label(u"label14"_ustr))
    , m_xIconSizeLB(m_xBuilder->weld_combo_box(u"iconsize"_ustr))
    , m_xIconSizeImg(m_xBuilder->weld_widget(u"lockiconsize"_ustr))
    , m_xSidebarIconSizeLabel(m_xBuilder->weld_label(u"label9"_ustr))
    , m_xSidebarIconSizeLB(m_xBuilder->weld_combo_box(u"sidebariconsize"_ustr))
    , m_xSidebarIconSizeImg(m_xBuilder->weld_widget(u"locksidebariconsize"_ustr))
    , m_xNotebookbarIconSizeLabel(m_xBuilder->weld_label(u"label8"_ustr))
    , m_xNotebookbarIconSizeLB(m_xBuilder->weld_combo_box(u"notebookbariconsize"_ustr))
    , m_xNotebookbarIconSizeImg(m_xBuilder->weld_widget(u"locknotebookbariconsize"_ustr))
    , m_xIconStyleLabel(m_xBuilder->weld_label(u"label6"_ustr))
    , m_xIconStyleLB(m_xBuilder->weld_combo_box(u"iconstyle"_ustr))
    , m_xIconStyleImg(m_xBuilder->weld_widget(u"lockiconstyle"_ustr))
    , m_xFontAntiAliasing(m_xBuilder->weld_check_button(u"aafont"_ustr))
    , m_xFontAntiAliasingImg(m_xBuilder->weld_widget(u"lockaafont"_ustr))
    , m_xAAPointLimitLabel(m_xBuilder->weld_label(u"aafrom"_ustr))
    , m_xAAPointLimitLabelImg(m_xBuilder->weld_widget(u"lockaafrom"_ustr))
    , m_xAAPointLimit(m_xBuilder->weld_metric_spin_button(u"aanf"_ustr, FieldUnit::PIXEL))
    , m_xFontShowCB(m_xBuilder->weld_check_button(u"showfontpreview"_ustr))
    , m_xFontShowImg(m_xBuilder->weld_widget(u"lockshowfontpreview"_ustr))
    , m_xUseHardwareAccell(m_xBuilder->weld_check_button(u"useaccel"_ustr))
    , m_xUseHardwareAccellImg(m_xBuilder->weld_widget(u"lockuseaccel"_ustr))
    , m_xUseAntiAliase(m_xBuilder->weld_check_button(u"useaa"_ustr))
    , m_xUseAntiAliaseImg(m_xBuilder->weld_widget(u"lockuseaa"_ustr))
    , m_xUseSkia(m_xBuilder->weld_check_button(u"useskia"_ustr))
    , m_xUseSkiaImg(m_xBuilder->weld_widget(u"lockuseskia"_ustr))
    , m_xForceSkiaRaster(m_xBuilder->weld_check_button(u"forceskiaraster"_ustr))
    , m_xForceSkiaRasterImg(m_xBuilder->weld_widget(u"lockforceskiaraster"_ustr))
    , m_xSkiaStatusEnabled(m_xBuilder->weld_label(u"skiaenabled"_ustr))
    , m_xSkiaStatusDisabled(m_xBuilder->weld_label(u"skiadisabled"_ustr))
    , m_xSkiaLog(m_xBuilder->weld_button(u"btnSkialog"_ustr))
    , m_xMouseMiddleLabel(m_xBuilder->weld_label(u"label12"_ustr))
    , m_xMouseMiddleLB(m_xBuilder->weld_combo_box(u"mousemiddle"_ustr))
    , m_xMouseMiddleImg(m_xBuilder->weld_widget(u"lockmousemiddle"_ustr))
    , m_xMoreIcons(m_xBuilder->weld_button(u"btnMoreIcons"_ustr))
    , m_xRunGPTests(m_xBuilder->weld_button(u"btn_rungptest"_ustr))
    , m_sAutoStr(m_xIconStyleLB->get_text(0))
{
    m_xFontAntiAliasing->connect_toggled( LINK( this, OfaViewTabPage, OnAntialiasingToggled ) );

    m_xUseSkia->connect_toggled(LINK(this, OfaViewTabPage, OnUseSkiaToggled));
    m_xSkiaLog->connect_clicked(LINK(this, OfaViewTabPage, OnCopySkiaLog));

    UpdateIconThemes();

    m_xIconStyleLB->set_active(0);

    m_xMoreIcons->connect_clicked(LINK(this, OfaViewTabPage, OnMoreIconsClick));
    m_xRunGPTests->connect_clicked(LINK(this, OfaViewTabPage, OnRunGPTestClick));

    // Hide "Run Graphics Test" button if Experimental Mode is off
    if (!officecfg::Office::Common::Misc::ExperimentalMode::get())
        m_xRunGPTests->hide();
}

void OfaViewTabPage::UpdateIconThemes()
{
    // Set known icon themes
    m_xIconStyleLB->clear();
    StyleSettings aStyleSettings = Application::GetSettings().GetStyleSettings();
    mInstalledIconThemes = aStyleSettings.GetInstalledIconThemes();
    std::sort(mInstalledIconThemes.begin(), mInstalledIconThemes.end(), DisplayNameCompareLessThan);

    // Start with the automatically chosen icon theme
    OUString autoThemeId = aStyleSettings.GetAutomaticallyChosenIconTheme();
    const vcl::IconThemeInfo& autoIconTheme = vcl::IconThemeInfo::FindIconThemeById(mInstalledIconThemes, autoThemeId);

    OUString entryForAuto = m_sAutoStr + " (" + autoIconTheme.GetDisplayName() + ")";
    m_xIconStyleLB->append(u"auto"_ustr, entryForAuto); // index 0 means choose style automatically

    // separate auto and other icon themes
    m_xIconStyleLB->append_separator(u""_ustr);

    for (auto const& installIconTheme : mInstalledIconThemes)
        m_xIconStyleLB->append(installIconTheme.GetThemeId(), installIconTheme.GetDisplayName());
}

OfaViewTabPage::~OfaViewTabPage()
{
}

IMPL_LINK_NOARG(OfaViewTabPage, OnRunGPTestClick, weld::Button&, void)
{
    GraphicsTestsDialog m_xGraphicsTestDialog(m_xContainer.get());
    m_xGraphicsTestDialog.run();
}

IMPL_STATIC_LINK_NOARG(OfaViewTabPage, OnMoreIconsClick, weld::Button&, void)
{
    css::uno::Sequence<css::beans::PropertyValue> aArgs{ comphelper::makePropertyValue(
        u"AdditionsTag"_ustr, u"Icons"_ustr) };
    comphelper::dispatchCommand(u".uno:AdditionsDialog"_ustr, aArgs);
}

IMPL_LINK_NOARG( OfaViewTabPage, OnAntialiasingToggled, weld::Toggleable&, void )
{
    bool bAAEnabled = m_xFontAntiAliasing->get_active() && !officecfg::Office::Common::View::FontAntiAliasing::MinPixelHeight::isReadOnly();

    m_xAAPointLimitLabel->set_sensitive(bAAEnabled);
    m_xAAPointLimit->set_sensitive(bAAEnabled);
}

IMPL_LINK_NOARG(OfaViewTabPage, OnUseSkiaToggled, weld::Toggleable&, void)
{
    UpdateSkiaStatus();
}

IMPL_LINK_NOARG(OfaViewTabPage, OnCopySkiaLog, weld::Button&, void)
{
#if HAVE_FEATURE_SKIA
    css::uno::Reference<css::datatransfer::clipboard::XClipboard> xClipboard =
        css::datatransfer::clipboard::SystemClipboard::create(
            comphelper::getProcessComponentContext());
    OUString sInfo = SkiaHelper::readLog();
    vcl::unohelper::TextDataObject::CopyStringTo(sInfo, xClipboard);
    m_xSkiaLog->set_from_icon_name(RID_SVXBMP_COPY);
#endif
}

void OfaViewTabPage::HideSkiaWidgets()
{
    m_xUseSkia->hide();
    m_xForceSkiaRaster->hide();
    m_xSkiaStatusEnabled->hide();
    m_xSkiaStatusDisabled->hide();
    m_xSkiaLog->hide();
}

void OfaViewTabPage::UpdateSkiaStatus()
{
#if HAVE_FEATURE_SKIA
    bool skiaHidden = true;

    // For now Skia is used mainly on Windows, enable the controls there.
    if (Application::GetToolkitName() == "win")
        skiaHidden = false;
    // It can also be used on Linux, but only with the rarely used 'gen' backend.
    if (Application::GetToolkitName() == "x11")
        skiaHidden = false;
    // OSX backend has Skia support too.
    if (Application::GetToolkitName() == "osx")
        skiaHidden = false;

    if (skiaHidden)
    {
        HideSkiaWidgets();
        return;
    }

    // Easier than a custom translation string.
    bool bEnabled = SkiaHelper::isVCLSkiaEnabled();
    m_xSkiaStatusEnabled->set_visible(bEnabled);
    m_xSkiaStatusDisabled->set_visible(!bEnabled);

    m_xUseSkia->set_sensitive(!officecfg::Office::Common::VCL::UseSkia::isReadOnly());
    m_xUseSkiaImg->set_visible(officecfg::Office::Common::VCL::UseSkia::isReadOnly());
    m_xForceSkiaRaster->set_sensitive(m_xUseSkia->get_active() && !officecfg::Office::Common::VCL::ForceSkiaRaster::isReadOnly());
    m_xForceSkiaRasterImg->set_visible(officecfg::Office::Common::VCL::ForceSkiaRaster::isReadOnly());
    m_xSkiaLog->set_sensitive(bEnabled);

    // Technically the 'use hardware acceleration' option could be used to mean !forceSkiaRaster, but the implementation
    // of the option is so tied to the implementation of the canvas module that it's simpler to ignore it.
    UpdateHardwareAccelStatus();
#else
    HideSkiaWidgets();
#endif
}

std::unique_ptr<SfxTabPage> OfaViewTabPage::Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet )
{
    return std::make_unique<OfaViewTabPage>(pPage, pController, *rAttrSet);
}

OUString OfaViewTabPage::GetAllStrings()
{
    OUString sAllStrings;
    OUString labels[] = { u"label16"_ustr, u"label1"_ustr,      u"label6"_ustr,       u"label15"_ustr,
                          u"label14"_ustr, u"label8"_ustr,      u"label9"_ustr,       u"label4"_ustr, u"label12"_ustr,
                          u"label2"_ustr,  u"skiaenabled"_ustr, u"skiadisabled"_ustr, u"label5"_ustr, u"aafrom"_ustr };

    for (const auto& label : labels)
    {
        if (const auto pString = m_xBuilder->weld_label(label))
            sAllStrings += pString->get_label() + " ";
    }

    OUString checkButton[]
        = { u"useaccel"_ustr, u"useaa"_ustr, u"useskia"_ustr, u"forceskiaraster"_ustr, u"showfontpreview"_ustr, u"aafont"_ustr };

    for (const auto& check : checkButton)
    {
        if (const auto pString = m_xBuilder->weld_check_button(check))
            sAllStrings += pString->get_label() + " ";
    }

    sAllStrings += m_xSkiaLog->get_label() + " " + m_xRunGPTests->get_label() + " ";

    return sAllStrings.replaceAll("_", "");
}

bool OfaViewTabPage::FillItemSet( SfxItemSet* )
{
    bool bModified = false;
    bool bRepaintWindows(false);
    std::shared_ptr<comphelper::ConfigurationChanges> xChanges(comphelper::ConfigurationChanges::create());

    SvtMiscOptions aMiscOptions;
    const sal_Int32 nSizeLB_NewSelection = m_xIconSizeLB->get_active();
    if( nSizeLB_InitialSelection != nSizeLB_NewSelection )
    {
        // from now on it's modified, even if via auto setting the same size was set as now selected in the LB
        sal_Int16 eSet = SFX_SYMBOLS_SIZE_AUTO;
        switch( nSizeLB_NewSelection )
        {
            case 0: eSet = SFX_SYMBOLS_SIZE_AUTO;  break;
            case 1: eSet = SFX_SYMBOLS_SIZE_SMALL; break;
            case 2: eSet = SFX_SYMBOLS_SIZE_LARGE; break;
            case 3: eSet = SFX_SYMBOLS_SIZE_32; break;
            default:
                SAL_WARN("cui.options", "OfaViewTabPage::FillItemSet(): This state of m_xIconSizeLB should not be possible!");
        }
        aMiscOptions.SetSymbolsSize( eSet );
    }

    const sal_Int32 nSidebarSizeLB_NewSelection = m_xSidebarIconSizeLB->get_active();
    if( nSidebarSizeLB_InitialSelection != nSidebarSizeLB_NewSelection )
    {
        // from now on it's modified, even if via auto setting the same size was set as now selected in the LB
        ToolBoxButtonSize eSet = ToolBoxButtonSize::DontCare;
        switch( nSidebarSizeLB_NewSelection )
        {
            case 0: eSet = ToolBoxButtonSize::DontCare;  break;
            case 1: eSet = ToolBoxButtonSize::Small; break;
            case 2: eSet = ToolBoxButtonSize::Large; break;
            default:
                SAL_WARN("cui.options", "OfaViewTabPage::FillItemSet(): This state of m_xSidebarIconSizeLB should not be possible!");
        }
        officecfg::Office::Common::Misc::SidebarIconSize::set(static_cast<sal_Int16>(eSet), xChanges);
    }

    const sal_Int32 nNotebookbarSizeLB_NewSelection = m_xNotebookbarIconSizeLB->get_active();
    if( nNotebookbarSizeLB_InitialSelection != nNotebookbarSizeLB_NewSelection )
    {
        // from now on it's modified, even if via auto setting the same size was set as now selected in the LB
        ToolBoxButtonSize eSet = ToolBoxButtonSize::DontCare;
        switch( nNotebookbarSizeLB_NewSelection )
        {
            case 0: eSet = ToolBoxButtonSize::DontCare;  break;
            case 1: eSet = ToolBoxButtonSize::Small; break;
            case 2: eSet = ToolBoxButtonSize::Large; break;
            default:
                SAL_WARN("cui.options", "OfaViewTabPage::FillItemSet(): This state of m_xNotebookbarIconSizeLB should not be possible!");
        }
        officecfg::Office::Common::Misc::NotebookbarIconSize::set(static_cast<sal_Int16>(eSet), xChanges);
    }

    const sal_Int32 nStyleLB_NewSelection = m_xIconStyleLB->get_active();
    if( nStyleLB_InitialSelection != nStyleLB_NewSelection )
    {
        aMiscOptions.SetIconTheme(m_xIconStyleLB->get_active_id());
        nStyleLB_InitialSelection = nStyleLB_NewSelection;
    }

    bool bAppearanceChanged = false;
    std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());

    // Middle Mouse Button
    MouseMiddleButtonAction eOldMiddleMouse = static_cast<MouseMiddleButtonAction>(officecfg::Office::Common::View::Dialog::MiddleMouseButton::get());
    short eNewMiddleMouse = m_xMouseMiddleLB->get_active();
    if(eNewMiddleMouse > 2)
        eNewMiddleMouse = 2;

    if ( eNewMiddleMouse != static_cast<short>(eOldMiddleMouse) )
    {
        officecfg::Office::Common::View::Dialog::MiddleMouseButton::set(eNewMiddleMouse, batch);
        bAppearanceChanged = true;
    }

    if (m_xFontAntiAliasing->get_state_changed_from_saved())
    {
        bool b = m_xFontAntiAliasing->get_active();
        officecfg::Office::Common::View::FontAntiAliasing::Enabled::set(b, batch);
        bAppearanceChanged = true;
    }

    if (m_xAAPointLimit->get_value_changed_from_saved())
    {
        sal_Int64 i = m_xAAPointLimit->get_value(FieldUnit::PIXEL);
        officecfg::Office::Common::View::FontAntiAliasing::MinPixelHeight::set(i, batch);
        bAppearanceChanged = true;
    }

    if (m_xFontShowCB->get_state_changed_from_saved())
    {
        officecfg::Office::Common::Font::View::ShowFontBoxWYSIWYG::set(m_xFontShowCB->get_active(), xChanges);
        bModified = true;
    }

    // #i95644#  if disabled, do not use value, see in ::Reset()
    if (m_xUseHardwareAccell->get_sensitive())
    {
        if(m_xUseHardwareAccell->get_state_changed_from_saved())
        {
            pCanvasSettings->EnabledHardwareAcceleration(m_xUseHardwareAccell->get_active());
            bModified = true;
        }
    }

    // #i95644#  if disabled, do not use value, see in ::Reset()
    if (m_xUseAntiAliase->get_sensitive())
    {
        if (m_xUseAntiAliase->get_active() != SvtOptionsDrawinglayer::IsAntiAliasing())
        {
            SvtOptionsDrawinglayer::SetAntiAliasing(m_xUseAntiAliase->get_active(), /*bTemporary*/false);
            bModified = true;
            bRepaintWindows = true;
        }
    }

    if (m_xUseSkia->get_state_changed_from_saved() ||
        m_xForceSkiaRaster->get_state_changed_from_saved())
    {
        officecfg::Office::Common::VCL::UseSkia::set(m_xUseSkia->get_active(), xChanges);
        officecfg::Office::Common::VCL::ForceSkiaRaster::set(m_xForceSkiaRaster->get_active(), xChanges);
        bModified = true;
    }

    xChanges->commit();

    if ( bAppearanceChanged )
    {
        batch->commit();
        SvtTabAppearanceCfg::SetApplicationDefaults ( GetpApp() );
    }

    if(bRepaintWindows)
    {
        vcl::Window* pAppWindow = Application::GetFirstTopLevelWindow();

        while(pAppWindow)
        {
            pAppWindow->Invalidate();
            pAppWindow = Application::GetNextTopLevelWindow(pAppWindow);
        }
    }

    if (m_xUseSkia->get_state_changed_from_saved() ||
        m_xForceSkiaRaster->get_state_changed_from_saved())
    {
        SolarMutexGuard aGuard;
        if( svtools::executeRestartDialog(
                comphelper::getProcessComponentContext(), nullptr,
                svtools::RESTART_REASON_SKIA))
            GetDialogController()->response(RET_OK);
    }

    return bModified;
}

void OfaViewTabPage::Reset( const SfxItemSet* )
{
    SvtMiscOptions aMiscOptions;
    bool bEnable = true;

    if (SvtMiscOptions::GetSymbolsSize() != SFX_SYMBOLS_SIZE_AUTO)
    {
        nSizeLB_InitialSelection = 1;

        if (SvtMiscOptions::GetSymbolsSize() == SFX_SYMBOLS_SIZE_LARGE)
            nSizeLB_InitialSelection = 2;
        else if (SvtMiscOptions::GetSymbolsSize() == SFX_SYMBOLS_SIZE_32)
            nSizeLB_InitialSelection = 3;
    }
    bEnable = !officecfg::Office::Common::Misc::SymbolSet::isReadOnly();
    m_xIconSizeLB->set_active( nSizeLB_InitialSelection );
    m_xIconSizeLabel->set_sensitive(bEnable);
    m_xIconSizeLB->set_sensitive(bEnable);
    m_xMoreIcons->set_sensitive(bEnable);
    m_xIconSizeImg->set_visible(!bEnable);
    m_xIconSizeLB->save_value();

    ToolBoxButtonSize eSidebarIconSize = static_cast<ToolBoxButtonSize>(officecfg::Office::Common::Misc::SidebarIconSize::get());
    if( eSidebarIconSize == ToolBoxButtonSize::DontCare )
        ; // do nothing
    else if( eSidebarIconSize == ToolBoxButtonSize::Small )
        nSidebarSizeLB_InitialSelection = 1;
    else if( eSidebarIconSize == ToolBoxButtonSize::Large )
        nSidebarSizeLB_InitialSelection = 2;

    bEnable = !officecfg::Office::Common::Misc::SidebarIconSize::isReadOnly();
    m_xSidebarIconSizeLB->set_active( nSidebarSizeLB_InitialSelection );
    m_xSidebarIconSizeLabel->set_sensitive(bEnable);
    m_xSidebarIconSizeLB->set_sensitive(bEnable);
    m_xSidebarIconSizeImg->set_visible(!bEnable);
    m_xSidebarIconSizeLB->save_value();

    ToolBoxButtonSize eNotebookbarIconSize = static_cast<ToolBoxButtonSize>(officecfg::Office::Common::Misc::NotebookbarIconSize::get());
    if( eNotebookbarIconSize == ToolBoxButtonSize::DontCare )
        ; // do nothing
    else if( eNotebookbarIconSize == ToolBoxButtonSize::Small )
        nNotebookbarSizeLB_InitialSelection = 1;
    else if( eNotebookbarIconSize == ToolBoxButtonSize::Large )
        nNotebookbarSizeLB_InitialSelection = 2;

    bEnable = !officecfg::Office::Common::Misc::NotebookbarIconSize::isReadOnly();
    m_xNotebookbarIconSizeLB->set_active(nNotebookbarSizeLB_InitialSelection);
    m_xNotebookbarIconSizeLabel->set_sensitive(bEnable);
    m_xNotebookbarIconSizeLB->set_sensitive(bEnable);
    m_xNotebookbarIconSizeImg->set_visible(!bEnable);
    m_xNotebookbarIconSizeLB->save_value();

    // tdf#153497 set name of automatic icon theme, it may have changed due to "Apply" while this page is visible
    UpdateIconThemes();

    if (aMiscOptions.IconThemeWasSetAutomatically()) {
        nStyleLB_InitialSelection = 0;
    }
    else {
        const OUString selected = SvtMiscOptions::GetIconTheme();
        const vcl::IconThemeInfo& selectedInfo =
                vcl::IconThemeInfo::FindIconThemeById(mInstalledIconThemes, selected);
        nStyleLB_InitialSelection = m_xIconStyleLB->find_text(selectedInfo.GetDisplayName());
    }

    bEnable = !officecfg::Office::Common::Misc::SymbolStyle::isReadOnly();
    m_xIconStyleLB->set_active(nStyleLB_InitialSelection);
    m_xIconStyleLabel->set_sensitive(bEnable);
    m_xIconStyleLB->set_sensitive(bEnable);
    m_xIconStyleImg->set_visible(!bEnable);
    m_xIconStyleLB->save_value();

    // Middle Mouse Button
    bEnable = !officecfg::Office::Common::View::Dialog::MiddleMouseButton::isReadOnly();
    sal_Int16 nMiddleMouseButton = officecfg::Office::Common::View::Dialog::MiddleMouseButton::get();
    m_xMouseMiddleLB->set_active(static_cast<short>(nMiddleMouseButton));
    m_xMouseMiddleLabel->set_sensitive(bEnable);
    m_xMouseMiddleLB->set_sensitive(bEnable);
    m_xMouseMiddleImg->set_visible(!bEnable);
    m_xMouseMiddleLB->save_value();

    bEnable = !officecfg::Office::Common::View::FontAntiAliasing::Enabled::isReadOnly();
    bool bFontAntiAliasing = officecfg::Office::Common::View::FontAntiAliasing::Enabled::get();
    m_xFontAntiAliasing->set_active( bFontAntiAliasing );
    m_xFontAntiAliasing->set_sensitive(bEnable);
    m_xFontAntiAliasingImg->set_visible(!bEnable);

    bEnable = !officecfg::Office::Common::View::FontAntiAliasing::MinPixelHeight::isReadOnly();
    sal_Int16 nFontAntiAliasingMinPixelHeight = officecfg::Office::Common::View::FontAntiAliasing::MinPixelHeight::get();
    m_xAAPointLimit->set_value(nFontAntiAliasingMinPixelHeight, FieldUnit::PIXEL);
    m_xAAPointLimit->set_sensitive(bEnable);
    m_xAAPointLimitLabelImg->set_visible(!bEnable);

    // WorkingSet
    bEnable = !officecfg::Office::Common::Font::View::ShowFontBoxWYSIWYG::isReadOnly();
    m_xFontShowCB->set_active(officecfg::Office::Common::Font::View::ShowFontBoxWYSIWYG::get());
    m_xFontShowCB->set_sensitive(bEnable);
    m_xFontShowImg->set_visible(!bEnable);

    UpdateHardwareAccelStatus();
    m_xUseHardwareAccell->save_state();

    { // #i95644# AntiAliasing
        m_xUseAntiAliase->set_active(SvtOptionsDrawinglayer::IsAntiAliasing());
        bEnable = !officecfg::Office::Common::Drawinglayer::AntiAliasing::isReadOnly();
        m_xUseAntiAliase->set_sensitive(bEnable);
        m_xUseAntiAliaseImg->set_visible(!bEnable);
        m_xUseAntiAliase->save_state();
    }

    m_xUseSkia->set_active(officecfg::Office::Common::VCL::UseSkia::get());
    m_xForceSkiaRaster->set_active(officecfg::Office::Common::VCL::ForceSkiaRaster::get());
    m_xUseSkia->save_state();
    m_xForceSkiaRaster->save_state();

    m_xFontAntiAliasing->save_state();
    m_xAAPointLimit->save_value();
    m_xFontShowCB->save_state();

    OnAntialiasingToggled(*m_xFontAntiAliasing);
    UpdateSkiaStatus();
}

void OfaViewTabPage::UpdateHardwareAccelStatus()
{
    // #i95644# HW accel (unified to disable mechanism)
    if(pCanvasSettings->IsHardwareAccelerationAvailable())
    {
        m_xUseHardwareAccell->set_active(pCanvasSettings->IsHardwareAccelerationEnabled());
        m_xUseHardwareAccell->set_sensitive(!pCanvasSettings->IsHardwareAccelerationRO());
        m_xUseHardwareAccellImg->set_visible(pCanvasSettings->IsHardwareAccelerationRO());
    }
    else
    {
        m_xUseHardwareAccell->set_active(false);
        m_xUseHardwareAccell->set_sensitive(false);
        m_xUseHardwareAccellImg->set_visible(true);
    }
#if HAVE_FEATURE_SKIA
    m_xUseHardwareAccell->set_sensitive(!m_xUseSkia->get_active());
#endif
}

struct LanguageConfig_Impl
{
    SvtCTLOptions aCTLLanguageOptions;
    SvtSysLocaleOptions aSysLocaleOptions;
    SvtLinguConfig aLinguConfig;
};

static bool bLanguageCurrentDoc_Impl = false;

// some things we'll need...
constexpr OUString sAccessSrvc = u"com.sun.star.configuration.ConfigurationAccess"_ustr;
constexpr OUStringLiteral sAccessUpdSrvc = u"com.sun.star.configuration.ConfigurationUpdateAccess";
constexpr OUString sInstalledLocalesPath = u"org.openoffice.Setup/Office/InstalledLocales"_ustr;
constexpr OUString sUserLocalePath = u"org.openoffice.Office.Linguistic/General"_ustr;
constexpr OUString sUserLocaleKey = u"UILocale"_ustr;
static Sequence< OUString > seqInstalledLanguages;

static OUString lcl_getDatePatternsConfigString( const LocaleDataWrapper& rLocaleWrapper )
{
    const Sequence< OUString >& aDateAcceptancePatterns = rLocaleWrapper.getDateAcceptancePatterns();
    sal_Int32 nPatterns = aDateAcceptancePatterns.getLength();
    OUStringBuffer aBuf( nPatterns * 6 );   // 6 := length of Y-M-D;
    SAL_WARN_IF( !nPatterns, "cui.options", "No date acceptance pattern");
    if (nPatterns)
    {
        aBuf.append(aDateAcceptancePatterns[0]);
        for (sal_Int32 i=1; i < nPatterns; ++i)
            aBuf.append(";" + aDateAcceptancePatterns[i]);
    }
    return aBuf.makeStringAndClear();
}

namespace
{
    //what ui language will be selected by default if the user override of General::UILocale is unset ?
    LanguageTag GetInstalledLocaleForSystemUILanguage()
    {
        css::uno::Sequence<OUString> inst(officecfg::Setup::Office::InstalledLocales::get()->getElementNames());
        return LanguageTag(getInstalledLocaleForSystemUILanguage(inst, false)).makeFallback();
    }
}

OfaLanguagesTabPage::OfaLanguagesTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet)
    : SfxTabPage(pPage, pController, u"cui/ui/optlanguagespage.ui"_ustr, u"OptLanguagesPage"_ustr, &rSet)
    , pLangConfig(new LanguageConfig_Impl)
    , m_bDatePatternsValid(false)
    , m_xUserInterfaceLB(m_xBuilder->weld_combo_box(u"userinterface"_ustr))
    , m_xLocaleSettingFT(m_xBuilder->weld_label(u"localesettingFT"_ustr))
    , m_xLocaleSettingLB(new SvxLanguageBox(m_xBuilder->weld_combo_box(u"localesetting"_ustr)))
    , m_xLocaleSettingImg(m_xBuilder->weld_widget(u"locklocalesetting"_ustr))
    , m_xDecimalSeparatorFT(m_xBuilder->weld_label(u"label6"_ustr))
    , m_xDecimalSeparatorCB(m_xBuilder->weld_check_button(u"decimalseparator"_ustr))
    , m_xDecimalSeparatorImg(m_xBuilder->weld_widget(u"lockdecimalseparator"_ustr))
    , m_xCurrencyFT(m_xBuilder->weld_label(u"defaultcurrency"_ustr))
    , m_xCurrencyLB(m_xBuilder->weld_combo_box(u"currencylb"_ustr))
    , m_xCurrencyImg(m_xBuilder->weld_widget(u"lockcurrencylb"_ustr))
    , m_xDatePatternsFT(m_xBuilder->weld_label(u"dataaccpatterns"_ustr))
    , m_xDatePatternsED(m_xBuilder->weld_entry(u"datepatterns"_ustr))
    , m_xDatePatternsImg(m_xBuilder->weld_widget(u"lockdatepatterns"_ustr))
    , m_xWesternLanguageLB(new SvxLanguageBox(m_xBuilder->weld_combo_box(u"westernlanguage"_ustr)))
    , m_xWesternLanguageFT(m_xBuilder->weld_label(u"western"_ustr))
    , m_xWesternLanguageImg(m_xBuilder->weld_widget(u"lockwesternlanguage"_ustr))
    , m_xAsianLanguageLB(new SvxLanguageBox(m_xBuilder->weld_combo_box(u"asianlanguage"_ustr)))
    , m_xComplexLanguageLB(new SvxLanguageBox(m_xBuilder->weld_combo_box(u"complexlanguage"_ustr)))
    , m_xCurrentDocCB(m_xBuilder->weld_check_button(u"currentdoc"_ustr))
    , m_xAsianSupportCB(m_xBuilder->weld_check_button(u"asiansupport"_ustr))
    , m_xAsianSupportImg(m_xBuilder->weld_widget(u"lockasiansupport"_ustr))
    , m_xCTLSupportCB(m_xBuilder->weld_check_button(u"ctlsupport"_ustr))
    , m_xCTLSupportImg(m_xBuilder->weld_widget(u"lockctlsupport"_ustr))
    , m_xIgnoreLanguageChangeCB(m_xBuilder->weld_check_button(u"ignorelanguagechange"_ustr))
    , m_xIgnoreLanguageChangeImg(m_xBuilder->weld_widget(u"lockignorelanguagechange"_ustr))
{
    // tdf#125483 save original default label
    m_sDecimalSeparatorLabel = m_xDecimalSeparatorCB->get_label();

    // initialize user interface language selection
    m_sSystemDefaultString = SvtLanguageTable::GetLanguageString( LANGUAGE_SYSTEM );

    OUString aUILang = m_sSystemDefaultString +
                       " - " +
                       SvtLanguageTable::GetLanguageString(GetInstalledLocaleForSystemUILanguage().getLanguageType());

    m_xUserInterfaceLB->append(u"0"_ustr, aUILang);
    m_xUserInterfaceLB->append_separator(u""_ustr);
    try
    {
        Reference< XMultiServiceFactory > theConfigProvider(
            css::configuration::theDefaultProvider::get(
                comphelper::getProcessComponentContext()));
        // find out which locales are currently installed and add them to the listbox
        Sequence< Any > theArgs{ Any(NamedValue(u"nodepath"_ustr, Any(sInstalledLocalesPath))) };
        Reference< XNameAccess > theNameAccess(
            theConfigProvider->createInstanceWithArguments(sAccessSrvc, theArgs ), UNO_QUERY_THROW );
        seqInstalledLanguages = theNameAccess->getElementNames();
        LanguageType aLang = LANGUAGE_DONTKNOW;
        std::vector< std::pair<sal_Int32, OUString> > aUILanguages;
        for (sal_Int32 i=0; i<seqInstalledLanguages.getLength(); i++)
        {
            aLang = LanguageTag::convertToLanguageTypeWithFallback(seqInstalledLanguages[i]);
            if (aLang != LANGUAGE_DONTKNOW)
            {
                OUString aLangStr( SvtLanguageTable::GetLanguageString( aLang ) );
                aUILanguages.emplace_back(i+1, aLangStr);
            }
        }

        std::sort(aUILanguages.begin(), aUILanguages.end(), [](const auto& l1, const auto& l2) {
            static const auto aSorter = comphelper::string::NaturalStringSorter(
                comphelper::getProcessComponentContext(),
                Application::GetSettings().GetUILanguageTag().getLocale());
            return aSorter.compare(l1.second, l2.second) < 0;
        });

        // tdf#114694: append the sorted list after the default entry and separator.
        for (const auto & [ nGroupID, sGroupName ] : aUILanguages)
        {
            m_xUserInterfaceLB->append(OUString::number(nGroupID), sGroupName);
        }

        m_xUserInterfaceLB->set_active(0);

        // find out whether the user has a specific locale specified
        Sequence< Any > theArgs2{ Any(NamedValue(u"nodepath"_ustr, Any(sUserLocalePath))) };
        theNameAccess.set(
            theConfigProvider->createInstanceWithArguments(sAccessSrvc, theArgs2 ), UNO_QUERY_THROW );
        if (theNameAccess->hasByName(sUserLocaleKey))
            theNameAccess->getByName(sUserLocaleKey) >>= m_sUserLocaleValue;
        // select the user specified locale in the listbox
        if (!m_sUserLocaleValue.isEmpty())
        {
            for (sal_Int32 i = 0, nEntryCount = m_xUserInterfaceLB->get_count(); i < nEntryCount; ++i)
            {
                sal_Int32 d = m_xUserInterfaceLB->get_id(i).toInt32();
                if ( d > 0 && seqInstalledLanguages.getLength() > d-1 && seqInstalledLanguages[d-1] == m_sUserLocaleValue)
                    m_xUserInterfaceLB->set_active(i);
            }
        }

    }
    catch (const Exception &)
    {
        // we'll just leave the box in its default setting and won't
        // even give it event handler...
        TOOLS_WARN_EXCEPTION("cui.options", "ignoring" );
    }

    m_xWesternLanguageLB->SetLanguageList(
        SvxLanguageListFlags::WESTERN | SvxLanguageListFlags::ONLY_KNOWN, true, false, true, true,
        LANGUAGE_SYSTEM, css::i18n::ScriptType::LATIN);

    m_xAsianLanguageLB->SetLanguageList(
        SvxLanguageListFlags::CJK | SvxLanguageListFlags::ONLY_KNOWN, true, false, true, true,
        LANGUAGE_SYSTEM, css::i18n::ScriptType::ASIAN);

    m_xComplexLanguageLB->SetLanguageList(
        SvxLanguageListFlags::CTL | SvxLanguageListFlags::ONLY_KNOWN, true, false, true, true,
        LANGUAGE_SYSTEM, css::i18n::ScriptType::COMPLEX);

    m_xLocaleSettingLB->SetLanguageList(
        SvxLanguageListFlags::ALL | SvxLanguageListFlags::ONLY_KNOWN, false, false, false, true,
        LANGUAGE_USER_SYSTEM_CONFIG, css::i18n::ScriptType::WEAK);

    const NfCurrencyTable& rCurrTab = SvNumberFormatter::GetTheCurrencyTable();
    const NfCurrencyEntry& rCurr = SvNumberFormatter::GetCurrencyEntry( LANGUAGE_SYSTEM );
    // insert SYSTEM entry
    OUString aDefaultCurr = m_sSystemDefaultString + " - " + rCurr.GetBankSymbol();
    m_xCurrencyLB->append(u"default"_ustr, aDefaultCurr);
    m_xCurrencyLB->append_separator(u""_ustr);

    assert(m_xCurrencyLB->find_id(u"default"_ustr) != -1);
    // all currencies
    OUString aTwoSpace( u"  "_ustr );
    sal_uInt16 nCurrCount = rCurrTab.size();
    std::vector< const NfCurrencyEntry* > aCurrencies;
    // first entry is SYSTEM, skip it
    for ( sal_uInt16 j=1; j < nCurrCount; ++j )
    {
        aCurrencies.push_back(&rCurrTab[j]);
    }
    std::sort(aCurrencies.begin(), aCurrencies.end(),
              [](const NfCurrencyEntry* c1, const NfCurrencyEntry* c2) {
                  return c1->GetBankSymbol().compareTo(c2->GetBankSymbol()) < 0;
              });

    for (auto &v : aCurrencies)
    {
        OUString aStr_ = v->GetBankSymbol() +
                         aTwoSpace +
                         v->GetSymbol();
        aStr_ = ApplyLreOrRleEmbedding( aStr_ ) +
                aTwoSpace +
                ApplyLreOrRleEmbedding( SvtLanguageTable::GetLanguageString( v->GetLanguage() ) );
        m_xCurrencyLB->append(weld::toId(v), aStr_);
    }

    m_xCurrencyLB->set_active(0);

    m_xLocaleSettingLB->connect_changed( LINK( this, OfaLanguagesTabPage, LocaleSettingHdl ) );
    m_xDatePatternsED->connect_changed( LINK( this, OfaLanguagesTabPage, DatePatternsHdl ) );

    Link<weld::Toggleable&,void> aLink( LINK( this, OfaLanguagesTabPage, SupportHdl ) );
    m_xAsianSupportCB->connect_toggled( aLink );
    m_xCTLSupportCB->connect_toggled( aLink );

    m_bOldAsian = SvtCJKOptions::IsAnyEnabled();
    m_xAsianSupportCB->set_active(m_bOldAsian);
    m_xAsianSupportCB->save_state();
    bool bReadonly = SvtCJKOptions::IsAnyReadOnly();
    m_xAsianSupportCB->set_sensitive(!bReadonly);
    m_xAsianSupportImg->set_visible(bReadonly);
    SupportHdl(*m_xAsianSupportCB);

    m_bOldCtl = SvtCTLOptions::IsCTLFontEnabled();
    m_xCTLSupportCB->set_active(m_bOldCtl);
    m_xCTLSupportCB->save_state();
    bReadonly = pLangConfig->aCTLLanguageOptions.IsReadOnly(SvtCTLOptions::E_CTLFONT);
    m_xCTLSupportCB->set_sensitive(!bReadonly);
    m_xCTLSupportImg->set_visible(bReadonly);
    SupportHdl(*m_xCTLSupportCB);

    m_xIgnoreLanguageChangeCB->set_active( pLangConfig->aSysLocaleOptions.IsIgnoreLanguageChange() );
}

OfaLanguagesTabPage::~OfaLanguagesTabPage()
{
}

std::unique_ptr<SfxTabPage> OfaLanguagesTabPage::Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet )
{
    return std::make_unique<OfaLanguagesTabPage>(pPage, pController, *rAttrSet);
}

static void lcl_Update(std::unique_ptr<SfxVoidItem> pInvalidItems[], std::unique_ptr<SfxBoolItem> pBoolItems[], sal_uInt16 nCount)
{
    SfxViewFrame* pCurrentFrm = SfxViewFrame::Current();
    SfxViewFrame* pViewFrm = SfxViewFrame::GetFirst();
    while(pViewFrm)
    {
        SfxBindings& rBind = pViewFrm->GetBindings();
        for(sal_uInt16 i = 0; i < nCount; i++)
        {
            if(pCurrentFrm == pViewFrm)
                rBind.InvalidateAll(false);
            rBind.SetState( *pInvalidItems[i] );
            rBind.SetState( *pBoolItems[i] );
        }
        pViewFrm = SfxViewFrame::GetNext(*pViewFrm);
    }
}

OUString OfaLanguagesTabPage::GetAllStrings()
{
    OUString sAllStrings;
    OUString labels[]
        = { u"label1"_ustr, u"label4"_ustr,          u"label7"_ustr, u"localesettingFT"_ustr, u"defaultcurrency"_ustr,
            u"label6"_ustr, u"dataaccpatterns"_ustr, u"label2"_ustr, u"western"_ustr,         u"label3"_ustr };

    for (const auto& label : labels)
    {
        if (const auto pString = m_xBuilder->weld_label(label))
            sAllStrings += pString->get_label() + " ";
    }

    OUString checkButton[] = { u"decimalseparator"_ustr, u"asiansupport"_ustr, u"ctlsupport"_ustr, u"currentdoc"_ustr,
                               u"ignorelanguagechange"_ustr };

    for (const auto& check : checkButton)
    {
        if (const auto pString = m_xBuilder->weld_check_button(check))
            sAllStrings += pString->get_label() + " ";
    }

    return sAllStrings.replaceAll("_", "");
}

bool OfaLanguagesTabPage::FillItemSet( SfxItemSet* rSet )
{
    // lock configuration broadcasters so that we can coordinate the notifications
    pLangConfig->aSysLocaleOptions.BlockBroadcasts( true );
    pLangConfig->aCTLLanguageOptions.BlockBroadcasts( true );
    pLangConfig->aLinguConfig.BlockBroadcasts( true );

    /*
     * Sequence checking only matters when CTL support is enabled.
     *
     * So we only need to check for sequence checking if
     * a) previously it was unchecked and is now checked or
     * b) it was already checked but the CTL language has changed
     */
    if (
         m_xCTLSupportCB->get_active() &&
         (m_xCTLSupportCB->get_saved_state() != TRISTATE_TRUE ||
         m_xComplexLanguageLB->get_active_id_changed_from_saved())
       )
    {
        //sequence checking has to be switched on depending on the selected CTL language
        LanguageType eCTLLang = m_xComplexLanguageLB->get_active_id();
        bool bOn = MsLangId::needsSequenceChecking( eCTLLang);
        pLangConfig->aCTLLanguageOptions.SetCTLSequenceCheckingRestricted(bOn);
        pLangConfig->aCTLLanguageOptions.SetCTLSequenceChecking(bOn);
        pLangConfig->aCTLLanguageOptions.SetCTLSequenceCheckingTypeAndReplace(bOn);
    }
    try
    {
        // handle settings for UI Language
        // a change of setting needs to bring up a warning message
        OUString aLangString;
        sal_Int32 d = m_xUserInterfaceLB->get_active_id().toInt32();
        if( d > 0 && seqInstalledLanguages.getLength() > d-1)
            aLangString = seqInstalledLanguages[d-1];

        /*
        if( m_xUserInterfaceLB->GetSelectedEntryPos() > 0)
            aLangString = ConvertLanguageToIsoString(m_xUserInterfaceLB->get_active_id());
        */
        Reference< XMultiServiceFactory > theConfigProvider(
            css::configuration::theDefaultProvider::get(
                comphelper::getProcessComponentContext()));
        Sequence< Any > theArgs{ Any(NamedValue(u"nodepath"_ustr, Any(sUserLocalePath))) };
        Reference< XPropertySet >xProp(
            theConfigProvider->createInstanceWithArguments(sAccessUpdSrvc, theArgs ), UNO_QUERY_THROW );
        if ( m_sUserLocaleValue != aLangString)
        {
            // OSL_FAIL("UserInterface language was changed, restart.");
            // write new value
            xProp->setPropertyValue(sUserLocaleKey, Any(aLangString));
            Reference< XChangesBatch >(xProp, UNO_QUERY_THROW)->commitChanges();
            // display info
            SolarMutexGuard aGuard;
            if (svtools::executeRestartDialog(
                    comphelper::getProcessComponentContext(), GetFrameWeld(),
                    svtools::RESTART_REASON_LANGUAGE_CHANGE))
                GetDialogController()->response(RET_OK);

            // tell quickstarter to stop being a veto listener

            const Reference< XComponentContext >& xContext(
                comphelper::getProcessComponentContext());
            css::office::Quickstart::createAndSetVeto(xContext, false, false, false/*DisableVeto*/);
        }
    }
    catch (const Exception&)
    {
        // we'll just leave the box in its default setting and won't
        // even give it event handler...
        TOOLS_WARN_EXCEPTION("cui.options", "ignoring");
    }

    LanguageTag aLanguageTag( pLangConfig->aSysLocaleOptions.GetLanguageTag());
    LanguageType eOldLocale = (aLanguageTag.isSystemLocale() ? LANGUAGE_SYSTEM :
            aLanguageTag.makeFallback().getLanguageType());
    LanguageType eNewLocale = m_xLocaleSettingLB->get_active_id();

    // If the "Default ..." entry was selected that means SYSTEM, the actual
    // eNewLocale value is temporary for the dialog only, do not resolve to
    // what system currently is.
    if (eNewLocale == LANGUAGE_USER_SYSTEM_CONFIG)
        eNewLocale = LANGUAGE_SYSTEM;

    if ( eOldLocale != eNewLocale )
    {
        // an empty string denotes SYSTEM locale
        OUString sNewLang;
        if ( eNewLocale != LANGUAGE_SYSTEM )
            sNewLang = LanguageTag::convertToBcp47( eNewLocale);

        // locale nowadays get to AppSettings via notification
        // this will happen after releasing the lock on the ConfigurationBroadcaster at
        // the end of this method
        pLangConfig->aSysLocaleOptions.SetLocaleConfigString( sNewLang );
        rSet->Put( SfxBoolItem( SID_OPT_LOCALE_CHANGED, true ) );

        SvtScriptType nNewType = SvtLanguageOptions::GetScriptTypeOfLanguage( eNewLocale );
        bool bNewCJK = bool( nNewType & SvtScriptType::ASIAN );
        auto batch = comphelper::ConfigurationChanges::create();
        SvtCompatibilityDefault aCompatOpts(batch);
        aCompatOpts.set(u"ExpandWordSpace"_ustr, !bNewCJK);
        batch->commit();
    }

    if(m_xDecimalSeparatorCB->get_state_changed_from_saved())
        pLangConfig->aSysLocaleOptions.SetDecimalSeparatorAsLocale(m_xDecimalSeparatorCB->get_active());

    if(m_xIgnoreLanguageChangeCB->get_state_changed_from_saved())
        pLangConfig->aSysLocaleOptions.SetIgnoreLanguageChange(m_xIgnoreLanguageChangeCB->get_active());

    // Configured currency, for example, USD-en-US or EUR-de-DE, or empty for locale default.
    OUString sOldCurr = pLangConfig->aSysLocaleOptions.GetCurrencyConfigString();
    OUString sId = m_xCurrencyLB->get_active_id();
    const NfCurrencyEntry* pCurr = sId == "default" ? nullptr : weld::fromId<const NfCurrencyEntry*>(sId);
    OUString sNewCurr;
    if ( pCurr )
        sNewCurr = SvtSysLocaleOptions::CreateCurrencyConfigString(
            pCurr->GetBankSymbol(), pCurr->GetLanguage() );
    if ( sOldCurr != sNewCurr )
        pLangConfig->aSysLocaleOptions.SetCurrencyConfigString( sNewCurr );

    // Configured date acceptance patterns, for example Y-M-D;M-D or empty for
    // locale default.
    if (m_bDatePatternsValid && m_xDatePatternsED->get_value_changed_from_saved())
        pLangConfig->aSysLocaleOptions.SetDatePatternsConfigString( m_xDatePatternsED->get_text());

    SfxObjectShell* pCurrentDocShell = SfxObjectShell::Current();
    Reference< css::linguistic2::XLinguProperties > xLinguProp = LinguMgr::GetLinguPropertySet();
    bool bCurrentDocCBChecked = m_xCurrentDocCB->get_active();
    if (m_xCurrentDocCB->get_sensitive())
        bLanguageCurrentDoc_Impl = bCurrentDocCBChecked;
    bool bCurrentDocCBChanged = m_xCurrentDocCB->get_state_changed_from_saved();

    bool bValChanged = m_xWesternLanguageLB->get_active_id_changed_from_saved();
    if( (bCurrentDocCBChanged && !bCurrentDocCBChecked) || bValChanged)
    {
        LanguageType eSelectLang = m_xWesternLanguageLB->get_active_id();
        if(!bCurrentDocCBChecked)
        {
            Any aValue;
            Locale aLocale = LanguageTag::convertToLocale( eSelectLang, false);
            aValue <<= aLocale;
            pLangConfig->aLinguConfig.SetProperty( u"DefaultLocale", aValue );
            if (xLinguProp.is())
                xLinguProp->setDefaultLocale( aLocale );
        }
        if(pCurrentDocShell)
        {
            rSet->Put(SvxLanguageItem(MsLangId::resolveSystemLanguageByScriptType(eSelectLang, css::i18n::ScriptType::LATIN),
                SID_ATTR_LANGUAGE));
        }
    }
    bValChanged = m_xAsianLanguageLB->get_active_id_changed_from_saved();
    if( (bCurrentDocCBChanged && !bCurrentDocCBChecked) || bValChanged)
    {
        LanguageType eSelectLang = m_xAsianLanguageLB->get_active_id();
        if(!bCurrentDocCBChecked)
        {
            Any aValue;
            Locale aLocale = LanguageTag::convertToLocale( eSelectLang, false);
            aValue <<= aLocale;
            pLangConfig->aLinguConfig.SetProperty( u"DefaultLocale_CJK", aValue );
            if (xLinguProp.is())
                xLinguProp->setDefaultLocale_CJK( aLocale );
        }
        if(pCurrentDocShell)
        {
            rSet->Put(SvxLanguageItem(MsLangId::resolveSystemLanguageByScriptType(eSelectLang, css::i18n::ScriptType::ASIAN),
                SID_ATTR_CHAR_CJK_LANGUAGE));
        }
    }
    bValChanged = m_xComplexLanguageLB->get_active_id_changed_from_saved();
    if( (bCurrentDocCBChanged && !bCurrentDocCBChecked) || bValChanged)
    {
        LanguageType eSelectLang = m_xComplexLanguageLB->get_active_id();
        if(!bCurrentDocCBChecked)
        {
            Any aValue;
            Locale aLocale = LanguageTag::convertToLocale( eSelectLang, false);
            aValue <<= aLocale;
            pLangConfig->aLinguConfig.SetProperty( u"DefaultLocale_CTL", aValue );
            if (xLinguProp.is())
                xLinguProp->setDefaultLocale_CTL( aLocale );
        }
        if(pCurrentDocShell)
        {
            rSet->Put(SvxLanguageItem(MsLangId::resolveSystemLanguageByScriptType(eSelectLang, css::i18n::ScriptType::COMPLEX),
                SID_ATTR_CHAR_CTL_LANGUAGE));
        }
    }

    if(m_xAsianSupportCB->get_state_changed_from_saved() )
    {
        bool bChecked = m_xAsianSupportCB->get_active();
        SvtCJKOptions::SetAll(bChecked);

        //iterate over all bindings to invalidate vertical text direction
        const sal_uInt16 STATE_COUNT = 2;

        std::unique_ptr<SfxBoolItem> pBoolItems[STATE_COUNT];
        pBoolItems[0].reset(new SfxBoolItem(SID_VERTICALTEXT_STATE, false));
        pBoolItems[1].reset(new SfxBoolItem(SID_TEXT_FITTOSIZE_VERTICAL, false));

        std::unique_ptr<SfxVoidItem> pInvalidItems[STATE_COUNT];
        pInvalidItems[0].reset(new SfxVoidItem(SID_VERTICALTEXT_STATE));
        pInvalidItems[1].reset(new SfxVoidItem(SID_TEXT_FITTOSIZE_VERTICAL));

        lcl_Update(pInvalidItems, pBoolItems, STATE_COUNT);
    }

    if ( m_xCTLSupportCB->get_state_changed_from_saved() )
    {
        SvtSearchOptions aOpt;
        aOpt.SetIgnoreDiacritics_CTL(true);
        aOpt.SetIgnoreKashida_CTL(true);
        aOpt.Commit();
        pLangConfig->aCTLLanguageOptions.SetCTLFontEnabled( m_xCTLSupportCB->get_active() );

        const sal_uInt16 STATE_COUNT = 1;
        std::unique_ptr<SfxBoolItem> pBoolItems[STATE_COUNT];
        pBoolItems[0].reset(new SfxBoolItem(SID_CTLFONT_STATE, false));
        std::unique_ptr<SfxVoidItem> pInvalidItems[STATE_COUNT];
        pInvalidItems[0].reset(new SfxVoidItem(SID_CTLFONT_STATE));
        lcl_Update(pInvalidItems, pBoolItems, STATE_COUNT);
    }

    if ( pLangConfig->aSysLocaleOptions.IsModified() )
        pLangConfig->aSysLocaleOptions.Commit();

    // first release the lock on the ConfigurationBroadcaster for Locale changes
    // it seems that our code relies on the fact that before other changes like e.g. currency
    // are broadcasted locale changes have been done
    pLangConfig->aSysLocaleOptions.BlockBroadcasts( false );
    pLangConfig->aCTLLanguageOptions.BlockBroadcasts( false );
    pLangConfig->aLinguConfig.BlockBroadcasts( false );

    return false;
}

void OfaLanguagesTabPage::Reset( const SfxItemSet* rSet )
{
    LanguageTag aLanguageTag( pLangConfig->aSysLocaleOptions.GetLanguageTag());
    if ( aLanguageTag.isSystemLocale() )
        m_xLocaleSettingLB->set_active_id( LANGUAGE_USER_SYSTEM_CONFIG );
    else
        m_xLocaleSettingLB->set_active_id( aLanguageTag.makeFallback().getLanguageType());
    bool bReadonly = pLangConfig->aSysLocaleOptions.IsReadOnly(SvtSysLocaleOptions::EOption::Locale);
    m_xLocaleSettingLB->set_sensitive(!bReadonly);
    m_xLocaleSettingFT->set_sensitive(!bReadonly);
    m_xLocaleSettingImg->set_visible(bReadonly);


    m_xDecimalSeparatorCB->set_active( pLangConfig->aSysLocaleOptions.IsDecimalSeparatorAsLocale());
    bReadonly = pLangConfig->aSysLocaleOptions.IsReadOnly(SvtSysLocaleOptions::EOption::DecimalSeparator);
    m_xDecimalSeparatorCB->set_sensitive(!bReadonly);
    m_xDecimalSeparatorFT->set_sensitive(!bReadonly);
    m_xDecimalSeparatorImg->set_visible(bReadonly);
    m_xDecimalSeparatorCB->save_state();

    m_xIgnoreLanguageChangeCB->set_active( pLangConfig->aSysLocaleOptions.IsIgnoreLanguageChange());
    bReadonly = pLangConfig->aSysLocaleOptions.IsReadOnly(SvtSysLocaleOptions::EOption::IgnoreLanguageChange);
    m_xIgnoreLanguageChangeCB->set_sensitive(!bReadonly);
    m_xIgnoreLanguageChangeImg->set_visible(bReadonly);
    m_xIgnoreLanguageChangeCB->save_state();

    // let LocaleSettingHdl enable/disable checkboxes for CJK/CTL support
    // #i15812# must be done *before* the configured currency is set
    // and update the decimal separator used for the given locale
    LocaleSettingHdl(*m_xLocaleSettingLB->get_widget());

    // configured currency, for example, USD-en-US or EUR-de-DE, or empty for locale default
    const NfCurrencyEntry* pCurr = nullptr;
    OUString sCurrency = pLangConfig->aSysLocaleOptions.GetCurrencyConfigString();
    if ( !sCurrency.isEmpty() )
    {
        LanguageType eLang;
        OUString aAbbrev;
        SvtSysLocaleOptions::GetCurrencyAbbrevAndLanguage( aAbbrev, eLang, sCurrency );
        pCurr = SvNumberFormatter::GetCurrencyEntry( aAbbrev, eLang );
    }
    // if pCurr==nullptr the SYSTEM entry is selected
    OUString sId = !pCurr ? u"default"_ustr : weld::toId(pCurr);
    m_xCurrencyLB->set_active_id(sId);
    bReadonly = pLangConfig->aSysLocaleOptions.IsReadOnly(SvtSysLocaleOptions::EOption::Currency);
    m_xCurrencyLB->set_sensitive(!bReadonly);
    m_xCurrencyFT->set_sensitive(!bReadonly);
    m_xCurrencyImg->set_visible(bReadonly);

    // date acceptance patterns
    OUString aDatePatternsString = pLangConfig->aSysLocaleOptions.GetDatePatternsConfigString();
    if (aDatePatternsString.isEmpty())
    {
        const LocaleDataWrapper& rLocaleWrapper( Application::GetSettings().GetLocaleDataWrapper() );
        aDatePatternsString = lcl_getDatePatternsConfigString( rLocaleWrapper);
        // Let's assume patterns are valid at this point.
        m_bDatePatternsValid = true;
    }
    else
    {
        bool bModified = false;
        m_bDatePatternsValid = validateDatePatterns( bModified, aDatePatternsString);
    }
    m_xDatePatternsED->set_text(aDatePatternsString);
    m_xDatePatternsED->set_message_type( m_bDatePatternsValid ?
            weld::EntryMessageType::Normal : weld::EntryMessageType::Error);
    bReadonly = pLangConfig->aSysLocaleOptions.IsReadOnly(SvtSysLocaleOptions::EOption::DatePatterns);
    m_xDatePatternsED->set_sensitive(!bReadonly);
    m_xDatePatternsFT->set_sensitive(!bReadonly);
    m_xDatePatternsImg->set_visible(bReadonly);
    m_xDatePatternsED->save_value();

    //western/CJK/CLK language
    LanguageType eCurLang = LANGUAGE_NONE;
    LanguageType eCurLangCJK = LANGUAGE_NONE;
    LanguageType eCurLangCTL = LANGUAGE_NONE;
    SfxObjectShell* pCurrentDocShell = SfxObjectShell::Current();
    //collect the configuration values first
    m_xCurrentDocCB->set_sensitive(false);

    Any aWestLang;
    Any aCJKLang;
    Any aCTLLang;
    try
    {
        aWestLang = pLangConfig->aLinguConfig.GetProperty(u"DefaultLocale");
        Locale aLocale;
        aWestLang >>= aLocale;

        eCurLang = LanguageTag::convertToLanguageType( aLocale, false);

        aCJKLang = pLangConfig->aLinguConfig.GetProperty(u"DefaultLocale_CJK");
        aLocale = Locale();
        aCJKLang >>= aLocale;
        eCurLangCJK = LanguageTag::convertToLanguageType( aLocale, false);

        aCTLLang = pLangConfig->aLinguConfig.GetProperty(u"DefaultLocale_CTL");
        aLocale = Locale();
        aCTLLang >>= aLocale;
        eCurLangCTL = LanguageTag::convertToLanguageType( aLocale, false);
    }
    catch (const Exception&)
    {
    }
    //overwrite them by the values provided by the DocShell
    if(pCurrentDocShell)
    {
        m_xCurrentDocCB->set_sensitive(true);
        m_xCurrentDocCB->set_active(bLanguageCurrentDoc_Impl);
        if( const SvxLanguageItem* pLangItem = rSet->GetItemIfSet(SID_ATTR_LANGUAGE, false))
        {
            LanguageType eTempCurLang = pLangItem->GetValue();
            if (MsLangId::resolveSystemLanguageByScriptType(eCurLang, css::i18n::ScriptType::LATIN) != eTempCurLang)
                eCurLang = eTempCurLang;
        }

        if( const SvxLanguageItem* pLang = rSet->GetItemIfSet(SID_ATTR_CHAR_CJK_LANGUAGE, false))
        {
            LanguageType eTempCurLang = pLang->GetValue();
            if (MsLangId::resolveSystemLanguageByScriptType(eCurLangCJK, css::i18n::ScriptType::ASIAN) != eTempCurLang)
                eCurLangCJK = eTempCurLang;
        }

        if( const SvxLanguageItem* pLang = rSet->GetItemIfSet(SID_ATTR_CHAR_CTL_LANGUAGE, false))
        {
            LanguageType eTempCurLang = pLang->GetValue();
            if (MsLangId::resolveSystemLanguageByScriptType(eCurLangCTL, css::i18n::ScriptType::COMPLEX) != eTempCurLang)
                eCurLangCTL = eTempCurLang;
        }
    }
    if(LANGUAGE_NONE == eCurLang || LANGUAGE_DONTKNOW == eCurLang)
        m_xWesternLanguageLB->set_active_id(LANGUAGE_NONE);
    else
        m_xWesternLanguageLB->set_active_id(eCurLang);

    if(LANGUAGE_NONE == eCurLangCJK || LANGUAGE_DONTKNOW == eCurLangCJK)
        m_xAsianLanguageLB->set_active_id(LANGUAGE_NONE);
    else
        m_xAsianLanguageLB->set_active_id(eCurLangCJK);

    if(LANGUAGE_NONE == eCurLangCTL || LANGUAGE_DONTKNOW == eCurLangCTL)
        m_xComplexLanguageLB->set_active_id(LANGUAGE_NONE);
    else
        m_xComplexLanguageLB->set_active_id(eCurLangCTL);

    m_xWesternLanguageLB->save_active_id();
    m_xAsianLanguageLB->save_active_id();
    m_xComplexLanguageLB->save_active_id();
    m_xIgnoreLanguageChangeCB->save_state();
    m_xCurrentDocCB->save_state();

    bool bEnable = !pLangConfig->aLinguConfig.IsReadOnly( u"DefaultLocale" );
    m_xWesternLanguageFT->set_sensitive( bEnable );
    m_xWesternLanguageLB->set_sensitive( bEnable );
    m_xWesternLanguageImg->set_visible( !bEnable );

    // check the box "For the current document only"
    // set the focus to the Western Language box
    const SfxBoolItem* pLang = rSet->GetItemIfSet(SID_SET_DOCUMENT_LANGUAGE, false );
    if ( pLang && pLang->GetValue() )
    {
        m_xWesternLanguageLB->grab_focus();
        m_xCurrentDocCB->set_sensitive(true);
        m_xCurrentDocCB->set_active(true);
    }
}

IMPL_LINK(OfaLanguagesTabPage, SupportHdl, weld::Toggleable&, rBox, void)
{
    bool bCheck = rBox.get_active();
    if ( m_xAsianSupportCB.get() == &rBox )
    {
        bool bReadonly = pLangConfig->aLinguConfig.IsReadOnly(u"DefaultLocale_CJK");
        bCheck = ( bCheck && !bReadonly );
        m_xAsianLanguageLB->set_sensitive( bCheck );
        if (rBox.get_sensitive())
            m_bOldAsian = bCheck;
    }
    else if ( m_xCTLSupportCB.get() == &rBox )
    {
        bool bReadonly = pLangConfig->aLinguConfig.IsReadOnly(u"DefaultLocale_CTL");
        bCheck = ( bCheck && !bReadonly  );
        m_xComplexLanguageLB->set_sensitive( bCheck );
        if (rBox.get_sensitive())
            m_bOldCtl = bCheck;
    }
    else
        SAL_WARN( "cui.options", "OfaLanguagesTabPage::SupportHdl(): wrong rBox" );
}

namespace
{
    void lcl_checkLanguageCheckBox(weld::CheckButton& _rCB, bool _bNewValue, bool _bOldValue)
    {
        if ( _bNewValue )
            _rCB.set_active(true);
        else
            _rCB.set_active( _bOldValue );
// #i15082# do not call save_state() in running dialog...
//      _rCB.save_state();
        _rCB.set_sensitive( !_bNewValue );
    }
}

IMPL_LINK_NOARG(OfaLanguagesTabPage, LocaleSettingHdl, weld::ComboBox&, void)
{
    LanguageType eLang = m_xLocaleSettingLB->get_active_id();
    SvtScriptType nType = SvtLanguageOptions::GetScriptTypeOfLanguage(eLang);
    // first check if CTL must be enabled
    // #103299# - if CTL font setting is not readonly
    if(!pLangConfig->aCTLLanguageOptions.IsReadOnly(SvtCTLOptions::E_CTLFONT))
    {
        bool bIsCTLFixed = bool(nType & SvtScriptType::COMPLEX);
        lcl_checkLanguageCheckBox(*m_xCTLSupportCB, bIsCTLFixed, m_bOldCtl);
        SupportHdl(*m_xCTLSupportCB);
    }
    // second check if CJK must be enabled
    // #103299# - if CJK support is not readonly
    if(!SvtCJKOptions::IsAnyReadOnly())
    {
        bool bIsCJKFixed = bool(nType & SvtScriptType::ASIAN);
        lcl_checkLanguageCheckBox(*m_xAsianSupportCB, bIsCJKFixed, m_bOldAsian);
        SupportHdl(*m_xAsianSupportCB);
    }

    const NfCurrencyEntry& rCurr = SvNumberFormatter::GetCurrencyEntry(
            (eLang == LANGUAGE_USER_SYSTEM_CONFIG) ? MsLangId::getConfiguredSystemLanguage() : eLang);
    static constexpr OUString aDefaultID = u"default"_ustr;
    // Update the "Default ..." currency.
    m_xCurrencyLB->remove_id(aDefaultID);
    OUString aDefaultCurr = m_sSystemDefaultString + " - " + rCurr.GetBankSymbol();
    m_xCurrencyLB->insert(0, aDefaultCurr, &aDefaultID, nullptr, nullptr);
    assert(m_xCurrencyLB->find_id(aDefaultID) != -1);
    m_xCurrencyLB->set_active_text(aDefaultCurr);

    // obtain corresponding locale data
    LocaleDataWrapper aLocaleWrapper(( LanguageTag(eLang) ));

    // update the decimal separator key of the related CheckBox
    OUString sTempLabel(m_sDecimalSeparatorLabel);
    sTempLabel = sTempLabel.replaceFirst("%1", aLocaleWrapper.getNumDecimalSep() );
    m_xDecimalSeparatorCB->set_label(sTempLabel);

    // update the date acceptance patterns
    OUString aDatePatternsString = lcl_getDatePatternsConfigString( aLocaleWrapper);
    m_bDatePatternsValid = true;
    m_xDatePatternsED->set_text( aDatePatternsString);
    m_xDatePatternsED->set_message_type(weld::EntryMessageType::Normal);
}

IMPL_LINK( OfaLanguagesTabPage, DatePatternsHdl, weld::Entry&, rEd, void )
{
    OUString aPatterns(rEd.get_text());
    bool bModified = false;
    const bool bValid = validateDatePatterns( bModified, aPatterns);
    if (bModified)
    {
        // gtk3 keeps the cursor position on equal length set_text() but at
        // least the 'gen' backend does not and resets to 0.
        const int nCursorPos = rEd.get_position();
        rEd.set_text(aPatterns);
        rEd.set_position(nCursorPos);
    }
    if (bValid)
        rEd.set_message_type(weld::EntryMessageType::Normal);
    else
        rEd.set_message_type(weld::EntryMessageType::Error);
    m_bDatePatternsValid = bValid;
}

bool OfaLanguagesTabPage::validateDatePatterns( bool& rbModified, OUString& rPatterns )
{
    bool bValid = true;
    if (!rPatterns.isEmpty())
    {
        OUStringBuffer aBuf( rPatterns);
        sal_Int32 nChar = 0;
        for (sal_Int32 nIndex=0; nIndex >= 0 && bValid; ++nChar)
        {
            const OUString aPat( rPatterns.getToken( 0, ';', nIndex));
            if (aPat.isEmpty() && nIndex < 0)
            {
                // Indicating failure when about to append a pattern is too
                // confusing. Empty patterns are ignored anyway when sequencing
                // to SvtSysLocale.
                continue;   // for
            }
            else if (aPat.getLength() < 2)
                bValid = false;
            else
            {
                bool bY, bM, bD;
                bY = bM = bD = false;
                bool bSep = true;
                if (aPat.getLength() == 3)
                {
                    // Disallow a pattern that would match a numeric input with
                    // decimal separator, like M.D
                    const LanguageType eLang = m_xLocaleSettingLB->get_active_id();
                    const LocaleDataWrapper aLocaleWrapper(( LanguageTag(eLang)));
                    if (    aPat[1] == aLocaleWrapper.getNumDecimalSep().toChar()
                         || aPat[1] == aLocaleWrapper.getNumDecimalSepAlt().toChar())
                    {
                        bValid = false;
                    }
                }
                for (sal_Int32 i = 0; i < aPat.getLength() && bValid; /*nop*/)
                {
                    const sal_Int32 j = i;
                    const sal_uInt32 c = aPat.iterateCodePoints( &i);
                    // Only one Y,M,D per pattern, separated by any character(s).
                    switch (c)
                    {
                        case 'y':
                        case 'Y':
                            if (bY || !bSep)
                                bValid = false;
                            else if (c == 'y')
                            {
                                aBuf[nChar] = 'Y';
                                rbModified = true;
                            }
                            bY = true;
                            bSep = false;
                            break;
                        case 'm':
                        case 'M':
                            if (bM || !bSep)
                                bValid = false;
                            else if (c == 'm')
                            {
                                aBuf[nChar] = 'M';
                                rbModified = true;
                            }
                            bM = true;
                            bSep = false;
                            break;
                        case 'd':
                        case 'D':
                            if (bD || !bSep)
                                bValid = false;
                            else if (c == 'd')
                            {
                                aBuf[nChar] = 'D';
                                rbModified = true;
                            }
                            bD = true;
                            bSep = false;
                            break;
                        default:
                            // A pattern must not start with a separator (but
                            // may end with).
                            if (!(bY || bM || bD))
                                bValid = false;
                            bSep = true;
                    }
                    nChar += i-j;
                }
                // At least one of Y,M,D
                bValid &= (bY || bM || bD);
            }
        }
        if (rbModified)
            rPatterns = aBuf.makeStringAndClear();
    }
    return bValid;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
