Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 1896 was 1896, checked in by swagner, 15 years ago

Fixed #648

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