using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using AxCopilot.Models; using AxCopilot.Services; using Microsoft.Win32; namespace AxCopilot.Views; public partial class ScheduleEditorWindow : Window { private readonly SettingsService _settings; private readonly ScheduleEntry? _editing; // null = 새 스케줄 private string _triggerType = "daily"; private string _actionType = "app"; private bool _enabled = true; private bool _conditionMustRun = true; // 요일 버튼 → Border 참조 private Border[] _dayBtns = null!; public ScheduleEditorWindow(ScheduleEntry? entry, SettingsService settings) { InitializeComponent(); _settings = settings; _editing = entry; _dayBtns = new[] { BtnSun, BtnMon, BtnTue, BtnWed, BtnThu, BtnFri, BtnSat }; Loaded += OnLoaded; } // ─── 초기화 ───────────────────────────────────────────────────────────── private void OnLoaded(object sender, RoutedEventArgs e) { // 다크 테마 색상 var dimBg = TryFindResource("ItemBackground") as Brush ?? new SolidColorBrush(Color.FromRgb(0x25, 0x26, 0x37)); var accent = TryFindResource("AccentColor") as Brush ?? new SolidColorBrush(Color.FromRgb(0x4B, 0x5E, 0xFC)); var border = TryFindResource("BorderColor") as Brush ?? new SolidColorBrush(Color.FromRgb(0x2E, 0x2F, 0x4A)); // 요일 버튼 기본 색 foreach (var b in _dayBtns) { b.Background = dimBg; b.BorderBrush = border; b.BorderThickness = new Thickness(1); if (b.Child is TextBlock tb) tb.Foreground = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray; } if (_editing != null) LoadFromEntry(_editing); else SetTriggerUi("daily"); SetActionUi(_actionType); UpdateToggleUi(_enabled); SetConditionModeUi(_conditionMustRun); } private void LoadFromEntry(ScheduleEntry e) { NameBox.Text = e.Name; TimeBox.Text = e.TriggerTime; _enabled = e.Enabled; _triggerType = e.TriggerType; _actionType = e.ActionType; if (e.TriggerDate != null) DateBox.Text = e.TriggerDate; SetTriggerUi(e.TriggerType); // 요일 복원 foreach (var b in _dayBtns) { if (int.TryParse(b.Tag?.ToString(), out var day) && e.WeekDays.Contains(day)) SetDaySelected(b, true); } if (e.ActionType == "app") { AppPathBox.Text = e.ActionTarget; AppArgsBox.Text = e.ActionArgs ?? ""; } else { NotifMsgBox.Text = e.ActionTarget; } // 조건 복원 ConditionProcessBox.Text = e.ConditionProcess ?? ""; _conditionMustRun = e.ConditionProcessMustRun; SetConditionModeUi(_conditionMustRun); } // ─── 트리거 유형 ───────────────────────────────────────────────────────── private void TriggerType_Click(object sender, MouseButtonEventArgs e) { if (sender is Border b && b.Tag is string tag) SetTriggerUi(tag); } private void SetTriggerUi(string type) { _triggerType = type; var accent = TryFindResource("AccentColor") as Brush ?? Brushes.Blue; var dimBg = TryFindResource("ItemBackground") as Brush ?? Brushes.DimGray; var secFg = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray; var white = Brushes.White; // 버튼 배경·텍스트 색 초기화 void SetBtn(Border btn, TextBlock txt, bool active) { btn.Background = active ? accent : dimBg; txt.Foreground = active ? white : secFg; } SetBtn(BtnDaily, TxtDaily, type == "daily"); SetBtn(BtnWeekdays, TxtWeekdays, type == "weekdays"); SetBtn(BtnWeekly, TxtWeekly, type == "weekly"); SetBtn(BtnOnce, TxtOnce, type == "once"); // 요일 패널 / 날짜 패널 표시 WeekDaysPanel.Visibility = type == "weekly" ? Visibility.Visible : Visibility.Collapsed; DatePanel.Visibility = type == "once" ? Visibility.Visible : Visibility.Collapsed; // once 기본값 if (type == "once" && string.IsNullOrWhiteSpace(DateBox.Text)) DateBox.Text = DateTime.Now.AddDays(1).ToString("yyyy-MM-dd"); } // ─── 요일 선택 ────────────────────────────────────────────────────────── private void WeekDay_Click(object sender, MouseButtonEventArgs e) { if (sender is not Border btn) return; var accent = TryFindResource("AccentColor") as Brush ?? Brushes.Blue; var dimBg = TryFindResource("ItemBackground") as Brush ?? Brushes.DimGray; bool current = btn.Background == accent; SetDaySelected(btn, !current); } private void SetDaySelected(Border btn, bool selected) { var accent = TryFindResource("AccentColor") as Brush ?? Brushes.Blue; var dimBg = TryFindResource("ItemBackground") as Brush ?? Brushes.DimGray; var secFg = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray; btn.Background = selected ? accent : dimBg; if (btn.Child is TextBlock tb) tb.Foreground = selected ? Brushes.White : secFg; } private List GetSelectedDays() { var accent = TryFindResource("AccentColor") as Brush ?? Brushes.Blue; var list = new List(); foreach (var b in _dayBtns) { if (b.Background == accent && int.TryParse(b.Tag?.ToString(), out var day)) list.Add(day); } return list; } // ─── 액션 유형 ────────────────────────────────────────────────────────── private void ActionType_Click(object sender, MouseButtonEventArgs e) { if (sender is Border b && b.Tag is string tag) SetActionUi(tag); } private void SetActionUi(string type) { _actionType = type; var accent = TryFindResource("AccentColor") as Brush ?? Brushes.Blue; var dimBg = TryFindResource("ItemBackground") as Brush ?? Brushes.DimGray; var secFg = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray; var white = Brushes.White; bool isApp = type == "app"; BtnActionApp.Background = isApp ? accent : dimBg; BtnActionNotif.Background = !isApp ? accent : dimBg; TxtActionApp.Foreground = isApp ? white : secFg; TxtActionNotif.Foreground = !isApp ? white : secFg; // 아이콘 TextBlock은 StackPanel의 첫 번째 자식 if (BtnActionApp.Child is StackPanel spApp && spApp.Children.Count > 0) ((TextBlock)spApp.Children[0]).Foreground = isApp ? white : secFg; if (BtnActionNotif.Child is StackPanel spNotif && spNotif.Children.Count > 0) ((TextBlock)spNotif.Children[0]).Foreground = !isApp ? white : secFg; AppPathPanel.Visibility = isApp ? Visibility.Visible : Visibility.Collapsed; NotifPanel.Visibility = !isApp ? Visibility.Visible : Visibility.Collapsed; } // ─── 앱 찾아보기 ───────────────────────────────────────────────────────── private void BtnBrowseApp_Click(object sender, MouseButtonEventArgs e) { var dlg = new OpenFileDialog { Title = "실행 파일 선택", Filter = "실행 파일|*.exe;*.bat;*.cmd;*.lnk;*.ps1|모든 파일|*.*" }; if (dlg.ShowDialog(this) == true) AppPathBox.Text = dlg.FileName; } // ─── 조건 모드 (L6-4) ─────────────────────────────────────────────────── private void ConditionMode_Click(object sender, MouseButtonEventArgs e) { if (sender is Border b && b.Tag is string tag) SetConditionModeUi(tag == "run"); } private void SetConditionModeUi(bool mustRun) { _conditionMustRun = mustRun; var accent = TryFindResource("AccentColor") as Brush ?? Brushes.Blue; var dimBg = TryFindResource("ItemBackground") as Brush ?? Brushes.DimGray; var secFg = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray; BtnCondRun.Background = mustRun ? accent : dimBg; BtnCondNotRun.Background = !mustRun ? accent : dimBg; TxtCondRun.Foreground = mustRun ? Brushes.White : secFg; TxtCondNotRun.Foreground = !mustRun ? Brushes.White : secFg; } // ─── 활성화 토글 ───────────────────────────────────────────────────────── private void EnabledToggle_Click(object sender, MouseButtonEventArgs e) { _enabled = !_enabled; UpdateToggleUi(_enabled); } private void UpdateToggleUi(bool enabled) { var accent = TryFindResource("AccentColor") as Brush ?? new SolidColorBrush(Color.FromRgb(0x4B, 0x5E, 0xFC)); var off = new SolidColorBrush(Color.FromRgb(0x3A, 0x3B, 0x5A)); EnabledToggle.Background = enabled ? accent : off; // 썸 위치 애니메이션 var da = new DoubleAnimation( enabled ? 1.0 : -1.0, // 실제 HorizontalAlignment·Margin으로 처리 TimeSpan.FromMilliseconds(150)); EnabledThumb.HorizontalAlignment = enabled ? HorizontalAlignment.Right : HorizontalAlignment.Left; EnabledThumb.Margin = enabled ? new Thickness(0, 0, 2, 0) : new Thickness(2, 0, 0, 0); } // ─── 저장 ──────────────────────────────────────────────────────────────── private void BtnSave_Click(object sender, MouseButtonEventArgs e) { var name = NameBox.Text.Trim(); if (string.IsNullOrWhiteSpace(name)) { MessageBox.Show("스케줄 이름을 입력하세요.", "저장 오류", MessageBoxButton.OK, MessageBoxImage.Warning); return; } var timeStr = TimeBox.Text.Trim(); if (!TimeSpan.TryParseExact(timeStr, new[] { @"hh\:mm", @"h\:mm" }, System.Globalization.CultureInfo.InvariantCulture, out _)) { MessageBox.Show("실행 시각을 HH:mm 형식으로 입력하세요. (예: 09:00)", "저장 오류", MessageBoxButton.OK, MessageBoxImage.Warning); return; } if (_triggerType == "once") { var dateStr = DateBox.Text.Trim(); if (!DateTime.TryParse(dateStr, out _)) { MessageBox.Show("실행 날짜를 yyyy-MM-dd 형식으로 입력하세요.", "저장 오류", MessageBoxButton.OK, MessageBoxImage.Warning); return; } } if (_actionType == "app" && string.IsNullOrWhiteSpace(AppPathBox.Text)) { MessageBox.Show("실행할 앱 경로를 입력하세요.", "저장 오류", MessageBoxButton.OK, MessageBoxImage.Warning); return; } // 기존 항목 편집 or 신규 생성 var entry = _editing ?? new ScheduleEntry(); entry.Name = name; entry.Enabled = _enabled; entry.TriggerType = _triggerType; entry.TriggerTime = timeStr; entry.WeekDays = _triggerType == "weekly" ? GetSelectedDays() : new List(); entry.TriggerDate = _triggerType == "once" ? DateBox.Text.Trim() : null; entry.ActionType = _actionType; entry.ActionTarget = _actionType == "app" ? AppPathBox.Text.Trim() : NotifMsgBox.Text.Trim(); entry.ActionArgs = _actionType == "app" ? AppArgsBox.Text.Trim() : ""; // 조건 저장 (L6-4) entry.ConditionProcess = ConditionProcessBox.Text.Trim(); entry.ConditionProcessMustRun = _conditionMustRun; var schedules = _settings.Settings.Schedules; if (_editing == null) schedules.Add(entry); // 편집 모드: 이미 리스트 내 참조이므로 별도 추가 불필요 _settings.Save(); NotificationService.Notify("AX Copilot", $"스케줄 '{entry.Name}' 저장됨"); Close(); } // ─── 윈도우 컨트롤 ────────────────────────────────────────────────────── private void TitleBar_MouseDown(object sender, MouseButtonEventArgs e) { if (e.LeftButton == MouseButtonState.Pressed) DragMove(); } private void BtnClose_Click(object sender, MouseButtonEventArgs e) => Close(); private void BtnCancel_Click(object sender, MouseButtonEventArgs e) => Close(); }