4 using System.Collections.Generic;
5 using System.Collections.Specialized;
8 using System.Windows.Controls;
9 using System.Windows.Controls.Primitives;
10 using System.Windows.Documents;
11 using System.Windows.Media;
13 using SiliconStudio.Core.Diagnostics;
15 namespace SiliconStudio.Presentation.Controls
20 [TemplatePart(Name =
"PART_LogTextBox", Type = typeof(RichTextBox))]
21 [TemplatePart(Name =
"PART_ClearLog", Type = typeof(ButtonBase))]
22 [TemplatePart(Name =
"PART_PreviousResult", Type = typeof(ButtonBase))]
23 [TemplatePart(Name =
"PART_NextResult", Type = typeof(ButtonBase))]
26 private readonly List<TextRange> searchMatches =
new List<TextRange>();
27 private int currentResult;
32 private RichTextBox logTextBox;
37 public static readonly DependencyProperty LogMessagesProperty = DependencyProperty.Register(
"LogMessages", typeof(
ICollection<ILogMessage>), typeof(
TextLogViewer),
new PropertyMetadata(null, LogMessagesPropertyChanged));
42 public static readonly DependencyProperty AutoScrollProperty = DependencyProperty.Register(
"AutoScroll", typeof(
bool), typeof(
TextLogViewer),
new PropertyMetadata(
true));
47 public static readonly DependencyProperty IsToolBarVisibleProperty = DependencyProperty.Register(
"IsToolBarVisible", typeof(
bool), typeof(
TextLogViewer),
new PropertyMetadata(
true));
52 public static readonly DependencyProperty CanClearLogProperty = DependencyProperty.Register(
"CanClearLog", typeof(
bool), typeof(
TextLogViewer),
new PropertyMetadata(
true));
57 public static readonly DependencyProperty CanFilterLogProperty = DependencyProperty.Register(
"CanFilterLog", typeof(
bool), typeof(
TextLogViewer),
new PropertyMetadata(
true));
62 public static readonly DependencyProperty CanSearchLogProperty = DependencyProperty.Register(
"CanSearchLog", typeof(
bool), typeof(
TextLogViewer),
new PropertyMetadata(
true));
67 public static readonly DependencyProperty SearchTokenProperty = DependencyProperty.Register(
"SearchToken", typeof(
string), typeof(
TextLogViewer),
new PropertyMetadata(
"", SearchTokenChanged));
72 public static readonly DependencyProperty SearchMatchCaseProperty = DependencyProperty.Register(
"SearchMatchCase", typeof(
bool), typeof(
TextLogViewer),
new PropertyMetadata(
false, SearchTokenChanged));
77 public static readonly DependencyProperty SearchMatchWordProperty = DependencyProperty.Register(
"SearchMatchWord", typeof(
bool), typeof(
TextLogViewer),
new PropertyMetadata(
false, SearchTokenChanged));
82 public static readonly DependencyProperty SearchMatchBrushProperty = DependencyProperty.Register(
"SearchMatchBrush", typeof(Brush), typeof(
TextLogViewer),
new PropertyMetadata(Brushes.LightSteelBlue, TextPropertyChanged));
87 public static readonly DependencyProperty DebugBrushProperty = DependencyProperty.Register(
"DebugBrush", typeof(Brush), typeof(
TextLogViewer),
new PropertyMetadata(Brushes.White, TextPropertyChanged));
92 public static readonly DependencyProperty VerboseBrushProperty = DependencyProperty.Register(
"VerboseBrush", typeof(Brush), typeof(
TextLogViewer),
new PropertyMetadata(Brushes.White, TextPropertyChanged));
97 public static readonly DependencyProperty InfoBrushProperty = DependencyProperty.Register(
"InfoBrush", typeof(Brush), typeof(
TextLogViewer),
new PropertyMetadata(Brushes.White, TextPropertyChanged));
102 public static readonly DependencyProperty WarningBrushProperty = DependencyProperty.Register(
"WarningBrush", typeof(Brush), typeof(
TextLogViewer),
new PropertyMetadata(Brushes.White, TextPropertyChanged));
107 public static readonly DependencyProperty ErrorBrushProperty = DependencyProperty.Register(
"ErrorBrush", typeof(Brush), typeof(
TextLogViewer),
new PropertyMetadata(Brushes.White, TextPropertyChanged));
112 public static readonly DependencyProperty FatalBrushProperty = DependencyProperty.Register(
"FatalBrush", typeof(Brush), typeof(
TextLogViewer),
new PropertyMetadata(Brushes.White, TextPropertyChanged));
117 public static readonly DependencyProperty ShowDebugMessagesProperty = DependencyProperty.Register(
"ShowDebugMessages", typeof(
bool), typeof(
TextLogViewer),
new PropertyMetadata(
true, TextPropertyChanged));
122 public static readonly DependencyProperty ShowVerboseMessagesProperty = DependencyProperty.Register(
"ShowVerboseMessages", typeof(
bool), typeof(
TextLogViewer),
new PropertyMetadata(
true, TextPropertyChanged));
127 public static readonly DependencyProperty ShowInfoMessagesProperty = DependencyProperty.Register(
"ShowInfoMessages", typeof(
bool), typeof(
TextLogViewer),
new PropertyMetadata(
true, TextPropertyChanged));
132 public static readonly DependencyProperty ShowWarningMessagesProperty = DependencyProperty.Register(
"ShowWarningMessages", typeof(
bool), typeof(
TextLogViewer),
new PropertyMetadata(
true, TextPropertyChanged));
137 public static readonly DependencyProperty ShowErrorMessagesProperty = DependencyProperty.Register(
"ShowErrorMessages", typeof(
bool), typeof(
TextLogViewer),
new PropertyMetadata(
true, TextPropertyChanged));
142 public static readonly DependencyProperty ShowFatalMessagesProperty = DependencyProperty.Register(
"ShowFatalMessages", typeof(
bool), typeof(
TextLogViewer),
new PropertyMetadata(
true, TextPropertyChanged));
152 public bool AutoScroll {
get {
return (
bool)GetValue(AutoScrollProperty); } set { SetValue(AutoScrollProperty, value); } }
157 public bool IsToolBarVisible {
get {
return (
bool)GetValue(IsToolBarVisibleProperty); } set { SetValue(IsToolBarVisibleProperty, value); } }
162 public bool CanClearLog {
get {
return (
bool)GetValue(CanClearLogProperty); } set { SetValue(CanClearLogProperty, value); } }
167 public bool CanFilterLog {
get {
return (
bool)GetValue(CanFilterLogProperty); } set { SetValue(CanFilterLogProperty, value); } }
172 public bool CanSearchLog {
get {
return (
bool)GetValue(CanSearchLogProperty); } set { SetValue(CanSearchLogProperty, value); } }
177 public string SearchToken {
get {
return (
string)GetValue(SearchTokenProperty); } set { SetValue(SearchTokenProperty, value); } }
182 public bool SearchMatchCase {
get {
return (
bool)GetValue(SearchMatchCaseProperty); } set { SetValue(SearchMatchCaseProperty, value); } }
187 public bool SearchMatchWord {
get {
return (
bool)GetValue(SearchMatchWordProperty); } set { SetValue(SearchMatchWordProperty, value); } }
192 public Brush SearchMatchBrush {
get {
return (Brush)GetValue(SearchMatchBrushProperty); } set { SetValue(SearchMatchBrushProperty, value); } }
197 public Brush DebugBrush {
get {
return (Brush)GetValue(DebugBrushProperty); } set { SetValue(DebugBrushProperty, value); } }
202 public Brush VerboseBrush {
get {
return (Brush)GetValue(VerboseBrushProperty); } set { SetValue(VerboseBrushProperty, value); } }
207 public Brush InfoBrush {
get {
return (Brush)GetValue(InfoBrushProperty); } set { SetValue(InfoBrushProperty, value); } }
212 public Brush WarningBrush {
get {
return (Brush)GetValue(WarningBrushProperty); } set { SetValue(WarningBrushProperty, value); } }
217 public Brush ErrorBrush {
get {
return (Brush)GetValue(ErrorBrushProperty); } set { SetValue(ErrorBrushProperty, value); } }
222 public Brush FatalBrush {
get {
return (Brush)GetValue(FatalBrushProperty); } set { SetValue(FatalBrushProperty, value); } }
227 public bool ShowDebugMessages {
get {
return (
bool)GetValue(ShowDebugMessagesProperty); } set { SetValue(ShowDebugMessagesProperty, value); } }
232 public bool ShowVerboseMessages {
get {
return (
bool)GetValue(ShowVerboseMessagesProperty); } set { SetValue(ShowVerboseMessagesProperty, value); } }
237 public bool ShowInfoMessages {
get {
return (
bool)GetValue(ShowInfoMessagesProperty); } set { SetValue(ShowInfoMessagesProperty, value); } }
242 public bool ShowWarningMessages {
get {
return (
bool)GetValue(ShowWarningMessagesProperty); } set { SetValue(ShowWarningMessagesProperty, value); } }
247 public bool ShowErrorMessages {
get {
return (
bool)GetValue(ShowErrorMessagesProperty); } set { SetValue(ShowErrorMessagesProperty, value); } }
252 public bool ShowFatalMessages {
get {
return (
bool)GetValue(ShowFatalMessagesProperty); } set { SetValue(ShowFatalMessagesProperty, value); } }
257 base.OnApplyTemplate();
259 logTextBox = GetTemplateChild(
"PART_LogTextBox") as RichTextBox;
260 if (logTextBox == null)
261 throw new InvalidOperationException(
"A part named 'PART_LogTextBox' must be present in the ControlTemplate, and must be of type 'RichTextBox'.");
263 var clearLogButton = GetTemplateChild(
"PART_ClearLog") as ButtonBase;
264 if (clearLogButton != null)
266 clearLogButton.Click += ClearLog;
269 var previousResultButton = GetTemplateChild(
"PART_PreviousResult") as ButtonBase;
270 if (previousResultButton != null)
272 previousResultButton.Click += PreviousResultClicked;
274 var nextResultButton = GetTemplateChild(
"PART_NextResult") as ButtonBase;
275 if (nextResultButton != null)
277 nextResultButton.Click += NextResultClicked;
288 private void ResetText()
290 if (logTextBox != null)
292 ClearSearchResults();
293 var document =
new FlowDocument(
new Paragraph());
294 if (LogMessages != null)
296 var logMessages = LogMessages.ToList();
297 AppendText(document, logMessages);
299 logTextBox.Document = document;
305 if (document == null)
throw new ArgumentNullException(
"document");
306 if (logTextBox != null && logMessages != null)
308 var paragraph = (Paragraph)document.Blocks.AsEnumerable().First();
309 var stringComparison = SearchMatchCase ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
310 var searchToken = SearchToken;
311 foreach (var message
in logMessages.Where(x => ShouldDisplayMessage(x.Type)))
313 var lineText = message + Environment.NewLine;
314 var logColor = GetLogColor(message.Type);
315 if (
string.IsNullOrEmpty(searchToken))
317 paragraph.Inlines.Add(
new Run(lineText) { Foreground = logColor });
323 int tokenIndex = lineText.IndexOf(searchToken, stringComparison);
324 if (tokenIndex == -1)
326 paragraph.Inlines.Add(
new Run(lineText) { Foreground = logColor });
329 bool acceptResult =
true;
330 if (SearchMatchWord && lineText.Length > 1)
334 char c = lineText[tokenIndex - 1];
335 if ((c >=
'A' && c <=
'A') || (c >=
'a' && c <=
'z'))
336 acceptResult =
false;
338 if (tokenIndex + searchToken.Length < lineText.Length)
340 char c = lineText[tokenIndex + searchToken.Length];
341 if ((c >=
'A' && c <=
'A') || (c >=
'a' && c <=
'z'))
342 acceptResult =
false;
349 paragraph.Inlines.Add(
new Run(lineText.Substring(0, tokenIndex)) { Foreground = logColor });
351 var tokenRun =
new Run(lineText.Substring(tokenIndex, searchToken.Length)) {
Background = SearchMatchBrush, Foreground = logColor };
352 paragraph.Inlines.Add(tokenRun);
353 var tokenRange =
new TextRange(tokenRun.ContentStart, tokenRun.ContentEnd);
354 searchMatches.Add(tokenRange);
355 lineText = lineText.Substring(tokenIndex + searchToken.Length);
357 }
while (lineText.Length > 0);
364 private void ClearSearchResults()
366 searchMatches.Clear();
369 private void SelectFirstOccurence()
371 if (searchMatches.Count > 0)
373 SelectSearchResult(0);
377 private void SelectPreviousOccurence()
379 if (searchMatches.Count > 0)
381 int previousResult = (searchMatches.Count + currentResult - 1) % searchMatches.Count;
382 SelectSearchResult(previousResult);
386 private void SelectNextOccurence()
388 if (searchMatches.Count > 0)
390 int nextResult = (currentResult + 1) % searchMatches.Count;
391 SelectSearchResult(nextResult);
395 private void SelectSearchResult(
int resultIndex)
397 var result = searchMatches[resultIndex];
398 logTextBox.Selection.Select(result.Start, result.End);
399 Rect selectionRect = logTextBox.Selection.Start.GetCharacterRect(LogicalDirection.Forward);
400 double offset = selectionRect.Top + logTextBox.VerticalOffset;
401 logTextBox.ScrollToVerticalOffset(offset - logTextBox.ActualHeight / 2);
402 logTextBox.BringIntoView();
403 currentResult = resultIndex;
410 case LogMessageType.Debug:
411 return ShowDebugMessages;
412 case LogMessageType.Verbose:
413 return ShowVerboseMessages;
414 case LogMessageType.Info:
415 return ShowInfoMessages;
416 case LogMessageType.Warning:
417 return ShowWarningMessages;
418 case LogMessageType.Error:
419 return ShowErrorMessages;
420 case LogMessageType.Fatal:
421 return ShowFatalMessages;
423 throw new ArgumentOutOfRangeException(
"type");
431 case LogMessageType.Debug:
433 case LogMessageType.Verbose:
435 case LogMessageType.Info:
437 case LogMessageType.Warning:
439 case LogMessageType.Error:
441 case LogMessageType.Fatal:
444 throw new ArgumentOutOfRangeException(
"type");
448 private static void TextPropertyChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
450 var logViewer = (TextLogViewer)d;
451 logViewer.ResetText();
452 if (logViewer.logTextBox != null)
454 logViewer.logTextBox.ScrollToEnd();
461 private static void LogMessagesPropertyChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
463 var logViewer = (TextLogViewer)d;
465 var newValue = e.NewValue as ICollection<ILogMessage>;
466 if (oldValue != null)
471 if (notifyCollectionChanged != null)
473 notifyCollectionChanged.CollectionChanged -= logViewer.LogMessagesCollectionChanged;
476 if (e.NewValue != null)
479 var notifyCollectionChanged = newValue as INotifyCollectionChanged;
481 if (notifyCollectionChanged != null)
483 notifyCollectionChanged.CollectionChanged += logViewer.LogMessagesCollectionChanged;
486 logViewer.ResetText();
492 private static void SearchTokenChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
494 var logViewer = (TextLogViewer)d;
495 logViewer.ResetText();
496 logViewer.SelectFirstOccurence();
502 private void LogMessagesCollectionChanged(
object sender, NotifyCollectionChangedEventArgs e)
504 bool shouldScroll = AutoScroll && logTextBox != null && logTextBox.ExtentHeight - logTextBox.ViewportHeight - logTextBox.VerticalOffset < 1.0;
506 if (e.Action == NotifyCollectionChangedAction.Add)
508 if (e.NewItems != null)
510 if (logTextBox != null)
512 if (logTextBox.Document == null)
514 logTextBox.Document =
new FlowDocument(
new Paragraph());
516 AppendText(logTextBox.Document, e.NewItems.Cast<
ILogMessage>());
527 logTextBox.ScrollToEnd();
533 SelectPreviousOccurence();
538 SelectNextOccurence();
This control displays a collection of ILogMessage.
LogMessageType
Type of a LogMessage.
The base interface for log messages used by the logging infrastructure.
override void OnApplyTemplate()