// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
namespace WixToolset.Core
{
using System;
using System.Collections;
using System.Xml.Linq;
using WixToolset.Data;
using WixToolset.Data.Tuples;
using WixToolset.Data.WindowsInstaller;
using WixToolset.Extensibility;
///
/// Compiler of the WiX toolset.
///
internal partial class Compiler : ICompiler
{
// NameToBit arrays
private static readonly string[] TextControlAttributes = { "Transparent", "NoPrefix", "NoWrap", "FormatSize", "UserLanguage" };
private static readonly string[] HyperlinkControlAttributes = { "Transparent" };
private static readonly string[] EditControlAttributes = { "Multiline", null, null, null, null, "Password" };
private static readonly string[] ProgressControlAttributes = { "ProgressBlocks" };
private static readonly string[] VolumeControlAttributes = { "Removable", "Fixed", "Remote", "CDROM", "RAMDisk", "Floppy", "ShowRollbackCost" };
private static readonly string[] ListboxControlAttributes = { "Sorted", null, null, null, "UserLanguage" };
private static readonly string[] ListviewControlAttributes = { "Sorted", null, null, null, "FixedSize", "Icon16", "Icon32" };
private static readonly string[] ComboboxControlAttributes = { "Sorted", "ComboList", null, null, "UserLanguage" };
private static readonly string[] RadioControlAttributes = { "Image", "PushLike", "Bitmap", "Icon", "FixedSize", "Icon16", "Icon32", null, "HasBorder" };
private static readonly string[] ButtonControlAttributes = { "Image", null, "Bitmap", "Icon", "FixedSize", "Icon16", "Icon32", "ElevationShield" };
private static readonly string[] IconControlAttributes = { "Image", null, null, null, "FixedSize", "Icon16", "Icon32" };
private static readonly string[] BitmapControlAttributes = { "Image", null, null, null, "FixedSize" };
private static readonly string[] CheckboxControlAttributes = { null, "PushLike", "Bitmap", "Icon", "FixedSize", "Icon16", "Icon32" };
///
/// Parses UI elements.
///
/// Element to parse.
private void ParseUIElement(XElement node)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
Identifier id = null;
var embeddedUICount = 0;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Id":
id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
foreach (var child in node.Elements())
{
if (CompilerCore.WixNamespace == child.Name.Namespace)
{
switch (child.Name.LocalName)
{
case "BillboardAction":
this.ParseBillboardActionElement(child);
break;
case "ComboBox":
this.ParseControlGroupElement(child, TupleDefinitionType.ComboBox, "ListItem");
break;
case "Dialog":
this.ParseDialogElement(child);
break;
case "DialogRef":
this.ParseSimpleRefElement(child, "Dialog");
break;
case "EmbeddedUI":
if (0 < embeddedUICount) // there can be only one embedded UI
{
var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
this.Core.Write(ErrorMessages.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, child.Name.LocalName));
}
this.ParseEmbeddedUIElement(child);
++embeddedUICount;
break;
case "Error":
this.ParseErrorElement(child);
break;
case "ListBox":
this.ParseControlGroupElement(child, TupleDefinitionType.ListBox, "ListItem");
break;
case "ListView":
this.ParseControlGroupElement(child, TupleDefinitionType.ListView, "ListItem");
break;
case "ProgressText":
this.ParseActionTextElement(child);
break;
case "Publish":
var order = 0;
this.ParsePublishElement(child, null, null, ref order);
break;
case "RadioButtonGroup":
var radioButtonType = this.ParseRadioButtonGroupElement(child, null, RadioButtonType.NotSet);
if (RadioButtonType.Bitmap == radioButtonType || RadioButtonType.Icon == radioButtonType)
{
var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
this.Core.Write(ErrorMessages.RadioButtonBitmapAndIconDisallowed(childSourceLineNumbers));
}
break;
case "TextStyle":
this.ParseTextStyleElement(child);
break;
case "UIText":
this.ParseUITextElement(child);
break;
// the following are available indentically under the UI and Product elements for document organization use only
case "AdminUISequence":
this.ParseSequenceElement(child, SequenceTable.AdminUISequence);
break;
case "InstallUISequence":
this.ParseSequenceElement(child, SequenceTable.InstallUISequence);
break;
case "Binary":
this.ParseBinaryElement(child);
break;
case "Property":
this.ParsePropertyElement(child);
break;
case "PropertyRef":
this.ParseSimpleRefElement(child, "Property");
break;
case "UIRef":
this.ParseSimpleRefElement(child, "WixUI");
break;
default:
this.Core.UnexpectedElement(node, child);
break;
}
}
else
{
this.Core.ParseExtensionElement(node, child);
}
}
if (null != id && !this.Core.EncounteredError)
{
var tuple = new WixUITuple(sourceLineNumbers, id);
this.Core.AddTuple(tuple);
}
}
///
/// Parses a list item element.
///
/// Element to parse.
/// Table to add row to.
/// Identifier of property referred to by list item.
/// Relative order of list items.
private void ParseListItemElement(XElement node, TupleDefinitionType tupleType, string property, ref int order)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
string icon = null;
string text = null;
string value = null;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Icon":
if (TupleDefinitionType.ListView == tupleType)
{
icon = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
this.Core.CreateSimpleReference(sourceLineNumbers, "Binary", icon);
}
else
{
this.Core.Write(ErrorMessages.IllegalAttributeExceptOnElement(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "ListView"));
}
break;
case "Text":
text = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Value":
value = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (null == value)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value"));
}
this.Core.ParseForExtensionElements(node);
if (!this.Core.EncounteredError)
{
var tuple = this.Core.CreateTuple(sourceLineNumbers, tupleType);
tuple.Set(0, property);
tuple.Set(1, ++order);
tuple.Set(2, value);
tuple.Set(3, text);
if (null != icon)
{
tuple.Set(4, icon);
}
}
}
///
/// Parses a radio button element.
///
/// Element to parse.
/// Identifier of property referred to by radio button.
/// Relative order of radio buttons.
/// Type of this radio button.
private RadioButtonType ParseRadioButtonElement(XElement node, string property, ref int order)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
var type = RadioButtonType.NotSet;
string value = null;
string x = null;
string y = null;
string width = null;
string height = null;
string text = null;
string tooltip = null;
string help = null;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Bitmap":
if (RadioButtonType.NotSet != type)
{
this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttributes(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "Icon", "Text"));
}
text = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
this.Core.CreateSimpleReference(sourceLineNumbers, "Binary", text);
type = RadioButtonType.Bitmap;
break;
case "Height":
height = this.Core.GetAttributeLocalizableIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue);
break;
case "Help":
help = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Icon":
if (RadioButtonType.NotSet != type)
{
this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttributes(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "Bitmap", "Text"));
}
text = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
this.Core.CreateSimpleReference(sourceLineNumbers, "Binary", text);
type = RadioButtonType.Icon;
break;
case "Text":
if (RadioButtonType.NotSet != type)
{
this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "Bitmap", "Icon"));
}
text = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
type = RadioButtonType.Text;
break;
case "ToolTip":
tooltip = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Value":
value = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Width":
width = this.Core.GetAttributeLocalizableIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue);
break;
case "X":
x = this.Core.GetAttributeLocalizableIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue);
break;
case "Y":
y = this.Core.GetAttributeLocalizableIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (null == value)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value"));
}
if (null == x)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "X"));
}
if (null == y)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Y"));
}
if (null == width)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Width"));
}
if (null == height)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Height"));
}
this.Core.ParseForExtensionElements(node);
if (!this.Core.EncounteredError)
{
var tuple = new RadioButtonTuple(sourceLineNumbers)
{
Property = property,
Order = ++order,
Value = value,
Text = text,
Help = (null != tooltip || null != help) ? String.Concat(tooltip, "|", help) : null
};
tuple.Set((int)RadioButtonTupleFields.X, x);
tuple.Set((int)RadioButtonTupleFields.Y, y);
tuple.Set((int)RadioButtonTupleFields.Width, width);
tuple.Set((int)RadioButtonTupleFields.Height, height);
this.Core.AddTuple(tuple);
}
return type;
}
///
/// Parses a billboard element.
///
/// Element to parse.
private void ParseBillboardActionElement(XElement node)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
string action = null;
var order = 0;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Id":
action = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
this.Core.CreateSimpleReference(sourceLineNumbers, "WixAction", "InstallExecuteSequence", action);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (null == action)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
}
foreach (var child in node.Elements())
{
if (CompilerCore.WixNamespace == child.Name.Namespace)
{
switch (child.Name.LocalName)
{
case "Billboard":
order = order + 1;
this.ParseBillboardElement(child, action, order);
break;
default:
this.Core.UnexpectedElement(node, child);
break;
}
}
else
{
this.Core.ParseExtensionElement(node, child);
}
}
}
///
/// Parses a billboard element.
///
/// Element to parse.
/// Action for the billboard.
/// Order of the billboard.
private void ParseBillboardElement(XElement node, string action, int order)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
Identifier id = null;
string feature = null;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Id":
id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
break;
case "Feature":
feature = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
this.Core.CreateSimpleReference(sourceLineNumbers, "Feature", feature);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (null == id)
{
id = this.Core.CreateIdentifier("bil", action, order.ToString(), feature);
}
foreach (var child in node.Elements())
{
if (CompilerCore.WixNamespace == child.Name.Namespace)
{
switch (child.Name.LocalName)
{
case "Control":
// These are all thrown away.
IntermediateTuple lastTabRow = null;
string firstControl = null;
string defaultControl = null;
string cancelControl = null;
this.ParseControlElement(child, id.Id, TupleDefinitionType.BBControl, ref lastTabRow, ref firstControl, ref defaultControl, ref cancelControl, false);
break;
default:
this.Core.UnexpectedElement(node, child);
break;
}
}
else
{
this.Core.ParseExtensionElement(node, child);
}
}
if (!this.Core.EncounteredError)
{
var tuple = new BillboardTuple(sourceLineNumbers, id)
{
FeatureRef = feature,
Action = action,
Ordering = order
};
this.Core.AddTuple(tuple);
}
}
///
/// Parses a control group element.
///
/// Element to parse.
/// Table referred to by control group.
/// Expected child elements.
private void ParseControlGroupElement(XElement node, TupleDefinitionType tupleType, string childTag)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
var order = 0;
string property = null;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Property":
property = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (null == property)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Property"));
}
foreach (var child in node.Elements())
{
if (CompilerCore.WixNamespace == child.Name.Namespace)
{
if (childTag != child.Name.LocalName)
{
this.Core.UnexpectedElement(node, child);
}
switch (child.Name.LocalName)
{
case "ListItem":
this.ParseListItemElement(child, tupleType, property, ref order);
break;
case "Property":
this.ParsePropertyElement(child);
break;
default:
this.Core.UnexpectedElement(node, child);
break;
}
}
else
{
this.Core.ParseExtensionElement(node, child);
}
}
}
///
/// Parses a radio button control group element.
///
/// Element to parse.
/// Property associated with this radio button group.
/// Specifies the current type of radio buttons in the group.
/// The current type of radio buttons in the group.
private RadioButtonType ParseRadioButtonGroupElement(XElement node, string property, RadioButtonType groupType)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
var order = 0;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Property":
property = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
this.Core.CreateSimpleReference(sourceLineNumbers, "Property", property);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (null == property)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Property"));
}
foreach (var child in node.Elements())
{
if (CompilerCore.WixNamespace == child.Name.Namespace)
{
switch (child.Name.LocalName)
{
case "RadioButton":
var type = this.ParseRadioButtonElement(child, property, ref order);
if (RadioButtonType.NotSet == groupType)
{
groupType = type;
}
else if (groupType != type)
{
var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
this.Core.Write(ErrorMessages.RadioButtonTypeInconsistent(childSourceLineNumbers));
}
break;
default:
this.Core.UnexpectedElement(node, child);
break;
}
}
else
{
this.Core.ParseExtensionElement(node, child);
}
}
return groupType;
}
///
/// Parses an action text element.
///
/// Element to parse.
private void ParseActionTextElement(XElement node)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
string action = null;
string template = null;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Action":
action = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Template":
template = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (null == action)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Action"));
}
this.Core.ParseForExtensionElements(node);
if (!this.Core.EncounteredError)
{
var tuple = new ActionTextTuple(sourceLineNumbers)
{
Action = action,
Description = Common.GetInnerText(node),
Template = template
};
this.Core.AddTuple(tuple);
}
}
///
/// Parses an ui text element.
///
/// Element to parse.
private void ParseUITextElement(XElement node)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
Identifier id = null;
string text = null;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Id":
id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
text = Common.GetInnerText(node);
if (null == id)
{
id = this.Core.CreateIdentifier("txt", text);
}
this.Core.ParseForExtensionElements(node);
if (!this.Core.EncounteredError)
{
var tuple = new UITextTuple(sourceLineNumbers, id)
{
Text = text,
};
this.Core.AddTuple(tuple);
}
}
///
/// Parses a text style element.
///
/// Element to parse.
private void ParseTextStyleElement(XElement node)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
Identifier id = null;
var color = CompilerConstants.IntegerNotSet;
var bold = false;
var italic = false;
var strike = false;
var underline = false;
string faceName = null;
var size = "0";
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Id":
id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
break;
// RGB Values
case "Red":
var redColor = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Byte.MaxValue);
if (CompilerConstants.IllegalInteger != redColor)
{
if (CompilerConstants.IntegerNotSet == color)
{
color = redColor;
}
else
{
color += redColor;
}
}
break;
case "Green":
var greenColor = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Byte.MaxValue);
if (CompilerConstants.IllegalInteger != greenColor)
{
if (CompilerConstants.IntegerNotSet == color)
{
color = greenColor * 256;
}
else
{
color += greenColor * 256;
}
}
break;
case "Blue":
var blueColor = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Byte.MaxValue);
if (CompilerConstants.IllegalInteger != blueColor)
{
if (CompilerConstants.IntegerNotSet == color)
{
color = blueColor * 65536;
}
else
{
color += blueColor * 65536;
}
}
break;
// Style values
case "Bold":
bold = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "Italic":
italic = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "Strike":
strike = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "Underline":
underline = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
// Font values
case "FaceName":
faceName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Size":
size = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (null == id)
{
this.Core.CreateIdentifier("txs", faceName, size.ToString(), color.ToString(), bold.ToString(), italic.ToString(), strike.ToString(), underline.ToString());
}
if (null == faceName)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "FaceName"));
}
this.Core.ParseForExtensionElements(node);
if (!this.Core.EncounteredError)
{
var tuple = new TextStyleTuple(sourceLineNumbers, id)
{
FaceName = faceName,
Color = color,
Bold = bold,
Italic = italic,
Strike = strike,
Underline = underline,
};
tuple.Set((int)TextStyleTupleFields.Size, size);
this.Core.AddTuple(tuple);
}
}
///
/// Parses a dialog element.
///
/// Element to parse.
private void ParseDialogElement(XElement node)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
Identifier id = null;
var hidden = false;
var modal = true;
var minimize = true;
var customPalette = false;
var errorDialog = false;
var keepModeless = false;
var height = 0;
string title = null;
var leftScroll = false;
var rightAligned = false;
var rightToLeft = false;
var systemModal = false;
var trackDiskSpace = false;
var width = 0;
var x = 50;
var y = 50;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Id":
id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
break;
case "Height":
height = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue);
break;
case "Title":
title = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Width":
width = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue);
break;
case "X":
x = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, 100);
break;
case "Y":
y = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, 100);
break;
case "CustomPalette":
customPalette = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "ErrorDialog":
errorDialog = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "Hidden":
hidden = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "KeepModeless":
keepModeless = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "LeftScroll":
leftScroll = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "Modeless":
modal = YesNoType.Yes != this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "NoMinimize":
minimize = YesNoType.Yes != this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "RightAligned":
rightAligned = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "RightToLeft":
rightToLeft = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "SystemModal":
systemModal = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "TrackDiskSpace":
trackDiskSpace = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (null == id)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
id = Identifier.Invalid;
}
IntermediateTuple lastTabRow = null;
string cancelControl = null;
string defaultControl = null;
string firstControl = null;
foreach (var child in node.Elements())
{
if (CompilerCore.WixNamespace == child.Name.Namespace)
{
switch (child.Name.LocalName)
{
case "Control":
this.ParseControlElement(child, id.Id, TupleDefinitionType.Control, ref lastTabRow, ref firstControl, ref defaultControl, ref cancelControl, trackDiskSpace);
break;
default:
this.Core.UnexpectedElement(node, child);
break;
}
}
else
{
this.Core.ParseExtensionElement(node, child);
}
}
if (null != lastTabRow && null != lastTabRow[1])
{
if (firstControl != lastTabRow[1].ToString())
{
lastTabRow.Set(10, firstControl);
}
}
if (null == firstControl)
{
this.Core.Write(ErrorMessages.NoFirstControlSpecified(sourceLineNumbers, id.Id));
}
if (!this.Core.EncounteredError)
{
var tuple = new DialogTuple(sourceLineNumbers, id)
{
HCentering = x,
VCentering = y,
Width = width,
Height = height,
CustomPalette = customPalette,
ErrorDialog = errorDialog,
Visible = !hidden,
Modal = modal,
KeepModeless = keepModeless,
LeftScroll = leftScroll,
Minimize = minimize,
RightAligned = rightAligned,
RightToLeft = rightToLeft,
SystemModal = systemModal,
TrackDiskSpace = trackDiskSpace,
Title = title,
FirstControlRef = firstControl,
DefaultControlRef = defaultControl,
CancelControlRef = cancelControl,
};
this.Core.AddTuple(tuple);
}
}
///
/// Parses a control element.
///
/// Element to parse.
/// Identifier for parent dialog.
/// Table control belongs in.
/// Last row in the tab order.
/// Name of the first control in the tab order.
/// Name of the default control.
/// Name of the candle control.
/// True if the containing dialog tracks disk space.
private void ParseControlElement(XElement node, string dialog, TupleDefinitionType tupleType, ref IntermediateTuple lastTabTuple, ref string firstControl, ref string defaultControl, ref string cancelControl, bool trackDiskSpace)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
Identifier controlId = null;
var bits = new BitArray(32);
string checkBoxPropertyRef = null;
string checkboxValue = null;
string controlType = null;
var disabled = false;
string height = null;
string help = null;
var isCancel = false;
var isDefault = false;
var notTabbable = false;
string property = null;
var publishOrder = 0;
string sourceFile = null;
string text = null;
string tooltip = null;
var radioButtonsType = RadioButtonType.NotSet;
string width = null;
string x = null;
string y = null;
var hidden = false;
var sunken = false;
var indirect = false;
var integer = false;
var rightToLeft = false;
var rightAligned = false;
var leftScroll = false;
// The rest of the method relies on the control's Type, so we have to get that first.
var typeAttribute = node.Attribute("Type");
if (null == typeAttribute)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Type"));
}
else
{
controlType = this.Core.GetAttributeValue(sourceLineNumbers, typeAttribute);
}
string[] specialAttributes;
switch (controlType)
{
case "Billboard":
specialAttributes = null;
notTabbable = true;
disabled = true;
this.Core.EnsureTable(sourceLineNumbers, "Billboard");
break;
case "Bitmap":
specialAttributes = BitmapControlAttributes;
notTabbable = true;
disabled = true;
break;
case "CheckBox":
specialAttributes = CheckboxControlAttributes;
break;
case "ComboBox":
specialAttributes = ComboboxControlAttributes;
break;
case "DirectoryCombo":
specialAttributes = VolumeControlAttributes;
break;
case "DirectoryList":
specialAttributes = null;
break;
case "Edit":
specialAttributes = EditControlAttributes;
break;
case "GroupBox":
specialAttributes = null;
notTabbable = true;
break;
case "Hyperlink":
specialAttributes = HyperlinkControlAttributes;
break;
case "Icon":
specialAttributes = IconControlAttributes;
notTabbable = true;
disabled = true;
break;
case "Line":
specialAttributes = null;
notTabbable = true;
disabled = true;
break;
case "ListBox":
specialAttributes = ListboxControlAttributes;
break;
case "ListView":
specialAttributes = ListviewControlAttributes;
break;
case "MaskedEdit":
specialAttributes = EditControlAttributes;
break;
case "PathEdit":
specialAttributes = EditControlAttributes;
break;
case "ProgressBar":
specialAttributes = ProgressControlAttributes;
notTabbable = true;
disabled = true;
break;
case "PushButton":
specialAttributes = ButtonControlAttributes;
break;
case "RadioButtonGroup":
specialAttributes = RadioControlAttributes;
break;
case "ScrollableText":
specialAttributes = null;
break;
case "SelectionTree":
specialAttributes = null;
break;
case "Text":
specialAttributes = TextControlAttributes;
notTabbable = true;
break;
case "VolumeCostList":
specialAttributes = VolumeControlAttributes;
notTabbable = true;
break;
case "VolumeSelectCombo":
specialAttributes = VolumeControlAttributes;
break;
default:
specialAttributes = null;
notTabbable = true;
break;
}
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Id":
controlId = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
break;
case "Type": // already processed
break;
case "Cancel":
isCancel = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "CheckBoxPropertyRef":
checkBoxPropertyRef = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "CheckBoxValue":
checkboxValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Default":
isDefault = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "Height":
height = this.Core.GetAttributeLocalizableIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue);
break;
case "Help":
help = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "IconSize":
var iconSizeValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
if (null != specialAttributes)
{
switch (iconSizeValue)
{
case "16":
this.Core.TrySetBitFromName(specialAttributes, "Icon16", YesNoType.Yes, bits, 16);
break;
case "32":
this.Core.TrySetBitFromName(specialAttributes, "Icon32", YesNoType.Yes, bits, 16);
break;
case "48":
this.Core.TrySetBitFromName(specialAttributes, "Icon16", YesNoType.Yes, bits, 16);
this.Core.TrySetBitFromName(specialAttributes, "Icon32", YesNoType.Yes, bits, 16);
break;
case "":
break;
default:
this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, iconSizeValue, "16", "32", "48"));
break;
}
//if (0 < iconSizeValue.Length)
//{
// var iconsSizeType = Wix.Control.ParseIconSizeType(iconSizeValue);
// switch (iconsSizeType)
// {
// case Wix.Control.IconSizeType.Item16:
// this.Core.TrySetBitFromName(specialAttributes, "Icon16", YesNoType.Yes, bits, 16);
// break;
// case Wix.Control.IconSizeType.Item32:
// this.Core.TrySetBitFromName(specialAttributes, "Icon32", YesNoType.Yes, bits, 16);
// break;
// case Wix.Control.IconSizeType.Item48:
// this.Core.TrySetBitFromName(specialAttributes, "Icon16", YesNoType.Yes, bits, 16);
// this.Core.TrySetBitFromName(specialAttributes, "Icon32", YesNoType.Yes, bits, 16);
// break;
// default:
// this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, iconSizeValue, "16", "32", "48"));
// break;
// }
//}
}
else
{
this.Core.Write(ErrorMessages.IllegalAttributeValueWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, iconSizeValue, "Type"));
}
break;
case "Property":
property = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "TabSkip":
notTabbable = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "Text":
text = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "ToolTip":
tooltip = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Width":
width = this.Core.GetAttributeLocalizableIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue);
break;
case "X":
x = this.Core.GetAttributeLocalizableIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue);
break;
case "Y":
y = this.Core.GetAttributeLocalizableIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue);
break;
case "Disabled":
disabled = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "Hidden":
hidden = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "Sunken":
sunken = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "Indirect":
indirect = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "Integer":
integer = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "RightToLeft":
rightToLeft = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "RightAligned":
rightAligned = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "LeftScroll":
leftScroll = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
default:
var attribValue = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
if (null == specialAttributes || !this.Core.TrySetBitFromName(specialAttributes, attrib.Name.LocalName, attribValue, bits, 16))
{
this.Core.UnexpectedAttribute(node, attrib);
}
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
var attributes = this.Core.CreateIntegerFromBitArray(bits);
//if (disabled)
//{
// attributes |= WindowsInstallerConstants.MsidbControlAttributesEnabled; // bit will be inverted when stored
//}
if (null == height)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Height"));
}
if (null == width)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Width"));
}
if (null == x)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "X"));
}
if (null == y)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Y"));
}
if (null == controlId)
{
controlId = this.Core.CreateIdentifier("ctl", dialog, x, y, height, width);
}
if (isCancel)
{
cancelControl = controlId.Id;
}
if (isDefault)
{
defaultControl = controlId.Id;
}
foreach (var child in node.Elements())
{
if (CompilerCore.WixNamespace == child.Name.Namespace)
{
var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
switch (child.Name.LocalName)
{
case "Binary":
this.ParseBinaryElement(child);
break;
case "ComboBox":
this.ParseControlGroupElement(child, TupleDefinitionType.ComboBox, "ListItem");
break;
case "Condition":
this.ParseConditionElement(child, node.Name.LocalName, controlId.Id, dialog);
break;
case "ListBox":
this.ParseControlGroupElement(child, TupleDefinitionType.ListBox, "ListItem");
break;
case "ListView":
this.ParseControlGroupElement(child, TupleDefinitionType.ListView, "ListItem");
break;
case "Property":
this.ParsePropertyElement(child);
break;
case "Publish":
this.ParsePublishElement(child, dialog ?? String.Empty, controlId.Id, ref publishOrder);
break;
case "RadioButtonGroup":
radioButtonsType = this.ParseRadioButtonGroupElement(child, property, radioButtonsType);
break;
case "Subscribe":
this.ParseSubscribeElement(child, dialog, controlId.Id);
break;
case "Text":
foreach (var attrib in child.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "SourceFile":
sourceFile = this.Core.GetAttributeValue(childSourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(child, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(child, attrib);
}
}
text = Common.GetInnerText(child);
if (!String.IsNullOrEmpty(text) && null != sourceFile)
{
this.Core.Write(ErrorMessages.IllegalAttributeWithInnerText(childSourceLineNumbers, child.Name.LocalName, "SourceFile"));
}
break;
default:
this.Core.UnexpectedElement(node, child);
break;
}
}
else
{
this.Core.ParseExtensionElement(node, child);
}
}
// If the radio buttons have icons, then we need to add the icon attribute.
switch (radioButtonsType)
{
case RadioButtonType.Bitmap:
attributes |= WindowsInstallerConstants.MsidbControlAttributesBitmap;
break;
case RadioButtonType.Icon:
attributes |= WindowsInstallerConstants.MsidbControlAttributesIcon;
break;
case RadioButtonType.Text:
// Text is the default so nothing needs to be added bits
break;
}
// the logic for creating control rows is a little tricky because of the way tabable controls are set
IntermediateTuple tuple = null;
if (!this.Core.EncounteredError)
{
if ("CheckBox" == controlType)
{
if (String.IsNullOrEmpty(property) && String.IsNullOrEmpty(checkBoxPropertyRef))
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Property", "CheckBoxPropertyRef", true));
}
else if (!String.IsNullOrEmpty(property) && !String.IsNullOrEmpty(checkBoxPropertyRef))
{
this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Property", "CheckBoxPropertyRef"));
}
else if (!String.IsNullOrEmpty(property))
{
var checkBoxTuple = new CheckBoxTuple(sourceLineNumbers)
{
Property = property,
Value = checkboxValue
};
this.Core.AddTuple(checkBoxTuple);
}
else
{
this.Core.CreateSimpleReference(sourceLineNumbers, "CheckBox", checkBoxPropertyRef);
}
}
var id = new Identifier(controlId.Access, dialog, controlId.Id);
if (TupleDefinitionType.BBControl == tupleType)
{
var bbTuple = new BBControlTuple(sourceLineNumbers, id)
{
BillboardRef = dialog,
BBControl = controlId.Id,
Type = controlType,
Attributes = attributes,
Enabled = !disabled,
Indirect = indirect,
Integer = integer,
LeftScroll = leftScroll,
RightAligned = rightAligned,
RightToLeft = rightToLeft,
Sunken = sunken,
Visible = !hidden,
Text = text,
SourceFile = sourceFile
};
bbTuple.Set((int)BBControlTupleFields.X, x);
bbTuple.Set((int)BBControlTupleFields.Y, y);
bbTuple.Set((int)BBControlTupleFields.Width, width);
bbTuple.Set((int)BBControlTupleFields.Height, height);
this.Core.AddTuple(bbTuple);
tuple = bbTuple;
}
else
{
var controlTuple = new ControlTuple(sourceLineNumbers, id)
{
DialogRef = dialog,
Control = controlId.Id,
Type = controlType,
Attributes = attributes,
Enabled = !disabled,
Indirect = indirect,
Integer = integer,
LeftScroll = leftScroll,
RightAligned = rightAligned,
RightToLeft = rightToLeft,
Sunken = sunken,
Visible = !hidden,
Property = !String.IsNullOrEmpty(property) ? property : checkBoxPropertyRef,
Text = text,
Help = (null == tooltip && null == help) ? null : String.Concat(tooltip, "|", help), // Separator is required, even if only one is non-null.};
SourceFile = sourceFile
};
controlTuple.Set((int)BBControlTupleFields.X, x);
controlTuple.Set((int)BBControlTupleFields.Y, y);
controlTuple.Set((int)BBControlTupleFields.Width, width);
controlTuple.Set((int)BBControlTupleFields.Height, height);
this.Core.AddTuple(controlTuple);
tuple = controlTuple;
}
}
if (!notTabbable)
{
if (TupleDefinitionType.BBControl == tupleType)
{
this.Core.Write(ErrorMessages.TabbableControlNotAllowedInBillboard(sourceLineNumbers, node.Name.LocalName, controlType));
}
if (null == firstControl)
{
firstControl = controlId.Id;
}
if (null != lastTabTuple)
{
lastTabTuple.Set(10, controlId.Id);
}
lastTabTuple = tuple;
}
// bitmap and icon controls contain a foreign key into the binary table in the text column;
// add a reference if the identifier of the binary entry is known during compilation
if (("Bitmap" == controlType || "Icon" == controlType) && Common.IsIdentifier(text))
{
this.Core.CreateSimpleReference(sourceLineNumbers, "Binary", text);
}
}
///
/// Parses a publish control event element.
///
/// Element to parse.
/// Identifier of parent dialog.
/// Identifier of parent control.
/// Relative order of controls.
private void ParsePublishElement(XElement node, string dialog, string control, ref int order)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
string argument = null;
string condition = null;
string controlEvent = null;
string property = null;
// give this control event a unique ordering
order++;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Control":
if (null != control)
{
this.Core.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, node.Parent.Name.LocalName));
}
control = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
break;
case "Dialog":
if (null != dialog)
{
this.Core.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, node.Parent.Name.LocalName));
}
dialog = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
this.Core.CreateSimpleReference(sourceLineNumbers, "Dialog", dialog);
break;
case "Event":
controlEvent = Compiler.UppercaseFirstChar(this.Core.GetAttributeValue(sourceLineNumbers, attrib));
break;
case "Order":
order = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, 2147483647);
break;
case "Property":
property = String.Concat("[", this.Core.GetAttributeValue(sourceLineNumbers, attrib), "]");
break;
case "Value":
argument = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
condition = this.Core.GetConditionInnerText(node);
if (null == control)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Control"));
}
if (null == dialog)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Dialog"));
}
if (null == controlEvent && null == property) // need to specify at least one
{
this.Core.Write(ErrorMessages.ExpectedAttributes(sourceLineNumbers, node.Name.LocalName, "Event", "Property"));
}
else if (null != controlEvent && null != property) // cannot specify both
{
this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Event", "Property"));
}
if (null == argument)
{
if (null != controlEvent)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value", "Event"));
}
else if (null != property)
{
// if this is setting a property to null, put a special value in the argument column
argument = "{}";
}
}
this.Core.ParseForExtensionElements(node);
if (!this.Core.EncounteredError)
{
var tuple = new ControlEventTuple(sourceLineNumbers)
{
DialogRef = dialog,
ControlRef = control,
Event = controlEvent ?? property,
Argument = argument,
Condition = condition,
Ordering = order
};
this.Core.AddTuple(tuple);
}
if ("DoAction" == controlEvent && null != argument)
{
// if we're not looking at a standard action or a formatted string then create a reference
// to the custom action.
if (!WindowsInstallerStandard.IsStandardAction(argument) && !Common.ContainsProperty(argument))
{
this.Core.CreateSimpleReference(sourceLineNumbers, "CustomAction", argument);
}
}
// if we're referring to a dialog but not through a property, add it to the references
if (("NewDialog" == controlEvent || "SpawnDialog" == controlEvent || "SpawnWaitDialog" == controlEvent || "SelectionBrowse" == controlEvent) && Common.IsIdentifier(argument))
{
this.Core.CreateSimpleReference(sourceLineNumbers, "Dialog", argument);
}
}
///
/// Parses a control subscription element.
///
/// Element to parse.
/// Identifier of dialog.
/// Identifier of control.
private void ParseSubscribeElement(XElement node, string dialog, string control)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
string controlAttribute = null;
string eventMapping = null;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Attribute":
controlAttribute = Compiler.UppercaseFirstChar(this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib));
break;
case "Event":
eventMapping = Compiler.UppercaseFirstChar(this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib));
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
this.Core.ParseForExtensionElements(node);
if (!this.Core.EncounteredError)
{
var tuple = new EventMappingTuple(sourceLineNumbers)
{
DialogRef = dialog,
ControlRef = control,
Event = eventMapping,
Attribute = controlAttribute
}; ;
this.Core.AddTuple(tuple);
}
}
}
}