From dd7dcdbb0a7438a90b7c844871d4af2dfda87f33 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 27 Nov 2005 16:09:46 +0000 Subject: [PATCH] Added support for installing and uninstalling user AddIns. git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@809 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../AddInManager/Project/Src/AddInControl.cs | 37 +++- .../Project/Src/InstallableAddIn.cs | 82 ++++++++- .../AddInManager/Project/Src/ManagerForm.cs | 71 ++++---- .../Project/Debugger.AddIn.addin | 2 +- .../Core/Project/Src/AddInTree/AddIn/AddIn.cs | 4 +- .../Project/Src/AddInTree/AddInManager.cs | 161 +++++++++++++++++- .../Core/Project/Src/AddInTree/AddInTree.cs | 5 +- .../Core/Project/Src/AddInTree/CoreStartup.cs | 35 ++-- src/Main/StartUp/Project/SharpDevelopMain.cs | 9 +- 9 files changed, 336 insertions(+), 70 deletions(-) diff --git a/src/AddIns/Misc/AddInManager/Project/Src/AddInControl.cs b/src/AddIns/Misc/AddInManager/Project/Src/AddInControl.cs index 99701f7fef..61c103051f 100644 --- a/src/AddIns/Misc/AddInManager/Project/Src/AddInControl.cs +++ b/src/AddIns/Misc/AddInManager/Project/Src/AddInControl.cs @@ -17,6 +17,7 @@ namespace ICSharpCode.AddInManager public class AddInControl : Control { AddIn addIn; + bool isExternal; public AddIn AddIn { get { @@ -28,7 +29,11 @@ namespace ICSharpCode.AddInManager { this.addIn = addIn; this.BackColor = SystemColors.Window; - this.Size = new Size(100, 40); + + isExternal = !FileUtility.IsBaseDirectory(FileUtility.ApplicationRootPath, addIn.FileName) + && !FileUtility.IsBaseDirectory(PropertyService.ConfigDirectory, addIn.FileName); + + this.ClientSize = new Size(100, isExternal ? 35 + pathHeight : 35); this.SetStyle(ControlStyles.Selectable, true); this.SetStyle(ControlStyles.UserPaint, true); this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true); @@ -107,10 +112,30 @@ namespace ICSharpCode.AddInManager } RectangleF textBounds = bounds; textBounds.Offset(innerMargin, innerMargin); - textBounds.Inflate(-innerMargin * 2, -innerMargin * 2); - g.DrawString(description, Font, textBrush, textBounds); + textBounds.Inflate(-innerMargin * 2, -innerMargin * 2 + 2); + if (isExternal) + textBounds.Height -= pathHeight; + using (StringFormat sf = new StringFormat(StringFormatFlags.LineLimit)) { + sf.Trimming = StringTrimming.EllipsisWord; + g.DrawString(description, Font, textBrush, textBounds, sf); + } + if (isExternal) { + textBounds.Y = textBounds.Bottom + 2; + textBounds.Height = pathHeight + 2; + using (Font font = new Font(Font.Name, 7, FontStyle.Italic)) { + using (StringFormat sf = new StringFormat(StringFormatFlags.NoWrap)) { + sf.Trimming = StringTrimming.EllipsisPath; + sf.Alignment = StringAlignment.Far; + g.DrawString(addIn.FileName, font, + selected ? SystemBrushes.HighlightText : SystemBrushes.ControlText, + textBounds, sf); + } + } + } } + const int pathHeight = 10; + string GetText(out Brush textBrush) { switch (addIn.Action) { @@ -137,6 +162,12 @@ namespace ICSharpCode.AddInManager case AddInAction.Update: textBrush = SystemBrushes.ActiveCaption; return "AddIn will be updated after restarting SharpDevelop"; + case AddInAction.InstalledTwice: + textBrush = Brushes.Red; + return "Duplicate installation"; + case AddInAction.DependencyError: + textBrush = Brushes.Red; + return "Dependency failed"; default: textBrush = Brushes.Yellow; return addIn.Action.ToString(); diff --git a/src/AddIns/Misc/AddInManager/Project/Src/InstallableAddIn.cs b/src/AddIns/Misc/AddInManager/Project/Src/InstallableAddIn.cs index 1512b52fe6..2da52a2ba4 100644 --- a/src/AddIns/Misc/AddInManager/Project/Src/InstallableAddIn.cs +++ b/src/AddIns/Misc/AddInManager/Project/Src/InstallableAddIn.cs @@ -1,12 +1,15 @@ -/* - * Created by SharpDevelop. - * User: Daniel Grunwald - * Date: 27.11.2005 - * Time: 11:42 - */ +// +// 2002-2005 AlphaSierraPapa +// GNU General Public License +// +// $Revision$ +// using System; +using System.Collections.Generic; +using System.IO; using ICSharpCode.Core; +using ICSharpCode.SharpZipLib.Zip; namespace ICSharpCode.AddInManager { @@ -27,7 +30,12 @@ namespace ICSharpCode.AddInManager this.fileName = fileName; this.isPackage = isPackage; if (isPackage) { - throw new NotImplementedException(); + ZipFile file = new ZipFile(fileName); + try { + LoadAddInFromZip(file); + } finally { + file.Close(); + } } else { addIn = AddIn.Load(fileName); } @@ -35,13 +43,71 @@ namespace ICSharpCode.AddInManager throw new AddInLoadException("The AddIn must have an for use with the AddIn-Manager."); } + void LoadAddInFromZip(ZipFile file) + { + ZipEntry addInEntry = null; + foreach (ZipEntry entry in file) { + if (entry.Name.EndsWith(".addin")) { + if (addInEntry != null) + throw new AddInLoadException("The package may only contain one .addin file."); + addInEntry = entry; + } + } + if (addInEntry == null) + throw new AddInLoadException("The package must contain one .addin file."); + using (Stream s = file.GetInputStream(addInEntry)) { + using (StreamReader r = new StreamReader(s)) { + addIn = AddIn.Load(r); + } + } + } + public void Install() { + foreach (string identity in addIn.Manifest.Identities.Keys) { + ICSharpCode.Core.AddInManager.AbortRemoveUserAddInOnNextStart(identity); + } if (isPackage) { - throw new NotImplementedException(); + string targetDir = Path.Combine(ICSharpCode.Core.AddInManager.AddInInstallTemp, + addIn.Manifest.PrimaryIdentity); + if (Directory.Exists(targetDir)) + Directory.Delete(targetDir, true); + Directory.CreateDirectory(targetDir); + FastZip fastZip = new FastZip(); + fastZip.CreateEmptyDirectories = true; + fastZip.ExtractZip(fileName, targetDir, null); + + addIn.Action = AddInAction.Install; + AddInTree.InsertAddIn(addIn); } else { ICSharpCode.Core.AddInManager.AddExternalAddIns(new AddIn[] { addIn }); } } + + public static void Uninstall(IList addIns) + { + foreach (AddIn addIn in addIns) { + foreach (string identity in addIn.Manifest.Identities.Keys) { + // delete from installation temp (if installation or update is pending) + string targetDir = Path.Combine(ICSharpCode.Core.AddInManager.AddInInstallTemp, + identity); + if (Directory.Exists(targetDir)) + Directory.Delete(targetDir, true); + + // remove the user AddIn + targetDir = Path.Combine(ICSharpCode.Core.AddInManager.UserAddInPath, identity); + if (Directory.Exists(targetDir)) { + if (!addIn.Enabled) { + try { + Directory.Delete(targetDir, true); + continue; + } catch { + } + } + ICSharpCode.Core.AddInManager.RemoveUserAddInOnNextStart(identity); + } + } + } + } } } diff --git a/src/AddIns/Misc/AddInManager/Project/Src/ManagerForm.cs b/src/AddIns/Misc/AddInManager/Project/Src/ManagerForm.cs index 6d6f5cb6ca..2ed3333cf1 100644 --- a/src/AddIns/Misc/AddInManager/Project/Src/ManagerForm.cs +++ b/src/AddIns/Misc/AddInManager/Project/Src/ManagerForm.cs @@ -47,17 +47,19 @@ namespace ICSharpCode.AddInManager ICSharpCode.SharpDevelop.Gui.FormLocationHelper.Apply(this, "AddInManager.WindowBounds", true); CreateAddInList(); - splitContainer.Panel1.Paint += delegate(object sender, PaintEventArgs e) { - if (visibleAddInCount == 0) { - Rectangle rect = splitContainer.Panel1.ClientRectangle; - rect.Offset(16, 16); - rect.Inflate(-32, -32); - e.Graphics.DrawString("You don't have any AddIns installed.\n" + - "Download an AddIn from the Internet, then click 'Install AddIn' and " + - "choose the downloaded file to install it.", - Font, SystemBrushes.ControlText, rect); - } - }; + } + + void OnSplitContainerPanel1Paint(object sender, PaintEventArgs e) + { + if (visibleAddInCount == 0) { + Rectangle rect = splitContainer.Panel1.ClientRectangle; + rect.Offset(16, 16); + rect.Inflate(-32, -32); + e.Graphics.DrawString("You don't have any AddIns installed.\n" + + "Download an AddIn from the Internet, then click 'Install AddIn' and " + + "choose the downloaded file to install it.", + Font, SystemBrushes.ControlText, rect); + } } void CreateAddInList() @@ -189,8 +191,6 @@ namespace ICSharpCode.AddInManager splitContainer.Panel2Collapsed = selected.Count == 0; if (selected.Count > 0) { dependencyTable.Visible = false; - uninstallButton.Enabled = false; - runActionButton.Enabled = false; runActionButton.Visible = true; uninstallButton.Visible = true; @@ -199,9 +199,13 @@ namespace ICSharpCode.AddInManager bool allInstalling = true; bool allUninstalling = true; bool allUninstallable = true; + bool hasErrors = false; foreach (AddIn addIn in selected) { allEnabled &= addIn.Action == AddInAction.Enable; - allDisabled &= addIn.Action == AddInAction.Disable; + if (addIn.Action == AddInAction.DependencyError || addIn.Action == AddInAction.InstalledTwice) + hasErrors = true; + else + allDisabled &= addIn.Action == AddInAction.Disable; allInstalling &= addIn.Action == AddInAction.Install; allUninstalling &= addIn.Action == AddInAction.Uninstall; if (allUninstallable) { @@ -221,6 +225,8 @@ namespace ICSharpCode.AddInManager actionGroupBox.Text = runActionButton.Text = "Enable"; actionDescription.Text = "Enables the selected AddIns."; runActionButton.Enabled = ShowDependencies(selected, true); + if (hasErrors) + runActionButton.Enabled = false; uninstallButton.Enabled = allUninstallable; } else if (allInstalling) { selectedAction = AddInAction.Uninstall; @@ -443,7 +449,7 @@ namespace ICSharpCode.AddInManager void UninstallButtonClick(object sender, EventArgs e) { ICSharpCode.Core.AddInManager.RemoveExternalAddIns(selected); - // TODO: delete user addins + InstallableAddIn.Uninstall(selected); RefreshAddInList(); } #endregion @@ -461,21 +467,25 @@ namespace ICSharpCode.AddInManager void RunActionButtonClick(object sender, EventArgs e) { - if (selectedAction == AddInAction.Disable) { - ICSharpCode.Core.AddInManager.Disable(selected); - } else if (selectedAction == AddInAction.Enable) { - ICSharpCode.Core.AddInManager.Enable(selected); - } else if (selectedAction == AddInAction.Install) { - // install new AddIns - foreach (InstallableAddIn addInPackage in shownAddInPackages) { - addInPackage.Install(); - } - RefreshAddInList(); - } else if (selectedAction == AddInAction.Uninstall) { - UninstallButtonClick(sender, e); - return; - } else { - throw new NotImplementedException(); + switch (selectedAction) { + case AddInAction.Disable: + ICSharpCode.Core.AddInManager.Disable(selected); + break; + case AddInAction.Enable: + ICSharpCode.Core.AddInManager.Enable(selected); + break; + case AddInAction.Install: + // install new AddIns + foreach (InstallableAddIn addInPackage in shownAddInPackages) { + addInPackage.Install(); + } + RefreshAddInList(); + return; + case AddInAction.Uninstall: + UninstallButtonClick(sender, e); + return; + default: + throw new NotImplementedException(); } foreach (AddInControl ctl in splitContainer.Panel1.Controls) { ctl.Invalidate(); @@ -579,6 +589,7 @@ namespace ICSharpCode.AddInManager // this.splitContainer.Panel1.AutoScroll = true; this.splitContainer.Panel1.BackColor = System.Drawing.SystemColors.Window; + this.splitContainer.Panel1.Paint += new System.Windows.Forms.PaintEventHandler(this.OnSplitContainerPanel1Paint); this.splitContainer.Panel1MinSize = 100; // // splitContainer.Panel2 diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.addin b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.addin index ec37c02fe5..a56a219d4a 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.addin +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.addin @@ -1,4 +1,4 @@ - + diff --git a/src/Main/Core/Project/Src/AddInTree/AddIn/AddIn.cs b/src/Main/Core/Project/Src/AddInTree/AddIn/AddIn.cs index 9c4dbbe579..09ca8a1547 100644 --- a/src/Main/Core/Project/Src/AddInTree/AddIn/AddIn.cs +++ b/src/Main/Core/Project/Src/AddInTree/AddIn/AddIn.cs @@ -19,8 +19,8 @@ namespace ICSharpCode.Core string addInFileName = null; AddInManifest manifest = new AddInManifest(); Dictionary paths = new Dictionary(); - AddInAction action = AddInAction.Enable; - bool enabled = true; + AddInAction action = AddInAction.Disable; + bool enabled; static bool hasShownErrorMessage = false; diff --git a/src/Main/Core/Project/Src/AddInTree/AddInManager.cs b/src/Main/Core/Project/Src/AddInTree/AddInManager.cs index 3fc474d848..5af59b9f47 100644 --- a/src/Main/Core/Project/Src/AddInTree/AddInManager.cs +++ b/src/Main/Core/Project/Src/AddInTree/AddInManager.cs @@ -19,12 +19,34 @@ namespace ICSharpCode.Core Disable, Install, Uninstall, - Update + Update, + InstalledTwice, + DependencyError } public static class AddInManager { static string configurationFileName; + static string addInInstallTemp; + static string userAddInPath; + + public static string UserAddInPath { + get { + return userAddInPath; + } + set { + userAddInPath = value; + } + } + + public static string AddInInstallTemp { + get { + return addInInstallTemp; + } + set { + addInInstallTemp = value; + } + } public static string ConfigurationFileName { get { @@ -35,6 +57,133 @@ namespace ICSharpCode.Core } } + /// + /// Installs the AddIns from AddInInstallTemp to the UserAddInPath. + /// In case of installation errors, a error message is displayed to the user + /// and the affected AddIn is added to the disabled list. + /// + public static void InstallAddIns(List disabled) + { + if (!Directory.Exists(addInInstallTemp)) + return; + LoggingService.Info("AddInManager.InstallAddIns started"); + if (!Directory.Exists(userAddInPath)) + Directory.CreateDirectory(userAddInPath); + string removeFile = Path.Combine(addInInstallTemp, "remove.txt"); + bool allOK = true; + List notRemoved = new List(); + if (File.Exists(removeFile)) { + using (StreamReader r = new StreamReader(removeFile)) { + string addInName; + while ((addInName = r.ReadLine()) != null) { + addInName = addInName.Trim(); + if (addInName.Length == 0) + continue; + string targetDir = Path.Combine(userAddInPath, addInName); + if (!UninstallAddIn(disabled, addInName, targetDir)) { + notRemoved.Add(addInName); + allOK = false; + } + } + } + if (notRemoved.Count == 0) { + LoggingService.Info("Deleting remove.txt"); + File.Delete(removeFile); + } else { + LoggingService.Info("Rewriting remove.txt"); + using (StreamWriter w = new StreamWriter(removeFile)) { + notRemoved.ForEach(w.WriteLine); + } + } + } + foreach (string sourceDir in Directory.GetDirectories(addInInstallTemp)) { + string addInName = Path.GetFileName(sourceDir); + string targetDir = Path.Combine(userAddInPath, addInName); + if (notRemoved.Contains(addInName)) { + LoggingService.Info("Skipping installation of " + addInName + " because deinstallation failed."); + continue; + } + if (UninstallAddIn(disabled, addInName, targetDir)) { + LoggingService.Info("Installing " + addInName + "..."); + Directory.Move(sourceDir, targetDir); + } else { + allOK = false; + } + } + if (allOK) { + try { + Directory.Delete(addInInstallTemp, false); + } catch (Exception ex) { + LoggingService.Warn("Error removing install temp", ex); + } + } + LoggingService.Info("AddInManager.InstallAddIns finished"); + } + + static bool UninstallAddIn(List disabled, string addInName, string targetDir) + { + if (Directory.Exists(targetDir)) { + LoggingService.Info("Removing " + addInName + "..."); + try { + Directory.Delete(targetDir, true); + } catch (Exception ex) { + disabled.Add(addInName); + MessageService.ShowError("Error removing " + addInName + ":\n" + + ex.Message + "\nThe AddIn will be " + + "removed on the next start of SharpDevelop and is disabled " + + "for now."); + return false; + } + } + return true; + } + + public static void RemoveUserAddInOnNextStart(string identity) + { + List removeEntries = new List(); + string removeFile = Path.Combine(addInInstallTemp, "remove.txt"); + if (File.Exists(removeFile)) { + using (StreamReader r = new StreamReader(removeFile)) { + string addInName; + while ((addInName = r.ReadLine()) != null) { + addInName = addInName.Trim(); + if (addInName.Length > 0) + removeEntries.Add(addInName); + } + } + if (removeEntries.Contains(identity)) + return; + } + removeEntries.Add(identity); + if (!Directory.Exists(addInInstallTemp)) + Directory.CreateDirectory(addInInstallTemp); + using (StreamWriter w = new StreamWriter(removeFile)) { + removeEntries.ForEach(w.WriteLine); + } + } + + public static void AbortRemoveUserAddInOnNextStart(string identity) + { + string removeFile = Path.Combine(addInInstallTemp, "remove.txt"); + if (!File.Exists(removeFile)) { + return; + } + List removeEntries = new List(); + using (StreamReader r = new StreamReader(removeFile)) { + string addInName; + while ((addInName = r.ReadLine()) != null) { + addInName = addInName.Trim(); + if (addInName.Length > 0) + removeEntries.Add(addInName); + } + } + if (removeEntries.Remove(identity)) { + using (StreamWriter w = new StreamWriter(removeFile)) { + removeEntries.ForEach(w.WriteLine); + } + } + } + public static void AddExternalAddIns(IList addIns) { List addInFiles = new List(); @@ -83,8 +232,14 @@ namespace ICSharpCode.Core disabled.Remove(identity); } if (addIn.Action == AddInAction.Uninstall) { - if (!addInFiles.Contains(addIn.FileName)) - addInFiles.Add(addIn.FileName); + if (FileUtility.IsBaseDirectory(userAddInPath, addIn.FileName)) { + foreach (string identity in addIn.Manifest.Identities.Keys) { + AbortRemoveUserAddInOnNextStart(identity); + } + } else { + if (!addInFiles.Contains(addIn.FileName)) + addInFiles.Add(addIn.FileName); + } } addIn.Action = AddInAction.Enable; } diff --git a/src/Main/Core/Project/Src/AddInTree/AddInTree.cs b/src/Main/Core/Project/Src/AddInTree/AddInTree.cs index 42498f03c3..11630af555 100644 --- a/src/Main/Core/Project/Src/AddInTree/AddInTree.cs +++ b/src/Main/Core/Project/Src/AddInTree/AddInTree.cs @@ -214,6 +214,7 @@ namespace ICSharpCode.Core static void DisableAddin(AddIn addIn, Dictionary dict, Dictionary addInDict) { addIn.Enabled = false; + addIn.Action = AddInAction.DependencyError; foreach (string name in addIn.Manifest.Identities.Keys) { dict.Remove(name); addInDict.Remove(name); @@ -227,6 +228,7 @@ namespace ICSharpCode.Core Dictionary addInDict = new Dictionary(); foreach (string fileName in addInFiles) { AddIn addIn = AddIn.Load(fileName); + addIn.Enabled = true; if (disabledAddIns != null && disabledAddIns.Count > 0) { foreach (string name in addIn.Manifest.Identities.Keys) { if (disabledAddIns.Contains(name)) { @@ -240,7 +242,8 @@ namespace ICSharpCode.Core if (dict.ContainsKey(pair.Key)) { MessageService.ShowError("Name '" + pair.Key + "' is used by " + "'" + addInDict[pair.Key].FileName + "' and '" + fileName + "'"); - DisableAddin(addIn, dict, addInDict); + addIn.Enabled = false; + addIn.Action = AddInAction.InstalledTwice; break; } else { dict.Add(pair.Key, pair.Value); diff --git a/src/Main/Core/Project/Src/AddInTree/CoreStartup.cs b/src/Main/Core/Project/Src/AddInTree/CoreStartup.cs index 03bb2d8520..6973cad3ec 100644 --- a/src/Main/Core/Project/Src/AddInTree/CoreStartup.cs +++ b/src/Main/Core/Project/Src/AddInTree/CoreStartup.cs @@ -22,7 +22,6 @@ namespace ICSharpCode.Core string configDirectory; string dataDirectory; string applicationName; - string addInConfigurationFile; /// /// Sets the name used for the properties (only name, without path or extension). @@ -67,18 +66,6 @@ namespace ICSharpCode.Core } } - /// - /// Gets/Sets the configuration file used to store the enabled/disabled state of the AddIns. - /// - public string AddInConfigurationFile { - get { - return addInConfigurationFile; - } - set { - addInConfigurationFile = value; - } - } - public CoreStartup(string applicationName) { if (applicationName == null) @@ -93,12 +80,26 @@ namespace ICSharpCode.Core addInFiles.AddRange(FileUtility.SearchDirectory(addInDir, "*.addin")); } - public void RunInitialization() + public void ConfigureExternalAddIns(string addInConfigurationFile) { - if (addInConfigurationFile != null) { - AddInManager.ConfigurationFileName = addInConfigurationFile; - AddInManager.LoadAddInConfiguration(addInFiles, disabledAddIns); + AddInManager.ConfigurationFileName = addInConfigurationFile; + AddInManager.LoadAddInConfiguration(addInFiles, disabledAddIns); + } + + public void ConfigureUserAddIns(string addInInstallTemp, string userAddInPath) + { + AddInManager.AddInInstallTemp = addInInstallTemp; + AddInManager.UserAddInPath = userAddInPath; + if (Directory.Exists(addInInstallTemp)) { + AddInManager.InstallAddIns(disabledAddIns); + } + if (Directory.Exists(userAddInPath)) { + AddAddInsFromDirectory(userAddInPath); } + } + + public void RunInitialization() + { AddInTree.Load(addInFiles, disabledAddIns); // run workspace autostart commands diff --git a/src/Main/StartUp/Project/SharpDevelopMain.cs b/src/Main/StartUp/Project/SharpDevelopMain.cs index 49e11eaac6..47e83b631c 100644 --- a/src/Main/StartUp/Project/SharpDevelopMain.cs +++ b/src/Main/StartUp/Project/SharpDevelopMain.cs @@ -177,11 +177,10 @@ namespace ICSharpCode.SharpDevelop LoggingService.Info("Looking for AddIns..."); c.AddAddInsFromDirectory(Path.Combine(FileUtility.ApplicationRootPath, "AddIns")); - string fileName = Path.Combine(PropertyService.ConfigDirectory, "AddIns"); - if (Directory.Exists(fileName)) { - c.AddAddInsFromDirectory(fileName); - } - c.AddInConfigurationFile = Path.Combine(PropertyService.ConfigDirectory, "AddIns.xml"); + + c.ConfigureExternalAddIns(Path.Combine(PropertyService.ConfigDirectory, "AddIns.xml")); + c.ConfigureUserAddIns(Path.Combine(PropertyService.ConfigDirectory, "AddInInstallTemp"), + Path.Combine(PropertyService.ConfigDirectory, "AddIns")); LoggingService.Info("Loading AddInTree..."); c.RunInitialization();