Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 29 was 29, checked in by gkronber, 17 years ago
  • refactored pluginInfrastructure
  • fixed #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 && currentPlugin.State == (PluginState.Installed | PluginState.Disabled);
313        });
314
315        if(oldPlugins.Count == 1) {
316          if(oldPlugins[0].PluginVersion < availablePlugin.Version) {
317            upgrades.Add(availablePlugin);
318          }
319        }
320      });
321
322      return upgrades;
323    }
324
325    private List<PluginDescription> FindOverridingPlugins(List<PluginTag> allTags, List<PluginDescription> allAvailablePlugins) {
326      List<PluginDescription> overrides = new List<PluginDescription>();
327      allAvailablePlugins.ForEach(delegate(PluginDescription availablePlugin) {
328        List<PluginTag> currentPlugins = allTags.FindAll(delegate(PluginTag currentPlugin) {
329          return currentPlugin.PluginName == availablePlugin.Name && currentPlugin.State == PluginState.Available;
330        });
331
332        if(currentPlugins.Count == 1) {
333          if(currentPlugins[0].PluginVersion < availablePlugin.Version) {
334            overrides.Add(availablePlugin);
335          }
336        }
337      });
338
339      return overrides;
340    }
341
342    private void RebuildActionHulls() {
343      Dictionary<PluginTag, PluginAction> oldActions = new Dictionary<PluginTag, PluginAction>(actions);
344      actions.Clear();
345
346      foreach(PluginAction action in oldActions.Values) {
347        if(action.Action == ManagerAction.Install) {
348          MarkInstall(action.Plugin);
349        } else if(action.Action == ManagerAction.Remove) {
350          MarkRemove(action.Plugin);
351        } else
352          throw new InvalidOperationException();
353      }
354
355      // update the GUI to represent new state of the selected plugin
356      if(pluginTreeView.SelectedNode != null && pluginTreeView.SelectedNode.Tag is PluginTag) {
357        UpdateActionButtons((PluginTag)pluginTreeView.SelectedNode.Tag);
358        DisplayPluginInfo(((PluginTag)pluginTreeView.SelectedNode.Tag).GetPluginDetails());
359      }
360    }
361
362
363
364    private void MarkInstall(PluginTag actionTag) {
365      if(!CheckInstallConflicts(actionTag)) {
366        CreateNewInstallAction(actionTag);
367      } else {
368        HandleInstallConflict(actionTag);
369      }
370    }
371
372    private void UnmarkInstall(PluginTag actionTag) {
373      if(!CheckNoActionConflict(actionTag)) {
374        List<PluginAction> rootActions = GetActionsInvolving(actionTag);
375        PluginAction rootAction = rootActions[0];
376        actions.Remove(rootAction.Plugin);
377        UpdateTreeNodes(rootAction.Hull);
378      } else {
379        HandleNoActionConflict(actionTag);
380      }
381    }
382
383    private void HandleNoActionConflict(PluginTag actionTag) {
384      List<PluginAction> conflictingActions = GetOverlappingActions(actionTag, ManagerAction.Any);
385      PluginAction theAction = GetSmallestActionInvolving(actionTag, conflictingActions);
386      conflictingActions.Remove(theAction);
387      string action = theAction.GetInverseActionString();
388      DialogResult userResult = ShowGenericConflictDialog(action, theAction, conflictingActions);
389      if(userResult == DialogResult.OK) {
390        conflictingActions.ForEach(delegate(PluginAction conflictingAction) {
391          actions.Remove(conflictingAction.Plugin);
392          UpdateTreeNodes(conflictingAction.Hull);
393        });
394
395        PluginAction rootAction = GetSmallestActionInvolving(actionTag, GetOverlappingActions(actionTag, ManagerAction.Any));
396        actions.Remove(rootAction.Plugin);
397        UpdateTreeNodes(rootAction.Hull);
398      }
399    }
400
401    private bool CheckNoActionConflict(PluginTag actionTag) {
402      return GetOverlappingActions(actionTag, ManagerAction.Any).Count > 1;
403    }
404
405    private void MarkRemove(PluginTag actionTag) {
406      if(!CheckRemoveConflicts(actionTag)) {
407        CreateNewRemoveAction(actionTag);
408      } else {
409        HandleRemoveConflict(actionTag);
410      }
411    }
412
413    private void UnmarkRemove(PluginTag pluginTag) {
414      if(!CheckNoActionConflict(pluginTag)) {
415        List<PluginAction> rootActions = GetActionsInvolving(pluginTag);
416        // if there is no conflict then there can only be one action connected to the pluginTag
417        PluginAction rootAction = rootActions[0];
418        // kill the root action
419        actions.Remove(rootAction.Plugin);
420        UpdateTreeNodes(rootAction.Hull);
421      } else {
422        HandleNoActionConflict(pluginTag);
423      }
424    }
425
426    private void CreateNewRemoveAction(PluginTag actionTag) {
427      CreateNewAction(actionTag, ManagerAction.Remove, actionTag.GetDependentTags());
428    }
429
430    private void CreateNewInstallAction(PluginTag actionTag) {
431      CreateNewAction(actionTag, ManagerAction.Install, actionTag.GetDependencyTags());
432    }
433
434    private void CreateNewAction(PluginTag actionTag, ManagerAction action, List<PluginTag> hull) {
435      PluginAction newAction = new PluginAction();
436      newAction.Action = action;
437      newAction.Plugin = actionTag;
438      newAction.Hull = hull;
439      newAction.Hull.Add(actionTag);
440
441
442      actions[actionTag] = newAction;
443      UpdateTreeNodes(newAction.Hull);
444    }
445
446    private bool CheckRemoveConflicts(PluginTag actionTag) {
447      return GetOverlappingActions(actionTag, ManagerAction.Install).Count > 0;
448    }
449
450    private void HandleRemoveConflict(PluginTag actionTag) {
451      List<PluginAction> conflictingActions = GetOverlappingActions(actionTag, ManagerAction.Install);
452
453      // create a planned action to display in the conflict message box
454      PluginAction plannedRemoveAction = new PluginAction();
455      plannedRemoveAction.Action = ManagerAction.Remove;
456      plannedRemoveAction.Plugin = actionTag;
457
458      DialogResult userResult = ShowGenericConflictDialog("Remove", plannedRemoveAction, conflictingActions);
459      if(userResult == DialogResult.OK) {
460        // if user says ok then remove the old actions and create a new remove action
461        conflictingActions.ForEach(delegate(PluginAction conflictingAction) {
462          actions.Remove(conflictingAction.Plugin);
463          UpdateTreeNodes(conflictingAction.Hull);
464        });
465
466        CreateNewRemoveAction(actionTag);
467      }
468    }
469
470    private bool CheckInstallConflicts(PluginTag actionTag) {
471      return GetOverlappingActions(actionTag, ManagerAction.Remove).Count > 0;
472    }
473
474    private void HandleInstallConflict(PluginTag actionTag) {
475      List<PluginAction> conflictingActions = GetOverlappingActions(actionTag, ManagerAction.Remove);
476
477      // create a planned action to display in the conflict message box
478      PluginAction plannedInstallAction = new PluginAction();
479      plannedInstallAction.Action = ManagerAction.Install;
480      plannedInstallAction.Plugin = actionTag;
481
482      DialogResult userResult = ShowGenericConflictDialog("Install", plannedInstallAction, conflictingActions);
483
484      if(userResult == DialogResult.OK) {
485        conflictingActions.ForEach(delegate(PluginAction conflictingAction) {
486          actions.Remove(conflictingAction.Plugin);
487          UpdateTreeNodes(conflictingAction.Hull);
488        });
489
490        CreateNewInstallAction(actionTag);
491      }
492    }
493
494    private DialogResult ShowGenericConflictDialog(string action, PluginAction theAction, List<PluginAction> conflictingActions) {
495      string message = "The actions:\n\n";
496      conflictingActions.ForEach(delegate(PluginAction conflictingAction) {
497        message += " - " + conflictingAction.Action + " " + conflictingAction.Plugin.PluginName + "\n";
498      });
499      message += "\nconflict with the action: " + action + " " + theAction.Plugin.PluginName + ".\n\n";
500      message += "\nSolution:\n";
501      conflictingActions.ForEach(delegate(PluginAction conflictingAction) {
502        message += " - " + conflictingAction.GetInverseActionString() + " " + conflictingAction.Plugin.PluginName + "\n";
503      });
504      message += " - " + action + " " + theAction.Plugin.PluginName + "\n\n";
505      message += "Do you want to continue?";
506
507      return MessageBox.Show(message, "Resolve conflicting actions", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning);
508
509    }
510
511    private List<PluginAction> GetActionsInvolving(PluginTag actionTag) {
512      List<PluginAction> filteredActions = new List<PluginAction>();
513      foreach(PluginAction action in actions.Values) {
514        if(action.Hull.Contains(actionTag)) {
515          filteredActions.Add(action);
516        }
517      }
518      return filteredActions;
519    }
520
521    private PluginAction GetSmallestActionInvolving(PluginTag actionTag, List<PluginAction> actions) {
522      PluginAction smallestAction;
523      // if there is an action defined for which actionTag is the root then us it as root tag
524      // otherwise use the root of the action that contains pluginTag in its hull and has the smallest hull of all actions
525      smallestAction = actions.Find(delegate(PluginAction action) {
526        return action.Plugin == actionTag;
527      });
528      // if nothing is found then get the action with the smallest hull
529      if(smallestAction == null) {
530        // find the action with the smallest hull
531        smallestAction = actions[0];
532        actions.ForEach(delegate(PluginAction action) {
533          if(action.Hull.Count < smallestAction.Hull.Count)
534            smallestAction = action;
535        });
536      }
537
538      return smallestAction;
539    }
540
541    /// <summary>
542    /// Similar to GetRootActions but filter only specific ManagerActions
543    /// </summary>
544    /// <param name="pluginTag"></param>
545    /// <param name="overlapAction"></param>
546    /// <returns></returns>
547    private List<PluginAction> GetOverlappingActions(PluginTag actionTag, ManagerAction overlapAction) {
548      List<PluginAction> overlappingActions = new List<PluginAction>();
549      foreach(PluginAction action in actions.Values) {
550        if(((action.Action & overlapAction) > 0) &&
551          action.Hull.Contains(actionTag)) {
552          overlappingActions.Add(action);
553        }
554      }
555
556      return overlappingActions;
557    }
558
559    private void UpdateTreeNodes(List<PluginTag> hull) {
560      hull.ForEach(delegate(PluginTag tag) {
561        FindPluginNodes(tag).ForEach(delegate(TreeNode node) {
562          ManagerAction action = GetAction(tag);
563          if(action != ManagerAction.None) {
564            node.Text = tag.PluginName + " - Action: " + action;
565            if(action == ManagerAction.Remove) {
566              node.ImageIndex = 3;
567              node.SelectedImageIndex = 3;
568            } else if(action == ManagerAction.Install) {
569              node.ImageIndex = 2;
570              node.SelectedImageIndex = 2;
571            }
572          } else if(tag.State == PluginState.Upgradeable) {
573            node.Text = tag.PluginName + " - Upgrade: " + tag.PluginVersion + " -> " + tag.UpgradePluginDescription.Version;
574            node.ImageIndex = 2;
575            node.SelectedImageIndex = 2;
576          } else {
577            node.Text = tag.PluginName;
578            node.ImageIndex = 0;
579            node.SelectedImageIndex = 0;
580          }
581        });
582      });
583    }
584
585    private void UpdateActionButtons(PluginTag tag) {
586
587      publishButton.Enabled = (tag.State & PluginState.Installed) == PluginState.Installed;
588      installButton.Enabled = (tag.State & PluginState.Available) == PluginState.Available;
589      deleteButton.Enabled = (tag.State & (PluginState.Installed | PluginState.Upgradeable | PluginState.Disabled)) != 0;
590
591      installButton.Checked = GetAction(tag) == ManagerAction.Install;
592      deleteButton.Checked = GetAction(tag) == ManagerAction.Remove;
593
594      publishMenuItem.Enabled = publishButton.Enabled;
595      installMenuItem.Enabled = installButton.Enabled;
596      deleteMenuItem.Enabled = deleteButton.Enabled;
597      installMenuItem.Checked = installButton.Checked;
598      deleteMenuItem.Checked = deleteButton.Checked;
599
600    }
601
602    private ManagerAction GetAction(PluginTag tag) {
603      ManagerAction plannedAction = ManagerAction.None;
604      if(actions.ContainsKey(tag)) {
605        plannedAction = actions[tag].Action;
606      } else {
607        foreach(PluginAction action in actions.Values) {
608          if(action.Hull.Contains(tag)) {
609            plannedAction = action.Action;
610          }
611        }
612      }
613
614      if(plannedAction == ManagerAction.Install
615        && (tag.State == PluginState.Installed || tag.State == PluginState.Upgradeable)) {
616        return ManagerAction.None;
617      } else if(plannedAction == ManagerAction.Remove
618        && tag.State == PluginState.Available) {
619        return ManagerAction.None;
620      } else {
621        return plannedAction;
622      }
623    }
624
625    private List<TreeNode> FindPluginNodes(PluginTag pluginTag) {
626      List<TreeNode> nodes = new List<TreeNode>();
627      foreach(TreeNode rootNode in new TreeNode[] { installedPlugins, availablePlugins, allPlugins, disabledPlugins }) {
628        foreach(TreeNode node in rootNode.Nodes) {
629          if(pluginTag.Equals(node.Tag)) {
630            nodes.Add(node);
631          }
632        }
633      }
634      return nodes;
635    }
636
637
638
639    private void DisplayPluginInfo(string pluginInformation) {
640      infoTextBox.Text = pluginInformation;
641    }
642
643
644    private void upgradeButton_Click(object sender, EventArgs args) {
645      try {
646        ClearTemporaryFiles();
647        if(!DownloadFiles()) {
648          return;
649        }
650        OnDeletePlugins();
651        OnPreUpgradePlugins();
652        PluginManager.Manager.UnloadAllPlugins();
653        BackupOldFiles();
654        DeleteOldFiles();
655        InstallNewFiles();
656        PluginManager.Manager.LoadAllPlugins();
657        InitializePlugins();
658        OnPostUpgradePlugins();
659        OnInstallPlugins();
660        ClearTemporaryFiles();
661      } catch(Exception e) {
662        ShowErrorDialog(e + "");
663      }
664    }
665    private void OnDeletePlugins() {
666      allTags.ForEach(delegate(PluginTag tag) {
667        if(tag.State == PluginState.Installed) {
668          List<PluginAction> actions = GetActionsInvolving(tag);
669          if(actions.Count > 0 && actions[0].Action == ManagerAction.Remove) {
670            PluginManager.Manager.OnDelete(tag.Plugin);
671          }
672        }
673      });
674    }
675    private void OnInstallPlugins() {
676      allTags.ForEach(delegate(PluginTag tag) {
677        if(tag.State == PluginState.Available) {
678          List<PluginAction> actions = GetActionsInvolving(tag);
679          if(actions.Count > 0 && actions[0].Action == ManagerAction.Install) {
680            PluginManager.Manager.OnInstall(tag.Plugin);
681          }
682        }
683      });
684    }
685
686
687    private List<string> upgradedPlugins = new List<string>();
688    private void OnPreUpgradePlugins() {
689      upgradedPlugins.Clear();
690      allTags.ForEach(delegate(PluginTag tag) {
691        if(tag.State == PluginState.Upgradeable) {
692          PluginManager.Manager.OnPreUpdate(tag.Plugin);
693
694          // save the name of the plugin in  a list that is used later to call OnPostUpdate for all plugins
695          upgradedPlugins.Add(tag.PluginName);
696        }
697      });
698    }
699
700    private void OnPostUpgradePlugins() {
701      allTags.ForEach(delegate(PluginTag tag) {
702        if(upgradedPlugins.Contains(tag.PluginName)) {
703          PluginManager.Manager.OnPostUpdate(tag.Plugin);
704        }
705      });
706      upgradedPlugins.Clear();
707    }
708
709    /// <summary>
710    /// Deletes all files in the directories cacheDir, backupDir, tempDir
711    /// </summary>
712    private void ClearTemporaryFiles() {
713      // can't really handle exceptions here -> let higher layer handle them
714      string[] filenames = Directory.GetFiles(cacheDir, "*", SearchOption.AllDirectories);
715      foreach(string filename in filenames) {
716        File.Delete(filename);
717      }
718      filenames = Directory.GetFiles(backupDir, "*", SearchOption.AllDirectories);
719      foreach(string filename in filenames) {
720        File.Delete(filename);
721      }
722      filenames = Directory.GetFiles(tempDir, "*", SearchOption.AllDirectories);
723      foreach(string filename in filenames) {
724        File.Delete(filename);
725      }
726    }
727
728    /// <summary>
729    /// Extracts zip packages in cacheDir to tempDir and then copies the files from tempDir to pluginDir
730    /// When there is a problem on extraction or later when copying the files to the pluginDir the method
731    /// delets all files that have already been copied from tempDir to pluginDir (the filename exists in both
732    /// locations) and then copies all files in the backup directory back to the plugin directory.
733    /// </summary>
734    /// <returns></returns>
735    private void InstallNewFiles() {
736      try {
737        // extract all packages
738        string[] packages = Directory.GetFiles(cacheDir, "*", SearchOption.AllDirectories);
739        FastZip fastZip = new FastZip();
740        foreach(string package in packages) {
741          fastZip.ExtractZip(package, tempDir, String.Empty);
742        }
743
744        // copy extracted files to plugin dir
745        string[] extractedFiles = Directory.GetFiles(tempDir, "*", SearchOption.AllDirectories);
746        foreach(string extractedFile in extractedFiles) {
747          File.Copy(extractedFile, pluginDir + extractedFile.Remove(0, tempDir.Length));
748        }
749      } catch(Exception e) {
750        infoTextBox.Text = e + "";
751        // remove already copied files
752        string[] extractedFiles = Directory.GetFiles(tempDir, "*", SearchOption.AllDirectories);
753        foreach(string extractedFile in extractedFiles) {
754          string filename = pluginDir + extractedFile.Remove(0, tempDir.Length);
755          if(File.Exists(filename)) {
756            File.Delete(filename);
757          }
758        }
759
760        // restore files from backup
761        string[] backupFiles = Directory.GetFiles(backupDir, "*", SearchOption.AllDirectories);
762        foreach(string backupFile in backupFiles) {
763          File.Copy(backupFile, pluginDir + backupFile.Remove(0, backupDir.Length));
764        }
765
766        throw e;
767      }
768    }
769
770    /// <summary>
771    /// Deletes all files of plugins that have been marked as 'Remove' or 'Upgrade'.
772    /// If there is a problem when removing the files then all files that have been removed earlier
773    /// (the filename existis in backupDir but not in pluginDir) are copied from the backupDir to the pluginDir
774    /// </summary>
775    /// <returns></returns>
776    private void DeleteOldFiles() {
777      try {
778        allTags.ForEach(delegate(PluginTag tag) {
779          List<PluginAction> involvingActions = GetActionsInvolving(tag);
780
781          if(tag.State == PluginState.Upgradeable || (involvingActions.Count > 0 && involvingActions[0].Action == ManagerAction.Remove)) {
782            tag.Plugin.Files.ForEach(delegate(string filename) {
783              File.Delete(filename);
784            });
785          }
786        });
787      } catch(Exception e) {
788        infoTextBox.Text = e + "";
789        // restore deleted files from backup
790        string[] backupFiles = Directory.GetFiles(backupDir, "*", SearchOption.AllDirectories);
791        foreach(string backupFile in backupFiles) {
792          string oldFileName = pluginDir + backupFile.Remove(0, backupDir.Length);
793          if(!File.Exists(oldFileName)) {
794            File.Move(backupFile, oldFileName);
795          }
796        }
797        throw e;
798      }
799    }
800
801    /// <summary>
802    /// Copies all files of plugins that are marked with 'Remove' or 'Upgrade' to the backup directory.
803    /// When there is a problem all files in the backup directory are deleted.
804    /// </summary>
805    /// <returns></returns>
806    private void BackupOldFiles() {
807      try {
808        allTags.ForEach(delegate(PluginTag tag) {
809          List<PluginAction> actionsInvolving = GetActionsInvolving(tag);
810
811          if(tag.State == PluginState.Upgradeable || (actionsInvolving.Count > 0 && actionsInvolving[0].Action == ManagerAction.Remove)) {
812            tag.Plugin.Files.ForEach(delegate(string filename) {
813              File.Copy(filename, backupDir + filename.Remove(0, pluginDir.Length));
814            });
815          }
816        });
817      } catch(Exception e) {
818        infoTextBox.Text = e + "";
819        // delete all files in the backup directory
820        string[] copiedFiles = Directory.GetFiles(backupDir, "*", SearchOption.AllDirectories);
821        string filesString = "";
822        foreach(string fs in copiedFiles) {
823          filesString += fs + "\n";
824        }
825        foreach(string copiedFile in copiedFiles) {
826          File.Delete(copiedFile);
827        }
828        throw e;
829      }
830    }
831
832    private bool DownloadFiles() {
833      DownloaderDialog dialog = new DownloaderDialog();
834      IEnumerator<PluginTag> pluginEnumerator = allTags.GetEnumerator();
835      BackgroundWorker worker = new BackgroundWorker();
836      worker.WorkerReportsProgress = true;
837      worker.WorkerSupportsCancellation = true;
838
839      worker.DoWork += delegate(object sender, DoWorkEventArgs args) {
840        // count number of plugins to download
841        int numberOfPlugins = 0;
842        allTags.ForEach(delegate(PluginTag current) {
843          if(current.UpgradeAvailable()) {
844            numberOfPlugins++;
845          } else {
846            List<PluginAction> actionsInvolving = GetActionsInvolving(current);
847
848            if(actionsInvolving.Count > 0 && actionsInvolving[0].Action == ManagerAction.Install && current.State == PluginState.Available) {
849              numberOfPlugins++;
850            }
851          }
852        });
853
854        // download
855        int downloaded = 0;
856        Invoke((MethodInvoker)delegate() {
857          infoTextBox.Text = "Downloading " + numberOfPlugins + " plugins.\n";
858        });
859
860        allTags.ForEach(delegate(PluginTag current) {
861          if(worker.CancellationPending) {
862            args.Cancel = true;
863            return;
864          }
865          List<PluginAction> actionsInvolving = GetActionsInvolving(current);
866          if(current.UpgradeAvailable() ||
867            (actionsInvolving.Count > 0 && actionsInvolving[0].Action == ManagerAction.Install && current.State == PluginState.Available)) {
868            dialog.SetDownloadDescription("Downloading " + current.PluginName + " ...");
869            Invoke((MethodInvoker)delegate() { infoTextBox.Text += "Downloading " + current.PluginName + " ..."; });
870
871            long nBytes = 0;
872            if(current.UpgradeAvailable()) {
873              nBytes = current.UpgradePluginDescription.Source.DownloadPlugin(current.UpgradePluginDescription);
874            } else {
875              nBytes = current.PluginDescription.Source.DownloadPlugin(current.PluginDescription);
876            }
877
878            worker.ReportProgress(downloaded / numberOfPlugins);
879            Invoke((MethodInvoker)delegate() { infoTextBox.Text += " " + nBytes + " bytes.\n"; });
880          }
881        });
882      };
883
884      worker.ProgressChanged += delegate(object sender, ProgressChangedEventArgs args) {
885        dialog.SetProgress(args.ProgressPercentage);
886      };
887
888      worker.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs args) {
889        if(args.Cancelled) {
890          infoTextBox.Text += "Cancelled downloading plugins";
891          dialog.DialogResult = DialogResult.Cancel;
892        } else if(args.Error != null) {
893          infoTextBox.Text += "Error while downloading plugins:\n" + args.Error;
894          dialog.DialogResult = DialogResult.Cancel;
895        }
896        dialog.DialogResult = DialogResult.OK;
897        dialog.Close();
898      };
899
900
901      dialog.Shown += delegate(object sender, EventArgs args) {
902        worker.RunWorkerAsync();
903      };
904
905      dialog.OnCancel += delegate() {
906        worker.CancelAsync();
907        dialog.SetDownloadDescription("Cancelling download...");
908        infoTextBox.Text += "Cancelled!\n";
909      };
910
911      if(dialog.ShowDialog() == DialogResult.Cancel) {
912        return false;
913      } else {
914        return true;
915      }
916    }
917
918    private void removeButton_Clicked(object sender, EventArgs e) {
919      // get the tag of the selected treeNode
920      PluginTag actionTag = (PluginTag)pluginTreeView.SelectedNode.Tag;
921      List<PluginAction> rootActions = GetActionsInvolving(actionTag);
922      if(rootActions.Count > 0) {
923        UnmarkRemove(actionTag);
924      } else {
925        MarkRemove(actionTag);
926      }
927
928      // reflect the change of plugin actions in the install/remove buttons
929      UpdateActionButtons(actionTag);
930      // update the plugin detail information of the selected plugin
931      DisplayPluginInfo(actionTag.GetPluginDetails());
932    }
933
934    private void installButton_Clicked(object sender, EventArgs e) {
935      // get the tag of the selected treeNode
936      PluginTag actionTag = (PluginTag)pluginTreeView.SelectedNode.Tag;
937      List<PluginAction> rootActions = GetActionsInvolving(actionTag);
938
939      if(rootActions.Count > 0) {
940        UnmarkInstall(actionTag);
941      } else {
942        MarkInstall(actionTag);
943      }
944
945      // reflect the change of plugin actions in the install/remove buttons
946      UpdateActionButtons(actionTag);
947      // update the plugin detail information of the selected plugin
948      DisplayPluginInfo(actionTag.GetPluginDetails());
949    }
950
951    private void ShowErrorDialog(string message) {
952      ErrorDialog dialog = new ErrorDialog(message, "Exception");
953      dialog.ShowDialog();
954    }
955
956    private void managePluginSourcesToolStripMenuItem_Click(object sender, EventArgs e) {
957      PluginSourceEditor sourceEditor = new PluginSourceEditor();
958      sourceEditor.ShowDialog();
959    }
960
961    private void refreshPluginListToolStripMenuItem_Click(object sender, EventArgs e) {
962      PluginManager.Manager.UnloadAllPlugins();
963      PluginManager.Manager.LoadAllPlugins();
964
965      InitializePlugins();
966    }
967
968    private void pluginTreeView_BeforeSelect(object sender, TreeViewCancelEventArgs e) {
969      if(e.Node.Tag != null) {
970
971        UpdateActionButtons((PluginTag)e.Node.Tag);
972        // display the plugin details in the lower pane
973        DisplayPluginInfo(((PluginTag)e.Node.Tag).GetPluginDetails());
974
975      } else {
976        // when a node was selected that doesn't represent a plugin (root node) then install and remove are not possible
977        publishButton.Enabled = false;
978        installButton.Enabled = false;
979        deleteButton.Enabled = false;
980      }
981    }
982
983    private void pluginTreeView_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e) {
984      // dumb solution to automatically select the node on right clicks which opens the context menu because
985      // per default the treeview doesn't select nodes on right click
986      if(e.Button == MouseButtons.Right) {
987        pluginTreeView.SelectedNode = e.Node;
988      }
989    }
990
991    private void closeToolStripMenuItem_Click(object sender, EventArgs e) {
992      Close();
993    }
994
995    private void installPluginFromFileToolStripMenuItem_Click(object sender, EventArgs args) {
996      OpenFileDialog dialog = new OpenFileDialog();
997      dialog.Multiselect = false;
998      DialogResult result = dialog.ShowDialog();
999      if(result == DialogResult.OK) {
1000        string packageName = dialog.FileName;
1001        try {
1002          ClearTemporaryFiles();
1003
1004          FastZip fastZip = new FastZip();
1005          fastZip.ExtractZip(packageName, cacheDir, String.Empty);
1006
1007          // check if none of the files exist
1008          foreach(string filename in Directory.GetFiles(cacheDir)) {
1009            if(File.Exists(pluginDir + filename.Remove(0, cacheDir.Length))) {
1010              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.");
1011              ClearTemporaryFiles();
1012              return;
1013            }
1014          }
1015
1016          PluginManager.Manager.UnloadAllPlugins();
1017          // move the files
1018          foreach(string filename in Directory.GetFiles(cacheDir)) {
1019            File.Move(filename, pluginDir + filename.Remove(0, cacheDir.Length));
1020          }
1021          PluginManager.Manager.LoadAllPlugins();
1022          InitializePlugins();
1023
1024        } catch(Exception e) {
1025          ShowErrorDialog(e + "");
1026        } finally {
1027          ClearTemporaryFiles();
1028        }
1029      }
1030    }
1031
1032    private void pluginTreeView_KeyPress(object sender, KeyPressEventArgs e) {
1033      e.Handled = true;
1034      if(e.KeyChar == 'i' && installButton.Enabled) {
1035        installButton_Clicked(sender, e);
1036      } else if(e.KeyChar == 'd' && deleteButton.Enabled) {
1037        removeButton_Clicked(sender, e);
1038      }
1039    }
1040  }
1041}
Note: See TracBrowser for help on using the repository browser.