Skip to content

Commit

Permalink
Add picture support in payload preview (#88)
Browse files Browse the repository at this point in the history
* Add picture support.

* Fix payload format selection.
  • Loading branch information
chkr1011 authored Feb 22, 2024
1 parent 55efe61 commit a317560
Show file tree
Hide file tree
Showing 13 changed files with 231 additions and 142 deletions.
12 changes: 6 additions & 6 deletions Source/Controls/BufferEditor/BufferEditor.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -103,30 +103,30 @@
Margin="0,0,10,0" />

<RadioButton GroupName="PayloadType"
IsChecked="{Binding Path=IsText, RelativeSource={RelativeSource AncestorType={x:Type controls:BufferEditor}}}"
IsChecked="{CompiledBinding Path=IsText, RelativeSource={RelativeSource TemplatedParent}}"
Content="Text" />

<RadioButton GroupName="PayloadType"
IsChecked="{Binding Path=IsJson, RelativeSource={RelativeSource AncestorType={x:Type controls:BufferEditor}}}"
IsChecked="{CompiledBinding Path=IsJson, RelativeSource={RelativeSource TemplatedParent}}"
Content="JSON" />

<RadioButton GroupName="PayloadType"
IsChecked="{Binding Path=IsXml, RelativeSource={RelativeSource AncestorType={x:Type controls:BufferEditor}}}"
IsChecked="{CompiledBinding Path=IsXml, RelativeSource={RelativeSource TemplatedParent}}"
Content="XML" />

<RadioButton GroupName="PayloadType"
IsChecked="{Binding Path=IsBase64, RelativeSource={RelativeSource AncestorType={x:Type controls:BufferEditor}}}"
IsChecked="{CompiledBinding Path=IsBase64, RelativeSource={RelativeSource TemplatedParent}}"
Content="Base64" />

<RadioButton GroupName="PayloadType"
IsChecked="{Binding Path=IsPath, RelativeSource={RelativeSource AncestorType={x:Type controls:BufferEditor}}}"
IsChecked="{CompiledBinding Path=IsPath, RelativeSource={RelativeSource TemplatedParent}}"
Content="Path" />
</StackPanel>
</Grid>

<avaloniaEdit:TextEditor Grid.Row="1"
x:Name="TextEditor"
WordWrap="{Binding ElementName=TextWrappingCheckBox, Path=IsChecked}"
WordWrap="{CompiledBinding ElementName=TextWrappingCheckBox, Path=IsChecked}"
Background="Transparent"
Margin="5"
Classes="code_text"
Expand Down
7 changes: 5 additions & 2 deletions Source/Controls/BufferEditor/BufferEditor.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,11 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang
change.Property == IsXmlProperty ||
change.Property == IsPathProperty)
{
SyncGrammar();
SyncBufferFormat();
if (change.NewValue is true)
{
SyncGrammar();
SyncBufferFormat();
}
}
}

Expand Down
34 changes: 25 additions & 9 deletions Source/Controls/BufferPreview/BufferPreviewView.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
Background="{DynamicResource TextControlBackground}">

<!-- The text base previews. -->
<Grid RowDefinitions="Auto,*">
<Grid RowDefinitions="Auto,*"
x:Name="Container">

<!-- Options -->
<Grid Classes="tool_bar"
Expand All @@ -27,7 +28,7 @@
<!-- The Text Wrapping button -->
<ToggleButton Classes="tool_bar_button"
IsChecked="True"
IsVisible="{Binding !ShowRaw, RelativeSource={RelativeSource TemplatedParent}}"
IsVisible="{CompiledBinding ShowText, RelativeSource={RelativeSource TemplatedParent}}"
x:Name="TextWrappingCheckBox">
<PathIcon Data="{StaticResource text_wrap_regular}"
ToolTip.Tip="Toggle word wrap" />
Expand All @@ -37,7 +38,7 @@

<!-- The Copy button -->
<Button Classes="tool_bar_button"
IsVisible="{Binding !ShowRaw, RelativeSource={RelativeSource TemplatedParent}}"
IsVisible="{CompiledBinding ShowText, RelativeSource={RelativeSource TemplatedParent}}"
x:Name="CopyToClipboardButton">
<PathIcon Data="{StaticResource copy_regular}"
ToolTip.Tip="Copy to clipboard" />
Expand All @@ -54,24 +55,34 @@
<!-- The Format selector -->
<StackPanel Grid.Column="2"
Orientation="Horizontal">

<CheckBox IsChecked="{CompiledBinding UseBase64PreDecoding, RelativeSource={RelativeSource TemplatedParent}}"
Content="Unwrap Base64"
Margin="0,0,5,0"
ToolTip.Tip="When active the payload will be unwrapped from a Base64 compatible string" />

