// 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.Symbols;
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, SymbolDefinitionType.ComboBox, "ListItem");
break;
case "Dialog":
this.ParseDialogElement(child);
break;
case "DialogRef":
this.ParseSimpleRefElement(child, SymbolDefinitions.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, SymbolDefinitionType.ListBox, "ListItem");
break;
case "ListView":
this.ParseControlGroupElement(child, SymbolDefinitionType.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, SymbolDefinitions.Property);
break;
case "UIRef":
this.ParseSimpleRefElement(child, SymbolDefinitions.WixUI);
break;
default:
this.Core.UnexpectedElement(node, child);
break;
}
}
else
{
this.Core.ParseExtensionElement(node, child);
}
}
if (null != id && !this.Core.EncounteredError)
{
this.Core.AddSymbol(new WixUISymbol(sourceLineNumbers, id));
}
}
///
/// Parses a list item element.
///
/// Element to parse.
/// Type of symbol to create.
/// Identifier of property referred to by list item.
/// Relative order of list items.
private void ParseListItemElement(XElement node, SymbolDefinitionType symbolType, 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 (SymbolDefinitionType.ListView == symbolType)
{
icon = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.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)
{
switch (symbolType)
{
case SymbolDefinitionType.ComboBox:
this.Core.AddSymbol(new ComboBoxSymbol(sourceLineNumbers)
{
Property = property,
Order = ++order,
Value = value,
Text = text,
});
break;
case SymbolDefinitionType.ListBox:
this.Core.AddSymbol(new ListBoxSymbol(sourceLineNumbers)
{
Property = property,
Order = ++order,
Value = value,
Text = text,
});
break;
case SymbolDefinitionType.ListView:
var symbol = this.Core.AddSymbol(new ListViewSymbol(sourceLineNumbers)
{
Property = property,
Order = ++order,
Value = value,
Text = text,
});
if (null != icon)
{
symbol.BinaryRef = icon;
}
break;
default:
throw new ArgumentOutOfRangeException(nameof(symbolType));
}
}
}
///
/// 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, SymbolDefinitions.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, SymbolDefinitions.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 symbol = this.Core.AddSymbol(new RadioButtonSymbol(sourceLineNumbers)
{
Property = property,
Order = ++order,
Value = value,
Text = text,
Help = (null != tooltip || null != help) ? String.Concat(tooltip, "|", help) : null
});
symbol.Set((int)RadioButtonSymbolFields.X, x);
symbol.Set((int)RadioButtonSymbolFields.Y, y);
symbol.Set((int)RadioButtonSymbolFields.Width, width);
symbol.Set((int)RadioButtonSymbolFields.Height, height);
}
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, SymbolDefinitions.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, SymbolDefinitions.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.
ControlSymbol lastTabSymbol = null;
string firstControl = null;
string defaultControl = null;
string cancelControl = null;
this.ParseControlElement(child, id.Id, SymbolDefinitionType.BBControl, ref lastTabSymbol, ref firstControl, ref defaultControl, ref cancelControl);
break;
default:
this.Core.UnexpectedElement(node, child);
break;
}
}
else
{
this.Core.ParseExtensionElement(node, child);
}
}
if (!this.Core.EncounteredError)
{
this.Core.AddSymbol(new BillboardSymbol(sourceLineNumbers, id)
{
FeatureRef = feature,
Action = action,
Ordering = order
});
}
}
///
/// Parses a control group element.
///
/// Element to parse.
/// Symbol type referred to by control group.
/// Expected child elements.
private void ParseControlGroupElement(XElement node, SymbolDefinitionType symbolType, 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, symbolType, 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, SymbolDefinitions.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 message = 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 "Message":
message = 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.VerifyNoInnerText(sourceLineNumbers, node);
this.Core.ParseForExtensionElements(node);
if (!this.Core.EncounteredError)
{
this.Core.AddSymbol(new ActionTextSymbol(sourceLineNumbers)
{
Action = action,
Description = message,
Template = template,
});
}
}
///
/// 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;
case "Value":
text = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
this.Core.VerifyNoInnerText(sourceLineNumbers, node);
if (null == id)
{
id = this.Core.CreateIdentifier("txt", text);
}
this.Core.ParseForExtensionElements(node);
if (!this.Core.EncounteredError)
{
this.Core.AddSymbol(new UITextSymbol(sourceLineNumbers, id)
{
Text = text,
});
}
}
///
/// Parses a text style element.
///
/// Element to parse.
private void ParseTextStyleElement(XElement node)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
Identifier id = null;
int? red = null;
int? green = null;
int? blue = null;
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)
{
red = redColor;
}
break;
case "Green":
var greenColor = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Byte.MaxValue);
if (CompilerConstants.IllegalInteger != greenColor)
{
green = greenColor;
}
break;
case "Blue":
var blueColor = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Byte.MaxValue);
if (CompilerConstants.IllegalInteger != blueColor)
{
blue = blueColor;
}
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(), (red ?? 0).ToString(), (green ?? 0).ToString(), (blue ?? 0).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)
{
this.Core.AddSymbol(new TextStyleSymbol(sourceLineNumbers, id)
{
FaceName = faceName,
LocalizedSize = size,
Red = red,
Green = green,
Blue = blue,
Bold = bold,
Italic = italic,
Strike = strike,
Underline = underline,
});
}
}
///
/// 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;
}
ControlSymbol lastTabSymbol = 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, SymbolDefinitionType.Control, ref lastTabSymbol, ref firstControl, ref defaultControl, ref cancelControl);
break;
default:
this.Core.UnexpectedElement(node, child);
break;
}
}
else
{
this.Core.ParseExtensionElement(node, child);
}
}
if (null != lastTabSymbol && null != lastTabSymbol.Control)
{
if (firstControl != lastTabSymbol.Control)
{
lastTabSymbol.NextControlRef = firstControl;
}
}
if (null == firstControl)
{
this.Core.Write(ErrorMessages.NoFirstControlSpecified(sourceLineNumbers, id.Id));
}
if (!this.Core.EncounteredError)
{
this.Core.AddSymbol(new DialogSymbol(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,
});
}
}
///
/// Parses a control element.
///
/// Element to parse.
/// Identifier for parent dialog.
/// Table control belongs in.
/// Last control 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, SymbolDefinitionType symbolType, ref ControlSymbol lastTabSymbol, ref string firstControl, ref string defaultControl, ref string cancelControl)
{
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;
string defaultCondition = null;
string enableCondition = null;
string disableCondition = null;
string hideCondition = null;
string showCondition = 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, WindowsInstallerTableDefinitions.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 "DefaultCondition":
defaultCondition = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "EnableCondition":
enableCondition = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "DisableCondition":
disableCondition = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "HideCondition":
hideCondition = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "ShowCondition":
showCondition = this.Core.GetAttributeValue(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;
}
}
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, SymbolDefinitionType.ComboBox, "ListItem");
break;
case "ListBox":
this.ParseControlGroupElement(child, SymbolDefinitionType.ListBox, "ListItem");
break;
case "ListView":
this.ParseControlGroupElement(child, SymbolDefinitionType.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;
case "Value":
text = this.Core.GetAttributeValue(childSourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(child, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(child, attrib);
}
}
this.Core.VerifyNoInnerText(sourceLineNumbers, node);
if (!String.IsNullOrEmpty(text) && null != sourceFile)
{
this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(childSourceLineNumbers, child.Name.LocalName, "SourceFile", "Value"));
}
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
IntermediateSymbol symbol = 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))
{
this.Core.AddSymbol(new CheckBoxSymbol(sourceLineNumbers)
{
Property = property,
Value = checkboxValue,
});
}
else
{
this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.CheckBox, checkBoxPropertyRef);
}
}
var id = new Identifier(controlId.Access, dialog, controlId.Id);
if (SymbolDefinitionType.BBControl == symbolType)
{
var bbSymbol = this.Core.AddSymbol(new BBControlSymbol(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 = String.IsNullOrEmpty(sourceFile) ? null : new IntermediateFieldPathValue { Path = sourceFile }
});
bbSymbol.Set((int)BBControlSymbolFields.X, x);
bbSymbol.Set((int)BBControlSymbolFields.Y, y);
bbSymbol.Set((int)BBControlSymbolFields.Width, width);
bbSymbol.Set((int)BBControlSymbolFields.Height, height);
symbol = bbSymbol;
}
else
{
var controlSymbol = this.Core.AddSymbol(new ControlSymbol(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 = String.IsNullOrEmpty(sourceFile) ? null : new IntermediateFieldPathValue { Path = sourceFile }
});
controlSymbol.Set((int)BBControlSymbolFields.X, x);
controlSymbol.Set((int)BBControlSymbolFields.Y, y);
controlSymbol.Set((int)BBControlSymbolFields.Width, width);
controlSymbol.Set((int)BBControlSymbolFields.Height, height);
symbol = controlSymbol;
}
if (!String.IsNullOrEmpty(defaultCondition))
{
this.Core.AddSymbol(new ControlConditionSymbol(sourceLineNumbers)
{
DialogRef = dialog,
ControlRef = controlId.Id,
Action = "Default",
Condition = defaultCondition,
});
}
if (!String.IsNullOrEmpty(enableCondition))
{
this.Core.AddSymbol(new ControlConditionSymbol(sourceLineNumbers)
{
DialogRef = dialog,
ControlRef = controlId.Id,
Action = "Enable",
Condition = enableCondition,
});
}
if (!String.IsNullOrEmpty(disableCondition))
{
this.Core.AddSymbol(new ControlConditionSymbol(sourceLineNumbers)
{
DialogRef = dialog,
ControlRef = controlId.Id,
Action = "Disable",
Condition = disableCondition,
});
}
if (!String.IsNullOrEmpty(hideCondition))
{
this.Core.AddSymbol(new ControlConditionSymbol(sourceLineNumbers)
{
DialogRef = dialog,
ControlRef = controlId.Id,
Action = "Hide",
Condition = hideCondition,
});
}
if (!String.IsNullOrEmpty(showCondition))
{
this.Core.AddSymbol(new ControlConditionSymbol(sourceLineNumbers)
{
DialogRef = dialog,
ControlRef = controlId.Id,
Action = "Show",
Condition = showCondition,
});
}
}
if (!notTabbable)
{
if (symbol is ControlSymbol controlSymbol)
{
if (null != lastTabSymbol)
{
lastTabSymbol.NextControlRef = controlSymbol.Control;
}
lastTabSymbol = controlSymbol;
}
else if (symbol != null)
{
this.Core.Write(ErrorMessages.TabbableControlNotAllowedInBillboard(sourceLineNumbers, node.Name.LocalName, controlType));
}
if (null == firstControl)
{
firstControl = controlId.Id;
}
}
// 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, SymbolDefinitions.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 "Condition":
condition = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
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, SymbolDefinitions.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);
}
}
this.Core.VerifyNoInnerText(sourceLineNumbers, 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)
{
this.Core.AddSymbol(new ControlEventSymbol(sourceLineNumbers)
{
DialogRef = dialog,
ControlRef = control,
Event = controlEvent ?? property,
Argument = argument,
Condition = condition,
Ordering = order
});
}
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) && !this.Core.ContainsProperty(argument))
{
this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.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, SymbolDefinitions.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)
{
this.Core.AddSymbol(new EventMappingSymbol(sourceLineNumbers)
{
DialogRef = dialog,
ControlRef = control,
Event = eventMapping,
Attribute = controlAttribute,
});
}
}
}
}