#region License Information /* HeuristicLab * Copyright (C) 2002-2019 Heuristic and Evolutionary Algorithms Laboratory (HEAL) * * This file is part of HeuristicLab. * * HeuristicLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * HeuristicLab is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HeuristicLab. If not, see . */ #endregion using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Windows.Forms; using HeuristicLab.PluginInfrastructure.Manager; namespace HeuristicLab.PluginInfrastructure.Advanced { internal partial class AvailablePluginsView : InstallationManagerControl { private class RefreshBackgroundWorkerResult { public IEnumerable RemotePlugins { get; set; } public IEnumerable RemoteProducts { get; set; } } private class UpdateOrInstallPluginsBackgroundWorkerArgument { public IEnumerable PluginsToUpdate { get; set; } public IEnumerable PluginsToInstall { get; set; } } private const string PluginDiscoveryMessage = "Looking for new plugins..."; private BackgroundWorker refreshServerPluginsBackgroundWorker; private BackgroundWorker updateOrInstallPluginsBackgroundWorker; private IEnumerable products; private IEnumerable plugins; private IEnumerable CheckedPlugins { get { return (from item in pluginsListView.Items.OfType() where item.Checked let plugin = item.Tag as IPluginDescription where plugin != null select plugin).ToList(); } } private InstallationManager installationManager; public InstallationManager InstallationManager { get { return installationManager; } set { installationManager = value; } } private PluginManager pluginManager; public PluginManager PluginManager { get { return pluginManager; } set { pluginManager = value; } } public AvailablePluginsView() { InitializeComponent(); productImageList.Images.Add(HeuristicLab.PluginInfrastructure.Resources.Setup_Install); productLargeImageList.Images.Add(HeuristicLab.PluginInfrastructure.Resources.Setup_Install); pluginsImageList.Images.Add(HeuristicLab.PluginInfrastructure.Resources.Plugin); refreshServerPluginsBackgroundWorker = new BackgroundWorker(); refreshServerPluginsBackgroundWorker.DoWork += new DoWorkEventHandler(refreshServerPluginsBackgroundWorker_DoWork); refreshServerPluginsBackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(refreshServerPluginsBackgroundWorker_RunWorkerCompleted); updateOrInstallPluginsBackgroundWorker = new BackgroundWorker(); updateOrInstallPluginsBackgroundWorker.DoWork += new DoWorkEventHandler(updateOrInstallPluginsBackgroundWorker_DoWork); updateOrInstallPluginsBackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(updateOrInstallPluginsBackgroundWorker_RunWorkerCompleted); } #region event handlers for refresh server plugins background worker void refreshServerPluginsBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (e.Error != null) { StatusView.ShowError("Connection Error", "There was an error while connecting to the server." + Environment.NewLine + "Please check your connection settings and user credentials."); } else { RefreshBackgroundWorkerResult refreshResult = (RefreshBackgroundWorkerResult)e.Result; products = refreshResult.RemoteProducts; plugins = refreshResult.RemotePlugins; UpdateControl(); } StatusView.UnlockUI(); StatusView.RemoveMessage(PluginDiscoveryMessage); StatusView.HideProgressIndicator(); } void refreshServerPluginsBackgroundWorker_DoWork(object sender, DoWorkEventArgs e) { RefreshBackgroundWorkerResult result = new RefreshBackgroundWorkerResult(); result.RemotePlugins = installationManager.GetRemotePluginList(); result.RemoteProducts = installationManager.GetRemoteProductList(); e.Result = result; } #endregion #region event handlers for plugin update background worker void updateOrInstallPluginsBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (e.Error != null) { StatusView.ShowError("Connection Error", "There was an error while connecting to the server." + Environment.NewLine + "Please check your connection settings and user credentials."); } else { UpdateControl(); } StatusView.UnlockUI(); StatusView.HideProgressIndicator(); } void updateOrInstallPluginsBackgroundWorker_DoWork(object sender, DoWorkEventArgs e) { UpdateOrInstallPluginsBackgroundWorkerArgument info = (UpdateOrInstallPluginsBackgroundWorkerArgument)e.Argument; bool cancelled = false; if (info.PluginsToInstall.Count() > 0) installationManager.Install(info.PluginsToInstall, out cancelled); if (info.PluginsToUpdate.Count() > 0) installationManager.Update(info.PluginsToUpdate, out cancelled); if (!cancelled && (info.PluginsToInstall.Count() > 0 || info.PluginsToUpdate.Count() > 0)) pluginManager.DiscoverAndCheckPlugins(); } #endregion #region button events private void refreshRemoteButton_Click(object sender, EventArgs e) { StatusView.LockUI(); StatusView.ShowProgressIndicator(); StatusView.ShowMessage(PluginDiscoveryMessage); refreshServerPluginsBackgroundWorker.RunWorkerAsync(); } private void installPluginsButton_Click(object sender, EventArgs e) { StatusView.LockUI(); StatusView.ShowProgressIndicator(); var updateOrInstallInfo = new UpdateOrInstallPluginsBackgroundWorkerArgument(); // if there is a local plugin with same name and same major and minor version then it's an update var pluginsToUpdate = from remotePlugin in CheckedPlugins let matchingLocalPlugins = from localPlugin in pluginManager.Plugins where localPlugin.Name == remotePlugin.Name where localPlugin.Version.Major == remotePlugin.Version.Major where localPlugin.Version.Minor == remotePlugin.Version.Minor where IsNewerThan(remotePlugin, localPlugin) select localPlugin where matchingLocalPlugins.Count() > 0 select remotePlugin; // otherwise install a new plugin var pluginsToInstall = CheckedPlugins.Except(pluginsToUpdate); updateOrInstallInfo.PluginsToInstall = pluginsToInstall; updateOrInstallInfo.PluginsToUpdate = pluginsToUpdate; updateOrInstallPluginsBackgroundWorker.RunWorkerAsync(updateOrInstallInfo); } private void installProductsButton_Click(object sender, EventArgs e) { StatusView.LockUI(); StatusView.ShowProgressIndicator(); var updateOrInstallInfo = new UpdateOrInstallPluginsBackgroundWorkerArgument(); var selectedProduct = (DeploymentService.ProductDescription)productsListView.SelectedItems[0].Tag; // if there is a local plugin with same name and same major and minor version then it's an update var pluginsToUpdate = from plugin in selectedProduct.Plugins let matchingLocalPlugins = from localPlugin in pluginManager.Plugins where localPlugin.Name == plugin.Name where localPlugin.Version.Major == plugin.Version.Major where localPlugin.Version.Minor == plugin.Version.Minor where IsNewerThan(plugin, localPlugin) select localPlugin where matchingLocalPlugins.Count() > 0 select plugin; // otherwise install a new plugin var pluginsToInstall = selectedProduct.Plugins.Except(pluginsToUpdate); updateOrInstallInfo.PluginsToInstall = pluginsToInstall .Cast() .ToList(); updateOrInstallInfo.PluginsToUpdate = pluginsToUpdate .Cast() .ToList(); updateOrInstallPluginsBackgroundWorker.RunWorkerAsync(updateOrInstallInfo); } private void showLargeIconsButton_CheckedChanged(object sender, EventArgs e) { productsListView.View = View.LargeIcon; } private void showDetailsButton_CheckedChanged(object sender, EventArgs e) { productsListView.View = View.Details; } #endregion private void UpdateControl() { // clear products view List productItemsToDelete = new List(productsListView.Items.OfType()); productItemsToDelete.ForEach(item => productsListView.Items.Remove(item)); // populate products list view foreach (var product in products) { var item = CreateListViewItem(product); productsListView.Items.Add(item); } var allPluginsListViewItem = new ListViewItem(); allPluginsListViewItem.Text = "All Plugins"; allPluginsListViewItem.ImageIndex = 0; productsListView.Items.Add(allPluginsListViewItem); Util.ResizeColumns(productsListView.Columns.OfType()); } private void UpdatePluginsList() { pluginsListView.Items.Clear(); // populate plugins list if (productsListView.SelectedItems.Count > 0) { pluginsListView.SuppressItemCheckedEvents = true; var selectedItem = productsListView.SelectedItems[0]; if (selectedItem.Text == "All Plugins") { foreach (var plugin in plugins) { var item = CreateListViewItem(plugin); pluginsListView.Items.Add(item); } } else { var selectedProduct = (DeploymentService.ProductDescription)productsListView.SelectedItems[0].Tag; foreach (var plugin in selectedProduct.Plugins) { var item = CreateListViewItem(plugin); pluginsListView.Items.Add(item); } } Util.ResizeColumns(pluginsListView.Columns.OfType()); pluginsListView.SuppressItemCheckedEvents = false; } } private ListViewItem CreateListViewItem(DeploymentService.ProductDescription product) { ListViewItem item = new ListViewItem(new string[] { product.Name, product.Version.ToString() }); item.Tag = product; item.ImageIndex = 0; return item; } private ListViewItem CreateListViewItem(IPluginDescription plugin) { ListViewItem item = new ListViewItem(new string[] { plugin.Name, plugin.Version.ToString(), plugin.Description }); item.Tag = plugin; item.ImageIndex = 0; return item; } #region products list view events private void productsListView_SelectedIndexChanged(object sender, EventArgs e) { UpdatePluginsList(); installProductsButton.Enabled = (productsListView.SelectedItems.Count > 0 && productsListView.SelectedItems[0].Text != "All Plugins"); } #endregion #region item checked event handler private void remotePluginsListView_ItemChecked(object sender, ItemCheckedEventArgs e) { foreach (ListViewItem item in pluginsListView.SelectedItems) { IPluginDescription plugin = (IPluginDescription)item.Tag; if (e.Item.Checked) HandlePluginChecked(plugin); else HandlePluginUnchecked(plugin); } installPluginsButton.Enabled = pluginsListView.CheckedItems.Count > 0; } private void HandlePluginUnchecked(IPluginDescription plugin) { // also uncheck all dependent plugins List modifiedItems = new List(); modifiedItems.AddRange(FindItemsForPlugin(plugin)); var dependentPlugins = Util.GetAllDependents(plugin, plugins); foreach (var dependentPlugin in dependentPlugins) { // there can be multiple entries for a single plugin in different groups foreach (var item in FindItemsForPlugin(dependentPlugin)) { if (item != null && item.Checked) { if (!modifiedItems.Contains(item)) modifiedItems.Add(item); } } } pluginsListView.UncheckItems(modifiedItems); } private void HandlePluginChecked(IPluginDescription plugin) { // also check all dependencies List modifiedItems = new List(); modifiedItems.AddRange(FindItemsForPlugin(plugin)); foreach (var dep in Util.GetAllDependencies(plugin)) { // there can be multiple entries for a single plugin in different groups foreach (ListViewItem item in FindItemsForPlugin(dep)) { if (item != null && !item.Checked) { if (!modifiedItems.Contains(item)) modifiedItems.Add(item); } } } pluginsListView.CheckItems(modifiedItems); } #endregion #region helper methods private IEnumerable FindItemsForPlugin(IPluginDescription plugin) { return (from item in pluginsListView.Items.OfType() let otherPlugin = item.Tag as IPluginDescription where otherPlugin != null && otherPlugin.Name == plugin.Name && otherPlugin.Version == plugin.Version select item); } // compares for two plugins with same major and minor version if plugin1 is newer than plugin2 private static bool IsNewerThan(IPluginDescription plugin1, IPluginDescription plugin2) { // newer: build version is higher, or if build version is the same revision is higher return plugin1.Version.Build > plugin2.Version.Build || (plugin1.Version.Build == plugin2.Version.Build && plugin1.Version.Revision > plugin2.Version.Revision); } #endregion } }