Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 18 was 18, checked in by gkronber, 16 years ago

made install and delete actions available via keys 'i' and 'd' (ticket #21)

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