Free cookie consent management tool by TermsFeed Policy Generator

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

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