- TerminalView: ContextMenuStrip (Kopieren/Alles auswaehlen/Alles kopieren/Terminal leeren) am rtbTerminal - macht das Kopieren entdeckbar (Ctrl+C funktioniert zusaetzlich nativ). - JobsView: ToolStrip 'toolStripJobs' (docked Top) mit Starter-Button 'Aktualisieren' (dgvJobs.Refresh) - weitere Job-Steuerelemente folgen nach und nach. Beide via Designer (.Designer.cs), Logik im Code-Behind. Build 0 Fehler, --smoke-ui ok. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
129 lines
5.2 KiB
C#
129 lines
5.2 KiB
C#
using System;
|
||
using System.Collections.Concurrent;
|
||
using System.Windows.Forms;
|
||
using PolyTraderSharp.Services;
|
||
|
||
namespace PolyTraderSharp.Ui.Views
|
||
{
|
||
/// <summary>
|
||
/// Terminal-/Log-Ansicht. Designbar (siehe TerminalView.Designer.cs). Die Laufzeit-
|
||
/// Abhängigkeit (TerminalLogger) wird per <see cref="Initialize"/> injiziert, damit der
|
||
/// parameterlose Konstruktor für den VS-Designer nutzbar bleibt.
|
||
/// </summary>
|
||
public partial class TerminalView : Form
|
||
{
|
||
private TerminalLogger? _logger;
|
||
private readonly ConcurrentQueue<LogMessageEventArgs> _logQueue = new();
|
||
private readonly System.Windows.Forms.Timer _uiLogTimer = new() { Interval = 250 };
|
||
private bool _autoScroll = true;
|
||
private EventHandler<LogMessageEventArgs>? _logHandler;
|
||
|
||
public TerminalView()
|
||
{
|
||
InitializeComponent();
|
||
|
||
// Steuerelemente + statische Eigenschaften stammen aus dem Designer.
|
||
// Hier nur noch Verhalten verdrahten und einen sinnvollen Default wählen.
|
||
if (cbLogLevel.Items.Count > 0) cbLogLevel.SelectedIndex = 0;
|
||
btnAutoscroll.Click += (_, _) => ToggleAutoscroll();
|
||
_uiLogTimer.Tick += ProcessLogQueue;
|
||
|
||
// Kontextmenü: Text aus dem Terminal kopieren (Ctrl+C funktioniert zusätzlich nativ).
|
||
miCopy.Click += (_, _) => { if (rtbTerminal.SelectionLength > 0) rtbTerminal.Copy(); };
|
||
miSelectAll.Click += (_, _) => rtbTerminal.SelectAll();
|
||
miCopyAll.Click += (_, _) => CopyAllToClipboard();
|
||
miClear.Click += (_, _) => rtbTerminal.Clear();
|
||
}
|
||
|
||
private void CopyAllToClipboard()
|
||
{
|
||
try
|
||
{
|
||
if (!string.IsNullOrEmpty(rtbTerminal.Text))
|
||
Clipboard.SetText(rtbTerminal.Text);
|
||
}
|
||
catch { /* Zwischenablage kann kurzzeitig belegt sein – bewusst ignorieren */ }
|
||
}
|
||
|
||
/// <summary>Verbindet die View mit dem Logger (Laufzeit-DI).</summary>
|
||
public void Initialize(TerminalLogger logger)
|
||
{
|
||
_logger = logger;
|
||
|
||
// Jüngste Historie vorladen, damit das Fenster beim Öffnen nicht leer ist.
|
||
foreach (var e in _logger.GetHistory(TimeSpan.FromMinutes(10)))
|
||
_logQueue.Enqueue(e);
|
||
|
||
_logHandler = (_, e) => _logQueue.Enqueue(e);
|
||
_logger.OnLogMessage += _logHandler;
|
||
|
||
_uiLogTimer.Start();
|
||
Disposed += OnDisposed;
|
||
}
|
||
|
||
private void OnDisposed(object? sender, EventArgs e)
|
||
{
|
||
_uiLogTimer.Stop();
|
||
if (_logger != null && _logHandler != null)
|
||
_logger.OnLogMessage -= _logHandler;
|
||
}
|
||
|
||
private void ToggleAutoscroll()
|
||
{
|
||
_autoScroll = !_autoScroll;
|
||
btnAutoscroll.BackColor = _autoScroll ? System.Drawing.Color.LightGreen : System.Drawing.Color.IndianRed;
|
||
btnAutoscroll.Text = _autoScroll ? "Stop Autoscroll" : "Start Autoscroll";
|
||
}
|
||
|
||
private void ProcessLogQueue(object? sender, EventArgs e)
|
||
{
|
||
if (_logQueue.IsEmpty || !IsHandleCreated) return;
|
||
|
||
string filter = cbLogLevel.SelectedItem?.ToString() ?? "Alle";
|
||
TimeZoneInfo berlinTz = TimeZoneInfo.FindSystemTimeZoneById("W. Europe Standard Time");
|
||
bool appended = false;
|
||
int count = 0;
|
||
const int maxProcess = 500;
|
||
|
||
rtbTerminal.SuspendLayout();
|
||
|
||
while (count < maxProcess && _logQueue.TryDequeue(out var logEvent))
|
||
{
|
||
count++;
|
||
if (filter != "Alle" && logEvent.Level.ToString() != filter) continue;
|
||
|
||
DateTime logTime = logEvent.Timestamp.Kind == DateTimeKind.Utc
|
||
? TimeZoneInfo.ConvertTimeFromUtc(logEvent.Timestamp, berlinTz)
|
||
: TimeZoneInfo.ConvertTime(logEvent.Timestamp, berlinTz);
|
||
string timeStr = $"[{logTime:HH:mm:ss}]";
|
||
|
||
System.Drawing.Color c = System.Drawing.Color.White;
|
||
if (logEvent.Level == LogLevel.Error) c = System.Drawing.Color.Red;
|
||
else if (logEvent.Level == LogLevel.Warning) c = System.Drawing.Color.Yellow;
|
||
else if (logEvent.Level == LogLevel.Trade) c = System.Drawing.Color.LightGreen;
|
||
else if (logEvent.Level == LogLevel.TradeReasoning) c = System.Drawing.Color.Orange;
|
||
|
||
rtbTerminal.SelectionStart = rtbTerminal.TextLength;
|
||
rtbTerminal.SelectionLength = 0;
|
||
rtbTerminal.SelectionColor = c;
|
||
rtbTerminal.AppendText($"{timeStr} [{logEvent.Level}] {logEvent.Message}\n");
|
||
appended = true;
|
||
}
|
||
|
||
if (appended)
|
||
{
|
||
if (rtbTerminal.TextLength > 80000)
|
||
{
|
||
rtbTerminal.Clear();
|
||
rtbTerminal.SelectionColor = System.Drawing.Color.LightPink;
|
||
rtbTerminal.AppendText($"[{DateTime.Now:HH:mm:ss}] [System] Terminal Auto-Clear (RAM Limit erreicht). Vollständige Logs im Ordner /Logs.\n");
|
||
}
|
||
|
||
if (_autoScroll) rtbTerminal.ScrollToCaret();
|
||
}
|
||
|
||
rtbTerminal.ResumeLayout();
|
||
}
|
||
}
|
||
}
|