Free cookie consent management tool by TermsFeed Policy Generator

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

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

removed waring dialog when for deleting files (ticket #12)

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