aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Logging/Views/ApplicationLogDetailsView.xaml
blob: c7ebcaf0292194f2d38460e5f916594488be28e9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
<UserControl x:Class="Tango.MachineStudio.Logging.Views.ApplicationLogDetailsView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:converters="clr-namespace:Tango.SharedUI.Converters;assembly=Tango.SharedUI"
             xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
             xmlns:controls="clr-namespace:Tango.SharedUI.Controls;assembly=Tango.SharedUI"
             xmlns:localConverters="clr-namespace:Tango.MachineStudio.Logging.Converters"
             xmlns:vm="clr-namespace:Tango.MachineStudio.Logging.ViewModels"
             xmlns:entities="clr-namespace:Tango.BL.Entities;assembly=Tango.BL"
             xmlns:local="clr-namespace:Tango.MachineStudio.Logging.Views"
             mc:Ignorable="d" 
             Height="500" Width="800" Background="White" d:DataContext="{d:DesignInstance Type=vm:LogDetailsViewVM, IsDesignTimeCreatable=False}">

    <UserControl.Resources>
        <converters:DateTimeUTCToStringConverter x:Key="DateTimeUTCToStringConverter" />
        <localConverters:LogItemToMessageConverter x:Key="LogItemToMessageConverter" />
    </UserControl.Resources>

    <Grid>
        <Grid Grid.RowSpan="2">
            <Grid.RowDefinitions>
                <RowDefinition Height="60"/>
                <RowDefinition Height="31*"/>
                <RowDefinition Height="50"/>
            </Grid.RowDefinitions>

            <StackPanel Orientation="Horizontal">
                <materialDesign:PackIcon Width="42" Height="42" VerticalAlignment="Center">
                    <materialDesign:PackIcon.Style>
                        <Style TargetType="materialDesign:PackIcon">
                            <Setter Property="Kind" Value="Alert"></Setter>
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding Log.Category}" Value="Info">
                                    <Setter Property="Kind" Value="Information"></Setter>
                                    <Setter Property="Foreground" Value="DimGray"></Setter>
                                </DataTrigger>
                                <DataTrigger Binding="{Binding Log.Category}" Value="Warning">
                                    <Setter Property="Kind" Value="Alert"></Setter>
                                    <Setter Property="Foreground" Value="#FFA300"></Setter>
                                </DataTrigger>
                                <DataTrigger Binding="{Binding Log.Category}" Value="Error">
                                    <Setter Property="Kind" Value="AlertOctagon"></Setter>
                                    <Setter Property="Foreground" Value="#FF5C5C"></Setter>
                                </DataTrigger>
                                <DataTrigger Binding="{Binding Log.Category}" Value="Critical">
                                    <Setter Property="Kind" Value="BellPlus"></Setter>
                                    <Setter Property="Foreground" Value="Red"></Setter>
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </materialDesign:PackIcon.Style>
                </materialDesign:PackIcon>
                <TextBlock VerticalAlignment="Center" Text="{Binding Log.Message}" TextWrapping="NoWrap" Height="22" TextTrimming="CharacterEllipsis" Width="500" Margin="10 0 0 0" FontSize="16"></TextBlock>
            </StackPanel>

            <Grid Grid.Row="1">
                <DockPanel>
                    <Grid DockPanel.Dock="Top">
                        <controls:TableGrid RowHeight="30">
                            <TextBlock Text="Time Stamp:" FontWeight="SemiBold" />
                            <TextBlock Text="{Binding Log.TimeStamp,Converter={StaticResource DateTimeUTCToStringConverter},ConverterParameter='MM/dd/yyyy HH:mm:ss.fff'}"></TextBlock>
                            <TextBlock Text="Category:" FontWeight="SemiBold" />
                            <TextBlock Text="{Binding Log.Category}"></TextBlock>
                            <TextBlock Text="File:" FontWeight="SemiBold" />
                            <TextBlock Text="{Binding Log.RelativeCallerFile}"></TextBlock>
                            <TextBlock Text="Method:" FontWeight="SemiBold" />
                            <TextBlock Text="{Binding Log.CallerMethodName}"></TextBlock>
                            <TextBlock Text="Line:" FontWeight="SemiBold" />
                            <TextBlock Text="{Binding Log.CallerLineNumber}"></TextBlock>
                        </controls:TableGrid>
                    </Grid>

                    <Border Padding="5" BorderThickness="1" BorderBrush="Gainsboro">
                        <TextBox BorderThickness="0" Text="{Binding Log,Converter={StaticResource LogItemToMessageConverter}}" Style="{x:Null}" TextWrapping="Wrap" IsReadOnly="True" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto" Background="Transparent" />
                    </Border>
                </DockPanel>
            </Grid>

            <Grid Grid.Row="2">
                <Button HorizontalAlignment="Right" Width="140" Command="{Binding CloseCommand}">CLOSE</Button>
            </Grid>
        </Grid>
    </Grid>
