List:Commits« Previous MessageNext Message »
From:Iggy Galarza Date:March 21 2011 3:29pm
Subject:bzr commit into wex-installer-1.0 branch (iggy:375)
View as plain text  
#At file:///C:/src/bzr.mysql/wex/installer/ based on revid:iggy@stripped

  375 Iggy Galarza	2011-03-21 [merge]
      Auto-Merge.

    modified:
      WexInstaller.Core/Package.cs
      WexInstaller.Core/Product.cs
      WexInstaller.Core/ProductFeature.cs
      WexInstaller.Core/ProductManager.cs
      WexInstaller/Controls/FeatureBox.cs
      WexInstaller/Controls/FeatureTreeView.cs
      WexInstaller/InstallWizard/InstallProgressPanel.cs
=== modified file 'WexInstaller.Core/Package.cs'
--- a/WexInstaller.Core/Package.cs	2011-03-18 10:29:04 +0000
+++ b/WexInstaller.Core/Package.cs	2011-03-21 10:34:56 +0000
@@ -4,394 +4,398 @@ using System.Diagnostics;
 using System.IO;
 using System.Text;
 using System.Xml.Serialization;
+
 using Microsoft.Win32;
 
 namespace WexInstaller.Core
 {
-    public class Package
-    {
-        private string packageCode;
-        private FileVersionInfo msiVersion;
-
-        [XmlAttribute("type")]
-        public PackageType Type { get; set; }
+  public class Package
+  {
+    private string packageCode;
+    private FileVersionInfo msiVersion;
+
+    [XmlAttribute("type")]
+    public PackageType Type { get; set; }
 
-        [XmlAttribute("arch")]
-        public PackageArchitecture Architecture { get; set; }
+    [XmlAttribute("arch")]
+    public PackageArchitecture Architecture { get; set; }
 
-        [XmlAttribute("filename")]
-        public string FileName { get; set; }
+    [XmlAttribute("filename")]
+    public string FileName { get; set; }
 
-        [XmlAttribute("id")]
-        public string Id
-        {
-            get { return packageCode; }
-            set { packageCode = value; UpdateOptionalParameters(); }
-        }
+    [XmlAttribute("id")]
+    public string Id
+    {
+      get { return packageCode; }
+      set { packageCode = value; UpdateOptionalParameters(); }
+    }
 
-        [XmlAttribute("thisVersion")]
-        public string ThisVersion { get; set; }
+    [XmlAttribute("thisVersion")]
+    public string ThisVersion { get; set; }
 
-        [XmlElement("Feature")]
-        public List<ProductFeature> Features { get; set; }
+    [XmlElement("Feature")]
+    public List<ProductFeature> Features { get; set; }
 
-        [XmlIgnore]
-        public string UrlBase { get; set; }
+    [XmlIgnore]
+    public string UrlBase { get; set; }
 
-        [XmlIgnore]
-        public string FullPath { get { return InstallerConfiguration.ProductCachePath + FileName; } }
+    [XmlIgnore]
+    public string FullPath { get { return InstallerConfiguration.ProductCachePath + FileName; } }
 
-        [XmlIgnore]
-        public string RemotePath 

+    [XmlIgnore]
+    public string RemotePath 
+    {
+      get
+      {
+        string remoteURL = String.Empty;
+        if (String.IsNullOrEmpty(UrlBase) == false)
         {
-            get
-            {
-                string remoteURL = String.Empty;
-                if (String.IsNullOrEmpty(UrlBase) == false)
-                {
-                    if (UrlBase.Contains("http:"))
-                    {
-                        remoteURL = String.Format("{0}/{1}", UrlBase, FileName);
-                    }
-                    else
-                    {
-                        remoteURL = String.Format("http://downloads.mysql.com/archives/{0}", UrlBase);
-                        if (!remoteURL.EndsWith("/"))
-                        {
-                            remoteURL += "/";
-                        }
-                        remoteURL += FileName;
-                    }
-                }
-                return remoteURL;
-            } 
+          if (UrlBase.Contains("http:"))
+            remoteURL = String.Format("{0}/{1}", UrlBase, FileName);
+          else
+          {
+            remoteURL = String.Format("http://downloads.mysql.com/archives/{0}", UrlBase);
+            if (!remoteURL.EndsWith("/"))
+              remoteURL += "/";
+            remoteURL += FileName;
+          }
         }
+        return remoteURL;
+      } 
+    }
 
-        [XmlIgnore]
-        public string TempFileName { get { return String.Format("{0}.temp", FullPath); } }
+    [XmlIgnore]
+    public string TempFileName { get { return String.Format("{0}.temp", FullPath); } }
 
-        [XmlIgnore]
-        public string DisplayName { get; set; }
+    [XmlIgnore]
+    public string DisplayName { get; set; }
 
-        [XmlIgnore]
-        public string Publisher { get; set; }
+    [XmlIgnore]
+    public string Publisher { get; set; }
 
-        [XmlIgnore]
-        public string MSICache { get; set; }
+    [XmlIgnore]
+    public string MSICache { get; set; }
 
-        [XmlIgnore]
-        public bool Installed { get { return (DisplayName != String.Empty); } }
+    [XmlIgnore]
+    public bool Installed { get { return (DisplayName != String.Empty); } }
 
-        [XmlIgnore]
-        public bool FoundLocal { get { return File.Exists(FullPath); } }
+    [XmlIgnore]
+    public bool FoundLocal { get { return File.Exists(FullPath); } }
 
-        [XmlIgnore]
-        public Dictionary<string, string> RegistryEntries { get; set; }
+    [XmlIgnore]
+    public Dictionary<string, string> RegistryEntries { get; set; }
 
-        [XmlIgnore]
-        public int FeatureCount
+    [XmlIgnore]
+    public int FeatureCount
+    {
+      get
+      {
+        int count = 0;
+        foreach (ProductFeature f in Features)
         {
-            get
-            {
-                int count = 0;
-                foreach (ProductFeature f in Features)
-                {
-                    count++;
-                    count += f.FeatureCount;
-                }
-                return count;
-            }
+          count++; // TODO: Does the package itself count as feature?
+          count += f.FeatureCount;
         }
+        return count;
+      }
+    }
 
-        public Package()
-        {
-            RegistryEntries = new Dictionary<string, string>();
-            Features = new List<ProductFeature>();
-            DisplayName     = String.Empty;
-            Publisher       = String.Empty;
-            MSICache        = String.Empty;
-            msiVersion = FileVersionInfo.GetVersionInfo(Environment.SystemDirectory + "\\msi.dll");
-        }
+    /// <summary>
+    /// Returns all child and grand child features.
+    /// </summary>
+    [XmlIgnore]
+    public List<ProductFeature> AllFeatures
+    {
+      get
+      {
+        List<ProductFeature> result = new List<ProductFeature>();
+        foreach (ProductFeature feature in Features)
+          result.AddRange(feature.AllFeatures);
+        return result;
+      }
+    }
 
-        private string GetMSIProductInfo(string propertyName)
-        {
-            int productInfoLength = 512;
-            StringBuilder productInfo = new StringBuilder(productInfoLength);
-            MSIEnumError status = MsiInterop.MsiGetProductInfo(Id, propertyName, productInfo, ref productInfoLength);
+    public Package()
+    {
+      RegistryEntries = new Dictionary<string, string>();
+      Features = new List<ProductFeature>();
+      DisplayName     = String.Empty;
+      Publisher       = String.Empty;
+      MSICache        = String.Empty;
+      msiVersion = FileVersionInfo.GetVersionInfo(Environment.SystemDirectory + "\\msi.dll");
+    }
 
-            return (status == MSIEnumError.Success) ? productInfo.ToString() : String.Empty;
-        }
+    private string GetMSIProductInfo(string propertyName)
+    {
+      int productInfoLength = 512;
+      StringBuilder productInfo = new StringBuilder(productInfoLength);
+      MSIEnumError status = MsiInterop.MsiGetProductInfo(Id, propertyName, productInfo, ref productInfoLength);
 
-        public bool UpdateOptionalParameters()
-        {
-            bool success = false;
+      return (status == MSIEnumError.Success) ? productInfo.ToString() : String.Empty;
+    }
 
-            if (Id != String.Empty)
-            {
-                DisplayName = GetMSIProductInfo("ProductName");
-                Publisher = GetMSIProductInfo("Publisher");
-                MSICache = GetMSIProductInfo("LocalPackage");
-                if (ThisVersion == null)
-                    ThisVersion = GetMSIProductInfo("VersionString");
-                if (FileName == null)
-                    FileName = GetMSIProductInfo("PackageName");
+    public bool UpdateOptionalParameters()
+    {
+      bool success = false;
 
-                if (DisplayName != String.Empty && Publisher != String.Empty)
-                {
-                    if (RegistryEntries.Count > 0)
-                        RegistryEntries.Clear();
+      if (Id != String.Empty)
+      {
+        DisplayName = GetMSIProductInfo("ProductName");
+        Publisher = GetMSIProductInfo("Publisher");
+        MSICache = GetMSIProductInfo("LocalPackage");
+        if (ThisVersion == null)
+          ThisVersion = GetMSIProductInfo("VersionString");
+        if (FileName == null)
+          FileName = GetMSIProductInfo("PackageName");
 
-                    // Open registry and fetch all the keys and their values into a local dictionary.
-                    RegistryKey packageKey = Registry.LocalMachine;
-                    packageKey = packageKey.OpenSubKey("SOFTWARE\\");
-                    if (Architecture == PackageArchitecture.X86)
-                    {
-                        // Try to open the 32-bit Subkey.
-                        try
-                        {
-                            RegistryKey tempKey = packageKey.OpenSubKey("Wow6432Node\\");
-                            if (tempKey != null)
-                                packageKey = tempKey;
-                        }
-                        catch
-                        {
-                            // This fails when running on a 32 bit machine or OS version < Vista
-                        }
-                    }
-
-                    RegistryKey publisherKey = packageKey.OpenSubKey(String.Format("{0}\\{1}", Publisher, DisplayName), false);
-                    if (publisherKey != null)
-                    {
-                        packageKey = publisherKey;
-                    }
-                    else
-                    {
-                        packageKey = packageKey.OpenSubKey(String.Format("{0}\\{1}", "MySQL AB", DisplayName), false);
-                        if (packageKey != null)
-                            Publisher = "MySQL AB";
-                    }
-
-                    if (packageKey != null)
-                    {
-                        foreach (string valuename in packageKey.GetValueNames())
-                        {
-                            RegistryEntries.Add(valuename, packageKey.GetValue(valuename).ToString());
-                        }
-                        packageKey.Close();
-                    }
-                }
+        if (DisplayName != String.Empty && Publisher != String.Empty)
+        {
+          if (RegistryEntries.Count > 0)
+            RegistryEntries.Clear();
+
+          // Open registry and fetch all the keys and their values into a local dictionary.
+          RegistryKey packageKey = Registry.LocalMachine;
+          packageKey = packageKey.OpenSubKey("SOFTWARE\\");
+          if (Architecture == PackageArchitecture.X86)
+          {
+            // Try to open the 32-bit Subkey.
+            try
+            {
+              RegistryKey tempKey = packageKey.OpenSubKey("Wow6432Node\\");
+              if (tempKey != null)
+                packageKey = tempKey;
             }
-            else
+            catch
             {
-                DisplayName = String.Empty;
+              // This fails when running on a 32 bit machine or OS version < Vista
             }
+          }
 
-            return success;
+          RegistryKey publisherKey = packageKey.OpenSubKey(String.Format("{0}\\{1}", Publisher, DisplayName), false);
+          if (publisherKey != null)
+            packageKey = publisherKey;
+          else

+          {
+            packageKey = packageKey.OpenSubKey(String.Format("{0}\\{1}", "MySQL AB", DisplayName), false);
+            if (packageKey != null)
+              Publisher = "MySQL AB";
+          }
+
+          if (packageKey != null)
+          {
+            foreach (string valuename in packageKey.GetValueNames())
+              RegistryEntries.Add(valuename, packageKey.GetValue(valuename).ToString());
+            packageKey.Close();
+          }
         }
+      }
+      else
+        DisplayName = String.Empty;
 
-        public long GetInstallationSizeEstimate()
-        {
-            long defaultBase = 0;
-
-            if (FoundLocal)
-            {
-                // Disable MSI UI.
-                InstallUILevel internalUILevel = InstallUILevel.None;
-
-                if (msiVersion.FileMajorPart >= 5)
-                {
-                    // Allow to installer to elevate, even in silent mode
-                    internalUILevel |= InstallUILevel.UACOnly;
-                }
-                MsiInterop.MsiSetInternalUI(internalUILevel, IntPtr.Zero);
-
-                UIntPtr packageHandle = UIntPtr.Zero;
-                uint retVal = MsiInterop.MsiOpenPackage(FullPath, ref packageHandle);
+      return success;
+    }
 
-                if (retVal == 0)
-                {
-                    uint results = 0;
-                    results += MsiInterop.MsiDoAction(packageHandle, "CostInitialize");
-                    results += MsiInterop.MsiDoAction(packageHandle, "FileCost");
-                    results += MsiInterop.MsiDoAction(packageHandle, "CostFinalize");
-                    results += MsiInterop.MsiDoAction(packageHandle, "InstallValidate");
-                    // The EstimateSize is not available until after the InstallValidate action is complete.
-
-                    if (results == 0)
-                    {
-                        // Now, you can find the estimated size per feature.
-                        foreach (ProductFeature f in Features)
-                        {
-                            int cost = 0;
-                            if (MsiInterop.MsiGetFeatureCost(packageHandle, f.Name, MSICostTree.Self, InstallState.Source, out cost) == 0)
-                                if (cost != -2147483648) // Null value returned when EstimateSize is uninitialized.
-                                    f.SizeEstimate = cost * 512 * 1024; // Number of bytes
-                            defaultBase += f.SizeEstimate;
-
-                            foreach (ProductFeature f1 in f.Features)
-                            {
-                                int cost1 = 0;
-                                if (MsiInterop.MsiGetFeatureCost(packageHandle, f1.Name, MSICostTree.Self, InstallState.Source, out cost1) == 0)
-                                    if (cost != -2147483648)
-                                        f1.SizeEstimate = cost1 * 512 * 1024; // Number of bytes
-                                defaultBase += f1.SizeEstimate;
-                            }
-                        }
-                    }
-                    else
-                    {
-                        // Invalid package.
-                    }
-                }
+    public long GetInstallationSizeEstimate()
+    {
+      long defaultBase = 0;
 
-                MsiInterop.MsiCloseHandle(packageHandle);
-            }
+      if (FoundLocal)
+      {
+        // Disable MSI UI.
+        InstallUILevel internalUILevel = InstallUILevel.None;
 
-            return defaultBase;
+        if (msiVersion.FileMajorPart >= 5)
+        {
+          // Allow to installer to elevate, even in silent mode
+          internalUILevel |= InstallUILevel.UACOnly;
         }
+        MsiInterop.MsiSetInternalUI(internalUILevel, IntPtr.Zero);
+
+        UIntPtr packageHandle = UIntPtr.Zero;
+        uint retVal = MsiInterop.MsiOpenPackage(FullPath, ref packageHandle);
 
-        private void ProcessFeatures(List<ProductFeature> toAdd, List<ProductFeature> toRemove)
+        if (retVal == 0)
         {
+          uint results = 0;
+          results += MsiInterop.MsiDoAction(packageHandle, "CostInitialize");
+          results += MsiInterop.MsiDoAction(packageHandle, "FileCost");
+          results += MsiInterop.MsiDoAction(packageHandle, "CostFinalize");
+          results += MsiInterop.MsiDoAction(packageHandle, "InstallValidate");
+          // The EstimateSize is not available until after the InstallValidate action is complete.
+
+          if (results == 0)
+          {
+            // Now, you can find the estimated size per feature.
             foreach (ProductFeature f in Features)
             {
-                if (f.Installed != f.ProposedInstalled)
-                {
-                    if (!f.Installed) toAdd.Add(f);
-                    else toRemove.Add(f);
-                }
-                f.ProcessFeatures(toAdd, toRemove);
+              int cost = 0;
+              if (MsiInterop.MsiGetFeatureCost(packageHandle, f.Name, MSICostTree.Self, InstallState.Source, out cost) == 0)
+                if (cost != -2147483648) // Null value returned when EstimateSize is uninitialized.
+                  f.SizeEstimate = cost * 512 * 1024; // Number of bytes
+              defaultBase += f.SizeEstimate;
+
+              foreach (ProductFeature f1 in f.Features)
+              {
+                int cost1 = 0;
+                if (MsiInterop.MsiGetFeatureCost(packageHandle, f1.Name, MSICostTree.Self, InstallState.Source, out cost1) == 0)
+                  if (cost != -2147483648)
+                    f1.SizeEstimate = cost1 * 512 * 1024; // Number of bytes
+                defaultBase += f1.SizeEstimate;
+              }
             }
+          }
+          else
+          {
+            // Invalid package.
+          }
         }
 
-        public string GetCommandLine()
-        {
-            List<ProductFeature> toAdd = new List<ProductFeature>();
-            List<ProductFeature> toRemove = new List<ProductFeature>();
+        MsiInterop.MsiCloseHandle(packageHandle);
+      }
 
-            ProcessFeatures(toAdd, toRemove);
-            if (toAdd.Count == FeatureCount && toRemove.Count == 0)
-                return "ADDLOCAL=ALL";
-            else if (toAdd.Count == 0 && toRemove.Count == FeatureCount)
-                return "REMOVE=ALL";
-            else
-            {
-                StringBuilder cmdLine = new StringBuilder();
-                if (toAdd.Count > 0)
-                {
-                    cmdLine.Append("ADDLOCAL=");
-                    string delimiter = "";
-                    foreach (ProductFeature f in toAdd)
-                    {
-                        cmdLine.AppendFormat("{0}{1}", delimiter, f.Name);
-                        delimiter = ",";
-                    }
-                    cmdLine.Append(" ");
-                }
-                if (toRemove.Count > 0)
-                {
-                    cmdLine.Append("REMOVE=");
-                    string delimiter = "";
-                    foreach (ProductFeature f in toRemove)
-                    {
-                        cmdLine.AppendFormat("{0}{1}", delimiter, f.Name);
-                        delimiter = ",";
-                    }
-                }
-                return cmdLine.ToString();
-            }
-        }
+      return defaultBase;
+    }
 
-        public void PostInitialize()
+    private void ProcessFeatures(List<ProductFeature> toAdd, List<ProductFeature> toRemove)
+    {
+      foreach (ProductFeature f in Features)
+      {
+        if (f.Installed != f.ProposedInstalled)
         {
-            if (String.IsNullOrEmpty(Id) == false)  // Valid Package.
-            {
-                // Synchronize Installed Feature state.
-                StringBuilder FeatureCode = new StringBuilder(256);
-                StringBuilder ParentCode = new StringBuilder(256);
-                MSIEnumError rc = MSIEnumError.Success;
-                uint CurrentIndex = 0;
-
-                do
-                {
-                    if (FeatureCode.Length > 0)
-                    {
-                        foreach (ProductFeature pf in Features)
-                        {
-                            if (pf.Name == FeatureCode.ToString())
-                            {
-                                InstallState currentState = MsiInterop.MsiQueryFeatureState(Id, FeatureCode.ToString());
-                                pf.Installed = (currentState == InstallState.Local);
-                                pf.ProposedInstalled = pf.Installed;
-                                break;
-                            }
-
-                            if (ParentCode.Length > 0)
-                            {
-                                if (pf.Name == ParentCode.ToString())
-                                {
-                                    foreach (ProductFeature cf in pf.Features)
-                                    {
-                                        if (cf.Name == FeatureCode.ToString())
-                                        {
-                                            InstallState currentState = MsiInterop.MsiQueryFeatureState(Id, FeatureCode.ToString());
-                                            cf.Installed = (currentState == InstallState.Local);
-                                            cf.ProposedInstalled = cf.Installed;
-                                            break;
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                        ParentCode.Remove(0, ParentCode.Length);
-                        FeatureCode.Remove(0, FeatureCode.Length);
-                    }
-
-
-                    try
-                    {
-                        rc = (MSIEnumError)MsiInterop.MsiEnumFeatures(Id, CurrentIndex++, FeatureCode, ParentCode);
-                    }
-                    catch (Exception e)
-                    {
-                        Logger.LogException(e);
-                    }
-                }
-                while (rc == MSIEnumError.Success);
-
-                if (rc != MSIEnumError.NoMoreItems)
-                {
-                    // Record the real error.
-                    Logger.LogError(String.Format("Found error: {0}", rc.ToString()));
-                }
-            }
+          if (!f.Installed)
+            toAdd.Add(f);
+          else
+            toRemove.Add(f);
         }
+        f.ProcessFeatures(toAdd, toRemove);
+      }
     }
 
-    public enum PackageType 
+    public string GetCommandLine()
     {
-        MSI
-    }
+      List<ProductFeature> toAdd = new List<ProductFeature>();
+      List<ProductFeature> toRemove = new List<ProductFeature>();
 
-    public enum PackageArchitecture
-    {
-        X86,
-        X64,
-        Any
+      ProcessFeatures(toAdd, toRemove);
+      if (toAdd.Count == FeatureCount && toRemove.Count == 0)
+        return "ADDLOCAL=ALL";
+      else if (toAdd.Count == 0 && toRemove.Count == FeatureCount)
+        return "REMOVE=ALL";
+      else
+      {
+        StringBuilder cmdLine = new StringBuilder();
+        if (toAdd.Count > 0)
+        {
+          cmdLine.Append("ADDLOCAL=");
+          string delimiter = "";
+          foreach (ProductFeature f in toAdd)
+          {
+            cmdLine.AppendFormat("{0}{1}", delimiter, f.Name);
+            delimiter = ",";
+          }
+          cmdLine.Append(" ");
+        }
+        if (toRemove.Count > 0)
+        {
+          cmdLine.Append("REMOVE=");
+          string delimiter = "";
+          foreach (ProductFeature f in toRemove)
+          {
+            cmdLine.AppendFormat("{0}{1}", delimiter, f.Name);
+            delimiter = ",";
+          }
+        }
+        return cmdLine.ToString();
+      }
     }
 
-    public enum PackagePlatform
+    public void PostInitialize()
     {
-        Windows,
-        Any
-    }
+      if (String.IsNullOrEmpty(Id) == false)  // Valid Package.
+      {
+        // Synchronize Installed Feature state.
+        StringBuilder FeatureCode = new StringBuilder(256);
+        StringBuilder ParentCode = new StringBuilder(256);
+        MSIEnumError rc = MSIEnumError.Success;
+        uint CurrentIndex = 0;
 
-    public enum PackageMaturity
-    {
-        CTP,
-        Alpha,
-        Beta,
-        RC,
-        GA
+        do
+        {
+          if (FeatureCode.Length > 0)
+          {
+            foreach (ProductFeature pf in Features)
+            {
+              if (pf.Name == FeatureCode.ToString())
+              {
+                InstallState currentState = MsiInterop.MsiQueryFeatureState(Id, FeatureCode.ToString());
+                pf.Installed = (currentState == InstallState.Local);
+                pf.ProposedInstalled = pf.Installed;
+                break;
+              }
+
+              if (ParentCode.Length > 0 && (pf.Name == ParentCode.ToString()))
+              {
+                foreach (ProductFeature cf in pf.Features)
+                {
+                  if (cf.Name == FeatureCode.ToString())
+                  {
+                    InstallState currentState = MsiInterop.MsiQueryFeatureState(Id, FeatureCode.ToString());
+                    cf.Installed = (currentState == InstallState.Local);
+                    cf.ProposedInstalled = cf.Installed;
+                    break;
+                  }
+                }
+              }
+            }
+            ParentCode.Remove(0, ParentCode.Length);
+            FeatureCode.Remove(0, FeatureCode.Length);
+          }
+
+          try
+          {
+            rc = (MSIEnumError)MsiInterop.MsiEnumFeatures(Id, CurrentIndex++, FeatureCode, ParentCode);
+          }
+          catch (Exception e)
+          {
+            Logger.LogException(e);
+          }
+        }
+        while (rc == MSIEnumError.Success);
+
+        if (rc != MSIEnumError.NoMoreItems)
+        {
+          // Record the real error.
+          Logger.LogError(String.Format("Found error: {0}", rc.ToString()));
+        }
+      }
     }
+  }
+
+  public enum PackageType 
+  {
+    MSI
+  }
+
+  public enum PackageArchitecture
+  {
+    X86,
+    X64,
+    Any
+  }
+
+  public enum PackagePlatform
+  {
+    Windows,
+    Any
+  }
+
+  public enum PackageMaturity
+  {
+    CTP,
+    Alpha,
+    Beta,
+    RC,
+    GA
+  }
 
 }

=== modified file 'WexInstaller.Core/Product.cs'
--- a/WexInstaller.Core/Product.cs	2011-03-18 10:29:04 +0000
+++ b/WexInstaller.Core/Product.cs	2011-03-21 12:59:48 +0000
@@ -229,7 +229,7 @@ namespace WexInstaller.Core
     {
       bool hasChanges;
       hasChanges = Installed != ProposedInstalled;
-      foreach (ProductFeature feature in GetPackage().Features)
+      foreach (ProductFeature feature in GetPackage().AllFeatures)
         hasChanges |= feature.HasChanges();
       return hasChanges;
     }
@@ -251,12 +251,24 @@ namespace WexInstaller.Core
       return keyValue;
     }
 
+    /// <summary>
+    /// Returns all direct features of a product.
+    /// </summary>
     public List<ProductFeature> GetProductFeatures()
     {
       Package pack = GetPackage();
       return pack.Features;
     }
 
+    /// <summary>
+    /// Returns all features for a product, including their sub-features.
+    /// </summary>
+    public List<ProductFeature> GetAllProductFeatures()
+    {
+      Package pack = GetPackage();
+      return pack.AllFeatures;
+    }
+
     // MSI Installation in a background worker thread.
     private void ChainedInstallerUpdated(object sender, ChainedInstallerEventArgs c)
     {
@@ -480,16 +492,58 @@ namespace WexInstaller.Core
       return;
     }
 
+    private enum ChangeType { Unknown, Installation, Update, Removal }
+
+    /// <summary>
+    /// Applies all the changes the user has selected.
+    /// Note: only one type of change can be processed at a time (update, installation, removal).
+    /// </summary>
     public void MakeChanges()
     {
-      if (Installed && ProposedInstalled)
-        Update();
-      else
-        if (!Installed && ProposedInstalled)
-          Install();
+      ChangeType type = ChangeType.Unknown;
+
+      // First check the product as a whole for the change type.
+      if (Installed || ProposedInstalled)
+      {
+        if (Installed)
+          if (ProposedInstalled)
+            type = ChangeType.Update;
+          else
+            type = ChangeType.Removal;
         else
-          if (Installed && !ProposedInstalled)
-            Remove();
+          type = ChangeType.Installation;
+      }
+
+      // If there was no change for the product as such go through the features to find changes.
+      if (type == ChangeType.Unknown)
+      {
+        foreach (ProductFeature feature in GetAllProductFeatures())
+          if (feature.Installed || feature.ProposedInstalled)
+          {
+            // We only look for the first change in the feature list, whatever it is and
+            // determine our next action from that.
+            if (feature.Installed)
+              if (feature.ProposedInstalled)
+                type = ChangeType.Update;
+              else
+                type = ChangeType.Removal;
+            else
+              type = ChangeType.Installation;
+          }
+      }
+
+      switch (type)
+      {
+        case ChangeType.Installation:
+          Install();
+          break;
+        case ChangeType.Removal:
+          Remove();
+          break;
+        case ChangeType.Update:
+          Update();
+          break;
+      }
     }
 
     public long GetInstallationSizeEstimate()

=== modified file 'WexInstaller.Core/ProductFeature.cs'
--- a/WexInstaller.Core/ProductFeature.cs	2011-03-18 10:29:04 +0000
+++ b/WexInstaller.Core/ProductFeature.cs	2011-03-21 10:34:56 +0000
@@ -125,6 +125,22 @@ namespace WexInstaller.Core
       }
     }
 
+    /// <summary>
+    /// Returns all child and grand child features.
+    /// </summary>
+    [XmlIgnore]
+    public List<ProductFeature> AllFeatures
+    {
+      get
+      {
+        List<ProductFeature> result = new List<ProductFeature>();
+        result.Add(this);
+        foreach (ProductFeature feature in Features)
+          result.AddRange(feature.AllFeatures);
+        return result;
+      }
+    }
+
     public ProductFeature()
     {
       Features = new List<ProductFeature>();

=== modified file 'WexInstaller.Core/ProductManager.cs'
--- a/WexInstaller.Core/ProductManager.cs	2011-03-18 18:08:07 +0000
+++ b/WexInstaller.Core/ProductManager.cs	2011-03-21 15:29:28 +0000
@@ -82,6 +82,8 @@ namespace WexInstaller.Core
         {
           p.PostInitialize(true);
           p.SetParent(pc);
+          foreach (ProductFeature feature in p.GetProductFeatures())
+            feature.SetParent(p);
 
           // Initialize the controller if there are any
           if (p.Controller != null)

=== modified file 'WexInstaller/Controls/FeatureBox.cs'
--- a/WexInstaller/Controls/FeatureBox.cs	2011-03-18 17:04:52 +0000
+++ b/WexInstaller/Controls/FeatureBox.cs	2011-03-21 10:02:47 +0000
@@ -34,7 +34,20 @@ namespace WexInstaller.Controls
 
     #region Properties
 
-    internal FeatureTreeView FeatureTree { get; set; }
+    private FeatureTreeView featureTreeview = null;
+    internal FeatureTreeView FeatureTree
+    {
+      get { return featureTreeview; }
+      set
+      {
+        if (featureTreeview != null)
+          featureTreeview.ProductInstallationProposalChanged -= featureTreeview_ProductInstallationProposalChanged;
+        featureTreeview = value;
+        if (featureTreeview != null)
+          featureTreeview.ProductInstallationProposalChanged +=
+            new EventHandler<FeatureTreeView.ProductChangedEventArgs>(featureTreeview_ProductInstallationProposalChanged);
+      }
+    }
 
     public ProductElement SelectedObject
     {
@@ -173,7 +186,7 @@ namespace WexInstaller.Controls
           }
         }
 
-        // No iterate over all categories and their products and see if any of the products matches
+        // Now iterate over all categories and their products and see if any of the products matches
         // any of the products in the reference catalog(s).
         Dictionary<string, Product> products = new Dictionary<string, Product>();
         foreach (Product p in category.Products)
@@ -190,6 +203,17 @@ namespace WexInstaller.Controls
       return result;
     }
 
+    private void InvalidateCheckbox(TreeNode child)
+    {
+      // The checkbox place is before the text centered in an area twice as wide as 
+      // the checkbox itself.
+      Rectangle checkBoxRectangle = new Rectangle();
+      checkBoxRectangle.Location = new Point(CBGlyphSize.Width / 2,
+        child.Bounds.Top + ((featureList.ItemHeight - CBGlyphSize.Height) / 2));
+      checkBoxRectangle.Size = CBGlyphSize;
+      featureList.Invalidate(checkBoxRectangle);
+    }
+
     #endregion
 
     #region Drawing
@@ -225,33 +249,6 @@ namespace WexInstaller.Controls
       g.DrawString(SelectedObject.Description, smallFont, blackBrush, textPt);
     }
 
-    #endregion
-
-    #region Event handling
-
-    private void featureList_AfterCheck(object sender, TreeViewEventArgs e)
-    {
-      Product p = e.Node.Tag as Product;
-      if (p != null)
-      {
-        if (p.Installed)
-          return;
-        p.ProposedInstalled = !p.ProposedInstalled;
-
-        List<ProductFeature> productFeatures = p.GetProductFeatures();
-        foreach (ProductFeature pf in productFeatures)
-          pf.ProposedInstalled = p.ProposedInstalled;
-      }
-      else
-      {
-        ProductFeature pf = e.Node.Tag as ProductFeature;
-        pf.ProposedInstalled = !pf.ProposedInstalled;
-      }
-      featureList.Refresh();
-      if (FeatureTree != null)
-        FeatureTree.Refresh();
-    }
-
     private void featureList_DrawNode(object sender, DrawTreeNodeEventArgs e)
     {
       if (e.Bounds.Width <= 0 || e.Bounds.Height <= 0)
@@ -287,5 +284,60 @@ namespace WexInstaller.Controls
 
     #endregion
 
+    #region Event handling
+
+    private void featureList_AfterCheck(object sender, TreeViewEventArgs e)
+    {
+      Product p = e.Node.Tag as Product;
+      if (p != null)
+      {
+        if (p.Installed)
+          return;
+        p.ProposedInstalled = !p.ProposedInstalled;
+
+        List<ProductFeature> productFeatures = p.GetProductFeatures();
+        foreach (ProductFeature pf in productFeatures)
+          pf.ProposedInstalled = p.ProposedInstalled;
+
+        InvalidateCheckbox(e.Node); // Redraw only the single node whose state was changed.
+      }
+      else
+      {
+        ProductFeature pf = e.Node.Tag as ProductFeature;
+        pf.ProposedInstalled = !pf.ProposedInstalled;
+
+        // Check if at least one (visible) product feature for the owning product is proposed for installation.
+        // If so set also all invisible features too. Otherwise remove invisible features from the 
+        // installation list so that the entire product is set to no-installation.
+        ProductElement element = pf;
+        do
+        {
+          element = element.Parent;
+        } while (element != null && !(element is Product));
+
+        if (element is Product)
+        {
+          Product product = element as Product;
+          bool anyFeatureInstalled = false;
+          foreach (TreeNode node in featureList.Nodes)
+            anyFeatureInstalled |= (node.Tag as ProductFeature).ProposedInstalled;
+          foreach (ProductFeature feature in product.GetProductFeatures())
+            if (feature.Display == "0") // Invisible feature.
+              feature.ProposedInstalled = anyFeatureInstalled;
+        }
+
+        featureList.Invalidate(); // There can be sub features that changed state too, so redraw everything.
+      }
+      if (FeatureTree != null)
+        FeatureTree.Invalidate();
+    }
+
+    void featureTreeview_ProductInstallationProposalChanged(object sender, FeatureTreeView.ProductChangedEventArgs e)
+    {
+      featureList.Invalidate();
+    }
+
+    #endregion
+
   }
 }

=== modified file 'WexInstaller/Controls/FeatureTreeView.cs'
--- a/WexInstaller/Controls/FeatureTreeView.cs	2011-03-18 17:04:52 +0000
+++ b/WexInstaller/Controls/FeatureTreeView.cs	2011-03-21 10:34:56 +0000
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
 using System.Drawing;
 using System.Windows.Forms;
 using System.Windows.Forms.VisualStyles;
@@ -19,6 +20,16 @@ namespace WexInstaller.Controls
 
     public CatalogArchitecture Architecture { get; set; }
 
+    public class ProductChangedEventArgs : EventArgs
+    {
+      public ProductChangedEventArgs(Product product)
+      {
+        this.Product = product;
+      }
+
+      public Product Product{ get; set; }
+    }
+
     #endregion
 
     #region Construction and setup
@@ -127,6 +138,8 @@ namespace WexInstaller.Controls
 
       foreach (ProductFeature pf in productFeatures)
         pf.ProposedInstalled = doInstall;
+
+      OnProductInstallationProposalChanged(p);
     }
 
     private CheckBoxState GetNodeCheckboxState(TreeNode node)
@@ -162,7 +175,7 @@ namespace WexInstaller.Controls
 
     private CheckBoxState GetStateForProduct(Product p)
     {
-      List<ProductFeature> productFeatures = p.GetProductFeatures();
+      List<ProductFeature> productFeatures = p.GetAllProductFeatures();
       CheckBoxState state;
       if (productFeatures.Count > 0)
         state = GetStateFromFeatureList(productFeatures);
@@ -382,6 +395,16 @@ namespace WexInstaller.Controls
       base.OnNodeMouseClick(e);
     }
 
+    public event EventHandler<ProductChangedEventArgs> ProductInstallationProposalChanged;
+    protected internal void OnProductInstallationProposalChanged(Product product)
+    {
+      if (ProductInstallationProposalChanged != null)
+      {
+        ProductChangedEventArgs args = new ProductChangedEventArgs(product);
+        ProductInstallationProposalChanged(this, args);
+      }
+    }
+
     #endregion
 
     private void InitializeComponent()

=== modified file 'WexInstaller/InstallWizard/InstallProgressPanel.cs'
--- a/WexInstaller/InstallWizard/InstallProgressPanel.cs	2011-03-10 18:30:35 +0000
+++ b/WexInstaller/InstallWizard/InstallProgressPanel.cs	2011-03-21 12:59:48 +0000
@@ -59,7 +59,6 @@ namespace WexInstaller
       if (!finished)
       {
         started = false;
-        nextOk = true;
         progressLevels.Clear();
         NextButton.Text = Properties.Resources.NextButtonExecuteText;
       }
@@ -72,7 +71,8 @@ namespace WexInstaller
       {
         foreach (Product p in pc.Products)
         {
-          if (p.IsUpgrade || (p.ProposedInstalled && p.HasChanges()))
+          //if (p.IsUpgrade || (p.ProposedInstalled && p.HasChanges()))
+          if (p.IsUpgrade || p.HasChanges())
           {
             if (!p.FoundLocal)
               leftToDownload++;
@@ -95,6 +95,8 @@ namespace WexInstaller
       }
       leftToInstall = productList.Items.Count;
 
+      nextOk = leftToInstall > 0;
+
       base.Activate();
     }
 
@@ -319,7 +321,20 @@ namespace WexInstaller
               Resources.ContinueAfterDownloadErrorText, Resources.ConfirmContinueTitle,
               MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation);
             if (answer == DialogResult.Yes)
+            {
+              // Disable all try-again links before starting the installation.
+              foreach (ListViewItem item in productList.Items)
+              {
+                Product product = item.Tag as Product;
+                if ((product.CurrentState == ProductState.DownloadError) ||
+                  (product.CurrentState == ProductState.DownloadCancelled) ||
+                  (product.CurrentState == ProductState.DownloadNoMirror))
+                {
+                  item.SubItems[3].Text = "--";
+                }
+              }
               BeginInvoke(new MethodInvoker(InstallNextPackage));
+            }
           }
         }
       }

Attachment: [text/bzr-bundle] bzr/iggy@mysql.com-20110321152928-bwc47fymydaeyb78.bundle
Thread
bzr commit into wex-installer-1.0 branch (iggy:375) Iggy Galarza21 Mar