Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.PluginInfrastructure.GUI/ManagerForm.cs @ 2059

Last change on this file since 2059 was 1899, checked in by swagner, 16 years ago

Fixed #651

File size: 42.6 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2008 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
21
22using System;
23using System.Collections.Generic;
24using System.ComponentModel;
25using System.Data;
26using System.Drawing;
27using System.Text;
28using System.Windows.Forms;
29using System.Diagnostics;
30using System.Net;
31using System.IO;
32using ICSharpCode.SharpZipLib.Zip;
33
34namespace HeuristicLab.PluginInfrastructure.GUI {
35  public partial class ManagerForm : Form {
36    private List<PluginTag> allTags = new List<PluginTag>();
37    private Dictionary<PluginTag, PluginAction> actions = new Dictionary<PluginTag, PluginAction>();
38    private List<PluginDescription> allAvailablePlugins = new List<PluginDescription>();
39    private string pluginDir = Application.StartupPath + "/" + HeuristicLab.PluginInfrastructure.Properties.Settings.Default.PluginDir;
40    private string cacheDir = Application.StartupPath + "/" + HeuristicLab.PluginInfrastructure.GUI.Properties.Settings.Default.CacheDir;
41    private string backupDir = Application.StartupPath + "/" + HeuristicLab.PluginInfrastructure.GUI.Properties.Settings.Default.BackupDir;
42    private string tempDir = Application.StartupPath + "/" + HeuristicLab.PluginInfrastructure.GUI.Properties.Settings.Default.TempDir;
43    private const string AVAILABLE_PLUGINS = "Available plugins";
44    private const string DISABLED_PLUGINS = "Disabled plugins";
45    private const string INSTALLED_PLUGINS = "Installed plugins";
46
47    public ManagerForm() {
48      InitializeComponent();
49      InitializePlugins();
50      Cursor = Cursors.Arrow;
51    }
52
53    private void InitializePlugins() {
54      listView.View = View.Details;
55      listView.FullRowSelect = true;
56      listView.Items.Clear();
57      allTags.Clear();
58      actions.Clear();
59
60      publishButton.Enabled = false;
61      publishMenuItem.Enabled = false;
62      installButton.Enabled = false;
63      installButton.Checked = false;
64      installMenuItem.Enabled = false;
65      installMenuItem.Checked = false;
66      deleteButton.Enabled = false;
67      deleteButton.Checked = false;
68      deleteMenuItem.Enabled = false;
69      deleteMenuItem.Checked = false;
70
71      foreach(PluginInfo pluginInfo in PluginManager.Manager.ActivePlugins) {
72        // create a new PluginAction tag for the plugin
73        PluginTag tag = new PluginTag(allTags, pluginInfo, PluginState.Installed);
74        allTags.Add(tag);
75        // add to "installed plugins" item
76        ListViewItem installedPluginsItem = new ListViewItem(pluginInfo.Name);
77        installedPluginsItem.Tag = tag;
78        installedPluginsItem.ImageIndex = 0;
79        installedPluginsItem.Group = listView.Groups[INSTALLED_PLUGINS];
80        installedPluginsItem.SubItems.Add(pluginInfo.Version.ToString());
81        listView.Items.Add(installedPluginsItem);
82      }
83      foreach(PluginInfo pluginInfo in PluginManager.Manager.DisabledPlugins) {
84        PluginTag tag = new PluginTag(allTags, pluginInfo, PluginState.Disabled);
85        allTags.Add(tag);
86        ListViewItem disabledPluginsItem = new ListViewItem(pluginInfo.Name);
87        disabledPluginsItem.Tag = tag;
88        disabledPluginsItem.ImageIndex = 0;
89        disabledPluginsItem.Group = listView.Groups[DISABLED_PLUGINS];
90        disabledPluginsItem.SubItems.Add(pluginInfo.Version.ToString());
91        listView.Items.Add(disabledPluginsItem);
92      }
93
94      allAvailablePlugins = FilterMostRecentPluginVersions(allAvailablePlugins);
95      // find all plugins that are installed for which a new version is available
96      List<PluginDescription> upgrades = FindUpgrades(allTags, allAvailablePlugins);
97      // find all available plugins that are not installed and new (= new name not new version) since the last update
98      List<PluginDescription> newPlugins = FindNewPlugins(allTags, allAvailablePlugins);
99      // find all plugins that are available (not installed) for which a new version has been released since the last update
100      List<PluginDescription> overridingPlugins = FindOverridingPlugins(allTags, allAvailablePlugins);
101      newPlugins.ForEach(delegate(PluginDescription plugin) {
102        PluginTag tag = new PluginTag(allTags, plugin, PluginState.Available);
103        allTags.Add(tag);
104        ListViewItem availableItem = new ListViewItem(plugin.Name);
105        availableItem.Tag = tag;
106        availableItem.ImageIndex = 0;
107        availableItem.Group = listView.Groups[AVAILABLE_PLUGINS];
108        availableItem.SubItems.Add(plugin.Version.ToString());
109        listView.Items.Add(availableItem);
110      });
111      upgrades.ForEach(delegate(PluginDescription upgrade) {
112        // find the installed plugins that have the same name
113        List<PluginTag> oldPlugins = allTags.FindAll(delegate(PluginTag tag) {
114          return tag.PluginName == upgrade.Name;
115        });
116        PluginTag oldPlugin = oldPlugins[0];
117        // store the upgrade in the old plugin
118        oldPlugin.UpgradePluginDescription = upgrade;
119        UpdateTreeNodes(oldPlugins);
120      });
121      overridingPlugins.ForEach(delegate(PluginDescription overridingPlugin) {
122        List<PluginTag> currentPlugins = allTags.FindAll(delegate(PluginTag tag) {
123          return tag.PluginName == overridingPlugin.Name;
124        });
125        PluginTag currentPlugin = currentPlugins[0];
126        // replace the plugin description of the available plugin to point to the overriding plugin
127        currentPlugin.PluginDescription = overridingPlugin;
128      });
129      toolStripStatusLabel.Text = "Installed: " + listView.Groups[INSTALLED_PLUGINS].Items.Count +
130        " Updates: " + upgrades.Count + " Available: " + listView.Groups[AVAILABLE_PLUGINS].Items.Count;
131      RebuildActionHulls();
132
133      listView.Sort();
134    }
135
136    private void aboutToolStripMenuItem_Click(object sender, EventArgs e) {
137      AboutBox box = new AboutBox();
138      box.ShowDialog();
139    }
140
141    private void publishButton_Click(object sender, EventArgs args) {
142      if(listView.SelectedItems.Count == 0) return;
143      List<PluginInfo> plugins = new List<PluginInfo>();
144      foreach(ListViewItem item in listView.SelectedItems) {
145        plugins.Add(((PluginTag)item.Tag).Plugin);
146      }
147      PublishPlugin(plugins);
148    }
149
150    private void PublishPlugin(List<PluginInfo> plugins) {
151      StringBuilder xmlEntries = new StringBuilder();
152      try {
153        infoTextBox.Text = "Publishing plugin:\n";
154        foreach(PluginInfo plugin in plugins) {
155          string packageFileName = plugin.Name + "-" + plugin.Version + ".zip";
156          ZipFile zipFile = ZipFile.Create(packageFileName);
157          zipFile.NameTransform = new PluginNameTransform();
158          zipFile.BeginUpdate();
159
160          infoTextBox.Text+="Creating " + packageFileName + "...\n";
161          foreach(string filename in plugin.Files) {
162            infoTextBox.Text += "Adding " + filename + "\n";
163            zipFile.Add(filename);
164          }
165
166          zipFile.CommitUpdate();
167          zipFile.Close();
168          FileInfo fileInfo = new FileInfo(packageFileName);
169          infoTextBox.Text += "Created " + packageFileName + " (" + fileInfo.Length + " bytes)\n";
170          xmlEntries.Append("  <Plugin Name=\"").Append(plugin.Name).Append("\" Version=\"").Append(plugin.Version)
171            .Append("\" Build=\"").Append(plugin.BuildDate.ToUniversalTime().ToString(System.Globalization.CultureInfo.InvariantCulture.DateTimeFormat))
172            .Append("\">").AppendLine();
173          foreach(PluginInfo dependency in plugin.Dependencies) {
174            xmlEntries.Append("    <Dependency Name=\"").Append(dependency.Name).Append("\" />").AppendLine();
175          }
176          xmlEntries.Append("  </Plugin>").AppendLine();
177        }
178
179        infoTextBox.Text += "Upload the zip files to your plugin source and add the following to" +
180" the file plugins.xml residing in the base directory of your plugin source.\n\n";
181        infoTextBox.Text += xmlEntries.ToString();
182      } catch(Exception exception) {
183        infoTextBox.Text += "\nThere was an error!\n" + exception;
184      }
185    }
186
187    private void updateButton_Click(object sender, EventArgs e) {
188      // connect to all plugin sources and get a list of available plugins
189      // log progress in the infoPane
190      BackgroundWorker worker = new BackgroundWorker();
191      worker.WorkerSupportsCancellation = true;
192      worker.WorkerReportsProgress = true;
193
194      DownloaderDialog dialog = new DownloaderDialog();
195      dialog.OnCancel += delegate() {
196        worker.CancelAsync();
197      };
198
199      worker.ProgressChanged += delegate(object progressChangedSender, ProgressChangedEventArgs args) {
200        dialog.SetProgress(args.ProgressPercentage);
201      };
202
203      worker.DoWork += delegate(object doWorkSender, DoWorkEventArgs args) {
204        allAvailablePlugins.Clear();
205        dialog.SetDownloadDescription("Updating available plugins...");
206        int i = 0;
207        int n = HeuristicLab.PluginInfrastructure.GUI.Properties.Settings.Default.PluginSources.Count;
208        foreach(string pluginSourceUrl in HeuristicLab.PluginInfrastructure.GUI.Properties.Settings.Default.PluginSources) {
209          if(!worker.CancellationPending) {
210            dialog.SetDownloadDescription("Connecting to " + pluginSourceUrl + "...");
211            PluginSource source = PluginSource.TryCreate(pluginSourceUrl);
212            if(source == null) {
213              Invoke((MethodInvoker)delegate() {
214                infoTextBox.Text += "Error! Couldn't access the plugin source " + pluginSourceUrl + ".\n";
215              });
216            } else {
217              dialog.SetDownloadDescription("Getting list of available plugins from " + pluginSourceUrl + "...");
218              List<PluginDescription> availablePlugins = source.AvailablePlugins();
219              allAvailablePlugins.AddRange(availablePlugins);
220              Invoke((MethodInvoker)delegate() {
221                infoTextBox.Text += pluginSourceUrl + " " + availablePlugins.Count + " plugins available.\n";
222              });
223
224              worker.ReportProgress((int)((++i / (double)n) * 100.0));
225            }
226          }
227        }
228        if(worker.CancellationPending) {
229          args.Cancel = true;
230        } else {
231          args.Cancel = false;
232          args.Result = allAvailablePlugins;
233        }
234      };
235
236      worker.RunWorkerCompleted += delegate(object runWorkerCompletedSender, RunWorkerCompletedEventArgs args) {
237        if(!args.Cancelled && args.Error == null) {
238          InitializePlugins();
239        }
240        dialog.Close();
241      };
242
243      dialog.Show();
244      worker.RunWorkerAsync();
245    }
246
247
248    private List<PluginDescription> FilterMostRecentPluginVersions(List<PluginDescription> list) {
249      List<PluginDescription> newList = new List<PluginDescription>();
250
251      list.ForEach(delegate(PluginDescription tag) {
252        // find all entries with the same plugin name
253        List<PluginDescription> samePlugins = list.FindAll(delegate(PluginDescription otherTag) {
254          return tag.Name == otherTag.Name;
255        });
256
257        // keep only the most recent one
258        PluginDescription mostRecentVersion = samePlugins[0];
259        if(samePlugins.Count > 1) {
260          samePlugins.ForEach(delegate(PluginDescription tag2) {
261            if(tag2.Version > mostRecentVersion.Version ||
262              (tag2.Version == mostRecentVersion.Version && tag2.BuildDate>mostRecentVersion.BuildDate)) {
263              mostRecentVersion = tag2;
264            }
265          });
266        }
267        if(!newList.Contains(mostRecentVersion)) {
268          newList.Add(mostRecentVersion);
269        }
270
271      });
272
273      return newList;
274    }
275
276    private List<PluginDescription> FindNewPlugins(List<PluginTag> allTags, List<PluginDescription> allAvailablePlugins) {
277      List<PluginDescription> newPlugins = new List<PluginDescription>();
278      // for each of the available plugins check if there is an installed plugin that has the same name
279      // only if there is no installed plugin with the same name then it is a new plugin
280      // NOTE: make sure to keep only the most recent entry of a plugin in the allAvailablePlugins list
281      allAvailablePlugins.ForEach(delegate(PluginDescription availablePlugin) {
282        if(!allTags.Exists(delegate(PluginTag tag2) {
283          return availablePlugin.Name == tag2.PluginName;
284        })) {
285          newPlugins.Add(availablePlugin);
286        }
287      });
288
289      return newPlugins;
290    }
291
292    private List<PluginDescription> FindUpgrades(List<PluginTag> allTags, List<PluginDescription> allAvailablePlugins) {
293      List<PluginDescription> upgrades = new List<PluginDescription>();
294      // for each of the available plugins check if there is an installed plugin that has the same name
295      // only if there is an installed plugin with the same name and the available plugin has a more recent version it is an upgrade
296      // NOTE: make sure to keep only the most recent entry of a plugin in the allAvailablePlugins list
297      allAvailablePlugins.ForEach(delegate(PluginDescription availablePlugin) {
298        List<PluginTag> oldPlugins = allTags.FindAll(delegate(PluginTag currentPlugin) {
299          return currentPlugin.PluginName == availablePlugin.Name &&
300            (currentPlugin.State & (PluginState.Installed | PluginState.Disabled)) != 0;
301        });
302
303        if(oldPlugins.Count == 1) {
304          if(oldPlugins[0].PluginVersion < availablePlugin.Version ||
305            (oldPlugins[0].PluginVersion == availablePlugin.Version && oldPlugins[0].PluginBuildDate<availablePlugin.BuildDate)) {
306            upgrades.Add(availablePlugin);
307          }
308        }
309      });
310
311      return upgrades;
312    }
313
314    private List<PluginDescription> FindOverridingPlugins(List<PluginTag> allTags, List<PluginDescription> allAvailablePlugins) {
315      List<PluginDescription> overrides = new List<PluginDescription>();
316      allAvailablePlugins.ForEach(delegate(PluginDescription availablePlugin) {
317        List<PluginTag> currentPlugins = allTags.FindAll(delegate(PluginTag currentPlugin) {
318          return currentPlugin.PluginName == availablePlugin.Name && currentPlugin.State == PluginState.Available;
319        });
320
321        if(currentPlugins.Count == 1 && (currentPlugins[0].PluginVersion < availablePlugin.Version ||
322          (currentPlugins[0].PluginVersion==availablePlugin.Version && currentPlugins[0].PluginBuildDate<availablePlugin.BuildDate))) {
323          overrides.Add(availablePlugin);
324        }
325      });
326
327      return overrides;
328    }
329
330    private void RebuildActionHulls() {
331      Dictionary<PluginTag, PluginAction> oldActions = new Dictionary<PluginTag, PluginAction>(actions);
332      actions.Clear();
333
334      foreach(PluginAction action in oldActions.Values) {
335        if(action.Action == ManagerAction.Install) {
336          MarkInstall(action.Plugin);
337        } else if(action.Action == ManagerAction.Remove) {
338          MarkRemove(action.Plugin);
339        } else
340          throw new InvalidOperationException();
341      }
342
343      // update the GUI to represent new state of the selected plugin
344      if(listView.SelectedItems.Count>0 && listView.SelectedItems[0].Tag is PluginTag) {
345        UpdateActionButtons((PluginTag)listView.SelectedItems[0].Tag);
346        DisplayPluginInfo(((PluginTag)listView.SelectedItems[0].Tag).GetPluginDetails());
347      }
348    }
349
350    private void MarkInstall(PluginTag actionTag) {
351      if(!CheckInstallConflicts(actionTag)) {
352        CreateNewInstallAction(actionTag);
353      } else {
354        HandleInstallConflict(actionTag);
355      }
356    }
357
358    private void UnmarkInstall(PluginTag actionTag) {
359      if(!CheckNoActionConflict(actionTag)) {
360        List<PluginAction> rootActions = GetActionsInvolving(actionTag);
361        PluginAction rootAction = rootActions[0];
362        actions.Remove(rootAction.Plugin);
363        UpdateTreeNodes(rootAction.Hull);
364      } else {
365        HandleNoActionConflict(actionTag);
366      }
367    }
368
369    private void HandleNoActionConflict(PluginTag actionTag) {
370      List<PluginAction> conflictingActions = GetOverlappingActions(actionTag, ManagerAction.Any);
371      PluginAction theAction = GetSmallestActionInvolving(actionTag, conflictingActions);
372      conflictingActions.Remove(theAction);
373      string action = theAction.GetInverseActionString();
374      DialogResult userResult = ShowGenericConflictDialog(action, theAction, conflictingActions);
375      if(userResult == DialogResult.OK) {
376        conflictingActions.ForEach(delegate(PluginAction conflictingAction) {
377          actions.Remove(conflictingAction.Plugin);
378          UpdateTreeNodes(conflictingAction.Hull);
379        });
380
381        PluginAction rootAction = GetSmallestActionInvolving(actionTag, GetOverlappingActions(actionTag, ManagerAction.Any));
382        actions.Remove(rootAction.Plugin);
383        UpdateTreeNodes(rootAction.Hull);
384      }
385    }
386
387    private bool CheckNoActionConflict(PluginTag actionTag) {
388      return GetOverlappingActions(actionTag, ManagerAction.Any).Count > 1;
389    }
390
391    private void MarkRemove(PluginTag actionTag) {
392      if(!CheckRemoveConflicts(actionTag)) {
393        CreateNewRemoveAction(actionTag);
394      } else {
395        HandleRemoveConflict(actionTag);
396      }
397    }
398
399    private void UnmarkRemove(PluginTag pluginTag) {
400      if(!CheckNoActionConflict(pluginTag)) {
401        List<PluginAction> rootActions = GetActionsInvolving(pluginTag);
402        // if there is no conflict then there can only be one action connected to the pluginTag
403        PluginAction rootAction = rootActions[0];
404        // kill the root action
405        actions.Remove(rootAction.Plugin);
406        UpdateTreeNodes(rootAction.Hull);
407      } else {
408        HandleNoActionConflict(pluginTag);
409      }
410    }
411
412    private void CreateNewRemoveAction(PluginTag actionTag) {
413      CreateNewAction(actionTag, ManagerAction.Remove, actionTag.GetDependentTags());
414    }
415
416    private void CreateNewInstallAction(PluginTag actionTag) {
417      CreateNewAction(actionTag, ManagerAction.Install, actionTag.GetDependencyTags());
418    }
419
420    private void CreateNewAction(PluginTag actionTag, ManagerAction action, List<PluginTag> hull) {
421      PluginAction newAction = new PluginAction();
422      newAction.Action = action;
423      newAction.Plugin = actionTag;
424      newAction.Hull = hull;
425      newAction.Hull.Add(actionTag);
426      actions[actionTag] = newAction;
427      UpdateTreeNodes(newAction.Hull);
428    }
429
430    private bool CheckRemoveConflicts(PluginTag actionTag) {
431      return GetOverlappingActions(actionTag, ManagerAction.Install).Count > 0;
432    }
433
434    private void HandleRemoveConflict(PluginTag actionTag) {
435      List<PluginAction> conflictingActions = GetOverlappingActions(actionTag, ManagerAction.Install);
436
437      // create a planned action to display in the conflict message box
438      PluginAction plannedRemoveAction = new PluginAction();
439      plannedRemoveAction.Action = ManagerAction.Remove;
440      plannedRemoveAction.Plugin = actionTag;
441
442      DialogResult userResult = ShowGenericConflictDialog("Remove", plannedRemoveAction, conflictingActions);
443      if(userResult == DialogResult.OK) {
444        // if user says ok then remove the old actions and create a new remove action
445        conflictingActions.ForEach(delegate(PluginAction conflictingAction) {
446          actions.Remove(conflictingAction.Plugin);
447          UpdateTreeNodes(conflictingAction.Hull);
448        });
449
450        CreateNewRemoveAction(actionTag);
451      }
452    }
453
454    private bool CheckInstallConflicts(PluginTag actionTag) {
455      return GetOverlappingActions(actionTag, ManagerAction.Remove).Count > 0;
456    }
457
458    private void HandleInstallConflict(PluginTag actionTag) {
459      List<PluginAction> conflictingActions = GetOverlappingActions(actionTag, ManagerAction.Remove);
460
461      // create a planned action to display in the conflict message box
462      PluginAction plannedInstallAction = new PluginAction();
463      plannedInstallAction.Action = ManagerAction.Install;
464      plannedInstallAction.Plugin = actionTag;
465
466      DialogResult userResult = ShowGenericConflictDialog("Install", plannedInstallAction, conflictingActions);
467
468      if(userResult == DialogResult.OK) {
469        conflictingActions.ForEach(delegate(PluginAction conflictingAction) {
470          actions.Remove(conflictingAction.Plugin);
471          UpdateTreeNodes(conflictingAction.Hull);
472        });
473
474        CreateNewInstallAction(actionTag);
475      }
476    }
477
478    private DialogResult ShowGenericConflictDialog(string action, PluginAction theAction, List<PluginAction> conflictingActions) {
479      string message = "The actions:\n\n";
480      conflictingActions.ForEach(delegate(PluginAction conflictingAction) {
481        message += " - " + conflictingAction.Action + " " + conflictingAction.Plugin.PluginName + "\n";
482      });
483      message += "\nconflict with the action: " + action + " " + theAction.Plugin.PluginName + ".\n\n";
484      message += "\nSolution:\n";
485      conflictingActions.ForEach(delegate(PluginAction conflictingAction) {
486        message += " - " + conflictingAction.GetInverseActionString() + " " + conflictingAction.Plugin.PluginName + "\n";
487      });
488      message += " - " + action + " " + theAction.Plugin.PluginName + "\n\n";
489      message += "Do you want to continue?";
490
491      return MessageBox.Show(message, "Resolve conflicting actions", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning);
492
493    }
494
495    private List<PluginAction> GetActionsInvolving(PluginTag actionTag) {
496      List<PluginAction> filteredActions = new List<PluginAction>();
497      foreach(PluginAction action in actions.Values) {
498        if(action.Hull.Contains(actionTag)) {
499          filteredActions.Add(action);
500        }
501      }
502      return filteredActions;
503    }
504
505    private PluginAction GetSmallestActionInvolving(PluginTag actionTag, List<PluginAction> actions) {
506      PluginAction smallestAction;
507      // if there is an action defined for which actionTag is the root then us it as root tag
508      // otherwise use the root of the action that contains pluginTag in its hull and has the smallest hull of all actions
509      smallestAction = actions.Find(delegate(PluginAction action) {
510        return action.Plugin == actionTag;
511      });
512      // if nothing is found then get the action with the smallest hull
513      if(smallestAction == null) {
514        // find the action with the smallest hull
515        smallestAction = actions[0];
516        actions.ForEach(delegate(PluginAction action) {
517          if(action.Hull.Count < smallestAction.Hull.Count)
518            smallestAction = action;
519        });
520      }
521
522      return smallestAction;
523    }
524
525    /// <summary>
526    /// Similar to GetRootActions but filter only specific ManagerActions
527    /// </summary>
528    /// <param name="pluginTag"></param>
529    /// <param name="overlapAction"></param>
530    /// <returns></returns>
531    private List<PluginAction> GetOverlappingActions(PluginTag actionTag, ManagerAction overlapAction) {
532      List<PluginAction> overlappingActions = new List<PluginAction>();
533      foreach(PluginAction action in actions.Values) {
534        if(((action.Action & overlapAction) > 0) &&
535          action.Hull.Contains(actionTag)) {
536          overlappingActions.Add(action);
537        }
538      }
539
540      return overlappingActions;
541    }
542
543    private void UpdateTreeNodes(List<PluginTag> hull) {
544      hull.ForEach(delegate(PluginTag tag) {
545        FindPluginItems(tag).ForEach(delegate(ListViewItem item) {
546          ManagerAction action = GetAction(tag);
547          if(action != ManagerAction.None) {
548            item.Text = tag.PluginName + " - Action: " + action;
549            if(action == ManagerAction.Remove) {
550              item.ImageIndex = 3;
551            } else if(action == ManagerAction.Install) {
552              item.ImageIndex = 2;
553            }
554          } else if(tag.State == PluginState.Upgradeable) {
555            item.Text = tag.PluginName + " - Upgrade: " + tag.PluginVersion + " -> " + tag.UpgradePluginDescription.Version;
556            item.ImageIndex = 2;
557          } else {
558            item.Text = tag.PluginName;
559            item.ImageIndex = 0;
560          }
561        });
562      });
563    }
564
565    private void UpdateActionButtons(PluginTag tag) {
566      publishButton.Enabled = (tag.State & PluginState.Installed) == PluginState.Installed;
567      installButton.Enabled = (tag.State & PluginState.Available) == PluginState.Available;
568      deleteButton.Enabled = (tag.State & (PluginState.Installed | PluginState.Upgradeable | PluginState.Disabled)) != 0;
569
570      installButton.Checked = GetAction(tag) == ManagerAction.Install;
571      deleteButton.Checked = GetAction(tag) == ManagerAction.Remove;
572
573      publishMenuItem.Enabled = publishButton.Enabled;
574      installMenuItem.Enabled = installButton.Enabled;
575      deleteMenuItem.Enabled = deleteButton.Enabled;
576      installMenuItem.Checked = installButton.Checked;
577      deleteMenuItem.Checked = deleteButton.Checked;
578    }
579
580    private ManagerAction GetAction(PluginTag tag) {
581      ManagerAction plannedAction = ManagerAction.None;
582      if(actions.ContainsKey(tag)) {
583        plannedAction = actions[tag].Action;
584      } else {
585        foreach(PluginAction action in actions.Values) {
586          if(action.Hull.Contains(tag)) {
587            plannedAction = action.Action;
588          }
589        }
590      }
591
592      if(plannedAction == ManagerAction.Install
593        && (tag.State == PluginState.Installed || tag.State == PluginState.Upgradeable)) {
594        return ManagerAction.None;
595      } else if(plannedAction == ManagerAction.Remove
596        && tag.State == PluginState.Available) {
597        return ManagerAction.None;
598      } else {
599        return plannedAction;
600      }
601    }
602
603    private List<ListViewItem> FindPluginItems(PluginTag pluginTag) {
604      List<ListViewItem> items = new List<ListViewItem>();
605      foreach(ListViewGroup group in listView.Groups) {
606        foreach(ListViewItem item in group.Items) {
607          if(pluginTag.Equals(item.Tag)) {
608            items.Add(item);
609          }
610        }
611      }
612      return items;
613    }
614    private void DisplayPluginInfo(string pluginInformation) {
615      infoTextBox.Text = pluginInformation;
616    }
617    private void upgradeButton_Click(object sender, EventArgs args) {
618      try {
619        ClearTemporaryFiles();
620        if(!DownloadFiles()) {
621          return;
622        }
623        OnDeletePlugins();
624        OnPreUpgradePlugins();
625        try {
626          PluginManager.Manager.UnloadAllPlugins();
627          BackupOldFiles();
628          DeleteOldFiles();
629          InstallNewFiles();
630        }
631        finally {
632          PluginManager.Manager.LoadAllPlugins();
633        }
634        InitializePlugins();
635        OnPostUpgradePlugins();
636        OnInstallPlugins();
637        ClearTemporaryFiles();
638      } catch(Exception e) {
639        ShowErrorDialog(e + "");
640      }
641    }
642    private void OnDeletePlugins() {
643      allTags.ForEach(delegate(PluginTag tag) {
644        if(tag.State == PluginState.Installed) {
645          List<PluginAction> actions = GetActionsInvolving(tag);
646          if(actions.Count > 0 && actions[0].Action == ManagerAction.Remove) {
647            PluginManager.Manager.OnDelete(tag.Plugin);
648          }
649        }
650      });
651    }
652    private void OnInstallPlugins() {
653      allTags.ForEach(delegate(PluginTag tag) {
654        if(tag.State == PluginState.Available) {
655          List<PluginAction> actions = GetActionsInvolving(tag);
656          if(actions.Count > 0 && actions[0].Action == ManagerAction.Install) {
657            PluginManager.Manager.OnInstall(tag.Plugin);
658          }
659        }
660      });
661    }
662    private List<string> upgradedPlugins = new List<string>();
663    private void OnPreUpgradePlugins() {
664      upgradedPlugins.Clear();
665      allTags.ForEach(delegate(PluginTag tag) {
666        if(tag.State == PluginState.Upgradeable) {
667          PluginManager.Manager.OnPreUpdate(tag.Plugin);
668
669          // save the name of the plugin in  a list that is used later to call OnPostUpdate for all plugins
670          upgradedPlugins.Add(tag.PluginName);
671        }
672      });
673    }
674    private void OnPostUpgradePlugins() {
675      allTags.ForEach(delegate(PluginTag tag) {
676        if(upgradedPlugins.Contains(tag.PluginName)) {
677          PluginManager.Manager.OnPostUpdate(tag.Plugin);
678        }
679      });
680      upgradedPlugins.Clear();
681    }
682    /// <summary>
683    /// Deletes all files in the directories cacheDir, backupDir, tempDir
684    /// </summary>
685    private void ClearTemporaryFiles() {
686      // can't really handle exceptions here -> let higher layer handle them
687      string[] filenames = Directory.GetFiles(cacheDir, "*", SearchOption.AllDirectories);
688      foreach(string filename in filenames) {
689        File.Delete(filename);
690      }
691      filenames = Directory.GetFiles(backupDir, "*", SearchOption.AllDirectories);
692      foreach(string filename in filenames) {
693        File.Delete(filename);
694      }
695      filenames = Directory.GetFiles(tempDir, "*", SearchOption.AllDirectories);
696      foreach(string filename in filenames) {
697        File.Delete(filename);
698      }
699    }
700
701    /// <summary>
702    /// Extracts zip packages in cacheDir to tempDir and then copies the files from tempDir to pluginDir
703    /// When there is a problem on extraction or later when copying the files to the pluginDir the method
704    /// delets all files that have already been copied from tempDir to pluginDir (the filename exists in both
705    /// locations) and then copies all files in the backup directory back to the plugin directory.
706    /// </summary>
707    /// <returns></returns>
708    private void InstallNewFiles() {
709      try {
710        // extract all packages
711        string[] packages = Directory.GetFiles(cacheDir, "*", SearchOption.AllDirectories);
712        FastZip fastZip = new FastZip();
713        foreach(string package in packages) {
714          fastZip.ExtractZip(package, tempDir, String.Empty);
715        }
716
717        // copy extracted files to plugin dir
718        string[] extractedFiles = Directory.GetFiles(tempDir, "*", SearchOption.AllDirectories);
719        foreach(string extractedFile in extractedFiles) {
720          File.Copy(extractedFile, pluginDir + extractedFile.Remove(0, tempDir.Length));
721        }
722      } catch(Exception e) {
723        infoTextBox.Text = e + "";
724        // remove already copied files
725        string[] extractedFiles = Directory.GetFiles(tempDir, "*", SearchOption.AllDirectories);
726        foreach(string extractedFile in extractedFiles) {
727          string filename = pluginDir + extractedFile.Remove(0, tempDir.Length);
728          if(File.Exists(filename)) {
729            File.Delete(filename);
730          }
731        }
732
733        // restore files from backup
734        string[] backupFiles = Directory.GetFiles(backupDir, "*", SearchOption.AllDirectories);
735        foreach(string backupFile in backupFiles) {
736          File.Copy(backupFile, pluginDir + backupFile.Remove(0, backupDir.Length));
737        }
738
739        throw e;
740      }
741    }
742
743    /// <summary>
744    /// Deletes all files of plugins that have been marked as 'Remove' or 'Upgrade'.
745    /// If there is a problem when removing the files then all files that have been removed earlier
746    /// (the filename existis in backupDir but not in pluginDir) are copied from the backupDir to the pluginDir
747    /// </summary>
748    /// <returns></returns>
749    private void DeleteOldFiles() {
750      try {
751        allTags.ForEach(delegate(PluginTag tag) {
752          List<PluginAction> involvingActions = GetActionsInvolving(tag);
753
754          if((tag.State == PluginState.Upgradeable) ||
755             (involvingActions.Count > 0 && involvingActions[0].Action == ManagerAction.Remove && tag.State != PluginState.Available)) {
756            tag.Plugin.Files.ForEach(delegate(string filename) {
757              File.Delete(filename);
758            });
759          }
760        });
761      } catch(Exception e) {
762        infoTextBox.Text = e + "";
763        // restore deleted files from backup
764        string[] backupFiles = Directory.GetFiles(backupDir, "*", SearchOption.AllDirectories);
765        foreach(string backupFile in backupFiles) {
766          string oldFileName = pluginDir + backupFile.Remove(0, backupDir.Length);
767          if(!File.Exists(oldFileName)) {
768            File.Move(backupFile, oldFileName);
769          }
770        }
771        throw e;
772      }
773    }
774
775    /// <summary>
776    /// Copies all files of plugins that are marked with 'Remove' or 'Upgrade' to the backup directory.
777    /// When there is a problem all files in the backup directory are deleted.
778    /// </summary>
779    /// <returns></returns>
780    private void BackupOldFiles() {
781      try {
782        allTags.ForEach(delegate(PluginTag tag) {
783          List<PluginAction> actionsInvolving = GetActionsInvolving(tag);
784
785          if((tag.State == PluginState.Upgradeable) ||
786             (actionsInvolving.Count > 0 && actionsInvolving[0].Action == ManagerAction.Remove && tag.State != PluginState.Available)) {
787            tag.Plugin.Files.ForEach(delegate(string filename) {
788              File.Copy(filename, backupDir + filename.Remove(0, pluginDir.Length));
789            });
790          }
791        });
792      } catch(Exception e) {
793        infoTextBox.Text = e + "";
794        // delete all files in the backup directory
795        string[] copiedFiles = Directory.GetFiles(backupDir, "*", SearchOption.AllDirectories);
796        string filesString = "";
797        foreach(string fs in copiedFiles) {
798          filesString += fs + "\n";
799        }
800        foreach(string copiedFile in copiedFiles) {
801          File.Delete(copiedFile);
802        }
803        throw e;
804      }
805    }
806
807    private bool DownloadFiles() {
808      DownloaderDialog dialog = new DownloaderDialog();
809      IEnumerator<PluginTag> pluginEnumerator = allTags.GetEnumerator();
810      BackgroundWorker worker = new BackgroundWorker();
811      worker.WorkerReportsProgress = true;
812      worker.WorkerSupportsCancellation = true;
813
814      worker.DoWork += delegate(object sender, DoWorkEventArgs args) {
815        // count number of plugins to download
816        int numberOfPlugins = 0;
817        allTags.ForEach(delegate(PluginTag current) {
818          if(current.UpgradeAvailable()) {
819            numberOfPlugins++;
820          } else {
821            List<PluginAction> actionsInvolving = GetActionsInvolving(current);
822
823            if(actionsInvolving.Count > 0 && actionsInvolving[0].Action == ManagerAction.Install && current.State == PluginState.Available) {
824              numberOfPlugins++;
825            }
826          }
827        });
828
829        // download
830        int downloaded = 0;
831        Invoke((MethodInvoker)delegate() {
832          infoTextBox.Text = "Downloading " + numberOfPlugins + " plugins.\n";
833        });
834
835        allTags.ForEach(delegate(PluginTag current) {
836          if(worker.CancellationPending) {
837            args.Cancel = true;
838            return;
839          }
840          List<PluginAction> actionsInvolving = GetActionsInvolving(current);
841          if(current.UpgradeAvailable() ||
842            (actionsInvolving.Count > 0 && actionsInvolving[0].Action == ManagerAction.Install && current.State == PluginState.Available)) {
843            dialog.SetDownloadDescription("Downloading " + current.PluginName + " ...");
844            Invoke((MethodInvoker)delegate() { infoTextBox.Text += "Downloading " + current.PluginName + " ..."; });
845
846            long nBytes = 0;
847            if(current.UpgradeAvailable()) {
848              nBytes = current.UpgradePluginDescription.Source.DownloadPlugin(current.UpgradePluginDescription);
849            } else {
850              nBytes = current.PluginDescription.Source.DownloadPlugin(current.PluginDescription);
851            }
852
853            worker.ReportProgress(downloaded / numberOfPlugins);
854            Invoke((MethodInvoker)delegate() { infoTextBox.Text += " " + nBytes + " bytes.\n"; });
855          }
856        });
857      };
858
859      worker.ProgressChanged += delegate(object sender, ProgressChangedEventArgs args) {
860        dialog.SetProgress(args.ProgressPercentage);
861      };
862
863      worker.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs args) {
864        if(args.Cancelled) {
865          infoTextBox.Text += "Cancelled downloading plugins";
866          dialog.DialogResult = DialogResult.Cancel;
867        } else if(args.Error != null) {
868          infoTextBox.Text += "Error while downloading plugins:\n" + args.Error;
869          dialog.DialogResult = DialogResult.Cancel;
870        }
871        dialog.DialogResult = DialogResult.OK;
872        dialog.Close();
873      };
874
875
876      dialog.Shown += delegate(object sender, EventArgs args) {
877        worker.RunWorkerAsync();
878      };
879
880      dialog.OnCancel += delegate() {
881        worker.CancelAsync();
882        dialog.SetDownloadDescription("Cancelling download...");
883        infoTextBox.Text += "Cancelled!\n";
884      };
885
886      if(dialog.ShowDialog() == DialogResult.Cancel) {
887        return false;
888      } else {
889        return true;
890      }
891    }
892
893    private void removeButton_Clicked(object sender, EventArgs e) {
894      // get the tag of the selected listViewItem
895      PluginTag actionTag = (PluginTag)listView.SelectedItems[0].Tag;
896      List<PluginAction> rootActions = GetActionsInvolving(actionTag);
897      if(rootActions.Count > 0) {
898        UnmarkRemove(actionTag);
899      } else {
900        MarkRemove(actionTag);
901      }
902
903      // reflect the change of plugin actions in the install/remove buttons
904      UpdateActionButtons(actionTag);
905      // update the plugin detail information of the selected plugin
906      DisplayPluginInfo(actionTag.GetPluginDetails());
907    }
908
909    private void installButton_Clicked(object sender, EventArgs e) {
910      // get the tag of the selected listViewItem
911      PluginTag actionTag = (PluginTag)listView.SelectedItems[0].Tag;
912      List<PluginAction> rootActions = GetActionsInvolving(actionTag);
913
914      if(rootActions.Count > 0) {
915        UnmarkInstall(actionTag);
916      } else {
917        MarkInstall(actionTag);
918      }
919
920      // reflect the change of plugin actions in the install/remove buttons
921      UpdateActionButtons(actionTag);
922      // update the plugin detail information of the selected plugin
923      DisplayPluginInfo(actionTag.GetPluginDetails());
924    }
925
926    private void ShowErrorDialog(string message) {
927      ErrorDialog dialog = new ErrorDialog(message, "Exception");
928      dialog.ShowDialog();
929    }
930
931    private void managePluginSourcesToolStripMenuItem_Click(object sender, EventArgs e) {
932      PluginSourceEditor sourceEditor = new PluginSourceEditor();
933      sourceEditor.ShowDialog();
934    }
935
936    private void refreshPluginListToolStripMenuItem_Click(object sender, EventArgs e) {
937      PluginManager.Manager.UnloadAllPlugins();
938      PluginManager.Manager.LoadAllPlugins();
939
940      InitializePlugins();
941    }
942
943    private void closeToolStripMenuItem_Click(object sender, EventArgs e) {
944      Close();
945    }
946
947    private void installPluginFromFileToolStripMenuItem_Click(object sender, EventArgs args) {
948      OpenFileDialog dialog = new OpenFileDialog();
949      dialog.Multiselect = false;
950      DialogResult result = dialog.ShowDialog();
951      if(result == DialogResult.OK) {
952        string packageName = dialog.FileName;
953        try {
954          ClearTemporaryFiles();
955
956          FastZip fastZip = new FastZip();
957          fastZip.ExtractZip(packageName, cacheDir, String.Empty);
958
959          // check if none of the files exist
960          foreach(string filename in Directory.GetFiles(cacheDir)) {
961            if(File.Exists(pluginDir + filename.Remove(0, cacheDir.Length))) {
962              ShowErrorDialog("Sorry can't install the plugin " + packageName + "\nThe file: " + filename.Remove(0, cacheDir.Length) + " already exist in " + pluginDir + "\nIt seems the plugin is already installed.");
963              ClearTemporaryFiles();
964              return;
965            }
966          }
967
968          PluginManager.Manager.UnloadAllPlugins();
969          // move the files
970          foreach(string filename in Directory.GetFiles(cacheDir)) {
971            File.Move(filename, pluginDir + filename.Remove(0, cacheDir.Length));
972          }
973          PluginManager.Manager.LoadAllPlugins();
974          InitializePlugins();
975
976        } catch(Exception e) {
977          ShowErrorDialog(e + "");
978        } finally {
979          ClearTemporaryFiles();
980        }
981      }
982    }
983
984    private void pluginTreeView_KeyDown(object sender, KeyEventArgs e) {
985      if(e.KeyData == Keys.I && installButton.Enabled) {
986        e.Handled = true;
987        e.SuppressKeyPress = true;
988        installButton_Clicked(sender, e);
989      } else if((e.KeyData == Keys.D || e.KeyData == Keys.Delete) && deleteButton.Enabled) {
990        e.Handled = true;
991        e.SuppressKeyPress = true;
992        removeButton_Clicked(sender, e);
993      }
994    }
995
996    private void listView_MouseDown(object sender, MouseEventArgs e) {
997      // dumb solution to automatically select the node on right clicks which opens the context menu because
998      // per default the treeview doesn't select nodes on right click
999      if(e.Button == MouseButtons.Right) {
1000        ListViewItem clickedItem = listView.GetItemAt(e.X, e.Y);
1001        if (clickedItem != null) {
1002          // clear previous selection
1003          while (listView.SelectedItems.Count > 0)
1004            listView.SelectedItems[0].Selected = false;
1005          // select clicked item
1006          clickedItem.Selected = true;
1007        }
1008      }
1009    }
1010
1011    private void listView_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e) {
1012      if(e.Item.Tag != null && e.IsSelected) {
1013        UpdateActionButtons((PluginTag)e.Item.Tag);
1014        // display the plugin details in the lower pane
1015        DisplayPluginInfo(((PluginTag)e.Item.Tag).GetPluginDetails());
1016      } else if(e.Item.Tag!=null) {
1017        // when an item was 'unselected' or was selected but doesn't represent a plugin then install and remove are not possible
1018        publishButton.Enabled = false;
1019        installButton.Enabled = false;
1020        deleteButton.Enabled = false;
1021        publishMenuItem.Enabled = false;
1022        installMenuItem.Enabled = false;
1023        deleteMenuItem.Enabled = false;
1024      }
1025    }
1026  }
1027}
Note: See TracBrowser for help on using the repository browser.