Free cookie consent management tool by TermsFeed Policy Generator

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

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

fixed #22 by moving the code that creates tree-nodes for availablePlugins into the InitPlugins method

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