main
cs 1,809 lines 76.9 KB
Raw
1 // 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.
2
3 namespace WixToolset.Core
4 {
5 using System;
6 using System.Collections;
7 using System.Xml.Linq;
8 using WixToolset.Data;
9 using WixToolset.Data.Symbols;
10 using WixToolset.Data.WindowsInstaller;
11 using WixToolset.Extensibility;
12
13 /// <summary>
14 /// Compiler of the WiX toolset.
15 /// </summary>
16 internal partial class Compiler : ICompiler
17 {
18 // NameToBit arrays
19 private static readonly string[] TextControlAttributes = { "Transparent", "NoPrefix", "NoWrap", "FormatSize", "UserLanguage" };
20 private static readonly string[] HyperlinkControlAttributes = { "Transparent" };
21 private static readonly string[] EditControlAttributes = { "Multiline", null, null, null, null, "Password" };
22 private static readonly string[] ProgressControlAttributes = { "ProgressBlocks" };
23 private static readonly string[] VolumeControlAttributes = { "Removable", "Fixed", "Remote", "CDROM", "RAMDisk", "Floppy", "ShowRollbackCost" };
24 private static readonly string[] ListboxControlAttributes = { "Sorted", null, null, null, "UserLanguage" };
25 private static readonly string[] ListviewControlAttributes = { "Sorted", null, null, null, "FixedSize", "Icon16", "Icon32" };
26 private static readonly string[] ComboboxControlAttributes = { "Sorted", "ComboList", null, null, "UserLanguage" };
27 private static readonly string[] RadioControlAttributes = { "Image", "PushLike", "Bitmap", "Icon", "FixedSize", "Icon16", "Icon32", null, "HasBorder" };
28 private static readonly string[] ButtonControlAttributes = { "Image", null, "Bitmap", "Icon", "FixedSize", "Icon16", "Icon32", "ElevationShield" };
29 private static readonly string[] IconControlAttributes = { "Image", null, null, null, "FixedSize", "Icon16", "Icon32" };
30 private static readonly string[] BitmapControlAttributes = { "Image", null, null, null, "FixedSize" };
31 private static readonly string[] CheckboxControlAttributes = { null, "PushLike", "Bitmap", "Icon", "FixedSize", "Icon16", "Icon32" };
32
33 /// <summary>
34 /// Parses UI elements.
35 /// </summary>
36 /// <param name="node">Element to parse.</param>
37 private void ParseUIElement(XElement node)
38 {
39 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
40 Identifier id = null;
41 var embeddedUICount = 0;
42
43 foreach (var attrib in node.Attributes())
44 {
45 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
46 {
47 switch (attrib.Name.LocalName)
48 {
49 case "Id":
50 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
51 break;
52 default:
53 this.Core.UnexpectedAttribute(node, attrib);
54 break;
55 }
56 }
57 else
58 {
59 this.Core.ParseExtensionAttribute(node, attrib);
60 }
61 }
62
63 foreach (var child in node.Elements())
64 {
65 if (CompilerCore.WixNamespace == child.Name.Namespace)
66 {
67 switch (child.Name.LocalName)
68 {
69 case "BillboardAction":
70 this.ParseBillboardActionElement(child);
71 break;
72 case "ComboBox":
73 this.ParseControlGroupElement(child, SymbolDefinitionType.ComboBox, "ListItem");
74 break;
75 case "Dialog":
76 this.ParseDialogElement(child);
77 break;
78 case "DialogRef":
79 this.ParseSimpleRefElement(child, SymbolDefinitions.Dialog);
80 break;
81 case "EmbeddedUI":
82 if (0 < embeddedUICount) // there can be only one embedded UI
83 {
84 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
85 this.Core.Write(ErrorMessages.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, child.Name.LocalName));
86 }
87 this.ParseEmbeddedUIElement(child);
88 ++embeddedUICount;
89 break;
90 case "Error":
91 this.ParseErrorElement(child);
92 break;
93 case "ListBox":
94 this.ParseControlGroupElement(child, SymbolDefinitionType.ListBox, "ListItem");
95 break;
96 case "ListView":
97 this.ParseControlGroupElement(child, SymbolDefinitionType.ListView, "ListItem");
98 break;
99 case "ProgressText":
100 this.ParseActionTextElement(child);
101 break;
102 case "Publish":
103 var order = 0;
104 this.ParsePublishElement(child, null, null, ref order);
105 break;
106 case "RadioButtonGroup":
107 var radioButtonType = this.ParseRadioButtonGroupElement(child, null, RadioButtonType.NotSet);
108 if (RadioButtonType.Bitmap == radioButtonType || RadioButtonType.Icon == radioButtonType)
109 {
110 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
111 this.Core.Write(ErrorMessages.RadioButtonBitmapAndIconDisallowed(childSourceLineNumbers));
112 }
113 break;
114 case "TextStyle":
115 this.ParseTextStyleElement(child);
116 break;
117 case "UIText":
118 this.ParseUITextElement(child);
119 break;
120
121 // the following are available indentically under the UI and Product elements for document organization use only
122 case "AdminUISequence":
123 this.ParseSequenceElement(child, SequenceTable.AdminUISequence);
124 break;
125 case "InstallUISequence":
126 this.ParseSequenceElement(child, SequenceTable.InstallUISequence);
127 break;
128 case "Binary":
129 this.ParseBinaryElement(child);
130 break;
131 case "Property":
132 this.ParsePropertyElement(child);
133 break;
134 case "PropertyRef":
135 this.ParseSimpleRefElement(child, SymbolDefinitions.Property);
136 break;
137 case "UIRef":
138 this.ParseSimpleRefElement(child, SymbolDefinitions.WixUI);
139 break;
140
141 default:
142 this.Core.UnexpectedElement(node, child);
143 break;
144 }
145 }
146 else
147 {
148 this.Core.ParseExtensionElement(node, child);
149 }
150 }
151
152 if (null != id && !this.Core.EncounteredError)
153 {
154 this.Core.AddSymbol(new WixUISymbol(sourceLineNumbers, id));
155 }
156 }
157
158 /// <summary>
159 /// Parses a list item element.
160 /// </summary>
161 /// <param name="node">Element to parse.</param>
162 /// <param name="symbolType">Type of symbol to create.</param>
163 /// <param name="property">Identifier of property referred to by list item.</param>
164 /// <param name="order">Relative order of list items.</param>
165 private void ParseListItemElement(XElement node, SymbolDefinitionType symbolType, string property, ref int order)
166 {
167 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
168 string icon = null;
169 string text = null;
170 string value = null;
171
172 foreach (var attrib in node.Attributes())
173 {
174 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
175 {
176 switch (attrib.Name.LocalName)
177 {
178 case "Icon":
179 if (SymbolDefinitionType.ListView == symbolType)
180 {
181 icon = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
182 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Binary, icon);
183 }
184 else
185 {
186 this.Core.Write(ErrorMessages.IllegalAttributeExceptOnElement(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "ListView"));
187 }
188 break;
189 case "Text":
190 text = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
191 break;
192 case "Value":
193 value = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
194 break;
195 default:
196 this.Core.UnexpectedAttribute(node, attrib);
197 break;
198 }
199 }
200 else
201 {
202 this.Core.ParseExtensionAttribute(node, attrib);
203 }
204 }
205
206 if (null == value)
207 {
208 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value"));
209 }
210
211 this.Core.ParseForExtensionElements(node);
212
213 if (!this.Core.EncounteredError)
214 {
215 switch (symbolType)
216 {
217 case SymbolDefinitionType.ComboBox:
218 this.Core.AddSymbol(new ComboBoxSymbol(sourceLineNumbers)
219 {
220 Property = property,
221 Order = ++order,
222 Value = value,
223 Text = text,
224 });
225 break;
226 case SymbolDefinitionType.ListBox:
227 this.Core.AddSymbol(new ListBoxSymbol(sourceLineNumbers)
228 {
229 Property = property,
230 Order = ++order,
231 Value = value,
232 Text = text,
233 });
234 break;
235 case SymbolDefinitionType.ListView:
236 var symbol = this.Core.AddSymbol(new ListViewSymbol(sourceLineNumbers)
237 {
238 Property = property,
239 Order = ++order,
240 Value = value,
241 Text = text,
242 });
243
244 if (null != icon)
245 {
246 symbol.BinaryRef = icon;
247 }
248 break;
249 default:
250 throw new ArgumentOutOfRangeException(nameof(symbolType));
251 }
252 }
253 }
254
255 /// <summary>
256 /// Parses a radio button element.
257 /// </summary>
258 /// <param name="node">Element to parse.</param>
259 /// <param name="property">Identifier of property referred to by radio button.</param>
260 /// <param name="order">Relative order of radio buttons.</param>
261 /// <returns>Type of this radio button.</returns>
262 private RadioButtonType ParseRadioButtonElement(XElement node, string property, ref int order)
263 {
264 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
265 var type = RadioButtonType.NotSet;
266 string value = null;
267 string x = null;
268 string y = null;
269 string width = null;
270 string height = null;
271 string text = null;
272 string tooltip = null;
273 string help = null;
274
275 foreach (var attrib in node.Attributes())
276 {
277 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
278 {
279 switch (attrib.Name.LocalName)
280 {
281 case "Bitmap":
282 if (RadioButtonType.NotSet != type)
283 {
284 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttributes(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "Icon", "Text"));
285 }
286 text = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
287 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Binary, text);
288 type = RadioButtonType.Bitmap;
289 break;
290 case "Height":
291 height = this.Core.GetAttributeLocalizableIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue);
292 break;
293 case "Help":
294 help = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
295 break;
296 case "Icon":
297 if (RadioButtonType.NotSet != type)
298 {
299 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttributes(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "Bitmap", "Text"));
300 }
301 text = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
302 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Binary, text);
303 type = RadioButtonType.Icon;
304 break;
305 case "Text":
306 if (RadioButtonType.NotSet != type)
307 {
308 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "Bitmap", "Icon"));
309 }
310 text = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
311 type = RadioButtonType.Text;
312 break;
313 case "ToolTip":
314 tooltip = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
315 break;
316 case "Value":
317 value = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
318 break;
319 case "Width":
320 width = this.Core.GetAttributeLocalizableIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue);
321 break;
322 case "X":
323 x = this.Core.GetAttributeLocalizableIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue);
324 break;
325 case "Y":
326 y = this.Core.GetAttributeLocalizableIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue);
327 break;
328 default:
329 this.Core.UnexpectedAttribute(node, attrib);
330 break;
331 }
332 }
333 else
334 {
335 this.Core.ParseExtensionAttribute(node, attrib);
336 }
337 }
338
339 if (null == value)
340 {
341 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value"));
342 }
343
344 if (null == x)
345 {
346 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "X"));
347 }
348
349 if (null == y)
350 {
351 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Y"));
352 }
353
354 if (null == width)
355 {
356 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Width"));
357 }
358
359 if (null == height)
360 {
361 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Height"));
362 }
363
364 this.Core.ParseForExtensionElements(node);
365
366 if (!this.Core.EncounteredError)
367 {
368 var symbol = this.Core.AddSymbol(new RadioButtonSymbol(sourceLineNumbers)
369 {
370 Property = property,
371 Order = ++order,
372 Value = value,
373 Text = text,
374 Help = (null != tooltip || null != help) ? String.Concat(tooltip, "|", help) : null
375 });
376
377 symbol.Set((int)RadioButtonSymbolFields.X, x);
378 symbol.Set((int)RadioButtonSymbolFields.Y, y);
379 symbol.Set((int)RadioButtonSymbolFields.Width, width);
380 symbol.Set((int)RadioButtonSymbolFields.Height, height);
381 }
382
383 return type;
384 }
385
386 /// <summary>
387 /// Parses a billboard element.
388 /// </summary>
389 /// <param name="node">Element to parse.</param>
390 private void ParseBillboardActionElement(XElement node)
391 {
392 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
393 string action = null;
394 var order = 0;
395
396 foreach (var attrib in node.Attributes())
397 {
398 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
399 {
400 switch (attrib.Name.LocalName)
401 {
402 case "Id":
403 action = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
404 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.WixAction, "InstallExecuteSequence", action);
405 break;
406 default:
407 this.Core.UnexpectedAttribute(node, attrib);
408 break;
409 }
410 }
411 else
412 {
413 this.Core.ParseExtensionAttribute(node, attrib);
414 }
415 }
416
417 if (null == action)
418 {
419 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
420 }
421
422 foreach (var child in node.Elements())
423 {
424 if (CompilerCore.WixNamespace == child.Name.Namespace)
425 {
426 switch (child.Name.LocalName)
427 {
428 case "Billboard":
429 order = order + 1;
430 this.ParseBillboardElement(child, action, order);
431 break;
432 default:
433 this.Core.UnexpectedElement(node, child);
434 break;
435 }
436 }
437 else
438 {
439 this.Core.ParseExtensionElement(node, child);
440 }
441 }
442 }
443
444 /// <summary>
445 /// Parses a billboard element.
446 /// </summary>
447 /// <param name="node">Element to parse.</param>
448 /// <param name="action">Action for the billboard.</param>
449 /// <param name="order">Order of the billboard.</param>
450 private void ParseBillboardElement(XElement node, string action, int order)
451 {
452 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
453 Identifier id = null;
454 string feature = null;
455
456 foreach (var attrib in node.Attributes())
457 {
458 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
459 {
460 switch (attrib.Name.LocalName)
461 {
462 case "Id":
463 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
464 break;
465 case "Feature":
466 feature = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
467 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Feature, feature);
468 break;
469 default:
470 this.Core.UnexpectedAttribute(node, attrib);
471 break;
472 }
473 }
474 else
475 {
476 this.Core.ParseExtensionAttribute(node, attrib);
477 }
478 }
479
480 if (null == id)
481 {
482 id = this.Core.CreateIdentifier("bil", action, order.ToString(), feature);
483 }
484
485 foreach (var child in node.Elements())
486 {
487 if (CompilerCore.WixNamespace == child.Name.Namespace)
488 {
489 switch (child.Name.LocalName)
490 {
491 case "Control":
492 // These are all thrown away.
493 ControlSymbol lastTabSymbol = null;
494 string firstControl = null;
495 string defaultControl = null;
496 string cancelControl = null;
497
498 this.ParseControlElement(child, id.Id, SymbolDefinitionType.BBControl, ref lastTabSymbol, ref firstControl, ref defaultControl, ref cancelControl);
499 break;
500 default:
501 this.Core.UnexpectedElement(node, child);
502 break;
503 }
504 }
505 else
506 {
507 this.Core.ParseExtensionElement(node, child);
508 }
509 }
510
511
512 if (!this.Core.EncounteredError)
513 {
514 this.Core.AddSymbol(new BillboardSymbol(sourceLineNumbers, id)
515 {
516 FeatureRef = feature,
517 Action = action,
518 Ordering = order
519 });
520 }
521 }
522
523 /// <summary>
524 /// Parses a control group element.
525 /// </summary>
526 /// <param name="node">Element to parse.</param>
527 /// <param name="symbolType">Symbol type referred to by control group.</param>
528 /// <param name="childTag">Expected child elements.</param>
529 private void ParseControlGroupElement(XElement node, SymbolDefinitionType symbolType, string childTag)
530 {
531 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
532 var order = 0;
533 string property = null;
534
535 foreach (var attrib in node.Attributes())
536 {
537 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
538 {
539 switch (attrib.Name.LocalName)
540 {
541 case "Property":
542 property = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
543 break;
544 default:
545 this.Core.UnexpectedAttribute(node, attrib);
546 break;
547 }
548 }
549 else
550 {
551 this.Core.ParseExtensionAttribute(node, attrib);
552 }
553 }
554
555 if (null == property)
556 {
557 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Property"));
558 }
559
560 foreach (var child in node.Elements())
561 {
562 if (CompilerCore.WixNamespace == child.Name.Namespace)
563 {
564 if (childTag != child.Name.LocalName)
565 {
566 this.Core.UnexpectedElement(node, child);
567 }
568
569 switch (child.Name.LocalName)
570 {
571 case "ListItem":
572 this.ParseListItemElement(child, symbolType, property, ref order);
573 break;
574 case "Property":
575 this.ParsePropertyElement(child);
576 break;
577 default:
578 this.Core.UnexpectedElement(node, child);
579 break;
580 }
581 }
582 else
583 {
584 this.Core.ParseExtensionElement(node, child);
585 }
586 }
587 }
588
589 /// <summary>
590 /// Parses a radio button control group element.
591 /// </summary>
592 /// <param name="node">Element to parse.</param>
593 /// <param name="property">Property associated with this radio button group.</param>
594 /// <param name="groupType">Specifies the current type of radio buttons in the group.</param>
595 /// <returns>The current type of radio buttons in the group.</returns>
596 private RadioButtonType ParseRadioButtonGroupElement(XElement node, string property, RadioButtonType groupType)
597 {
598 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
599 var order = 0;
600
601 foreach (var attrib in node.Attributes())
602 {
603 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
604 {
605 switch (attrib.Name.LocalName)
606 {
607 case "Property":
608 property = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
609 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Property, property);
610 break;
611 default:
612 this.Core.UnexpectedAttribute(node, attrib);
613 break;
614 }
615 }
616 else
617 {
618 this.Core.ParseExtensionAttribute(node, attrib);
619 }
620 }
621
622 if (null == property)
623 {
624 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Property"));
625 }
626
627 foreach (var child in node.Elements())
628 {
629 if (CompilerCore.WixNamespace == child.Name.Namespace)
630 {
631 switch (child.Name.LocalName)
632 {
633 case "RadioButton":
634 var type = this.ParseRadioButtonElement(child, property, ref order);
635 if (RadioButtonType.NotSet == groupType)
636 {
637 groupType = type;
638 }
639 else if (groupType != type)
640 {
641 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
642 this.Core.Write(ErrorMessages.RadioButtonTypeInconsistent(childSourceLineNumbers));
643 }
644 break;
645 default:
646 this.Core.UnexpectedElement(node, child);
647 break;
648 }
649 }
650 else
651 {
652 this.Core.ParseExtensionElement(node, child);
653 }
654 }
655
656
657 return groupType;
658 }
659
660 /// <summary>
661 /// Parses an action text element.
662 /// </summary>
663 /// <param name="node">Element to parse.</param>
664 private void ParseActionTextElement(XElement node)
665 {
666 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
667 string action = null;
668 string message = null;
669 string template = null;
670
671 foreach (var attrib in node.Attributes())
672 {
673 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
674 {
675 switch (attrib.Name.LocalName)
676 {
677 case "Action":
678 action = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
679 break;
680 case "Message":
681 message = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
682 break;
683 case "Template":
684 template = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
685 break;
686 default:
687 this.Core.UnexpectedAttribute(node, attrib);
688 break;
689 }
690 }
691 else
692 {
693 this.Core.ParseExtensionAttribute(node, attrib);
694 }
695 }
696
697 if (null == action)
698 {
699 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Action"));
700 }
701
702 this.Core.ParseForExtensionElements(node);
703
704 if (!this.Core.EncounteredError)
705 {
706 this.Core.AddSymbol(new ActionTextSymbol(sourceLineNumbers)
707 {
708 Action = action,
709 Description = message,
710 Template = template,
711 });
712 }
713 }
714
715 /// <summary>
716 /// Parses an ui text element.
717 /// </summary>
718 /// <param name="node">Element to parse.</param>
719 private void ParseUITextElement(XElement node)
720 {
721 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
722 Identifier id = null;
723 string text = null;
724
725 foreach (var attrib in node.Attributes())
726 {
727 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
728 {
729 switch (attrib.Name.LocalName)
730 {
731 case "Id":
732 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
733 break;
734 case "Value":
735 text = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
736 break;
737 default:
738 this.Core.UnexpectedAttribute(node, attrib);
739 break;
740 }
741 }
742 else
743 {
744 this.Core.ParseExtensionAttribute(node, attrib);
745 }
746 }
747
748 if (null == id)
749 {
750 id = this.Core.CreateIdentifier("txt", text);
751 }
752
753 this.Core.ParseForExtensionElements(node);
754
755 if (!this.Core.EncounteredError)
756 {
757 this.Core.AddSymbol(new UITextSymbol(sourceLineNumbers, id)
758 {
759 Text = text,
760 });
761 }
762 }
763
764 /// <summary>
765 /// Parses a text style element.
766 /// </summary>
767 /// <param name="node">Element to parse.</param>
768 private void ParseTextStyleElement(XElement node)
769 {
770 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
771 Identifier id = null;
772 int? red = null;
773 int? green = null;
774 int? blue = null;
775 var bold = false;
776 var italic = false;
777 var strike = false;
778 var underline = false;
779 string faceName = null;
780 var size = "0";
781
782 foreach (var attrib in node.Attributes())
783 {
784 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
785 {
786 switch (attrib.Name.LocalName)
787 {
788 case "Id":
789 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
790 break;
791
792 // RGB Values
793 case "Red":
794 var redColor = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Byte.MaxValue);
795 if (CompilerConstants.IllegalInteger != redColor)
796 {
797 red = redColor;
798 }
799 break;
800 case "Green":
801 var greenColor = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Byte.MaxValue);
802 if (CompilerConstants.IllegalInteger != greenColor)
803 {
804 green = greenColor;
805 }
806 break;
807 case "Blue":
808 var blueColor = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Byte.MaxValue);
809 if (CompilerConstants.IllegalInteger != blueColor)
810 {
811 blue = blueColor;
812 }
813 break;
814
815 // Style values
816 case "Bold":
817 bold = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
818 break;
819 case "Italic":
820 italic = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
821 break;
822 case "Strike":
823 strike = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
824 break;
825 case "Underline":
826 underline = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
827 break;
828
829 // Font values
830 case "FaceName":
831 faceName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
832 break;
833 case "Size":
834 size = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
835 break;
836
837 default:
838 this.Core.UnexpectedAttribute(node, attrib);
839 break;
840 }
841 }
842 else
843 {
844 this.Core.ParseExtensionAttribute(node, attrib);
845 }
846 }
847
848 if (null == id)
849 {
850 this.Core.CreateIdentifier("txs", faceName, size.ToString(), (red ?? 0).ToString(), (green ?? 0).ToString(), (blue ?? 0).ToString(), bold.ToString(), italic.ToString(), strike.ToString(), underline.ToString());
851 }
852
853 if (null == faceName)
854 {
855 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "FaceName"));
856 }
857
858 this.Core.ParseForExtensionElements(node);
859
860 if (!this.Core.EncounteredError)
861 {
862 this.Core.AddSymbol(new TextStyleSymbol(sourceLineNumbers, id)
863 {
864 FaceName = faceName,
865 LocalizedSize = size,
866 Red = red,
867 Green = green,
868 Blue = blue,
869 Bold = bold,
870 Italic = italic,
871 Strike = strike,
872 Underline = underline,
873 });
874 }
875 }
876
877 /// <summary>
878 /// Parses a dialog element.
879 /// </summary>
880 /// <param name="node">Element to parse.</param>
881 private void ParseDialogElement(XElement node)
882 {
883 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
884 Identifier id = null;
885 var hidden = false;
886 var modal = true;
887 var minimize = true;
888 var customPalette = false;
889 var errorDialog = false;
890 var keepModeless = false;
891 var height = 0;
892 string title = null;
893 var leftScroll = false;
894 var rightAligned = false;
895 var rightToLeft = false;
896 var systemModal = false;
897 var trackDiskSpace = false;
898 var width = 0;
899 var x = 50;
900 var y = 50;
901
902 foreach (var attrib in node.Attributes())
903 {
904 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
905 {
906 switch (attrib.Name.LocalName)
907 {
908 case "Id":
909 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
910 break;
911 case "Height":
912 height = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue);
913 break;
914 case "Title":
915 title = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
916 break;
917 case "Width":
918 width = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue);
919 break;
920 case "X":
921 x = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, 100);
922 break;
923 case "Y":
924 y = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, 100);
925 break;
926 case "CustomPalette":
927 customPalette = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
928 break;
929 case "ErrorDialog":
930 errorDialog = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
931 break;
932 case "Hidden":
933 hidden = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
934 break;
935 case "KeepModeless":
936 keepModeless = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
937 break;
938 case "LeftScroll":
939 leftScroll = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
940 break;
941 case "Modeless":
942 modal = YesNoType.Yes != this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
943 break;
944 case "NoMinimize":
945 minimize = YesNoType.Yes != this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
946 break;
947 case "RightAligned":
948 rightAligned = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
949 break;
950 case "RightToLeft":
951 rightToLeft = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
952 break;
953 case "SystemModal":
954 systemModal = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
955 break;
956 case "TrackDiskSpace":
957 trackDiskSpace = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
958 break;
959
960 default:
961 this.Core.UnexpectedAttribute(node, attrib);
962 break;
963 }
964 }
965 else
966 {
967 this.Core.ParseExtensionAttribute(node, attrib);
968 }
969 }
970
971 if (null == id)
972 {
973 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
974 id = Identifier.Invalid;
975 }
976
977 ControlSymbol lastTabSymbol = null;
978 string cancelControl = null;
979 string defaultControl = null;
980 string firstControl = null;
981
982 foreach (var child in node.Elements())
983 {
984 if (CompilerCore.WixNamespace == child.Name.Namespace)
985 {
986 switch (child.Name.LocalName)
987 {
988 case "Control":
989 this.ParseControlElement(child, id.Id, SymbolDefinitionType.Control, ref lastTabSymbol, ref firstControl, ref defaultControl, ref cancelControl);
990 break;
991 default:
992 this.Core.UnexpectedElement(node, child);
993 break;
994 }
995 }
996 else
997 {
998 this.Core.ParseExtensionElement(node, child);
999 }
1000 }
1001
1002 if (null != lastTabSymbol && null != lastTabSymbol.Control)
1003 {
1004 if (firstControl != lastTabSymbol.Control)
1005 {
1006 lastTabSymbol.NextControlRef = firstControl;
1007 }
1008 }
1009
1010 if (null == firstControl)
1011 {
1012 this.Core.Write(ErrorMessages.NoFirstControlSpecified(sourceLineNumbers, id.Id));
1013 }
1014
1015 if (!this.Core.EncounteredError)
1016 {
1017 this.Core.AddSymbol(new DialogSymbol(sourceLineNumbers, id)
1018 {
1019 HCentering = x,
1020 VCentering = y,
1021 Width = width,
1022 Height = height,
1023 CustomPalette = customPalette,
1024 ErrorDialog = errorDialog,
1025 Visible = !hidden,
1026 Modal = modal,
1027 KeepModeless = keepModeless,
1028 LeftScroll = leftScroll,
1029 Minimize = minimize,
1030 RightAligned = rightAligned,
1031 RightToLeft = rightToLeft,
1032 SystemModal = systemModal,
1033 TrackDiskSpace = trackDiskSpace,
1034 Title = title,
1035 FirstControlRef = firstControl,
1036 DefaultControlRef = defaultControl,
1037 CancelControlRef = cancelControl,
1038 });
1039 }
1040 }
1041
1042 /// <summary>
1043 /// Parses a control element.
1044 /// </summary>
1045 /// <param name="node">Element to parse.</param>
1046 /// <param name="dialog">Identifier for parent dialog.</param>
1047 /// <param name="symbolType">Table control belongs in.</param>
1048 /// <param name="lastTabSymbol">Last control in the tab order.</param>
1049 /// <param name="firstControl">Name of the first control in the tab order.</param>
1050 /// <param name="defaultControl">Name of the default control.</param>
1051 /// <param name="cancelControl">Name of the candle control.</param>
1052 private void ParseControlElement(XElement node, string dialog, SymbolDefinitionType symbolType, ref ControlSymbol lastTabSymbol, ref string firstControl, ref string defaultControl, ref string cancelControl)
1053 {
1054 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1055 Identifier controlId = null;
1056 var bits = new BitArray(32);
1057 string checkBoxPropertyRef = null;
1058 string checkboxValue = null;
1059 string controlType = null;
1060 var disabled = false;
1061 string height = null;
1062 string help = null;
1063 var isCancel = false;
1064 var isDefault = false;
1065 var notTabbable = false;
1066 string property = null;
1067 var publishOrder = 0;
1068 string sourceFile = null;
1069 string text = null;
1070 string tooltip = null;
1071 var radioButtonsType = RadioButtonType.NotSet;
1072 string width = null;
1073 string x = null;
1074 string y = null;
1075
1076 string defaultCondition = null;
1077 string enableCondition = null;
1078 string disableCondition = null;
1079 string hideCondition = null;
1080 string showCondition = null;
1081
1082 var hidden = false;
1083 var sunken = false;
1084 var indirect = false;
1085 var integer = false;
1086 var rightToLeft = false;
1087 var rightAligned = false;
1088 var leftScroll = false;
1089
1090 // The rest of the method relies on the control's Type, so we have to get that first.
1091 var typeAttribute = node.Attribute("Type");
1092 if (null == typeAttribute)
1093 {
1094 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Type"));
1095 }
1096 else
1097 {
1098 controlType = this.Core.GetAttributeValue(sourceLineNumbers, typeAttribute);
1099 }
1100
1101 string[] specialAttributes;
1102 switch (controlType)
1103 {
1104 case "Billboard":
1105 specialAttributes = null;
1106 notTabbable = true;
1107 disabled = true;
1108
1109 this.Core.EnsureTable(sourceLineNumbers, WindowsInstallerTableDefinitions.Billboard);
1110 break;
1111 case "Bitmap":
1112 specialAttributes = BitmapControlAttributes;
1113 notTabbable = true;
1114 disabled = true;
1115 break;
1116 case "CheckBox":
1117 specialAttributes = CheckboxControlAttributes;
1118 break;
1119 case "ComboBox":
1120 specialAttributes = ComboboxControlAttributes;
1121 break;
1122 case "DirectoryCombo":
1123 specialAttributes = VolumeControlAttributes;
1124 break;
1125 case "DirectoryList":
1126 specialAttributes = null;
1127 break;
1128 case "Edit":
1129 specialAttributes = EditControlAttributes;
1130 break;
1131 case "GroupBox":
1132 specialAttributes = null;
1133 notTabbable = true;
1134 break;
1135 case "Hyperlink":
1136 specialAttributes = HyperlinkControlAttributes;
1137 break;
1138 case "Icon":
1139 specialAttributes = IconControlAttributes;
1140 notTabbable = true;
1141 disabled = true;
1142 break;
1143 case "Line":
1144 specialAttributes = null;
1145 notTabbable = true;
1146 disabled = true;
1147 break;
1148 case "ListBox":
1149 specialAttributes = ListboxControlAttributes;
1150 break;
1151 case "ListView":
1152 specialAttributes = ListviewControlAttributes;
1153 break;
1154 case "MaskedEdit":
1155 specialAttributes = EditControlAttributes;
1156 break;
1157 case "PathEdit":
1158 specialAttributes = EditControlAttributes;
1159 break;
1160 case "ProgressBar":
1161 specialAttributes = ProgressControlAttributes;
1162 notTabbable = true;
1163 disabled = true;
1164 break;
1165 case "PushButton":
1166 specialAttributes = ButtonControlAttributes;
1167 break;
1168 case "RadioButtonGroup":
1169 specialAttributes = RadioControlAttributes;
1170 break;
1171 case "ScrollableText":
1172 specialAttributes = null;
1173 break;
1174 case "SelectionTree":
1175 specialAttributes = null;
1176 break;
1177 case "Text":
1178 specialAttributes = TextControlAttributes;
1179 notTabbable = true;
1180 break;
1181 case "VolumeCostList":
1182 specialAttributes = VolumeControlAttributes;
1183 notTabbable = true;
1184 break;
1185 case "VolumeSelectCombo":
1186 specialAttributes = VolumeControlAttributes;
1187 break;
1188 default:
1189 this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, typeAttribute.Name.LocalName, controlType,
1190 "Billboard", "Bitmap", "CheckBox", "ComboBox", "DirectoryCombo", "DirectoryList", "Edit", "GroupBox",
1191 "Hyperlink", "Icon", "Line", "ListBox", "ListView", "MaskedEdit", "PathEdit", "ProgressBar", "PushButton",
1192 "RadioButtonGroup", "ScrollableText", "SelectionTree", "Text", "VolumeCostList", "VolumeSelectCombo"));
1193 return;
1194 }
1195
1196 foreach (var attrib in node.Attributes())
1197 {
1198 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1199 {
1200 switch (attrib.Name.LocalName)
1201 {
1202 case "Id":
1203 controlId = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
1204 break;
1205 case "Type": // already processed
1206 break;
1207 case "Cancel":
1208 isCancel = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1209 break;
1210 case "CheckBoxPropertyRef":
1211 checkBoxPropertyRef = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1212 break;
1213 case "CheckBoxValue":
1214 checkboxValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1215 break;
1216 case "Default":
1217 isDefault = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1218 break;
1219 case "DefaultCondition":
1220 defaultCondition = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1221 break;
1222 case "EnableCondition":
1223 enableCondition = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1224 break;
1225 case "DisableCondition":
1226 disableCondition = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1227 break;
1228 case "HideCondition":
1229 hideCondition = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1230 break;
1231 case "ShowCondition":
1232 showCondition = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1233 break;
1234 case "Height":
1235 height = this.Core.GetAttributeLocalizableIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue);
1236 break;
1237 case "Help":
1238 help = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1239 break;
1240 case "IconSize":
1241 var iconSizeValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1242 if (null != specialAttributes)
1243 {
1244 switch (iconSizeValue)
1245 {
1246 case "16":
1247 this.Core.TrySetBitFromName(specialAttributes, "Icon16", YesNoType.Yes, bits, 16);
1248 break;
1249 case "32":
1250 this.Core.TrySetBitFromName(specialAttributes, "Icon32", YesNoType.Yes, bits, 16);
1251 break;
1252 case "48":
1253 this.Core.TrySetBitFromName(specialAttributes, "Icon16", YesNoType.Yes, bits, 16);
1254 this.Core.TrySetBitFromName(specialAttributes, "Icon32", YesNoType.Yes, bits, 16);
1255 break;
1256 case "":
1257 break;
1258 default:
1259 this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, iconSizeValue, "16", "32", "48"));
1260 break;
1261 }
1262 }
1263 else
1264 {
1265 this.Core.Write(ErrorMessages.IllegalAttributeValueWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, iconSizeValue, "Type"));
1266 }
1267 break;
1268 case "Property":
1269 property = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1270 break;
1271 case "TabSkip":
1272 notTabbable = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1273 break;
1274 case "Text":
1275 text = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1276 break;
1277 case "ToolTip":
1278 tooltip = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1279 break;
1280 case "Width":
1281 width = this.Core.GetAttributeLocalizableIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue);
1282 break;
1283 case "X":
1284 x = this.Core.GetAttributeLocalizableIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue);
1285 break;
1286 case "Y":
1287 y = this.Core.GetAttributeLocalizableIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue);
1288 break;
1289 case "Disabled":
1290 disabled = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1291 break;
1292 case "Hidden":
1293 hidden = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1294 break;
1295 case "Sunken":
1296 sunken = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1297 break;
1298 case "Indirect":
1299 indirect = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1300 break;
1301 case "Integer":
1302 integer = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1303 break;
1304 case "RightToLeft":
1305 rightToLeft = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1306 break;
1307 case "RightAligned":
1308 rightAligned = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1309 break;
1310 case "LeftScroll":
1311 leftScroll = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1312 break;
1313 default:
1314 var attribValue = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1315 if (null == specialAttributes || !this.Core.TrySetBitFromName(specialAttributes, attrib.Name.LocalName, attribValue, bits, 16))
1316 {
1317 this.Core.UnexpectedAttribute(node, attrib);
1318 }
1319 break;
1320 }
1321 }
1322 else
1323 {
1324 this.Core.ParseExtensionAttribute(node, attrib);
1325 }
1326 }
1327
1328 var attributes = this.Core.CreateIntegerFromBitArray(bits);
1329
1330 //if (disabled)
1331 //{
1332 // attributes |= WindowsInstallerConstants.MsidbControlAttributesEnabled; // bit will be inverted when stored
1333 //}
1334
1335 if (null == height)
1336 {
1337 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Height"));
1338 }
1339
1340 if (null == width)
1341 {
1342 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Width"));
1343 }
1344
1345 if (null == x)
1346 {
1347 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "X"));
1348 }
1349
1350 if (null == y)
1351 {
1352 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Y"));
1353 }
1354
1355 if (null == controlId)
1356 {
1357 controlId = this.Core.CreateIdentifier("ctl", dialog, x, y, height, width);
1358 }
1359
1360 if (isCancel)
1361 {
1362 cancelControl = controlId.Id;
1363 }
1364
1365 if (isDefault)
1366 {
1367 defaultControl = controlId.Id;
1368 }
1369
1370 foreach (var child in node.Elements())
1371 {
1372 if (CompilerCore.WixNamespace == child.Name.Namespace)
1373 {
1374 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
1375 switch (child.Name.LocalName)
1376 {
1377 case "Binary":
1378 this.ParseBinaryElement(child);
1379 break;
1380 case "ComboBox":
1381 this.ParseControlGroupElement(child, SymbolDefinitionType.ComboBox, "ListItem");
1382 break;
1383 case "ListBox":
1384 this.ParseControlGroupElement(child, SymbolDefinitionType.ListBox, "ListItem");
1385 break;
1386 case "ListView":
1387 this.ParseControlGroupElement(child, SymbolDefinitionType.ListView, "ListItem");
1388 break;
1389 case "Property":
1390 this.ParsePropertyElement(child);
1391 break;
1392 case "Publish":
1393 this.ParsePublishElement(child, dialog ?? String.Empty, controlId.Id, ref publishOrder);
1394 break;
1395 case "RadioButtonGroup":
1396 radioButtonsType = this.ParseRadioButtonGroupElement(child, property, radioButtonsType);
1397 break;
1398 case "Subscribe":
1399 this.ParseSubscribeElement(child, dialog, controlId.Id);
1400 break;
1401 case "Text":
1402 foreach (var attrib in child.Attributes())
1403 {
1404 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1405 {
1406 switch (attrib.Name.LocalName)
1407 {
1408 case "SourceFile":
1409 sourceFile = this.Core.GetAttributeValue(childSourceLineNumbers, attrib);
1410 break;
1411 case "Value":
1412 text = this.Core.GetAttributeValue(childSourceLineNumbers, attrib);
1413 break;
1414 default:
1415 this.Core.UnexpectedAttribute(child, attrib);
1416 break;
1417 }
1418 }
1419 else
1420 {
1421 this.Core.ParseExtensionAttribute(child, attrib);
1422 }
1423 }
1424
1425 this.Core.InnerTextDisallowed(child, "Value");
1426
1427 if (!String.IsNullOrEmpty(text) && null != sourceFile)
1428 {
1429 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(childSourceLineNumbers, child.Name.LocalName, "SourceFile", "Value"));
1430 }
1431 break;
1432 default:
1433 this.Core.UnexpectedElement(node, child);
1434 break;
1435 }
1436 }
1437 else
1438 {
1439 this.Core.ParseExtensionElement(node, child);
1440 }
1441 }
1442
1443 this.Core.InnerTextDisallowed(node);
1444
1445 // If the radio buttons have icons, then we need to add the icon attribute.
1446 switch (radioButtonsType)
1447 {
1448 case RadioButtonType.Bitmap:
1449 attributes |= WindowsInstallerConstants.MsidbControlAttributesBitmap;
1450 break;
1451 case RadioButtonType.Icon:
1452 attributes |= WindowsInstallerConstants.MsidbControlAttributesIcon;
1453 break;
1454 case RadioButtonType.Text:
1455 // Text is the default so nothing needs to be added bits
1456 break;
1457 }
1458
1459 // the logic for creating control rows is a little tricky because of the way tabable controls are set
1460 IntermediateSymbol symbol = null;
1461 if (!this.Core.EncounteredError)
1462 {
1463 if ("CheckBox" == controlType)
1464 {
1465 if (String.IsNullOrEmpty(property) && String.IsNullOrEmpty(checkBoxPropertyRef))
1466 {
1467 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Property", "CheckBoxPropertyRef", true));
1468 }
1469 else if (!String.IsNullOrEmpty(property) && !String.IsNullOrEmpty(checkBoxPropertyRef))
1470 {
1471 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Property", "CheckBoxPropertyRef"));
1472 }
1473 else if (!String.IsNullOrEmpty(property))
1474 {
1475 this.Core.AddSymbol(new CheckBoxSymbol(sourceLineNumbers)
1476 {
1477 Property = property,
1478 Value = checkboxValue,
1479 });
1480 }
1481 else
1482 {
1483 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Property, checkBoxPropertyRef);
1484 }
1485 }
1486
1487 var id = new Identifier(controlId.Access, dialog, controlId.Id);
1488
1489 if (SymbolDefinitionType.BBControl == symbolType)
1490 {
1491 var bbSymbol = this.Core.AddSymbol(new BBControlSymbol(sourceLineNumbers, id)
1492 {
1493 BillboardRef = dialog,
1494 BBControl = controlId.Id,
1495 Type = controlType,
1496 Attributes = attributes,
1497 Enabled = !disabled,
1498 Indirect = indirect,
1499 Integer = integer,
1500 LeftScroll = leftScroll,
1501 RightAligned = rightAligned,
1502 RightToLeft = rightToLeft,
1503 Sunken = sunken,
1504 Visible = !hidden,
1505 Text = text,
1506 SourceFile = String.IsNullOrEmpty(sourceFile) ? null : new IntermediateFieldPathValue { Path = sourceFile }
1507 });
1508
1509 bbSymbol.Set((int)BBControlSymbolFields.X, x);
1510 bbSymbol.Set((int)BBControlSymbolFields.Y, y);
1511 bbSymbol.Set((int)BBControlSymbolFields.Width, width);
1512 bbSymbol.Set((int)BBControlSymbolFields.Height, height);
1513
1514 symbol = bbSymbol;
1515 }
1516 else
1517 {
1518 var controlSymbol = this.Core.AddSymbol(new ControlSymbol(sourceLineNumbers, id)
1519 {
1520 DialogRef = dialog,
1521 Control = controlId.Id,
1522 Type = controlType,
1523 Attributes = attributes,
1524 Enabled = !disabled,
1525 Indirect = indirect,
1526 Integer = integer,
1527 LeftScroll = leftScroll,
1528 RightAligned = rightAligned,
1529 RightToLeft = rightToLeft,
1530 Sunken = sunken,
1531 Visible = !hidden,
1532 Property = !String.IsNullOrEmpty(property) ? property : checkBoxPropertyRef,
1533 Text = text,
1534 Help = (null == tooltip && null == help) ? null : String.Concat(tooltip, "|", help), // Separator is required, even if only one is non-null.};
1535 SourceFile = String.IsNullOrEmpty(sourceFile) ? null : new IntermediateFieldPathValue { Path = sourceFile }
1536 });
1537
1538 controlSymbol.Set((int)ControlSymbolFields.X, x);
1539 controlSymbol.Set((int)ControlSymbolFields.Y, y);
1540 controlSymbol.Set((int)ControlSymbolFields.Width, width);
1541 controlSymbol.Set((int)ControlSymbolFields.Height, height);
1542
1543 symbol = controlSymbol;
1544 }
1545
1546 if (!String.IsNullOrEmpty(defaultCondition))
1547 {
1548 this.Core.AddSymbol(new ControlConditionSymbol(sourceLineNumbers)
1549 {
1550 DialogRef = dialog,
1551 ControlRef = controlId.Id,
1552 Action = "Default",
1553 Condition = defaultCondition,
1554 });
1555 }
1556
1557 if (!String.IsNullOrEmpty(enableCondition))
1558 {
1559 this.Core.AddSymbol(new ControlConditionSymbol(sourceLineNumbers)
1560 {
1561 DialogRef = dialog,
1562 ControlRef = controlId.Id,
1563 Action = "Enable",
1564 Condition = enableCondition,
1565 });
1566 }
1567
1568 if (!String.IsNullOrEmpty(disableCondition))
1569 {
1570 this.Core.AddSymbol(new ControlConditionSymbol(sourceLineNumbers)
1571 {
1572 DialogRef = dialog,
1573 ControlRef = controlId.Id,
1574 Action = "Disable",
1575 Condition = disableCondition,
1576 });
1577 }
1578
1579 if (!String.IsNullOrEmpty(hideCondition))
1580 {
1581 this.Core.AddSymbol(new ControlConditionSymbol(sourceLineNumbers)
1582 {
1583 DialogRef = dialog,
1584 ControlRef = controlId.Id,
1585 Action = "Hide",
1586 Condition = hideCondition,
1587 });
1588 }
1589
1590 if (!String.IsNullOrEmpty(showCondition))
1591 {
1592 this.Core.AddSymbol(new ControlConditionSymbol(sourceLineNumbers)
1593 {
1594 DialogRef = dialog,
1595 ControlRef = controlId.Id,
1596 Action = "Show",
1597 Condition = showCondition,
1598 });
1599 }
1600 }
1601
1602 if (!notTabbable)
1603 {
1604 if (symbol is ControlSymbol controlSymbol)
1605 {
1606 if (null != lastTabSymbol)
1607 {
1608 lastTabSymbol.NextControlRef = controlSymbol.Control;
1609 }
1610 lastTabSymbol = controlSymbol;
1611 }
1612 else if (symbol != null)
1613 {
1614 this.Core.Write(ErrorMessages.TabbableControlNotAllowedInBillboard(sourceLineNumbers, node.Name.LocalName, controlType));
1615 }
1616
1617 if (null == firstControl)
1618 {
1619 firstControl = controlId.Id;
1620 }
1621 }
1622
1623 // bitmap and icon controls contain a foreign key into the binary table in the text column;
1624 // add a reference if the identifier of the binary entry is known during compilation
1625 if (("Bitmap" == controlType || "Icon" == controlType) && Common.IsIdentifier(text))
1626 {
1627 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Binary, text);
1628 }
1629 }
1630
1631 /// <summary>
1632 /// Parses a publish control event element.
1633 /// </summary>
1634 /// <param name="node">Element to parse.</param>
1635 /// <param name="dialog">Identifier of parent dialog.</param>
1636 /// <param name="control">Identifier of parent control.</param>
1637 /// <param name="order">Relative order of controls.</param>
1638 private void ParsePublishElement(XElement node, string dialog, string control, ref int order)
1639 {
1640 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1641 string argument = null;
1642 string condition = null;
1643 string controlEvent = null;
1644 string property = null;
1645
1646 // give this control event a unique ordering
1647 order++;
1648
1649 foreach (var attrib in node.Attributes())
1650 {
1651 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1652 {
1653 switch (attrib.Name.LocalName)
1654 {
1655 case "Condition":
1656 condition = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1657 break;
1658 case "Control":
1659 if (null != control)
1660 {
1661 this.Core.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, node.Parent.Name.LocalName));
1662 }
1663 control = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
1664 break;
1665 case "Dialog":
1666 if (null != dialog)
1667 {
1668 this.Core.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, node.Parent.Name.LocalName));
1669 }
1670 dialog = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
1671 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Dialog, dialog);
1672 break;
1673 case "Event":
1674 controlEvent = Compiler.UppercaseFirstChar(this.Core.GetAttributeValue(sourceLineNumbers, attrib));
1675 break;
1676 case "Order":
1677 order = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, 2147483647);
1678 break;
1679 case "Property":
1680 property = String.Concat("[", this.Core.GetAttributeValue(sourceLineNumbers, attrib), "]");
1681 break;
1682 case "Value":
1683 argument = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1684 break;
1685 default:
1686 this.Core.UnexpectedAttribute(node, attrib);
1687 break;
1688 }
1689 }
1690 else
1691 {
1692 this.Core.ParseExtensionAttribute(node, attrib);
1693 }
1694 }
1695
1696 if (null == control)
1697 {
1698 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Control"));
1699 }
1700
1701 if (null == dialog)
1702 {
1703 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Dialog"));
1704 }
1705
1706 if (null == controlEvent && null == property) // need to specify at least one
1707 {
1708 this.Core.Write(ErrorMessages.ExpectedAttributes(sourceLineNumbers, node.Name.LocalName, "Event", "Property"));
1709 }
1710 else if (null != controlEvent && null != property) // cannot specify both
1711 {
1712 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Event", "Property"));
1713 }
1714
1715 if (null == argument)
1716 {
1717 if (null != controlEvent)
1718 {
1719 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value", "Event"));
1720 }
1721 else if (null != property)
1722 {
1723 // if this is setting a property to null, put a special value in the argument column
1724 argument = "{}";
1725 }
1726 }
1727
1728 this.Core.ParseForExtensionElements(node);
1729
1730 if (!this.Core.EncounteredError)
1731 {
1732 this.Core.AddSymbol(new ControlEventSymbol(sourceLineNumbers)
1733 {
1734 DialogRef = dialog,
1735 ControlRef = control,
1736 Event = controlEvent ?? property,
1737 Argument = argument,
1738 Condition = condition,
1739 Ordering = order
1740 });
1741 }
1742
1743 if ("DoAction" == controlEvent && null != argument)
1744 {
1745 // if we're not looking at a standard action or a formatted string then create a reference
1746 // to the custom action.
1747 if (!WindowsInstallerStandard.IsStandardAction(argument) && !this.Core.ContainsProperty(argument))
1748 {
1749 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.CustomAction, argument);
1750 }
1751 }
1752
1753 // if we're referring to a dialog but not through a property, add it to the references
1754 if (("NewDialog" == controlEvent || "SpawnDialog" == controlEvent || "SpawnWaitDialog" == controlEvent || "SelectionBrowse" == controlEvent) && Common.IsIdentifier(argument))
1755 {
1756 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Dialog, argument);
1757 }
1758 }
1759
1760 /// <summary>
1761 /// Parses a control subscription element.
1762 /// </summary>
1763 /// <param name="node">Element to parse.</param>
1764 /// <param name="dialog">Identifier of dialog.</param>
1765 /// <param name="control">Identifier of control.</param>
1766 private void ParseSubscribeElement(XElement node, string dialog, string control)
1767 {
1768 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1769 string controlAttribute = null;
1770 string eventMapping = null;
1771
1772 foreach (var attrib in node.Attributes())
1773 {
1774 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1775 {
1776 switch (attrib.Name.LocalName)
1777 {
1778 case "Attribute":
1779 controlAttribute = Compiler.UppercaseFirstChar(this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib));
1780 break;
1781 case "Event":
1782 eventMapping = Compiler.UppercaseFirstChar(this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib));
1783 break;
1784 default:
1785 this.Core.UnexpectedAttribute(node, attrib);
1786 break;
1787 }
1788 }
1789 else
1790 {
1791 this.Core.ParseExtensionAttribute(node, attrib);
1792 }
1793 }
1794
1795 this.Core.ParseForExtensionElements(node);
1796
1797 if (!this.Core.EncounteredError)
1798 {
1799 this.Core.AddSymbol(new EventMappingSymbol(sourceLineNumbers)
1800 {
1801 DialogRef = dialog,
1802 ControlRef = control,
1803 Event = eventMapping,
1804 Attribute = controlAttribute,
1805 });
1806 }
1807 }
1808 }
1809 }