<Separator Classes="tool_bar_separator" />

<Label Classes="tool_bar_label"
Content="Format"
Margin="0,0,10,0" />
Margin="5,0,10,0" />
<ComboBox Classes="tool_bar_combo_box"
MinWidth="100"
VerticalAlignment="Stretch"
ItemsSource="{Binding Path=Formats, RelativeSource={RelativeSource TemplatedParent}}"
SelectedItem="{Binding Path=SelectedFormat, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}">
ItemsSource="{CompiledBinding Path=Formats, RelativeSource={RelativeSource TemplatedParent}, Mode=OneTime}"
SelectedItem="{CompiledBinding Path=SelectedFormat, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>


</StackPanel>
</Grid>

<avaloniaEdit:TextEditor WordWrap="{Binding ElementName=TextWrappingCheckBox, Path=IsChecked}"
<avaloniaEdit:TextEditor WordWrap="{CompiledBinding ElementName=TextWrappingCheckBox, Path=IsChecked}"
IsReadOnly="True"
x:Name="TextEditor"
Margin="5"
Expand All @@ -81,12 +92,17 @@
Grid.Row="1"
ShowLineNumbers="True"
FontFamily="{StaticResource MonospaceFontFamily}"
IsVisible="{Binding !ShowRaw, RelativeSource={RelativeSource TemplatedParent}}" />
IsVisible="{CompiledBinding ShowText, RelativeSource={RelativeSource TemplatedParent}}" />

<controls:HexBox Grid.Row="1"
x:Name="HexBox"
BorderThickness="0"
IsVisible="{Binding ShowRaw, RelativeSource={RelativeSource TemplatedParent}}" />
IsVisible="{CompiledBinding ShowRaw, RelativeSource={RelativeSource TemplatedParent}}" />

<Viewbox Grid.Row="1"
Stretch="Uniform"
x:Name="PictureBox"
IsVisible="{CompiledBinding ShowPicture, RelativeSource={RelativeSource TemplatedParent}}" />
</Grid>
</Border>
</ControlTemplate>
Expand Down
152 changes: 106 additions & 46 deletions Source/Controls/BufferPreview/BufferPreviewView.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
using Avalonia.Media.Imaging;
using Avalonia.Platform.Storage;
using Avalonia.Threading;
using AvaloniaEdit;
Expand Down Expand Up @@ -38,13 +39,18 @@ public sealed class BufferInspectorView : TemplatedControl

public static readonly StyledProperty<bool> ShowRawProperty = AvaloniaProperty.Register<BufferInspectorView, bool>(nameof(ShowRaw));

public static readonly StyledProperty<bool> ShowTextProperty = AvaloniaProperty.Register<BufferInspectorView, bool>(nameof(ShowText));

public static readonly StyledProperty<bool> ShowPictureProperty = AvaloniaProperty.Register<BufferInspectorView, bool>(nameof(ShowPicture));

public static readonly StyledProperty<bool> UseBase64PreDecodingProperty = AvaloniaProperty.Register<BufferInspectorView, bool>(nameof(UseBase64PreDecoding));

public static readonly StyledProperty<string?> SelectedFormatNameProperty = AvaloniaProperty.Register<BufferInspectorView, string?>(nameof(SelectedFormatName), "UTF-8");

readonly RegistryOptions _textEditorRegistryOptions = new(ThemeName.Dark);

string _content = string.Empty;
Button? _copyToClipboardButton;
HexBox? _hexBox;
Viewbox? _pictureBox;
Button? _saveToFileButton;
TextEditor? _textEditor;
TextMate.Installation? _textMateInstallation;
Expand Down Expand Up @@ -103,7 +109,7 @@ public string? SelectedFormatName
return JsonSerializerService.Instance?.Format(json) ?? string.Empty;
}),


new BufferConverter("Picture", null, _ => "PICTURE"), // Special case!
new BufferConverter("RAW", null, _ => "RAW"), // Special case!
new BufferConverter("Unicode", null, b => Encoding.Unicode.GetString(b)),
new BufferConverter("UTF-8", null, b => Encoding.UTF8.GetString(b)),
Expand All @@ -117,42 +123,78 @@ public string? SelectedFormatName
})
];

public bool ShowPicture
{
get => GetValue(ShowPictureProperty);
set => SetValue(ShowPictureProperty, value);
}

public bool ShowRaw
{
get => GetValue(ShowRawProperty);
set => SetValue(ShowRawProperty, value);
}

public bool ShowText
{
get => GetValue(ShowTextProperty);
set => SetValue(ShowTextProperty, value);
}

