diff --git a/AutoTrackR2/HomePage.xaml b/AutoTrackR2/HomePage.xaml index 58c0e95..2666233 100644 --- a/AutoTrackR2/HomePage.xaml +++ b/AutoTrackR2/HomePage.xaml @@ -52,115 +52,115 @@ Margin="0,0,0,10" VerticalAlignment="Top"> <StackPanel - VerticalAlignment="Top" - HorizontalAlignment="Center" - Width="152" - Margin="10,5,10,5"> - <TextBlock Name="PilotNameTitle" - Text="Pilot" - Width="152" - Height="20" - Background="Transparent" - FontFamily="{StaticResource Orbitron}" - Margin="0,5,0,0" - Foreground="{DynamicResource AltTextBrush}" - FontSize="14"/> - <TextBlock Name="PilotNameTextBox" - Text="" - Width="152" - Height="20" - Background="Transparent" - FontFamily="{StaticResource Orbitron}" - Margin="0,0,0,0" - Foreground="{DynamicResource TextBrush}" - FontSize="10" - TextAlignment="Center"/> - <TextBlock Name="PlayerShipTitle" - Text="Ship" - Width="152" - Height="20" - Background="Transparent" - FontFamily="{StaticResource Orbitron}" - Margin="0,5,0,0" - Foreground="{DynamicResource AltTextBrush}" - FontSize="14"/> - <TextBlock Name="PlayerShipTextBox" - Text="" - Width="152" - Height="20" - Background="Transparent" - FontFamily="{StaticResource Orbitron}" - Margin="0,0,0,0" - Foreground="{DynamicResource TextBrush}" - FontSize="10" - TextAlignment="Center"/> - <TextBlock Name="GameModeTitle" - Text="Game Mode" - Width="152" - Height="20" - Background="Transparent" - FontFamily="{StaticResource Orbitron}" - Margin="0,5,0,0" - Foreground="{DynamicResource AltTextBrush}" - FontSize="14"/> - <TextBlock Name="GameModeTextBox" - Text="" - Width="152" - Height="20" - Background="Transparent" - FontFamily="{StaticResource Orbitron}" - Margin="0,0,0,0" - Foreground="{DynamicResource TextBrush}" - FontSize="10" - TextAlignment="Center"/> - <TextBlock Name="LocationTitle" - Text="Location" - Width="152" - Height="20" - Background="Transparent" - FontFamily="{StaticResource Orbitron}" - Margin="0,5,0,0" - Foreground="{DynamicResource AltTextBrush}" - FontSize="14"/> - <TextBlock Name="LocationTextBox" - Text="Unknown" - Width="152" - Height="20" - Background="Transparent" - FontFamily="{StaticResource Orbitron}" - Margin="0,0,0,0" - Foreground="{DynamicResource TextBrush}" - FontSize="10" - TextAlignment="Center"/> - <TextBlock Name="KillTallyTitle" - Text="Kill Tally" - Width="152" - Height="20" - Background="Transparent" - FontFamily="{StaticResource Orbitron}" - Margin="0,5,0,0" - Foreground="{DynamicResource AltTextBrush}" - FontSize="14"/> - <TextBlock Name="KillTallyTextBox" - Text="" - Width="152" - Height="20" - Background="Transparent" - FontFamily="{StaticResource Orbitron}" - Margin="0,0,0,0" - Foreground="{DynamicResource TextBrush}" - FontSize="10" - TextAlignment="Center"/> - <TextBox x:Name="DebugPanel" - Text="" - Width="152" - Height="98" - Background="Transparent" - FontFamily="{StaticResource Orbitron}" - Foreground="{DynamicResource TextBrush}" - FontSize="8" - BorderThickness="0" - Margin="0,9,0,0"/> + VerticalAlignment="Top" + HorizontalAlignment="Center" + Width="152" + Margin="10,5,10,5"> + <TextBlock Name="PilotNameTitle" + Text="Pilot" + Width="152" + Height="20" + Background="Transparent" + FontFamily="{StaticResource Orbitron}" + Margin="0,5,0,0" + Foreground="{DynamicResource AltTextBrush}" + FontSize="14"/> + <TextBlock Name="PilotNameTextBox" + Text="" + Width="152" + Height="20" + Background="Transparent" + FontFamily="{StaticResource Orbitron}" + Margin="0,0,0,0" + Foreground="{DynamicResource TextBrush}" + FontSize="10" + TextAlignment="Center"/> + <TextBlock Name="PlayerShipTitle" + Text="Ship" + Width="152" + Height="20" + Background="Transparent" + FontFamily="{StaticResource Orbitron}" + Margin="0,5,0,0" + Foreground="{DynamicResource AltTextBrush}" + FontSize="14"/> + <TextBlock Name="PlayerShipTextBox" + Text="" + Width="152" + Height="20" + Background="Transparent" + FontFamily="{StaticResource Orbitron}" + Margin="0,0,0,0" + Foreground="{DynamicResource TextBrush}" + FontSize="10" + TextAlignment="Center"/> + <TextBlock Name="GameModeTitle" + Text="Game Mode" + Width="152" + Height="20" + Background="Transparent" + FontFamily="{StaticResource Orbitron}" + Margin="0,5,0,0" + Foreground="{DynamicResource AltTextBrush}" + FontSize="14"/> + <TextBlock Name="GameModeTextBox" + Text="" + Width="152" + Height="20" + Background="Transparent" + FontFamily="{StaticResource Orbitron}" + Margin="0,0,0,0" + Foreground="{DynamicResource TextBrush}" + FontSize="10" + TextAlignment="Center"/> + <TextBlock Name="LocationTitle" + Text="Location" + Width="152" + Height="20" + Background="Transparent" + FontFamily="{StaticResource Orbitron}" + Margin="0,5,0,0" + Foreground="{DynamicResource AltTextBrush}" + FontSize="14"/> + <TextBlock Name="LocationTextBox" + Text="Unknown" + Width="152" + Height="20" + Background="Transparent" + FontFamily="{StaticResource Orbitron}" + Margin="0,0,0,0" + Foreground="{DynamicResource TextBrush}" + FontSize="10" + TextAlignment="Center"/> + <TextBlock Name="KillTallyTitle" + Text="Kill Tally" + Width="152" + Height="20" + Background="Transparent" + FontFamily="{StaticResource Orbitron}" + Margin="0,5,0,0" + Foreground="{DynamicResource AltTextBrush}" + FontSize="14"/> + <TextBlock Name="KillTallyTextBox" + Text="" + Width="152" + Height="20" + Background="Transparent" + FontFamily="{StaticResource Orbitron}" + Margin="0,0,0,0" + Foreground="{DynamicResource TextBrush}" + FontSize="10" + TextAlignment="Center"/> + <TextBox x:Name="DebugPanel" + Text="" + Width="152" + Height="98" + Background="Transparent" + FontFamily="{StaticResource Orbitron}" + Foreground="{DynamicResource TextBrush}" + FontSize="8" + BorderThickness="0" + Margin="0,9,0,0"/> </StackPanel> </Border> <StackPanel Grid.Row="1" diff --git a/AutoTrackR2/HomePage.xaml.cs b/AutoTrackR2/HomePage.xaml.cs index 2928870..b4a8dcf 100644 --- a/AutoTrackR2/HomePage.xaml.cs +++ b/AutoTrackR2/HomePage.xaml.cs @@ -21,14 +21,14 @@ public partial class HomePage : UserControl private LogHandler? _logHandler; private KillHistoryManager _killHistoryManager; + private LogBackupProcessor? _logBackupProcessor; private bool _UIEventsRegistered = false; private System.Timers.Timer _statusCheckTimer; private bool _isLogHandlerRunning = false; - private int _counter = 1; - private System.Timers.Timer _counterTimer; private bool _isInitializing = false; private System.Timers.Timer? _initializationTimer; private bool _wasStarCitizenRunningOnStart = false; + private bool _isProcessingLogBackups = false; public HomePage() { @@ -62,15 +62,16 @@ public partial class HomePage : UserControl _statusCheckTimer = new System.Timers.Timer(1000); // Check every second _statusCheckTimer.Elapsed += CheckStarCitizenStatus; _statusCheckTimer.Start(); - - // Initialize and start the counter timer - _counterTimer = new System.Timers.Timer(1000); // Update every second - _counterTimer.Elapsed += UpdateCounter; - _counterTimer.Start(); } private void CheckStarCitizenStatus(object? sender, ElapsedEventArgs e) { + if (_isProcessingLogBackups) + { + // Simulate TrackR as running during log backup processing + Dispatcher.Invoke(() => UpdateStatusIndicator(true)); + return; + } bool isRunning = IsStarCitizenRunning(); Dispatcher.Invoke(() => { @@ -569,12 +570,40 @@ public partial class HomePage : UserControl return Process.GetProcessesByName("StarCitizen").Length > 0; } - private void UpdateCounter(object? sender, ElapsedEventArgs e) + public async void ProcessLogBackups_Click(object sender, RoutedEventArgs e) { - Dispatcher.Invoke(() => + if (_isProcessingLogBackups) { - DebugPanel.Text = _counter.ToString(); - _counter = (_counter % 10) + 1; // Count from 1 to 10 and loop - }); + MessageBox.Show("Already processing log backups. Please wait.", "Processing", MessageBoxButton.OK, MessageBoxImage.Information); + return; + } + + try + { + _isProcessingLogBackups = true; + UpdateStatusIndicator(true, true); // Set to yellow for processing + + if (_logBackupProcessor == null) + { + var logBackupsPath = Path.Combine(Path.GetDirectoryName(ConfigManager.LogFile)!, "logbackups"); + _logBackupProcessor = new LogBackupProcessor(logBackupsPath, _killHistoryManager, _logHandler?.GetEventHandlers() ?? new List<ILogEventHandler>()); + } + + await _logBackupProcessor.ProcessLogBackupsAsync((logFile) => + { + DebugPanel.Text = $"Processing: {Path.GetFileName(logFile)}"; + }); + MessageBox.Show("Log backups processed successfully!", "Success", MessageBoxButton.OK, MessageBoxImage.Information); + DebugPanel.Text = ""; // Clear the debug panel after successful processing + } + catch (Exception ex) + { + MessageBox.Show($"Error processing log backups: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error); + } + finally + { + _isProcessingLogBackups = false; + UpdateStatusIndicator(IsStarCitizenRunning()); + } } } diff --git a/AutoTrackR2/KillHistoryManager.cs b/AutoTrackR2/KillHistoryManager.cs index 16871fa..14c1cfb 100644 --- a/AutoTrackR2/KillHistoryManager.cs +++ b/AutoTrackR2/KillHistoryManager.cs @@ -2,117 +2,132 @@ using System.IO; using System.Text; using System.Linq; +using System.Diagnostics; +using System.Threading; +using System.Collections.Concurrent; +using System.Threading.Tasks; namespace AutoTrackR2; public class KillHistoryManager { - private string _killHistoryPath; + private readonly string _killHistoryPath; private readonly string _headers = "KillTime,EnemyPilot,EnemyShip,Enlisted,RecordNumber,OrgAffiliation,Player,Weapon,Ship,Method,Mode,GameVersion,TrackRver,Logged,PFP,Hash\n"; private readonly KillStreakManager _killStreakManager; + private readonly ConcurrentQueue<KillData> _killQueue; + private readonly CancellationTokenSource _cancellationTokenSource; + private readonly Task _processingTask; + private bool _killStreakSoundEnabled = true; public KillHistoryManager(string logPath, string soundsPath) { - _killHistoryPath = logPath; - _killStreakManager = new KillStreakManager(soundsPath); + var appDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "AutoTrackR2"); + Directory.CreateDirectory(appDataPath); // Ensure the directory exists + _killHistoryPath = Path.Combine(appDataPath, "Kill-log.csv"); + // Create the CSV file with headers if it doesn't exist if (!File.Exists(_killHistoryPath)) { File.WriteAllText(_killHistoryPath, _headers); } - else + + _killStreakManager = new KillStreakManager(soundsPath); + _killQueue = new ConcurrentQueue<KillData>(); + _cancellationTokenSource = new CancellationTokenSource(); + + // Start the background processing task + _processingTask = Task.Run(ProcessKillQueue); + } + + private async Task ProcessKillQueue() + { + while (!_cancellationTokenSource.Token.IsCancellationRequested) { - CheckAndFixMalformedCsv(); + try + { + if (_killQueue.TryDequeue(out var kill)) + { + await ProcessKillAsync(kill); + } + else + { + await Task.Delay(100, _cancellationTokenSource.Token); + } + } + catch (OperationCanceledException) + { + break; + } + catch (Exception ex) + { + Debug.WriteLine($"Error processing kill: {ex.Message}"); + await Task.Delay(1000, _cancellationTokenSource.Token); + } } } - private void CheckAndFixMalformedCsv() + private async Task ProcessKillAsync(KillData kill) { try { - // Try to read the file to check if it's malformed - using var reader = new StreamReader(_killHistoryPath); - var firstLine = reader.ReadLine(); - - // If the file is empty or doesn't start with the correct headers, it's malformed - if (string.IsNullOrEmpty(firstLine) || firstLine != _headers.TrimEnd('\n')) + // Ensure all fields are properly escaped for CSV + var fields = new[] { - // Create a backup of the malformed file - string backupPath = Path.Combine( - Path.GetDirectoryName(_killHistoryPath)!, - "Kill-log.old" - ); + kill.KillTime.ToString(), + EscapeCsvField(kill.EnemyPilot), + EscapeCsvField(kill.EnemyShip), + EscapeCsvField(kill.Enlisted), + EscapeCsvField(kill.RecordNumber), + EscapeCsvField(kill.OrgAffiliation), + EscapeCsvField(kill.Player), + EscapeCsvField(kill.Weapon), + EscapeCsvField(kill.Ship), + EscapeCsvField(kill.Method), + EscapeCsvField(kill.Mode), + EscapeCsvField(kill.GameVersion), + EscapeCsvField(kill.TrackRver), + EscapeCsvField(kill.Logged), + EscapeCsvField(kill.PFP), + EscapeCsvField(kill.Hash) + }; - // If Kill-log.old already exists, delete it - if (File.Exists(backupPath)) - { - File.Delete(backupPath); - } + var csvLine = string.Join(",", fields); - // Rename the malformed file - File.Move(_killHistoryPath, backupPath); - - // Create a new file with correct headers - File.WriteAllText(_killHistoryPath, _headers); - } + // Use FileShare.Read to allow other processes to read while we write + using var stream = new FileStream(_killHistoryPath, FileMode.Append, FileAccess.Write, FileShare.Read); + using var writer = new StreamWriter(stream); + await writer.WriteLineAsync(csvLine); } catch (Exception ex) { - // If there's any error reading the file, consider it malformed - Console.WriteLine($"Error reading CSV file: {ex.Message}"); - string backupPath = Path.Combine( - Path.GetDirectoryName(_killHistoryPath)!, - "Kill-log.old" - ); - - // If Kill-log.old already exists, delete it - if (File.Exists(backupPath)) - { - File.Delete(backupPath); - } - - // Rename the malformed file - File.Move(_killHistoryPath, backupPath); - - // Create a new file with correct headers - File.WriteAllText(_killHistoryPath, _headers); + Debug.WriteLine($"Error writing kill to CSV: {ex.Message}"); + throw; } } - public void AddKill(KillData killData) + private string EscapeCsvField(string field) { - // Ensure the CSV file exists - // This should only happen if the file was deleted or corrupted - if (!File.Exists(_killHistoryPath)) - { - File.WriteAllText(_killHistoryPath, _headers); - } - - // Remove comma from Enlisted - killData.Enlisted = killData.Enlisted?.Replace(",", string.Empty); - - // Append the new kill data to the CSV file - var csv = new StringBuilder(); - csv.AppendLine($"\"{killData.KillTime}\",\"{killData.EnemyPilot}\",\"{killData.EnemyShip}\",\"{killData.Enlisted}\",\"{killData.RecordNumber}\",\"{killData.OrgAffiliation}\",\"{killData.Player}\",\"{killData.Weapon}\",\"{killData.Ship}\",\"{killData.Method}\",\"{killData.Mode}\",\"{killData.GameVersion}\",\"{killData.TrackRver}\",\"{killData.Logged}\",\"{killData.PFP}\",\"{killData.Hash}\""); + if (string.IsNullOrEmpty(field)) return ""; - // Check file can be written to - try + // If the field contains any special characters, wrap it in quotes + if (field.Contains(",") || field.Contains("\"") || field.Contains("\n") || field.Contains("\r")) { - using var fileStream = new FileStream(_killHistoryPath, FileMode.Append, FileAccess.Write, FileShare.Read); - using var writer = new StreamWriter(fileStream); - writer.Write(csv.ToString()); + // Double up any quotes + field = field.Replace("\"", "\"\""); + return $"\"{field}\""; + } - // Trigger kill streak sound only if enabled - if (ConfigManager.KillStreakEnabled == 1) - { - _killStreakManager.OnKill(); - } - } - catch (IOException ex) - { - // Handle the exception (e.g., log it) - Console.WriteLine($"Error writing to file: {ex.Message}"); - } + return field; + } + + public void AddKill(KillData kill) + { + _killQueue.Enqueue(kill); + } + + public void PlayKillStreakSound() + { + _killStreakManager.OnKill(); } public void ResetKillStreak() @@ -120,23 +135,30 @@ public class KillHistoryManager _killStreakManager.OnDeath(); } + public void Dispose() + { + _cancellationTokenSource.Cancel(); + _processingTask.Wait(); + _cancellationTokenSource.Dispose(); + } + public List<KillData> GetKills() { var kills = new List<KillData>(); using var reader = new StreamReader(new FileStream(_killHistoryPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)); reader.ReadLine(); // Skip headers - + while (reader.Peek() >= 0) { var line = reader.ReadLine(); - + // Remove extra quotes from CSV data // Todo: These quotes are for handling commas in the data, but not sure if they're necessary line = line?.Replace("\"", string.Empty); - + var data = line?.Split(','); - + kills.Add(new KillData { KillTime = data?[0], diff --git a/AutoTrackR2/LogBackupProcessor.cs b/AutoTrackR2/LogBackupProcessor.cs new file mode 100644 index 0000000..ace0b6c --- /dev/null +++ b/AutoTrackR2/LogBackupProcessor.cs @@ -0,0 +1,83 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using AutoTrackR2.LogEventHandlers; +using System.Windows; +using System.Linq; +using System.Threading; + +namespace AutoTrackR2; + +public class LogBackupProcessor +{ + private readonly string _logBackupsPath; + private readonly KillHistoryManager _killHistoryManager; + private readonly List<ILogEventHandler> _logEventHandlers; + + public LogBackupProcessor(string logBackupsPath, KillHistoryManager killHistoryManager, List<ILogEventHandler> logEventHandlers) + { + _logBackupsPath = logBackupsPath; + _killHistoryManager = killHistoryManager; + _logEventHandlers = logEventHandlers; + } + + public async Task ProcessLogBackupsAsync(Action<string>? onLogFileProcessed = null) + { + if (!Directory.Exists(_logBackupsPath)) + { + Console.WriteLine($"Log backups directory not found: {_logBackupsPath}"); + return; + } + + var logFiles = Directory.GetFiles(_logBackupsPath, "*.log", SearchOption.AllDirectories) + .Where(file => File.GetLastWriteTime(file) >= new DateTime(2025, 3, 27)) + .ToArray(); + Array.Sort(logFiles); // Process files in chronological order + + for (int i = 0; i < logFiles.Length; i++) + { + var logFile = logFiles[i]; + onLogFileProcessed?.Invoke(logFile); + await ProcessLogFileAsync(logFile); + } + } + + private async Task ProcessLogFileAsync(string logFilePath) + { + try + { + using var reader = new StreamReader(logFilePath); + string? line; + var lines = new List<string>(); + while ((line = await reader.ReadLineAsync()) != null) + { + lines.Add(line); + } + int actorDeathCount = 0; + await Task.Run(() => Parallel.ForEach(lines, line => + { + var entry = new LogEntry { Message = line }; + foreach (var handler in _logEventHandlers) + { + if (handler.Pattern.IsMatch(line)) + { + handler.Handle(entry); + if (handler is ActorDeathEvent) + { + Interlocked.Increment(ref actorDeathCount); + } + } + } + })); + Console.WriteLine($"Processed {actorDeathCount} actor deaths in {logFilePath}"); + // Wait 5 seconds after processing the file before moving to the next file + await Task.Delay(TimeSpan.FromSeconds(5)); + } + catch (Exception ex) + { + Console.WriteLine($"Error processing log file {logFilePath}: {ex.Message}"); + } + } +} \ No newline at end of file diff --git a/AutoTrackR2/LogHandler.cs b/AutoTrackR2/LogHandler.cs index d919078..c8da3c7 100644 --- a/AutoTrackR2/LogHandler.cs +++ b/AutoTrackR2/LogHandler.cs @@ -46,9 +46,10 @@ public class LogHandler new GameVersionEvent(), new JumpDriveStateChangedEvent(), new RequestJumpFailedEvent(), - new VehicleDestructionEvent() + new VehicleDestructionEvent(), + new ActorDeathEvent() ]; - + public LogHandler(string? logPath) { if (string.IsNullOrEmpty(logPath)) @@ -104,8 +105,6 @@ public class LogHandler HandleLogEntry(line); } - // Ensures that any deaths already in log aren't sent to the APIs until the monitor thread is running - _eventHandlers.Add(new ActorDeathEvent()); StartMonitoring(); } @@ -218,4 +217,9 @@ public class LogHandler } _gameProcessState = newGameProcessState; } + + public List<ILogEventHandler> GetEventHandlers() + { + return new List<ILogEventHandler>(_eventHandlers); + } } \ No newline at end of file diff --git a/AutoTrackR2/MainWindow.xaml b/AutoTrackR2/MainWindow.xaml index 5d47783..b8c4f33 100644 --- a/AutoTrackR2/MainWindow.xaml +++ b/AutoTrackR2/MainWindow.xaml @@ -1,37 +1,80 @@ <Window x:Class="AutoTrackR2.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - Title="AutoTrackR" Height="450" Width="800" - WindowStyle="None" ResizeMode="NoResize" + Title="AutoTrackR" + Height="450" + Width="800" + WindowStyle="None" + ResizeMode="NoResize" AllowsTransparency="True" Style="{StaticResource CustomWindowStyle}"> <Grid> <!-- Custom Title Bar --> - <DockPanel Height="30" VerticalAlignment="Top" MouseDown="TitleBar_MouseDown" Margin="5" Background="Transparent"> - <StackPanel Orientation="Horizontal" HorizontalAlignment="Right"> - <Button Content="_" Width="30" Height="25" Click="MinimizeWindow" Style="{StaticResource TitleButtonStyle}" FontFamily="{StaticResource Orbitron}"/> - <Button Content="X" Width="30" Height="25" Click="CloseWindow" Style="{StaticResource TitleButtonStyle}" FontFamily="{StaticResource Orbitron}"/> + <DockPanel Height="30" + VerticalAlignment="Top" + MouseDown="TitleBar_MouseDown" + Margin="5" + Background="Transparent"> + <StackPanel Orientation="Horizontal" + HorizontalAlignment="Right"> + <Button Content="_" + Width="30" + Height="25" + Click="MinimizeWindow" + Style="{StaticResource TitleButtonStyle}" + FontFamily="{StaticResource Orbitron}"/> + <Button Content="X" + Width="30" + Height="25" + Click="CloseWindow" + Style="{StaticResource TitleButtonStyle}" + FontFamily="{StaticResource Orbitron}"/> </StackPanel> </DockPanel> <!-- Main Content Area --> - <Grid Margin="0,30,0,0"> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="150"/> - <ColumnDefinition Width="*"/> - </Grid.ColumnDefinitions> + <Grid Margin="0,30,0,0"> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="150"/> + <ColumnDefinition Width="*"/> + </Grid.ColumnDefinitions> - <!-- Left Tab Panel --> - <StackPanel VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="5,0,0,0"> - <Image x:Name="Logo" Height="138" Source="/Assets/AutoTrackR.png" Stretch="Fill" Width="141" RenderOptions.BitmapScalingMode="Fant"/> - <Button Content="Home" Name="HomeTab" Margin="10,40,10,10" Height="40" Style="{StaticResource TabButtonStyle}" Click="TabButton_Click"/> - <Button Content="Config" Name="ConfigTab" Margin="10" Height="40" Style="{StaticResource TabButtonStyle}" Click="TabButton_Click"/> - </StackPanel> + <!-- Left Tab Panel --> + <StackPanel VerticalAlignment="Stretch" + HorizontalAlignment="Stretch" + Margin="5,0,0,0"> + <Image x:Name="Logo" + Height="138" + Source="/Assets/AutoTrackR.png" + Stretch="Fill" + Width="141" + RenderOptions.BitmapScalingMode="Fant"/> + <Button Content="Home" + Name="HomeTab" + Margin="10,40,10,10" + Height="40" + Style="{StaticResource TabButtonStyle}" + Click="TabButton_Click"/> + <Button Content="Config" + Name="ConfigTab" + Margin="10" + Height="40" + Style="{StaticResource TabButtonStyle}" + Click="TabButton_Click"/> + <Button Content="Process Log Backups" + Name="ProcessLogBackupsButton" + Margin="10" + Height="40" + Style="{StaticResource TabButtonStyle}" + Click="ProcessLogBackups_Click"/> + </StackPanel> - <!-- Content Area --> - <ContentControl Grid.Column="1" Name="ContentControl" Margin="10"> - <!-- Default content can be set here --> - </ContentControl> - </Grid> + <!-- Content Area --> + <ContentControl Grid.Column="1" + Name="ContentControl" + Margin="10"> + <!-- Default content can be set here --> + </ContentControl> + </Grid> </Grid> </Window> \ No newline at end of file diff --git a/AutoTrackR2/MainWindow.xaml.cs b/AutoTrackR2/MainWindow.xaml.cs index 6254e34..3a9479a 100644 --- a/AutoTrackR2/MainWindow.xaml.cs +++ b/AutoTrackR2/MainWindow.xaml.cs @@ -120,6 +120,11 @@ namespace AutoTrackR2 UpdateTabVisuals(); } + private void ProcessLogBackups_Click(object sender, RoutedEventArgs e) + { + homePage.ProcessLogBackups_Click(sender, e); + } + private void UpdateTabStates(string activeTab) { foreach (var key in tabStates.Keys)