/* -*- 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_extensions.h>

#include <strings.hrc>
#include <helpids.h>

#include "dp_gui.h"
#include "dp_gui_dialog2.hxx"
#include "dp_gui_extensionboxwithbuttons.hxx"
#include "dp_gui_extlistbox.hxx"
#include <dp_shared.hxx>
#include "dp_gui_theextmgr.hxx"
#include "dp_gui_extensioncmdqueue.hxx"
#include <dp_misc.h>
#include <dp_update.hxx>
#include <dp_identifier.hxx>

#include <fpicker/strings.hrc>

#include <utility>
#include <vcl/commandevent.hxx>
#include <vcl/svapp.hxx>

#include <sal/log.hxx>
#include <rtl/ustrbuf.hxx>

#include <svtools/restartdialog.hxx>

#include <sfx2/filedlghelper.hxx>
#include <sfx2/sfxdlg.hxx>

#include <comphelper/anytostring.hxx>
#include <cppuhelper/exc_hlp.hxx>
#include <cppuhelper/supportsservice.hxx>

#include <comphelper/processfactory.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <unotools/configmgr.hxx>

#include <com/sun/star/deployment/DeploymentException.hpp>
#include <com/sun/star/lang/IllegalArgumentException.hpp>
#include <com/sun/star/system/SystemShellExecuteFlags.hpp>
#include <com/sun/star/system/SystemShellExecute.hpp>

#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>

#include <officecfg/Office/ExtensionManager.hxx>

#include <map>
#include <memory>
#include <vector>

using namespace ::com::sun::star;
using namespace ::com::sun::star::system;


namespace dp_gui {

constexpr OUStringLiteral USER_PACKAGE_MANAGER = u"user";
constexpr OUString SHARED_PACKAGE_MANAGER = u"shared"_ustr;
constexpr OUStringLiteral BUNDLED_PACKAGE_MANAGER = u"bundled";

//                             DialogHelper

DialogHelper::DialogHelper(weld::Widget* pParent, const OUString& rUIFile,
                           const OUString& rDialogId,
                           const uno::Reference<uno::XComponentContext>& xContext)
    : weld::GenericDialogController(pParent, rUIFile, rDialogId)
    , m_nEventID(nullptr)
{
    m_xContext = xContext;
}

DialogHelper::~DialogHelper()
{
    if ( m_nEventID )
        Application::RemoveUserEvent( m_nEventID );
}


bool DialogHelper::IsSharedPkgMgr( const uno::Reference< deployment::XPackage > &xPackage )
{
    return xPackage->getRepositoryName() == SHARED_PACKAGE_MANAGER;
}

bool DialogHelper::continueOnSharedExtension(const uno::Reference<deployment::XPackage>& xPackage,
                                             TranslateId pResID, bool& bHadWarning)
{
    if ( !bHadWarning && IsSharedPkgMgr( xPackage ) )
    {
        const SolarMutexGuard guard;
        incBusy();
        std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(
            m_xDialog.get(), VclMessageType::Warning, VclButtonsType::OkCancel, DpResId(pResID)));
        bHadWarning = true;

        bool bRet = RET_OK == xBox->run();
        xBox.reset();
        decBusy();
        return bRet;
    }
    else
        return true;
}

void DialogHelper::openWebBrowser(const OUString& sURL, const OUString& sTitle)
{
    if ( sURL.isEmpty() ) // Nothing to do, when the URL is empty
        return;

    try
    {
        uno::Reference< XSystemShellExecute > xSystemShellExecute(
            SystemShellExecute::create(m_xContext));
        //throws css::lang::IllegalArgumentException, css::system::SystemShellExecuteException
        xSystemShellExecute->execute( sURL, OUString(),  SystemShellExecuteFlags::URIS_ONLY );
    }
    catch ( const uno::Exception& )
    {
        uno::Any exc( ::cppu::getCaughtException() );
        OUString msg( ::comphelper::anyToString( exc ) );
        const SolarMutexGuard guard;
        incBusy();
        std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(
            getDialog(), VclMessageType::Warning, VclButtonsType::Ok, msg));
        xErrorBox->set_title(sTitle);
        xErrorBox->run();
        xErrorBox.reset();
        decBusy();
    }
}

bool DialogHelper::installExtensionWarn(std::u16string_view rExtensionName)
{
    const SolarMutexGuard guard;

    // Check if extension installation is disabled in the expert configurations
    if (officecfg::Office::ExtensionManager::ExtensionSecurity::DisableExtensionInstallation::get())
    {
        incBusy();
        std::unique_ptr<weld::MessageDialog> xWarnBox(Application::CreateMessageDialog(
            getDialog(), VclMessageType::Warning, VclButtonsType::Ok,
            DpResId(RID_STR_WARNING_INSTALL_EXTENSION_DISABLED)));
        xWarnBox->run();
        xWarnBox.reset();
        decBusy();

        return false;
    }

    incBusy();
    std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(
        getDialog(), VclMessageType::Warning, VclButtonsType::OkCancel,
        DpResId(RID_STR_WARNING_INSTALL_EXTENSION)));
    OUString sText(xInfoBox->get_primary_text());
    sText = sText.replaceAll("%NAME", rExtensionName);
    xInfoBox->set_primary_text(sText);

    bool bRet = RET_OK == xInfoBox->run();
    xInfoBox.reset();
    decBusy();
    return bRet;
}

bool DialogHelper::installForAllUsers(bool &bInstallForAll)
{
    const SolarMutexGuard guard;
    incBusy();
    std::unique_ptr<weld::Builder> xBuilder(
        Application::CreateBuilder(getDialog(), u"desktop/ui/installforalldialog.ui"_ustr));
    std::unique_ptr<weld::MessageDialog> xQuery(xBuilder->weld_message_dialog(u"InstallForAllDialog"_ustr));
    short nRet = xQuery->run();
    xQuery.reset();
    decBusy();
    if (nRet == RET_CANCEL)
        return false;

    bInstallForAll = ( nRet == RET_NO );
    return true;
}

void DialogHelper::PostUserEvent( const Link<void*,void>& rLink, void* pCaller )
{
    if ( m_nEventID )
        Application::RemoveUserEvent( m_nEventID );

    m_nEventID = Application::PostUserEvent(rLink, pCaller);
}

//                             ExtMgrDialog
ExtMgrDialog::ExtMgrDialog(weld::Window* pParent, TheExtensionManager& rManager)
    : DialogHelper(pParent, u"desktop/ui/extensionmanager.ui"_ustr, u"ExtensionManagerDialog"_ustr,
                   rManager.getContext())
    , m_bHasProgress(false)
    , m_bProgressChanged(false)
    , m_bStartProgress(false)
    , m_bStopProgress(false)
    , m_bEnableWarning(false)
    , m_bDisableWarning(false)
    , m_bDeleteWarning(false)
    , m_bClosed(false)
    , m_nProgress(0)
    , m_aIdle( "ExtMgrDialog m_aIdle TimeOutHdl" )
    , m_rManager(rManager)
    , m_xExtensionBox(new ExtensionBoxWithButtons(
          *this, m_xBuilder->weld_scrolled_window(u"scroll"_ustr, true), rManager))
    , m_xExtensionBoxWnd(new weld::CustomWeld(*m_xBuilder, u"extensions"_ustr, *m_xExtensionBox))
    , m_xOptionsBtn(m_xBuilder->weld_button(u"optionsbtn"_ustr))
    , m_xAddBtn(m_xBuilder->weld_button(u"addbtn"_ustr))
    , m_xRemoveBtn(m_xBuilder->weld_button(u"removebtn"_ustr))
    , m_xEnableBtn(m_xBuilder->weld_button(u"enablebtn"_ustr))
    , m_xUpdateBtn(m_xBuilder->weld_button(u"updatebtn"_ustr))
    , m_xCloseBtn(m_xBuilder->weld_button(u"close"_ustr))
    , m_xBundledCbx(m_xBuilder->weld_check_button(u"bundled"_ustr))
    , m_xSharedCbx(m_xBuilder->weld_check_button(u"shared"_ustr))
    , m_xUserCbx(m_xBuilder->weld_check_button(u"user"_ustr))
    , m_xGetExtensions(m_xBuilder->weld_link_button(u"getextensions"_ustr))
    , m_xProgressText(m_xBuilder->weld_label(u"progressft"_ustr))
    , m_xProgressBar(m_xBuilder->weld_progress_bar(u"progressbar"_ustr))
    , m_xCancelBtn(m_xBuilder->weld_button(u"cancel"_ustr))
    , m_xSearchEntry(m_xBuilder->weld_entry(u"search"_ustr))
{
    m_xEnableBtn->set_help_id(HID_EXTENSION_MANAGER_LISTBOX_ENABLE);

    m_xOptionsBtn->connect_clicked( LINK( this, ExtMgrDialog, HandleOptionsBtn ) );
    m_xAddBtn->connect_clicked( LINK( this, ExtMgrDialog, HandleAddBtn ) );
    m_xRemoveBtn->connect_clicked( LINK( this, ExtMgrDialog, HandleRemoveBtn ) );
    m_xEnableBtn->connect_clicked( LINK( this, ExtMgrDialog, HandleEnableBtn ) );
    m_xCloseBtn->connect_clicked( LINK( this, ExtMgrDialog, HandleCloseBtn ) );

    m_xCancelBtn->connect_clicked( LINK( this, ExtMgrDialog, HandleCancelBtn ) );

    m_xBundledCbx->connect_toggled( LINK( this, ExtMgrDialog, HandleExtTypeCbx ) );
    m_xSharedCbx->connect_toggled( LINK( this, ExtMgrDialog, HandleExtTypeCbx ) );
    m_xUserCbx->connect_toggled( LINK( this, ExtMgrDialog, HandleExtTypeCbx ) );

    m_xSearchEntry->connect_changed( LINK( this, ExtMgrDialog, HandleSearch ) );

    m_xBundledCbx->set_active(true);
    m_xSharedCbx->set_active(true);
    m_xUserCbx->set_active(true);

    m_xProgressBar->hide();

#if ENABLE_EXTENSION_UPDATE
    m_xUpdateBtn->connect_clicked( LINK( this, ExtMgrDialog, HandleUpdateBtn ) );
    m_xUpdateBtn->set_sensitive(false);
#else
    m_xUpdateBtn->hide();
#endif

    if (officecfg::Office::ExtensionManager::ExtensionSecurity::DisableExtensionInstallation::get())
    {
        m_xAddBtn->set_sensitive(false);
        m_xAddBtn->set_tooltip_text(DpResId(RID_STR_WARNING_INSTALL_EXTENSION_DISABLED));
    }
    if (officecfg::Office::ExtensionManager::ExtensionSecurity::DisableExtensionRemoval::get())
    {
        m_xRemoveBtn->set_sensitive(false);
        m_xRemoveBtn->set_tooltip_text(DpResId(RID_STR_WARNING_REMOVE_EXTENSION_DISABLED));
    }

    m_aIdle.SetPriority(TaskPriority::LOWEST);
    m_aIdle.SetInvokeHandler( LINK( this, ExtMgrDialog, TimeOutHdl ) );
}

ExtMgrDialog::~ExtMgrDialog()
{
    m_aIdle.Stop();
}

void ExtMgrDialog::setGetExtensionsURL( const OUString &rURL )
{
    m_xGetExtensions->set_uri( rURL );
}

void ExtMgrDialog::addPackageToList( const uno::Reference< deployment::XPackage > &xPackage,
                                     bool bLicenseMissing )
{
    const SolarMutexGuard aGuard;
    m_xUpdateBtn->set_sensitive(true);

    bool bSearchMatch = m_xSearchEntry->get_text().isEmpty();
    if (!m_xSearchEntry->get_text().isEmpty()
        && xPackage->getDisplayName().toAsciiLowerCase().indexOf(
               m_xSearchEntry->get_text().toAsciiLowerCase())
               >= 0)
    {
        bSearchMatch = true;
    }

    if (!bSearchMatch)
        return;

    if (m_xBundledCbx->get_active() && (xPackage->getRepositoryName() == BUNDLED_PACKAGE_MANAGER) )
    {
        m_xExtensionBox->addEntry( xPackage, bLicenseMissing );
    }
    else if (m_xSharedCbx->get_active() && (xPackage->getRepositoryName() == SHARED_PACKAGE_MANAGER) )
    {
        m_xExtensionBox->addEntry( xPackage, bLicenseMissing );
    }
    else if (m_xUserCbx->get_active() && (xPackage->getRepositoryName() == USER_PACKAGE_MANAGER ))
    {
        m_xExtensionBox->addEntry( xPackage, bLicenseMissing );
    }
}

void ExtMgrDialog::updateList()
{
    // re-creates the list of packages with addEntry selecting the packages
    prepareChecking();
    m_rManager.createPackageList();
    checkEntries();
}

void ExtMgrDialog::prepareChecking()
{
    m_xExtensionBox->prepareChecking();
}

void ExtMgrDialog::checkEntries()
{
    const SolarMutexGuard guard;
    m_xExtensionBox->checkEntries();
}

bool ExtMgrDialog::removeExtensionWarn(std::u16string_view rExtensionName)
{
    const SolarMutexGuard guard;
    incBusy();
    std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(m_xDialog.get(),
                                                  VclMessageType::Warning, VclButtonsType::OkCancel,
                                                  DpResId(RID_STR_WARNING_REMOVE_EXTENSION)));

    OUString sText(xInfoBox->get_primary_text());
    sText = sText.replaceAll("%NAME", rExtensionName);
    xInfoBox->set_primary_text(sText);

    bool bRet = RET_OK == xInfoBox->run();
    xInfoBox.reset();
    decBusy();

    return bRet;
}

void ExtMgrDialog::enablePackage( const uno::Reference< deployment::XPackage > &xPackage,
                                  bool bEnable )
{
    if ( !xPackage.is() )
        return;

    if ( bEnable )
    {
        if (!continueOnSharedExtension(xPackage, RID_STR_WARNING_ENABLE_SHARED_EXTENSION,
                                       m_bEnableWarning))
            return;
    }
    else
    {
        if (!continueOnSharedExtension(xPackage, RID_STR_WARNING_DISABLE_SHARED_EXTENSION,
                                       m_bDisableWarning))
            return;
    }

    m_rManager.getCmdQueue()->enableExtension(xPackage, bEnable);
}


void ExtMgrDialog::removePackage( const uno::Reference< deployment::XPackage > &xPackage )
{
    if ( !xPackage.is() )
        return;

    if ( !IsSharedPkgMgr( xPackage ) || m_bDeleteWarning )
    {
        if ( ! removeExtensionWarn( xPackage->getDisplayName() ) )
            return;
    }

    if (!continueOnSharedExtension(xPackage, RID_STR_WARNING_REMOVE_SHARED_EXTENSION,
                                   m_bDeleteWarning))
        return;

    m_rManager.getCmdQueue()->removeExtension(xPackage);
}


void ExtMgrDialog::updatePackage( const uno::Reference< deployment::XPackage > &xPackage )
{
    if ( !xPackage.is() )
        return;

    // get the extension with highest version
    uno::Sequence<uno::Reference<deployment::XPackage>> seqExtensions
        = m_rManager.getExtensionManager()->getExtensionsWithSameIdentifier(
            dp_misc::getIdentifier(xPackage), xPackage->getName(),
            uno::Reference<ucb::XCommandEnvironment>());
    uno::Reference<deployment::XPackage> extension =
        dp_misc::getExtensionWithHighestVersion(seqExtensions);
    OSL_ASSERT(extension.is());
    std::vector< css::uno::Reference< css::deployment::XPackage > > vEntries { extension };

    m_rManager.getCmdQueue()->checkForUpdates(std::move(vEntries));
}


bool ExtMgrDialog::acceptLicense( const uno::Reference< deployment::XPackage > &xPackage )
{
    if ( !xPackage.is() )
        return false;

    m_rManager.getCmdQueue()->acceptLicense(xPackage);

    return true;
}


uno::Sequence< OUString > ExtMgrDialog::raiseAddPicker()
{
    sfx2::FileDialogHelper aDlgHelper(ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE, FileDialogFlags::NONE, m_xDialog.get());
    aDlgHelper.SetContext(sfx2::FileDialogHelper::ExtensionManager);
    aDlgHelper.SetTitle(DpResId(RID_STR_ADD_PACKAGES));

    // collect and set filter list:
    typedef std::map< OUString, OUString > t_string2string;
    t_string2string title2filter;
    OUStringBuffer supportedFilters;

    const uno::Sequence<uno::Reference<deployment::XPackageTypeInfo>> packageTypes(
        m_rManager.getExtensionManager()->getSupportedPackageTypes());

    for ( uno::Reference< deployment::XPackageTypeInfo > const & xPackageType : packageTypes )
    {
        const OUString filter( xPackageType->getFileFilter() );
        if (!filter.isEmpty())
        {
            const OUString title( xPackageType->getShortDescription() );
            const std::pair< t_string2string::iterator, bool > insertion(
                title2filter.emplace( title, filter ) );
            if (!supportedFilters.isEmpty())
                supportedFilters.append(';');
            supportedFilters.append(filter);
            if ( ! insertion.second )
            { // already existing, append extensions:
                insertion.first->second = insertion.first->second +
                    ";" + filter;
            }
        }
    }

    static const OUString StrAllFiles = []()
        {
            const SolarMutexGuard guard;
            std::locale loc = Translate::Create("fps");
            return Translate::get(STR_FILTERNAME_ALL, loc);
        }();

    // All files at top:
    aDlgHelper.AddFilter( StrAllFiles, u"*.*"_ustr );
    aDlgHelper.AddFilter( DpResId(RID_STR_ALL_SUPPORTED), supportedFilters.makeStringAndClear() );
    // then supported ones:
    for (auto const& elem : title2filter)
    {
        try
        {
            aDlgHelper.AddFilter( elem.first, elem.second );
        }
        catch (const lang::IllegalArgumentException &)
        {
            TOOLS_WARN_EXCEPTION( "desktop", "" );
        }
    }
    aDlgHelper.SetCurrentFilter( DpResId(RID_STR_ALL_SUPPORTED) );

    if ( aDlgHelper.Execute() == ERRCODE_ABORT )
        return uno::Sequence<OUString>(); // cancelled

    uno::Sequence< OUString > files( aDlgHelper.GetSelectedFiles() );
    OSL_ASSERT( files.hasElements() );
    return files;
}

void ExtMgrDialog::enableOptionsButton( bool bEnable )
{
    m_xOptionsBtn->set_sensitive( bEnable );
}

void ExtMgrDialog::enableRemoveButton( bool bEnable )
{
    m_xRemoveBtn->set_sensitive( bEnable && !officecfg::Office::ExtensionManager::ExtensionSecurity::DisableExtensionRemoval::get());

    if (officecfg::Office::ExtensionManager::ExtensionSecurity::DisableExtensionRemoval::get())
    {
        m_xRemoveBtn->set_tooltip_text(DpResId(RID_STR_WARNING_REMOVE_EXTENSION_DISABLED));
    }
    else
    {
        m_xRemoveBtn->set_tooltip_text(u""_ustr);
    }
}

void ExtMgrDialog::enableEnableButton( bool bEnable )
{
    m_xEnableBtn->set_sensitive( bEnable );
}

void ExtMgrDialog::enableButtontoEnable( bool bEnable )
{
    if (bEnable)
    {
        m_xEnableBtn->set_label( DpResId( RID_CTX_ITEM_ENABLE ) );
        m_xEnableBtn->set_help_id( HID_EXTENSION_MANAGER_LISTBOX_ENABLE );
    }
    else
    {
        m_xEnableBtn->set_label( DpResId( RID_CTX_ITEM_DISABLE ) );
        m_xEnableBtn->set_help_id( HID_EXTENSION_MANAGER_LISTBOX_DISABLE );
    }
}

IMPL_LINK_NOARG(ExtMgrDialog, HandleCancelBtn, weld::Button&, void)
{
    if ( m_xAbortChannel.is() )
    {
        try
        {
            m_xAbortChannel->sendAbort();
        }
        catch ( const uno::RuntimeException & )
        {
            TOOLS_WARN_EXCEPTION( "dbaccess", "" );
        }
    }
}

IMPL_LINK_NOARG(ExtMgrDialog, HandleCloseBtn, weld::Button&, void)
{
    //only suggest restart if modified and this is the first close attempt
    if (!m_bClosed && m_rManager.isModified())
    {
        m_rManager.clearModified();

        //only suggest restart if we're actually running, e.g. not from standalone unopkg gui
        if (dp_misc::office_is_running())
        {
            SolarMutexGuard aGuard;
            svtools::executeRestartDialog(comphelper::getProcessComponentContext(),
                                                          m_xDialog.get(),
                                                          svtools::RESTART_REASON_EXTENSION_INSTALL);
        }
    }

    m_xDialog->response(RET_CANCEL);
}

IMPL_LINK( ExtMgrDialog, startProgress, void*, _bLockInterface, void )
{
    SolarMutexGuard aGuard;
    bool bLockInterface = static_cast<bool>(_bLockInterface);

    if ( m_bStartProgress && !m_bHasProgress )
        m_aIdle.Start();

    if ( m_bStopProgress )
    {
        if ( m_xProgressBar->get_visible() )
            m_xProgressBar->set_percentage( 100 );
        m_xAbortChannel.clear();

        SAL_INFO( "desktop.deployment", " startProgress handler: stop" );
    }
    else
    {
        SAL_INFO( "desktop.deployment", " startProgress handler: start" );
    }

    m_xCancelBtn->set_sensitive( bLockInterface );
    m_xAddBtn->set_sensitive( !bLockInterface && !officecfg::Office::ExtensionManager::ExtensionSecurity::DisableExtensionInstallation::get());
    if (officecfg::Office::ExtensionManager::ExtensionSecurity::DisableExtensionInstallation::get())
    {
        m_xAddBtn->set_tooltip_text(DpResId(RID_STR_WARNING_INSTALL_EXTENSION_DISABLED));
    }
    else
    {
        m_xAddBtn->set_tooltip_text(u""_ustr);
    }

    m_xUpdateBtn->set_sensitive(!bLockInterface && m_xExtensionBox->GetEntryCount());
    m_xExtensionBox->enableButtons( !bLockInterface );

    clearEventID();
}


void ExtMgrDialog::showProgress( bool _bStart )
{
    SolarMutexGuard aGuard;

    bool bStart = _bStart;

    if ( bStart )
    {
        m_nProgress = 0;
        m_bStartProgress = true;
        SAL_INFO( "desktop.deployment", "showProgress start" );
    }
    else
    {
        m_nProgress = 100;
        m_bStopProgress = true;
        SAL_INFO( "desktop.deployment", "showProgress stop!" );
    }

    DialogHelper::PostUserEvent( LINK( this, ExtMgrDialog, startProgress ), reinterpret_cast<void*>(bStart) );
    m_aIdle.Start();
}


void ExtMgrDialog::updateProgress(sal_Int32 nProgress)
{
    SolarMutexGuard aGuard;
    if ( m_nProgress != nProgress )
    {
        m_nProgress = nProgress;
        m_aIdle.Start();
    }
}


void ExtMgrDialog::updateProgress( const OUString &rText,
                                   const uno::Reference< task::XAbortChannel > &xAbortChannel)
{
    SolarMutexGuard aGuard;

    m_xAbortChannel = xAbortChannel;
    m_sProgressText = rText;
    m_bProgressChanged = true;
    m_aIdle.Start();
}


void ExtMgrDialog::updatePackageInfo( const uno::Reference< deployment::XPackage > &xPackage )
{
    const SolarMutexGuard aGuard;
    m_xExtensionBox->updateEntry( xPackage );
}

IMPL_LINK_NOARG(ExtMgrDialog, HandleOptionsBtn, weld::Button&, void)
{
    const sal_Int32 nActive = m_xExtensionBox->getSelIndex();

    if (nActive != ExtensionBox::ENTRY_NOTFOUND)
    {
        SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create();

        OUString sExtensionId = m_xExtensionBox->GetEntryData( nActive )->m_xPackage->getIdentifier().Value;
        ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateOptionsDialog(m_xDialog.get(), sExtensionId));

        pDlg->Execute();
    }
}

IMPL_LINK_NOARG(ExtMgrDialog, HandleAddBtn, weld::Button&, void)
{
    incBusy();

    uno::Sequence< OUString > aFileList = raiseAddPicker();

    if ( aFileList.hasElements() )
    {
        m_rManager.installPackage(aFileList[0], *this);
    }

    decBusy();
}

IMPL_LINK_NOARG(ExtMgrDialog, HandleRemoveBtn, weld::Button&, void)
{
    const sal_Int32 nActive = m_xExtensionBox->getSelIndex();

    if (nActive != ExtensionBox::ENTRY_NOTFOUND)
    {
        TEntry_Impl pEntry = m_xExtensionBox->GetEntryData( nActive );
        removePackage( pEntry->m_xPackage );
    }
}

IMPL_LINK_NOARG(ExtMgrDialog, HandleEnableBtn, weld::Button&, void)
{
    const sal_Int32 nActive = m_xExtensionBox->getSelIndex();

    if (nActive != ExtensionBox::ENTRY_NOTFOUND)
    {
        TEntry_Impl pEntry = m_xExtensionBox->GetEntryData( nActive );

        if ( pEntry->m_bMissingLic )
            acceptLicense( pEntry->m_xPackage );
        else
        {
            const bool bEnable(pEntry->m_eState != PackageState::REGISTERED);
            enablePackage( pEntry->m_xPackage, bEnable );
        }
    }
}

IMPL_LINK_NOARG(ExtMgrDialog, HandleExtTypeCbx, weld::Toggleable&, void)
{
    updateList();
}

IMPL_LINK_NOARG(ExtMgrDialog, HandleSearch, weld::Entry&, void)
{
    updateList();
}

IMPL_LINK_NOARG(ExtMgrDialog, HandleUpdateBtn, weld::Button&, void)
{
#if ENABLE_EXTENSION_UPDATE
    m_rManager.checkUpdates();
#else
    (void) this;
#endif
}

IMPL_LINK_NOARG(ExtMgrDialog, TimeOutHdl, Timer *, void)
{
    SolarMutexGuard aGuard;
    if ( m_bStopProgress )
    {
        m_bHasProgress = false;
        m_bStopProgress = false;
        m_xProgressText->hide();
        m_xProgressBar->hide();
        m_xCancelBtn->hide();
    }
    else
    {
        if ( m_bProgressChanged )
        {
            m_bProgressChanged = false;
            m_xProgressText->set_label(m_sProgressText);
        }

        if ( m_bStartProgress )
        {
            m_bStartProgress = false;
            m_bHasProgress = true;
            m_xProgressBar->show();
            m_xProgressText->show();
            m_xCancelBtn->set_sensitive(true);
            m_xCancelBtn->show();
        }

        if ( m_xProgressBar->get_visible() )
            m_xProgressBar->set_percentage(m_nProgress);
    }
}

void ExtMgrDialog::Close()
{
    m_rManager.terminateDialog();
    m_bClosed = true;
}

//UpdateRequiredDialog
UpdateRequiredDialog::UpdateRequiredDialog(weld::Window* pParent, TheExtensionManager& rManager)
    : DialogHelper(pParent, u"desktop/ui/updaterequireddialog.ui"_ustr,
                   u"UpdateRequiredDialog"_ustr, rManager.getContext())
    , m_sCloseText(DpResId(RID_STR_CLOSE_BTN))
    , m_bHasProgress(false)
    , m_bProgressChanged(false)
    , m_bStartProgress(false)
    , m_bStopProgress(false)
    , m_bHasLockedEntries(false)
    , m_nProgress(0)
    , m_aIdle( "UpdateRequiredDialog m_aIdle TimeOutHdl" )
    , m_rManager(rManager)
    , m_xExtensionBox(
          new ExtensionBox(m_xBuilder->weld_scrolled_window(u"scroll"_ustr, true), rManager))
    , m_xExtensionBoxWnd(new weld::CustomWeld(*m_xBuilder, u"extensions"_ustr, *m_xExtensionBox))
    , m_xUpdateNeeded(m_xBuilder->weld_label(u"updatelabel"_ustr))
    , m_xUpdateBtn(m_xBuilder->weld_button(u"ok"_ustr))
    , m_xCloseBtn(m_xBuilder->weld_button(u"disable"_ustr))
    , m_xCancelBtn(m_xBuilder->weld_button(u"cancel"_ustr))
    , m_xProgressText(m_xBuilder->weld_label(u"progresslabel"_ustr))
    , m_xProgressBar(m_xBuilder->weld_progress_bar(u"progress"_ustr))
{
    m_xUpdateBtn->connect_clicked( LINK( this, UpdateRequiredDialog, HandleUpdateBtn ) );
    m_xCloseBtn->connect_clicked( LINK( this, UpdateRequiredDialog, HandleCloseBtn ) );
    m_xCancelBtn->connect_clicked( LINK( this, UpdateRequiredDialog, HandleCancelBtn ) );

    OUString aText = m_xUpdateNeeded->get_label();
    aText = aText.replaceAll(
        "%PRODUCTNAME", utl::ConfigManager::getProductName());
    m_xUpdateNeeded->set_label(aText);

    m_xProgressBar->hide();
    m_xUpdateBtn->set_sensitive( false );
    m_xCloseBtn->grab_focus();

    m_aIdle.SetPriority( TaskPriority::LOWEST );
    m_aIdle.SetInvokeHandler( LINK( this, UpdateRequiredDialog, TimeOutHdl ) );
}

UpdateRequiredDialog::~UpdateRequiredDialog()
{
    m_aIdle.Stop();
}

void UpdateRequiredDialog::addPackageToList( const uno::Reference< deployment::XPackage > &xPackage,
                                             bool bLicenseMissing )
{
    // We will only add entries to the list with unsatisfied dependencies
    if ( !bLicenseMissing && !checkDependencies( xPackage ) )
    {
        m_bHasLockedEntries |= m_rManager.isReadOnly(xPackage);
        const SolarMutexGuard aGuard;
        m_xUpdateBtn->set_sensitive(true);
        m_xExtensionBox->addEntry( xPackage );
    }
}


void UpdateRequiredDialog::prepareChecking()
{
    m_xExtensionBox->prepareChecking();
}


void UpdateRequiredDialog::checkEntries()
{
    const SolarMutexGuard guard;
    m_xExtensionBox->checkEntries();

    if ( ! hasActiveEntries() )
    {
        m_xCloseBtn->set_label( m_sCloseText );
        m_xCloseBtn->grab_focus();
    }
}


IMPL_LINK_NOARG(UpdateRequiredDialog, HandleCancelBtn, weld::Button&, void)
{
    if ( m_xAbortChannel.is() )
    {
        try
        {
            m_xAbortChannel->sendAbort();
        }
        catch ( const uno::RuntimeException & )
        {
            TOOLS_WARN_EXCEPTION( "desktop", "" );
        }
    }
}


IMPL_LINK( UpdateRequiredDialog, startProgress, void*, _bLockInterface, void )
{
    SolarMutexGuard aGuard;
    bool bLockInterface = static_cast<bool>(_bLockInterface);

    if ( m_bStartProgress && !m_bHasProgress )
        m_aIdle.Start();

    if ( m_bStopProgress )
    {
        if ( m_xProgressBar->get_visible() )
            m_xProgressBar->set_percentage( 100 );
        m_xAbortChannel.clear();
        SAL_INFO( "desktop.deployment", " startProgress handler: stop" );
    }
    else
    {
        SAL_INFO( "desktop.deployment", " startProgress handler: start" );
    }

    m_xCancelBtn->set_sensitive( bLockInterface );
    m_xUpdateBtn->set_sensitive( false );
    clearEventID();
}


void UpdateRequiredDialog::showProgress( bool _bStart )
{
    SolarMutexGuard aGuard;

    bool bStart = _bStart;

    if ( bStart )
    {
        m_nProgress = 0;
        m_bStartProgress = true;
        SAL_INFO( "desktop.deployment", "showProgress start" );
    }
    else
    {
        m_nProgress = 100;
        m_bStopProgress = true;
        SAL_INFO( "desktop.deployment", "showProgress stop!" );
    }

    DialogHelper::PostUserEvent( LINK( this, UpdateRequiredDialog, startProgress ), reinterpret_cast<void*>(bStart) );
    m_aIdle.Start();
}


void UpdateRequiredDialog::updateProgress(sal_Int32 nProgress)
{
    SolarMutexGuard aGuard;
    if ( m_nProgress != nProgress )
    {
        m_nProgress = nProgress;
        m_aIdle.Start();
    }
}


void UpdateRequiredDialog::updateProgress( const OUString &rText,
                                           const uno::Reference< task::XAbortChannel > &xAbortChannel)
{
    SolarMutexGuard aGuard;

    m_xAbortChannel = xAbortChannel;
    m_sProgressText = rText;
    m_bProgressChanged = true;
    m_aIdle.Start();
}


void UpdateRequiredDialog::updatePackageInfo( const uno::Reference< deployment::XPackage > &xPackage )
{
    // We will remove all updated packages with satisfied dependencies, but
    // we will show all disabled entries so the user sees the result
    // of the 'disable all' button
    const SolarMutexGuard aGuard;
    if ( isEnabled( xPackage ) && checkDependencies( xPackage ) )
        m_xExtensionBox->removeEntry( xPackage );
    else
        m_xExtensionBox->updateEntry( xPackage );

    if ( ! hasActiveEntries() )
    {
        m_xCloseBtn->set_label( m_sCloseText );
        m_xCloseBtn->grab_focus();
    }
}


IMPL_LINK_NOARG(UpdateRequiredDialog, HandleUpdateBtn, weld::Button&, void)
{
    std::vector< uno::Reference< deployment::XPackage > > vUpdateEntries;
    {
        SolarMutexGuard aGuard;

        sal_Int32 nCount = m_xExtensionBox->GetEntryCount();

        for ( sal_Int32 i = 0; i < nCount; ++i )
        {
            TEntry_Impl pEntry = m_xExtensionBox->GetEntryData( i );
            vUpdateEntries.push_back( pEntry->m_xPackage );
        }
    }

    m_rManager.getCmdQueue()->checkForUpdates(std::move(vUpdateEntries));
}


IMPL_LINK_NOARG(UpdateRequiredDialog, HandleCloseBtn, weld::Button&, void)
{
    SolarMutexGuard aGuard;

    if ( !isBusy() )
    {
        if ( m_bHasLockedEntries )
            m_xDialog->response(-1);
        else if ( hasActiveEntries() )
            disableAllEntries();
        else
            m_xDialog->response(RET_CANCEL);
    }
}


IMPL_LINK_NOARG(UpdateRequiredDialog, TimeOutHdl, Timer *, void)
{
    if ( m_bStopProgress )
    {
        m_bHasProgress = false;
        m_bStopProgress = false;
        m_xProgressText->hide();
        m_xProgressBar->hide();
        m_xCancelBtn->hide();
    }
    else
    {
        if ( m_bProgressChanged )
        {
            m_bProgressChanged = false;
            m_xProgressText->set_label( m_sProgressText );
        }

        if ( m_bStartProgress )
        {
            m_bStartProgress = false;
            m_bHasProgress = true;
            m_xProgressBar->show();
            m_xProgressText->show();
            m_xCancelBtn->set_sensitive(true);
            m_xCancelBtn->show();
        }

        if (m_xProgressBar->get_visible())
            m_xProgressBar->set_percentage(m_nProgress);
    }
}

// VCL::Dialog
short UpdateRequiredDialog::run()
{
    //ToDo
    //I believe m_bHasLockedEntries was used to prevent showing extensions which cannot
    //be disabled because they are in a read only repository. However, disabling extensions
    //is now always possible because the registration data of all repositories
    //are in the user installation.
    //Therefore all extensions could be displayed and all the handling around m_bHasLockedEntries
    //could be removed.
    if ( m_bHasLockedEntries )
    {
        // Set other text, disable update btn, remove not shared entries from list;
        m_xUpdateNeeded->set_label( DpResId( RID_STR_NO_ADMIN_PRIVILEGE ) );
        m_xCloseBtn->set_label( DpResId( RID_STR_EXIT_BTN ) );
        m_xUpdateBtn->set_sensitive( false );
        m_xExtensionBox->RemoveUnlocked();
    }

    return GenericDialogController::run();
}

// Check dependencies of all packages

bool UpdateRequiredDialog::isEnabled( const uno::Reference< deployment::XPackage > &xPackage )
{
    bool bRegistered = false;
    try {
        beans::Optional< beans::Ambiguous< sal_Bool > > option( xPackage->isRegistered( uno::Reference< task::XAbortChannel >(),
                                                                                        uno::Reference< ucb::XCommandEnvironment >() ) );
        if ( option.IsPresent )
        {
            ::beans::Ambiguous< sal_Bool > const & reg = option.Value;
            if ( reg.IsAmbiguous )
                bRegistered = false;
            else
                bRegistered = reg.Value;
        }
        else
            bRegistered = false;
    }
    catch ( const uno::RuntimeException & ) { throw; }
    catch (const uno::Exception & ) {
        TOOLS_WARN_EXCEPTION( "desktop", "" );
        bRegistered = false;
    }

    return bRegistered;
}

// Checks the dependencies no matter if the extension is enabled or disabled!
bool UpdateRequiredDialog::checkDependencies( const uno::Reference< deployment::XPackage > &xPackage )
{
    bool bDependenciesValid = false;
    try {
        bDependenciesValid = xPackage->checkDependencies( uno::Reference< ucb::XCommandEnvironment >() );
    }
    catch ( const deployment::DeploymentException & ) {}
    return bDependenciesValid;
}


bool UpdateRequiredDialog::hasActiveEntries()
{
    bool bRet = false;
    tools::Long nCount = m_xExtensionBox->GetEntryCount();
    for ( tools::Long nIndex = 0; nIndex < nCount; nIndex++ )
    {
        TEntry_Impl pEntry = m_xExtensionBox->GetEntryData( nIndex );

        if ( isEnabled(pEntry->m_xPackage) && !checkDependencies( pEntry->m_xPackage ) )
        {
            bRet = true;
            break;
        }
    }

    return bRet;
}


void UpdateRequiredDialog::disableAllEntries()
{
    SolarMutexGuard aGuard;

    incBusy();

    tools::Long nCount = m_xExtensionBox->GetEntryCount();
    for ( tools::Long nIndex = 0; nIndex < nCount; nIndex++ )
    {
        TEntry_Impl pEntry = m_xExtensionBox->GetEntryData( nIndex );
        m_rManager.getCmdQueue()->enableExtension(pEntry->m_xPackage, false);
    }

    decBusy();

    if ( ! hasActiveEntries() )
        m_xCloseBtn->set_label( m_sCloseText );
}

//                             ShowLicenseDialog
ShowLicenseDialog::ShowLicenseDialog(weld::Window* pParent,
                                     const uno::Reference< deployment::XPackage> &xPackage)
    : GenericDialogController(pParent, u"desktop/ui/showlicensedialog.ui"_ustr, u"ShowLicenseDialog"_ustr)
    , m_xLicenseText(m_xBuilder->weld_text_view(u"textview"_ustr))
{
    m_xLicenseText->set_size_request(m_xLicenseText->get_approximate_digit_width() * 72,
                                     m_xLicenseText->get_height_rows(21));
    m_xLicenseText->set_text(xPackage->getLicenseText());
}

ShowLicenseDialog::~ShowLicenseDialog()
{
}

// UpdateRequiredDialogService

UpdateRequiredDialogService::UpdateRequiredDialogService( SAL_UNUSED_PARAMETER uno::Sequence< uno::Any > const&,
                                                          uno::Reference< uno::XComponentContext > xComponentContext )
    : m_xComponentContext(std::move( xComponentContext ))
{
}

// XServiceInfo
OUString UpdateRequiredDialogService::getImplementationName()
{
    return u"com.sun.star.comp.deployment.ui.UpdateRequiredDialog"_ustr;
}

sal_Bool UpdateRequiredDialogService::supportsService( const OUString& ServiceName )
{
    return cppu::supportsService(this, ServiceName);
}

css::uno::Sequence< OUString > UpdateRequiredDialogService::getSupportedServiceNames()
{
    return { u"com.sun.star.deployment.ui.UpdateRequiredDialog"_ustr };
}


// XExecutableDialog

void UpdateRequiredDialogService::setTitle( OUString const & )
{
}


sal_Int16 UpdateRequiredDialogService::execute()
{
    ::rtl::Reference< ::dp_gui::TheExtensionManager > xManager( TheExtensionManager::get(
                                                              m_xComponentContext) );
    xManager->createDialog(true, {});
    sal_Int16 nRet = xManager->execute();

    return nRet;
}


} //namespace dp_gui

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