diff --git a/AndroidLogViewer/AndroidLogViewer.csproj b/AndroidLogViewer/AndroidLogViewer.csproj index 8a4eb94..b311a3c 100644 --- a/AndroidLogViewer/AndroidLogViewer.csproj +++ b/AndroidLogViewer/AndroidLogViewer.csproj @@ -58,6 +58,7 @@ MSBuild:Compile Designer + diff --git a/AndroidLogViewer/Command/BindingRedirector.cs b/AndroidLogViewer/Command/BindingRedirector.cs new file mode 100644 index 0000000..ddb9808 --- /dev/null +++ b/AndroidLogViewer/Command/BindingRedirector.cs @@ -0,0 +1,36 @@ +using System.Windows; + +namespace AndroidLogViewer.Command +{ + public class BindingRedirector : DependencyObject + { + public static readonly DependencyProperty LeftProperty = DependencyProperty.Register( + "Left", typeof(object), typeof(BindingRedirector), new PropertyMetadata(default(object), LeftChanged)); + + private static void LeftChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((BindingRedirector) d).Right = e.NewValue; + } + + public object Left + { + get { return (object) GetValue(LeftProperty); } + set { SetValue(LeftProperty, value); } + } + + public static readonly DependencyProperty RightProperty = DependencyProperty.Register( + "Right", typeof(object), typeof(BindingRedirector), new PropertyMetadata(default(object) , RightChanged)); + + private static void RightChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((BindingRedirector) d).Left = e.NewValue; + } + + public object Right + { + get { return (object) GetValue(RightProperty); } + set { SetValue(RightProperty, value); } + } + + } +} \ No newline at end of file diff --git a/AndroidLogViewer/Dialogs/Export/ExportDialogViewModel.cs b/AndroidLogViewer/Dialogs/Export/ExportDialogViewModel.cs index 00c0823..d0d0d25 100644 --- a/AndroidLogViewer/Dialogs/Export/ExportDialogViewModel.cs +++ b/AndroidLogViewer/Dialogs/Export/ExportDialogViewModel.cs @@ -97,7 +97,7 @@ private void ExecuteConfirmCommand() } else { - lines = _entries.Select(x=> $"{x.Time} {x.Process} {x.Thread} {x.Level} {x.Tag}: {x.Message}"); + lines = _entries.Select(FormatLogEntry); } @@ -105,5 +105,10 @@ private void ExecuteConfirmCommand() Done(null); } + + public static string FormatLogEntry(LogEntry x) + { + return $"{x.Time} {x.Process} {x.Thread} {x.Level} {x.Tag}: {x.Message}"; + } } } \ No newline at end of file diff --git a/AndroidLogViewer/LogcatParser.cs b/AndroidLogViewer/LogcatParser.cs index 70cf05d..ab271fb 100644 --- a/AndroidLogViewer/LogcatParser.cs +++ b/AndroidLogViewer/LogcatParser.cs @@ -14,12 +14,13 @@ public class LogcatParser private const string TagRegularExpression = "(?[^:]+):"; private const string MessageRegularExpression = "(?.*)"; private static readonly string TrailingLineRegularExpression =$"(?\\s+?){MessageRegularExpression}"; - private static readonly string DefaultLogcatRegularExpression = $"(?{DateTimeRegularExpression}\\s+{PidRegularExpression}\\s+{TidRegularExpression}\\s+{LogLevelRegularExpression}\\s+{TagRegularExpression}\\s+?){MessageRegularExpression}"; - private static readonly string AndroidStudioRegularExpression = $"(?{DateTimeRegularExpression}\\s+{PidRegularExpression}-{TidRegularExpression}[^\\s]+\\s+{LogLevelRegularExpression}/{TagRegularExpression}\\s+?){MessageRegularExpression}"; + private static readonly string DefaultLogcatRegularExpression = $"(?{DateTimeRegularExpression}\\s+{PidRegularExpression}\\s+{TidRegularExpression}\\s+{LogLevelRegularExpression}\\s*{TagRegularExpression}\\s+?){MessageRegularExpression}"; + private static readonly string AndroidStudioRegularExpression = $"(?{DateTimeRegularExpression}\\s+{PidRegularExpression}-{TidRegularExpression}[^\\s]+\\s*{LogLevelRegularExpression}/{TagRegularExpression}\\s+?){MessageRegularExpression}"; + private static readonly string StartOfLogExpression = "(?\\s*---+)(?.*)"; - private static readonly string[] RecognizedRegularExpressions = {DefaultLogcatRegularExpression, AndroidStudioRegularExpression, TrailingLineRegularExpression}; - private static readonly string Pattern = $"^{string.Join("|", RecognizedRegularExpressions.Select(x => $"({x})"))}$"; + private static readonly string[] RecognizedRegularExpressions = {DefaultLogcatRegularExpression, AndroidStudioRegularExpression, TrailingLineRegularExpression, StartOfLogExpression}; + private static readonly string Pattern = $"{string.Join("|", RecognizedRegularExpressions.Select(x => $"^({x})$"))}"; private static readonly Regex RegularExpression = new Regex(Pattern, RegexOptions.Compiled); static LogcatParser() @@ -41,6 +42,7 @@ public static ObservableCollection ReadLogEntries(TextReader reader) if (match.Success) { LogEntry newEntry = null; + if (match.Groups["datetime"].Success) { newEntry = new LogEntry @@ -56,6 +58,13 @@ public static ObservableCollection ReadLogEntries(TextReader reader) pivotEntry = newEntry; } + else if (match.Groups["startoflog"].Success) + { + newEntry = new LogEntry + { + Message = $"--- {match.Groups["message"].Value.Trim()}" + }; + } else if (match.Groups["trailingline"].Success && pivotEntry != null) { var trimmedMessage = match.Groups["message"].Value.Trim(); @@ -72,6 +81,7 @@ public static ObservableCollection ReadLogEntries(TextReader reader) Time = pivotEntry.Time, }; } + if (newEntry != null) result.Add(newEntry); } diff --git a/AndroidLogViewer/MainWindow.xaml b/AndroidLogViewer/MainWindow.xaml index fed21e8..20732a1 100644 --- a/AndroidLogViewer/MainWindow.xaml +++ b/AndroidLogViewer/MainWindow.xaml @@ -212,6 +212,9 @@ Style="{StaticResource MonospaceFont}" SelectedIndex="{Binding SelectedLogEntryIndex}" SelectedItem="{Binding Path=SelectedLogEntry, Mode=OneWayToSource}"> + + + + + diff --git a/AndroidLogViewer/MainWindow.xaml.cs b/AndroidLogViewer/MainWindow.xaml.cs index aa8cc3f..8954dcb 100644 --- a/AndroidLogViewer/MainWindow.xaml.cs +++ b/AndroidLogViewer/MainWindow.xaml.cs @@ -1,17 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; +using System.Windows; using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; namespace AndroidLogViewer { @@ -31,5 +19,10 @@ private void CloseWindow(object sender, RoutedEventArgs e) { Close(); } + + private void ExecuteCopyLogItems(object sender, ExecutedRoutedEventArgs e) + { + (DataContext as MainWindowViewModel)?.CopySelectionCommand?.Execute(LogItems.SelectedItems); + } } } diff --git a/AndroidLogViewer/MainWindowViewModel.cs b/AndroidLogViewer/MainWindowViewModel.cs index d6e9f73..6ea03d2 100644 --- a/AndroidLogViewer/MainWindowViewModel.cs +++ b/AndroidLogViewer/MainWindowViewModel.cs @@ -49,6 +49,8 @@ public MainWindowViewModel() ExportCommand = new DelegateCommand( () => ShowDialog(new ExportDialogViewModel(_defaultView.OfType().ToArray())).FireAndForget(), () => !string.IsNullOrEmpty(FileName)).ObservesProperty(() => FileName); + ExportSelectionCommand = new DelegateCommand>(x => ShowDialog(new ExportDialogViewModel(x.OfType().ToArray())).FireAndForget()); + CopySelectionCommand = new DelegateCommand>(items => Clipboard.SetText(string.Join("\n", items.OfType().Select(ExportDialogViewModel.FormatLogEntry)))); OpenFileCommand = new DelegateCommand(OpenFile); OpenUrlCommand = new DelegateCommand(OpenUrl); @@ -149,6 +151,10 @@ private bool FilterLogEntries(object x) public ICommand ExportCommand { get; } + public ICommand ExportSelectionCommand { get; } + + public ICommand CopySelectionCommand { get; } + #region INotifyPropertyChanged public event PropertyChangedEventHandler PropertyChanged;