Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 76 was 38, checked in by gkronber, 17 years ago

removed branch "All plugins" in the plugin-manager

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