Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.PluginInfrastructure/Advanced/RemotePluginInstaller.cs @ 3608

Last change on this file since 3608 was 3608, checked in by gkronber, 14 years ago

Changed plugin manager GUI as suggested by reviewers. #989 (Implement review comments in plugin infrastructure)

File size: 18.3 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2010 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21using System;
22using System.Collections.Generic;
23using System.ComponentModel;
24using System.Drawing;
25using System.Data;
26using System.Linq;
27using System.Text;
28using System.Windows.Forms;
29using HeuristicLab.PluginInfrastructure.Manager;
30
31namespace HeuristicLab.PluginInfrastructure.Advanced {
32  internal partial class RemotePluginInstallerView : InstallationManagerControl {
33    private class RefreshBackgroundWorkerResult {
34      public IEnumerable<IPluginDescription> RemotePlugins { get; set; }
35      public IEnumerable<DeploymentService.ProductDescription> RemoteProducts { get; set; }
36    }
37    private class UpdateOrInstallPluginsBackgroundWorkerArgument {
38      public IEnumerable<IPluginDescription> PluginsToUpdate { get; set; }
39      public IEnumerable<IPluginDescription> PluginsToInstall { get; set; }
40    }
41    private const string PluginDiscoveryMessage = "Looking for new plugins...";
42    private BackgroundWorker refreshServerPluginsBackgroundWorker;
43    private BackgroundWorker updateOrInstallPluginsBackgroundWorker;
44
45    private IEnumerable<DeploymentService.ProductDescription> products;
46    private IEnumerable<IPluginDescription> plugins;
47
48    private IEnumerable<IPluginDescription> CheckedPlugins {
49      get {
50        return (from item in pluginsListView.Items.OfType<ListViewItem>()
51                where item.Checked
52                let plugin = item.Tag as IPluginDescription
53                where plugin != null
54                select plugin).ToList();
55      }
56    }
57
58    private InstallationManager installationManager;
59    public InstallationManager InstallationManager {
60      get { return installationManager; }
61      set { installationManager = value; }
62    }
63    private PluginManager pluginManager;
64    public PluginManager PluginManager {
65      get { return pluginManager; }
66      set { pluginManager = value; }
67    }
68    public RemotePluginInstallerView() {
69      InitializeComponent();
70      productImageList.Images.Add(HeuristicLab.PluginInfrastructure.Resources.Resources.Setup_Install);
71      productLargeImageList.Images.Add(HeuristicLab.PluginInfrastructure.Resources.Resources.Setup_Install);
72      pluginsImageList.Images.Add(HeuristicLab.PluginInfrastructure.Resources.Resources.plugin_16);
73      refreshServerPluginsBackgroundWorker = new BackgroundWorker();
74      refreshServerPluginsBackgroundWorker.DoWork += new DoWorkEventHandler(refreshServerPluginsBackgroundWorker_DoWork);
75      refreshServerPluginsBackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(refreshServerPluginsBackgroundWorker_RunWorkerCompleted);
76
77      updateOrInstallPluginsBackgroundWorker = new BackgroundWorker();
78      updateOrInstallPluginsBackgroundWorker.DoWork += new DoWorkEventHandler(updateOrInstallPluginsBackgroundWorker_DoWork);
79      updateOrInstallPluginsBackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(updateOrInstallPluginsBackgroundWorker_RunWorkerCompleted);
80    }
81
82
83    #region event handlers for refresh server plugins background worker
84    void refreshServerPluginsBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
85      if (e.Error != null) {
86        StatusView.ShowError("Connection Error",
87          "There was an error while connecting to the server." + Environment.NewLine +
88          "Please check your connection settings and user credentials.");
89      } else {
90        RefreshBackgroundWorkerResult refreshResult = (RefreshBackgroundWorkerResult)e.Result;
91        products = refreshResult.RemoteProducts;
92        plugins = refreshResult.RemotePlugins;
93        UpdateControl();
94      }
95      StatusView.UnlockUI();
96      StatusView.RemoveMessage(PluginDiscoveryMessage);
97      StatusView.HideProgressIndicator();
98    }
99
100    void refreshServerPluginsBackgroundWorker_DoWork(object sender, DoWorkEventArgs e) {
101      RefreshBackgroundWorkerResult result = new RefreshBackgroundWorkerResult();
102      result.RemotePlugins = installationManager.GetRemotePluginList();
103      result.RemoteProducts = installationManager.GetRemoteProductList();
104      e.Result = result;
105    }
106    #endregion
107    #region event handlers for plugin update background worker
108    void updateOrInstallPluginsBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
109      if (e.Error != null) {
110        StatusView.ShowError("Connection Error",
111          "There was an error while connecting to the server." + Environment.NewLine +
112          "Please check your connection settings and user credentials.");
113      } else {
114        UpdateControl();
115      }
116      StatusView.UnlockUI();
117      StatusView.HideProgressIndicator();
118    }
119
120    void updateOrInstallPluginsBackgroundWorker_DoWork(object sender, DoWorkEventArgs e) {
121      UpdateOrInstallPluginsBackgroundWorkerArgument info = (UpdateOrInstallPluginsBackgroundWorkerArgument)e.Argument;
122      if (info.PluginsToInstall.Count() > 0)
123        installationManager.Install(info.PluginsToInstall);
124      if (info.PluginsToUpdate.Count() > 0)
125        installationManager.Update(info.PluginsToUpdate);
126
127      if (info.PluginsToInstall.Count() > 0 || info.PluginsToUpdate.Count() > 0)
128        pluginManager.DiscoverAndCheckPlugins();
129    }
130    #endregion
131
132
133    #region button events
134    private void refreshRemoteButton_Click(object sender, EventArgs e) {
135      StatusView.LockUI();
136      StatusView.ShowProgressIndicator();
137      StatusView.ShowMessage(PluginDiscoveryMessage);
138      refreshServerPluginsBackgroundWorker.RunWorkerAsync();
139    }
140    private void installPluginsButton_Click(object sender, EventArgs e) {
141      StatusView.LockUI();
142      StatusView.ShowProgressIndicator();
143      var updateOrInstallInfo = new UpdateOrInstallPluginsBackgroundWorkerArgument();
144      // if there is a local plugin with same name and same major and minor version then it's an update
145      var pluginsToUpdate = from remotePlugin in CheckedPlugins
146                            let matchingLocalPlugins = from localPlugin in pluginManager.Plugins
147                                                       where localPlugin.Name == remotePlugin.Name
148                                                       where localPlugin.Version.Major == remotePlugin.Version.Major
149                                                       where localPlugin.Version.Minor == remotePlugin.Version.Minor
150                                                       where IsNewerThan(remotePlugin, localPlugin)
151                                                       select localPlugin
152                            where matchingLocalPlugins.Count() > 0
153                            select remotePlugin;
154
155      // otherwise install a new plugin
156      var pluginsToInstall = CheckedPlugins.Except(pluginsToUpdate);
157
158      updateOrInstallInfo.PluginsToInstall = pluginsToInstall;
159      updateOrInstallInfo.PluginsToUpdate = pluginsToUpdate;
160      updateOrInstallPluginsBackgroundWorker.RunWorkerAsync(updateOrInstallInfo);
161    }
162    private void installProductsButton_Click(object sender, EventArgs e) {
163      StatusView.LockUI();
164      StatusView.ShowProgressIndicator();
165      var updateOrInstallInfo = new UpdateOrInstallPluginsBackgroundWorkerArgument();
166      var selectedProduct = (DeploymentService.ProductDescription)productsListView.SelectedItems[0].Tag;
167      // if there is a local plugin with same name and same major and minor version then it's an update
168      var pluginsToUpdate = from plugin in selectedProduct.Plugins
169                            let matchingLocalPlugins = from localPlugin in pluginManager.Plugins
170                                                       where localPlugin.Name == plugin.Name
171                                                       where localPlugin.Version.Major == plugin.Version.Major
172                                                       where localPlugin.Version.Minor == plugin.Version.Minor
173                                                       where IsNewerThan(plugin, localPlugin)
174                                                       select localPlugin
175                            where matchingLocalPlugins.Count() > 0
176                            select plugin;
177
178      // otherwise install a new plugin
179      var pluginsToInstall = selectedProduct.Plugins.Except(pluginsToUpdate);
180
181      updateOrInstallInfo.PluginsToInstall = (IEnumerable<IPluginDescription>)pluginsToInstall.ToList();
182      updateOrInstallInfo.PluginsToUpdate = (IEnumerable<IPluginDescription>)pluginsToUpdate.ToList();
183      updateOrInstallPluginsBackgroundWorker.RunWorkerAsync(updateOrInstallInfo);
184    }
185
186    private void showLargeIconsButton_CheckedChanged(object sender, EventArgs e) {
187      productsListView.View = View.LargeIcon;
188    }
189
190    private void showDetailsButton_CheckedChanged(object sender, EventArgs e) {
191      productsListView.View = View.Details;
192    }
193
194    #endregion
195
196    private void UpdateControl() {
197      // clear products view
198      List<ListViewItem> productItemsToDelete = new List<ListViewItem>(productsListView.Items.OfType<ListViewItem>());
199      productItemsToDelete.ForEach(item => productsListView.Items.Remove(item));
200
201      // populate products list view
202      foreach (var product in products) {
203        var item = CreateListViewItem(product);
204        productsListView.Items.Add(item);
205      }
206      var allPluginsListViewItem = new ListViewItem();
207      allPluginsListViewItem.Text = "All Plugins";
208      allPluginsListViewItem.ImageIndex = 0;
209      productsListView.Items.Add(allPluginsListViewItem);
210      foreach (ColumnHeader column in productsListView.Columns)
211        if (productsListView.Items.Count > 0)
212          column.AutoResize(ColumnHeaderAutoResizeStyle.ColumnContent);
213        else column.AutoResize(ColumnHeaderAutoResizeStyle.HeaderSize);
214    }
215
216    private void UpdatePluginsList() {
217      // clear plugins list view
218      List<ListViewItem> pluginItemsToDelete = new List<ListViewItem>(pluginsListView.Items.OfType<ListViewItem>());
219      pluginItemsToDelete.ForEach(item => pluginsListView.Items.Remove(item));
220
221      // populate plugins list
222      if (productsListView.SelectedItems.Count > 0) {
223        pluginsListView.SuppressItemCheckedEvents = true;
224
225        var selectedItem = productsListView.SelectedItems[0];
226        if (selectedItem.Text == "All Plugins") {
227          foreach (var plugin in plugins) {
228            var item = CreateListViewItem(plugin);
229            pluginsListView.Items.Add(item);
230          }
231        } else {
232          var selectedProduct = (DeploymentService.ProductDescription)productsListView.SelectedItems[0].Tag;
233          foreach (var plugin in selectedProduct.Plugins) {
234            var item = CreateListViewItem(plugin);
235            pluginsListView.Items.Add(item);
236          }
237        }
238
239        foreach (ColumnHeader column in pluginsListView.Columns)
240          if (pluginsListView.Items.Count > 0)
241            column.AutoResize(ColumnHeaderAutoResizeStyle.ColumnContent);
242          else column.AutoResize(ColumnHeaderAutoResizeStyle.HeaderSize);
243
244        pluginsListView.SuppressItemCheckedEvents = false;
245      }
246    }
247
248    private ListViewItem CreateListViewItem(DeploymentService.ProductDescription product) {
249      ListViewItem item = new ListViewItem(new string[] { product.Name, product.Version.ToString() });
250      item.Tag = product;
251      item.ImageIndex = 0;
252      return item;
253    }
254
255    private ListViewItem CreateListViewItem(IPluginDescription plugin) {
256      ListViewItem item = new ListViewItem(new string[] { plugin.Name, plugin.Version.ToString(), plugin.Description });
257      item.Tag = plugin;
258      item.ImageIndex = 0;
259      return item;
260    }
261
262    #region products list view events
263    private void productsListView_SelectedIndexChanged(object sender, EventArgs e) {
264      UpdatePluginsList();
265      installProductsButton.Enabled = (productsListView.SelectedItems.Count > 0 &&
266        productsListView.SelectedItems[0].Text != "All Plugins");
267    }
268    #endregion
269
270    #region item checked event handler
271    private void remotePluginsListView_ItemChecked(object sender, ItemCheckedEventArgs e) {
272      foreach (ListViewItem item in pluginsListView.SelectedItems) {
273        // dispatch by check state and type of item (product/plugin)
274        IPluginDescription plugin = item.Tag as IPluginDescription;
275        if (plugin != null)
276          if (e.Item.Checked)
277            HandlePluginChecked(plugin);
278          else
279            HandlePluginUnchecked(plugin);
280        else {
281          DeploymentService.ProductDescription product = item.Tag as DeploymentService.ProductDescription;
282          if (product != null)
283            if (e.Item.Checked)
284              HandleProductChecked(product);
285            else
286              HandleProductUnchecked(product);
287        }
288      }
289      installPluginsButton.Enabled = pluginsListView.CheckedItems.Count > 0;
290    }
291
292    private void HandleProductUnchecked(HeuristicLab.PluginInfrastructure.Advanced.DeploymentService.ProductDescription product) {
293      // also uncheck the plugins of the product
294      List<ListViewItem> modifiedItems = new List<ListViewItem>();
295      modifiedItems.Add(FindItemForProduct(product));
296      foreach (var plugin in product.Plugins) {
297        // there can be multiple entries for a single plugin in different groups
298        foreach (var item in FindItemsForPlugin(plugin)) {
299          if (item != null && item.Checked)
300            modifiedItems.Add(item);
301        }
302      }
303      pluginsListView.UncheckItems(modifiedItems);
304    }
305
306    private void HandleProductChecked(HeuristicLab.PluginInfrastructure.Advanced.DeploymentService.ProductDescription product) {
307      // also check all plugins of the product
308      List<ListViewItem> modifiedItems = new List<ListViewItem>();
309      modifiedItems.Add(FindItemForProduct(product));
310      foreach (var plugin in product.Plugins) {
311        // there can be multiple entries for a single plugin in different groups
312        foreach (var item in FindItemsForPlugin(plugin)) {
313          if (item != null && !item.Checked) {
314            if (!modifiedItems.Contains(item))
315              modifiedItems.Add(item);
316          }
317        }
318      }
319      pluginsListView.CheckItems(modifiedItems);
320    }
321
322    private void HandlePluginUnchecked(IPluginDescription plugin) {
323      // also uncheck all dependent plugins
324      List<ListViewItem> modifiedItems = new List<ListViewItem>();
325      modifiedItems.AddRange(FindItemsForPlugin(plugin));
326      var dependentPlugins = from otherPlugin in plugins
327                             where otherPlugin.Dependencies.Any(dep => dep.Name == plugin.Name && dep.Version == plugin.Version)
328                             select otherPlugin;
329      foreach (var dependentPlugin in dependentPlugins) {
330        // there can be multiple entries for a single plugin in different groups
331        foreach (var item in FindItemsForPlugin(dependentPlugin)) {
332          if (item != null && item.Checked) {
333            if (!modifiedItems.Contains(item))
334              modifiedItems.Add(item);
335          }
336        }
337      }
338      // also uncheck all products containing this plugin
339      var dependentProducts = from product in products
340                              where product.Plugins.Any(p => p.Name == plugin.Name && p.Version == plugin.Version)
341                              select product;
342      foreach (var dependentProduct in dependentProducts) {
343        var item = FindItemForProduct(dependentProduct);
344        if (item != null && item.Checked) {
345          if (!modifiedItems.Contains(item))
346            modifiedItems.Add(item);
347        }
348      }
349      pluginsListView.UncheckItems(modifiedItems);
350    }
351
352    private void HandlePluginChecked(IPluginDescription plugin) {
353      // also check all dependencies
354      List<ListViewItem> modifiedItems = new List<ListViewItem>();
355      modifiedItems.AddRange(FindItemsForPlugin(plugin));
356      foreach (var dep in plugin.Dependencies) {
357        // there can be multiple entries for a single plugin in different groups
358        foreach (ListViewItem item in FindItemsForPlugin(dep)) {
359          if (item != null && !item.Checked) {
360            if (!modifiedItems.Contains(item))
361              modifiedItems.Add(item);
362          }
363        }
364      }
365      pluginsListView.CheckItems(modifiedItems);
366    }
367
368    #endregion
369
370    #region helper methods
371    private IEnumerable<ListViewItem> FindItemsForPlugin(IPluginDescription plugin) {
372      return (from item in pluginsListView.Items.OfType<ListViewItem>()
373              let otherPlugin = item.Tag as IPluginDescription
374              where otherPlugin != null && otherPlugin.Name == plugin.Name && otherPlugin.Version == plugin.Version
375              select item);
376    }
377
378    private ListViewItem FindItemForProduct(DeploymentService.ProductDescription product) {
379      return (from item in pluginsListView.Items.OfType<ListViewItem>()
380              let otherProduct = item.Tag as DeploymentService.ProductDescription
381              where otherProduct != null && otherProduct.Name == product.Name && otherProduct.Version == product.Version
382              select item).SingleOrDefault();
383    }
384
385    private bool IsNewerThan(IPluginDescription plugin1, IPluginDescription plugin2) {
386      // newer: build version is higher, or if build version is the same revision is higher
387      if (plugin1.Version.Build < plugin2.Version.Build) return false;
388      else if (plugin1.Version.Build > plugin2.Version.Build) return true;
389      else return plugin1.Version.Revision > plugin2.Version.Revision;
390    }
391    #endregion
392
393  }
394}
Note: See TracBrowser for help on using the repository browser.