Separate persona download thread

And do not re-download the already existing images

Change-Id: I1b9c6b85aec6ffa448f56d4250317c3d6a50db1d
Reviewed-on: https://gerrit.libreoffice.org/61148
Tested-by: Jenkins
Reviewed-by: Muhammet Kara <[email protected]>
diff --git a/cui/source/options/personalization.cxx b/cui/source/options/personalization.cxx
index 6ac90e7..d30c1c1 100644
--- a/cui/source/options/personalization.cxx
+++ b/cui/source/options/personalization.cxx
@@ -179,14 +179,15 @@

    if( !aSelectedPersona.isEmpty() )
    {
        m_pSearchThread = new SearchAndParseThread( this, aSelectedPersona, false );
        m_pSearchThread->launch();
        m_pGetPersonaThread = new GetPersonaThread( this, aSelectedPersona );
        m_pGetPersonaThread->launch();
    }

    else
    {
        if( m_pSearchThread.is() )
        if ( m_pSearchThread.is() )
            m_pSearchThread->StopExecution();

        EndDialog( RET_OK );
    }
}
@@ -195,6 +196,8 @@
{
    if( m_pSearchThread.is() )
        m_pSearchThread->StopExecution();
    if( m_pGetPersonaThread.is() )
        m_pGetPersonaThread->StopExecution();

    EndDialog();
}
@@ -203,6 +206,8 @@
{
    if( m_pSearchThread.is() )
        m_pSearchThread->StopExecution();
    if ( m_pGetPersonaThread.is() )
        return;

    for( sal_Int32 index = 0; index < MAX_RESULTS; index++ )
    {
@@ -701,12 +706,14 @@
    OUString gallery = "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE( "bootstrap") "::UserInstallation}";
    rtl::Bootstrap::expandMacros( gallery );
    gallery += "/user/gallery/personas/" + aSlug + "/";
    osl::Directory::createPath( gallery );

    OUString aPreviewFile( INetURLObject( aPreviewURL ).getName() );

    try {
        xFileAccess->copy( aPreviewURL, gallery + aPreviewFile );
        osl::Directory::createPath( gallery );

        if ( !xFileAccess->exists( gallery + aPreviewFile ) )
            xFileAccess->copy( aPreviewURL, gallery + aPreviewFile );
    }
    catch ( const uno::Exception & )
    {
@@ -721,209 +728,207 @@

void SearchAndParseThread::execute()
{
    if( m_aURL.startsWith( "https://" ) )
    m_pPersonaDialog->ClearSearchResults();
    OUString sProgress( CuiResId( RID_SVXSTR_SEARCHING ) ), sError;
    m_pPersonaDialog->SetProgress( sProgress );

    PersonasDocHandler* pHandler = new PersonasDocHandler();
    Reference<XComponentContext> xContext( ::comphelper::getProcessComponentContext() );
    Reference< xml::sax::XParser > xParser = xml::sax::Parser::create(xContext);
    Reference< xml::sax::XDocumentHandler > xDocHandler = pHandler;
    uno::Reference< ucb::XSimpleFileAccess3 > xFileAccess( ucb::SimpleFileAccess::create( comphelper::getProcessComponentContext() ), uno::UNO_QUERY );
    uno::Reference< io::XInputStream > xStream;
    xParser->setDocumentHandler( xDocHandler );

    if( !m_bDirectURL )
    {
        m_pPersonaDialog->ClearSearchResults();
        OUString sProgress( CuiResId( RID_SVXSTR_SEARCHING ) ), sError;
        m_pPersonaDialog->SetProgress( sProgress );
        if ( !xFileAccess.is() )
            return;

        PersonasDocHandler* pHandler = new PersonasDocHandler();
        Reference<XComponentContext> xContext( ::comphelper::getProcessComponentContext() );
        Reference< xml::sax::XParser > xParser = xml::sax::Parser::create(xContext);
        Reference< xml::sax::XDocumentHandler > xDocHandler = pHandler;
        uno::Reference< ucb::XSimpleFileAccess3 > xFileAccess( ucb::SimpleFileAccess::create( comphelper::getProcessComponentContext() ), uno::UNO_QUERY );
        uno::Reference< io::XInputStream > xStream;
        xParser->setDocumentHandler( xDocHandler );
        try {
            css:: uno::Reference< task::XInteractionHandler > xIH(
                        css::task::InteractionHandler::createWithParent( xContext, nullptr ) );

        if( !m_bDirectURL )
        {
            if ( !xFileAccess.is() )
                return;
            xFileAccess->setInteractionHandler( new comphelper::SimpleFileAccessInteraction( xIH ) );

            try {
                css:: uno::Reference< task::XInteractionHandler > xIH(
                    css::task::InteractionHandler::createWithParent( xContext, nullptr ) );

                xFileAccess->setInteractionHandler( new comphelper::SimpleFileAccessInteraction( xIH ) );

                xStream = xFileAccess->openFileRead( m_aURL );
                if( !xStream.is() )
                {
                    // in case of a returned CommandFailedException
                    // SimpleFileAccess serves it, returning an empty stream
                    SolarMutexGuard aGuard;
                    sError = CuiResId(RID_SVXSTR_SEARCHERROR);
                    sError = sError.replaceAll("%1", m_aURL);
                    m_pPersonaDialog->SetProgress( OUString() );

                    std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
                                                              VclMessageType::Error, VclButtonsType::Ok,
                                                              sError));
                    xBox->run();
                    return;
                }
            }
            catch (...)
            xStream = xFileAccess->openFileRead( m_aURL );
            if( !xStream.is() )
            {
                // a catch all clause, in case the exception is not
                // served elsewhere
                // in case of a returned CommandFailedException
                // SimpleFileAccess serves it, returning an empty stream
                SolarMutexGuard aGuard;
                sError = CuiResId(RID_SVXSTR_SEARCHERROR);
                sError = sError.replaceAll("%1", m_aURL);
                m_pPersonaDialog->SetProgress( OUString() );

                std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
                                                                                           VclMessageType::Error, VclButtonsType::Ok,
                                                                                           sError));
                xBox->run();
                return;
            }
        }
        catch (...)
        {
            // a catch all clause, in case the exception is not
            // served elsewhere
            SolarMutexGuard aGuard;
            sError = CuiResId(RID_SVXSTR_SEARCHERROR);
            sError = sError.replaceAll("%1", m_aURL);
            m_pPersonaDialog->SetProgress( OUString() );
            std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
                                                                                       VclMessageType::Error, VclButtonsType::Ok,
                                                                                       sError));
            xBox->run();
            return;
        }

        xml::sax::InputSource aParserInput;
        aParserInput.aInputStream = xStream;
        xParser->parseStream( aParserInput );

        if( !pHandler->hasResults() )
        {
            sProgress = CuiResId( RID_SVXSTR_NORESULTS );
            m_pPersonaDialog->SetProgress( sProgress );
            return;
        }
    }

    std::vector<OUString> vLearnmoreURLs;
    sal_Int32 nIndex = 0;
    GraphicFilter aFilter;
    Graphic aGraphic;

    if( !m_bDirectURL )
        vLearnmoreURLs = pHandler->getLearnmoreURLs();
    else
        vLearnmoreURLs.push_back( m_aURL );

    for (auto const& learnMoreUrl : vLearnmoreURLs)
    {
        OUString sPreviewFile, aPersonaSetting;
        bool bResult = getPreviewFile( learnMoreUrl, &sPreviewFile, &aPersonaSetting );
        // parsing is buggy at times, as HTML is not proper. Skip it.
        if(aPersonaSetting.isEmpty() || !bResult)
        {
            if( m_bDirectURL )
            {
                SolarMutexGuard aGuard;
                sError = CuiResId(RID_SVXSTR_SEARCHERROR);
                sError = sError.replaceAll("%1", m_aURL);
                m_pPersonaDialog->SetProgress( OUString() );
                std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
                                                          VclMessageType::Error, VclButtonsType::Ok,
                                                          sError));
                                                                                           VclMessageType::Error, VclButtonsType::Ok,
                                                                                           sError));
                xBox->run();
                return;
            }

            xml::sax::InputSource aParserInput;
            aParserInput.aInputStream = xStream;
            xParser->parseStream( aParserInput );

            if( !pHandler->hasResults() )
            {
                sProgress = CuiResId( RID_SVXSTR_NORESULTS );
                m_pPersonaDialog->SetProgress( sProgress );
                return;
            }
            continue;
        }
        INetURLObject aURLObj( sPreviewFile );

        std::vector<OUString> vLearnmoreURLs;
        sal_Int32 nIndex = 0;
        GraphicFilter aFilter;
        Graphic aGraphic;

        if( !m_bDirectURL )
            vLearnmoreURLs = pHandler->getLearnmoreURLs();
        else
            vLearnmoreURLs.push_back( m_aURL );

        for (auto const& learnMoreUrl : vLearnmoreURLs)
        {
            OUString sPreviewFile, aPersonaSetting;
            bool bResult = getPreviewFile( learnMoreUrl, &sPreviewFile, &aPersonaSetting );
            // parsing is buggy at times, as HTML is not proper. Skip it.
            if(aPersonaSetting.isEmpty() || !bResult)
            {
                if( m_bDirectURL )
                {
                    SolarMutexGuard aGuard;
                    sError = CuiResId(RID_SVXSTR_SEARCHERROR);
                    sError = sError.replaceAll("%1", m_aURL);
                    m_pPersonaDialog->SetProgress( OUString() );
                    std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
                                                              VclMessageType::Error, VclButtonsType::Ok,
                                                              sError));
                    xBox->run();
                    return;
                }
                continue;
            }
            INetURLObject aURLObj( sPreviewFile );

            // Stop the thread if requested -- before taking the solar mutex.
            if( !m_bExecute )
                return;

            // for VCL to be able to create bitmaps / do visual changes in the thread
            SolarMutexGuard aGuard;
            aFilter.ImportGraphic( aGraphic, aURLObj );
            BitmapEx aBmp = aGraphic.GetBitmapEx();

            m_pPersonaDialog->SetImages( Image( aBmp ), nIndex++ );
            m_pPersonaDialog->setOptimalLayoutSize();
            m_pPersonaDialog->AddPersonaSetting( aPersonaSetting );
            if (nIndex >= MAX_RESULTS)
                break;
        }

        // Stop the thread if requested -- before taking the solar mutex.
        if( !m_bExecute )
            return;

        // for VCL to be able to create bitmaps / do visual changes in the thread
        SolarMutexGuard aGuard;
        sProgress.clear();
        m_pPersonaDialog->SetProgress( sProgress );
        aFilter.ImportGraphic( aGraphic, aURLObj );
        BitmapEx aBmp = aGraphic.GetBitmapEx();

        m_pPersonaDialog->SetImages( Image( aBmp ), nIndex++ );
        m_pPersonaDialog->setOptimalLayoutSize();
        m_pPersonaDialog->AddPersonaSetting( aPersonaSetting );
        if (nIndex >= MAX_RESULTS)
            break;
    }

    else
    {
        OUString sProgress( CuiResId( RID_SVXSTR_APPLYPERSONA ) ), sError;
        m_pPersonaDialog->SetProgress( sProgress );
    if( !m_bExecute )
        return;

        uno::Reference< ucb::XSimpleFileAccess3 > xFileAccess( ucb::SimpleFileAccess::create( comphelper::getProcessComponentContext() ), uno::UNO_QUERY );
        if ( !xFileAccess.is() )
            return;
    SolarMutexGuard aGuard;
    sProgress.clear();
    m_pPersonaDialog->SetProgress( sProgress );
    m_pPersonaDialog->setOptimalLayoutSize();
}

        OUString aSlug, aName, aHeaderURL, aFooterURL, aTextColor, aAccentColor;
        OUString aPersonaSetting;