</UserControl>
/// <summary> /// Abstact base class for all types that can contain child nodes /// </summary> public abstract class AXmlContainer: AXmlObject { /// <summary> /// Children of the node. It is read-only. /// Note that is has CollectionChanged event. /// </summary> public AXmlObjectCollection<AXmlObject> Children { get; private set; } /// <summary> Create new container </summary> protected AXmlContainer() { this.Children = new AXmlObjectCollection<AXmlObject>(); } #region Helpper methods ObservableCollection<AXmlElement> elements; /// <summary> Gets direcly nested elements (non-recursive) </summary> public ObservableCollection<AXmlElement> Elements { get { if (elements == null) { elements = new FilteredCollection<AXmlElement, AXmlObjectCollection<AXmlObject>>(this.Children); } return elements; } } internal AXmlObject FirstChild { get { return this.Children[0]; } } internal AXmlObject LastChild { get { return this.Children[this.Children.Count - 1]; } } #endregion /// <inheritdoc/> public override IEnumerable<AXmlObject> GetSelfAndAllChildren() { return (new AXmlObject[] { this }).Flatten( delegate(AXmlObject i) { AXmlContainer container = i as AXmlContainer; if (container != null) return container.Children; else return null; } ); } /// <summary> /// Gets a child fully containg the given offset. /// Goes recursively down the tree. /// Specail case if at the end of attribute or text /// </summary> public AXmlObject GetChildAtOffset(int offset) { foreach(AXmlObject child in this.Children) { if ((child is AXmlAttribute || child is AXmlText) && offset == child.EndOffset) return child; if (child.StartOffset < offset && offset < child.EndOffset) { AXmlContainer container = child as AXmlContainer; if (container != null) { return container.GetChildAtOffset(offset); } else { return child; } } } return this; // No childs at offset } // Only these four methods should be used to modify the collection /// <summary> To be used exlucively by the parser </summary> internal void AddChild(AXmlObject item) { // Childs can be only added to newly parsed items Assert(this.Parent == null, "I have to be new"); Assert(item.IsCached, "Added item must be in cache"); // Do not set parent pointer this.Children.InsertItemAt(this.Children.Count, item); } /// <summary> To be used exlucively by the parser </summary> internal void AddChildren(IEnumerable<AXmlObject> items) { // Childs can be only added to newly parsed items Assert(this.Parent == null, "I have to be new"); // Do not set parent pointer this.Children.InsertItemsAt(this.Children.Count, items.ToList()); } /// <summary> /// To be used exclusively by the children update algorithm. /// Insert child and keep links consistent. /// </summary> void InsertChild(int index, AXmlObject item) { AXmlParser.Log("Inserting {0} at index {1}", item, index); Assert(this.Document != null, "Can not insert to dangling object"); Assert(item.Parent != this, "Can not own item twice"); SetParentPointersInTree(item); this.Children.InsertItemAt(index, item); this.Document.OnObjectInserted(index, item); } /// <summary> Recursively fix all parent pointer in a tree </summary> /// <remarks> /// Cache constraint: /// If cached item has parent set, then the whole subtree must be consistent and document set /// </remarks> void SetParentPointersInTree(AXmlObject item) { // All items come from the parser cache if (item.Parent == null) { // Dangling object - either a new parser object or removed tree (still cached) item.Parent = this; item.Document = this.Document; AXmlContainer container = item as AXmlContainer; if (container != null) { foreach(AXmlObject child in container.Children) { container.SetParentPointersInTree(child); } } } else if (item.Parent == this) { // If node is attached and then deattached, it will have null parent pointer // but valid subtree - so its children will alredy have correct parent pointer // like in this case // item.DebugCheckConsistency(false); // Rest of the tree is consistent - do not recurse } else { // From cache & parent set => consitent subtree // item.DebugCheckConsistency(false); // The parent (or any futher parents) can not be part of parsed document // becuase otherwise this item would be included twice => safe to change parents // Maintain cache constraint by setting parents to null foreach(AXmlObject ancest in item.GetAncestors().ToList()) { ancest.Parent = null; } item.Parent = this; // Rest of the tree is consistent - do not recurse } } /// <summary> /// To be used exclusively by the children update algorithm. /// Remove child, set parent to null and notify the document /// </summary> void RemoveChild(int index) { AXmlObject removed = this.Children[index]; AXmlParser.Log("Removing {0} at index {1}", removed, index); // Stop tracking if the object can not be used again if (!removed.IsCached) this.Document.Parser.TrackedSegments.RemoveParsedObject(removed); // Null parent pointer Assert(removed.Parent == this, "Inconsistent child"); removed.Parent = null; this.Children.RemoveItemAt(index); this.Document.OnObjectRemoved(index, removed); } /// <summary> Verify that the subtree is consistent. Only in debug build. </summary> /// <remarks> Parent pointers might be null or pointing somewhere else in parse tree </remarks> internal override void DebugCheckConsistency(bool checkParentPointers) { base.DebugCheckConsistency(checkParentPointers); AXmlObject prevChild = null; int myStartOffset = this.StartOffset; int myEndOffset = this.EndOffset; foreach(AXmlObject child in this.Children) { Assert(child.Length != 0, "Empty child"); if (checkParentPointers) { Assert(child.Parent != null, "Null parent reference"); Assert(child.Parent == this, "Inccorect parent reference"); } if (this.Document != null) { Assert(child.Document != null, "Child has null document"); Assert(child.Document == this.Document, "Child is in different document"); } if (this.IsCached) Assert(child.IsCached, "Child not in cache"); Assert(myStartOffset <= child.StartOffset && child.EndOffset <= myEndOffset, "Child not within parent text range"); if (prevChild != null) Assert(prevChild.EndOffset <= child.StartOffset, "Overlaping childs"); child.DebugCheckConsistency(checkParentPointers); prevChild = child; } } /// <remarks> /// Note the the method is not called recuively. /// Only the helper methods are recursive. /// </remarks> internal void UpdateTreeFrom(AXmlContainer srcContainer) { this.StartOffset = srcContainer.StartOffset; // Force the update this.UpdateDataFrom(srcContainer); RemoveChildrenNotIn(srcContainer.Children); InsertAndUpdateChildrenFrom(srcContainer.Children); } void RemoveChildrenNotIn(IList<AXmlObject> srcList) { Dictionary<int, AXmlObject> srcChildren = srcList.ToDictionary(i => i.StartOffset); for(int i = 0; i < this.Children.Count;) { AXmlObject child = this.Children[i]; AXmlObject srcChild; if (srcChildren.TryGetValue(child.StartOffset, out srcChild) && child.CanUpdateDataFrom(srcChild)) { // Keep only one item with given offset (we might have several due to deletion) srcChildren.Remove(child.StartOffset); // If contaner that needs updating AXmlContainer childAsContainer = child as AXmlContainer; if (childAsContainer != null && child.LastUpdatedFrom != srcChild) childAsContainer.RemoveChildrenNotIn(((AXmlContainer)srcChild).Children); i++; } else { RemoveChild(i); } } } void InsertAndUpdateChildrenFrom(IList<AXmlObject> srcList) { for(int i = 0; i < srcList.Count; i++) { // End of our list? if (i == this.Children.Count) { InsertChild(i, srcList[i]); continue; } AXmlObject child = this.Children[i]; AXmlObject srcChild = srcList[i]; if (child.CanUpdateDataFrom(srcChild)) { // includes offset test // Does it need updating? if (child.LastUpdatedFrom != srcChild) { child.UpdateDataFrom(srcChild); AXmlContainer childAsContainer = child as AXmlContainer; if (childAsContainer != null) childAsContainer.InsertAndUpdateChildrenFrom(((AXmlContainer)srcChild).Children); } } else { InsertChild(i, srcChild); } } Assert(this.Children.Count == srcList.Count, "List lengths differ after update"); } } }