Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 2 was 2, checked in by swagner, 16 years ago

Added HeuristicLab 3.0 sources from former SVN repository at revision 52

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