GetPersonaThread::GetPersonaThread( SelectPersonaDialog* pDialog,
                          const OUString& rSelectedPersona ) :
            Thread( "cuiPersonasGetPersonaThread" ),
            m_pPersonaDialog( pDialog ),
            m_aSelectedPersona( rSelectedPersona ),
            m_bExecute( true )
{
}

        // get the required fields from m_aURL
        sal_Int32 nOldIndex = 0;
        sal_Int32 nNewIndex = m_aURL.indexOf( ';', nOldIndex );
        aSlug = m_aURL.copy( nOldIndex, ( nNewIndex - nOldIndex ) );
GetPersonaThread::~GetPersonaThread()
{
    //TODO: Clean-up
}

        nOldIndex = nNewIndex + 1;
        nNewIndex = m_aURL.indexOf( ';', nOldIndex );
        aName = m_aURL.copy(nOldIndex , ( nNewIndex - nOldIndex ) );
void GetPersonaThread::execute()
{
    OUString sProgress( CuiResId( RID_SVXSTR_APPLYPERSONA ) ), sError;
    m_pPersonaDialog->SetProgress( sProgress );

        nOldIndex = nNewIndex + 1;
        nNewIndex = m_aURL.indexOf( ';', nOldIndex );
        aHeaderURL = m_aURL.copy(nOldIndex , ( nNewIndex - nOldIndex ) );
    uno::Reference< ucb::XSimpleFileAccess3 > xFileAccess( ucb::SimpleFileAccess::create( comphelper::getProcessComponentContext() ), uno::UNO_QUERY );
    if ( !xFileAccess.is() )
        return;

        nOldIndex = nNewIndex + 1;
        nNewIndex = m_aURL.indexOf( ';', nOldIndex );
        aFooterURL = m_aURL.copy( nOldIndex,  ( nNewIndex - nOldIndex ) );
    OUString aSlug, aName, aHeaderURL, aFooterURL, aTextColor, aAccentColor;
    OUString aPersonaSetting;

        nOldIndex = nNewIndex + 1;
        nNewIndex = m_aURL.indexOf( ';', nOldIndex );
        aTextColor = m_aURL.copy( nOldIndex, ( nNewIndex - nOldIndex ) );
    // get the required fields from m_aSelectedPersona
    sal_Int32 nIndex = 0;

        nOldIndex = nNewIndex + 1;
        nNewIndex = m_aURL.getLength();
        aAccentColor = m_aURL.copy( nOldIndex, ( nNewIndex - nOldIndex ) );
    aSlug = m_aSelectedPersona.getToken(0, ';', nIndex);
    aName = m_aSelectedPersona.getToken(0, ';', nIndex);
    aHeaderURL = m_aSelectedPersona.getToken(0, ';', nIndex);
    aFooterURL = m_aSelectedPersona.getToken(0, ';', nIndex);
    aTextColor = m_aSelectedPersona.getToken(0, ';', nIndex);
    aAccentColor = m_aSelectedPersona.getToken(0, ';', nIndex);

        // copy the images to the user's gallery
        OUString gallery = "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE( "bootstrap") "::UserInstallation}";
        rtl::Bootstrap::expandMacros( gallery );
        gallery += "/user/gallery/personas/";
    // copy the images to the user's gallery
    OUString gallery = "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE( "bootstrap") "::UserInstallation}";
    rtl::Bootstrap::expandMacros( gallery );
    gallery += "/user/gallery/personas/";

    OUString aHeaderFile( aSlug + "/" + INetURLObject( aHeaderURL ).getName() );
    OUString aFooterFile( aSlug + "/" + INetURLObject( aFooterURL ).getName() );

    try {
        osl::Directory::createPath( gallery );

        OUString aHeaderFile( INetURLObject( aHeaderURL ).getName() );
        OUString aFooterFile( INetURLObject( aFooterURL ).getName() );

        aHeaderFile = aSlug + "/" + aHeaderFile;
        aFooterFile = aSlug + "/" + aFooterFile;

        try {
        if ( !xFileAccess->exists(gallery + aHeaderFile) )
            xFileAccess->copy( aHeaderURL, gallery + aHeaderFile );

        if ( !xFileAccess->exists(gallery + aFooterFile) )
            xFileAccess->copy( aFooterURL, gallery + aFooterFile );
        }
        catch ( const uno::Exception & )
        {
            SolarMutexGuard aGuard;
            sError = CuiResId( RID_SVXSTR_SEARCHERROR );
            sError = sError.replaceAll("%1", m_aURL);
            m_pPersonaDialog->SetProgress( OUString() );
            std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
                                                      VclMessageType::Error, VclButtonsType::Ok,
                                                      sError));
            xBox->run();
            return;
        }

        if( !m_bExecute )
            return;

        SolarMutexGuard aGuard;

        aPersonaSetting = aSlug + ";" + aName + ";" + aHeaderFile + ";" + aFooterFile
                + ";" + aTextColor + ";" + aAccentColor;
        m_pPersonaDialog->SetAppliedPersonaSetting( aPersonaSetting );
        m_pPersonaDialog->EndDialog( RET_OK );
    }
    catch ( const uno::Exception & )
    {
        SolarMutexGuard aGuard;
        sError = CuiResId( RID_SVXSTR_SEARCHERROR );
        sError = sError.replaceAll("%1", m_aSelectedPersona);
        m_pPersonaDialog->SetProgress( OUString() );
        std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
                                                                                   VclMessageType::Error, VclButtonsType::Ok,
                                                                                   sError));
        xBox->run();
        return;
    }

    if( !m_bExecute )
        return;

    SolarMutexGuard aGuard;

    aPersonaSetting = aSlug + ";" + aName + ";" + aHeaderFile + ";" + aFooterFile
            + ";" + aTextColor + ";" + aAccentColor;

    m_pPersonaDialog->SetAppliedPersonaSetting( aPersonaSetting );
    m_pPersonaDialog->EndDialog( RET_OK );
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/cui/source/options/personalization.hxx b/cui/source/options/personalization.hxx
index 9ee91e8..20ee552 100644
--- a/cui/source/options/personalization.hxx
+++ b/cui/source/options/personalization.hxx
@@ -25,6 +25,7 @@
class FixedText;
class FixedHyperlink;
class SearchAndParseThread;
class GetPersonaThread;

class SvxPersonalizationTabPage : public SfxTabPage
{
@@ -112,7 +113,8 @@
    explicit SelectPersonaDialog( vcl::Window *pParent );
    virtual ~SelectPersonaDialog() override;
    virtual void dispose() override;
    ::rtl::Reference< SearchAndParseThread > m_pSearchThread;
    ::rtl::Reference< SearchAndParseThread >    m_pSearchThread;
    ::rtl::Reference< GetPersonaThread >        m_pGetPersonaThread;

    OUString GetSelectedPersona() const;
    void SetProgress( const OUString& );
@@ -150,6 +152,25 @@
    void StopExecution() { m_bExecute = false; }
};

class GetPersonaThread: public salhelper::Thread
{
private:

    VclPtr<SelectPersonaDialog> m_pPersonaDialog;
    OUString m_aSelectedPersona;
    std::atomic<bool> m_bExecute;

    virtual ~GetPersonaThread() override;
    virtual void execute() override;

public:

    GetPersonaThread( SelectPersonaDialog* pDialog,
                          const OUString& rSelectedPersona );

    void StopExecution() { m_bExecute = false; }
};

#endif // INCLUDED_CUI_SOURCE_OPTIONS_PERSONALIZATION_HXX

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