public bool UseBase64PreDecoding
{
get => GetValue(UseBase64PreDecodingProperty);
set => SetValue(UseBase64PreDecodingProperty, value);
}

protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);

_hexBox = (HexBox)this.GetTemplateChild("HexBox");
_pictureBox = (Viewbox)this.GetTemplateChild("PictureBox");

_textEditor = (TextEditor)this.GetTemplateChild("TextEditor");
_textMateInstallation = _textEditor.InstallTextMate(_textEditorRegistryOptions);
SyncTextEditor();

_copyToClipboardButton = (Button)this.GetTemplateChild("CopyToClipboardButton");
_copyToClipboardButton.Click += OnCopyToClipboard;

_saveToFileButton = (Button)this.GetTemplateChild("SaveToFileButton");
_saveToFileButton.Click += OnSaveToFile;
ReadBuffer();

Sync();
}

protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);

if (change.Property == BufferProperty || change.Property == SelectedFormatProperty)
if (change.Property == SelectedFormatProperty)
{
ReadBuffer();
if (string.Equals(SelectedFormat?.Name, "RAW"))
{
ShowRaw = true;
ShowText = false;
ShowPicture = false;
}
else if (string.Equals(SelectedFormat?.Name, "Picture"))
{
ShowPicture = true;
ShowText = false;
ShowRaw = false;
}
else
{
ShowText = true;
ShowPicture = false;
ShowRaw = false;
}
}

if (change.Property == SelectedFormatProperty)
if (change.Property == UseBase64PreDecodingProperty || change.Property == SelectedFormatProperty || change.Property == BufferProperty)
{
ShowRaw = ReferenceEquals(SelectedFormat?.Name, "RAW");
Sync();
}

if (change.Property == SelectedFormatNameProperty)
Expand All @@ -163,10 +205,10 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang

void OnCopyToClipboard(object? sender, RoutedEventArgs e)
{
if (!string.IsNullOrEmpty(_content))
if (!string.IsNullOrEmpty(_textEditor!.Text))
{
var clipboard = TopLevel.GetTopLevel(this)?.Clipboard;
_ = clipboard?.SetTextAsync(_content);
_ = clipboard?.SetTextAsync(_textEditor.Text);
}
}

Expand Down Expand Up @@ -210,33 +252,6 @@ void OnSaveToFile(object? sender, RoutedEventArgs e)
});
}

void ReadBuffer()
{
var format = SelectedFormat;
if (format == null)
{
throw new InvalidOperationException();
}

if ((Buffer?.Length ?? 0) == 0)
{
_content = string.Empty;
}
else
{
try
{
_content = format.Convert(Buffer!);
}
catch (Exception exception)
{
_content = $"<{exception.Message}>";
}
}

SyncTextEditor();
}

void SelectFormat()
{
if (string.IsNullOrEmpty(SelectedFormatName))
Expand All @@ -254,9 +269,9 @@ void SelectFormat()
}
}

void SyncTextEditor()
void Sync()
{
if (_textEditor == null || _hexBox == null)
if (_textEditor == null || _hexBox == null || _pictureBox == null)
{
return;
}
Expand All @@ -266,16 +281,61 @@ void SyncTextEditor()
return;
}

_textMateInstallation?.SetGrammar(SelectedFormat.Grammar);

// It is important to set the content after the grammar so that
// the highlighting gets applied properly!
_textEditor.Text = _content;
var buffer = Buffer ?? Array.Empty<byte>();
if (UseBase64PreDecoding)
{
try
{
buffer = Convert.FromBase64String(Encoding.ASCII.GetString(buffer));
}
catch
{
// Go ahead if decoding did not work. It is probably not required.
}
}

if (SelectedFormat.Name == "RAW")
{
// Only fill the data of the hex box when it is actually used!
_hexBox.Value = Buffer;
_hexBox.Value = buffer;
}
else if (SelectedFormat.Name == "Picture")
{
try
{
if (_pictureBox.Child is Image existingImage)
{
((Bitmap)existingImage.Source!).Dispose();
}

_pictureBox.Child = new Image
{
Source = new Bitmap(new MemoryStream(buffer))
};
}
catch
{
// Ignore. We may can set a picture here indicating that the format is not supported.
_pictureBox.Child = null;
}
}
else
{
string text;
try
{
text = SelectedFormat.Convert(buffer);
}
catch (Exception exception)
{
text = exception.ToString();
}

_textMateInstallation?.SetGrammar(SelectedFormat.Grammar);

// It is important to set the content after the grammar so that
// the highlighting gets applied properly!
_textEditor.Text = text;
}
}
}
Loading

0 comments on commit a317560

Please sign in to comment.