using System; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using HandyControl.Data; using TwitchDownloaderCore.Services; using TwitchDownloaderWPF.Extensions; using TwitchDownloaderWPF.Models; using TwitchDownloaderWPF.Properties; using TwitchDownloaderWPF.Services; using CheckComboBoxItem = HandyControl.Controls.CheckComboBoxItem; namespace TwitchDownloaderWPF { /// /// Interaction logic for SettingsPage.xaml /// public partial class WindowSettings : Window { private bool _cancelSettingsChanges = true; private bool _refreshThemeOnCancel; private bool _refreshCultureOnCancel; public WindowSettings() { InitializeComponent(); } private void BtnTempBrowse_Click(object sender, RoutedEventArgs e) { var dialog = new Ookii.Dialogs.Wpf.VistaFolderBrowserDialog(); if (dialog.ShowDialog(this).GetValueOrDefault()) { TextTempPath.Text = dialog.SelectedPath; } } private void Window_Initialized(object sender, EventArgs e) { if (Settings.Default.TempPath == "") { TextTempPath.Text = Path.GetTempPath().TrimEnd('\\'); } else { TextTempPath.Text = Settings.Default.TempPath; } TextVodTemplate.Text = Settings.Default.TemplateVod; TextClipTemplate.Text = Settings.Default.TemplateClip; TextChatTemplate.Text = Settings.Default.TemplateChat; CheckDonation.IsChecked = Settings.Default.HideDonation; CheckVerboseErrors.IsChecked = Settings.Default.VerboseErrors; NumMaximumBandwidth.Value = Settings.Default.MaximumBandwidthKib; NumMaximumBandwidth.IsEnabled = Settings.Default.DownloadThrottleEnabled; CheckThrottleEnabled.IsChecked = Settings.Default.DownloadThrottleEnabled; RadioTimeFormatUtc.IsChecked = Settings.Default.UTCVideoTime; CheckReduceMotion.IsChecked = Settings.Default.ReduceMotion; if (Directory.Exists("Themes")) { // Setup theme dropdown ComboTheme.Items.Add("System"); // Cannot be localized string[] themeFiles = Directory.GetFiles("Themes", "*.xaml"); foreach (string themeFile in themeFiles) { ComboTheme.Items.Add(Path.GetFileNameWithoutExtension(themeFile)); } ComboTheme.SelectedItem = Settings.Default.GuiTheme; } // Setup culture dropdown var currentCulture = Settings.Default.GuiCulture; foreach (var (culture, index) in AvailableCultures.All.Select((x, index) => (x, index))) { ComboLocale.Items.Add(culture.NativeName); if (culture.Code == currentCulture) ComboLocale.SelectedIndex = index; } ComboLogLevels.Items.Add(new CheckComboBoxItem { Content = Translations.Strings.LogLevelVerbose, Tag = LogLevel.Verbose }); ComboLogLevels.Items.Add(new CheckComboBoxItem { Content = Translations.Strings.LogLevelInfo, Tag = LogLevel.Info }); ComboLogLevels.Items.Add(new CheckComboBoxItem { Content = Translations.Strings.LogLevelWarning, Tag = LogLevel.Warning }); ComboLogLevels.Items.Add(new CheckComboBoxItem { Content = Translations.Strings.LogLevelError, Tag = LogLevel.Error }); // ComboLogLevels.Items.Add(new CheckComboBoxItem { Content = Translations.Strings.LogLevelFfmpeg, Tag = LogLevel.Ffmpeg }); var currentLogLevels = (LogLevel)Settings.Default.LogLevels; foreach (CheckComboBoxItem item in ComboLogLevels.Items) { if (currentLogLevels.HasFlag((Enum)item.Tag)) { ComboLogLevels.SelectedItems.Add(item); } } var currentCollisionBehavior = (CollisionBehavior)Settings.Default.FileCollisionBehavior; for (var i = 0; i < ComboFileCollisionBehavior.Items.Count; i++) { var current = (ComboBoxItem)ComboFileCollisionBehavior.Items[i]!; if (currentCollisionBehavior == (CollisionBehavior)current.Tag) { ComboFileCollisionBehavior.SelectedIndex = i; break; } } } private void BtnClearCache_Click(object sender, RoutedEventArgs e) { var messageBoxResult = MessageBox.Show(this, Translations.Strings.ClearCacheConfirmation.Replace(@"\n", Environment.NewLine), Translations.Strings.DeleteConfirmation, MessageBoxButton.YesNo); if (messageBoxResult == MessageBoxResult.Yes) { //Let's clear the user selected temp folder and the default one CacheDirectoryService.ClearCacheDirectory(Settings.Default.TempPath, out _); CacheDirectoryService.ClearCacheDirectory(Path.GetTempPath(), out _); } } private void Window_OnSourceInitialized(object sender, EventArgs e) { App.RequestTitleBarChange(); } private void Window_Closing(object sender, EventArgs e) { if (_cancelSettingsChanges) { Settings.Default.Reload(); if (_refreshThemeOnCancel) { App.RequestAppThemeChange(); } if (_refreshCultureOnCancel) { App.RequestCultureChange(); } } } private void Hyperlink_RequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e) { Uri uri = e.Uri; if (!uri.IsAbsoluteUri) { string destinationPath = Path.Combine(Environment.CurrentDirectory, uri.OriginalString); uri = new Uri(destinationPath); } Process.Start(new ProcessStartInfo(uri.AbsoluteUri) { UseShellExecute = true }); e.Handled = true; } private void ComboTheme_OnSelectionChanged(object sender, SelectionChangedEventArgs e) { if (!IsInitialized) return; if (!((string)ComboTheme.SelectedItem).Equals(Settings.Default.GuiTheme, StringComparison.OrdinalIgnoreCase)) { _refreshThemeOnCancel = true; Settings.Default.GuiTheme = (string)ComboTheme.SelectedItem; App.RequestAppThemeChange(true); } } private void ComboLocale_OnSelectionChanged(object sender, SelectionChangedEventArgs e) { if (!IsInitialized) return; if (ComboLocale.SelectedIndex == -1) return; var selectedCulture = AvailableCultures.All[ComboLocale.SelectedIndex].Code; if (selectedCulture != Settings.Default.GuiCulture) { _refreshCultureOnCancel = true; Settings.Default.GuiCulture = selectedCulture; App.RequestCultureChange(); } } private void CheckThrottleEnabled_OnCheckedChanged(object sender, RoutedEventArgs e) { if (!IsInitialized) return; NumMaximumBandwidth.IsEnabled = CheckThrottleEnabled.IsChecked.GetValueOrDefault(); Settings.Default.DownloadThrottleEnabled = CheckThrottleEnabled.IsChecked.GetValueOrDefault(); } private void NumMaximumBandwidth_OnValueChanged(object sender, FunctionEventArgs e) { if (!IsInitialized) return; Settings.Default.MaximumBandwidthKib = (int)NumMaximumBandwidth.Value; } private void BtnResetSettings_OnClick(object sender, RoutedEventArgs e) { if (MessageBox.Show(this, Translations.Strings.ResetSettingsConfirmationMessage, Translations.Strings.ResetSettingsConfirmation, MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes) { Settings.Default.Reset(); Settings.Default.UpgradeRequired = false; Settings.Default.Save(); // TODO: Don't require restarting the application to apply var commandLine = Environment.CommandLine; var arguments = Environment.GetCommandLineArgs(); var fileName = arguments.FirstOrDefault(commandLine.StartsWith, ""); if (fileName.EndsWith(".exe")) { if (MessageBox.Show(this, Translations.Strings.TheApplicationMustBeRestartedMessage, string.Format(Translations.Strings.RestartTheApplication, nameof(TwitchDownloaderWPF)), MessageBoxButton.OKCancel, MessageBoxImage.Information) == MessageBoxResult.OK) { // Create a cmd window that waits 2 seconds before restarting the application var process = new Process { StartInfo = new ProcessStartInfo { FileName = "cmd.exe", Arguments = $"/C choice /C Y /N /D Y /T 2 & START \"\" \"{fileName}\"", WindowStyle = ProcessWindowStyle.Hidden, CreateNoWindow = true, WorkingDirectory = Environment.CurrentDirectory } }; process.Start(); Application.Current.Shutdown(); } } else { MessageBox.Show(this, Translations.Strings.TheApplicationMustBeRestartedMessage, string.Format(Translations.Strings.RestartTheApplication, nameof(TwitchDownloaderWPF)), MessageBoxButton.OK, MessageBoxImage.Information); } } } private void BtnSaveSettings_OnClick(object sender, RoutedEventArgs e) { _cancelSettingsChanges = false; Settings.Default.Save(); Close(); } private void BtnCancelSettings_OnClick(object sender, RoutedEventArgs e) { Close(); } private void TextTempPath_OnTextChanged(object sender, TextChangedEventArgs e) { if (!IsInitialized) return; Settings.Default.TempPath = TextTempPath.Text; } private void CheckDonation_OnCheckedChanged(object sender, RoutedEventArgs e) { if (!IsInitialized) return; Settings.Default.HideDonation = CheckDonation.IsChecked.GetValueOrDefault(); } private void RadioTimeFormat_OnCheckedChanged(object sender, RoutedEventArgs e) { if (!IsInitialized) return; Settings.Default.UTCVideoTime = RadioTimeFormatUtc.IsChecked.GetValueOrDefault(); } private void CheckVerboseErrors_OnCheckedChanged(object sender, RoutedEventArgs e) { if (!IsInitialized) return; Settings.Default.VerboseErrors = CheckVerboseErrors.IsChecked.GetValueOrDefault(); } private void TextVodTemplate_OnTextChanged(object sender, TextChangedEventArgs e) { if (!IsInitialized) return; Settings.Default.TemplateVod = TextVodTemplate.Text; } private void TextClipTemplate_OnTextChanged(object sender, TextChangedEventArgs e) { if (!IsInitialized) return; Settings.Default.TemplateClip = TextClipTemplate.Text; } private void TextChatTemplate_OnTextChanged(object sender, TextChangedEventArgs e) { if (!IsInitialized) return; Settings.Default.TemplateChat = TextChatTemplate.Text; } private void ComboLogLevels_OnSelectionChanged(object sender, SelectionChangedEventArgs e) { if (!IsInitialized) return; var newLogLevel = ComboLogLevels.SelectedItems .Cast() .Sum(item => (int)item.Tag); Settings.Default.LogLevels = newLogLevel; } private void ComboFileCollisionBehavior_OnSelectionChanged(object sender, SelectionChangedEventArgs e) { if (!IsInitialized) return; var behavior = (ComboBoxItem)ComboFileCollisionBehavior.SelectedItem; Settings.Default.FileCollisionBehavior = (int)behavior.Tag; } private void FilenameParameter_MouseDown(object sender, MouseButtonEventArgs e) { if (!IsInitialized || sender is not Run { Text: var parameter }) return; if (e.ChangedButton is not MouseButton.Left and not MouseButton.Middle) return; var focusedElement = Keyboard.FocusedElement; var textBox = GetFilenameTemplateTextBox(focusedElement); if (textBox is null) return; var oldCaretPos = textBox.CaretIndex; if (!textBox.TryInsertAtCaret(parameter)) return; if (e.ChangedButton is MouseButton.Middle && oldCaretPos != -1) { // If we inserted a *_custom template, we can focus inside the quotation marks var quoteIndex = parameter.LastIndexOf('"'); if (quoteIndex != -1) { textBox.CaretIndex = oldCaretPos + quoteIndex; } } e.Handled = true; } [return: MaybeNull] private TextBox GetFilenameTemplateTextBox(IInputElement inputElement) { if (ReferenceEquals(inputElement, TextVodTemplate)) return TextVodTemplate; if (ReferenceEquals(inputElement, TextClipTemplate)) return TextClipTemplate; if (ReferenceEquals(inputElement, TextChatTemplate)) return TextChatTemplate; return null; } private void CheckReduceMotion_OnCheckedChanged(object sender, RoutedEventArgs e) { if (!IsInitialized) return; Settings.Default.ReduceMotion = CheckReduceMotion.IsChecked.GetValueOrDefault(); } } }