summaryrefslogtreecommitdiffstats
path: root/desktop/source/deployment/gui
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /desktop/source/deployment/gui
parentInitial commit. (diff)
downloadlibreoffice-upstream.tar.xz
libreoffice-upstream.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'desktop/source/deployment/gui')
-rw-r--r--desktop/source/deployment/gui/deploymentgui.component34
-rw-r--r--desktop/source/deployment/gui/dp_gui.h28
-rw-r--r--desktop/source/deployment/gui/dp_gui_dependencydialog.cxx42
-rw-r--r--desktop/source/deployment/gui/dp_gui_dependencydialog.hxx49
-rw-r--r--desktop/source/deployment/gui/dp_gui_dialog2.cxx1403
-rw-r--r--desktop/source/deployment/gui/dp_gui_dialog2.hxx267
-rw-r--r--desktop/source/deployment/gui/dp_gui_extensioncmdqueue.cxx1115
-rw-r--r--desktop/source/deployment/gui/dp_gui_extensioncmdqueue.hxx94
-rw-r--r--desktop/source/deployment/gui/dp_gui_extlistbox.cxx1144
-rw-r--r--desktop/source/deployment/gui/dp_gui_extlistbox.hxx214
-rw-r--r--desktop/source/deployment/gui/dp_gui_service.cxx316
-rw-r--r--desktop/source/deployment/gui/dp_gui_theextmgr.cxx537
-rw-r--r--desktop/source/deployment/gui/dp_gui_theextmgr.hxx127
-rw-r--r--desktop/source/deployment/gui/dp_gui_updatedata.hxx74
-rw-r--r--desktop/source/deployment/gui/dp_gui_updatedialog.cxx994
-rw-r--r--desktop/source/deployment/gui/dp_gui_updatedialog.hxx168
-rw-r--r--desktop/source/deployment/gui/dp_gui_updateinstalldialog.cxx665
-rw-r--r--desktop/source/deployment/gui/dp_gui_updateinstalldialog.hxx112
-rw-r--r--desktop/source/deployment/gui/license_dialog.cxx244
-rw-r--r--desktop/source/deployment/gui/license_dialog.hxx53
20 files changed, 7680 insertions, 0 deletions
diff --git a/desktop/source/deployment/gui/deploymentgui.component b/desktop/source/deployment/gui/deploymentgui.component
new file mode 100644
index 000000000..3b56863c1
--- /dev/null
+++ b/desktop/source/deployment/gui/deploymentgui.component
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * 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 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.comp.deployment.ui.LicenseDialog"
+ constructor="desktop_LicenseDialog_get_implementation">
+ <service name="com.sun.star.deployment.ui.LicenseDialog"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.deployment.ui.PackageManagerDialog"
+ constructor="desktop_ServiceImpl_get_implementation">
+ <service name="com.sun.star.deployment.ui.PackageManagerDialog"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.deployment.ui.UpdateRequiredDialog"
+ constructor="desktop_UpdateRequiredDialogService_get_implementation">
+ <service name="com.sun.star.deployment.ui.UpdateRequiredDialog"/>
+ </implementation>
+</component>
diff --git a/desktop/source/deployment/gui/dp_gui.h b/desktop/source/deployment/gui/dp_gui.h
new file mode 100644
index 000000000..cb2932ca4
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui.h
@@ -0,0 +1,28 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+namespace dp_gui {
+
+enum PackageState { REGISTERED, NOT_REGISTERED, AMBIGUOUS, NOT_AVAILABLE };
+
+} // namespace dp_gui
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_dependencydialog.cxx b/desktop/source/deployment/gui/dp_gui_dependencydialog.cxx
new file mode 100644
index 000000000..f94b29891
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_dependencydialog.cxx
@@ -0,0 +1,42 @@
+/* -*- 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 <sal/config.h>
+
+#include <rtl/ustring.hxx>
+#include <vcl/weld.hxx>
+
+#include "dp_gui_dependencydialog.hxx"
+
+using dp_gui::DependencyDialog;
+
+DependencyDialog::DependencyDialog(weld::Window* parent, std::vector<OUString> const& dependencies)
+ : GenericDialogController(parent, "desktop/ui/dependenciesdialog.ui", "Dependencies")
+ , m_xList(m_xBuilder->weld_tree_view("depListTreeview"))
+{
+ m_xList->set_size_request(-1, m_xList->get_height_rows(10));
+ for (auto const& dependency : dependencies)
+ {
+ m_xList->append_text(dependency);
+ }
+}
+
+DependencyDialog::~DependencyDialog() {}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_dependencydialog.hxx b/desktop/source/deployment/gui/dp_gui_dependencydialog.hxx
new file mode 100644
index 000000000..bdbab41d1
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_dependencydialog.hxx
@@ -0,0 +1,49 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <vcl/weld.hxx>
+
+#include <vector>
+
+namespace vcl
+{
+class Window;
+}
+
+namespace dp_gui
+{
+class DependencyDialog : public weld::GenericDialogController
+{
+public:
+ DependencyDialog(weld::Window* parent, std::vector<OUString> const& dependencies);
+ virtual ~DependencyDialog() override;
+
+private:
+ DependencyDialog(DependencyDialog const&) = delete;
+ DependencyDialog& operator=(DependencyDialog const&) = delete;
+
+ std::unique_ptr<weld::TreeView> m_xList;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_dialog2.cxx b/desktop/source/deployment/gui/dp_gui_dialog2.cxx
new file mode 100644
index 000000000..7fd0bdac3
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_dialog2.cxx
@@ -0,0 +1,1403 @@
+/* -*- 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_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 <osl/mutex.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 <tools/diagnose_ex.h>
+#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/ExecutableDialogResults.hpp>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <com/sun/star/ui/dialogs/XFilePicker3.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 OUStringLiteral SHARED_PACKAGE_MANAGER = u"shared";
+constexpr OUStringLiteral BUNDLED_PACKAGE_MANAGER = u"bundled";
+
+// ExtBoxWithBtns_Impl
+class ExtBoxWithBtns_Impl : public ExtensionBox_Impl
+{
+ bool m_bInterfaceLocked;
+
+ ExtMgrDialog* m_pParent;
+
+ void SetButtonStatus( const TEntry_Impl& rEntry );
+ OString ShowPopupMenu( const Point &rPos, const tools::Long nPos );
+
+public:
+ explicit ExtBoxWithBtns_Impl(std::unique_ptr<weld::ScrolledWindow> xScroll);
+
+ void InitFromDialog(ExtMgrDialog *pParentDialog);
+
+ virtual bool MouseButtonDown( const MouseEvent& rMEvt ) override;
+ virtual bool Command( const CommandEvent& rCEvt ) override;
+
+ virtual void RecalcAll() override;
+ virtual void selectEntry( const tools::Long nPos ) override;
+
+ void enableButtons( bool bEnable );
+};
+
+ExtBoxWithBtns_Impl::ExtBoxWithBtns_Impl(std::unique_ptr<weld::ScrolledWindow> xScroll)
+ : ExtensionBox_Impl(std::move(xScroll))
+ , m_bInterfaceLocked(false)
+ , m_pParent(nullptr)
+{
+}
+
+void ExtBoxWithBtns_Impl::InitFromDialog(ExtMgrDialog *pParentDialog)
+{
+ setExtensionManager(pParentDialog->getExtensionManager());
+
+ m_pParent = pParentDialog;
+}
+
+void ExtBoxWithBtns_Impl::RecalcAll()
+{
+ const sal_Int32 nActive = getSelIndex();
+
+ if ( nActive != ExtensionBox_Impl::ENTRY_NOTFOUND )
+ {
+ SetButtonStatus( GetEntryData( nActive) );
+ }
+ else
+ {
+ m_pParent->enableOptionsButton( false );
+ m_pParent->enableRemoveButton( false );
+ m_pParent->enableEnableButton( false );
+ }
+
+ ExtensionBox_Impl::RecalcAll();
+}
+
+
+//This function may be called with nPos < 0
+void ExtBoxWithBtns_Impl::selectEntry( const tools::Long nPos )
+{
+ if ( HasActive() && ( nPos == getSelIndex() ) )
+ return;
+
+ ExtensionBox_Impl::selectEntry( nPos );
+}
+
+void ExtBoxWithBtns_Impl::SetButtonStatus(const TEntry_Impl& rEntry)
+{
+ bool bShowOptionBtn = true;
+
+ rEntry->m_bHasButtons = false;
+ if ( ( rEntry->m_eState == REGISTERED ) || ( rEntry->m_eState == NOT_AVAILABLE ) )
+ {
+ m_pParent->enableButtontoEnable( false );
+ }
+ else
+ {
+ m_pParent->enableButtontoEnable( true );
+ bShowOptionBtn = false;
+ }
+
+ if ( ( !rEntry->m_bUser || ( rEntry->m_eState == NOT_AVAILABLE ) || rEntry->m_bMissingDeps )
+ && !rEntry->m_bMissingLic )
+ {
+ m_pParent->enableEnableButton( false );
+ }
+ else
+ {
+ m_pParent->enableEnableButton( !rEntry->m_bLocked );
+ rEntry->m_bHasButtons = true;
+ }
+
+ if ( rEntry->m_bHasOptions && bShowOptionBtn )
+ {
+ m_pParent->enableOptionsButton( true );
+ rEntry->m_bHasButtons = true;
+ }
+ else
+ {
+ m_pParent->enableOptionsButton( false );
+ }
+
+ if ( rEntry->m_bUser || rEntry->m_bShared )
+ {
+ m_pParent->enableRemoveButton( !rEntry->m_bLocked );
+ rEntry->m_bHasButtons = true;
+ }
+ else
+ {
+ m_pParent->enableRemoveButton( false );
+ }
+}
+
+bool ExtBoxWithBtns_Impl::Command(const CommandEvent& rCEvt)
+{
+ if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
+ return ExtensionBox_Impl::Command(rCEvt);
+
+ const Point aMousePos(rCEvt.GetMousePosPixel());
+ const auto nPos = PointToPos(aMousePos);
+ OString sCommand = ShowPopupMenu(aMousePos, nPos);
+
+ if (sCommand == "CMD_ENABLE")
+ m_pParent->enablePackage( GetEntryData( nPos )->m_xPackage, true );
+ else if (sCommand == "CMD_DISABLE")
+ m_pParent->enablePackage( GetEntryData( nPos )->m_xPackage, false );
+ else if (sCommand == "CMD_UPDATE")
+ m_pParent->updatePackage( GetEntryData( nPos )->m_xPackage );
+ else if (sCommand == "CMD_REMOVE")
+ m_pParent->removePackage( GetEntryData( nPos )->m_xPackage );
+ else if (sCommand == "CMD_SHOW_LICENSE")
+ {
+ m_pParent->incBusy();
+ ShowLicenseDialog aLicenseDlg(m_pParent->getDialog(), GetEntryData(nPos)->m_xPackage);
+ aLicenseDlg.run();
+ m_pParent->decBusy();
+ }
+
+ return true;
+}
+
+OString ExtBoxWithBtns_Impl::ShowPopupMenu( const Point & rPos, const tools::Long nPos )
+{
+ if ( nPos >= static_cast<tools::Long>(getItemCount()) )
+ return "CMD_NONE";
+
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(nullptr, "desktop/ui/extensionmenu.ui"));
+ std::unique_ptr<weld::Menu> xPopup(xBuilder->weld_menu("menu"));
+
+#if ENABLE_EXTENSION_UPDATE
+ xPopup->append("CMD_UPDATE", DpResId( RID_CTX_ITEM_CHECK_UPDATE ) );
+#endif
+
+ if ( ! GetEntryData( nPos )->m_bLocked )
+ {
+ if ( GetEntryData( nPos )->m_bUser )
+ {
+ if ( GetEntryData( nPos )->m_eState == REGISTERED )
+ xPopup->append("CMD_DISABLE", DpResId(RID_CTX_ITEM_DISABLE));
+ else if ( GetEntryData( nPos )->m_eState != NOT_AVAILABLE )
+ xPopup->append("CMD_ENABLE", DpResId(RID_CTX_ITEM_ENABLE));
+ }
+ if (!officecfg::Office::ExtensionManager::ExtensionSecurity::DisableExtensionRemoval::get())
+ {
+ xPopup->append("CMD_REMOVE", DpResId(RID_CTX_ITEM_REMOVE));
+ }
+ }
+
+ if ( !GetEntryData( nPos )->m_sLicenseText.isEmpty() )
+ xPopup->append("CMD_SHOW_LICENSE", DpResId(RID_STR_SHOW_LICENSE_CMD));
+
+ return xPopup->popup_at_rect(GetDrawingArea(), tools::Rectangle(rPos, Size(1, 1)));
+}
+
+bool ExtBoxWithBtns_Impl::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ if (m_bInterfaceLocked)
+ return false;
+ return ExtensionBox_Impl::MouseButtonDown(rMEvt);
+}
+
+void ExtBoxWithBtns_Impl::enableButtons( bool bEnable )
+{
+ m_bInterfaceLocked = ! bEnable;
+
+ if ( bEnable )
+ {
+ sal_Int32 nIndex = getSelIndex();
+ if ( nIndex != ExtensionBox_Impl::ENTRY_NOTFOUND )
+ SetButtonStatus( GetEntryData( nIndex ) );
+ }
+ else
+ {
+ m_pParent->enableEnableButton( false );
+ m_pParent->enableOptionsButton( false );
+ m_pParent->enableRemoveButton( false );
+ }
+}
+
+// DialogHelper
+
+DialogHelper::DialogHelper(const uno::Reference< uno::XComponentContext > &xContext,
+ weld::Window* pWindow)
+ : m_pWindow(pWindow)
+ , 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,
+ weld::Widget* pParent,
+ TranslateId pResID,
+ bool &bHadWarning )
+{
+ if ( !bHadWarning && IsSharedPkgMgr( xPackage ) )
+ {
+ const SolarMutexGuard guard;
+ incBusy();
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent,
+ 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(getFrameWeld(),
+ 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(getFrameWeld(),
+ 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(getFrameWeld(),
+ 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(getFrameWeld(), "desktop/ui/installforalldialog.ui"));
+ std::unique_ptr<weld::MessageDialog> xQuery(xBuilder->weld_message_dialog("InstallForAllDialog"));
+ 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 *pManager)
+ : GenericDialogController(pParent, "desktop/ui/extensionmanager.ui", "ExtensionManagerDialog")
+ , DialogHelper(pManager->getContext(), m_xDialog.get())
+ , m_sAddPackages(DpResId(RID_STR_ADD_PACKAGES))
+ , 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_pManager(pManager)
+ , m_xExtensionBox(new ExtBoxWithBtns_Impl(m_xBuilder->weld_scrolled_window("scroll", true)))
+ , m_xExtensionBoxWnd(new weld::CustomWeld(*m_xBuilder, "extensions", *m_xExtensionBox))
+ , m_xOptionsBtn(m_xBuilder->weld_button("optionsbtn"))
+ , m_xAddBtn(m_xBuilder->weld_button("addbtn"))
+ , m_xRemoveBtn(m_xBuilder->weld_button("removebtn"))
+ , m_xEnableBtn(m_xBuilder->weld_button("enablebtn"))
+ , m_xUpdateBtn(m_xBuilder->weld_button("updatebtn"))
+ , m_xCloseBtn(m_xBuilder->weld_button("close"))
+ , m_xBundledCbx(m_xBuilder->weld_check_button("bundled"))
+ , m_xSharedCbx(m_xBuilder->weld_check_button("shared"))
+ , m_xUserCbx(m_xBuilder->weld_check_button("user"))
+ , m_xGetExtensions(m_xBuilder->weld_link_button("getextensions"))
+ , m_xProgressText(m_xBuilder->weld_label("progressft"))
+ , m_xProgressBar(m_xBuilder->weld_progress_bar("progressbar"))
+ , m_xCancelBtn(m_xBuilder->weld_button("cancel"))
+ , m_xSearchEntry(m_xBuilder->weld_entry("search"))
+{
+ m_xExtensionBox->InitFromDialog(this);
+
+ 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_pManager->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, m_xDialog.get(), RID_STR_WARNING_ENABLE_SHARED_EXTENSION, m_bEnableWarning))
+ return;
+ }
+ else
+ {
+ if (!continueOnSharedExtension(xPackage, m_xDialog.get(), RID_STR_WARNING_DISABLE_SHARED_EXTENSION, m_bDisableWarning))
+ return;
+ }
+
+ m_pManager->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, m_xDialog.get(), RID_STR_WARNING_REMOVE_SHARED_EXTENSION, m_bDeleteWarning))
+ return;
+
+ m_pManager->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_pManager->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_pManager->getCmdQueue()->checkForUpdates( std::move(vEntries) );
+}
+
+
+bool ExtMgrDialog::acceptLicense( const uno::Reference< deployment::XPackage > &xPackage )
+{
+ if ( !xPackage.is() )
+ return false;
+
+ m_pManager->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);
+ const uno::Reference<ui::dialogs::XFilePicker3>& xFilePicker = aDlgHelper.GetFilePicker();
+ xFilePicker->setTitle( m_sAddPackages );
+
+ // 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_pManager->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:
+ xFilePicker->appendFilter( StrAllFiles, "*.*" );
+ xFilePicker->appendFilter( DpResId(RID_STR_ALL_SUPPORTED), supportedFilters.makeStringAndClear() );
+ // then supported ones:
+ for (auto const& elem : title2filter)
+ {
+ try
+ {
+ xFilePicker->appendFilter( elem.first, elem.second );
+ }
+ catch (const lang::IllegalArgumentException &)
+ {
+ TOOLS_WARN_EXCEPTION( "desktop", "" );
+ }
+ }
+ xFilePicker->setCurrentFilter( DpResId(RID_STR_ALL_SUPPORTED) );
+
+ if ( xFilePicker->execute() != ui::dialogs::ExecutableDialogResults::OK )
+ return uno::Sequence<OUString>(); // cancelled
+
+ uno::Sequence< OUString > files( xFilePicker->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("");
+ }
+}
+
+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)
+{
+ bool bCallClose = true;
+
+ //only suggest restart if modified and this is the first close attempt
+ if (!m_bClosed && m_pManager->isModified())
+ {
+ m_pManager->clearModified();
+
+ //only suggest restart if we're actually running, e.g. not from standalone unopkg gui
+ if (dp_misc::office_is_running())
+ {
+ SolarMutexGuard aGuard;
+ bCallClose = !::svtools::executeRestartDialog(comphelper::getProcessComponentContext(),
+ m_xDialog.get(),
+ svtools::RESTART_REASON_EXTENSION_INSTALL);
+ }
+ }
+
+ if (bCallClose)
+ m_xDialog->response(RET_CANCEL);
+}
+
+IMPL_LINK( ExtMgrDialog, startProgress, void*, _bLockInterface, void )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ 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("");
+ }
+
+ m_xUpdateBtn->set_sensitive( !bLockInterface && m_xExtensionBox->getItemCount() );
+ m_xExtensionBox->enableButtons( !bLockInterface );
+
+ clearEventID();
+}
+
+
+void ExtMgrDialog::showProgress( bool _bStart )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ 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( const tools::Long nProgress )
+{
+ if ( m_nProgress != nProgress )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_nProgress = nProgress;
+ m_aIdle.Start();
+ }
+}
+
+
+void ExtMgrDialog::updateProgress( const OUString &rText,
+ const uno::Reference< task::XAbortChannel > &xAbortChannel)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ 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_Impl::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_pManager->installPackage( aFileList[0] );
+ }
+
+ decBusy();
+}
+
+IMPL_LINK_NOARG(ExtMgrDialog, HandleRemoveBtn, weld::Button&, void)
+{
+ const sal_Int32 nActive = m_xExtensionBox->getSelIndex();
+
+ if ( nActive != ExtensionBox_Impl::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_Impl::ENTRY_NOTFOUND )
+ {
+ TEntry_Impl pEntry = m_xExtensionBox->GetEntryData( nActive );
+
+ if ( pEntry->m_bMissingLic )
+ acceptLicense( pEntry->m_xPackage );
+ else
+ {
+ const bool bEnable( pEntry->m_eState != 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_pManager->checkUpdates();
+#else
+ (void) this;
+#endif
+}
+
+IMPL_LINK_NOARG(ExtMgrDialog, 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( static_cast<sal_uInt16>(m_nProgress) );
+ }
+}
+
+void ExtMgrDialog::Close()
+{
+ m_pManager->terminateDialog();
+ m_bClosed = true;
+}
+
+//UpdateRequiredDialog
+UpdateRequiredDialog::UpdateRequiredDialog(weld::Window *pParent, TheExtensionManager *pManager)
+ : GenericDialogController(pParent, "desktop/ui/updaterequireddialog.ui", "UpdateRequiredDialog")
+ , DialogHelper(pManager->getContext(), m_xDialog.get())
+ , 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_pManager(pManager)
+ , m_xExtensionBox(new ExtensionBox_Impl(m_xBuilder->weld_scrolled_window("scroll", true)))
+ , m_xExtensionBoxWnd(new weld::CustomWeld(*m_xBuilder, "extensions", *m_xExtensionBox))
+ , m_xUpdateNeeded(m_xBuilder->weld_label("updatelabel"))
+ , m_xUpdateBtn(m_xBuilder->weld_button("ok"))
+ , m_xCloseBtn(m_xBuilder->weld_button("disable"))
+ , m_xCancelBtn(m_xBuilder->weld_button("cancel"))
+ , m_xProgressText(m_xBuilder->weld_label("progresslabel"))
+ , m_xProgressBar(m_xBuilder->weld_progress_bar("progress"))
+{
+ m_xExtensionBox->setExtensionManager(pManager);
+
+ 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_pManager->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 )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ 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 )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ 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( const tools::Long nProgress )
+{
+ if ( m_nProgress != nProgress )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_nProgress = nProgress;
+ m_aIdle.Start();
+ }
+}
+
+
+void UpdateRequiredDialog::updateProgress( const OUString &rText,
+ const uno::Reference< task::XAbortChannel > &xAbortChannel)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ 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)
+{
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+
+ std::vector< uno::Reference< deployment::XPackage > > vUpdateEntries;
+ 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 );
+ }
+
+ aGuard.clear();
+
+ m_pManager->getCmdQueue()->checkForUpdates( std::move(vUpdateEntries) );
+}
+
+
+IMPL_LINK_NOARG(UpdateRequiredDialog, HandleCloseBtn, weld::Button&, void)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ 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()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ 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()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ incBusy();
+
+ tools::Long nCount = m_xExtensionBox->GetEntryCount();
+ for ( tools::Long nIndex = 0; nIndex < nCount; nIndex++ )
+ {
+ TEntry_Impl pEntry = m_xExtensionBox->GetEntryData( nIndex );
+ m_pManager->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, "desktop/ui/showlicensedialog.ui", "ShowLicenseDialog")
+ , m_xLicenseText(m_xBuilder->weld_text_view("textview"))
+{
+ 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 "com.sun.star.comp.deployment.ui.UpdateRequiredDialog";
+}
+
+sal_Bool UpdateRequiredDialogService::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > UpdateRequiredDialogService::getSupportedServiceNames()
+{
+ return { "com.sun.star.deployment.ui.UpdateRequiredDialog" };
+}
+
+
+// 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: */
diff --git a/desktop/source/deployment/gui/dp_gui_dialog2.hxx b/desktop/source/deployment/gui/dp_gui_dialog2.hxx
new file mode 100644
index 000000000..a11ff494e
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_dialog2.hxx
@@ -0,0 +1,267 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <vcl/timer.hxx>
+#include <vcl/idle.hxx>
+#include <vcl/locktoplevels.hxx>
+#include <vcl/customweld.hxx>
+#include <vcl/weld.hxx>
+
+#include <osl/mutex.hxx>
+
+#include <rtl/ustring.hxx>
+
+#include <cppuhelper/implbase.hxx>
+#include <unotools/resmgr.hxx>
+
+#include <com/sun/star/deployment/XPackage.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+struct ImplSVEvent;
+
+namespace dp_gui {
+
+class ExtBoxWithBtns_Impl;
+class ExtensionBox_Impl;
+class TheExtensionManager;
+
+class DialogHelper
+{
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ weld::Window* m_pWindow;
+ ImplSVEvent * m_nEventID;
+ TopLevelWindowLocker m_aBusy;
+
+public:
+ DialogHelper(const css::uno::Reference< css::uno::XComponentContext > &,
+ weld::Window* pWindow);
+ virtual ~DialogHelper();
+
+ void openWebBrowser(const OUString& rURL, const OUString& rTitle);
+ weld::Window* getFrameWeld() const { return m_pWindow; }
+ void PostUserEvent( const Link<void*,void>& rLink, void* pCaller );
+ void clearEventID() { m_nEventID = nullptr; }
+
+ virtual void showProgress( bool bStart ) = 0;
+ virtual void updateProgress( const OUString &rText,
+ const css::uno::Reference< css::task::XAbortChannel > &xAbortChannel) = 0;
+ virtual void updateProgress( const tools::Long nProgress ) = 0;
+
+ virtual void updatePackageInfo( const css::uno::Reference< css::deployment::XPackage > &xPackage ) = 0;
+ virtual void addPackageToList( const css::uno::Reference< css::deployment::XPackage > &xPackage,
+ bool bLicenseMissing = false ) = 0;
+
+ virtual void prepareChecking() = 0;
+ virtual void checkEntries() = 0;
+
+ static bool IsSharedPkgMgr( const css::uno::Reference< css::deployment::XPackage > &);
+ bool continueOnSharedExtension( const css::uno::Reference< css::deployment::XPackage > &,
+ weld::Widget* pParent,
+ TranslateId pResID,
+ bool &bHadWarning );
+
+ void incBusy() { m_aBusy.incBusy(m_pWindow); }
+ void decBusy() { m_aBusy.decBusy(); }
+ bool isBusy() const { return m_aBusy.isBusy(); }
+ bool installExtensionWarn(std::u16string_view rExtensionURL);
+ bool installForAllUsers(bool &bInstallForAll);
+};
+
+class ExtMgrDialog : public weld::GenericDialogController
+ , public DialogHelper
+{
+ const OUString m_sAddPackages;
+ OUString m_sProgressText;
+ ::osl::Mutex m_aMutex;
+ bool m_bHasProgress;
+ bool m_bProgressChanged;
+ bool m_bStartProgress;
+ bool m_bStopProgress;
+ bool m_bEnableWarning;
+ bool m_bDisableWarning;
+ bool m_bDeleteWarning;
+ bool m_bClosed;
+ tools::Long m_nProgress;
+ Idle m_aIdle;
+ TheExtensionManager *m_pManager;
+
+ css::uno::Reference< css::task::XAbortChannel > m_xAbortChannel;
+
+ std::unique_ptr<ExtBoxWithBtns_Impl> m_xExtensionBox;
+ std::unique_ptr<weld::CustomWeld> m_xExtensionBoxWnd;
+ std::unique_ptr<weld::Button> m_xOptionsBtn;
+ std::unique_ptr<weld::Button> m_xAddBtn;
+ std::unique_ptr<weld::Button> m_xRemoveBtn;
+ std::unique_ptr<weld::Button> m_xEnableBtn;
+ std::unique_ptr<weld::Button> m_xUpdateBtn;
+ std::unique_ptr<weld::Button> m_xCloseBtn;
+ std::unique_ptr<weld::CheckButton> m_xBundledCbx;
+ std::unique_ptr<weld::CheckButton> m_xSharedCbx;
+ std::unique_ptr<weld::CheckButton> m_xUserCbx;
+ std::unique_ptr<weld::LinkButton> m_xGetExtensions;
+ std::unique_ptr<weld::Label> m_xProgressText;
+ std::unique_ptr<weld::ProgressBar> m_xProgressBar;
+ std::unique_ptr<weld::Button> m_xCancelBtn;
+ std::unique_ptr<weld::Entry> m_xSearchEntry;
+
+ bool removeExtensionWarn(std::u16string_view rExtensionTitle);
+
+ DECL_LINK( HandleOptionsBtn, weld::Button&, void );
+ DECL_LINK( HandleAddBtn, weld::Button&, void );
+ DECL_LINK( HandleRemoveBtn, weld::Button&, void );
+ DECL_LINK( HandleEnableBtn, weld::Button&, void );
+ DECL_LINK( HandleUpdateBtn, weld::Button&, void );
+ DECL_LINK( HandleCancelBtn, weld::Button&, void );
+ DECL_LINK( HandleCloseBtn, weld::Button&, void );
+ DECL_LINK( HandleExtTypeCbx, weld::Toggleable&, void );
+ DECL_LINK( HandleSearch, weld::Entry&, void );
+ DECL_LINK( TimeOutHdl, Timer *, void );
+ DECL_LINK( startProgress, void *, void );
+
+public:
+ ExtMgrDialog(weld::Window * pParent, TheExtensionManager *pManager);
+ virtual ~ExtMgrDialog() override;
+
+ virtual void showProgress( bool bStart ) override;
+ virtual void updateProgress( const OUString &rText,
+ const css::uno::Reference< css::task::XAbortChannel > &xAbortChannel) override;
+ virtual void updateProgress( const tools::Long nProgress ) override;
+
+ virtual void updatePackageInfo( const css::uno::Reference< css::deployment::XPackage > &xPackage ) override;
+
+ void setGetExtensionsURL( const OUString &rURL );
+ virtual void addPackageToList( const css::uno::Reference< css::deployment::XPackage > &,
+ bool bLicenseMissing = false ) override;
+ void enablePackage(const css::uno::Reference< css::deployment::XPackage > &xPackage,
+ bool bEnable );
+ void removePackage(const css::uno::Reference< css::deployment::XPackage > &xPackage );
+ void updatePackage(const css::uno::Reference< css::deployment::XPackage > &xPackage );
+ bool acceptLicense(const css::uno::Reference< css::deployment::XPackage > &xPackage );
+
+ void Close();
+
+ TheExtensionManager* getExtensionManager() const { return m_pManager; }
+
+ virtual void updateList();
+ virtual void prepareChecking() override;
+ virtual void checkEntries() override;
+
+ css::uno::Sequence< OUString > raiseAddPicker();
+
+ void enableOptionsButton( bool bEnable );
+ void enableRemoveButton( bool bEnable );
+ void enableEnableButton( bool bEnable );
+ /*
+ * Transform the button to "Enable", or to "Disable"
+ * based on the value of bEnable.
+ */
+ void enableButtontoEnable( bool bEnable );
+};
+
+
+class UpdateRequiredDialog : public weld::GenericDialogController
+ , public DialogHelper
+{
+ const OUString m_sCloseText;
+ OUString m_sProgressText;
+ ::osl::Mutex m_aMutex;
+ bool m_bHasProgress;
+ bool m_bProgressChanged;
+ bool m_bStartProgress;
+ bool m_bStopProgress;
+ bool m_bHasLockedEntries;
+ tools::Long m_nProgress;
+ Idle m_aIdle;
+ TheExtensionManager *m_pManager;
+
+ css::uno::Reference< css::task::XAbortChannel > m_xAbortChannel;
+
+ std::unique_ptr<ExtensionBox_Impl> m_xExtensionBox;
+ std::unique_ptr<weld::CustomWeld> m_xExtensionBoxWnd;
+ std::unique_ptr<weld::Label> m_xUpdateNeeded;
+ std::unique_ptr<weld::Button> m_xUpdateBtn;
+ std::unique_ptr<weld::Button> m_xCloseBtn;
+ std::unique_ptr<weld::Button> m_xCancelBtn;
+ std::unique_ptr<weld::Label> m_xProgressText;
+ std::unique_ptr<weld::ProgressBar> m_xProgressBar;
+
+ DECL_LINK( HandleUpdateBtn, weld::Button&, void );
+ DECL_LINK( HandleCloseBtn, weld::Button&, void );
+ DECL_LINK( HandleCancelBtn, weld::Button&, void );
+ DECL_LINK( TimeOutHdl, Timer *, void );
+ DECL_LINK( startProgress, void *, void );
+
+ static bool isEnabled( const css::uno::Reference< css::deployment::XPackage > &xPackage );
+ static bool checkDependencies( const css::uno::Reference< css::deployment::XPackage > &xPackage );
+ bool hasActiveEntries();
+ void disableAllEntries();
+
+public:
+ UpdateRequiredDialog(weld::Window * pParent, TheExtensionManager *pManager);
+ virtual ~UpdateRequiredDialog() override;
+
+ virtual short run() override;
+
+ virtual void showProgress( bool bStart ) override;
+ virtual void updateProgress( const OUString &rText,
+ const css::uno::Reference< css::task::XAbortChannel > &xAbortChannel) override;
+ virtual void updateProgress( const tools::Long nProgress ) override;
+
+ virtual void updatePackageInfo( const css::uno::Reference< css::deployment::XPackage > &xPackage ) override;
+
+ virtual void addPackageToList( const css::uno::Reference< css::deployment::XPackage > &,
+ bool bLicenseMissing = false ) override;
+
+ virtual void prepareChecking() override;
+ virtual void checkEntries() override;
+};
+
+
+class ShowLicenseDialog : public weld::GenericDialogController
+{
+ std::unique_ptr<weld::TextView> m_xLicenseText;
+public:
+ ShowLicenseDialog(weld::Window * pParent, const css::uno::Reference< css::deployment::XPackage > &xPackage);
+ virtual ~ShowLicenseDialog() override;
+};
+
+class UpdateRequiredDialogService : public ::cppu::WeakImplHelper< css::ui::dialogs::XExecutableDialog, css::lang::XServiceInfo >
+{
+ css::uno::Reference< css::uno::XComponentContext > const m_xComponentContext;
+public:
+ UpdateRequiredDialogService( css::uno::Sequence< css::uno::Any > const & args,
+ css::uno::Reference< css::uno::XComponentContext> xComponentContext );
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XExecutableDialog
+ virtual void SAL_CALL setTitle( OUString const & title ) override;
+ virtual sal_Int16 SAL_CALL execute() override;
+};
+
+} // namespace dp_gui
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_extensioncmdqueue.cxx b/desktop/source/deployment/gui/dp_gui_extensioncmdqueue.cxx
new file mode 100644
index 000000000..fd70b7982
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_extensioncmdqueue.cxx
@@ -0,0 +1,1115 @@
+/* -*- 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 <sal/config.h>
+
+#include <com/sun/star/beans/NamedValue.hpp>
+
+#include <com/sun/star/deployment/DependencyException.hpp>
+#include <com/sun/star/deployment/LicenseException.hpp>
+#include <com/sun/star/deployment/VersionException.hpp>
+#include <com/sun/star/deployment/InstallException.hpp>
+#include <com/sun/star/deployment/PlatformException.hpp>
+
+#include <com/sun/star/deployment/ui/LicenseDialog.hpp>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/deployment/XPackage.hpp>
+
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/task/XAbortChannel.hpp>
+#include <com/sun/star/task/XInteractionAbort.hpp>
+#include <com/sun/star/task/XInteractionApprove.hpp>
+
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+
+#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/TypeClass.hpp>
+#include <o3tl/any.hxx>
+#include <osl/diagnose.h>
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+#include <salhelper/thread.hxx>
+#include <ucbhelper/content.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/anytostring.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+
+#include "dp_gui_extensioncmdqueue.hxx"
+#include "dp_gui_dependencydialog.hxx"
+#include "dp_gui_dialog2.hxx"
+#include <dp_shared.hxx>
+#include <strings.hrc>
+#include "dp_gui_theextmgr.hxx"
+#include "dp_gui_updatedialog.hxx"
+#include "dp_gui_updateinstalldialog.hxx"
+#include <dp_dependencies.hxx>
+#include <dp_misc.h>
+#include <dp_identifier.hxx>
+#include <dp_version.hxx>
+
+#include <condition_variable>
+#include <queue>
+#include <memory>
+#include <mutex>
+
+#ifdef _WIN32
+#include <o3tl/safeCoInitUninit.hxx>
+#endif
+
+
+using namespace ::com::sun::star;
+
+namespace {
+
+OUString getVersion( OUString const & sVersion )
+{
+ return ( sVersion.isEmpty() ) ? OUString( "0" ) : sVersion;
+}
+
+OUString getVersion( const uno::Reference< deployment::XPackage > &rPackage )
+{
+ return getVersion( rPackage->getVersion());
+}
+}
+
+
+namespace dp_gui {
+
+namespace {
+
+class ProgressCmdEnv
+ : public ::cppu::WeakImplHelper< ucb::XCommandEnvironment,
+ task::XInteractionHandler,
+ ucb::XProgressHandler >
+{
+ uno::Reference< task::XInteractionHandler2> m_xHandler;
+ uno::Reference< uno::XComponentContext > m_xContext;
+
+ DialogHelper* m_pDialogHelper;
+ OUString m_sTitle;
+ bool m_bWarnUser;
+ sal_Int32 m_nCurrentProgress;
+
+ void updateProgress();
+
+ /// @throws uno::RuntimeException
+ void update_( uno::Any const & Status );
+
+public:
+ /** When param bAskWhenInstalling = true, then the user is asked if he
+ agrees to install this extension. In case this extension is already installed
+ then the user is also notified and asked if he wants to replace that existing
+ extension. In first case an interaction request with an InstallException
+ will be handled and in the second case a VersionException will be handled.
+ */
+
+ ProgressCmdEnv( uno::Reference< uno::XComponentContext > xContext,
+ DialogHelper* pDialogHelper,
+ OUString aTitle )
+ : m_xContext(std::move( xContext ))
+ , m_pDialogHelper( pDialogHelper )
+ , m_sTitle(std::move( aTitle ))
+ , m_bWarnUser( false )
+ , m_nCurrentProgress(0)
+ {}
+
+ weld::Window* activeDialog() { return m_pDialogHelper ? m_pDialogHelper->getFrameWeld() : nullptr; }
+
+ void startProgress();
+ void stopProgress();
+ void progressSection( const OUString &rText,
+ const uno::Reference< task::XAbortChannel > &xAbortChannel );
+ void setWarnUser( bool bNewVal ) { m_bWarnUser = bNewVal; }
+
+ // XCommandEnvironment
+ virtual uno::Reference< task::XInteractionHandler > SAL_CALL getInteractionHandler() override;
+ virtual uno::Reference< ucb::XProgressHandler > SAL_CALL getProgressHandler() override;
+
+ // XInteractionHandler
+ virtual void SAL_CALL handle( uno::Reference< task::XInteractionRequest > const & xRequest ) override;
+
+ // XProgressHandler
+ virtual void SAL_CALL push( uno::Any const & Status ) override;
+ virtual void SAL_CALL update( uno::Any const & Status ) override;
+ virtual void SAL_CALL pop() override;
+};
+
+
+struct ExtensionCmd
+{
+ enum E_CMD_TYPE { ADD, ENABLE, DISABLE, REMOVE, CHECK_FOR_UPDATES, ACCEPT_LICENSE };
+
+ E_CMD_TYPE m_eCmdType;
+ bool m_bWarnUser;
+ OUString m_sExtensionURL;
+ OUString m_sRepository;
+ uno::Reference< deployment::XPackage > m_xPackage;
+ std::vector< uno::Reference< deployment::XPackage > > m_vExtensionList;
+
+ ExtensionCmd( const E_CMD_TYPE eCommand,
+ OUString aExtensionURL,
+ OUString aRepository,
+ const bool bWarnUser )
+ : m_eCmdType( eCommand ),
+ m_bWarnUser( bWarnUser ),
+ m_sExtensionURL(std::move( aExtensionURL )),
+ m_sRepository(std::move( aRepository )) {};
+ ExtensionCmd( const E_CMD_TYPE eCommand,
+ uno::Reference< deployment::XPackage > xPackage )
+ : m_eCmdType( eCommand ),
+ m_bWarnUser( false ),
+ m_xPackage(std::move( xPackage )) {};
+ ExtensionCmd( const E_CMD_TYPE eCommand,
+ std::vector<uno::Reference<deployment::XPackage > >&&vExtensionList )
+ : m_eCmdType( eCommand ),
+ m_bWarnUser( false ),
+ m_vExtensionList( std::move(vExtensionList) ) {};
+};
+
+}
+
+typedef std::shared_ptr< ExtensionCmd > TExtensionCmd;
+
+
+class ExtensionCmdQueue::Thread: public salhelper::Thread
+{
+public:
+ Thread( DialogHelper *pDialogHelper,
+ TheExtensionManager *pManager,
+ uno::Reference< uno::XComponentContext > xContext );
+
+ void addExtension( const OUString &rExtensionURL,
+ const OUString &rRepository,
+ const bool bWarnUser );
+ void removeExtension( const uno::Reference< deployment::XPackage > &rPackage );
+ void enableExtension( const uno::Reference< deployment::XPackage > &rPackage,
+ const bool bEnable );
+ void checkForUpdates( std::vector<uno::Reference<deployment::XPackage > > && vExtensionList );
+ void acceptLicense( const uno::Reference< deployment::XPackage > &rPackage );
+ void stop();
+ bool isBusy();
+
+private:
+ virtual ~Thread() override;
+
+ virtual void execute() override;
+
+ void _insert(const TExtensionCmd& rExtCmd);
+
+ void _addExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
+ const OUString &rPackageURL,
+ const OUString &rRepository,
+ const bool bWarnUser );
+ void _removeExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
+ const uno::Reference< deployment::XPackage > &xPackage );
+ void _enableExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
+ const uno::Reference< deployment::XPackage > &xPackage );
+ void _disableExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
+ const uno::Reference< deployment::XPackage > &xPackage );
+ void _checkForUpdates( std::vector<uno::Reference<deployment::XPackage > > &&vExtensionList );
+ void _acceptLicense( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
+ const uno::Reference< deployment::XPackage > &xPackage );
+
+ enum Input { NONE, START, STOP };
+
+ uno::Reference< uno::XComponentContext > m_xContext;
+ std::queue< TExtensionCmd > m_queue;
+
+ DialogHelper *m_pDialogHelper;
+ TheExtensionManager *m_pManager;
+
+ const OUString m_sEnablingPackages;
+ const OUString m_sDisablingPackages;
+ const OUString m_sAddingPackages;
+ const OUString m_sRemovingPackages;
+ const OUString m_sDefaultCmd;
+ const OUString m_sAcceptLicense;
+ std::condition_variable m_wakeup;
+ std::mutex m_mutex;
+ Input m_eInput;
+ bool m_bStopped;
+ bool m_bWorking;
+};
+
+
+void ProgressCmdEnv::startProgress()
+{
+ m_nCurrentProgress = 0;
+
+ if ( m_pDialogHelper )
+ m_pDialogHelper->showProgress( true );
+}
+
+
+void ProgressCmdEnv::stopProgress()
+{
+ if ( m_pDialogHelper )
+ m_pDialogHelper->showProgress( false );
+}
+
+
+void ProgressCmdEnv::progressSection( const OUString &rText,
+ const uno::Reference< task::XAbortChannel > &xAbortChannel )
+{
+ m_nCurrentProgress = 0;
+ if ( m_pDialogHelper )
+ {
+ m_pDialogHelper->updateProgress( rText, xAbortChannel );
+ m_pDialogHelper->updateProgress( 5 );
+ }
+}
+
+
+void ProgressCmdEnv::updateProgress()
+{
+ tools::Long nProgress = ((m_nCurrentProgress*5) % 100) + 5;
+ if ( m_pDialogHelper )
+ m_pDialogHelper->updateProgress( nProgress );
+}
+
+// XCommandEnvironment
+
+uno::Reference< task::XInteractionHandler > ProgressCmdEnv::getInteractionHandler()
+{
+ return this;
+}
+
+
+uno::Reference< ucb::XProgressHandler > ProgressCmdEnv::getProgressHandler()
+{
+ return this;
+}
+
+
+// XInteractionHandler
+
+void ProgressCmdEnv::handle( uno::Reference< task::XInteractionRequest > const & xRequest )
+{
+ uno::Any request( xRequest->getRequest() );
+ OSL_ASSERT( request.getValueTypeClass() == uno::TypeClass_EXCEPTION );
+ dp_misc::TRACE( "[dp_gui_cmdenv.cxx] incoming request:\n"
+ + ::comphelper::anyToString(request) + "\n");
+
+ lang::WrappedTargetException wtExc;
+ deployment::DependencyException depExc;
+ deployment::LicenseException licExc;
+ deployment::VersionException verExc;
+ deployment::InstallException instExc;
+ deployment::PlatformException platExc;
+
+ // selections:
+ bool approve = false;
+ bool abort = false;
+
+ if (request >>= wtExc) {
+ // handable deployment error signalled, e.g.
+ // bundle item registration failed, notify cause only:
+ uno::Any cause;
+ deployment::DeploymentException dpExc;
+ if (wtExc.TargetException >>= dpExc)
+ cause = dpExc.Cause;
+ else {
+ ucb::CommandFailedException cfExc;
+ if (wtExc.TargetException >>= cfExc)
+ cause = cfExc.Reason;
+ else
+ cause = wtExc.TargetException;
+ }
+ update_( cause );
+
+ // ignore intermediate errors of legacy packages, i.e.
+ // former pkgchk behaviour:
+ const uno::Reference< deployment::XPackage > xPackage( wtExc.Context, uno::UNO_QUERY );
+ OSL_ASSERT( xPackage.is() );
+ if ( xPackage.is() )
+ {
+ const uno::Reference< deployment::XPackageTypeInfo > xPackageType( xPackage->getPackageType() );
+ OSL_ASSERT( xPackageType.is() );
+ if (xPackageType.is())
+ {
+ approve = ( xPackage->isBundle() &&
+ xPackageType->getMediaType().match(
+ "application/vnd.sun.star.legacy-package-bundle" ));
+ }
+ }
+ abort = !approve;
+ }
+ else if (request >>= depExc)
+ {
+ std::vector< OUString > deps;
+ deps.reserve(depExc.UnsatisfiedDependencies.getLength());
+ for (auto const & i : std::as_const(depExc.UnsatisfiedDependencies))
+ {
+ deps.push_back( dp_misc::Dependencies::getErrorText(i) );
+ }
+ {
+ SolarMutexGuard guard;
+ if (m_pDialogHelper)
+ m_pDialogHelper->incBusy();
+ DependencyDialog aDlg(activeDialog(), deps);
+ short n = aDlg.run();
+ if (m_pDialogHelper)
+ m_pDialogHelper->decBusy();
+ // Distinguish between closing the dialog and programmatically
+ // canceling the dialog (headless VCL):
+ approve = n == RET_OK
+ || (n == RET_CANCEL && !Application::IsDialogCancelEnabled());
+ }
+ }
+ else if (request >>= licExc)
+ {
+ SolarMutexGuard guard;
+
+ weld::Window *pTopLevel = activeDialog();
+ if (m_pDialogHelper)
+ m_pDialogHelper->incBusy();
+ uno::Reference< ui::dialogs::XExecutableDialog > xDialog(
+ deployment::ui::LicenseDialog::create(
+ m_xContext, pTopLevel ? pTopLevel->GetXWindow() : nullptr,
+ licExc.ExtensionName, licExc.Text ) );
+ sal_Int16 res = xDialog->execute();
+ if (m_pDialogHelper)
+ m_pDialogHelper->decBusy();
+ if ( res == ui::dialogs::ExecutableDialogResults::CANCEL )
+ abort = true;
+ else if ( res == ui::dialogs::ExecutableDialogResults::OK )
+ approve = true;
+ else
+ {
+ OSL_ASSERT(false);
+ }
+ }
+ else if (request >>= verExc)
+ {
+ TranslateId id;
+ switch (dp_misc::compareVersions(
+ verExc.NewVersion, verExc.Deployed->getVersion() ))
+ {
+ case dp_misc::LESS:
+ id = RID_STR_WARNING_VERSION_LESS;
+ break;
+ case dp_misc::EQUAL:
+ id = RID_STR_WARNING_VERSION_EQUAL;
+ break;
+ default: // dp_misc::GREATER
+ id = RID_STR_WARNING_VERSION_GREATER;
+ break;
+ }
+ OSL_ASSERT( verExc.Deployed.is() );
+ bool bEqualNames = verExc.NewDisplayName ==
+ verExc.Deployed->getDisplayName();
+ {
+ SolarMutexGuard guard;
+
+ if (m_pDialogHelper)
+ m_pDialogHelper->incBusy();
+
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(activeDialog(),
+ VclMessageType::Warning, VclButtonsType::OkCancel, DpResId(id)));
+ OUString s;
+ if (bEqualNames)
+ {
+ s = xBox->get_primary_text();
+ }
+ else if (id != RID_STR_WARNING_VERSION_EQUAL)
+ {
+ //hypothetical: requires two instances of an extension with the same
+ //version to have different display names. Probably the developer forgot
+ //to change the version.
+ s = DpResId(RID_STR_WARNINGBOX_VERSION_EQUAL_DIFFERENT_NAMES);
+ }
+ else if (id != RID_STR_WARNING_VERSION_LESS)
+ {
+ s = DpResId(RID_STR_WARNINGBOX_VERSION_LESS_DIFFERENT_NAMES);
+ }
+ else if (id != RID_STR_WARNING_VERSION_GREATER)
+ {
+ s = DpResId(RID_STR_WARNINGBOX_VERSION_GREATER_DIFFERENT_NAMES);
+ }
+ s = s.replaceAll("$NAME", verExc.NewDisplayName);
+ s = s.replaceAll("$OLDNAME", verExc.Deployed->getDisplayName());
+ s = s.replaceAll("$NEW", getVersion(verExc.NewVersion));
+ s = s.replaceAll("$DEPLOYED", getVersion(verExc.Deployed));
+ xBox->set_primary_text(s);
+ approve = xBox->run() == RET_OK;
+ if (m_pDialogHelper)
+ m_pDialogHelper->decBusy();
+ abort = !approve;
+ }
+ }
+ else if (request >>= instExc)
+ {
+ if ( ! m_bWarnUser )
+ {
+ approve = true;
+ }
+ else
+ {
+ if ( m_pDialogHelper )
+ {
+ SolarMutexGuard guard;
+
+ approve = m_pDialogHelper->installExtensionWarn( instExc.displayName );
+ }
+ else
+ approve = false;
+ abort = !approve;
+ }
+ }
+ else if (request >>= platExc)
+ {
+ SolarMutexGuard guard;
+ OUString sMsg(DpResId(RID_STR_UNSUPPORTED_PLATFORM));
+ sMsg = sMsg.replaceAll("%Name", platExc.package->getDisplayName());
+ if (m_pDialogHelper)
+ m_pDialogHelper->incBusy();
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(activeDialog(),
+ VclMessageType::Warning, VclButtonsType::Ok, sMsg));
+ xBox->run();
+ if (m_pDialogHelper)
+ m_pDialogHelper->decBusy();
+ approve = true;
+ }
+
+ if (!approve && !abort)
+ {
+ // forward to UUI handler:
+ if (! m_xHandler.is()) {
+ // late init:
+ m_xHandler = task::InteractionHandler::createWithParentAndContext(m_xContext, nullptr, m_sTitle);
+ }
+ m_xHandler->handle( xRequest );
+ }
+ else
+ {
+ // select:
+ uno::Sequence< uno::Reference< task::XInteractionContinuation > > conts(
+ xRequest->getContinuations() );
+ uno::Reference< task::XInteractionContinuation > const * pConts = conts.getConstArray();
+ sal_Int32 len = conts.getLength();
+ for ( sal_Int32 pos = 0; pos < len; ++pos )
+ {
+ if (approve) {
+ uno::Reference< task::XInteractionApprove > xInteractionApprove( pConts[ pos ], uno::UNO_QUERY );
+ if (xInteractionApprove.is()) {
+ xInteractionApprove->select();
+ // don't query again for ongoing continuations:
+ approve = false;
+ }
+ }
+ else if (abort) {
+ uno::Reference< task::XInteractionAbort > xInteractionAbort( pConts[ pos ], uno::UNO_QUERY );
+ if (xInteractionAbort.is()) {
+ xInteractionAbort->select();
+ // don't query again for ongoing continuations:
+ abort = false;
+ }
+ }
+ }
+ }
+}
+
+
+// XProgressHandler
+
+void ProgressCmdEnv::push( uno::Any const & rStatus )
+{
+ update_( rStatus );
+}
+
+
+void ProgressCmdEnv::update_( uno::Any const & rStatus )
+{
+ OUString text;
+ if ( rStatus.hasValue() && !( rStatus >>= text) )
+ {
+ if ( auto e = o3tl::tryAccess<uno::Exception>(rStatus) )
+ text = e->Message;
+ if ( text.isEmpty() )
+ text = ::comphelper::anyToString( rStatus ); // fallback
+
+ const SolarMutexGuard aGuard;
+ if (m_pDialogHelper)
+ m_pDialogHelper->incBusy();
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(activeDialog(),
+ VclMessageType::Warning, VclButtonsType::Ok, text));
+ xBox->run();
+ if (m_pDialogHelper)
+ m_pDialogHelper->decBusy();
+ }
+ ++m_nCurrentProgress;
+ updateProgress();
+}
+
+
+void ProgressCmdEnv::update( uno::Any const & rStatus )
+{
+ update_( rStatus );
+}
+
+
+void ProgressCmdEnv::pop()
+{
+ update_( uno::Any() ); // no message
+}
+
+
+ExtensionCmdQueue::Thread::Thread( DialogHelper *pDialogHelper,
+ TheExtensionManager *pManager,
+ uno::Reference< uno::XComponentContext > xContext ) :
+ salhelper::Thread( "dp_gui_extensioncmdqueue" ),
+ m_xContext(std::move( xContext )),
+ m_pDialogHelper( pDialogHelper ),
+ m_pManager( pManager ),
+ m_sEnablingPackages( DpResId( RID_STR_ENABLING_PACKAGES ) ),
+ m_sDisablingPackages( DpResId( RID_STR_DISABLING_PACKAGES ) ),
+ m_sAddingPackages( DpResId( RID_STR_ADDING_PACKAGES ) ),
+ m_sRemovingPackages( DpResId( RID_STR_REMOVING_PACKAGES ) ),
+ m_sDefaultCmd( DpResId( RID_STR_ADD_PACKAGES ) ),
+ m_sAcceptLicense( DpResId( RID_STR_ACCEPT_LICENSE ) ),
+ m_eInput( NONE ),
+ m_bStopped( false ),
+ m_bWorking( false )
+{
+ OSL_ASSERT( pDialogHelper );
+}
+
+
+void ExtensionCmdQueue::Thread::addExtension( const OUString &rExtensionURL,
+ const OUString &rRepository,
+ const bool bWarnUser )
+{
+ if ( !rExtensionURL.isEmpty() )
+ {
+ TExtensionCmd pEntry = std::make_shared<ExtensionCmd>( ExtensionCmd::ADD, rExtensionURL, rRepository, bWarnUser );
+ _insert( pEntry );
+ }
+}
+
+
+void ExtensionCmdQueue::Thread::removeExtension( const uno::Reference< deployment::XPackage > &rPackage )
+{
+ if ( rPackage.is() )
+ {
+ TExtensionCmd pEntry = std::make_shared<ExtensionCmd>( ExtensionCmd::REMOVE, rPackage );
+ _insert( pEntry );
+ }
+}
+
+
+void ExtensionCmdQueue::Thread::acceptLicense( const uno::Reference< deployment::XPackage > &rPackage )
+{
+ if ( rPackage.is() )
+ {
+ TExtensionCmd pEntry = std::make_shared<ExtensionCmd>( ExtensionCmd::ACCEPT_LICENSE, rPackage );
+ _insert( pEntry );
+ }
+}
+
+
+void ExtensionCmdQueue::Thread::enableExtension( const uno::Reference< deployment::XPackage > &rPackage,
+ const bool bEnable )
+{
+ if ( rPackage.is() )
+ {
+ TExtensionCmd pEntry = std::make_shared<ExtensionCmd>( bEnable ? ExtensionCmd::ENABLE :
+ ExtensionCmd::DISABLE,
+ rPackage );
+ _insert( pEntry );
+ }
+}
+
+
+void ExtensionCmdQueue::Thread::checkForUpdates(
+ std::vector<uno::Reference<deployment::XPackage > > && vExtensionList )
+{
+ TExtensionCmd pEntry = std::make_shared<ExtensionCmd>( ExtensionCmd::CHECK_FOR_UPDATES, std::move(vExtensionList) );
+ _insert( pEntry );
+}
+
+
+//Stopping this thread will not abort the installation of extensions.
+void ExtensionCmdQueue::Thread::stop()
+{
+ std::scoped_lock aGuard( m_mutex );
+ m_bStopped = true;
+ m_eInput = STOP;
+ m_wakeup.notify_all();
+}
+
+
+bool ExtensionCmdQueue::Thread::isBusy()
+{
+ std::scoped_lock aGuard( m_mutex );
+ return m_bWorking;
+}
+
+
+ExtensionCmdQueue::Thread::~Thread() {}
+
+
+void ExtensionCmdQueue::Thread::execute()
+{
+#ifdef _WIN32
+ //Needed for use of the service "com.sun.star.system.SystemShellExecute" in
+ //DialogHelper::openWebBrowser
+ int nNbCallCoInitializeExForReinit = 0;
+ o3tl::safeCoInitializeEx(COINIT_APARTMENTTHREADED, nNbCallCoInitializeExForReinit);
+#endif
+ for (;;)
+ {
+ int nSize;
+ Input eInput;
+ {
+ std::unique_lock aGuard( m_mutex );
+ while (m_eInput == NONE) {
+ m_wakeup.wait(aGuard);
+ }
+ eInput = m_eInput;
+ m_eInput = NONE;
+ nSize = m_queue.size();
+ // coverity[missing_lock: FALSE] - maybe due to (by-design) unique_lock vs. scoped_lock?
+ m_bWorking = false;
+ }
+
+ if ( eInput == STOP )
+ break;
+
+ // We only install the extension which are currently in the queue.
+ // The progressbar will be set to show the progress of the current number
+ // of extensions. If we allowed to add extensions now then the progressbar may
+ // have reached the end while we still install newly added extensions.
+ if ( nSize == 0 )
+ continue;
+
+ ::rtl::Reference< ProgressCmdEnv > currentCmdEnv( new ProgressCmdEnv( m_xContext, m_pDialogHelper, m_sDefaultCmd ) );
+
+ // Do not lock the following part with addExtension. addExtension may be called in the main thread.
+ // If the message box "Do you want to install the extension (or similar)" is shown and then
+ // addExtension is called, which then blocks the main thread, then we deadlock.
+ bool bStartProgress = true;
+
+ while ( --nSize >= 0 )
+ {
+ {
+ std::scoped_lock aGuard( m_mutex );
+ m_bWorking = true;
+ }
+
+ try
+ {
+ TExtensionCmd pEntry;
+ {
+ std::scoped_lock queueGuard( m_mutex );
+ pEntry = m_queue.front();
+ m_queue.pop();
+ }
+
+ if ( bStartProgress && ( pEntry->m_eCmdType != ExtensionCmd::CHECK_FOR_UPDATES ) )
+ {
+ currentCmdEnv->startProgress();
+ bStartProgress = false;
+ }
+
+ switch ( pEntry->m_eCmdType ) {
+ case ExtensionCmd::ADD :
+ _addExtension( currentCmdEnv, pEntry->m_sExtensionURL, pEntry->m_sRepository, pEntry->m_bWarnUser );
+ break;
+ case ExtensionCmd::REMOVE :
+ _removeExtension( currentCmdEnv, pEntry->m_xPackage );
+ break;
+ case ExtensionCmd::ENABLE :
+ _enableExtension( currentCmdEnv, pEntry->m_xPackage );
+ break;
+ case ExtensionCmd::DISABLE :
+ _disableExtension( currentCmdEnv, pEntry->m_xPackage );
+ break;
+ case ExtensionCmd::CHECK_FOR_UPDATES :
+ _checkForUpdates( std::vector(pEntry->m_vExtensionList) );
+ break;
+ case ExtensionCmd::ACCEPT_LICENSE :
+ _acceptLicense( currentCmdEnv, pEntry->m_xPackage );
+ break;
+ }
+ }
+ catch ( const ucb::CommandAbortedException & )
+ {
+ //This exception is thrown when the user clicks cancel on the progressbar.
+ //Then we cancel the installation of all extensions and remove them from
+ //the queue.
+ {
+ std::scoped_lock queueGuard2(m_mutex);
+ while ( --nSize >= 0 )
+ m_queue.pop();
+ }
+ break;
+ }
+ catch ( const ucb::CommandFailedException & )
+ {
+ //This exception is thrown when a user clicked cancel in the messagebox which was
+ //started by the interaction handler. For example the user will be asked if he/she
+ //really wants to install the extension.
+ //These interactions run for exactly one extension at a time. Therefore we continue
+ //with installing the remaining extensions.
+ continue;
+ }
+ catch ( const uno::Exception & )
+ {
+ //Todo display the user an error
+ //see also DialogImpl::SyncPushButton::Click()
+ uno::Any exc( ::cppu::getCaughtException() );
+ OUString msg;
+ deployment::DeploymentException dpExc;
+ if (exc >>= dpExc)
+ {
+ if (auto e = o3tl::tryAccess<uno::Exception>(dpExc.Cause))
+ {
+ // notify error cause only:
+ msg = e->Message;
+ }
+ }
+ if (msg.isEmpty()) // fallback for debugging purposes
+ msg = ::comphelper::anyToString(exc);
+
+ const SolarMutexGuard guard;
+ if (m_pDialogHelper)
+ m_pDialogHelper->incBusy();
+
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(currentCmdEnv->activeDialog(),
+ VclMessageType::Warning, VclButtonsType::Ok, msg));
+ if (m_pDialogHelper)
+ xBox->set_title(m_pDialogHelper->getFrameWeld()->get_title());
+ xBox->run();
+ if (m_pDialogHelper)
+ m_pDialogHelper->decBusy();
+ //Continue with installation of the remaining extensions
+ }
+ {
+ std::scoped_lock aGuard( m_mutex );
+ m_bWorking = false;
+ }
+ }
+
+ {
+ // when leaving the while loop with break, we should set working to false, too
+ std::scoped_lock aGuard( m_mutex );
+ m_bWorking = false;
+ }
+
+ if ( !bStartProgress )
+ currentCmdEnv->stopProgress();
+ }
+ //end for
+#ifdef _WIN32
+ o3tl::safeCoUninitializeReinit(COINIT_MULTITHREADED, nNbCallCoInitializeExForReinit);
+#endif
+}
+
+
+void ExtensionCmdQueue::Thread::_addExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
+ const OUString &rPackageURL,
+ const OUString &rRepository,
+ const bool bWarnUser )
+{
+ //check if we have a string in anyTitle. For example "unopkg gui \" caused anyTitle to be void
+ //and anyTitle.get<OUString> throws as RuntimeException.
+ uno::Any anyTitle;
+ try
+ {
+ anyTitle = ::ucbhelper::Content( rPackageURL, rCmdEnv, m_xContext ).getPropertyValue( "Title" );
+ }
+ catch ( const uno::Exception & )
+ {
+ return;
+ }
+
+ OUString sName;
+ if ( ! (anyTitle >>= sName) )
+ {
+ OSL_FAIL("Could not get file name for extension.");
+ return;
+ }
+
+ rCmdEnv->setWarnUser( bWarnUser );
+ uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
+ uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
+ OUString sTitle(
+ m_sAddingPackages.replaceAll("%EXTENSION_NAME", sName));
+ rCmdEnv->progressSection( sTitle, xAbortChannel );
+
+ try
+ {
+ xExtMgr->addExtension(rPackageURL, uno::Sequence<beans::NamedValue>(),
+ rRepository, xAbortChannel, rCmdEnv );
+ }
+ catch ( const ucb::CommandFailedException & )
+ {
+ // When the extension is already installed we'll get a dialog asking if we want to overwrite. If we then press
+ // cancel this exception is thrown.
+ }
+ catch ( const ucb::CommandAbortedException & )
+ {
+ // User clicked the cancel button
+ // TODO: handle cancel
+ }
+ rCmdEnv->setWarnUser( false );
+}
+
+
+void ExtensionCmdQueue::Thread::_removeExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
+ const uno::Reference< deployment::XPackage > &xPackage )
+{
+ uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
+ uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
+ OUString sTitle(
+ m_sRemovingPackages.replaceAll("%EXTENSION_NAME",
+ xPackage->getDisplayName()));
+ rCmdEnv->progressSection( sTitle, xAbortChannel );
+
+ OUString id( dp_misc::getIdentifier( xPackage ) );
+ try
+ {
+ xExtMgr->removeExtension( id, xPackage->getName(), xPackage->getRepositoryName(), xAbortChannel, rCmdEnv );
+ }
+ catch ( const deployment::DeploymentException & )
+ {}
+ catch ( const ucb::CommandFailedException & )
+ {}
+ catch ( const ucb::CommandAbortedException & )
+ {}
+
+ // Check, if there are still updates to be notified via menu bar icon
+ uno::Sequence< uno::Sequence< OUString > > aItemList;
+ UpdateDialog::createNotifyJob( false, aItemList );
+}
+
+
+void ExtensionCmdQueue::Thread::_checkForUpdates(
+ std::vector<uno::Reference<deployment::XPackage > > && vExtensionList )
+{
+ const SolarMutexGuard guard;
+
+ if (m_pDialogHelper)
+ m_pDialogHelper->incBusy();
+
+ std::vector< UpdateData > vData;
+ UpdateDialog aUpdateDialog(m_xContext, m_pDialogHelper ? m_pDialogHelper->getFrameWeld() : nullptr, std::move(vExtensionList), &vData);
+
+ aUpdateDialog.notifyMenubar( true, false ); // prepare the checking, if there updates to be notified via menu bar icon
+
+ bool bOk = aUpdateDialog.run() == RET_OK;
+ if (m_pDialogHelper)
+ m_pDialogHelper->decBusy();
+
+ if (bOk && !vData.empty())
+ {
+ // If there is at least one directly downloadable extension then we
+ // open the install dialog.
+ std::vector< UpdateData > dataDownload;
+
+ for (auto const& data : vData)
+ {
+ if ( data.sWebsiteURL.isEmpty() )
+ dataDownload.push_back(data);
+ }
+
+ short nDialogResult = RET_OK;
+ if ( !dataDownload.empty() )
+ {
+ if (m_pDialogHelper)
+ m_pDialogHelper->incBusy();
+ UpdateInstallDialog aDlg(m_pDialogHelper ? m_pDialogHelper->getFrameWeld() : nullptr, dataDownload, m_xContext);
+ nDialogResult = aDlg.run();
+ if (m_pDialogHelper)
+ m_pDialogHelper->decBusy();
+ aUpdateDialog.notifyMenubar( false, true ); // Check, if there are still pending updates to be notified via menu bar icon
+ }
+ else
+ aUpdateDialog.notifyMenubar( false, false ); // Check, if there are pending updates to be notified via menu bar icon
+
+ //Now start the webbrowser and navigate to the websites where we get the updates
+ if ( RET_OK == nDialogResult )
+ {
+ for (auto const& data : vData)
+ {
+ if ( m_pDialogHelper && ( !data.sWebsiteURL.isEmpty() ) )
+ m_pDialogHelper->openWebBrowser( data.sWebsiteURL, m_pDialogHelper->getFrameWeld()->get_title() );
+ }
+ }
+ }
+ else
+ aUpdateDialog.notifyMenubar( false, false ); // check if there updates to be notified via menu bar icon
+}
+
+
+void ExtensionCmdQueue::Thread::_enableExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
+ const uno::Reference< deployment::XPackage > &xPackage )
+{
+ if ( !xPackage.is() )
+ return;
+
+ uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
+ uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
+ OUString sTitle(
+ m_sEnablingPackages.replaceAll("%EXTENSION_NAME",
+ xPackage->getDisplayName()));
+ rCmdEnv->progressSection( sTitle, xAbortChannel );
+
+ try
+ {
+ xExtMgr->enableExtension( xPackage, xAbortChannel, rCmdEnv );
+ if ( m_pDialogHelper )
+ m_pDialogHelper->updatePackageInfo( xPackage );
+ }
+ catch ( const ::ucb::CommandAbortedException & )
+ {}
+}
+
+
+void ExtensionCmdQueue::Thread::_disableExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
+ const uno::Reference< deployment::XPackage > &xPackage )
+{
+ if ( !xPackage.is() )
+ return;
+
+ uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
+ uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
+ OUString sTitle(
+ m_sDisablingPackages.replaceAll("%EXTENSION_NAME",
+ xPackage->getDisplayName()));
+ rCmdEnv->progressSection( sTitle, xAbortChannel );
+
+ try
+ {
+ xExtMgr->disableExtension( xPackage, xAbortChannel, rCmdEnv );
+ if ( m_pDialogHelper )
+ m_pDialogHelper->updatePackageInfo( xPackage );
+ }
+ catch ( const ::ucb::CommandAbortedException & )
+ {}
+}
+
+
+void ExtensionCmdQueue::Thread::_acceptLicense( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
+ const uno::Reference< deployment::XPackage > &xPackage )
+{
+ if ( !xPackage.is() )
+ return;
+
+ uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
+ uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
+ OUString sTitle(
+ m_sAcceptLicense.replaceAll("%EXTENSION_NAME",
+ xPackage->getDisplayName()));
+ rCmdEnv->progressSection( sTitle, xAbortChannel );
+
+ try
+ {
+ xExtMgr->checkPrerequisitesAndEnable( xPackage, xAbortChannel, rCmdEnv );
+ if ( m_pDialogHelper )
+ m_pDialogHelper->updatePackageInfo( xPackage );
+ }
+ catch ( const ::ucb::CommandAbortedException & )
+ {}
+}
+
+void ExtensionCmdQueue::Thread::_insert(const TExtensionCmd& rExtCmd)
+{
+ std::scoped_lock aGuard( m_mutex );
+
+ // If someone called stop then we do not process the command -> game over!
+ if ( m_bStopped )
+ return;
+
+ m_queue.push( rExtCmd );
+ m_eInput = START;
+ m_wakeup.notify_all();
+}
+
+
+ExtensionCmdQueue::ExtensionCmdQueue( DialogHelper * pDialogHelper,
+ TheExtensionManager *pManager,
+ const uno::Reference< uno::XComponentContext > &rContext )
+ : m_thread( new Thread( pDialogHelper, pManager, rContext ) )
+{
+ m_thread->launch();
+}
+
+ExtensionCmdQueue::~ExtensionCmdQueue() {
+ m_thread->stop();
+ m_thread->join();
+}
+
+void ExtensionCmdQueue::addExtension( const OUString & extensionURL,
+ const OUString & repository,
+ const bool bWarnUser )
+{
+ m_thread->addExtension( extensionURL, repository, bWarnUser );
+}
+
+void ExtensionCmdQueue::removeExtension( const uno::Reference< deployment::XPackage > &rPackage )
+{
+ m_thread->removeExtension( rPackage );
+}
+
+void ExtensionCmdQueue::enableExtension( const uno::Reference< deployment::XPackage > &rPackage,
+ const bool bEnable )
+{
+ m_thread->enableExtension( rPackage, bEnable );
+}
+
+void ExtensionCmdQueue::checkForUpdates( std::vector<uno::Reference<deployment::XPackage > > && vExtensionList )
+{
+ m_thread->checkForUpdates( std::move(vExtensionList) );
+}
+
+void ExtensionCmdQueue::acceptLicense( const uno::Reference< deployment::XPackage > &rPackage )
+{
+ m_thread->acceptLicense( rPackage );
+}
+
+void ExtensionCmdQueue::syncRepositories( const uno::Reference< uno::XComponentContext > &xContext )
+{
+ dp_misc::syncRepositories( false, new ProgressCmdEnv( xContext, nullptr, "Extension Manager" ) );
+}
+
+bool ExtensionCmdQueue::isBusy()
+{
+ return m_thread->isBusy();
+}
+
+void handleInteractionRequest( const uno::Reference< uno::XComponentContext > & xContext,
+ const uno::Reference< task::XInteractionRequest > & xRequest )
+{
+ ::rtl::Reference< ProgressCmdEnv > xCmdEnv( new ProgressCmdEnv( xContext, nullptr, "Extension Manager" ) );
+ xCmdEnv->handle( xRequest );
+}
+
+} //namespace dp_gui
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_extensioncmdqueue.hxx b/desktop/source/deployment/gui/dp_gui_extensioncmdqueue.hxx
new file mode 100644
index 000000000..3703d1e8c
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_extensioncmdqueue.hxx
@@ -0,0 +1,94 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <rtl/ref.hxx>
+
+#include <vector>
+
+#include "dp_gui_updatedata.hxx"
+
+/// @HTML
+
+namespace com::sun::star {
+ namespace task { class XInteractionRequest; }
+ namespace uno { class XComponentContext; }
+}
+
+namespace dp_gui {
+
+class DialogHelper;
+class TheExtensionManager;
+
+/**
+ Manages installing of extensions in the GUI mode. Requests for installing
+ Extensions can be asynchronous. For example, the Extension Manager is running
+ in an office process and someone uses the system integration to install an Extension.
+ That is, the user double clicks an extension symbol in a file browser, which then
+ causes an invocation of "unopkg gui ext". When at that time the Extension Manager
+ already performs a task, triggered by the user (for example, add, update, disable,
+ enable) then adding of the extension will be postponed until the user has finished
+ the task.
+
+ This class also ensures that the extensions are not installed in the main thread.
+ Doing so would cause a deadlock because of the progress bar which needs to be constantly
+ updated.
+*/
+class ExtensionCmdQueue {
+
+public:
+ /**
+ Create an instance.
+ */
+ ExtensionCmdQueue( DialogHelper * pDialogHelper,
+ TheExtensionManager *pManager,
+ const css::uno::Reference< css::uno::XComponentContext > & rContext);
+
+ ~ExtensionCmdQueue();
+
+ void addExtension( const OUString &rExtensionURL,
+ const OUString &rRepository,
+ const bool bWarnUser );
+ void removeExtension( const css::uno::Reference< css::deployment::XPackage > &rPackage );
+ void enableExtension( const css::uno::Reference< css::deployment::XPackage > &rPackage,
+ const bool bEnable );
+ void checkForUpdates( std::vector< css::uno::Reference< css::deployment::XPackage > > && vList );
+ void acceptLicense( const css::uno::Reference< css::deployment::XPackage > &rPackage );
+ static void syncRepositories( const css::uno::Reference< css::uno::XComponentContext > & xContext );
+
+ bool isBusy();
+private:
+ ExtensionCmdQueue(ExtensionCmdQueue const &) = delete;
+ ExtensionCmdQueue& operator =(ExtensionCmdQueue const &) = delete;
+
+ class Thread;
+
+ rtl::Reference< Thread > m_thread;
+};
+
+void handleInteractionRequest( const css::uno::Reference< css::uno::XComponentContext > & xContext,
+ const css::uno::Reference< css::task::XInteractionRequest > & xRequest );
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_extlistbox.cxx b/desktop/source/deployment/gui/dp_gui_extlistbox.cxx
new file mode 100644
index 000000000..d30d24f79
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_extlistbox.cxx
@@ -0,0 +1,1144 @@
+/* -*- 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 <dp_shared.hxx>
+#include <strings.hrc>
+#include "dp_gui.h"
+#include "dp_gui_extlistbox.hxx"
+#include "dp_gui_theextmgr.hxx"
+#include <dp_dependencies.hxx>
+#include <bitmaps.hlst>
+
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/i18n/CollatorOptions.hpp>
+#include <com/sun/star/deployment/DependencyException.hpp>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/deployment/ExtensionRemovedException.hpp>
+#include <com/sun/star/system/XSystemShellExecute.hpp>
+#include <com/sun/star/system/SystemShellExecuteFlags.hpp>
+#include <com/sun/star/system/SystemShellExecute.hpp>
+#include <cppuhelper/weakref.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <rtl/ustrbuf.hxx>
+#include <utility>
+#include <vcl/event.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/weldutils.hxx>
+#include <algorithm>
+
+constexpr OUStringLiteral USER_PACKAGE_MANAGER = u"user";
+constexpr OUStringLiteral SHARED_PACKAGE_MANAGER = u"shared";
+
+using namespace ::com::sun::star;
+
+namespace dp_gui {
+
+namespace {
+
+struct FindWeakRef
+{
+ const uno::Reference<deployment::XPackage> m_extension;
+
+ explicit FindWeakRef( uno::Reference<deployment::XPackage> ext): m_extension(std::move(ext)) {}
+ bool operator () (uno::WeakReference< deployment::XPackage > const & ref);
+};
+
+bool FindWeakRef::operator () (uno::WeakReference< deployment::XPackage > const & ref)
+{
+ const uno::Reference<deployment::XPackage> ext(ref);
+ return ext == m_extension;
+}
+
+} // end namespace
+
+// struct Entry_Impl
+
+Entry_Impl::Entry_Impl( const uno::Reference< deployment::XPackage > &xPackage,
+ const PackageState eState, const bool bReadOnly ) :
+ m_bActive( false ),
+ m_bLocked( bReadOnly ),
+ m_bHasOptions( false ),
+ m_bUser( false ),
+ m_bShared( false ),
+ m_bNew( false ),
+ m_bChecked( false ),
+ m_bMissingDeps( false ),
+ m_bHasButtons( false ),
+ m_bMissingLic( false ),
+ m_eState( eState ),
+ m_xPackage( xPackage )
+{
+ try
+ {
+ m_sTitle = xPackage->getDisplayName();
+ m_sVersion = xPackage->getVersion();
+ m_sDescription = xPackage->getDescription();
+ m_sLicenseText = xPackage->getLicenseText();
+
+ beans::StringPair aInfo( m_xPackage->getPublisherInfo() );
+ m_sPublisher = aInfo.First;
+ m_sPublisherURL = aInfo.Second;
+
+ // get the icons for the package if there are any
+ uno::Reference< graphic::XGraphic > xGraphic = xPackage->getIcon( false );
+ if ( xGraphic.is() )
+ m_aIcon = Image( xGraphic );
+
+ if ( eState == AMBIGUOUS )
+ m_sErrorText = DpResId( RID_STR_ERROR_UNKNOWN_STATUS );
+ else if ( eState == NOT_REGISTERED )
+ checkDependencies();
+ }
+ catch (const deployment::ExtensionRemovedException &) {}
+ catch (const uno::RuntimeException &) {}
+}
+
+
+Entry_Impl::~Entry_Impl()
+{}
+
+
+sal_Int32 Entry_Impl::CompareTo( const CollatorWrapper *pCollator, const TEntry_Impl& rEntry ) const
+{
+ sal_Int32 eCompare = pCollator->compareString( m_sTitle, rEntry->m_sTitle );
+ if ( eCompare == 0 )
+ {
+ eCompare = m_sVersion.compareTo( rEntry->m_sVersion );
+ if ( eCompare == 0 )
+ {
+ sal_Int32 nCompare = m_xPackage->getRepositoryName().compareTo( rEntry->m_xPackage->getRepositoryName() );
+ if ( nCompare < 0 )
+ eCompare = -1;
+ else if ( nCompare > 0 )
+ eCompare = 1;
+ }
+ }
+ return eCompare;
+}
+
+
+void Entry_Impl::checkDependencies()
+{
+ try {
+ m_xPackage->checkDependencies( uno::Reference< ucb::XCommandEnvironment >() );
+ }
+ catch ( const deployment::DeploymentException &e )
+ {
+ deployment::DependencyException depExc;
+ if ( e.Cause >>= depExc )
+ {
+ OUStringBuffer aMissingDep( DpResId( RID_STR_ERROR_MISSING_DEPENDENCIES ) );
+ for ( const auto& i : std::as_const(depExc.UnsatisfiedDependencies) )
+ {
+ aMissingDep.append("\n");
+ aMissingDep.append(dp_misc::Dependencies::getErrorText(i));
+ }
+ aMissingDep.append("\n");
+ m_sErrorText = aMissingDep.makeStringAndClear();
+ m_bMissingDeps = true;
+ }
+ }
+}
+
+// ExtensionRemovedListener
+
+void ExtensionRemovedListener::disposing( lang::EventObject const & rEvt )
+{
+ uno::Reference< deployment::XPackage > xPackage( rEvt.Source, uno::UNO_QUERY );
+
+ if ( xPackage.is() )
+ {
+ m_pParent->removeEntry( xPackage );
+ }
+}
+
+
+ExtensionRemovedListener::~ExtensionRemovedListener()
+{
+}
+
+
+// ExtensionBox_Impl
+ExtensionBox_Impl::ExtensionBox_Impl(std::unique_ptr<weld::ScrolledWindow> xScroll)
+ : m_bHasScrollBar( false )
+ , m_bHasActive( false )
+ , m_bNeedsRecalc( true )
+ , m_bInCheckMode( false )
+ , m_bAdjustActive( false )
+ , m_bInDelete( false )
+ , m_nActive( 0 )
+ , m_nTopIndex( 0 )
+ , m_nStdHeight( 0 )
+ , m_nActiveHeight( 0 )
+ , m_aSharedImage(StockImage::Yes, RID_BMP_SHARED)
+ , m_aLockedImage(StockImage::Yes, RID_BMP_LOCKED)
+ , m_aWarningImage(StockImage::Yes, RID_BMP_WARNING)
+ , m_aDefaultImage(StockImage::Yes, RID_BMP_EXTENSION)
+ , m_pManager( nullptr )
+ , m_xScrollBar(std::move(xScroll))
+{
+}
+
+void ExtensionBox_Impl::Init()
+{
+ m_xScrollBar->connect_vadjustment_changed( LINK( this, ExtensionBox_Impl, ScrollHdl ) );
+
+ auto nIconHeight = 2*TOP_OFFSET + SMALL_ICON_SIZE;
+ auto nTitleHeight = 2*TOP_OFFSET + GetTextHeight();
+ if ( nIconHeight < nTitleHeight )
+ m_nStdHeight = nTitleHeight;
+ else
+ m_nStdHeight = nIconHeight;
+ m_nStdHeight += GetTextHeight() + TOP_OFFSET;
+
+ nIconHeight = ICON_HEIGHT + 2*TOP_OFFSET + 1;
+ if ( m_nStdHeight < nIconHeight )
+ m_nStdHeight = nIconHeight;
+
+ m_nActiveHeight = m_nStdHeight;
+
+ m_xRemoveListener = new ExtensionRemovedListener( this );
+
+ m_pLocale.reset( new lang::Locale( Application::GetSettings().GetLanguageTag().getLocale() ) );
+ m_pCollator.reset( new CollatorWrapper( ::comphelper::getProcessComponentContext() ) );
+ m_pCollator->loadDefaultCollator( *m_pLocale, i18n::CollatorOptions::CollatorOptions_IGNORE_CASE );
+}
+
+ExtensionBox_Impl::~ExtensionBox_Impl()
+{
+ if ( ! m_bInDelete )
+ DeleteRemoved();
+
+ m_bInDelete = true;
+
+ for (auto const& entry : m_vEntries)
+ {
+ entry->m_xPackage->removeEventListener( m_xRemoveListener );
+ }
+
+ m_vEntries.clear();
+
+ m_xRemoveListener.clear();
+
+ m_pLocale.reset();
+ m_pCollator.reset();
+}
+
+sal_Int32 ExtensionBox_Impl::getItemCount() const
+{
+ return static_cast< sal_Int32 >( m_vEntries.size() );
+}
+
+
+sal_Int32 ExtensionBox_Impl::getSelIndex() const
+{
+ if ( m_bHasActive )
+ {
+ OSL_ASSERT( m_nActive >= -1);
+ return static_cast< sal_Int32 >( m_nActive );
+ }
+ else
+ return ENTRY_NOTFOUND;
+}
+
+
+// Title + description
+void ExtensionBox_Impl::CalcActiveHeight( const tools::Long nPos )
+{
+ const ::osl::MutexGuard aGuard( m_entriesMutex );
+
+ // get title height
+ tools::Long aTextHeight;
+ tools::Long nIconHeight = 2*TOP_OFFSET + SMALL_ICON_SIZE;
+ tools::Long nTitleHeight = 2*TOP_OFFSET + GetTextHeight();
+ if ( nIconHeight < nTitleHeight )
+ aTextHeight = nTitleHeight;
+ else
+ aTextHeight = nIconHeight;
+
+ // calc description height
+ Size aSize = GetOutputSizePixel();
+
+ aSize.AdjustWidth( -(ICON_OFFSET) );
+ aSize.setHeight( 10000 );
+
+ OUString aText( m_vEntries[ nPos ]->m_sErrorText );
+ if ( !aText.isEmpty() )
+ aText += "\n";
+ aText += m_vEntries[ nPos ]->m_sDescription;
+
+ tools::Rectangle aRect = GetDrawingArea()->get_ref_device().GetTextRect(tools::Rectangle( Point(), aSize ), aText,
+ DrawTextFlags::MultiLine | DrawTextFlags::WordBreak);
+ aTextHeight += aRect.GetHeight();
+
+ if ( aTextHeight < m_nStdHeight )
+ aTextHeight = m_nStdHeight;
+
+ m_nActiveHeight = aTextHeight;
+
+ if ( m_vEntries[ nPos ]->m_bHasButtons )
+ m_nActiveHeight += 2;
+}
+
+tools::Rectangle ExtensionBox_Impl::GetEntryRect( const tools::Long nPos ) const
+{
+ const ::osl::MutexGuard aGuard( m_entriesMutex );
+
+ Size aSize( GetOutputSizePixel() );
+
+ if ( m_vEntries[ nPos ]->m_bActive )
+ aSize.setHeight( m_nActiveHeight );
+ else
+ aSize.setHeight( m_nStdHeight );
+
+ Point aPos( 0, -m_nTopIndex + nPos * m_nStdHeight );
+ if ( m_bHasActive && ( nPos < m_nActive ) )
+ aPos.AdjustY(m_nActiveHeight - m_nStdHeight );
+
+ return tools::Rectangle( aPos, aSize );
+}
+
+
+void ExtensionBox_Impl::DeleteRemoved()
+{
+ const ::osl::MutexGuard aGuard( m_entriesMutex );
+
+ m_bInDelete = true;
+
+ m_vRemovedEntries.clear();
+
+ m_bInDelete = false;
+}
+
+
+//This function may be called with nPos < 0
+void ExtensionBox_Impl::selectEntry( const tools::Long nPos )
+{
+ bool invalidate = false;
+ {
+ //ToDo we should not use the guard at such a big scope here.
+ //Currently it is used to guard m_vEntries and m_nActive. m_nActive will be
+ //modified in this function.
+ //It would be probably best to always use a copy of m_vEntries
+ //and some other state variables from ExtensionBox_Impl for
+ //the whole painting operation. See issue i86993
+ ::osl::MutexGuard guard(m_entriesMutex);
+
+ if ( m_bInCheckMode )
+ return;
+
+ if ( m_bHasActive )
+ {
+ if ( nPos == m_nActive )
+ return;
+
+ m_bHasActive = false;
+ m_vEntries[ m_nActive ]->m_bActive = false;
+ }
+
+ if ( ( nPos >= 0 ) && ( o3tl::make_unsigned(nPos) < m_vEntries.size() ) )
+ {
+ m_bHasActive = true;
+ m_nActive = nPos;
+ m_vEntries[ nPos ]->m_bActive = true;
+
+ if ( IsReallyVisible() )
+ {
+ m_bAdjustActive = true;
+ }
+ }
+
+ if ( IsReallyVisible() )
+ {
+ m_bNeedsRecalc = true;
+ invalidate = true;
+ }
+ }
+
+ if (invalidate)
+ {
+ SolarMutexGuard g;
+ Invalidate();
+ }
+}
+
+
+void ExtensionBox_Impl::DrawRow(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect, const TEntry_Impl& rEntry)
+{
+ const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
+
+ if (rEntry->m_bActive)
+ rRenderContext.SetTextColor(rStyleSettings.GetHighlightTextColor());
+ else if ((rEntry->m_eState != REGISTERED) && (rEntry->m_eState != NOT_AVAILABLE))
+ rRenderContext.SetTextColor(rStyleSettings.GetDisableColor());
+ else
+ rRenderContext.SetTextColor(rStyleSettings.GetFieldTextColor());
+
+ if (rEntry->m_bActive)
+ {
+ rRenderContext.SetLineColor();
+ rRenderContext.SetFillColor(rStyleSettings.GetHighlightColor());
+ rRenderContext.DrawRect(rRect);
+ }
+ else
+ {
+ rRenderContext.SetBackground(rStyleSettings.GetFieldColor());
+ rRenderContext.SetTextFillColor();
+ rRenderContext.Erase(rRect);
+ }
+
+ // Draw extension icon
+ Point aPos( rRect.TopLeft() );
+ aPos += Point(TOP_OFFSET, TOP_OFFSET);
+ Image aImage;
+ if (!rEntry->m_aIcon)
+ aImage = m_aDefaultImage;
+ else
+ aImage = rEntry->m_aIcon;
+ Size aImageSize = aImage.GetSizePixel();
+ if ((aImageSize.Width() <= ICON_WIDTH ) && ( aImageSize.Height() <= ICON_HEIGHT ) )
+ rRenderContext.DrawImage(Point(aPos.X() + ((ICON_WIDTH - aImageSize.Width()) / 2),
+ aPos.Y() + ((ICON_HEIGHT - aImageSize.Height()) / 2)),
+ aImage);
+ else
+ rRenderContext.DrawImage(aPos, Size(ICON_WIDTH, ICON_HEIGHT), aImage);
+
+ // Setup fonts
+ // expand the point size of the desired font to the equivalent pixel size
+ weld::SetPointFont(rRenderContext, GetDrawingArea()->get_font());
+ vcl::Font aStdFont(rRenderContext.GetFont());
+ vcl::Font aBoldFont(aStdFont);
+ aBoldFont.SetWeight(WEIGHT_BOLD);
+ rRenderContext.SetFont(aBoldFont);
+ auto aTextHeight = rRenderContext.GetTextHeight();
+
+ // Get max title width
+ auto nMaxTitleWidth = rRect.GetWidth() - ICON_OFFSET;
+ nMaxTitleWidth -= (2 * SMALL_ICON_SIZE) + (4 * SPACE_BETWEEN);
+ rRenderContext.SetFont(aStdFont);
+ tools::Long nLinkWidth = 0;
+ if (!rEntry->m_sPublisher.isEmpty())
+ {
+ nLinkWidth = rRenderContext.GetTextWidth(rEntry->m_sPublisher);
+ nMaxTitleWidth -= nLinkWidth + (2 * SPACE_BETWEEN);
+ }
+ tools::Long aVersionWidth = rRenderContext.GetTextWidth(rEntry->m_sVersion);
+
+ aPos = rRect.TopLeft() + Point(ICON_OFFSET, TOP_OFFSET);
+
+ rRenderContext.SetFont(aBoldFont);
+ tools::Long aTitleWidth = rRenderContext.GetTextWidth(rEntry->m_sTitle) + (aTextHeight / 3);
+ if (aTitleWidth > nMaxTitleWidth - aVersionWidth)
+ {
+ aTitleWidth = nMaxTitleWidth - aVersionWidth - (aTextHeight / 3);
+ OUString aShortTitle = rRenderContext.GetEllipsisString(rEntry->m_sTitle, aTitleWidth);
+ rRenderContext.DrawText(aPos, aShortTitle);
+ aTitleWidth += (aTextHeight / 3);
+ }
+ else
+ rRenderContext.DrawText(aPos, rEntry->m_sTitle);
+
+ rRenderContext.SetFont(aStdFont);
+ rRenderContext.DrawText(Point(aPos.X() + aTitleWidth, aPos.Y()), rEntry->m_sVersion);
+
+ tools::Long nIconHeight = TOP_OFFSET + SMALL_ICON_SIZE;
+ tools::Long nTitleHeight = TOP_OFFSET + GetTextHeight();
+ if ( nIconHeight < nTitleHeight )
+ aTextHeight = nTitleHeight;
+ else
+ aTextHeight = nIconHeight;
+
+ // draw description
+ OUString sDescription;
+ if (!rEntry->m_sErrorText.isEmpty())
+ {
+ if (rEntry->m_bActive)
+ sDescription = rEntry->m_sErrorText + "\n" + rEntry->m_sDescription;
+ else
+ sDescription = rEntry->m_sErrorText;
+ }
+ else
+ sDescription = rEntry->m_sDescription;
+
+ aPos.AdjustY(aTextHeight );
+ if (rEntry->m_bActive)
+ {
+ tools::Long nExtraHeight = 0;
+
+ if (rEntry->m_bHasButtons)
+ nExtraHeight = 2;
+
+ rRenderContext.DrawText(tools::Rectangle(aPos.X(), aPos.Y(), rRect.Right(), rRect.Bottom() - nExtraHeight),
+ sDescription, DrawTextFlags::MultiLine | DrawTextFlags::WordBreak );
+ }
+ else
+ {
+ //replace LF to space, so words do not stick together in one line view
+ sDescription = sDescription.replace(0x000A, ' ');
+ const tools::Long nWidth = rRenderContext.GetTextWidth( sDescription );
+ if (nWidth > rRect.GetWidth() - aPos.X())
+ sDescription = rRenderContext.GetEllipsisString(sDescription, rRect.GetWidth() - aPos.X());
+ rRenderContext.DrawText(aPos, sDescription);
+ }
+
+ // Draw publisher link
+ if (!rEntry->m_sPublisher.isEmpty())
+ {
+ aPos = rRect.TopLeft() + Point( ICON_OFFSET + nMaxTitleWidth + (2*SPACE_BETWEEN), TOP_OFFSET );
+
+ rRenderContext.Push(vcl::PushFlags::FONT | vcl::PushFlags::TEXTCOLOR | vcl::PushFlags::TEXTFILLCOLOR);
+ rRenderContext.SetTextColor(rStyleSettings.GetLinkColor());
+ rRenderContext.SetTextFillColor(rStyleSettings.GetFieldColor());
+ vcl::Font aFont = rRenderContext.GetFont();
+ // to underline
+ aFont.SetUnderline(LINESTYLE_SINGLE);
+ rRenderContext.SetFont(aFont);
+ rRenderContext.DrawText(aPos, rEntry->m_sPublisher);
+ rEntry->m_aLinkRect = tools::Rectangle(aPos, Size(nLinkWidth, aTextHeight));
+ rRenderContext.Pop();
+ }
+
+ // Draw status icons
+ if (!rEntry->m_bUser)
+ {
+ aPos = rRect.TopRight() + Point( -(RIGHT_ICON_OFFSET + SMALL_ICON_SIZE), TOP_OFFSET );
+ if (rEntry->m_bLocked)
+ rRenderContext.DrawImage(aPos, Size(SMALL_ICON_SIZE, SMALL_ICON_SIZE), m_aLockedImage);
+ else
+ rRenderContext.DrawImage(aPos, Size(SMALL_ICON_SIZE, SMALL_ICON_SIZE), m_aSharedImage);
+ }
+ if ((rEntry->m_eState == AMBIGUOUS ) || rEntry->m_bMissingDeps || rEntry->m_bMissingLic)
+ {
+ aPos = rRect.TopRight() + Point(-(RIGHT_ICON_OFFSET + SPACE_BETWEEN + 2 * SMALL_ICON_SIZE), TOP_OFFSET);
+ rRenderContext.DrawImage(aPos, Size(SMALL_ICON_SIZE, SMALL_ICON_SIZE), m_aWarningImage);
+ }
+
+ rRenderContext.SetLineColor(COL_LIGHTGRAY);
+ rRenderContext.DrawLine(rRect.BottomLeft(), rRect.BottomRight());
+}
+
+
+void ExtensionBox_Impl::RecalcAll()
+{
+ if ( m_bHasActive )
+ CalcActiveHeight( m_nActive );
+
+ SetupScrollBar();
+
+ if ( m_bHasActive )
+ {
+ tools::Rectangle aEntryRect = GetEntryRect( m_nActive );
+
+ if ( m_bAdjustActive )
+ {
+ m_bAdjustActive = false;
+
+ // If the top of the selected entry isn't visible, make it visible
+ if ( aEntryRect.Top() < 0 )
+ {
+ m_nTopIndex += aEntryRect.Top();
+ aEntryRect.Move( 0, -aEntryRect.Top() );
+ }
+
+ // If the bottom of the selected entry isn't visible, make it visible even if now the top
+ // isn't visible any longer ( the buttons are more important )
+ Size aOutputSize = GetOutputSizePixel();
+ if ( aEntryRect.Bottom() > aOutputSize.Height() )
+ {
+ m_nTopIndex += ( aEntryRect.Bottom() - aOutputSize.Height() );
+ aEntryRect.Move( 0, -( aEntryRect.Bottom() - aOutputSize.Height() ) );
+ }
+
+ // If there is unused space below the last entry but all entries don't fit into the box,
+ // move the content down to use the whole space
+ const tools::Long nTotalHeight = GetTotalHeight();
+ if ( m_bHasScrollBar && ( aOutputSize.Height() + m_nTopIndex > nTotalHeight ) )
+ {
+ tools::Long nOffset = m_nTopIndex;
+ m_nTopIndex = nTotalHeight - aOutputSize.Height();
+ nOffset -= m_nTopIndex;
+ aEntryRect.Move( 0, nOffset );
+ }
+
+ if ( m_bHasScrollBar )
+ m_xScrollBar->vadjustment_set_value( m_nTopIndex );
+ }
+ }
+
+ m_bNeedsRecalc = false;
+}
+
+
+bool ExtensionBox_Impl::HandleCursorKey( sal_uInt16 nKeyCode )
+{
+ if ( m_vEntries.empty() )
+ return true;
+
+ tools::Long nSelect = 0;
+
+ if ( m_bHasActive )
+ {
+ tools::Long nPageSize = GetOutputSizePixel().Height() / m_nStdHeight;
+ if ( nPageSize < 2 )
+ nPageSize = 2;
+
+ if ( ( nKeyCode == KEY_DOWN ) || ( nKeyCode == KEY_RIGHT ) )
+ nSelect = m_nActive + 1;
+ else if ( ( nKeyCode == KEY_UP ) || ( nKeyCode == KEY_LEFT ) )
+ nSelect = m_nActive - 1;
+ else if ( nKeyCode == KEY_HOME )
+ nSelect = 0;
+ else if ( nKeyCode == KEY_END )
+ nSelect = m_vEntries.size() - 1;
+ else if ( nKeyCode == KEY_PAGEUP )
+ nSelect = m_nActive - nPageSize + 1;
+ else if ( nKeyCode == KEY_PAGEDOWN )
+ nSelect = m_nActive + nPageSize - 1;
+ }
+ else // when there is no selected entry, we will select the first or the last.
+ {
+ if ( ( nKeyCode == KEY_DOWN ) || ( nKeyCode == KEY_PAGEDOWN ) || ( nKeyCode == KEY_HOME ) )
+ nSelect = 0;
+ else if ( ( nKeyCode == KEY_UP ) || ( nKeyCode == KEY_PAGEUP ) || ( nKeyCode == KEY_END ) )
+ nSelect = m_vEntries.size() - 1;
+ }
+
+ if ( nSelect < 0 )
+ nSelect = 0;
+ if ( o3tl::make_unsigned(nSelect) >= m_vEntries.size() )
+ nSelect = m_vEntries.size() - 1;
+
+ selectEntry( nSelect );
+
+ return true;
+}
+
+
+void ExtensionBox_Impl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*rPaintRect*/)
+{
+ if ( !m_bInDelete )
+ DeleteRemoved();
+
+ if ( m_bNeedsRecalc )
+ RecalcAll();
+
+ Point aStart( 0, -m_nTopIndex );
+ Size aSize(GetOutputSizePixel());
+
+ const ::osl::MutexGuard aGuard( m_entriesMutex );
+
+ for (auto const& entry : m_vEntries)
+ {
+ aSize.setHeight( entry->m_bActive ? m_nActiveHeight : m_nStdHeight );
+ tools::Rectangle aEntryRect( aStart, aSize );
+ DrawRow(rRenderContext, aEntryRect, entry);
+ aStart.AdjustY(aSize.Height() );
+ }
+}
+
+
+tools::Long ExtensionBox_Impl::GetTotalHeight() const
+{
+ tools::Long nHeight = m_vEntries.size() * m_nStdHeight;
+
+ if ( m_bHasActive )
+ {
+ nHeight += m_nActiveHeight - m_nStdHeight;
+ }
+
+ return nHeight;
+}
+
+
+void ExtensionBox_Impl::SetupScrollBar()
+{
+ const Size aSize = GetOutputSizePixel();
+ const auto nTotalHeight = GetTotalHeight();
+ const bool bNeedsScrollBar = ( nTotalHeight > aSize.Height() );
+
+ if ( bNeedsScrollBar )
+ {
+ if ( m_nTopIndex + aSize.Height() > nTotalHeight )
+ m_nTopIndex = nTotalHeight - aSize.Height();
+
+ m_xScrollBar->vadjustment_configure(m_nTopIndex, 0, nTotalHeight,
+ m_nStdHeight, ( aSize.Height() * 4 ) / 5,
+ aSize.Height());
+
+ if (!m_bHasScrollBar)
+ m_xScrollBar->set_vpolicy(VclPolicyType::ALWAYS);
+ }
+ else if ( m_bHasScrollBar )
+ {
+ m_xScrollBar->set_vpolicy(VclPolicyType::NEVER);
+ m_nTopIndex = 0;
+ }
+
+ m_bHasScrollBar = bNeedsScrollBar;
+}
+
+
+void ExtensionBox_Impl::Resize()
+{
+ RecalcAll();
+ Invalidate();
+}
+
+void ExtensionBox_Impl::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ Size aSize = pDrawingArea->get_ref_device().LogicToPixel(Size(250, 150), MapMode(MapUnit::MapAppFont));
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+ CustomWidgetController::SetDrawingArea(pDrawingArea);
+ SetOutputSizePixel(aSize);
+
+ Init();
+}
+
+tools::Long ExtensionBox_Impl::PointToPos( const Point& rPos )
+{
+ tools::Long nPos = ( rPos.Y() + m_nTopIndex ) / m_nStdHeight;
+
+ if ( m_bHasActive && ( nPos > m_nActive ) )
+ {
+ if ( rPos.Y() + m_nTopIndex <= m_nActive*m_nStdHeight + m_nActiveHeight )
+ nPos = m_nActive;
+ else
+ nPos = ( rPos.Y() + m_nTopIndex - (m_nActiveHeight - m_nStdHeight) ) / m_nStdHeight;
+ }
+
+ return nPos;
+}
+
+bool ExtensionBox_Impl::MouseMove( const MouseEvent& rMEvt )
+{
+ bool bOverHyperlink = false;
+
+ auto nPos = PointToPos( rMEvt.GetPosPixel() );
+ if ( ( nPos >= 0 ) && ( o3tl::make_unsigned(nPos) < m_vEntries.size() ) )
+ {
+ const auto& rEntry = m_vEntries[nPos];
+ bOverHyperlink = !rEntry->m_sPublisher.isEmpty() && rEntry->m_aLinkRect.Contains(rMEvt.GetPosPixel());
+ }
+
+ if (bOverHyperlink)
+ SetPointer(PointerStyle::RefHand);
+ else
+ SetPointer(PointerStyle::Arrow);
+
+ return false;
+}
+
+OUString ExtensionBox_Impl::RequestHelp(tools::Rectangle& rRect)
+{
+ auto nPos = PointToPos( rRect.TopLeft() );
+ if ( ( nPos >= 0 ) && ( o3tl::make_unsigned(nPos) < m_vEntries.size() ) )
+ {
+ const auto& rEntry = m_vEntries[nPos];
+ bool bOverHyperlink = !rEntry->m_sPublisher.isEmpty() && rEntry->m_aLinkRect.Contains(rRect);
+ if (bOverHyperlink)
+ {
+ rRect = rEntry->m_aLinkRect;
+ return rEntry->m_sPublisherURL;
+ }
+ }
+
+ return OUString();
+}
+
+bool ExtensionBox_Impl::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ if ( !rMEvt.IsLeft() )
+ return false;
+
+ if (rMEvt.IsMod1() && m_bHasActive)
+ selectEntry(ExtensionBox_Impl::ENTRY_NOTFOUND); // Selecting a not existing entry will deselect the current one
+ else
+ {
+ auto nPos = PointToPos( rMEvt.GetPosPixel() );
+
+ if ( ( nPos >= 0 ) && ( o3tl::make_unsigned(nPos) < m_vEntries.size() ) )
+ {
+ const auto& rEntry = m_vEntries[nPos];
+ if (!rEntry->m_sPublisher.isEmpty() && rEntry->m_aLinkRect.Contains(rMEvt.GetPosPixel()))
+ {
+ try
+ {
+ css::uno::Reference<css::system::XSystemShellExecute> xSystemShellExecute(
+ css::system::SystemShellExecute::create(comphelper::getProcessComponentContext()));
+ //throws css::lang::IllegalArgumentException, css::system::SystemShellExecuteException
+ xSystemShellExecute->execute(rEntry->m_sPublisherURL, OUString(), css::system::SystemShellExecuteFlags::URIS_ONLY);
+ }
+ catch (...)
+ {
+ }
+ return true;
+ }
+ }
+
+ selectEntry( nPos );
+ }
+ return true;
+}
+
+bool ExtensionBox_Impl::KeyInput(const KeyEvent& rKEvt)
+{
+ if ( !m_bInDelete )
+ DeleteRemoved();
+
+ vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
+ sal_uInt16 nKeyCode = aKeyCode.GetCode();
+
+ bool bHandled = false;
+ if (nKeyCode != KEY_TAB && aKeyCode.GetGroup() == KEYGROUP_CURSOR)
+ bHandled = HandleCursorKey(nKeyCode);
+
+ return bHandled;
+}
+
+bool ExtensionBox_Impl::FindEntryPos( const TEntry_Impl& rEntry, const tools::Long nStart,
+ const tools::Long nEnd, tools::Long &nPos )
+{
+ nPos = nStart;
+ if ( nStart > nEnd )
+ return false;
+
+ sal_Int32 eCompare;
+
+ if ( nStart == nEnd )
+ {
+ eCompare = rEntry->CompareTo( m_pCollator.get(), m_vEntries[ nStart ] );
+ if ( eCompare < 0 )
+ return false;
+ else if ( eCompare == 0 )
+ {
+ //Workaround. See i86963.
+ if (rEntry->m_xPackage != m_vEntries[nStart]->m_xPackage)
+ return false;
+
+ if ( m_bInCheckMode )
+ m_vEntries[ nStart ]->m_bChecked = true;
+ return true;
+ }
+ else
+ {
+ nPos = nStart + 1;
+ return false;
+ }
+ }
+
+ const tools::Long nMid = nStart + ( ( nEnd - nStart ) / 2 );
+ eCompare = rEntry->CompareTo( m_pCollator.get(), m_vEntries[ nMid ] );
+
+ if ( eCompare < 0 )
+ return FindEntryPos( rEntry, nStart, nMid-1, nPos );
+ else if ( eCompare > 0 )
+ return FindEntryPos( rEntry, nMid+1, nEnd, nPos );
+ else
+ {
+ //Workaround.See i86963.
+ if (rEntry->m_xPackage != m_vEntries[nMid]->m_xPackage)
+ return false;
+
+ if ( m_bInCheckMode )
+ m_vEntries[ nMid ]->m_bChecked = true;
+ nPos = nMid;
+ return true;
+ }
+}
+
+void ExtensionBox_Impl::cleanVecListenerAdded()
+{
+ m_vListenerAdded.erase(std::remove_if(m_vListenerAdded.begin(), m_vListenerAdded.end(),
+ [](const uno::WeakReference<deployment::XPackage>& rxListener) {
+ const uno::Reference<deployment::XPackage> hardRef(rxListener);
+ return !hardRef.is();
+ }),
+ m_vListenerAdded.end());
+}
+
+void ExtensionBox_Impl::addEventListenerOnce(
+ uno::Reference<deployment::XPackage > const & extension)
+{
+ //make sure to only add the listener once
+ cleanVecListenerAdded();
+ if ( std::none_of(m_vListenerAdded.begin(), m_vListenerAdded.end(),
+ FindWeakRef(extension)) )
+ {
+ extension->addEventListener( m_xRemoveListener );
+ m_vListenerAdded.emplace_back(extension);
+ }
+}
+
+
+void ExtensionBox_Impl::addEntry( const uno::Reference< deployment::XPackage > &xPackage,
+ bool bLicenseMissing )
+{
+ PackageState eState = TheExtensionManager::getPackageState( xPackage );
+ bool bLocked = m_pManager->isReadOnly( xPackage );
+
+ TEntry_Impl pEntry = std::make_shared<Entry_Impl>( xPackage, eState, bLocked );
+
+ // Don't add empty entries
+ if ( pEntry->m_sTitle.isEmpty() )
+ return;
+
+ {
+ osl::MutexGuard guard(m_entriesMutex);
+ tools::Long nPos = 0;
+ if (m_vEntries.empty())
+ {
+ addEventListenerOnce(xPackage);
+ m_vEntries.push_back(pEntry);
+ }
+ else
+ {
+ if (!FindEntryPos(pEntry, 0, m_vEntries.size() - 1, nPos))
+ {
+ addEventListenerOnce(xPackage);
+ m_vEntries.insert(m_vEntries.begin() + nPos, pEntry);
+ }
+ else if (!m_bInCheckMode)
+ {
+ OSL_FAIL("ExtensionBox_Impl::addEntry(): Will not add duplicate entries");
+ }
+ }
+
+ pEntry->m_bHasOptions = m_pManager->supportsOptions(xPackage);
+ pEntry->m_bUser = (xPackage->getRepositoryName() == USER_PACKAGE_MANAGER);
+ pEntry->m_bShared = (xPackage->getRepositoryName() == SHARED_PACKAGE_MANAGER);
+ pEntry->m_bNew = m_bInCheckMode;
+ pEntry->m_bMissingLic = bLicenseMissing;
+
+ if (bLicenseMissing)
+ pEntry->m_sErrorText = DpResId(RID_STR_ERROR_MISSING_LICENSE);
+
+ //access to m_nActive must be guarded
+ if (!m_bInCheckMode && m_bHasActive && (m_nActive >= nPos))
+ m_nActive += 1;
+ }
+
+ if ( IsReallyVisible() )
+ Invalidate();
+
+ m_bNeedsRecalc = true;
+}
+
+void ExtensionBox_Impl::updateEntry( const uno::Reference< deployment::XPackage > &xPackage )
+{
+ for (auto const& entry : m_vEntries)
+ {
+ if ( entry->m_xPackage == xPackage )
+ {
+ PackageState eState = TheExtensionManager::getPackageState( xPackage );
+ entry->m_bHasOptions = m_pManager->supportsOptions( xPackage );
+ entry->m_eState = eState;
+ entry->m_sTitle = xPackage->getDisplayName();
+ entry->m_sVersion = xPackage->getVersion();
+ entry->m_sDescription = xPackage->getDescription();
+
+ if ( eState == REGISTERED )
+ entry->m_bMissingLic = false;
+
+ if ( eState == AMBIGUOUS )
+ entry->m_sErrorText = DpResId( RID_STR_ERROR_UNKNOWN_STATUS );
+ else if ( ! entry->m_bMissingLic )
+ entry->m_sErrorText.clear();
+
+ if ( IsReallyVisible() )
+ Invalidate();
+ break;
+ }
+ }
+}
+
+//This function is also called as a result of removing an extension.
+//see PackageManagerImpl::removePackage
+//The gui is a registered as listener on the package. Removing it will cause the
+//listeners to be notified and then this function is called. At this moment xPackage
+//is in the disposing state and all calls on it may result in a DisposedException.
+void ExtensionBox_Impl::removeEntry( const uno::Reference< deployment::XPackage > &xPackage )
+{
+ if ( m_bInDelete )
+ return;
+
+ bool invalidate = false;
+ {
+ ::osl::ClearableMutexGuard aGuard( m_entriesMutex );
+
+ auto iIndex = std::find_if(m_vEntries.begin(), m_vEntries.end(),
+ [&xPackage](const TEntry_Impl& rxEntry) { return rxEntry->m_xPackage == xPackage; });
+ if (iIndex != m_vEntries.end())
+ {
+ tools::Long nPos = iIndex - m_vEntries.begin();
+
+ // Entries mustn't be removed here, because they contain a hyperlink control
+ // which can only be deleted when the thread has the solar mutex. Therefore
+ // the entry will be moved into the m_vRemovedEntries list which will be
+ // cleared on the next paint event
+ m_vRemovedEntries.push_back( *iIndex );
+ (*iIndex)->m_xPackage->removeEventListener(m_xRemoveListener);
+ m_vEntries.erase( iIndex );
+
+ m_bNeedsRecalc = true;
+
+ if ( IsReallyVisible() )
+ invalidate = true;
+
+ if ( m_bHasActive )
+ {
+ if ( nPos < m_nActive )
+ m_nActive -= 1;
+ else if ( ( nPos == m_nActive ) &&
+ ( nPos == static_cast<tools::Long>(m_vEntries.size()) ) )
+ m_nActive -= 1;
+
+ m_bHasActive = false;
+ //clear before calling out of this method
+ aGuard.clear();
+ selectEntry( m_nActive );
+ }
+ }
+ }
+
+ if (invalidate)
+ {
+ SolarMutexGuard g;
+ Invalidate();
+ }
+}
+
+
+void ExtensionBox_Impl::RemoveUnlocked()
+{
+ bool bAllRemoved = false;
+
+ while ( ! bAllRemoved )
+ {
+ bAllRemoved = true;
+
+ ::osl::ClearableMutexGuard aGuard( m_entriesMutex );
+
+ for (auto const& entry : m_vEntries)
+ {
+ if ( !entry->m_bLocked )
+ {
+ bAllRemoved = false;
+ uno::Reference< deployment::XPackage> xPackage = entry->m_xPackage;
+ aGuard.clear();
+ removeEntry( xPackage );
+ break;
+ }
+ }
+ }
+}
+
+
+void ExtensionBox_Impl::prepareChecking()
+{
+ m_bInCheckMode = true;
+ for (auto const& entry : m_vEntries)
+ {
+ entry->m_bChecked = false;
+ entry->m_bNew = false;
+ }
+}
+
+
+void ExtensionBox_Impl::checkEntries()
+{
+ tools::Long nNewPos = -1;
+ tools::Long nChangedActivePos = -1;
+ tools::Long nPos = 0;
+ bool bNeedsUpdate = false;
+
+ {
+ osl::MutexGuard guard(m_entriesMutex);
+ auto iIndex = m_vEntries.begin();
+ while (iIndex != m_vEntries.end())
+ {
+ if (!(*iIndex)->m_bChecked)
+ {
+ (*iIndex)->m_bChecked = true;
+ bNeedsUpdate = true;
+ nPos = iIndex - m_vEntries.begin();
+ if ((*iIndex)->m_bNew)
+ { // add entry to list and correct active pos
+ if (nNewPos == -1)
+ nNewPos = nPos;
+ if (nPos <= m_nActive)
+ m_nActive += 1;
+ ++iIndex;
+ }
+ else
+ { // remove entry from list
+ if (nPos < nNewPos)
+ {
+ --nNewPos;
+ }
+ if (nPos < nChangedActivePos)
+ {
+ --nChangedActivePos;
+ }
+ if (nPos < m_nActive)
+ m_nActive -= 1;
+ else if (nPos == m_nActive)
+ {
+ nChangedActivePos = nPos;
+ m_nActive = -1;
+ m_bHasActive = false;
+ }
+ m_vRemovedEntries.push_back(*iIndex);
+ (*iIndex)->m_xPackage->removeEventListener(m_xRemoveListener);
+ iIndex = m_vEntries.erase(iIndex);
+ }
+ }
+ else
+ ++iIndex;
+ }
+ }
+
+ m_bInCheckMode = false;
+
+ if ( nNewPos != - 1)
+ selectEntry( nNewPos );
+ else if (nChangedActivePos != -1) {
+ selectEntry(nChangedActivePos);
+ }
+
+ if ( bNeedsUpdate )
+ {
+ m_bNeedsRecalc = true;
+ if ( IsReallyVisible() )
+ Invalidate();
+ }
+}
+
+IMPL_LINK(ExtensionBox_Impl, ScrollHdl, weld::ScrolledWindow&, rScrBar, void)
+{
+ m_nTopIndex = rScrBar.vadjustment_get_value();
+ Invalidate();
+}
+
+} //namespace dp_gui
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_extlistbox.hxx b/desktop/source/deployment/gui/dp_gui_extlistbox.hxx
new file mode 100644
index 000000000..d706975dc
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_extlistbox.hxx
@@ -0,0 +1,214 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <vcl/customweld.hxx>
+#include <vcl/image.hxx>
+#include <vcl/weld.hxx>
+
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/weakref.hxx>
+#include <unotools/collatorwrapper.hxx>
+
+#include <com/sun/star/lang/Locale.hpp>
+#include <com/sun/star/lang/XEventListener.hpp>
+#include <com/sun/star/deployment/XPackage.hpp>
+
+#include <memory>
+
+#include "dp_gui.h"
+
+namespace dp_gui {
+
+#define SMALL_ICON_SIZE 16
+#define TOP_OFFSET 5
+#define ICON_HEIGHT 42
+#define ICON_WIDTH 47
+#define ICON_OFFSET 72
+#define RIGHT_ICON_OFFSET 5
+#define SPACE_BETWEEN 3
+
+class TheExtensionManager;
+
+
+struct Entry_Impl;
+
+typedef std::shared_ptr< Entry_Impl > TEntry_Impl;
+
+struct Entry_Impl
+{
+ bool m_bActive :1;
+ bool m_bLocked :1;
+ bool m_bHasOptions :1;
+ bool m_bUser :1;
+ bool m_bShared :1;
+ bool m_bNew :1;
+ bool m_bChecked :1;
+ bool m_bMissingDeps :1;
+ bool m_bHasButtons :1;
+ bool m_bMissingLic :1;
+ PackageState m_eState;
+ OUString m_sTitle;
+ OUString m_sVersion;
+ OUString m_sDescription;
+ OUString m_sPublisher;
+ OUString m_sPublisherURL;
+ OUString m_sErrorText;
+ OUString m_sLicenseText;
+ Image m_aIcon;
+ tools::Rectangle m_aLinkRect;
+
+ css::uno::Reference<css::deployment::XPackage> m_xPackage;
+
+ Entry_Impl(const css::uno::Reference<css::deployment::XPackage> &xPackage,
+ const PackageState eState, const bool bReadOnly);
+ ~Entry_Impl();
+
+ sal_Int32 CompareTo(const CollatorWrapper *pCollator, const TEntry_Impl& rEntry) const;
+ void checkDependencies();
+};
+
+class ExtensionBox_Impl;
+
+
+class ExtensionRemovedListener : public ::cppu::WeakImplHelper<css::lang::XEventListener>
+{
+ ExtensionBox_Impl* m_pParent;
+
+public:
+
+ explicit ExtensionRemovedListener( ExtensionBox_Impl *pParent ) { m_pParent = pParent; }
+ virtual ~ExtensionRemovedListener() override;
+
+
+ // XEventListener
+ virtual void SAL_CALL disposing(css::lang::EventObject const& evt) override;
+};
+
+class ExtensionBox_Impl : public weld::CustomWidgetController
+{
+ bool m_bHasScrollBar : 1;
+ bool m_bHasActive : 1;
+ bool m_bNeedsRecalc : 1;
+ bool m_bInCheckMode : 1;
+ bool m_bAdjustActive : 1;
+ bool m_bInDelete : 1;
+ //Must be guarded together with m_vEntries to ensure a valid index at all times.
+ //Use m_entriesMutex as guard.
+ tools::Long m_nActive;
+ tools::Long m_nTopIndex;
+ tools::Long m_nStdHeight;
+ tools::Long m_nActiveHeight;
+ Image m_aSharedImage;
+ Image m_aLockedImage;
+ Image m_aWarningImage;
+ Image m_aDefaultImage;
+
+ rtl::Reference<ExtensionRemovedListener> m_xRemoveListener;
+
+ TheExtensionManager *m_pManager;
+ //This mutex is used for synchronizing access to m_vEntries.
+ //Currently it is used to synchronize adding, removing entries and
+ //functions like getItemName, getItemDescription, etc. to prevent
+ //that m_vEntries is accessed at an invalid index.
+ //ToDo: There are many more places where m_vEntries is read and which may
+ //fail. For example the Paint method is probable called from the main thread
+ //while new entries are added / removed in a separate thread.
+ mutable ::osl::Mutex m_entriesMutex;
+ std::vector< TEntry_Impl > m_vEntries;
+ std::vector< TEntry_Impl > m_vRemovedEntries;
+
+ std::unique_ptr<css::lang::Locale> m_pLocale;
+ std::unique_ptr<CollatorWrapper> m_pCollator;
+
+ //Holds weak references to extensions to which is we have added an XEventListener
+ std::vector< css::uno::WeakReference<
+ css::deployment::XPackage> > m_vListenerAdded;
+
+ std::unique_ptr<weld::ScrolledWindow> m_xScrollBar;
+
+ //Removes the dead weak references from m_vListenerAdded
+ void cleanVecListenerAdded();
+ void addEventListenerOnce(css::uno::Reference<css::deployment::XPackage> const & extension);
+
+ void CalcActiveHeight( const tools::Long nPos );
+ tools::Long GetTotalHeight() const;
+ void SetupScrollBar();
+ void DrawRow(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect, const TEntry_Impl& rEntry);
+ bool HandleCursorKey( sal_uInt16 nKeyCode );
+ bool FindEntryPos( const TEntry_Impl& rEntry, tools::Long nStart, tools::Long nEnd, tools::Long &nFound );
+ void DeleteRemoved();
+
+ DECL_LINK( ScrollHdl, weld::ScrolledWindow&, void );
+
+ void Init();
+public:
+ explicit ExtensionBox_Impl(std::unique_ptr<weld::ScrolledWindow> xScroll);
+ virtual ~ExtensionBox_Impl() override;
+
+ virtual bool MouseButtonDown( const MouseEvent& rMEvt ) override;
+ virtual bool MouseMove( const MouseEvent& rMEvt ) override;
+ virtual bool KeyInput(const KeyEvent& rKEvt) override;
+ virtual void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle &rPaintRect ) override;
+ virtual void Resize() override;
+ virtual OUString RequestHelp(tools::Rectangle& rRect) override;
+
+ virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override;
+
+ TEntry_Impl const & GetEntryData( tools::Long nPos ) { return m_vEntries[ nPos ]; }
+ tools::Long GetEntryCount() const { return static_cast<tools::Long>(m_vEntries.size()); }
+ tools::Rectangle GetEntryRect( const tools::Long nPos ) const;
+ bool HasActive() const { return m_bHasActive; }
+ tools::Long PointToPos( const Point& rPos );
+ virtual void RecalcAll();
+ void RemoveUnlocked();
+
+
+ virtual void selectEntry( const tools::Long nPos );
+ void addEntry(const css::uno::Reference<css::deployment::XPackage> &xPackage,
+ bool bLicenseMissing = false );
+ void updateEntry(const css::uno::Reference<css::deployment::XPackage> &xPackage );
+ void removeEntry(const css::uno::Reference<css::deployment::XPackage> &xPackage );
+
+ void prepareChecking();
+ void checkEntries();
+
+ void setExtensionManager(TheExtensionManager* pManager) { m_pManager = pManager; }
+
+ //These functions are used for automatic testing
+public:
+ enum { ENTRY_NOTFOUND = -1 };
+
+ /** @return The count of the entries in the list box. */
+ sal_Int32 getItemCount() const;
+
+ /** @return The index of the first selected entry in the list box.
+ When nothing is selected, which is the case when getItemCount returns '0',
+ then this function returns ENTRY_NOTFOUND */
+ /** @return The index of the first selected entry in the list box.
+ When nothing is selected, which is the case when getItemCount returns '0',
+ then this function returns ENTRY_NOTFOUND */
+ sal_Int32 getSelIndex() const;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_service.cxx b/desktop/source/deployment/gui/dp_gui_service.cxx
new file mode 100644
index 000000000..c359cb750
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_service.cxx
@@ -0,0 +1,316 @@
+/* -*- 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 <memory>
+#include "dp_gui_theextmgr.hxx"
+#include <osl/diagnose.h>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <unotools/configmgr.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/unwrapargs.hxx>
+#include <unotools/resmgr.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/window.hxx>
+#include <vcl/svapp.hxx>
+#include <com/sun/star/task/XJobExecutor.hpp>
+#include <com/sun/star/ui/dialogs/XAsynchronousExecutableDialog.hpp>
+
+#include <optional>
+#include "license_dialog.hxx"
+#include "dp_gui_dialog2.hxx"
+#include "dp_gui_extensioncmdqueue.hxx"
+#include <dp_misc.h>
+
+using namespace ::dp_misc;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace dp_gui {
+
+namespace {
+
+class MyApp : public Application
+{
+public:
+ MyApp();
+
+ MyApp(const MyApp&) = delete;
+ const MyApp& operator=(const MyApp&) = delete;
+
+ // Application
+ virtual int Main() override;
+ virtual void DeInit() override;
+};
+
+}
+
+MyApp::MyApp()
+{
+}
+
+
+int MyApp::Main()
+{
+ return EXIT_SUCCESS;
+}
+
+void MyApp::DeInit()
+{
+ css::uno::Reference< css::uno::XComponentContext > context(
+ comphelper::getProcessComponentContext());
+ dp_misc::disposeBridges(context);
+ css::uno::Reference< css::lang::XComponent >(
+ context, css::uno::UNO_QUERY_THROW)->dispose();
+ comphelper::setProcessServiceFactory(nullptr);
+}
+
+static OUString ReplaceProductNameHookProc( const OUString& rStr )
+{
+ if (rStr.indexOf( "%PRODUCT" ) == -1)
+ return rStr;
+
+ static const OUString sProductName = utl::ConfigManager::getProductName();
+ static const OUString sVersion = utl::ConfigManager::getProductVersion();
+ static const OUString sAboutBoxVersion = utl::ConfigManager::getAboutBoxProductVersion();
+ static const OUString sAboutBoxVersionSuffix = utl::ConfigManager::getAboutBoxProductVersionSuffix();
+ static const OUString sExtension = utl::ConfigManager::getProductExtension();
+ static const OUString sOOOVendor = utl::ConfigManager::getVendor();
+
+ OUString sRet = rStr.replaceAll( "%PRODUCTNAME", sProductName );
+ sRet = sRet.replaceAll( "%PRODUCTVERSION", sVersion );
+ sRet = sRet.replaceAll( "%ABOUTBOXPRODUCTVERSIONSUFFIX", sAboutBoxVersionSuffix );
+ sRet = sRet.replaceAll( "%ABOUTBOXPRODUCTVERSION", sAboutBoxVersion );
+ sRet = sRet.replaceAll( "%OOOVENDOR", sOOOVendor );
+ sRet = sRet.replaceAll( "%PRODUCTEXTENSION", sExtension );
+ return sRet;
+}
+
+namespace {
+
+class ServiceImpl
+ : public ::cppu::WeakImplHelper<ui::dialogs::XAsynchronousExecutableDialog,
+ task::XJobExecutor, css::lang::XServiceInfo>
+{
+ Reference<XComponentContext> const m_xComponentContext;
+ std::optional< Reference<awt::XWindow> > /* const */ m_parent;
+ std::optional<OUString> m_extensionURL;
+ OUString m_initialTitle;
+ bool m_bShowUpdateOnly;
+
+public:
+ ServiceImpl( Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext );
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XAsynchronousExecutableDialog
+ virtual void SAL_CALL setDialogTitle( OUString const & aTitle ) override;
+ virtual void SAL_CALL startExecuteModal(
+ Reference< ui::dialogs::XDialogClosedListener > const & xListener ) override;
+
+ // XJobExecutor
+ virtual void SAL_CALL trigger( OUString const & event ) override;
+};
+
+}
+
+ServiceImpl::ServiceImpl( Sequence<Any> const& args,
+ Reference<XComponentContext> const& xComponentContext)
+ : m_xComponentContext(xComponentContext),
+ m_bShowUpdateOnly( false )
+{
+ /* if true then this service is running in a unopkg process and not in an office process */
+ std::optional<OUString> view;
+ try {
+ std::optional<sal_Bool> unopkg;
+ comphelper::unwrapArgs( args, m_parent, view, unopkg );
+ return;
+ } catch ( const css::lang::IllegalArgumentException & ) {
+ }
+ try {
+ comphelper::unwrapArgs( args, m_extensionURL);
+ } catch ( const css::lang::IllegalArgumentException & ) {
+ }
+
+ ResHookProc pProc = Translate::GetReadStringHook();
+ if ( !pProc )
+ Translate::SetReadStringHook(ReplaceProductNameHookProc);
+}
+
+// XServiceInfo
+OUString ServiceImpl::getImplementationName()
+{
+ return "com.sun.star.comp.deployment.ui.PackageManagerDialog";
+}
+
+sal_Bool ServiceImpl::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > ServiceImpl::getSupportedServiceNames()
+{
+ return { "com.sun.star.deployment.ui.PackageManagerDialog" };
+}
+
+// XAsynchronousExecutableDialog
+
+void ServiceImpl::setDialogTitle( OUString const & title )
+{
+ if ( dp_gui::TheExtensionManager::s_ExtMgr.is() )
+ {
+ const SolarMutexGuard guard;
+ ::rtl::Reference< ::dp_gui::TheExtensionManager > dialog(
+ ::dp_gui::TheExtensionManager::get( m_xComponentContext,
+ m_parent ? *m_parent : Reference<awt::XWindow>(),
+ m_extensionURL ? *m_extensionURL : OUString() ) );
+ dialog->SetText( title );
+ }
+ else
+ m_initialTitle = title;
+}
+
+
+void ServiceImpl::startExecuteModal(
+ Reference< ui::dialogs::XDialogClosedListener > const & xListener )
+{
+ bool bCloseDialog = true; // only used if m_bShowUpdateOnly is true
+ std::unique_ptr<Application> app;
+ //ToDo: synchronize access to s_dialog !!!
+ if (! dp_gui::TheExtensionManager::s_ExtMgr.is())
+ {
+ const bool bAppUp = (GetpApp() != nullptr);
+ bool bOfficePipePresent;
+ try {
+ bOfficePipePresent = dp_misc::office_is_running();
+ }
+ catch (const Exception & exc) {
+ if (bAppUp) {
+ const SolarMutexGuard guard;
+ vcl::Window* pWin = Application::GetActiveTopWindow();
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pWin ? pWin->GetFrameWeld() : nullptr,
+ VclMessageType::Warning, VclButtonsType::Ok, exc.Message));
+ xBox->run();
+ }
+ throw;
+ }
+
+ if (! bOfficePipePresent) {
+ OSL_ASSERT( ! bAppUp );
+ app.reset( new MyApp );
+ if (! InitVCL() )
+ throw RuntimeException( "Cannot initialize VCL!",
+ static_cast<OWeakObject *>(this) );
+ Application::SetDisplayName(
+ utl::ConfigManager::getProductName() +
+ " " +
+ utl::ConfigManager::getProductVersion());
+ ExtensionCmdQueue::syncRepositories( m_xComponentContext );
+ }
+ }
+ else
+ {
+ // When m_bShowUpdateOnly is set, we are inside the office and the user clicked
+ // the update notification icon in the menu bar. We must not close the extensions
+ // dialog after displaying the update dialog when it has been visible before
+ if ( m_bShowUpdateOnly )
+ bCloseDialog = ! dp_gui::TheExtensionManager::s_ExtMgr->isVisible();
+ }
+
+ {
+ const SolarMutexGuard guard;
+ ::rtl::Reference< ::dp_gui::TheExtensionManager > myExtMgr(
+ ::dp_gui::TheExtensionManager::get(
+ m_xComponentContext,
+ m_parent ? *m_parent : Reference<awt::XWindow>(),
+ m_extensionURL ? *m_extensionURL : OUString() ) );
+ myExtMgr->createDialog( false );
+ if (!m_initialTitle.isEmpty()) {
+ myExtMgr->SetText( m_initialTitle );
+ m_initialTitle.clear();
+ }
+ if ( m_bShowUpdateOnly )
+ {
+ myExtMgr->checkUpdates();
+ if ( bCloseDialog )
+ myExtMgr->Close();
+ else
+ myExtMgr->ToTop();
+ }
+ else
+ {
+ myExtMgr->Show();
+ myExtMgr->ToTop();
+ }
+ }
+
+ if (app != nullptr)
+ {
+ Application::Execute();
+ DeInitVCL();
+ }
+
+ if (xListener.is())
+ xListener->dialogClosed(
+ ui::dialogs::DialogClosedEvent(
+ static_cast< ::cppu::OWeakObject * >(this),
+ sal_Int16(0)) );
+}
+
+// XJobExecutor
+
+void ServiceImpl::trigger( OUString const &rEvent )
+{
+ if ( rEvent == "SHOW_UPDATE_DIALOG" )
+ m_bShowUpdateOnly = true;
+ else
+ m_bShowUpdateOnly = false;
+
+ startExecuteModal( Reference< ui::dialogs::XDialogClosedListener >() );
+}
+
+} // namespace dp_gui
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+desktop_LicenseDialog_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
+{
+ return cppu::acquire(new dp_gui::LicenseDialog(args, context));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+desktop_ServiceImpl_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
+{
+ return cppu::acquire(new dp_gui::ServiceImpl(args, context));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+desktop_UpdateRequiredDialogService_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
+{
+ return cppu::acquire(new dp_gui::UpdateRequiredDialogService(args, context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_theextmgr.cxx b/desktop/source/deployment/gui/dp_gui_theextmgr.cxx
new file mode 100644
index 000000000..5ee3f53c6
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_theextmgr.cxx
@@ -0,0 +1,537 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <utility>
+#include <vcl/svapp.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/deployment/ExtensionManager.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/TerminationVetoException.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <comphelper/propertysequence.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <osl/diagnose.h>
+#include <tools/diagnose_ex.h>
+
+#include "dp_gui_dialog2.hxx"
+#include "dp_gui_extensioncmdqueue.hxx"
+#include "dp_gui_theextmgr.hxx"
+#include <dp_misc.h>
+#include <dp_update.hxx>
+
+constexpr OUStringLiteral USER_PACKAGE_MANAGER = u"user";
+constexpr OUStringLiteral SHARED_PACKAGE_MANAGER = u"shared";
+
+using namespace ::com::sun::star;
+
+namespace dp_gui {
+
+
+::rtl::Reference< TheExtensionManager > TheExtensionManager::s_ExtMgr;
+
+
+// TheExtensionManager
+
+
+TheExtensionManager::TheExtensionManager( uno::Reference< awt::XWindow > xParent,
+ const uno::Reference< uno::XComponentContext > &xContext ) :
+ m_xContext( xContext ),
+ m_xParent(std::move( xParent )),
+ m_bModified(false),
+ m_bExtMgrDialogExecuting(false)
+{
+ m_xExtensionManager = deployment::ExtensionManager::get( xContext );
+ m_xExtensionManager->addModifyListener( this );
+
+ uno::Reference< lang::XMultiServiceFactory > xConfig(
+ configuration::theDefaultProvider::get(xContext));
+ uno::Sequence<uno::Any> args(comphelper::InitAnyPropertySequence(
+ {
+ {"nodepath", uno::Any(OUString("/org.openoffice.Office.OptionsDialog/Nodes"))}
+ }));
+ m_xNameAccessNodes.set(
+ xConfig->createInstanceWithArguments( "com.sun.star.configuration.ConfigurationAccess", args),
+ uno::UNO_QUERY_THROW);
+
+ // get the 'get more extensions here' url
+ uno::Sequence<uno::Any> args2(comphelper::InitAnyPropertySequence(
+ {
+ {"nodepath", uno::Any(OUString("/org.openoffice.Office.ExtensionManager/ExtensionRepositories"))}
+ }));
+ uno::Reference< container::XNameAccess > xNameAccessRepositories;
+ xNameAccessRepositories.set(
+ xConfig->createInstanceWithArguments( "com.sun.star.configuration.ConfigurationAccess", args2),
+ uno::UNO_QUERY_THROW);
+ try
+ { //throws css::container::NoSuchElementException, css::lang::WrappedTargetException
+ uno::Any value = xNameAccessRepositories->getByName("WebsiteLink");
+ m_sGetExtensionsURL = value.get< OUString > ();
+ }
+ catch ( const uno::Exception& )
+ {}
+
+ if ( dp_misc::office_is_running() )
+ {
+ // the registration should be done after the construction has been ended
+ // otherwise an exception prevents object creation, but it is registered as a listener
+ m_xDesktop.set( frame::Desktop::create(xContext), uno::UNO_SET_THROW );
+ m_xDesktop->addTerminateListener( this );
+ }
+}
+
+TheExtensionManager::~TheExtensionManager()
+{
+ if (m_xUpdReqDialog)
+ m_xUpdReqDialog->response(RET_CANCEL);
+ assert(!m_xUpdReqDialog);
+ if (m_xExtMgrDialog)
+ {
+ if (m_bExtMgrDialogExecuting)
+ m_xExtMgrDialog->response(RET_CANCEL);
+ else
+ {
+ m_xExtMgrDialog->Close();
+ m_xExtMgrDialog.reset();
+ }
+ }
+ assert(!m_xExtMgrDialog);
+}
+
+void TheExtensionManager::createDialog( const bool bCreateUpdDlg )
+{
+ const SolarMutexGuard guard;
+
+ if ( bCreateUpdDlg )
+ {
+ if ( !m_xUpdReqDialog )
+ {
+ m_xUpdReqDialog.reset(new UpdateRequiredDialog(Application::GetFrameWeld(m_xParent), this));
+ m_xExecuteCmdQueue.reset( new ExtensionCmdQueue( m_xUpdReqDialog.get(), this, m_xContext ) );
+ createPackageList();
+ }
+ }
+ else if ( !m_xExtMgrDialog )
+ {
+ m_xExtMgrDialog = std::make_shared<ExtMgrDialog>(Application::GetFrameWeld(m_xParent), this);
+ m_xExecuteCmdQueue.reset( new ExtensionCmdQueue( m_xExtMgrDialog.get(), this, m_xContext ) );
+ m_xExtMgrDialog->setGetExtensionsURL( m_sGetExtensionsURL );
+ createPackageList();
+ }
+}
+
+void TheExtensionManager::Show()
+{
+ const SolarMutexGuard guard;
+
+ m_bExtMgrDialogExecuting = true;
+
+ weld::DialogController::runAsync(m_xExtMgrDialog, [this](sal_Int32 /*nResult*/) {
+ m_bExtMgrDialogExecuting = false;
+ auto xExtMgrDialog = m_xExtMgrDialog;
+ m_xExtMgrDialog.reset();
+ xExtMgrDialog->Close();
+ });
+}
+
+void TheExtensionManager::SetText( const OUString &rTitle )
+{
+ const SolarMutexGuard guard;
+
+ getDialog()->set_title( rTitle );
+}
+
+
+void TheExtensionManager::ToTop()
+{
+ const SolarMutexGuard guard;
+
+ getDialog()->present();
+}
+
+void TheExtensionManager::Close()
+{
+ if (m_xExtMgrDialog)
+ {
+ if (m_bExtMgrDialogExecuting)
+ m_xExtMgrDialog->response(RET_CANCEL);
+ else
+ m_xExtMgrDialog->Close();
+ }
+ else if (m_xUpdReqDialog)
+ m_xUpdReqDialog->response(RET_CANCEL);
+}
+
+sal_Int16 TheExtensionManager::execute()
+{
+ sal_Int16 nRet = 0;
+
+ if ( m_xUpdReqDialog )
+ {
+ nRet = m_xUpdReqDialog->run();
+ m_xUpdReqDialog.reset();
+ }
+
+ return nRet;
+}
+
+bool TheExtensionManager::isVisible()
+{
+ weld::Window* pDialog = getDialog();
+ return pDialog && pDialog->get_visible();
+}
+
+void TheExtensionManager::checkUpdates()
+{
+ std::vector< uno::Reference< deployment::XPackage > > vEntries;
+ uno::Sequence< uno::Sequence< uno::Reference< deployment::XPackage > > > xAllPackages;
+
+ try {
+ xAllPackages = m_xExtensionManager->getAllExtensions( uno::Reference< task::XAbortChannel >(),
+ uno::Reference< ucb::XCommandEnvironment >() );
+ } catch ( const deployment::DeploymentException & ) {
+ return;
+ } catch ( const ucb::CommandFailedException & ) {
+ return;
+ } catch ( const ucb::CommandAbortedException & ) {
+ return;
+ } catch ( const lang::IllegalArgumentException & e ) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException( e.Message,
+ e.Context, anyEx );
+ }
+
+ for ( auto const & i : std::as_const(xAllPackages) )
+ {
+ uno::Reference< deployment::XPackage > xPackage = dp_misc::getExtensionWithHighestVersion(i);
+ OSL_ASSERT(xPackage.is());
+ if ( xPackage.is() )
+ {
+ vEntries.push_back( xPackage );
+ }
+ }
+
+ m_xExecuteCmdQueue->checkForUpdates( std::move(vEntries) );
+}
+
+
+bool TheExtensionManager::installPackage( const OUString &rPackageURL, bool bWarnUser )
+{
+ if ( rPackageURL.isEmpty() )
+ return false;
+
+ createDialog( false );
+
+ bool bInstall = true;
+ bool bInstallForAll = false;
+
+ // DV! missing function is read only repository from extension manager
+ if ( !bWarnUser && ! m_xExtensionManager->isReadOnlyRepository( SHARED_PACKAGE_MANAGER ) )
+ bInstall = getDialogHelper()->installForAllUsers( bInstallForAll );
+
+ if ( !bInstall )
+ return false;
+
+ if ( bInstallForAll )
+ m_xExecuteCmdQueue->addExtension( rPackageURL, SHARED_PACKAGE_MANAGER, false );
+ else
+ m_xExecuteCmdQueue->addExtension( rPackageURL, USER_PACKAGE_MANAGER, bWarnUser );
+
+ return true;
+}
+
+
+void TheExtensionManager::terminateDialog()
+{
+ if ( dp_misc::office_is_running() )
+ return;
+
+ const SolarMutexGuard guard;
+ if (m_xExtMgrDialog)
+ {
+ if (m_bExtMgrDialogExecuting)
+ m_xExtMgrDialog->response(RET_CANCEL);
+ else
+ {
+ m_xExtMgrDialog->Close();
+ m_xExtMgrDialog.reset();
+ }
+ }
+ assert(!m_xExtMgrDialog);
+ if (m_xUpdReqDialog)
+ m_xUpdReqDialog->response(RET_CANCEL);
+ assert(!m_xUpdReqDialog);
+ Application::Quit();
+}
+
+
+void TheExtensionManager::createPackageList()
+{
+ uno::Sequence< uno::Sequence< uno::Reference< deployment::XPackage > > > xAllPackages;
+
+ try {
+ xAllPackages = m_xExtensionManager->getAllExtensions( uno::Reference< task::XAbortChannel >(),
+ uno::Reference< ucb::XCommandEnvironment >() );
+ } catch ( const deployment::DeploymentException & ) {
+ return;
+ } catch ( const ucb::CommandFailedException & ) {
+ return;
+ } catch ( const ucb::CommandAbortedException & ) {
+ return;
+ } catch ( const lang::IllegalArgumentException & e ) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException( e.Message,
+ e.Context, anyEx );
+ }
+
+ for ( uno::Sequence< uno::Reference< deployment::XPackage > > const & xPackageList : std::as_const(xAllPackages) )
+ {
+ for ( uno::Reference< deployment::XPackage > const & xPackage : xPackageList )
+ {
+ if ( xPackage.is() )
+ {
+ PackageState eState = getPackageState( xPackage );
+ getDialogHelper()->addPackageToList( xPackage );
+ // When the package is enabled, we can stop here, otherwise we have to look for
+ // another version of this package
+ if ( ( eState == REGISTERED ) || ( eState == NOT_AVAILABLE ) )
+ break;
+ }
+ }
+ }
+
+ const uno::Sequence< uno::Reference< deployment::XPackage > > xNoLicPackages = m_xExtensionManager->getExtensionsWithUnacceptedLicenses( SHARED_PACKAGE_MANAGER,
+ uno::Reference< ucb::XCommandEnvironment >() );
+ for ( uno::Reference< deployment::XPackage > const & xPackage : xNoLicPackages )
+ {
+ if ( xPackage.is() )
+ {
+ getDialogHelper()->addPackageToList( xPackage, true );
+ }
+ }
+}
+
+
+PackageState TheExtensionManager::getPackageState( const uno::Reference< deployment::XPackage > &xPackage )
+{
+ 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 )
+ return AMBIGUOUS;
+ else
+ return reg.Value ? REGISTERED : NOT_REGISTERED;
+ }
+ else
+ return NOT_AVAILABLE;
+ }
+ catch ( const uno::RuntimeException & ) {
+ throw;
+ }
+ catch (const uno::Exception &) {
+ TOOLS_WARN_EXCEPTION( "desktop", "" );
+ return NOT_AVAILABLE;
+ }
+}
+
+
+bool TheExtensionManager::isReadOnly( const uno::Reference< deployment::XPackage > &xPackage ) const
+{
+ if ( m_xExtensionManager.is() && xPackage.is() )
+ {
+ return m_xExtensionManager->isReadOnlyRepository( xPackage->getRepositoryName() );
+ }
+ else
+ return true;
+}
+
+
+// The function investigates if the extension supports options.
+bool TheExtensionManager::supportsOptions( const uno::Reference< deployment::XPackage > &xPackage ) const
+{
+ bool bOptions = false;
+
+ if ( ! xPackage->isBundle() )
+ return false;
+
+ beans::Optional< OUString > aId = xPackage->getIdentifier();
+
+ //a bundle must always have an id
+ OSL_ASSERT( aId.IsPresent );
+
+ //iterate over all available nodes
+ const uno::Sequence< OUString > seqNames = m_xNameAccessNodes->getElementNames();
+
+ for ( OUString const & nodeName : seqNames )
+ {
+ uno::Any anyNode = m_xNameAccessNodes->getByName( nodeName );
+ //If we have a node then it must contain the set of leaves. This is part of OptionsDialog.xcs
+ uno::Reference< XInterface> xIntNode = anyNode.get< uno::Reference< XInterface > >();
+ uno::Reference< container::XNameAccess > xNode( xIntNode, uno::UNO_QUERY_THROW );
+
+ uno::Any anyLeaves = xNode->getByName("Leaves");
+ uno::Reference< XInterface > xIntLeaves = anyLeaves.get< uno::Reference< XInterface > >();
+ uno::Reference< container::XNameAccess > xLeaves( xIntLeaves, uno::UNO_QUERY_THROW );
+
+ //iterate over all available leaves
+ const uno::Sequence< OUString > seqLeafNames = xLeaves->getElementNames();
+ for ( OUString const & leafName : seqLeafNames )
+ {
+ uno::Any anyLeaf = xLeaves->getByName( leafName );
+ uno::Reference< XInterface > xIntLeaf = anyLeaf.get< uno::Reference< XInterface > >();
+ uno::Reference< beans::XPropertySet > xLeaf( xIntLeaf, uno::UNO_QUERY_THROW );
+ //investigate the Id property if it matches the extension identifier which
+ //has been passed in.
+ uno::Any anyValue = xLeaf->getPropertyValue("Id");
+
+ OUString sId = anyValue.get< OUString >();
+ if ( sId == aId.Value )
+ {
+ bOptions = true;
+ break;
+ }
+ }
+ if ( bOptions )
+ break;
+ }
+ return bOptions;
+}
+
+
+// XEventListener
+void TheExtensionManager::disposing( lang::EventObject const & rEvt )
+{
+ bool shutDown = (rEvt.Source == m_xDesktop);
+
+ if ( shutDown && m_xDesktop.is() )
+ {
+ m_xDesktop->removeTerminateListener( this );
+ m_xDesktop.clear();
+ }
+
+ if ( !shutDown )
+ return;
+
+ if ( dp_misc::office_is_running() )
+ {
+ const SolarMutexGuard guard;
+ if (m_xExtMgrDialog)
+ {
+ if (m_bExtMgrDialogExecuting)
+ m_xExtMgrDialog->response(RET_CANCEL);
+ else
+ {
+ m_xExtMgrDialog->Close();
+ m_xExtMgrDialog.reset();
+ }
+ }
+ assert(!m_xExtMgrDialog);
+ if (m_xUpdReqDialog)
+ m_xUpdReqDialog->response(RET_CANCEL);
+ assert(!m_xUpdReqDialog);
+ }
+ s_ExtMgr.clear();
+}
+
+// XTerminateListener
+void TheExtensionManager::queryTermination( ::lang::EventObject const & )
+{
+ DialogHelper *pDialogHelper = getDialogHelper();
+
+ if ( m_xExecuteCmdQueue->isBusy() || ( pDialogHelper && pDialogHelper->isBusy() ) )
+ {
+ ToTop();
+ throw frame::TerminationVetoException(
+ "The office cannot be closed while the Extension Manager is running",
+ static_cast<frame::XTerminateListener*>(this));
+ }
+ else
+ {
+ clearModified();
+ if (m_xExtMgrDialog)
+ {
+ if (m_bExtMgrDialogExecuting)
+ m_xExtMgrDialog->response(RET_CANCEL);
+ else
+ {
+ m_xExtMgrDialog->Close();
+ m_xExtMgrDialog.reset();
+ }
+ }
+ if (m_xUpdReqDialog)
+ m_xUpdReqDialog->response(RET_CANCEL);
+ }
+}
+
+void TheExtensionManager::notifyTermination( ::lang::EventObject const & rEvt )
+{
+ disposing( rEvt );
+}
+
+// XModifyListener
+void TheExtensionManager::modified( ::lang::EventObject const & /*rEvt*/ )
+{
+ m_bModified = true;
+ DialogHelper *pDialogHelper = getDialogHelper();
+ if (!pDialogHelper)
+ return;
+ pDialogHelper->prepareChecking();
+ createPackageList();
+ pDialogHelper->checkEntries();
+}
+
+
+::rtl::Reference< TheExtensionManager > TheExtensionManager::get( const uno::Reference< uno::XComponentContext > &xContext,
+ const uno::Reference< awt::XWindow > &xParent,
+ const OUString & extensionURL )
+{
+ if ( s_ExtMgr.is() )
+ {
+ OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
+ if ( !extensionURL.isEmpty() )
+ s_ExtMgr->installPackage( extensionURL, true );
+ return s_ExtMgr;
+ }
+
+ ::rtl::Reference<TheExtensionManager> that( new TheExtensionManager( xParent, xContext ) );
+
+ const SolarMutexGuard guard;
+ if ( ! s_ExtMgr.is() )
+ {
+ OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
+ s_ExtMgr = that;
+ }
+
+ if ( !extensionURL.isEmpty() )
+ s_ExtMgr->installPackage( extensionURL, true );
+
+ return s_ExtMgr;
+}
+
+} //namespace dp_gui
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_theextmgr.hxx b/desktop/source/deployment/gui/dp_gui_theextmgr.hxx
new file mode 100644
index 000000000..13c329d6d
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_theextmgr.hxx
@@ -0,0 +1,127 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/deployment/XExtensionManager.hpp>
+#include <com/sun/star/frame/XDesktop2.hpp>
+#include <com/sun/star/frame/XTerminateListener.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/util/XModifyListener.hpp>
+
+#include "dp_gui.h"
+#include "dp_gui_dialog2.hxx"
+
+
+namespace dp_gui {
+
+
+class ExtensionCmdQueue;
+
+
+class TheExtensionManager :
+ public ::cppu::WeakImplHelper< css::frame::XTerminateListener,
+ css::util::XModifyListener >
+{
+private:
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ css::uno::Reference< css::frame::XDesktop2 > m_xDesktop;
+ css::uno::Reference< css::deployment::XExtensionManager > m_xExtensionManager;
+ css::uno::Reference< css::container::XNameAccess > m_xNameAccessNodes;
+ css::uno::Reference< css::awt::XWindow > m_xParent;
+ std::shared_ptr<ExtMgrDialog> m_xExtMgrDialog;
+ std::unique_ptr<UpdateRequiredDialog> m_xUpdReqDialog;
+ std::unique_ptr<ExtensionCmdQueue> m_xExecuteCmdQueue;
+
+ OUString m_sGetExtensionsURL;
+ bool m_bModified;
+ bool m_bExtMgrDialogExecuting;
+
+public:
+ static ::rtl::Reference<TheExtensionManager> s_ExtMgr;
+
+ TheExtensionManager( css::uno::Reference< css::awt::XWindow > xParent,
+ const css::uno::Reference< css::uno::XComponentContext > &xContext );
+ virtual ~TheExtensionManager() override;
+
+ void createDialog( const bool bCreateUpdDlg );
+ sal_Int16 execute();
+
+ bool isModified() const { return m_bModified; }
+ void clearModified() { m_bModified = false; }
+
+ weld::Window* getDialog()
+ {
+ if (m_xExtMgrDialog)
+ return m_xExtMgrDialog->getDialog();
+ if (m_xUpdReqDialog)
+ return m_xUpdReqDialog->getDialog();
+ return nullptr;
+ }
+ DialogHelper* getDialogHelper()
+ {
+ if (m_xExtMgrDialog)
+ return m_xExtMgrDialog.get();
+ return m_xUpdReqDialog.get();
+ }
+ ExtensionCmdQueue* getCmdQueue() const { return m_xExecuteCmdQueue.get(); }
+
+ void SetText( const OUString &rTitle );
+ void Show();
+ void ToTop();
+ void Close();
+ bool isVisible();
+
+
+ void checkUpdates();
+ bool installPackage( const OUString &rPackageURL, bool bWarnUser = false );
+ void createPackageList();
+
+ void terminateDialog();
+
+ // Tools
+ bool supportsOptions( const css::uno::Reference< css::deployment::XPackage > &xPackage ) const;
+ static PackageState getPackageState( const css::uno::Reference< css::deployment::XPackage > &xPackage );
+ const css::uno::Reference< css::uno::XComponentContext >& getContext() const { return m_xContext; }
+ const css::uno::Reference< css::deployment::XExtensionManager >& getExtensionManager() const { return m_xExtensionManager; }
+ bool isReadOnly( const css::uno::Reference< css::deployment::XPackage > &xPackage ) const;
+
+
+ static ::rtl::Reference<TheExtensionManager> get(
+ css::uno::Reference< css::uno::XComponentContext> const & xContext,
+ css::uno::Reference< css::awt::XWindow> const & xParent = nullptr,
+ OUString const & view = OUString() );
+
+ // XEventListener
+ virtual void SAL_CALL disposing( css::lang::EventObject const & evt ) override;
+
+ // XTerminateListener
+ virtual void SAL_CALL queryTermination( css::lang::EventObject const & evt ) override;
+ virtual void SAL_CALL notifyTermination( css::lang::EventObject const & evt ) override;
+
+ // XModifyListener
+ virtual void SAL_CALL modified( css::lang::EventObject const & evt ) override;
+};
+
+} // namespace dp_gui
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_updatedata.hxx b/desktop/source/deployment/gui/dp_gui_updatedata.hxx
new file mode 100644
index 000000000..efac4c587
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_updatedata.hxx
@@ -0,0 +1,74 @@
+/* -*- 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 .
+ */
+#pragma once
+
+#include <sal/config.h>
+#include <rtl/ustring.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <utility>
+
+namespace com::sun::star::deployment {
+ class XPackage;
+}
+namespace com::sun::star::xml::dom {
+ class XNode;
+}
+
+
+namespace dp_gui {
+
+struct UpdateData
+{
+ explicit UpdateData( css::uno::Reference< css::deployment::XPackage > xExt):
+ bIsShared(false), aInstalledPackage(std::move(xExt)) {};
+
+ //When entries added to the listbox then there can be one for the user update and one
+ //for the shared update. However, both list entries will contain the same UpdateData.
+ //isShared is used to indicate which one is used for the shared entry.
+ bool bIsShared;
+
+ //The currently installed extension which is going to be updated. If the extension exist in
+ //multiple repositories then it is the one with the highest version.
+ css::uno::Reference< css::deployment::XPackage > aInstalledPackage;
+
+ //The version of the update
+ OUString updateVersion;
+
+ //For online update
+
+ // The content of the update information.
+ //Only if aUpdateInfo is set then there is an online update available with a better version
+ //than any of the currently installed extensions with the same identifier.
+ css::uno::Reference< css::xml::dom::XNode > aUpdateInfo;
+ //The URL of the locally downloaded extension. It will only be set if there were no errors
+ //during the download
+ OUString sLocalURL;
+ //The URL of the website where the download can be obtained.
+ OUString sWebsiteURL;
+
+ //For local update
+
+ //The locale extension which is used as update for the user or shared repository.
+ //If set then the data for the online update (aUpdateInfo, sLocalURL, sWebsiteURL)
+ //are to be ignored.
+ css::uno::Reference< css::deployment::XPackage > aUpdateSource;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_updatedialog.cxx b/desktop/source/deployment/gui/dp_gui_updatedialog.cxx
new file mode 100644
index 000000000..2861d7785
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_updatedialog.cxx
@@ -0,0 +1,994 @@
+/* -*- 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 <sal/config.h>
+
+#include <utility>
+#include <vector>
+
+
+#include <optional>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/deployment/UpdateInformationProvider.hpp>
+#include <com/sun/star/deployment/ExtensionManager.hpp>
+#include <com/sun/star/deployment/XUpdateInformationProvider.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Exception.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/util/URL.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/xml/dom/XElement.hpp>
+#include <osl/diagnose.h>
+#include <rtl/ref.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+#include <salhelper/thread.hxx>
+#include <tools/gen.hxx>
+#include <tools/link.hxx>
+#include <unotools/configmgr.hxx>
+#include <vcl/svapp.hxx>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+
+#include <dp_dependencies.hxx>
+#include <dp_descriptioninfoset.hxx>
+#include <dp_identifier.hxx>
+#include <dp_misc.h>
+#include <dp_update.hxx>
+
+#include <strings.hrc>
+#include "dp_gui_updatedata.hxx"
+#include "dp_gui_updatedialog.hxx"
+#include <dp_shared.hxx>
+
+class KeyEvent;
+class MouseEvent;
+namespace com::sun::star::uno {
+ class XComponentContext;
+}
+
+using namespace ::com::sun::star;
+using dp_gui::UpdateDialog;
+
+namespace {
+
+sal_Unicode const LF = 0x000A;
+sal_Unicode const CR = 0x000D;
+
+constexpr OUStringLiteral IGNORED_UPDATES = u"/org.openoffice.Office.ExtensionManager/ExtensionUpdateData/IgnoredUpdates";
+constexpr OUStringLiteral PROPERTY_VERSION = u"Version";
+
+enum Kind { ENABLED_UPDATE, DISABLED_UPDATE, SPECIFIC_ERROR };
+
+OUString confineToParagraph(OUString const & text) {
+ // Confine arbitrary text to a single paragraph in a VclMultiLineEdit
+ // This assumes that U+000A and U+000D are the only paragraph separators in
+ // a VclMultiLineEdit, and that replacing them with a single space
+ // each is acceptable:
+ return text.replace(LF, ' ').replace(CR, ' ');
+}
+}
+
+struct UpdateDialog::DisabledUpdate {
+ OUString name;
+ uno::Sequence< OUString > unsatisfiedDependencies;
+ // We also want to show release notes and publisher for disabled updates
+ css::uno::Reference< css::xml::dom::XNode > aUpdateInfo;
+};
+
+struct UpdateDialog::SpecificError {
+ OUString name;
+ OUString message;
+};
+
+
+struct UpdateDialog::IgnoredUpdate {
+ OUString sExtensionID;
+ OUString sVersion;
+
+ IgnoredUpdate( OUString aExtensionID, OUString aVersion );
+};
+
+
+UpdateDialog::IgnoredUpdate::IgnoredUpdate( OUString aExtensionID, OUString aVersion ):
+ sExtensionID(std::move( aExtensionID )),
+ sVersion(std::move( aVersion ))
+{}
+
+
+struct UpdateDialog::Index
+{
+ Kind m_eKind;
+ bool m_bIgnored;
+ sal_uInt16 m_nIndex;
+ OUString m_aName;
+
+ Index( Kind theKind, sal_uInt16 nIndex, OUString aName ) :
+ m_eKind( theKind ),
+ m_bIgnored( false ),
+ m_nIndex( nIndex ),
+ m_aName(std::move( aName )) {}
+};
+
+
+class UpdateDialog::Thread: public salhelper::Thread {
+public:
+ Thread(
+ uno::Reference< uno::XComponentContext > const & context,
+ UpdateDialog & dialog,
+ std::vector< uno::Reference< deployment::XPackage > > && vExtensionList);
+
+ void stop();
+
+private:
+ virtual ~Thread() override;
+
+ virtual void execute() override;
+
+ void handleSpecificError(
+ uno::Reference< deployment::XPackage > const & package,
+ uno::Any const & exception) const;
+
+ OUString getUpdateDisplayString(
+ dp_gui::UpdateData const & data, std::u16string_view version = std::u16string_view()) const;
+
+ void prepareUpdateData(
+ css::uno::Reference< css::xml::dom::XNode > const & updateInfo,
+ UpdateDialog::DisabledUpdate & out_du,
+ dp_gui::UpdateData & out_data) const;
+
+ bool update(
+ UpdateDialog::DisabledUpdate const & du,
+ dp_gui::UpdateData const & data) const;
+
+ uno::Reference< uno::XComponentContext > m_context;
+ UpdateDialog & m_dialog;
+ std::vector< uno::Reference< deployment::XPackage > > m_vExtensionList;
+ uno::Reference< deployment::XUpdateInformationProvider > m_updateInformation;
+ uno::Reference< task::XInteractionHandler > m_xInteractionHdl;
+
+ // guarded by Application::GetSolarMutex():
+ bool m_stop;
+};
+
+UpdateDialog::Thread::Thread(
+ uno::Reference< uno::XComponentContext > const & context,
+ UpdateDialog & dialog,
+ std::vector< uno::Reference< deployment::XPackage > >&& vExtensionList):
+ salhelper::Thread("dp_gui_updatedialog"),
+ m_context(context),
+ m_dialog(dialog),
+ m_vExtensionList(std::move(vExtensionList)),
+ m_updateInformation(
+ deployment::UpdateInformationProvider::create(context)),
+ m_stop(false)
+{
+ if( m_context.is() )
+ {
+ m_xInteractionHdl =
+ task::InteractionHandler::createWithParent(m_context, dialog.getDialog()->GetXWindow());
+ m_updateInformation->setInteractionHandler( m_xInteractionHdl );
+ }
+}
+
+void UpdateDialog::Thread::stop() {
+ {
+ SolarMutexGuard g;
+ m_stop = true;
+ }
+ m_updateInformation->cancel();
+}
+
+UpdateDialog::Thread::~Thread()
+{
+ if ( m_xInteractionHdl.is() )
+ m_updateInformation->setInteractionHandler( uno::Reference< task::XInteractionHandler > () );
+}
+
+void UpdateDialog::Thread::execute()
+{
+ {
+ SolarMutexGuard g;
+ if ( m_stop ) {
+ return;
+ }
+ }
+ uno::Reference<deployment::XExtensionManager> extMgr =
+ deployment::ExtensionManager::get(m_context);
+
+ std::vector<std::pair<uno::Reference<deployment::XPackage>, uno::Any > > errors;
+
+ dp_misc::UpdateInfoMap updateInfoMap = dp_misc::getOnlineUpdateInfos(
+ m_context, extMgr, m_updateInformation, &m_vExtensionList, errors);
+
+ for (auto const& elem : errors)
+ handleSpecificError(elem.first, elem.second);
+
+ for (auto const& updateInfo : updateInfoMap)
+ {
+ dp_misc::UpdateInfo const & info = updateInfo.second;
+ UpdateData updateData(info.extension);
+ DisabledUpdate disableUpdate;
+ //determine if online updates meet the requirements
+ prepareUpdateData(info.info, disableUpdate, updateData);
+
+ //determine if the update is installed in the user or shared repository
+ OUString sOnlineVersion;
+ if (info.info.is())
+ sOnlineVersion = info.version;
+ OUString sVersionUser;
+ OUString sVersionShared;
+ OUString sVersionBundled;
+ uno::Sequence< uno::Reference< deployment::XPackage> > extensions;
+ try {
+ extensions = extMgr->getExtensionsWithSameIdentifier(
+ dp_misc::getIdentifier(info.extension), info.extension->getName(),
+ uno::Reference<ucb::XCommandEnvironment>());
+ } catch ( const lang::IllegalArgumentException& ) {
+ OSL_ASSERT(false);
+ continue;
+ } catch ( const css::ucb::CommandFailedException& ) {
+ OSL_ASSERT(false);
+ continue;
+ }
+ OSL_ASSERT(extensions.getLength() == 3);
+ if (extensions[0].is() )
+ sVersionUser = extensions[0]->getVersion();
+ if (extensions[1].is() )
+ sVersionShared = extensions[1]->getVersion();
+ if (extensions[2].is() )
+ sVersionBundled = extensions[2]->getVersion();
+
+ bool bSharedReadOnly = extMgr->isReadOnlyRepository("shared");
+
+ dp_misc::UPDATE_SOURCE sourceUser = dp_misc::isUpdateUserExtension(
+ bSharedReadOnly, sVersionUser, sVersionShared, sVersionBundled, sOnlineVersion);
+ dp_misc::UPDATE_SOURCE sourceShared = dp_misc::isUpdateSharedExtension(
+ bSharedReadOnly, sVersionShared, sVersionBundled, sOnlineVersion);
+
+ if (sourceUser != dp_misc::UPDATE_SOURCE_NONE)
+ {
+ if (sourceUser == dp_misc::UPDATE_SOURCE_SHARED)
+ {
+ updateData.aUpdateSource = extensions[1];
+ updateData.updateVersion = extensions[1]->getVersion();
+ }
+ else if (sourceUser == dp_misc::UPDATE_SOURCE_BUNDLED)
+ {
+ updateData.aUpdateSource = extensions[2];
+ updateData.updateVersion = extensions[2]->getVersion();
+ }
+ if (!update(disableUpdate, updateData))
+ return;
+ }
+
+ if (sourceShared != dp_misc::UPDATE_SOURCE_NONE)
+ {
+ if (sourceShared == dp_misc::UPDATE_SOURCE_BUNDLED)
+ {
+ updateData.aUpdateSource = extensions[2];
+ updateData.updateVersion = extensions[2]->getVersion();
+ }
+ updateData.bIsShared = true;
+ if (!update(disableUpdate, updateData))
+ return;
+ }
+ }
+
+
+ SolarMutexGuard g;
+ if (!m_stop) {
+ m_dialog.checkingDone();
+ }
+}
+
+//Parameter package can be null
+void UpdateDialog::Thread::handleSpecificError(
+ uno::Reference< deployment::XPackage > const & package,
+ uno::Any const & exception) const
+{
+ UpdateDialog::SpecificError data;
+ if (package.is())
+ data.name = package->getDisplayName();
+ uno::Exception e;
+ if (exception >>= e) {
+ data.message = e.Message;
+ }
+ SolarMutexGuard g;
+ if (!m_stop) {
+ m_dialog.addSpecificError(data);
+ }
+}
+
+OUString UpdateDialog::Thread::getUpdateDisplayString(
+ dp_gui::UpdateData const & data, std::u16string_view version) const
+{
+ OSL_ASSERT(data.aInstalledPackage.is());
+ OUStringBuffer b(data.aInstalledPackage->getDisplayName());
+ b.append(' ');
+ {
+ SolarMutexGuard g;
+ if(!m_stop)
+ b.append(m_dialog.m_version);
+ }
+ b.append(' ');
+ if (!version.empty())
+ b.append(version);
+ else
+ b.append(data.updateVersion);
+
+ if (!data.sWebsiteURL.isEmpty())
+ {
+ b.append(' ');
+ {
+ SolarMutexGuard g;
+ if(!m_stop)
+ b.append(m_dialog.m_browserbased);
+ }
+ }
+ return b.makeStringAndClear();
+}
+
+/** out_data will only be filled if all dependencies are ok.
+ */
+void UpdateDialog::Thread::prepareUpdateData(
+ uno::Reference< xml::dom::XNode > const & updateInfo,
+ UpdateDialog::DisabledUpdate & out_du,
+ dp_gui::UpdateData & out_data) const
+{
+ if (!updateInfo.is())
+ return;
+ dp_misc::DescriptionInfoset infoset(m_context, updateInfo);
+ OSL_ASSERT(!infoset.getVersion().isEmpty());
+ uno::Sequence< uno::Reference< xml::dom::XElement > > ds(
+ dp_misc::Dependencies::check(infoset));
+
+ out_du.aUpdateInfo = updateInfo;
+ out_du.unsatisfiedDependencies.realloc(ds.getLength());
+ auto p_unsatisfiedDependencies = out_du.unsatisfiedDependencies.getArray();
+ for (sal_Int32 i = 0; i < ds.getLength(); ++i) {
+ p_unsatisfiedDependencies[i] = dp_misc::Dependencies::getErrorText(ds[i]);
+ }
+
+ const ::std::optional< OUString> updateWebsiteURL(infoset.getLocalizedUpdateWebsiteURL());
+
+ out_du.name = getUpdateDisplayString(out_data, infoset.getVersion());
+
+ if (!out_du.unsatisfiedDependencies.hasElements())
+ {
+ out_data.aUpdateInfo = updateInfo;
+ out_data.updateVersion = infoset.getVersion();
+ if (updateWebsiteURL)
+ out_data.sWebsiteURL = *updateWebsiteURL;
+ }
+}
+
+bool UpdateDialog::Thread::update(
+ UpdateDialog::DisabledUpdate const & du,
+ dp_gui::UpdateData const & data) const
+{
+ bool ret = false;
+ if (!du.unsatisfiedDependencies.hasElements())
+ {
+ SolarMutexGuard g;
+ if (!m_stop) {
+ m_dialog.addEnabledUpdate(getUpdateDisplayString(data), data);
+ }
+ ret = !m_stop;
+ } else {
+ SolarMutexGuard g;
+ if (!m_stop) {
+ m_dialog.addDisabledUpdate(du);
+ }
+ ret = !m_stop;
+ }
+ return ret;
+}
+
+// UpdateDialog ----------------------------------------------------------
+UpdateDialog::UpdateDialog(
+ uno::Reference< uno::XComponentContext > const & context,
+ weld::Window * parent, std::vector<uno::Reference< deployment::XPackage > > && vExtensionList,
+ std::vector< dp_gui::UpdateData > * updateData)
+ : GenericDialogController(parent, "desktop/ui/updatedialog.ui", "UpdateDialog")
+ , m_context(context)
+ , m_none(DpResId(RID_DLG_UPDATE_NONE))
+ , m_noInstallable(DpResId(RID_DLG_UPDATE_NOINSTALLABLE))
+ , m_failure(DpResId(RID_DLG_UPDATE_FAILURE))
+ , m_unknownError(DpResId(RID_DLG_UPDATE_UNKNOWNERROR))
+ , m_noDescription(DpResId(RID_DLG_UPDATE_NODESCRIPTION))
+ , m_noInstall(DpResId(RID_DLG_UPDATE_NOINSTALL))
+ , m_noDependency(DpResId(RID_DLG_UPDATE_NODEPENDENCY))
+ , m_noDependencyCurVer(DpResId(RID_DLG_UPDATE_NODEPENDENCY_CUR_VER))
+ , m_browserbased(DpResId(RID_DLG_UPDATE_BROWSERBASED))
+ , m_version(DpResId(RID_DLG_UPDATE_VERSION))
+ , m_ignoredUpdate(DpResId(RID_DLG_UPDATE_IGNORED_UPDATE))
+ , m_updateData(*updateData)
+ , m_thread(new UpdateDialog::Thread(context, *this, std::move(vExtensionList)))
+ , m_xChecking(m_xBuilder->weld_label("UPDATE_CHECKING"))
+ , m_xThrobber(m_xBuilder->weld_spinner("THROBBER"))
+ , m_xUpdate(m_xBuilder->weld_label("UPDATE_LABEL"))
+ , m_xUpdates(m_xBuilder->weld_tree_view("checklist"))
+ , m_xAll(m_xBuilder->weld_check_button("UPDATE_ALL"))
+ , m_xDescription(m_xBuilder->weld_label("DESCRIPTION_LABEL"))
+ , m_xPublisherLabel(m_xBuilder->weld_label("PUBLISHER_LABEL"))
+ , m_xPublisherLink(m_xBuilder->weld_link_button("PUBLISHER_LINK"))
+ , m_xReleaseNotesLabel(m_xBuilder->weld_label("RELEASE_NOTES_LABEL"))
+ , m_xReleaseNotesLink(m_xBuilder->weld_link_button("RELEASE_NOTES_LINK"))
+ , m_xDescriptions(m_xBuilder->weld_text_view("DESCRIPTIONS"))
+ , m_xOk(m_xBuilder->weld_button("ok"))
+ , m_xClose(m_xBuilder->weld_button("close"))
+ , m_xHelp(m_xBuilder->weld_button("help"))
+{
+ auto nWidth = m_xDescriptions->get_approximate_digit_width() * 62;
+ auto nHeight = m_xDescriptions->get_height_rows(8);
+ m_xDescriptions->set_size_request(nWidth, nHeight);
+ m_xUpdates->set_size_request(nWidth, nHeight);
+
+ m_xUpdates->enable_toggle_buttons(weld::ColumnToggleType::Check);
+
+ OSL_ASSERT(updateData != nullptr);
+
+ m_xExtensionManager = deployment::ExtensionManager::get( context );
+
+ m_xUpdates->connect_changed(LINK(this, UpdateDialog, selectionHandler));
+ m_xUpdates->connect_toggled(LINK(this, UpdateDialog, entryToggled));
+ m_xAll->connect_toggled(LINK(this, UpdateDialog, allHandler));
+ m_xOk->connect_clicked(LINK(this, UpdateDialog, okHandler));
+ m_xClose->connect_clicked(LINK(this, UpdateDialog, closeHandler));
+ if (!dp_misc::office_is_running())
+ m_xHelp->set_sensitive(false);
+
+ initDescription();
+ getIgnoredUpdates();
+}
+
+UpdateDialog::~UpdateDialog()
+{
+}
+
+short UpdateDialog::run() {
+ m_xThrobber->start();
+ m_thread->launch();
+ short nRet = GenericDialogController::run();
+ m_thread->stop();
+ return nRet;
+}
+
+IMPL_LINK(UpdateDialog, entryToggled, const weld::TreeView::iter_col&, rRowCol, void)
+{
+ // error's can't be enabled
+ const UpdateDialog::Index* p = weld::fromId<UpdateDialog::Index const *>(m_xUpdates->get_id(rRowCol.first));
+ if (p->m_eKind == SPECIFIC_ERROR)
+ m_xUpdates->set_toggle(rRowCol.first, TRISTATE_FALSE);
+
+ enableOk();
+}
+
+void UpdateDialog::insertItem(const UpdateDialog::Index *pEntry, bool bEnabledCheckBox)
+{
+ int nEntry = m_xUpdates->n_children();
+ m_xUpdates->append();
+ m_xUpdates->set_toggle(nEntry, bEnabledCheckBox ? TRISTATE_TRUE : TRISTATE_FALSE);
+ m_xUpdates->set_text(nEntry, pEntry->m_aName, 0);
+ m_xUpdates->set_id(nEntry, weld::toId(pEntry));
+}
+
+void UpdateDialog::addAdditional(const UpdateDialog::Index * index, bool bEnabledCheckBox)
+{
+ m_xAll->set_sensitive(true);
+ if (m_xAll->get_active())
+ {
+ insertItem(index, bEnabledCheckBox);
+ m_xUpdate->set_sensitive(true);
+ m_xUpdates->set_sensitive(true);
+ m_xDescription->set_sensitive(true);
+ m_xDescriptions->set_sensitive(true);
+ }
+}
+
+void UpdateDialog::addEnabledUpdate( OUString const & name,
+ dp_gui::UpdateData const & data )
+{
+ sal_uInt16 nIndex = sal::static_int_cast< sal_uInt16 >( m_enabledUpdates.size() );
+ UpdateDialog::Index *pEntry = new UpdateDialog::Index( ENABLED_UPDATE, nIndex, name );
+
+ m_enabledUpdates.push_back( data );
+ m_ListboxEntries.emplace_back( pEntry );
+
+ if (!isIgnoredUpdate(pEntry))
+ {
+ insertItem(pEntry, true);
+ }
+ else
+ addAdditional(pEntry, false);
+
+ m_xUpdate->set_sensitive(true);
+ m_xUpdates->set_sensitive(true);
+ m_xDescription->set_sensitive(true);
+ m_xDescriptions->set_sensitive(true);
+}
+
+void UpdateDialog::addDisabledUpdate( UpdateDialog::DisabledUpdate const & data )
+{
+ sal_uInt16 nIndex = sal::static_int_cast< sal_uInt16 >( m_disabledUpdates.size() );
+ UpdateDialog::Index *pEntry = new UpdateDialog::Index( DISABLED_UPDATE, nIndex, data.name );
+
+ m_disabledUpdates.push_back( data );
+ m_ListboxEntries.emplace_back( pEntry );
+
+ isIgnoredUpdate( pEntry );
+ addAdditional(pEntry, false);
+}
+
+void UpdateDialog::addSpecificError( UpdateDialog::SpecificError const & data )
+{
+ sal_uInt16 nIndex = sal::static_int_cast< sal_uInt16 >( m_specificErrors.size() );
+ UpdateDialog::Index *pEntry = new UpdateDialog::Index( SPECIFIC_ERROR, nIndex, data.name );
+
+ m_specificErrors.push_back( data );
+ m_ListboxEntries.emplace_back( pEntry );
+
+ addAdditional(pEntry, false);
+}
+
+void UpdateDialog::checkingDone() {
+ m_xChecking->hide();
+ m_xThrobber->stop();
+ m_xThrobber->hide();
+ if (m_xUpdates->n_children() == 0)
+ {
+ clearDescription();
+ m_xDescription->set_sensitive(true);
+ m_xDescriptions->set_sensitive(true);
+
+ if ( m_disabledUpdates.empty() && m_specificErrors.empty() && m_ignoredUpdates.empty() )
+ showDescription( m_none );
+ else
+ showDescription( m_noInstallable );
+ }
+
+ enableOk();
+}
+
+void UpdateDialog::enableOk() {
+ if (!m_xChecking->get_visible()) {
+ int nChecked = 0;
+ for (int i = 0, nCount = m_xUpdates->n_children(); i < nCount; ++i) {
+ if (m_xUpdates->get_toggle(i) == TRISTATE_TRUE)
+ ++nChecked;
+ }
+ m_xOk->set_sensitive(nChecked != 0);
+ }
+}
+
+// *********************************************************************************
+void UpdateDialog::createNotifyJob( bool bPrepareOnly,
+ uno::Sequence< uno::Sequence< OUString > > const &rItemList )
+{
+ if ( !dp_misc::office_is_running() )
+ return;
+
+ // notify update check job
+ try
+ {
+ uno::Reference< lang::XMultiServiceFactory > xConfigProvider(
+ configuration::theDefaultProvider::get(
+ comphelper::getProcessComponentContext()));
+
+ uno::Sequence< uno::Any > aArgumentList{ uno::Any(comphelper::makePropertyValue(
+ "nodepath",
+ OUString("org.openoffice.Office.Addons/AddonUI/OfficeHelp/UpdateCheckJob"))) };
+
+ uno::Reference< container::XNameAccess > xNameAccess(
+ xConfigProvider->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationAccess", aArgumentList ),
+ uno::UNO_QUERY_THROW );
+
+ util::URL aURL;
+ xNameAccess->getByName("URL") >>= aURL.Complete;
+
+ uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+ uno::Reference < util::XURLTransformer > xTransformer = util::URLTransformer::create(xContext);
+
+ xTransformer->parseStrict(aURL);
+
+ uno::Reference < frame::XDesktop2 > xDesktop = frame::Desktop::create( xContext );
+ uno::Reference< frame::XDispatchProvider > xDispatchProvider( xDesktop->getCurrentFrame(),
+ uno::UNO_QUERY_THROW );
+ uno::Reference< frame::XDispatch > xDispatch = xDispatchProvider->queryDispatch(aURL, OUString(), 0);
+
+ if( xDispatch.is() )
+ {
+ uno::Sequence aPropList{ comphelper::makePropertyValue("updateList", rItemList),
+ comphelper::makePropertyValue("prepareOnly", bPrepareOnly) };
+
+ xDispatch->dispatch(aURL, aPropList );
+ }
+ }
+ catch( const uno::Exception& e )
+ {
+ dp_misc::TRACE( "Caught exception: "
+ + e.Message + "\n thread terminated.\n\n");
+ }
+}
+
+// *********************************************************************************
+void UpdateDialog::notifyMenubar( bool bPrepareOnly, bool bRecheckOnly )
+{
+ if ( !dp_misc::office_is_running() )
+ return;
+
+ uno::Sequence< uno::Sequence< OUString > > aItemList;
+
+ if ( ! bRecheckOnly )
+ {
+ sal_Int32 nCount = 0;
+ for (sal_uInt16 i = 0, nItemCount = m_xUpdates->n_children(); i < nItemCount; ++i)
+ {
+
+ UpdateDialog::Index const * p = weld::fromId<UpdateDialog::Index const*>(m_xUpdates->get_id(i));
+
+ if ( p->m_eKind == ENABLED_UPDATE )
+ {
+ dp_gui::UpdateData aUpdData = m_enabledUpdates[ p->m_nIndex ];
+
+ dp_misc::DescriptionInfoset aInfoset( m_context, aUpdData.aUpdateInfo );
+ uno::Sequence< OUString > aItem
+ {
+ dp_misc::getIdentifier( aUpdData.aInstalledPackage ),
+ aInfoset.getVersion()
+ };
+ aItemList.realloc( nCount + 1 );
+ aItemList.getArray()[ nCount ] = aItem;
+ nCount += 1;
+ }
+ else
+ continue;
+ }
+ }
+
+ createNotifyJob( bPrepareOnly, aItemList );
+}
+
+// *********************************************************************************
+
+void UpdateDialog::initDescription()
+{
+ m_xPublisherLabel->hide();
+ m_xPublisherLink->hide();
+ m_xReleaseNotesLabel->hide();
+ m_xReleaseNotesLink->hide();
+}
+
+void UpdateDialog::clearDescription()
+{
+ m_xPublisherLabel->hide();
+ m_xPublisherLink->hide();
+ m_xPublisherLink->set_label("");
+ m_xPublisherLink->set_uri("");
+ m_xReleaseNotesLabel->hide();
+ m_xReleaseNotesLink->hide();
+ m_xReleaseNotesLink->set_uri( "" );
+ m_xDescriptions->set_text("");
+}
+
+bool UpdateDialog::showDescription(uno::Reference< xml::dom::XNode > const & aUpdateInfo)
+{
+ dp_misc::DescriptionInfoset infoset(m_context, aUpdateInfo);
+ return showDescription(infoset.getLocalizedPublisherNameAndURL(),
+ infoset.getLocalizedReleaseNotesURL());
+}
+
+bool UpdateDialog::showDescription(uno::Reference< deployment::XPackage > const & aExtension)
+{
+ OSL_ASSERT(aExtension.is());
+ beans::StringPair pubInfo = aExtension->getPublisherInfo();
+ return showDescription(std::make_pair(pubInfo.First, pubInfo.Second),
+ "");
+}
+
+bool UpdateDialog::showDescription(std::pair< OUString, OUString > const & pairPublisher,
+ OUString const & sReleaseNotes)
+{
+ OUString sPub = pairPublisher.first;
+ OUString sURL = pairPublisher.second;
+
+ if ( sPub.isEmpty() && sURL.isEmpty() && sReleaseNotes.isEmpty() )
+ // nothing to show
+ return false;
+
+ if ( !sPub.isEmpty() )
+ {
+ m_xPublisherLabel->show();
+ m_xPublisherLink->show();
+ m_xPublisherLink->set_label(sPub);
+ m_xPublisherLink->set_uri(sURL);
+ }
+
+ if ( !sReleaseNotes.isEmpty() )
+ {
+ m_xReleaseNotesLabel->show();
+ m_xReleaseNotesLink->show();
+ m_xReleaseNotesLink->set_uri( sReleaseNotes );
+ }
+ return true;
+}
+
+bool UpdateDialog::showDescription( const OUString& rDescription)
+{
+ if ( rDescription.isEmpty() )
+ // nothing to show
+ return false;
+
+ m_xDescriptions->set_text(rDescription);
+ return true;
+}
+
+void UpdateDialog::getIgnoredUpdates()
+{
+ uno::Reference< lang::XMultiServiceFactory > xConfig(
+ configuration::theDefaultProvider::get(m_context));
+ beans::NamedValue aValue( "nodepath", uno::Any( OUString(IGNORED_UPDATES) ) );
+ uno::Sequence< uno::Any > args{ uno::Any(aValue) };
+
+ uno::Reference< container::XNameAccess > xNameAccess( xConfig->createInstanceWithArguments( "com.sun.star.configuration.ConfigurationAccess", args), uno::UNO_QUERY_THROW );
+ const uno::Sequence< OUString > aElementNames = xNameAccess->getElementNames();
+
+ for ( OUString const & aIdentifier : aElementNames )
+ {
+ OUString aVersion;
+
+ uno::Any aPropValue( uno::Reference< beans::XPropertySet >( xNameAccess->getByName( aIdentifier ), uno::UNO_QUERY_THROW )->getPropertyValue( PROPERTY_VERSION ) );
+ aPropValue >>= aVersion;
+ IgnoredUpdate *pData = new IgnoredUpdate( aIdentifier, aVersion );
+ m_ignoredUpdates.emplace_back( pData );
+ }
+}
+
+
+bool UpdateDialog::isIgnoredUpdate( UpdateDialog::Index * index )
+{
+ bool bIsIgnored = false;
+
+ if (! m_ignoredUpdates.empty() )
+ {
+ OUString aExtensionID;
+ OUString aVersion;
+
+ if ( index->m_eKind == ENABLED_UPDATE )
+ {
+ dp_gui::UpdateData aUpdData = m_enabledUpdates[ index->m_nIndex ];
+ aExtensionID = dp_misc::getIdentifier( aUpdData.aInstalledPackage );
+ aVersion = aUpdData.updateVersion;
+ }
+ else if ( index->m_eKind == DISABLED_UPDATE )
+ {
+ DisabledUpdate &rData = m_disabledUpdates[ index->m_nIndex ];
+ dp_misc::DescriptionInfoset aInfoset( m_context, rData.aUpdateInfo );
+ ::std::optional< OUString > aID( aInfoset.getIdentifier() );
+ if ( aID )
+ aExtensionID = *aID;
+ aVersion = aInfoset.getVersion();
+ }
+
+ for (auto const& ignoredUpdate : m_ignoredUpdates)
+ {
+ if ( ignoredUpdate->sExtensionID == aExtensionID )
+ {
+ if ( ( !ignoredUpdate->sVersion.isEmpty() ) || ( ignoredUpdate->sVersion == aVersion ) )
+ {
+ bIsIgnored = true;
+ index->m_bIgnored = true;
+ }
+ break;
+ }
+ }
+ }
+
+ return bIsIgnored;
+}
+
+
+IMPL_LINK_NOARG(UpdateDialog, selectionHandler, weld::TreeView&, void)
+{
+ OUStringBuffer b;
+ int nSelectedPos = m_xUpdates->get_selected_index();
+ clearDescription();
+
+ const UpdateDialog::Index* p = nullptr;
+ if (nSelectedPos != -1)
+ p = weld::fromId<UpdateDialog::Index const*>(m_xUpdates->get_id(nSelectedPos));
+ if (p != nullptr)
+ {
+ sal_uInt16 pos = p->m_nIndex;
+
+ switch (p->m_eKind)
+ {
+ case ENABLED_UPDATE:
+ {
+ if ( m_enabledUpdates[ pos ].aUpdateSource.is() )
+ showDescription( m_enabledUpdates[ pos ].aUpdateSource );
+ else
+ showDescription( m_enabledUpdates[ pos ].aUpdateInfo );
+
+ if ( p->m_bIgnored )
+ b.append( m_ignoredUpdate );
+
+ break;
+ }
+ case DISABLED_UPDATE:
+ {
+ if ( !m_disabledUpdates.empty() )
+ showDescription( m_disabledUpdates[pos].aUpdateInfo );
+
+ if ( p->m_bIgnored )
+ b.append( m_ignoredUpdate );
+
+ if ( m_disabledUpdates.empty() )
+ break;
+
+ UpdateDialog::DisabledUpdate & data = m_disabledUpdates[ pos ];
+ if (data.unsatisfiedDependencies.hasElements())
+ {
+ // create error string for version mismatch
+ OUString sVersion( "%VERSION" );
+ OUString sProductName( "%PRODUCTNAME" );
+ sal_Int32 nPos = m_noDependencyCurVer.indexOf( sVersion );
+ if ( nPos >= 0 )
+ {
+ m_noDependencyCurVer = m_noDependencyCurVer.replaceAt( nPos, sVersion.getLength(), utl::ConfigManager::getAboutBoxProductVersion() );
+ }
+ nPos = m_noDependencyCurVer.indexOf( sProductName );
+ if ( nPos >= 0 )
+ {
+ m_noDependencyCurVer = m_noDependencyCurVer.replaceAt( nPos, sProductName.getLength(), utl::ConfigManager::getProductName() );
+ }
+ nPos = m_noDependency.indexOf( sProductName );
+ if ( nPos >= 0 )
+ {
+ m_noDependency = m_noDependency.replaceAt( nPos, sProductName.getLength(), utl::ConfigManager::getProductName() );
+ }
+
+ b.append(m_noInstall);
+ b.append(LF);
+ b.append(m_noDependency);
+ for (sal_Int32 i = 0;
+ i < data.unsatisfiedDependencies.getLength(); ++i)
+ {
+ b.append(LF);
+ b.append(" ");
+ // U+2003 EM SPACE would be better than two spaces,
+ // but some fonts do not contain it
+ b.append(
+ confineToParagraph(
+ data.unsatisfiedDependencies[i]));
+ }
+ b.append(LF);
+ b.append(" ");
+ b.append(m_noDependencyCurVer);
+ }
+ break;
+ }
+ case SPECIFIC_ERROR:
+ {
+ UpdateDialog::SpecificError & data = m_specificErrors[ pos ];
+ b.append(m_failure);
+ b.append(LF);
+ b.append( data.message.isEmpty() ? m_unknownError : data.message );
+ break;
+ }
+ default:
+ OSL_ASSERT(false);
+ break;
+ }
+ }
+
+ if ( b.isEmpty() )
+ b.append( m_noDescription );
+
+ showDescription( b.makeStringAndClear() );
+}
+
+IMPL_LINK_NOARG(UpdateDialog, allHandler, weld::Toggleable&, void)
+{
+ if (m_xAll->get_active())
+ {
+ m_xUpdate->set_sensitive(true);
+ m_xUpdates->set_sensitive(true);
+ m_xDescription->set_sensitive(true);
+ m_xDescriptions->set_sensitive(true);
+
+ for (auto const& listboxEntry : m_ListboxEntries)
+ {
+ if ( listboxEntry->m_bIgnored || ( listboxEntry->m_eKind != ENABLED_UPDATE ) )
+ insertItem(listboxEntry.get(), false);
+ }
+ }
+ else
+ {
+ for (sal_uInt16 i = m_xUpdates->n_children(); i != 0 ;)
+ {
+ i -= 1;
+ UpdateDialog::Index const * p = weld::fromId<UpdateDialog::Index const*>(m_xUpdates->get_id(i));
+ if ( p->m_bIgnored || ( p->m_eKind != ENABLED_UPDATE ) )
+ {
+ m_xUpdates->remove(i);
+ }
+ }
+
+ if (m_xUpdates->n_children() == 0)
+ {
+ clearDescription();
+ m_xUpdate->set_sensitive(false);
+ m_xUpdates->set_sensitive(false);
+ if (m_xChecking->get_visible())
+ m_xDescription->set_sensitive(false);
+ else
+ showDescription(m_noInstallable);
+ }
+ }
+}
+
+IMPL_LINK_NOARG(UpdateDialog, okHandler, weld::Button&, void)
+{
+ //If users are going to update a shared extension then we need
+ //to warn them
+ for (auto const& enableUpdate : m_enabledUpdates)
+ {
+ OSL_ASSERT(enableUpdate.aInstalledPackage.is());
+ //If the user has no write access to the shared folder then the update
+ //for a shared extension is disable, that is it cannot be in m_enabledUpdates
+ }
+
+
+ for (sal_uInt16 i = 0, nCount = m_xUpdates->n_children(); i < nCount; ++i)
+ {
+ UpdateDialog::Index const * p =
+ weld::fromId<UpdateDialog::Index const*>(m_xUpdates->get_id(i));
+ if (p->m_eKind == ENABLED_UPDATE && m_xUpdates->get_toggle(i) == TRISTATE_TRUE) {
+ m_updateData.push_back( m_enabledUpdates[ p->m_nIndex ] );
+ }
+ }
+
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK_NOARG(UpdateDialog, closeHandler, weld::Button&, void)
+{
+ m_thread->stop();
+ m_xDialog->response(RET_CANCEL);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_updatedialog.hxx b/desktop/source/deployment/gui/dp_gui_updatedialog.hxx
new file mode 100644
index 000000000..cbf376955
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_updatedialog.hxx
@@ -0,0 +1,168 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <vector>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+#include <tools/link.hxx>
+#include <vcl/weld.hxx>
+
+#include "dp_gui_updatedata.hxx"
+
+/// @HTML
+
+class Image;
+class KeyEvent;
+class MouseEvent;
+class ResId;
+
+namespace com::sun::star {
+ namespace deployment { class XExtensionManager;
+ class XPackage; }
+ namespace uno { class XComponentContext; }
+}
+
+namespace dp_gui {
+/**
+ The modal &ldquo;Check for Updates&rdquo; dialog.
+*/
+class UpdateDialog: public weld::GenericDialogController {
+public:
+ /**
+ Create an instance.
+
+ <p>Exactly one of <code>selectedPackages</code> and
+ <code>packageManagers</code> must be non-null.</p>
+
+ @param context
+ a non-null component context
+
+ @param parent
+ the parent window, may be null
+
+ @param vExtensionList
+ check for updates for the contained extensions. There must only be one extension with
+ a particular identifier. If one extension is installed in several repositories, then the
+ one with the highest version must be used, because it contains the latest known update
+ information.
+ */
+ UpdateDialog(
+ css::uno::Reference< css::uno::XComponentContext > const & context,
+ weld::Window * parent,
+ std::vector< css::uno::Reference< css::deployment::XPackage > > && vExtensionList,
+ std::vector< dp_gui::UpdateData > * updateData);
+
+ virtual ~UpdateDialog() override;
+
+ virtual short run() override;
+
+ void notifyMenubar( bool bPrepareOnly, bool bRecheckOnly );
+ static void createNotifyJob( bool bPrepareOnly,
+ css::uno::Sequence< css::uno::Sequence< OUString > > const &rItemList );
+
+private:
+ UpdateDialog(UpdateDialog const &) = delete;
+ UpdateDialog& operator =(UpdateDialog const &) = delete;
+
+ struct DisabledUpdate;
+ struct SpecificError;
+ struct IgnoredUpdate;
+ struct Index;
+ friend struct Index;
+ class Thread;
+ friend class Thread;
+
+ friend class CheckListBox;
+
+ void insertItem(const UpdateDialog::Index *pIndex, bool bEnableCheckBox);
+ void addAdditional(const UpdateDialog::Index *pIndex, bool bEnableCheckBox);
+ bool isIgnoredUpdate( UpdateDialog::Index *pIndex );
+
+ void addEnabledUpdate( OUString const & name, dp_gui::UpdateData const & data );
+ void addDisabledUpdate( UpdateDialog::DisabledUpdate const & data );
+ void addSpecificError( UpdateDialog::SpecificError const & data );
+
+ void checkingDone();
+
+ void enableOk();
+
+ void getIgnoredUpdates();
+
+ void initDescription();
+ void clearDescription();
+ bool showDescription(css::uno::Reference<
+ css::deployment::XPackage > const & aExtension);
+ bool showDescription(std::pair< OUString, OUString > const & pairPublisher,
+ OUString const & sReleaseNotes);
+ bool showDescription( css::uno::Reference<
+ css::xml::dom::XNode > const & aUpdateInfo);
+ bool showDescription( const OUString& rDescription);
+
+ DECL_LINK(selectionHandler, weld::TreeView&, void);
+ DECL_LINK(allHandler, weld::Toggleable&, void);
+ DECL_LINK(okHandler, weld::Button&, void);
+ DECL_LINK(closeHandler, weld::Button&, void);
+ DECL_LINK(entryToggled, const weld::TreeView::iter_col&, void);
+
+ css::uno::Reference< css::uno::XComponentContext > m_context;
+ OUString m_none;
+ OUString m_noInstallable;
+ OUString m_failure;
+ OUString m_unknownError;
+ OUString m_noDescription;
+ OUString m_noInstall;
+ OUString m_noDependency;
+ OUString m_noDependencyCurVer;
+ OUString m_browserbased;
+ OUString m_version;
+ OUString m_ignoredUpdate;
+ std::vector< dp_gui::UpdateData > m_enabledUpdates;
+ std::vector< UpdateDialog::DisabledUpdate > m_disabledUpdates;
+ std::vector< UpdateDialog::SpecificError > m_specificErrors;
+ std::vector< std::unique_ptr<UpdateDialog::IgnoredUpdate> > m_ignoredUpdates;
+ std::vector< std::unique_ptr<Index> > m_ListboxEntries;
+ std::vector< dp_gui::UpdateData > & m_updateData;
+ rtl::Reference< UpdateDialog::Thread > m_thread;
+ css::uno::Reference< css::deployment::XExtensionManager > m_xExtensionManager;
+
+ std::unique_ptr<weld::Label> m_xChecking;
+ std::unique_ptr<weld::Spinner> m_xThrobber;
+ std::unique_ptr<weld::Label> m_xUpdate;
+ std::unique_ptr<weld::TreeView> m_xUpdates;
+ std::unique_ptr<weld::CheckButton> m_xAll;
+ std::unique_ptr<weld::Label> m_xDescription;
+ std::unique_ptr<weld::Label> m_xPublisherLabel;
+ std::unique_ptr<weld::LinkButton> m_xPublisherLink;
+ std::unique_ptr<weld::Label> m_xReleaseNotesLabel;
+ std::unique_ptr<weld::LinkButton> m_xReleaseNotesLink;
+ std::unique_ptr<weld::TextView> m_xDescriptions;
+ std::unique_ptr<weld::Button> m_xOk;
+ std::unique_ptr<weld::Button> m_xClose;
+ std::unique_ptr<weld::Button> m_xHelp;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_updateinstalldialog.cxx b/desktop/source/deployment/gui/dp_gui_updateinstalldialog.cxx
new file mode 100644
index 000000000..9b1aa423d
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_updateinstalldialog.cxx
@@ -0,0 +1,665 @@
+/* -*- 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 "dp_gui_updatedata.hxx"
+
+#include <sal/config.h>
+#include <osl/file.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/lang/WrappedTargetException.hpp>
+#include <com/sun/star/ucb/NameClash.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/ucb/XProgressHandler.hpp>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/deployment/ExtensionManager.hpp>
+#include <com/sun/star/deployment/LicenseException.hpp>
+#include <com/sun/star/deployment/VersionException.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/task/XInteractionApprove.hpp>
+
+#include <dp_descriptioninfoset.hxx>
+#include <strings.hrc>
+#include "dp_gui_updateinstalldialog.hxx"
+#include <dp_shared.hxx>
+#include <dp_ucb.h>
+#include <dp_misc.h>
+#include "dp_gui_extensioncmdqueue.hxx"
+#include <ucbhelper/content.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ref.hxx>
+#include <salhelper/thread.hxx>
+#include <com/sun/star/uno/Sequence.h>
+#include <comphelper/anytostring.hxx>
+
+#include <string_view>
+#include <vector>
+
+using dp_misc::StrTitle;
+
+namespace dp_gui {
+
+class UpdateInstallDialog::Thread: public salhelper::Thread {
+ friend class UpdateCommandEnv;
+public:
+ Thread(css::uno::Reference< css::uno::XComponentContext > const & ctx,
+ UpdateInstallDialog & dialog, std::vector< dp_gui::UpdateData > & aVecUpdateData);
+
+ void stop();
+
+private:
+ virtual ~Thread() override;
+
+ virtual void execute() override;
+ void downloadExtensions();
+ bool download(OUString const & aUrls, UpdateData & aUpdatData);
+ void installExtensions();
+ void removeTempDownloads();
+
+ UpdateInstallDialog & m_dialog;
+
+ // guarded by Application::GetSolarMutex():
+ css::uno::Reference< css::task::XAbortChannel > m_abort;
+ css::uno::Reference< css::uno::XComponentContext > m_xComponentContext;
+ std::vector< dp_gui::UpdateData > & m_aVecUpdateData;
+ ::rtl::Reference<UpdateCommandEnv> m_updateCmdEnv;
+
+ //A folder which is created in the temp directory in which then the updates are downloaded
+ OUString m_sDownloadFolder;
+
+ bool m_stop;
+
+};
+
+class UpdateCommandEnv
+ : public ::cppu::WeakImplHelper< css::ucb::XCommandEnvironment,
+ css::task::XInteractionHandler,
+ css::ucb::XProgressHandler >
+{
+ friend class UpdateInstallDialog::Thread;
+
+ ::rtl::Reference<UpdateInstallDialog::Thread> m_installThread;
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+
+public:
+ UpdateCommandEnv( css::uno::Reference< css::uno::XComponentContext > xCtx,
+ ::rtl::Reference<UpdateInstallDialog::Thread> thread);
+
+ // XCommandEnvironment
+ virtual css::uno::Reference<css::task::XInteractionHandler > SAL_CALL
+ getInteractionHandler() override;
+ virtual css::uno::Reference<css::ucb::XProgressHandler >
+ SAL_CALL getProgressHandler() override;
+
+ // XInteractionHandler
+ virtual void SAL_CALL handle(
+ css::uno::Reference<css::task::XInteractionRequest > const & xRequest ) override;
+
+ // XProgressHandler
+ virtual void SAL_CALL push( css::uno::Any const & Status ) override;
+ virtual void SAL_CALL update( css::uno::Any const & Status ) override;
+ virtual void SAL_CALL pop() override;
+};
+
+
+UpdateInstallDialog::Thread::Thread(
+ css::uno::Reference< css::uno::XComponentContext> const & xCtx,
+ UpdateInstallDialog & dialog,
+ std::vector< dp_gui::UpdateData > & aVecUpdateData):
+ salhelper::Thread("dp_gui_updateinstalldialog"),
+ m_dialog(dialog),
+ m_xComponentContext(xCtx),
+ m_aVecUpdateData(aVecUpdateData),
+ m_updateCmdEnv(new UpdateCommandEnv(xCtx, this)),
+ m_stop(false)
+{}
+
+void UpdateInstallDialog::Thread::stop() {
+ css::uno::Reference< css::task::XAbortChannel > abort;
+ {
+ SolarMutexGuard g;
+ abort = m_abort;
+ m_stop = true;
+ }
+ if (abort.is()) {
+ abort->sendAbort();
+ }
+}
+
+UpdateInstallDialog::Thread::~Thread() {}
+
+void UpdateInstallDialog::Thread::execute()
+{
+ try {
+ downloadExtensions();
+ installExtensions();
+ }
+ catch (...)
+ {
+ }
+
+ //clean up the temp directories
+ try {
+ removeTempDownloads();
+ } catch( ... ) {
+ }
+
+ {
+ //make sure m_dialog is still alive
+ SolarMutexGuard g;
+ if (! m_stop)
+ m_dialog.updateDone();
+ }
+ //UpdateCommandEnv keeps a reference to Thread and prevents destruction. Therefore remove it.
+ m_updateCmdEnv->m_installThread.clear();
+}
+
+UpdateInstallDialog::UpdateInstallDialog(
+ weld::Window* pParent,
+ std::vector<dp_gui::UpdateData> & aVecUpdateData,
+ css::uno::Reference< css::uno::XComponentContext > const & xCtx)
+ : GenericDialogController(pParent, "desktop/ui/updateinstalldialog.ui",
+ "UpdateInstallDialog")
+ , m_thread(new Thread(xCtx, *this, aVecUpdateData))
+ , m_bError(false)
+ , m_bNoEntry(true)
+ , m_sInstalling(DpResId(RID_DLG_UPDATE_INSTALL_INSTALLING))
+ , m_sFinished(DpResId(RID_DLG_UPDATE_INSTALL_FINISHED))
+ , m_sNoErrors(DpResId(RID_DLG_UPDATE_INSTALL_NO_ERRORS))
+ , m_sErrorDownload(DpResId(RID_DLG_UPDATE_INSTALL_ERROR_DOWNLOAD))
+ , m_sErrorInstallation(DpResId(RID_DLG_UPDATE_INSTALL_ERROR_INSTALLATION))
+ , m_sErrorLicenseDeclined(DpResId(RID_DLG_UPDATE_INSTALL_ERROR_LIC_DECLINED))
+ , m_sNoInstall(DpResId(RID_DLG_UPDATE_INSTALL_EXTENSION_NOINSTALL))
+ , m_sThisErrorOccurred(DpResId(RID_DLG_UPDATE_INSTALL_THIS_ERROR_OCCURRED))
+ , m_xFt_action(m_xBuilder->weld_label("DOWNLOADING"))
+ , m_xStatusbar(m_xBuilder->weld_progress_bar("STATUSBAR"))
+ , m_xFt_extension_name(m_xBuilder->weld_label("EXTENSION_NAME"))
+ , m_xMle_info(m_xBuilder->weld_text_view("INFO"))
+ , m_xHelp(m_xBuilder->weld_button("help"))
+ , m_xOk(m_xBuilder->weld_button("ok"))
+ , m_xCancel(m_xBuilder->weld_button("cancel"))
+{
+ m_xMle_info->set_size_request(m_xMle_info->get_approximate_digit_width() * 52,
+ m_xMle_info->get_height_rows(5));
+
+ m_xExtensionManager = css::deployment::ExtensionManager::get( xCtx );
+
+ m_xCancel->connect_clicked(LINK(this, UpdateInstallDialog, cancelHandler));
+ if ( ! dp_misc::office_is_running())
+ m_xHelp->set_sensitive(false);
+}
+
+UpdateInstallDialog::~UpdateInstallDialog()
+{
+}
+
+short UpdateInstallDialog::run()
+{
+ m_thread->launch();
+ short nRet = GenericDialogController::run();
+ m_thread->stop();
+ return nRet;
+}
+
+// make sure the solar mutex is locked before calling
+void UpdateInstallDialog::updateDone()
+{
+ if (!m_bError)
+ m_xMle_info->set_text(m_xMle_info->get_text() + m_sNoErrors);
+ m_xOk->set_sensitive(true);
+ m_xOk->grab_focus();
+ m_xCancel->set_sensitive(false);
+}
+
+// make sure the solar mutex is locked before calling
+//sets an error message in the text area
+void UpdateInstallDialog::setError(INSTALL_ERROR err, std::u16string_view sExtension,
+ std::u16string_view exceptionMessage)
+{
+ OUString sError;
+ m_bError = true;
+
+ switch (err)
+ {
+ case ERROR_DOWNLOAD:
+ sError = m_sErrorDownload;
+ break;
+ case ERROR_INSTALLATION:
+ sError = m_sErrorInstallation;
+ break;
+ case ERROR_LICENSE_DECLINED:
+ sError = m_sErrorLicenseDeclined;
+ break;
+
+ default:
+ OSL_ASSERT(false);
+ }
+
+ OUString sMsg(m_xMle_info->get_text());
+ sError = sError.replaceFirst("%NAME", sExtension);
+ //We want to have an empty line between the error messages. However,
+ //there shall be no empty line after the last entry.
+ if (m_bNoEntry)
+ m_bNoEntry = false;
+ else
+ sMsg += "\n";
+ sMsg += sError;
+ //Insert more information about the error
+ if (!exceptionMessage.empty())
+ sMsg += m_sThisErrorOccurred + exceptionMessage + "\n";
+
+ sMsg += m_sNoInstall + "\n";
+
+ m_xMle_info->set_text(sMsg);
+}
+
+void UpdateInstallDialog::setError(std::u16string_view exceptionMessage)
+{
+ m_bError = true;
+ m_xMle_info->set_text(m_xMle_info->get_text() + exceptionMessage + "\n");
+}
+
+IMPL_LINK_NOARG(UpdateInstallDialog, cancelHandler, weld::Button&, void)
+{
+ m_xDialog->response(RET_CANCEL);
+}
+
+void UpdateInstallDialog::Thread::downloadExtensions()
+{
+ try
+ {
+ //create the download directory in the temp folder
+ OUString sTempDir;
+ if (::osl::FileBase::getTempDirURL(sTempDir) != ::osl::FileBase::E_None)
+ throw css::uno::Exception("Could not get URL for the temp directory. No extensions will be installed.", nullptr);
+
+ //create a unique name for the directory
+ OUString tempEntry, destFolder;
+ if (::osl::File::createTempFile(&sTempDir, nullptr, &tempEntry ) != ::osl::File::E_None)
+ throw css::uno::Exception("Could not create a temporary file in " + sTempDir +
+ ". No extensions will be installed", nullptr );
+
+ tempEntry = tempEntry.copy( tempEntry.lastIndexOf( '/' ) + 1 );
+
+ destFolder = dp_misc::makeURL( sTempDir, tempEntry ) + "_";
+ m_sDownloadFolder = destFolder;
+ try
+ {
+ dp_misc::create_folder(nullptr, destFolder, m_updateCmdEnv );
+ } catch (const css::uno::Exception & e)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetException( e.Message + " No extensions will be installed",
+ nullptr, anyEx );
+ }
+
+
+ sal_uInt16 count = 0;
+ for (auto & updateData : m_aVecUpdateData)
+ {
+ if (!updateData.aUpdateInfo.is() || updateData.aUpdateSource.is())
+ continue;
+ //We assume that m_aVecUpdateData contains only information about extensions which
+ //can be downloaded directly.
+ OSL_ASSERT(updateData.sWebsiteURL.isEmpty());
+
+ //update the name of the extension which is to be downloaded
+ {
+ SolarMutexGuard g;
+ if (m_stop) {
+ return;
+ }
+ m_dialog.m_xFt_extension_name->set_label(updateData.aInstalledPackage->getDisplayName());
+ sal_uInt16 prog = (sal::static_int_cast<sal_uInt16>(100) * ++count) /
+ sal::static_int_cast<sal_uInt16>(m_aVecUpdateData.size());
+ m_dialog.m_xStatusbar->set_percentage(prog);
+ }
+ dp_misc::DescriptionInfoset info(m_xComponentContext, updateData.aUpdateInfo);
+ //remember occurring exceptions in case we need to print out error information
+ std::vector< std::pair<OUString, css::uno::Exception> > vecExceptions;
+ css::uno::Sequence<OUString> seqDownloadURLs = info.getUpdateDownloadUrls();
+ OSL_ENSURE(seqDownloadURLs.hasElements(), "No download URL provided!");
+ for (sal_Int32 j = 0; j < seqDownloadURLs.getLength(); j++)
+ {
+ try
+ {
+ OSL_ENSURE(!seqDownloadURLs[j].isEmpty(), "Download URL is empty!");
+ bool bCancelled = download(seqDownloadURLs[j], updateData);
+ if (bCancelled || !updateData.sLocalURL.isEmpty())
+ break;
+ }
+ catch ( css::uno::Exception & e )
+ {
+ vecExceptions.emplace_back(seqDownloadURLs[j], e);
+ //There can be several different errors, for example, the URL is wrong, webserver cannot be reached,
+ //name cannot be resolved. The UCB helper API does not specify different special exceptions for these
+ //cases. Therefore ignore and continue.
+ continue;
+ }
+ }
+ //update the progress and display download error
+ {
+ SolarMutexGuard g;
+ if (m_stop) {
+ return;
+ }
+ if (updateData.sLocalURL.isEmpty())
+ {
+ //Construct a string of all messages contained in the exceptions plus the respective download URLs
+ OUStringBuffer buf(256);
+ size_t nPos = 0;
+ for (auto const& elem : vecExceptions)
+ {
+ if (nPos)
+ buf.append("\n");
+ buf.append("Could not download ");
+ buf.append(elem.first);
+ buf.append(". ");
+ buf.append(elem.second.Message);
+ ++nPos;
+ }
+ m_dialog.setError(UpdateInstallDialog::ERROR_DOWNLOAD, updateData.aInstalledPackage->getDisplayName(),
+ buf);
+ }
+ }
+
+ }
+ }
+ catch (const css::uno::Exception & e)
+ {
+ SolarMutexGuard g;
+ if (m_stop) {
+ return;
+ }
+ m_dialog.setError(e.Message);
+ }
+}
+
+void UpdateInstallDialog::Thread::installExtensions()
+{
+ //Update the fix text in the dialog to "Installing extensions..."
+ {
+ SolarMutexGuard g;
+ if (m_stop) {
+ return;
+ }
+ m_dialog.m_xFt_action->set_label(m_dialog.m_sInstalling);
+ m_dialog.m_xStatusbar->set_percentage(0);
+ }
+
+ sal_uInt16 count = 0;
+ for (auto const& updateData : m_aVecUpdateData)
+ {
+ //update the name of the extension which is to be installed
+ {
+ SolarMutexGuard g;
+ if (m_stop) {
+ return;
+ }
+ //we only show progress after an extension has been installed.
+ if (count > 0) {
+ m_dialog.m_xStatusbar->set_percentage(
+ (sal::static_int_cast<sal_uInt16>(100) * count) /
+ sal::static_int_cast<sal_uInt16>(m_aVecUpdateData.size()));
+ }
+ m_dialog.m_xFt_extension_name->set_label(updateData.aInstalledPackage->getDisplayName());
+ }
+ bool bError = false;
+ bool bLicenseDeclined = false;
+ css::uno::Reference<css::deployment::XPackage> xExtension;
+ css::uno::Exception exc;
+ try
+ {
+ css::uno::Reference< css::task::XAbortChannel > xAbortChannel(
+ updateData.aInstalledPackage->createAbortChannel() );
+ {
+ SolarMutexGuard g;
+ if (m_stop) {
+ return;
+ }
+ m_abort = xAbortChannel;
+ }
+ if (!updateData.aUpdateSource.is() && !updateData.sLocalURL.isEmpty())
+ {
+ css::beans::NamedValue prop("EXTENSION_UPDATE", css::uno::Any(OUString("1")));
+ if (!updateData.bIsShared)
+ xExtension = m_dialog.getExtensionManager()->addExtension(
+ updateData.sLocalURL, css::uno::Sequence<css::beans::NamedValue>(&prop, 1),
+ "user", xAbortChannel, m_updateCmdEnv);
+ else
+ xExtension = m_dialog.getExtensionManager()->addExtension(
+ updateData.sLocalURL, css::uno::Sequence<css::beans::NamedValue>(&prop, 1),
+ "shared", xAbortChannel, m_updateCmdEnv);
+ }
+ else if (updateData.aUpdateSource.is())
+ {
+ OSL_ASSERT(updateData.aUpdateSource.is());
+ //I am not sure if we should obtain the install properties and pass them into
+ //add extension. Currently it contains only "SUPPRESS_LICENSE". So it could happen
+ //that a license is displayed when updating from the shared repository, although the
+ //shared extension was installed using "SUPPRESS_LICENSE".
+ css::beans::NamedValue prop("EXTENSION_UPDATE", css::uno::Any(OUString("1")));
+ if (!updateData.bIsShared)
+ xExtension = m_dialog.getExtensionManager()->addExtension(
+ updateData.aUpdateSource->getURL(), css::uno::Sequence<css::beans::NamedValue>(&prop, 1),
+ "user", xAbortChannel, m_updateCmdEnv);
+ else
+ xExtension = m_dialog.getExtensionManager()->addExtension(
+ updateData.aUpdateSource->getURL(), css::uno::Sequence<css::beans::NamedValue>(&prop, 1),
+ "shared", xAbortChannel, m_updateCmdEnv);
+ }
+ }
+ catch (css::deployment::DeploymentException & de)
+ {
+ if (de.Cause.has<css::deployment::LicenseException>())
+ {
+ bLicenseDeclined = true;
+ }
+ else
+ {
+ exc = de.Cause.get<css::uno::Exception>();
+ bError = true;
+ }
+ }
+ catch (css::uno::Exception& e)
+ {
+ exc = e;
+ bError = true;
+ }
+
+ if (bLicenseDeclined)
+ {
+ SolarMutexGuard g;
+ if (m_stop) {
+ return;
+ }
+ m_dialog.setError(UpdateInstallDialog::ERROR_LICENSE_DECLINED,
+ updateData.aInstalledPackage->getDisplayName(), std::u16string_view());
+ }
+ else if (!xExtension.is() || bError)
+ {
+ SolarMutexGuard g;
+ if (m_stop) {
+ return;
+ }
+ m_dialog.setError(UpdateInstallDialog::ERROR_INSTALLATION,
+ updateData.aInstalledPackage->getDisplayName(), exc.Message);
+ }
+ ++count;
+ }
+ {
+ SolarMutexGuard g;
+ if (m_stop) {
+ return;
+ }
+ m_dialog.m_xStatusbar->set_percentage(100);
+ m_dialog.m_xFt_extension_name->set_label(OUString());
+ m_dialog.m_xFt_action->set_label(m_dialog.m_sFinished);
+ }
+}
+
+void UpdateInstallDialog::Thread::removeTempDownloads()
+{
+ if (!m_sDownloadFolder.isEmpty())
+ {
+ dp_misc::erase_path(m_sDownloadFolder,
+ css::uno::Reference<css::ucb::XCommandEnvironment>(),false /* no throw: ignore errors */ );
+ //remove also the temp file which we have used to create the unique name
+ OUString tempFile = m_sDownloadFolder.copy(0, m_sDownloadFolder.getLength() - 1);
+ dp_misc::erase_path(tempFile, css::uno::Reference<css::ucb::XCommandEnvironment>(),false);
+ m_sDownloadFolder.clear();
+ }
+}
+
+bool UpdateInstallDialog::Thread::download(OUString const & sDownloadURL, UpdateData & aUpdateData)
+{
+ {
+ SolarMutexGuard g;
+ if (m_stop) {
+ return m_stop;
+ }
+ }
+
+ OSL_ASSERT(m_sDownloadFolder.getLength());
+ OUString destFolder, tempEntry;
+ if (::osl::File::createTempFile(
+ &m_sDownloadFolder,
+ nullptr, &tempEntry ) != ::osl::File::E_None)
+ {
+ //ToDo feedback in window that download of this component failed
+ throw css::uno::Exception("Could not create temporary file in folder " + destFolder + ".", nullptr);
+ }
+ tempEntry = tempEntry.copy( tempEntry.lastIndexOf( '/' ) + 1 );
+
+ destFolder = dp_misc::makeURL( m_sDownloadFolder, tempEntry ) + "_";
+
+ ::ucbhelper::Content destFolderContent;
+ dp_misc::create_folder( &destFolderContent, destFolder, m_updateCmdEnv );
+
+ ::ucbhelper::Content sourceContent;
+ (void)dp_misc::create_ucb_content(&sourceContent, sDownloadURL, m_updateCmdEnv);
+
+ const OUString sTitle( StrTitle::getTitle( sourceContent ) );
+
+ destFolderContent.transferContent(
+ sourceContent, ::ucbhelper::InsertOperation::Copy,
+ sTitle, css::ucb::NameClash::OVERWRITE );
+
+ {
+ //the user may have cancelled the dialog because downloading took too long
+ SolarMutexGuard g;
+ if (m_stop) {
+ return m_stop;
+ }
+ //all errors should be handled by the command environment.
+ aUpdateData.sLocalURL = destFolder + "/" + sTitle;
+ }
+
+ return m_stop;
+}
+
+UpdateCommandEnv::UpdateCommandEnv( css::uno::Reference< css::uno::XComponentContext > xCtx,
+ ::rtl::Reference<UpdateInstallDialog::Thread> thread)
+ : m_installThread(std::move(thread)),
+ m_xContext(std::move(xCtx))
+{
+}
+
+// XCommandEnvironment
+css::uno::Reference<css::task::XInteractionHandler> UpdateCommandEnv::getInteractionHandler()
+{
+ return this;
+}
+
+css::uno::Reference<css::ucb::XProgressHandler> UpdateCommandEnv::getProgressHandler()
+{
+ return this;
+}
+
+// XInteractionHandler
+void UpdateCommandEnv::handle(
+ css::uno::Reference< css::task::XInteractionRequest> const & xRequest )
+{
+ css::uno::Any request( xRequest->getRequest() );
+ OSL_ASSERT( request.getValueTypeClass() == css::uno::TypeClass_EXCEPTION );
+ dp_misc::TRACE("[dp_gui_cmdenv.cxx] incoming request:\n"
+ + ::comphelper::anyToString(request) + "\n\n");
+
+ css::deployment::VersionException verExc;
+ bool approve = false;
+
+ if (request >>= verExc)
+ { //We must catch the version exception during the update,
+ //because otherwise the user would be confronted with the dialogs, asking
+ //them if they want to replace an already installed version of the same extension.
+ //During an update we assume that we always want to replace the old version with the
+ //new version.
+ approve = true;
+ }
+
+ if (!approve)
+ {
+ //forward to interaction handler for main dialog.
+ handleInteractionRequest( m_xContext, xRequest );
+ }
+ else
+ {
+ // select:
+ css::uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation > > conts(
+ xRequest->getContinuations() );
+ css::uno::Reference< css::task::XInteractionContinuation > const * pConts =
+ conts.getConstArray();
+ sal_Int32 len = conts.getLength();
+ for ( sal_Int32 pos = 0; pos < len; ++pos )
+ {
+ if (approve) {
+ css::uno::Reference< css::task::XInteractionApprove > xInteractionApprove(
+ pConts[ pos ], css::uno::UNO_QUERY );
+ if (xInteractionApprove.is()) {
+ xInteractionApprove->select();
+ // don't query again for ongoing continuations:
+ approve = false;
+ }
+ }
+ }
+ }
+}
+
+// XProgressHandler
+void UpdateCommandEnv::push( css::uno::Any const & /*Status*/ )
+{
+}
+
+void UpdateCommandEnv::update( css::uno::Any const & /*Status */)
+{
+}
+
+void UpdateCommandEnv::pop()
+{
+}
+
+
+} //end namespace dp_gui
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_updateinstalldialog.hxx b/desktop/source/deployment/gui/dp_gui_updateinstalldialog.hxx
new file mode 100644
index 000000000..39b37ed4b
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_updateinstalldialog.hxx
@@ -0,0 +1,112 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+#include <vcl/weld.hxx>
+#include <rtl/ref.hxx>
+
+#include <string_view>
+#include <vector>
+
+/// @HTML
+namespace com::sun::star::deployment {
+ class XExtensionManager;
+}
+namespace com::sun::star::uno {
+ class XComponentContext;
+}
+
+namespace dp_gui {
+
+ struct UpdateData;
+ class UpdateCommandEnv;
+
+
+/**
+ The modal &ldquo;Download and Installation&rdquo; dialog.
+*/
+class UpdateInstallDialog : public weld::GenericDialogController
+{
+public:
+ /**
+ Create an instance.
+
+ @param parent
+ the parent window, may be null
+ */
+ UpdateInstallDialog(weld::Window* parent, std::vector<UpdateData> & aVecUpdateData,
+ css::uno::Reference< css::uno::XComponentContext > const & xCtx);
+
+ virtual ~UpdateInstallDialog() override;
+
+ virtual short run() override;
+
+private:
+ UpdateInstallDialog(UpdateInstallDialog const &) = delete;
+ UpdateInstallDialog& operator =(UpdateInstallDialog const &) = delete;
+
+ class Thread;
+ friend class Thread;
+ friend class UpdateCommandEnv;
+
+ DECL_LINK(cancelHandler, weld::Button&, void);
+
+ //signals in the dialog that we have finished.
+ void updateDone();
+ //Writes a particular error into the info listbox.
+ enum INSTALL_ERROR
+ {
+ ERROR_DOWNLOAD,
+ ERROR_INSTALLATION,
+ ERROR_LICENSE_DECLINED
+ };
+ void setError(INSTALL_ERROR err, std::u16string_view sExtension, std::u16string_view exceptionMessage);
+ void setError(std::u16string_view exceptionMessage);
+ const css::uno::Reference< css::deployment::XExtensionManager >& getExtensionManager() const
+ { return m_xExtensionManager; }
+
+ rtl::Reference< Thread > m_thread;
+ css::uno::Reference< css::deployment::XExtensionManager > m_xExtensionManager;
+ //Signals that an error occurred during download and installation
+ bool m_bError;
+ bool m_bNoEntry;
+
+ OUString m_sInstalling;
+ OUString m_sFinished;
+ OUString m_sNoErrors;
+ OUString m_sErrorDownload;
+ OUString m_sErrorInstallation;
+ OUString m_sErrorLicenseDeclined;
+ OUString m_sNoInstall;
+ OUString m_sThisErrorOccurred;
+
+ std::unique_ptr<weld::Label> m_xFt_action;
+ std::unique_ptr<weld::ProgressBar> m_xStatusbar;
+ std::unique_ptr<weld::Label> m_xFt_extension_name;
+ std::unique_ptr<weld::TextView> m_xMle_info;
+ std::unique_ptr<weld::Button> m_xHelp;
+ std::unique_ptr<weld::Button> m_xOk;
+ std::unique_ptr<weld::Button> m_xCancel;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/license_dialog.cxx b/desktop/source/deployment/gui/license_dialog.cxx
new file mode 100644
index 000000000..23f184a33
--- /dev/null
+++ b/desktop/source/deployment/gui/license_dialog.cxx
@@ -0,0 +1,244 @@
+/* -*- 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 <comphelper/unwrapargs.hxx>
+#include <vcl/event.hxx>
+#include <vcl/idle.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/threadex.hxx>
+#include <vcl/weld.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include "license_dialog.hxx"
+
+#include <functional>
+#include <string_view>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace dp_gui {
+
+namespace {
+
+struct LicenseDialogImpl : public weld::GenericDialogController
+{
+ bool m_bLicenseRead;
+ Idle m_aResized;
+ AutoTimer m_aRepeat;
+
+ std::unique_ptr<weld::Label> m_xFtHead;
+ std::unique_ptr<weld::Widget> m_xArrow1;
+ std::unique_ptr<weld::Widget> m_xArrow2;
+ std::unique_ptr<weld::TextView> m_xLicense;
+ std::unique_ptr<weld::Button> m_xDown;
+ std::unique_ptr<weld::Button> m_xAcceptButton;
+ std::unique_ptr<weld::Button> m_xDeclineButton;
+
+ void PageDown();
+ DECL_LINK(ScrollTimerHdl, Timer*, void);
+ DECL_LINK(ScrolledHdl, weld::TextView&, void);
+ DECL_LINK(ResizedHdl, Timer*, void);
+ DECL_LINK(CancelHdl, weld::Button&, void);
+ DECL_LINK(AcceptHdl, weld::Button&, void);
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+ DECL_STATIC_LINK(LicenseDialogImpl, KeyReleaseHdl, const KeyEvent&, bool);
+ DECL_LINK(MousePressHdl, const MouseEvent&, bool);
+ DECL_LINK(MouseReleaseHdl, const MouseEvent&, bool);
+ DECL_LINK(SizeAllocHdl, const Size&, void);
+
+ LicenseDialogImpl(weld::Window * pParent,
+ std::u16string_view sExtensionName,
+ const OUString & sLicenseText);
+
+ bool IsEndReached() const;
+};
+
+}
+
+LicenseDialogImpl::LicenseDialogImpl(
+ weld::Window * pParent,
+ std::u16string_view sExtensionName,
+ const OUString & sLicenseText)
+ : GenericDialogController(pParent, "desktop/ui/licensedialog.ui", "LicenseDialog")
+ , m_bLicenseRead(false)
+ , m_aResized("desktop LicenseDialogImpl m_aResized")
+ , m_aRepeat("LicenseDialogImpl m_aRepeat")
+ , m_xFtHead(m_xBuilder->weld_label("head"))
+ , m_xArrow1(m_xBuilder->weld_widget("arrow1"))
+ , m_xArrow2(m_xBuilder->weld_widget("arrow2"))
+ , m_xLicense(m_xBuilder->weld_text_view("textview"))
+ , m_xDown(m_xBuilder->weld_button("down"))
+ , m_xAcceptButton(m_xBuilder->weld_button("ok"))
+ , m_xDeclineButton(m_xBuilder->weld_button("cancel"))
+{
+ m_xArrow1->show();
+ m_xArrow2->hide();
+
+ m_xLicense->connect_size_allocate(LINK(this, LicenseDialogImpl, SizeAllocHdl));
+ m_xLicense->set_size_request(m_xLicense->get_approximate_digit_width() * 72,
+ m_xLicense->get_height_rows(21));
+
+ m_xLicense->set_text(sLicenseText);
+ m_xFtHead->set_label(m_xFtHead->get_label() + "\n" + sExtensionName);
+
+ m_xAcceptButton->connect_clicked( LINK(this, LicenseDialogImpl, AcceptHdl) );
+ m_xDeclineButton->connect_clicked( LINK(this, LicenseDialogImpl, CancelHdl) );
+
+ m_xLicense->connect_vadjustment_changed(LINK(this, LicenseDialogImpl, ScrolledHdl));
+ m_xDown->connect_mouse_press(LINK(this, LicenseDialogImpl, MousePressHdl));
+ m_xDown->connect_mouse_release(LINK(this, LicenseDialogImpl, MouseReleaseHdl));
+ m_xDown->connect_key_press(LINK(this, LicenseDialogImpl, KeyInputHdl));
+ m_xDown->connect_key_release(LINK(this, LicenseDialogImpl, KeyReleaseHdl));
+
+ m_aRepeat.SetTimeout(Application::GetSettings().GetMouseSettings().GetButtonRepeat());
+ m_aRepeat.SetInvokeHandler(LINK(this, LicenseDialogImpl, ScrollTimerHdl));
+
+ m_aResized.SetPriority(TaskPriority::LOWEST);
+ m_aResized.SetInvokeHandler(LINK(this, LicenseDialogImpl, ResizedHdl));
+}
+
+IMPL_LINK_NOARG(LicenseDialogImpl, SizeAllocHdl, const Size&, void)
+{
+ m_aResized.Start();
+}
+
+IMPL_LINK_NOARG(LicenseDialogImpl, AcceptHdl, weld::Button&, void)
+{
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK_NOARG(LicenseDialogImpl, CancelHdl, weld::Button&, void)
+{
+ m_xDialog->response(RET_CANCEL);
+}
+
+bool LicenseDialogImpl::IsEndReached() const
+{
+ return m_xLicense->vadjustment_get_value() + m_xLicense->vadjustment_get_page_size() >= m_xLicense->vadjustment_get_upper();
+}
+
+IMPL_LINK_NOARG(LicenseDialogImpl, ScrolledHdl, weld::TextView&, void)
+{
+ if (IsEndReached())
+ {
+ m_xDown->set_sensitive(false);
+ m_aRepeat.Stop();
+
+ if (!m_bLicenseRead)
+ {
+ m_xAcceptButton->set_sensitive(true);
+ m_xAcceptButton->grab_focus();
+ m_xArrow1->hide();
+ m_xArrow2->show();
+ m_bLicenseRead = true;
+ }
+ }
+ else
+ m_xDown->set_sensitive(true);
+}
+
+void LicenseDialogImpl::PageDown()
+{
+ m_xLicense->vadjustment_set_value(m_xLicense->vadjustment_get_value() +
+ m_xLicense->vadjustment_get_page_size());
+ ScrolledHdl(*m_xLicense);
+}
+
+IMPL_LINK(LicenseDialogImpl, KeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
+ if (aKeyCode.GetCode() == KEY_RETURN || aKeyCode.GetCode() == KEY_SPACE)
+ PageDown();
+ return false;
+}
+
+IMPL_LINK_NOARG(LicenseDialogImpl, ResizedHdl, Timer*, void)
+{
+ ScrolledHdl(*m_xLicense);
+}
+
+IMPL_LINK_NOARG(LicenseDialogImpl, ScrollTimerHdl, Timer*, void)
+{
+ PageDown();
+}
+
+IMPL_STATIC_LINK_NOARG(LicenseDialogImpl, KeyReleaseHdl, const KeyEvent&, bool)
+{
+ return false;
+}
+
+IMPL_LINK_NOARG(LicenseDialogImpl, MousePressHdl, const MouseEvent&, bool)
+{
+ PageDown();
+ m_aRepeat.Start();
+ return false;
+}
+
+IMPL_LINK_NOARG(LicenseDialogImpl, MouseReleaseHdl, const MouseEvent&, bool)
+{
+ m_aRepeat.Stop();
+ return false;
+}
+
+LicenseDialog::LicenseDialog( Sequence<Any> const& args,
+ Reference<XComponentContext> const& )
+{
+ comphelper::unwrapArgs( args, m_parent, m_sExtensionName, m_sLicenseText );
+}
+
+// XServiceInfo
+OUString LicenseDialog::getImplementationName()
+{
+ return "com.sun.star.comp.deployment.ui.LicenseDialog";
+}
+
+sal_Bool LicenseDialog::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > LicenseDialog::getSupportedServiceNames()
+{
+ return { "com.sun.star.deployment.ui.LicenseDialog" };
+}
+
+
+// XExecutableDialog
+
+void LicenseDialog::setTitle( OUString const & )
+{
+}
+
+sal_Int16 LicenseDialog::execute()
+{
+ return vcl::solarthread::syncExecute(
+ std::bind(&LicenseDialog::solar_execute, this));
+}
+
+sal_Int16 LicenseDialog::solar_execute()
+{
+ LicenseDialogImpl dlg(Application::GetFrameWeld(m_parent), m_sExtensionName, m_sLicenseText);
+ return dlg.run();
+}
+
+} // namespace dp_gui
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/license_dialog.hxx b/desktop/source/deployment/gui/license_dialog.hxx
new file mode 100644
index 000000000..3c628a880
--- /dev/null
+++ b/desktop/source/deployment/gui/license_dialog.hxx
@@ -0,0 +1,53 @@
+/* -*- 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 .
+ */
+#pragma once
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+namespace dp_gui
+{
+class LicenseDialog
+ : public ::cppu::WeakImplHelper<css::ui::dialogs::XExecutableDialog, css::lang::XServiceInfo>
+{
+ css::uno::Reference<css::awt::XWindow> /* const */ m_parent;
+ OUString m_sExtensionName;
+ OUString /* const */ m_sLicenseText;
+
+ sal_Int16 solar_execute();
+
+public:
+ LicenseDialog(css::uno::Sequence<css::uno::Any> const& args,
+ css::uno::Reference<css::uno::XComponentContext> const& xComponentContext);
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XExecutableDialog
+ virtual void SAL_CALL setTitle(OUString const& title) override;
+ virtual sal_Int16 SAL_CALL execute() override;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */