    /* -*- 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 <com/sun/star/container/XNameAccess.hpp>
    #include <com/sun/star/util/XFlushable.hpp>

    #include <com/sun/star/beans/PropertyValue.hpp>

    #include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
    #include <unotools/resmgr.hxx>
    #include <tools/urlobj.hxx>
    #include <svtools/headbar.hxx>
    #include <unotools/streamwrap.hxx>
    #include <unotools/pathoptions.hxx>
    #include <osl/file.hxx>
    #include <o3tl/enumrange.hxx>
    #include <vcl/builderfactory.hxx>
    #include <vcl/svapp.hxx>
    #include <vcl/weld.hxx>
    #include <sfx2/filedlghelper.hxx>
    #include <svtools/treelistentry.hxx>

    #include <rtl/uri.hxx>

    #include <algorithm>
    #include <memory>

    #include <strings.hrc>
    #include "xmlfiltersettingsdialog.hxx"
    #include "xmlfiltertabdialog.hxx"
    #include "xmlfiltertestdialog.hxx"
    #include "xmlfilterjar.hxx"
    #include <strings.hxx>

    using namespace osl;
    using namespace com::sun::star::lang;
    using namespace com::sun::star::uno;
    using namespace com::sun::star::io;
    using namespace com::sun::star::container;
    using namespace com::sun::star::beans;
    using namespace com::sun::star::util;

    using ::rtl::Uri;

    OUString XsltResId(const char* pId)
    {
        return Translate::get(pId, Translate::Create("flt"));
    }

    XMLFilterSettingsDialog::XMLFilterSettingsDialog(vcl::Window* pParent,
        const css::uno::Reference<css::uno::XComponentContext>& rxContext,
        Dialog::InitFlag eFlag)
        : ModelessDialog(pParent, "XMLFilterSettingsDialog", "filter/ui/xmlfiltersettings.ui", eFlag)
        , mxContext( rxContext )
        , m_bIsClosable(true)
        , m_sTemplatePath("$(user)/template/")
        , m_sDocTypePrefix("doctype:")
    {
        get(m_pCtrlFilterList, "filterlist");
        get(m_pPBNew, "new");
        get(m_pPBEdit, "edit");
        get(m_pPBTest, "test");
        get(m_pPBDelete, "delete");
        get(m_pPBSave, "save");
        get(m_pPBOpen, "open");
        get(m_pPBClose, "close");

        m_pFilterListBox = m_pCtrlFilterList->getListBox();
        m_pFilterListBox->SetSelectHdl( LINK( this, XMLFilterSettingsDialog, SelectionChangedHdl_Impl ) );
        m_pFilterListBox->SetDeselectHdl( LINK( this, XMLFilterSettingsDialog, SelectionChangedHdl_Impl ) );
        m_pFilterListBox->SetDoubleClickHdl( LINK( this, XMLFilterSettingsDialog, DoubleClickHdl_Impl ) );
        m_pFilterListBox->SetAccessibleName(XsltResId(STR_XML_FILTER_LISTBOX));
        m_pFilterListBox->SetHelpId(m_pCtrlFilterList->GetHelpId());

        m_pPBNew->SetClickHdl(LINK( this, XMLFilterSettingsDialog, ClickHdl_Impl ) );
        m_pPBEdit->SetClickHdl(LINK( this, XMLFilterSettingsDialog, ClickHdl_Impl ) );
        m_pPBTest->SetClickHdl(LINK( this, XMLFilterSettingsDialog, ClickHdl_Impl ) );
        m_pPBDelete->SetClickHdl(LINK( this, XMLFilterSettingsDialog, ClickHdl_Impl ) );
        m_pPBSave->SetClickHdl(LINK( this, XMLFilterSettingsDialog, ClickHdl_Impl ) );
        m_pPBOpen->SetClickHdl(LINK( this, XMLFilterSettingsDialog, ClickHdl_Impl ) );
        m_pPBClose->SetClickHdl(LINK( this, XMLFilterSettingsDialog, ClickHdl_Impl ) );

        try
        {
            mxFilterContainer.set( rxContext->getServiceManager()->createInstanceWithContext( "com.sun.star.document.FilterFactory", rxContext ), UNO_QUERY );
            mxTypeDetection.set( rxContext->getServiceManager()->createInstanceWithContext( "com.sun.star.document.TypeDetection", rxContext ), UNO_QUERY );
            mxExtendedTypeDetection.set( rxContext->getServiceManager()->createInstanceWithContext( "com.sun.star.document.ExtendedTypeDetectionFactory", rxContext ), UNO_QUERY );

            SvtPathOptions aOptions;
            m_sTemplatePath = aOptions.SubstituteVariable( m_sTemplatePath );
        }
        catch(const Exception&)
        {
            OSL_FAIL( "XMLFilterSettingsDialog::XMLFilterSettingsDialog exception caught!" );
        }
    }

    XMLFilterSettingsDialog::~XMLFilterSettingsDialog()
    {
        disposeOnce();
    }

    void XMLFilterSettingsDialog::dispose()
    {
        m_pFilterListBox.clear();
        m_pCtrlFilterList.clear();
        m_pPBNew.clear();
        m_pPBEdit.clear();
        m_pPBTest.clear();
        m_pPBDelete.clear();
        m_pPBSave.clear();
        m_pPBOpen.clear();
        m_pPBClose.clear();
        ModelessDialog::dispose();
    }

    IMPL_LINK(XMLFilterSettingsDialog, ClickHdl_Impl, Button *, pButton, void )
    {
        m_bIsClosable = false;

        if (m_pPBNew == pButton)
        {
            onNew();
        }
        else if (m_pPBEdit == pButton)
        {
            onEdit();
        }
        else if (m_pPBTest == pButton)
        {
            onTest();
        }
        else if (m_pPBDelete == pButton)
        {
            onDelete();
        }
        else if (m_pPBSave == pButton)
        {
            onSave();
        }
        else if (m_pPBOpen == pButton)
        {
            onOpen();
        }
        else if (m_pPBClose == pButton)
        {
            Close();
        }

        m_bIsClosable = true;
    }

    IMPL_LINK_NOARG(XMLFilterSettingsDialog, SelectionChangedHdl_Impl, SvTreeListBox*, void)
    {
        updateStates();
    }

    IMPL_LINK_NOARG(XMLFilterSettingsDialog, DoubleClickHdl_Impl, SvTreeListBox*, bool)
    {
        onEdit();
        return false;
    }

    short XMLFilterSettingsDialog::Execute()
    {
        m_pCtrlFilterList->GrabFocus();
        disposeFilterList();
        m_pFilterListBox->Clear();
        initFilterList();
        updateStates();

        return ModelessDialog::Execute();
    }

    void XMLFilterSettingsDialog::updateStates()
    {
        SvTreeListEntry* pSelectedEntry = m_pFilterListBox->FirstSelected();

        bool bHasSelection = pSelectedEntry != nullptr;

        bool bMultiSelection = bHasSelection && (m_pFilterListBox->NextSelected( pSelectedEntry ) != nullptr );
        bool bIsReadonly = false;
        bool bIsDefault = false;
        if(pSelectedEntry)
        {
            filter_info_impl* pInfo = static_cast<filter_info_impl*>(pSelectedEntry->GetUserData());
            bIsReadonly = pInfo->mbReadonly;

            for( auto nFact : o3tl::enumrange<SvtModuleOptions::EFactory>())
            {
                OUString sDefault = maModuleOpt.GetFactoryDefaultFilter(nFact);
                if( sDefault == pInfo->maFilterName )
                {
                    bIsDefault = true;
                    break;
                }
            }
        }
        m_pPBEdit->Enable( bHasSelection && !bMultiSelection && !bIsReadonly);
        m_pPBTest->Enable( bHasSelection && !bMultiSelection );
        m_pPBDelete->Enable( bHasSelection && !bMultiSelection && !bIsReadonly && !bIsDefault);
        m_pPBSave->Enable( bHasSelection );
    }

    /** is called when the user clicks on the "New" button */
    void XMLFilterSettingsDialog::onNew()
    {
        filter_info_impl aTempInfo;

        // create a unique filter name
        aTempInfo.maFilterName = createUniqueFilterName(XsltResId(STR_DEFAULT_FILTER_NAME));

        // init default extension
        aTempInfo.maExtension = STR_DEFAULT_EXTENSION;

        // set default ui name
        aTempInfo.maInterfaceName = createUniqueInterfaceName(XsltResId(STR_DEFAULT_UI_NAME));

        // set default application
        aTempInfo.maDocumentService = "com.sun.star.text.TextDocument";

        // execute XML Filter Dialog
        XMLFilterTabDialog aDlg(GetFrameWeld(), mxContext, &aTempInfo);
        if (aDlg.run() == RET_OK)
        {
            // insert the new filter
            insertOrEdit( aDlg.getNewFilterInfo() );
        }
    }

    /** is called when the user clicks on the "Edit" Button */
    void XMLFilterSettingsDialog::onEdit()
    {
        // get selected filter entry
        SvTreeListEntry* pEntry = m_pFilterListBox->FirstSelected();
        if( pEntry )
        {
            // get its filter info
            filter_info_impl* pOldInfo = static_cast<filter_info_impl*>(pEntry->GetUserData());

            // execute XML Filter Dialog
            XMLFilterTabDialog aDlg(GetFrameWeld(), mxContext, pOldInfo);
            if (aDlg.run() == RET_OK)
            {
                filter_info_impl* pNewInfo = aDlg.getNewFilterInfo();

                if( !(*pOldInfo == *pNewInfo) )
                {
                    // change filter
                    insertOrEdit( pNewInfo, pOldInfo );
                }
            }
        }
    }

    /** helper to create a sequence of strings from an extensions strings
        "ext1;ext2;ext3" will become { "ext1", "ext2", "ext3" } */
    static Sequence< OUString > createExtensionsSequence( const OUString& rExtensions )
    {
        // first count how many extensions we have inside the string
        int nExtensions = 0;

        int nLength = rExtensions.getLength();
        if( nLength )
        {
            // a non empty string has at least one extension
            nExtensions++;

            // now count the delimiters ';'
            const sal_Unicode * pString = rExtensions.getStr();
            int i;
            for( i = 0; i < nLength; i++, pString++ )
            {
                if( *pString == ';' )
                    nExtensions++;
            }
        }

        Sequence< OUString > aExtensions( nExtensions );

        // extract the extensions from the source string and fill the sequence

        int nLastIndex = 0;
        int nCurrentIndex = 0;
        int i;

        for( i = 0; i < nExtensions; i++ )
        {
            nLastIndex = rExtensions.indexOf( ';', nLastIndex );

            if( nLastIndex == -1 )
            {
                aExtensions[i] = rExtensions.copy( nCurrentIndex );
                break;
            }
            else
            {
                aExtensions[i] = rExtensions.copy( nCurrentIndex, nLastIndex - nCurrentIndex );
                nCurrentIndex = nLastIndex + 1;
                nLastIndex = nCurrentIndex;
            }
        }

        return aExtensions;
    }

    /** checks if the given name is unique inside the filter factory. If not,
        numbers are added until the returned name is unique */
    OUString XMLFilterSettingsDialog::createUniqueFilterName( const OUString& rFilterName )
    {
        OUString aFilterName( rFilterName );

        sal_Int32 nId = 2;

        while( mxFilterContainer->hasByName( aFilterName ) )
        {
            aFilterName = rFilterName + " " + OUString::number( nId++ );
        }

        return aFilterName;
    }

    /** checks if the given name is unique inside the type detection. If not,
        numbers are added until the returned name is unique */
    OUString XMLFilterSettingsDialog::createUniqueTypeName( const OUString& rTypeName )
    {
        OUString aTypeName( rTypeName );

        sal_Int32 nId = 2;

        while( mxFilterContainer->hasByName( aTypeName ) )
        {
            aTypeName = rTypeName + " " + OUString::number( nId++ );
        }

        return aTypeName;
    }

    /** checks if the given name is a unique ui name inside the filter factory. If not,
        numbers are added until the returned name is unique */
    OUString XMLFilterSettingsDialog::createUniqueInterfaceName( const OUString& rInterfaceName )
    {
        sal_Int32 nDefaultNumber = 0;

        try
        {
            Sequence< OUString > aFilterNames( mxFilterContainer->getElementNames() );

            Sequence< PropertyValue > aValues;
            for( OUString const & filterName : aFilterNames)
            {
                Any aAny( mxFilterContainer->getByName( filterName ) );
                if( !(aAny >>= aValues) )
                    continue;

                const sal_Int32 nValueCount( aValues.getLength() );
                PropertyValue* pValues = aValues.getArray();
                sal_Int32 nValue;

                for( nValue = 0; nValue < nValueCount; nValue++, pValues++ )
                {
                    if ( pValues->Name == "UIName" )
                    {
                        OUString aInterfaceName;
                        pValues->Value >>= aInterfaceName;

                        // see if this filter matches our default filter name
                        if( aInterfaceName.match( rInterfaceName ) )
                        {
                            // if yes, make sure we generate a unique name with a higher number
                            // this is dump but fast
                            sal_Int32 nNumber = aInterfaceName.copy( rInterfaceName.getLength() ).toInt32();
                            if( nNumber >= nDefaultNumber )
                                nDefaultNumber = nNumber + 1;
                        }
                    }
                }
            }
        }
        catch( const Exception& )
        {
            OSL_FAIL( "XMLFilterSettingsDialog::createUniqueInterfaceName exception caught!" );
        }

        OUString aInterfaceName( rInterfaceName );
        if( nDefaultNumber )
        {
            aInterfaceName += " " + OUString::number( nDefaultNumber );
        }

        return aInterfaceName;
    }

    /** inserts a new filter into the ui and configuration if pOldInfo is NULL.
        If pOldInfo is not null, the old filter will be replaced with the new settings */
    bool XMLFilterSettingsDialog::insertOrEdit( filter_info_impl* pNewInfo, const filter_info_impl* pOldInfo )
    {
        bool bOk = true;

        if( pOldInfo )
        {
            // see if we need to update the type name
            if( pOldInfo->maFilterName != pNewInfo->maFilterName )
            {
                if( pOldInfo->maType == pOldInfo->maFilterName )
                {
                    pNewInfo->maType.clear();
                }
            }

            // see if we need to clean up old stuff first
            try
            {
                // if filter name changed, we need to remove the old filter first
                if( pOldInfo->maFilterName != pNewInfo->maFilterName )
                    mxFilterContainer->removeByName( pOldInfo->maFilterName );

                // if type name changed, we need to remove the old type first
                if( pOldInfo->maType != pNewInfo->maType )
                    mxTypeDetection->removeByName( pOldInfo->maType );
            }
            catch( const Exception& )
            {
                OSL_FAIL( "XMLFilterSettingsDialog::insertOrEdit exception caught!" );
                bOk = false;
            }
        }

        filter_info_impl* pFilterEntry( nullptr );

        if( bOk )
        {
            // create or copy filter info
            if( pOldInfo )
            {
                // change existing filter entry in filter list box
                pFilterEntry = const_cast<filter_info_impl*>(pOldInfo);
                *pFilterEntry = *pNewInfo;
            }
            else
            {
                // add new entry to filter list box
                pFilterEntry = new filter_info_impl( *pNewInfo );
            }
        }

        // check if we need to copy the template
        if( bOk && !pFilterEntry->maImportTemplate.isEmpty() )
        {
            if( !pFilterEntry->maImportTemplate.matchIgnoreAsciiCase( m_sTemplatePath ) )
            {
                INetURLObject aSourceURL( pFilterEntry->maImportTemplate );
                if( !aSourceURL.GetName().isEmpty() )
                {
                    OUString aDestURL( m_sTemplatePath );
                    aDestURL += pFilterEntry->maFilterName + "/";
                    if( createDirectory( aDestURL ) )
                    {
                        aDestURL += aSourceURL.GetName();

                        SvFileStream aInputStream(pFilterEntry->maImportTemplate, StreamMode::READ );
                        Reference< XInputStream > xIS( new utl::OInputStreamWrapper( aInputStream ) );
                        SvFileStream aOutputStream(aDestURL, StreamMode::WRITE );
                        Reference< XOutputStream > xOS(  new utl::OOutputStreamWrapper( aOutputStream ) );

                        if( copyStreams( xIS, xOS ) )
                            pFilterEntry->maImportTemplate = aDestURL;
                    }
                }
            }
        }

        if( bOk )
        {
            if( pFilterEntry->maType.isEmpty() )
            {
                pFilterEntry->maType = createUniqueTypeName( pNewInfo->maFilterName );
            }

            // update import/export flags
            if( !pFilterEntry->maImportXSLT.isEmpty() )
            {
                pFilterEntry->maFlags |= 1;
            }
            else
            {
                pFilterEntry->maFlags &= ~1;
            }

            if( !pFilterEntry->maExportXSLT.isEmpty() )
            {
                pFilterEntry->maFlags |= 2;
            }
            else
            {
                pFilterEntry->maFlags &= ~2;
            }
            pFilterEntry->maFlags |= 0x80040;

            // 2. create user data for filter entry
            Sequence< OUString > aUserData( pFilterEntry->getFilterUserData());

            // 3. create property values for filter entry
            Sequence< PropertyValue > aFilterData( 8 );

            aFilterData[0].Name = "Type";
            aFilterData[0].Value <<= pFilterEntry->maType;

            aFilterData[1].Name = "UIName";
            aFilterData[1].Value <<= pFilterEntry->maInterfaceName;

            aFilterData[2].Name = "DocumentService";
            aFilterData[2].Value <<= pFilterEntry->maDocumentService;

            aFilterData[3].Name = "FilterService";
            aFilterData[3].Value <<= OUString( "com.sun.star.comp.Writer.XmlFilterAdaptor" );

            aFilterData[4].Name = "Flags";
            aFilterData[4].Value <<= pFilterEntry->maFlags;

            aFilterData[5].Name = "UserData";
            aFilterData[5].Value <<= aUserData;

            aFilterData[6].Name = "FileFormatVersion";
            aFilterData[6].Value <<= pFilterEntry->maFileFormatVersion;

            aFilterData[7].Name = "TemplateName";
            aFilterData[7].Value <<= pFilterEntry->maImportTemplate;

            // 4. insert new or replace existing filter
            try
            {
                Any aAny( makeAny( aFilterData ) );
                if( mxFilterContainer->hasByName( pFilterEntry->maFilterName ) )
                {
                    mxFilterContainer->replaceByName( pFilterEntry->maFilterName, aAny );
                }
                else
                {
                    mxFilterContainer->insertByName( pFilterEntry->maFilterName, aAny );
                }
            }
            catch( const Exception& )
            {
                OSL_FAIL( "XMLFilterSettingsDialog::insertOrEdit exception caught!" );
                bOk = false;
            }
        }

        // 5. prepare type information
        if( bOk )
        {
            Sequence< PropertyValue > aValues(4);

            aValues[0].Name = "UIName";
            aValues[0].Value <<= pFilterEntry->maInterfaceName;
            aValues[1].Name = "ClipboardFormat";
            OUString aDocType;
            if( !pFilterEntry->maDocType.match( m_sDocTypePrefix ) )
            {
                aDocType = m_sDocTypePrefix + pFilterEntry->maDocType;
            }
            else
            {
                aDocType = pFilterEntry->maDocType;
            }
            if (aDocType == m_sDocTypePrefix)
                aValues[1].Value <<= OUString();
            else
                aValues[1].Value <<= aDocType;

            aValues[2].Name = "DocumentIconID";
            aValues[2].Value <<= pFilterEntry->mnDocumentIconID;

            aValues[3].Name = "Extensions";
            aValues[3].Value <<= createExtensionsSequence( pFilterEntry->maExtension );

            // the detect service will only be registered, if a doctype/search token was specified
            if (aDocType.getLength() > m_sDocTypePrefix.getLength())
            {
                aValues.realloc(5);
                aValues[4].Name = "DetectService";
                aValues[4].Value <<= OUString( "com.sun.star.comp.filters.XMLFilterDetect" );
            }

            // 6. insert new or replace existing type information
            if( mxTypeDetection.is() )
            {
                try
                {
                    Any aAny( makeAny( aValues ) );
                    if( mxTypeDetection->hasByName( pFilterEntry->maType ) )
                    {
                        mxTypeDetection->replaceByName( pFilterEntry->maType, aAny );
                    }
                    else
                    {
                        mxTypeDetection->insertByName( pFilterEntry->maType, aAny );
                    }
                }
                catch( const Exception& )
                {
                    OSL_FAIL( "XMLFilterSettingsDialog::insertOrEdit exception caught!" );
                    bOk = false;
                }
            }

            if( bOk )
            {
                try
                {
                    Reference< XFlushable > xFlushable( mxTypeDetection, UNO_QUERY );
                    if( xFlushable.is() )
                        xFlushable->flush();
                }
                catch( const Exception& )
                {
                    OSL_FAIL( "XMLFilterSettingsDialog::insertOrEdit exception caught!" );
                    bOk = false;
                }
            }

            if( !bOk )
            {
                // we failed to add the type, so lets remove the filter
                try
                {
                    mxFilterContainer->removeByName( pFilterEntry->maFilterName );
                }
                catch( const Exception& )
                {
                    OSL_FAIL( "XMLFilterSettingsDialog::insertOrEdit exception caught!" );
                    bOk = false;
                }
            }
            else
            {
                if( bOk )
                {
                    try
                    {
                        Reference< XFlushable > xFlushable( mxFilterContainer, UNO_QUERY );
                        if( xFlushable.is() )
                            xFlushable->flush();
                    }
                    catch( const Exception& )
                    {
                        OSL_FAIL( "XMLFilterSettingsDialog::insertOrEdit exception caught!" );
                        bOk = false;
                    }

                    if( !bOk )
                    {
                        // we failed to add the filter, so lets remove the type
                        try
                        {
                            mxTypeDetection->removeByName( pFilterEntry->maType );
                        }
                        catch( const Exception& )
                        {
                            OSL_FAIL( "XMLFilterSettingsDialog::insertOrEdit exception caught!" );
                        }
                    }

                }
            }
        }

        if( bOk )
        {
            if( mxExtendedTypeDetection.is() )
            {
                OUString sFilterDetectService( "com.sun.star.comp.filters.XMLFilterDetect" );
                if( mxExtendedTypeDetection->hasByName( sFilterDetectService ) )
                {
                    Sequence< PropertyValue > aSequence;
                    if( mxExtendedTypeDetection->getByName( sFilterDetectService ) >>= aSequence )
                    {
                        sal_Int32 nCount = aSequence.getLength();
                        sal_Int32 nIndex;
                        for( nIndex = 0; nIndex < nCount; nIndex++ )
                        {
                            if ( aSequence[nIndex].Name == "Types" )
                            {
                                Sequence< OUString > aTypes;
                                if( aSequence[nIndex].Value >>= aTypes )
                                {
                                    sal_Int32 nStrCount = aTypes.getLength();
                                    sal_Int32 nStr;
                                    for( nStr = 0; nStr < nStrCount; nStr++ )
                                    {
                                        if( aTypes[nStr] == pFilterEntry->maType )
                                            break;
                                    }

                                    if( nStr == nStrCount )
                                    {
                                        aTypes.realloc( nStrCount + 1 );
                                        aTypes[nStrCount] = pFilterEntry->maType;

                                        aSequence[nIndex].Value <<= aTypes;

                                        mxExtendedTypeDetection->replaceByName( sFilterDetectService, makeAny( aSequence ) );

                                        Reference< XFlushable > xFlushable( mxExtendedTypeDetection, UNO_QUERY );
                                        if( xFlushable.is() )
                                            xFlushable->flush();
                                    }
                                }

                                break;
                            }
                        }
                    }
                }
            }
        }

        // update ui
        if( bOk )
        {
            if( pOldInfo )
            {
                m_pFilterListBox->changeEntry( pFilterEntry );
            }
            else
            {
                m_pFilterListBox->addFilterEntry( pFilterEntry );
                maFilterVector.push_back( std::unique_ptr<filter_info_impl>(pFilterEntry) );
            }
        }

        return bOk;
    }

    /** is called when the user clicks the "Test" button */
    void XMLFilterSettingsDialog::onTest()
    {
        // get the first selected filter
        SvTreeListEntry* pEntry = m_pFilterListBox->FirstSelected();
        if( pEntry )
        {
            filter_info_impl* pInfo = static_cast<filter_info_impl*>(pEntry->GetUserData());

            XMLFilterTestDialog aDlg(GetFrameWeld(), mxContext);
            aDlg.test( *pInfo );
        }
    }

    void XMLFilterSettingsDialog::onDelete()
    {
        SvTreeListEntry* pEntry = m_pFilterListBox->FirstSelected();
        if( pEntry )
        {
            filter_info_impl* pInfo = static_cast<filter_info_impl*>(pEntry->GetUserData());

            OUString aMessage(XsltResId(STR_WARN_DELETE));
            aMessage = aMessage.replaceFirst( "%s", pInfo->maFilterName );

            std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(GetFrameWeld(),
                                                       VclMessageType::Warning, VclButtonsType::YesNo,
                                                       aMessage));
            xWarn->set_default_response(RET_YES);
            if (xWarn->run() == RET_YES)
            {
                try
                {
                    if( mxFilterContainer->hasByName( pInfo->maFilterName ) )
                    {
                        mxFilterContainer->removeByName( pInfo->maFilterName );

                        bool bTypeStillUsed = false;

                        // now loop over all filter and see if someone else uses the same type
                        Sequence< OUString > aFilterNames( mxFilterContainer->getElementNames() );
                        OUString* pFilterName = aFilterNames.getArray();

                        const sal_Int32 nCount = aFilterNames.getLength();
                        sal_Int32 nFilter;
                        Sequence< PropertyValue > aValues;

                        for( nFilter = 0; (nFilter < nCount) && !bTypeStillUsed; nFilter++, pFilterName++ )
                        {
                            Any aAny( mxFilterContainer->getByName( *pFilterName ) );
                            if( !(aAny >>= aValues) )
                                continue;

                            const sal_Int32 nValueCount( aValues.getLength() );
                            PropertyValue* pValues = aValues.getArray();
                            sal_Int32 nValue;

                            for( nValue = 0; (nValue < nValueCount) && !bTypeStillUsed; nValue++, pValues++ )
                            {
                                if ( pValues->Name == "Type" )
                                {
                                    OUString aType;
                                    pValues->Value >>= aType;
                                    if( aType == pInfo->maType )
                                        bTypeStillUsed = true;

                                    break;
                                }
                            }
                        }

                        // if the type is not used anymore, remove it also
                        if( !bTypeStillUsed )
                        {
                            if( mxTypeDetection->hasByName( pInfo->maType ) )
                            {
                                mxTypeDetection->removeByName( pInfo->maType );
                            }
                        }

                        Reference< XFlushable > xFlushable( mxFilterContainer, UNO_QUERY );
                        if( xFlushable.is() )
                            xFlushable->flush();

                        xFlushable.set( mxTypeDetection, UNO_QUERY );
                        if( xFlushable.is() )
                            xFlushable->flush();

                        // now remove entry from ui
                        m_pFilterListBox->RemoveSelection();

                        // and delete the filter entry
                        maFilterVector.erase(std::find_if( maFilterVector.begin(), maFilterVector.end(),
                                                [&] (std::unique_ptr<filter_info_impl> const & p)
                                                { return p.get() == pInfo; }));
                    }
                }
                catch( const Exception& )
                {
                    OSL_FAIL( "XMLFilterSettingsDialog::onDelete exception caught!" );
                }
            }
        }

        updateStates();
    }

    void XMLFilterSettingsDialog::onSave()
    {
        XMLFilterVector aFilters;

        int nFilters = 0;

        SvTreeListEntry* pEntry = m_pFilterListBox->FirstSelected();
        while( pEntry )
        {
            filter_info_impl* pInfo = static_cast<filter_info_impl*>(pEntry->GetUserData());
            aFilters.push_back( pInfo );
            pEntry = m_pFilterListBox->NextSelected( pEntry );
            nFilters++;
        }

        // Open Fileopen-Dialog
           ::sfx2::FileDialogHelper aDlg(
            css::ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION,
            FileDialogFlags::NONE, GetFrameWeld());

        OUString aExtensions( "*.jar" );
        OUString aFilterName(XsltResId(STR_FILTER_PACKAGE));
        aFilterName += " (" + aExtensions + ")";

        aDlg.AddFilter( aFilterName, aExtensions );

        if ( aDlg.Execute() == ERRCODE_NONE )
        {
            XMLFilterJarHelper aJarHelper( mxContext );
            aJarHelper.savePackage( aDlg.GetPath(), aFilters );

            INetURLObject aURL( aDlg.GetPath() );

            OUString sPlaceholder( "%s" );

            OUString aMsg;
            if( nFilters > 0 )
            {
                aMsg = XsltResId(STR_FILTERS_HAVE_BEEN_SAVED);
                aMsg = aMsg.replaceFirst( sPlaceholder, OUString::number( nFilters ) );
                aMsg = aMsg.replaceFirst( sPlaceholder, aURL.GetName() );
            }
            else
            {
                aMsg = XsltResId(STR_FILTER_HAS_BEEN_SAVED);
                aMsg = aMsg.replaceFirst( sPlaceholder, (*aFilters.begin())->maFilterName );
                aMsg = aMsg.replaceFirst( sPlaceholder, aURL.GetName() );
            }

            std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetFrameWeld(),
                                                          VclMessageType::Info, VclButtonsType::Ok,
                                                          aMsg));
            xInfoBox->run();
        }
    }

    void XMLFilterSettingsDialog::onOpen()
    {
        XMLFilterVector aFilters;

        // Open Fileopen-Dialog
           ::sfx2::FileDialogHelper aDlg(
            css::ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE,
            FileDialogFlags::NONE, GetFrameWeld());

        OUString aExtensions( "*.jar" );
        OUString aFilterName(XsltResId(STR_FILTER_PACKAGE));
        aFilterName += " (" + aExtensions + ")";

        aDlg.AddFilter( aFilterName, aExtensions );

        if ( aDlg.Execute() == ERRCODE_NONE )
        {
            OUString aURL( aDlg.GetPath() );

            XMLFilterJarHelper aJarHelper( mxContext );
            aJarHelper.openPackage( aURL, aFilters );

            int nFilters = 0;
            for (auto const& filter : aFilters)
            {
                if( insertOrEdit(filter) )
                {
                    aFilterName = filter->maFilterName;
                    nFilters++;
                }

                delete filter;
            }

            disposeFilterList();
            initFilterList();

            OUString sPlaceholder( "%s" );
            OUString aMsg;
            if( nFilters == 0 )
            {
                INetURLObject aURLObj( aURL );
                aMsg = XsltResId(STR_NO_FILTERS_FOUND);
                aMsg = aMsg.replaceFirst( sPlaceholder, aURLObj.GetName() );
            }
            else if( nFilters == 1 )
            {
                aMsg = XsltResId(STR_FILTER_INSTALLED);
                aMsg = aMsg.replaceFirst( sPlaceholder, aFilterName );

            }
            else
            {
                aMsg = XsltResId(STR_FILTERS_INSTALLED);
                aMsg = aMsg.replaceFirst( sPlaceholder, OUString::number( nFilters ) );
            }

            std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetFrameWeld(),
                                                          VclMessageType::Info, VclButtonsType::Ok,
                                                          aMsg));
            xInfoBox->run();
        }
    }

    bool XMLFilterSettingsDialog::EventNotify( NotifyEvent& rNEvt )
    {
        // Because of tab control first call the base class.
        bool bRet = ModelessDialog::EventNotify(rNEvt);
        if ( !bRet )
        {
            if ( rNEvt.GetType() == MouseNotifyEvent::KEYINPUT )
            {
                const KeyEvent* pKEvt = rNEvt.GetKeyEvent();
                vcl::KeyCode aKeyCode = pKEvt->GetKeyCode();
                sal_uInt16 nKeyCode = aKeyCode.GetCode();
                bool bMod1 = pKEvt->GetKeyCode().IsMod1();

                if( nKeyCode == KEY_ESCAPE || (bMod1 && (nKeyCode == KEY_W)))
                {
                    Close();
                    return true;
                }
            }
        }

        return bRet;
    }

    void XMLFilterSettingsDialog::disposeFilterList()
    {
        maFilterVector.clear();
        m_pFilterListBox->Clear();
    }

    void XMLFilterSettingsDialog::initFilterList()
    {
        if( mxFilterContainer.is() )
        {
            Sequence< OUString > aFilterNames( mxFilterContainer->getElementNames() );

            Sequence< PropertyValue > aValues;

            std::unique_ptr<filter_info_impl> pTempFilter( new filter_info_impl );
            Sequence< OUString > aUserData;

            for( OUString const & filterName : aFilterNames )
            {
                aUserData.realloc(0);

                try
                {
                    Any aAny( mxFilterContainer->getByName( filterName ) );
                    if( !(aAny >>= aValues) )
                        continue;

                    OUString aFilterService;
                    pTempFilter->maFilterName = filterName;

                    const sal_Int32 nValueCount( aValues.getLength() );
                    PropertyValue* pValues = aValues.getArray();
                    sal_Int32 nValue;

                    for( nValue = 0; nValue < nValueCount; nValue++, pValues++ )
                    {
                        if ( pValues->Name == "Type" )
                        {
                            pValues->Value >>= pTempFilter->maType;
                        }
                        else if ( pValues->Name == "UIName" )
                        {
                            pValues->Value >>= pTempFilter->maInterfaceName;
                        }
                        else if ( pValues->Name == "DocumentService" )
                        {
                            pValues->Value >>= pTempFilter->maDocumentService;
                        }
                        else if ( pValues->Name == "FilterService" )
                        {
                            pValues->Value >>= aFilterService;
                        }
                        else if ( pValues->Name == "Flags" )
                        {
                            pValues->Value >>= pTempFilter->maFlags;
                        }
                        else if ( pValues->Name == "UserData" )
                        {
                            pValues->Value >>= aUserData;
                        }
                        else if ( pValues->Name == "FileFormatVersion" )
                        {
                            pValues->Value >>= pTempFilter->maFileFormatVersion;
                        }
                        else if ( pValues->Name == "TemplateName" )
                        {
                            pValues->Value >>= pTempFilter->maImportTemplate;
                        }
                        else if ( pValues->Name == "Finalized" )
                        {
                            pValues->Value >>= pTempFilter->mbReadonly;
                        }
                    }

                    // if this is not a XmlFilterAdaptor entry, skip it
                    if( aFilterService != "com.sun.star.comp.Writer.XmlFilterAdaptor" )
                        continue;

                    // if we don't have the needed user data, skip it
                    if( aUserData.getLength() < 6 )
                        continue;

                    // if this is not an XSLTFilter entry, skip it
                    if( aUserData[0] != "com.sun.star.documentconversion.XSLTFilter" )
                        continue;

                    // get filter information from userdata
                    pTempFilter->mbNeedsXSLT2 = aUserData[1].toBoolean();
                    pTempFilter->maImportService = aUserData[2];
                    pTempFilter->maExportService = aUserData[3];
                    pTempFilter->maImportXSLT = aUserData[4];
                    pTempFilter->maExportXSLT = aUserData[5];
                    if( aUserData.getLength() >= 8 )
                        pTempFilter->maComment = aUserData[7];

                    // get type information
                    if( mxTypeDetection.is() )
                    {
                        try
                        {
                            aAny = mxTypeDetection->getByName( pTempFilter->maType );
                            Sequence< PropertyValue > aValues2;

                            if( aAny >>= aValues2 )
                            {
                                const sal_Int32 nValueCount2( aValues2.getLength() );
                                PropertyValue* pValues2 = aValues2.getArray();
                                sal_Int32 nValue2;

                                for( nValue2 = 0; nValue2 < nValueCount2; nValue2++, pValues2++ )
                                {
                                    if ( pValues2->Name == "ClipboardFormat" )
                                    {
                                        OUString aDocType;
                                        pValues2->Value >>= aDocType;

                                        if( aDocType.match( m_sDocTypePrefix ) )
                                            aDocType = aDocType.copy( m_sDocTypePrefix.getLength() );

                                        pTempFilter->maDocType = aDocType;
                                    }
                                    else if ( pValues2->Name == "Extensions" )
                                    {
                                        Sequence< OUString > aExtensions;
                                        if( pValues2->Value >>= aExtensions )
                                        {
                                            pTempFilter->maExtension.clear();

                                            sal_Int32 nCount3( aExtensions.getLength() );
                                            OUString* pExtensions = aExtensions.getArray();
                                            sal_Int32 n;
                                            for( n = 0; n < nCount3; n++ )
                                            {
                                                if( n > 0 )
                                                    pTempFilter->maExtension += ";";
                                                pTempFilter->maExtension += *pExtensions++;
                                            }
                                        }
                                    }
                                    else if ( pValues2->Name == "DocumentIconID" )
                                    {
                                        pValues2->Value >>= pTempFilter->mnDocumentIconID;
                                    }
                                    else if ( pValues2->Name == "Finalized" )
                                    {
                                        // both the filter and the type may be finalized
                                        bool bTemp = false;
                                        pValues2->Value >>= bTemp;
                                        pTempFilter->mbReadonly |= bTemp;
                                    }
                                }
                            }
                        }
                        catch( const css::container::NoSuchElementException& )
                        {
                            OSL_FAIL( "Type not found, user error?" ); // TODO: error?
                        }
                    }

                    // add entry to internal container and to ui filter list box
                    maFilterVector.push_back( std::unique_ptr<filter_info_impl>(pTempFilter.get()) );
                    m_pFilterListBox->addFilterEntry( pTempFilter.release() );

                    pTempFilter.reset( new filter_info_impl );
                }
                catch( const Exception& )
                {
                    OSL_FAIL( "XMLFilterSettingsDialog::initFilterList exception caught!" );
                }

            }
        }

        SvTreeListEntry* pEntry = m_pFilterListBox->GetEntry( 0 );
        if( pEntry )
            m_pFilterListBox->Select( pEntry );
    }

    application_info_impl::application_info_impl( const sal_Char * pDocumentService, const OUString& rUINameRes, const sal_Char * mpXMLImporter, const sal_Char * mpXMLExporter )
    :   maDocumentService( pDocumentService, strlen( pDocumentService ), RTL_TEXTENCODING_ASCII_US ),
        maDocumentUIName(Translate::ExpandVariables(rUINameRes)),
        maXMLImporter( mpXMLImporter, strlen( mpXMLImporter ), RTL_TEXTENCODING_ASCII_US ),
        maXMLExporter( mpXMLExporter, strlen( mpXMLExporter ), RTL_TEXTENCODING_ASCII_US )
    {
    }

    std::vector< application_info_impl* >& getApplicationInfos()
    {
        static std::vector< application_info_impl* > aInfos;

        if( aInfos.empty() )
        {
            aInfos.push_back( new application_info_impl(
                "com.sun.star.text.TextDocument",
                STR_APPL_NAME_WRITER,
                "com.sun.star.comp.Writer.XMLImporter",
                "com.sun.star.comp.Writer.XMLExporter" ) );

            aInfos.push_back( new application_info_impl(
                "com.sun.star.sheet.SpreadsheetDocument",
                STR_APPL_NAME_CALC,
                "com.sun.star.comp.Calc.XMLImporter",
                "com.sun.star.comp.Calc.XMLExporter" ) );

            aInfos.push_back( new application_info_impl(
                "com.sun.star.presentation.PresentationDocument",
                STR_APPL_NAME_IMPRESS,
                "com.sun.star.comp.Impress.XMLImporter",
                "com.sun.star.comp.Impress.XMLExporter" ) );

            aInfos.push_back( new application_info_impl(
                "com.sun.star.drawing.DrawingDocument",
                STR_APPL_NAME_DRAW,
                "com.sun.star.comp.Draw.XMLImporter",
                "com.sun.star.comp.Draw.XMLExporter" ) );

            // --- oasis file formats...
            aInfos.push_back( new application_info_impl(
                "com.sun.star.text.TextDocument",
                STR_APPL_NAME_OASIS_WRITER,
                "com.sun.star.comp.Writer.XMLOasisImporter",
                "com.sun.star.comp.Writer.XMLOasisExporter" ) );

            aInfos.push_back( new application_info_impl(
                "com.sun.star.sheet.SpreadsheetDocument",
                STR_APPL_NAME_OASIS_CALC,
                "com.sun.star.comp.Calc.XMLOasisImporter",
                "com.sun.star.comp.Calc.XMLOasisExporter" ) );

            aInfos.push_back( new application_info_impl(
                "com.sun.star.presentation.PresentationDocument",
                STR_APPL_NAME_OASIS_IMPRESS,
                "com.sun.star.comp.Impress.XMLOasisImporter",
                "com.sun.star.comp.Impress.XMLOasisExporter" ) );

            aInfos.push_back( new application_info_impl(
                "com.sun.star.drawing.DrawingDocument",
                STR_APPL_NAME_OASIS_DRAW,
                "com.sun.star.comp.Draw.XMLOasisImporter",
                "com.sun.star.comp.Draw.XMLOasisExporter" ) );
        }

        return aInfos;
    }

    const application_info_impl* getApplicationInfo( const OUString& rServiceName )
    {
        std::vector< application_info_impl* >& rInfos = getApplicationInfos();
        for (auto const& info : rInfos)
        {
            if( rServiceName == info->maXMLExporter ||
                rServiceName == info->maXMLImporter)
            {
                return info;
            }
        }
        return nullptr;
    }

    OUString getApplicationUIName( const OUString& rServiceName )
    {
        const application_info_impl* pInfo = getApplicationInfo( rServiceName );
        if( pInfo )
        {
            return pInfo->maDocumentUIName;
        }
        else
        {
            OUString aRet = XsltResId(STR_UNKNOWN_APPLICATION);
            if( !rServiceName.isEmpty() )
            {
                aRet += " (" + rServiceName + ")";
            }
            return aRet;
        }
    }

    SvxPathControl::SvxPathControl(vcl::Window* pParent)
        : Window(pParent, WB_HIDE | WB_CLIPCHILDREN | WB_TABSTOP | WB_DIALOGCONTROL | WB_BORDER)
        , bHasBeenShown(false)
    {
        m_pVBox = VclPtr<VclVBox>::Create(this);

        m_pHeaderBar = VclPtr<HeaderBar>::Create(m_pVBox, WB_BOTTOMBORDER);
        m_pHeaderBar->set_height_request(GetTextHeight() + 6);

        m_pFocusCtrl = VclPtr<XMLFilterListBox>::Create(m_pVBox, this);
        m_pFocusCtrl->set_fill(true);
        m_pFocusCtrl->set_expand(true);

        m_pVBox->set_hexpand(true);
        m_pVBox->set_vexpand(true);
        m_pVBox->set_expand(true);
        m_pVBox->set_fill(true);
        m_pVBox->Show();
    }

    #define ITEMID_NAME     1
    #define ITEMID_TYPE     2

    void SvxPathControl::Resize()
    {
        Window::Resize();

        if (!m_pVBox)
            return;

        m_pVBox->SetSizePixel(GetSizePixel());

        if (!bHasBeenShown)
            bHasBeenShown = IsReallyShown();

        if (!bHasBeenShown)
        {
            std::vector<long> aWidths;
            m_pFocusCtrl->getPreferredDimensions(aWidths);
            if (aWidths.empty())
            {
                bHasBeenShown = false;
                return;
            }
            long nFirstColumnWidth = aWidths[1];
            m_pHeaderBar->SetItemSize(ITEMID_NAME, nFirstColumnWidth);
            m_pHeaderBar->SetItemSize(ITEMID_TYPE, 0xFFFF);
            long nTabs[] = {0, nFirstColumnWidth};
            m_pFocusCtrl->SetTabs(SAL_N_ELEMENTS(nTabs), nTabs, MapUnit::MapPixel);
        }
    }

    Size SvxPathControl::GetOptimalSize() const
    {
        Size aDefSize(LogicToPixel(Size(150, 0), MapMode(MapUnit::MapAppFont)));
        Size aOptSize(m_pVBox->GetOptimalSize());
        long nRowHeight(GetTextHeight());
        aOptSize.setHeight( nRowHeight * 10 );
        aOptSize.setWidth( std::max(aDefSize.Width(), aOptSize.Width()) );
        return aOptSize;
    }

    SvxPathControl::~SvxPathControl()
    {
        disposeOnce();
    }

    void SvxPathControl::dispose()
    {
        m_pFocusCtrl.disposeAndClear();
        m_pHeaderBar.disposeAndClear();
        m_pVBox.disposeAndClear();
        vcl::Window::dispose();
    }

    VCL_BUILDER_FACTORY(SvxPathControl)

    bool SvxPathControl::EventNotify(NotifyEvent& rNEvt)
    {
        bool bRet = Window::EventNotify(rNEvt);

        if ( m_pFocusCtrl && rNEvt.GetWindow() != m_pFocusCtrl && rNEvt.GetType() == MouseNotifyEvent::GETFOCUS )
            m_pFocusCtrl->GrabFocus();

        return bRet;
    }

    XMLFilterListBox::XMLFilterListBox(Window* pParent, SvxPathControl* pPathControl)
        : SvTabListBox(pParent, WB_SORT | WB_HSCROLL | WB_CLIPCHILDREN | WB_TABSTOP)
        , m_pHeaderBar(pPathControl->getHeaderBar())
    {
        Size aBoxSize( pParent->GetOutputSizePixel() );

        m_pHeaderBar->SetEndDragHdl( LINK( this, XMLFilterListBox, HeaderEndDrag_Impl ) );

        OUString aStr1(XsltResId(STR_COLUMN_HEADER_NAME));
        OUString aStr2(XsltResId(STR_COLUMN_HEADER_TYPE));

        long nTabSize = aBoxSize.Width() / 2;

        m_pHeaderBar->InsertItem( ITEMID_NAME, aStr1, nTabSize,
                                HeaderBarItemBits::LEFT | HeaderBarItemBits::VCENTER );
        m_pHeaderBar->InsertItem( ITEMID_TYPE, aStr2, nTabSize,
                                HeaderBarItemBits::LEFT | HeaderBarItemBits::VCENTER );

        static long nTabs[] = {0, nTabSize };

        SetSelectionMode( SelectionMode::Multiple );
        SetTabs( SAL_N_ELEMENTS(nTabs), nTabs, MapUnit::MapPixel );
        SetScrolledHdl( LINK( this, XMLFilterListBox, TabBoxScrollHdl_Impl ) );
        SetHighlightRange();
        Show();
        m_pHeaderBar->Show();
    }

    XMLFilterListBox::~XMLFilterListBox()
    {
        disposeOnce();
    }

    void XMLFilterListBox::dispose()
    {
        m_pHeaderBar.clear();
        SvTabListBox::dispose();
    }

    IMPL_LINK_NOARG( XMLFilterListBox, TabBoxScrollHdl_Impl, SvTreeListBox*, void )
    {
        m_pHeaderBar->SetOffset( -GetXOffset() );
    }

    IMPL_LINK( XMLFilterListBox, HeaderEndDrag_Impl, HeaderBar*, pBar, void )
    {
        if ( pBar && !pBar->GetCurItemId() )
            return;

        if ( !m_pHeaderBar->IsItemMode() )
        {
            Size aSz;
            sal_uInt16 nTabs = m_pHeaderBar->GetItemCount();
            long nTmpSz = 0;
            long nWidth = m_pHeaderBar->GetItemSize(ITEMID_NAME);
            long nBarWidth = m_pHeaderBar->GetSizePixel().Width();

            if(nWidth < 30)
                m_pHeaderBar->SetItemSize( ITEMID_TYPE, 30);
            else if ( ( nBarWidth - nWidth ) < 30 )
                m_pHeaderBar->SetItemSize( ITEMID_TYPE, nBarWidth - 30 );

            for ( sal_uInt16 i = 1; i <= nTabs; ++i )
            {
                long nW = m_pHeaderBar->GetItemSize(i);
                aSz.setWidth(  nW + nTmpSz );
                nTmpSz += nW;
                SetTab( i, PixelToLogic( aSz, MapMode(MapUnit::MapAppFont) ).Width() );
            }
        }
    }

    /** adds a new filter info entry to the ui filter list */
    void XMLFilterListBox::addFilterEntry( const filter_info_impl* pInfo )
    {
        const OUString aEntryStr( getEntryString( pInfo ) );
        InsertEntryToColumn( aEntryStr, TREELIST_APPEND, 0xffff, const_cast<filter_info_impl *>(pInfo) );
    }

    void XMLFilterListBox::changeEntry( const filter_info_impl* pInfo )
    {
        const sal_uLong nCount = GetEntryCount();
        sal_uLong nPos;
        for( nPos = 0; nPos < nCount; nPos++ )
        {
            SvTreeListEntry* pEntry = GetEntry( nPos );
            if( static_cast<filter_info_impl*>(pEntry->GetUserData()) == pInfo )
            {
                OUString aEntryText( getEntryString( pInfo ) );
                SetEntryText( aEntryText, pEntry );
                break;
            }
        }
    }

    OUString XMLFilterListBox::getEntryString( const filter_info_impl* pInfo )
    {
        OUString aEntryStr( pInfo->maFilterName + "\t");
        if ( !pInfo->maExportService.isEmpty() )
            aEntryStr += getApplicationUIName( pInfo->maExportService );
        else
            aEntryStr += getApplicationUIName( pInfo->maImportService );
        aEntryStr += " - ";

        if( pInfo->maFlags & 1 )
        {
            if( pInfo->maFlags & 2 )
            {
                aEntryStr += XsltResId(STR_IMPORT_EXPORT);
            }
            else
            {
                aEntryStr += XsltResId(STR_IMPORT_ONLY);
            }
        }
        else if( pInfo->maFlags & 2 )
        {
            aEntryStr += XsltResId(STR_EXPORT_ONLY);
        }
        else
        {
            aEntryStr += XsltResId(STR_UNDEFINED_FILTER);
        }

        return aEntryStr;
    }

    filter_info_impl::filter_info_impl()
        : maFlags(0x00080040)
        , maFileFormatVersion(0)
        , mnDocumentIconID(0)
        , mbReadonly(false)
        , mbNeedsXSLT2(false)
    {
    }

    bool filter_info_impl::operator==( const filter_info_impl& r ) const
    {
        return maFilterName == r.maFilterName &&
            maType == r.maType &&
            maDocumentService == r.maDocumentService &&
            maInterfaceName == r.maInterfaceName &&
            maComment == r.maComment &&
            maExtension == r.maExtension &&
            maDocType == r.maDocType &&
            maExportXSLT == r.maExportXSLT &&
            maImportXSLT == r.maImportXSLT &&
            maExportService == r.maExportService &&
            maImportService == r.maImportService &&
            maImportTemplate == r.maImportTemplate &&
            maFlags == r.maFlags &&
            maFileFormatVersion == r.maFileFormatVersion &&
            mbNeedsXSLT2 == r.mbNeedsXSLT2;
    }

    Sequence< OUString > filter_info_impl::getFilterUserData() const
    {
        Sequence< OUString > aUserData(8);

        aUserData[0] = "com.sun.star.documentconversion.XSLTFilter";
        aUserData[1] = OUString::boolean( mbNeedsXSLT2 );
        aUserData[2] = maImportService;
        aUserData[3] = maExportService;
        aUserData[4] = maImportXSLT;
        aUserData[5] = maExportXSLT;
        aUserData[7] = maComment;

        return aUserData;
    }

    OUString string_encode( const OUString & rText )
    {
        static sal_Bool const uricNoSlash[] = {
            false, false, false, false, false, false, false, false,
            false, false, false, false, false, false, false, false,
            false, false, false, false, false, false, false, false,
            false, false, false, false, false, false, false, false,
            false,  true, false, false,  true, false,  true,  true,  //  !"#$%&'
             true,  true,  true,  true, false,  true,  true, false,  // ()*+,-./
             true,  true,  true,  true,  true,  true,  true,  true,  // 01234567
             true,  true,  true, false, false,  true, false,  true,  // 89:;<=>?
             true,  true,  true,  true,  true,  true,  true,  true,  // @ABCDEFG
             true,  true,  true,  true,  true,  true,  true,  true,  // HIJKLMNO
             true,  true,  true,  true,  true,  true,  true,  true,  // PQRSTUVW
             true,  true,  true, false, false, false, false,  true,  // XYZ[\]^_
            false,  true,  true,  true,  true,  true,  true,  true,  // `abcdefg
             true,  true,  true,  true,  true,  true,  true,  true,  // hijklmno
             true,  true,  true,  true,  true,  true,  true,  true,  // pqrstuvw
             true,  true,  true, false, false, false,  true, false}; // xyz{|}~

        return Uri::encode( rText, uricNoSlash, rtl_UriEncodeCheckEscapes, RTL_TEXTENCODING_UTF8 );
    }

    OUString string_decode( const OUString & rText )
    {
        return Uri::decode( rText, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8 );
    }

    bool copyStreams( const Reference< XInputStream >& xIS, const Reference< XOutputStream >& xOS )
    {
        try
        {
            sal_Int32 nBufferSize = 512;
            Sequence< sal_Int8 > aDataBuffer(nBufferSize);

            sal_Int32 nRead;
            do
            {
                nRead = xIS->readBytes( aDataBuffer, nBufferSize );

                if( nRead )
                {
                    if( nRead < nBufferSize )
                    {
                        nBufferSize = nRead;
                        aDataBuffer.realloc(nRead);
                    }

                    xOS->writeBytes( aDataBuffer );
                }
            }
            while( nRead );

            xOS->flush();

            return true;
        }
        catch(const Exception&)
        {
            OSL_FAIL( "copyStreams() exception caught!" );
        }

        return false;
    }

    bool createDirectory( OUString const & rURL )
    {
        sal_Int32 nLastIndex = sizeof( "file:///" ) - 2;
        while( nLastIndex != -1 )
        {
            nLastIndex = rURL.indexOf( '/', nLastIndex + 1);
            if( nLastIndex != -1 )
            {
                OUString aDirURL( rURL.copy( 0, nLastIndex ) );
                Directory aDir( aDirURL );
                Directory::RC rc = aDir.open();
                if( rc == Directory::E_NOENT )
                    rc = osl::Directory::create( aDirURL );

                if( rc != Directory::E_None )
                {
                    return false;
                }
            }
        }

        return true;
    }

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

↑ V547 Expression 'bOk' is always true.

↑ V560 A part of conditional expression is always true: !bTypeStillUsed.