/* Subtitles.cs $ This file is part of the HandBrake source code. Homepage: . It may be used under the terms of the GNU General Public License. */ namespace Handbrake.Controls { using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.IO; using System.Linq; using System.Windows.Forms; using HandBrake.ApplicationServices; using HandBrake.ApplicationServices.Model.Encoding; using HandBrake.ApplicationServices.Services.Interfaces; using HandBrake.ApplicationServices.Utilities; /// /// The Subtitles Tab /// public partial class Subtitles : UserControl { #region Priavate Variables and Collections /// /// Map of languages to language codes /// private readonly IDictionary langMap = new Dictionary(); /// /// A List of SRT Files /// private readonly IDictionary srtFiles = new Dictionary(); /// /// The Subtitle List /// private readonly List subList = new List(); /// /// The User Setting Service. /// private static readonly IUserSettingService UserSettingService = ServiceManager.UserSettingService; #endregion /// /// Initializes a new instance of the class. /// public Subtitles() { InitializeComponent(); langMap = LanguageUtilities.MapLanguages(); foreach (string key in langMap.Keys) srt_lang.Items.Add(key); srt_charcode.SelectedIndex = 28; srt_lang.SelectedIndex = 40; } /// /// Gets SubtitlesList. /// public List SubtitlesList { get { return subList; } } #region Public Methods /// /// Gets the CLI Query for this panel /// /// A CliQuery string public string GetCliQuery { get { string query = string.Empty; if (lv_subList.Items.Count != 0) // If we have subtitle tracks { // BitMap and CC's string subtitleTracks = String.Empty; string subtitleForced = String.Empty; string subtitleBurn = String.Empty; string subtitleDefault = String.Empty; // SRT string srtFile = String.Empty; string srtCodeset = String.Empty; string srtOffset = String.Empty; string srtLang = String.Empty; string srtDefault = String.Empty; int srtCount = 0; int subCount = 0; foreach (SubtitleTrack item in subList) { string itemToAdd; if (item.IsSrtSubtitle) // We have an SRT file { srtCount++; // SRT track id. srtLang += srtLang == string.Empty ? langMap[item.SrtLang] : "," + langMap[item.SrtLang]; srtCodeset += srtCodeset == string.Empty ? item.SrtCharCode : "," + item.SrtCharCode; if (item.Default) srtDefault = srtCount.ToString(); itemToAdd = item.SrtPath; srtFile += srtFile == string.Empty ? itemToAdd : "," + itemToAdd; itemToAdd = item.SrtOffset.ToString(); srtOffset += srtOffset == string.Empty ? itemToAdd : "," + itemToAdd; } else // We have Bitmap or CC { subCount++; // Find --subtitle if (item.Track.Contains("Foreign Audio Search")) itemToAdd = "scan"; else { string[] tempSub = item.Track.Split(' '); itemToAdd = tempSub[0]; } subtitleTracks += subtitleTracks == string.Empty ? itemToAdd : "," + itemToAdd; // Find --subtitle-forced if (item.Forced) subtitleForced += subtitleForced == string.Empty ? subCount.ToString() : "," + subCount; // Find --subtitle-burn if (item.Burned) subtitleBurn = subCount.ToString(); // Find --subtitle-default if (item.Default) subtitleDefault = subCount.ToString(); } } // Build The CLI Subtitles Query if (subtitleTracks != string.Empty) { query += " --subtitle " + subtitleTracks; if (subtitleForced != string.Empty) query += " --subtitle-forced=" + subtitleForced; if (subtitleBurn != string.Empty) query += " --subtitle-burn=" + subtitleBurn; if (subtitleDefault != string.Empty) query += " --subtitle-default=" + subtitleDefault; } if (srtFile != string.Empty) // SRTs { query += " --srt-file " + "\"" + srtFile + "\""; if (srtCodeset != string.Empty) query += " --srt-codeset " + srtCodeset; if (srtOffset != string.Empty) query += " --srt-offset " + srtOffset; if (srtLang != string.Empty) query += " --srt-lang " + srtLang; if (srtDefault != string.Empty) query += " --srt-default=" + srtDefault; } } return query; } } /// /// Checks of the current settings will require the m4v file extension /// /// True if Yes public bool RequiresM4V() { return this.subList.Any(track => track.SubtitleType != SubtitleType.VobSub); } /// /// Automatically setup the subtitle tracks. /// This handles the automatic setup of subitles which the user can control from the program options /// /// List of Subtitles public void SetSubtitleTrackAuto(object[] subs) { drp_subtitleTracks.Items.Clear(); drp_subtitleTracks.Items.Add("Foreign Audio Search (Bitmap)"); drp_subtitleTracks.Items.AddRange(subs); drp_subtitleTracks.SelectedIndex = 0; Clear(); this.AutomaticSubtitleSelection(); } /// /// Automatic Subtitle Selection based on user preferences. /// public void AutomaticSubtitleSelection() { // Avoid trying to add elements when no subtitles are there. if (drp_subtitleTracks.Items.Count < 2) { return; } this.Clear(); // Array with the Index numbers of the prefered and additional languages. // This allows to have for each language the order in which they appear in the DVD list. Dictionary languageIndex = new Dictionary(); // This is used to keep the Prefered Language in the front and the other languages in order. ArrayList languageOrder = new ArrayList(); // New DUB Settings int mode = UserSettingService.GetUserSetting(UserSettingConstants.DubModeSubtitle); if (UserSettingService.GetUserSetting(UserSettingConstants.NativeLanguage) == "Any") mode = 0; // Native Language is not 'Any', so initialising the Language Dictionary if (mode >= 3) { languageIndex.Add(UserSettingService.GetUserSetting(UserSettingConstants.NativeLanguage), new ArrayList()); languageOrder.Add(UserSettingService.GetUserSetting(UserSettingConstants.NativeLanguage)); foreach (string item in UserSettingService.GetUserSetting(UserSettingConstants.SelectedLanguages)) { if (!languageIndex.ContainsKey(item)) { languageIndex.Add(item, new ArrayList()); languageOrder.Add(item); } } bool elementFound = false; int i = 0; foreach (object item in drp_subtitleTracks.Items) { foreach (KeyValuePair kvp in languageIndex) { if (item.ToString().Contains(kvp.Key)) { kvp.Value.Add(i); elementFound = true; } } i++; } // If there are no selected languages found, the first available will be taken. if (!elementFound) mode = 2; } switch (mode) { case 1: // Adding all audio tracks for (int i = 1; i < drp_subtitleTracks.Items.Count; i++) { drp_subtitleTracks.SelectedIndex = i; this.btn_addSubtitleTrack_Click(this, new EventArgs()); } break; case 2: // Adding only the first Audio Track drp_subtitleTracks.SelectedIndex = 1; if (drp_subtitleTracks.SelectedItem != null) this.btn_addSubtitleTrack_Click(this, new EventArgs()); break; case 3: foreach (string item in languageOrder) { if (languageIndex[item].Count > 0) { foreach (int i in languageIndex[item]) { drp_subtitleTracks.SelectedIndex = i; if (drp_subtitleTracks.SelectedItem != null) { this.btn_addSubtitleTrack_Click(this, new EventArgs()); //subList.ClearSelection(); } } } } break; case 4: if (languageIndex[(string)languageOrder[0]].Count > 0) { foreach (int i in languageIndex[(string)languageOrder[0]]) { drp_subtitleTracks.SelectedIndex = i; if (drp_subtitleTracks.SelectedItem != null) { this.btn_addSubtitleTrack_Click(this, new EventArgs()); //subList.ClearSelection(); } } } break; } // Revert the selection back tio the first item. drp_subtitleTracks.SelectedIndex = 0; // Add Closed Captions if the user has the option enabled. if (UserSettingService.GetUserSetting(UserSettingConstants.UseClosedCaption)) { foreach (object item in drp_subtitleTracks.Items) { if (item.ToString().Contains("Closed")) { drp_subtitleTracks.SelectedItem = item; btn_addSubtitleTrack_Click(this, EventArgs.Empty); } } } } /// /// Clear the Subtitle List /// public void Clear() { lv_subList.Items.Clear(); subList.Clear(); srtFiles.Clear(); } #endregion #region Primary Controls /// /// Add a subtitle Track /// /// /// The Sender /// /// /// The Event Args /// private void btn_addSubtitleTrack_Click(object sender, EventArgs e) { if (drp_subtitleTracks.SelectedItem == null) { MessageBox.Show("No Subtitle Track Selected.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } // Logic string forcedVal = check_forced.CheckState == CheckState.Checked ? "Yes" : "No"; string defaultSub = check_default.CheckState == CheckState.Checked ? "Yes" : "No"; string burnedVal = check_burned.CheckState == CheckState.Checked && (drp_subtitleTracks.Text.Contains("(VOBSUB)") || drp_subtitleTracks.Text.Contains("(SSA)")) ? "Yes" : "No"; string srtCode = "-", srtLangVal = "-", srtPath = "-", srtFile = "-"; int srtOffsetMs = 0; if (drp_subtitleTracks.SelectedItem.ToString().Contains(".srt")) { srtFiles.TryGetValue(drp_subtitleTracks.SelectedItem.ToString(), out srtPath); srtFile = drp_subtitleTracks.SelectedItem.ToString(); srtLangVal = srt_lang.SelectedItem.ToString(); srtCode = srt_charcode.SelectedItem.ToString(); srtOffsetMs = (int)srt_offset.Value; if (defaultSub == "Yes") this.SetDefaultToOffForAllSRTTracks(); } else { if (defaultSub == "Yes") this.SetDefaultToOffForAllTracks(); if (burnedVal == "Yes") this.SetBurnedToOffForAllTracks(); } string trackName = (drp_subtitleTracks.SelectedItem.ToString().Contains(".srt")) ? srtLangVal + " (" + srtFile + ")" : drp_subtitleTracks.SelectedItem.ToString(); SubtitleTrack track = new SubtitleTrack { Track = trackName, Forced = check_forced.Checked, Burned = check_burned.Checked, Default = check_default.Checked, SrtLang = srtLangVal, SrtCharCode = srtCode, SrtOffset = srtOffsetMs, SrtPath = srtPath, SrtFileName = srtFile }; lv_subList.Items.Add(track.ListView); subList.Add(track); } /// /// Add all audio tracks. /// /// /// The Sender /// /// /// The Event Args /// private void mnu_AddAll_Click(object sender, EventArgs e) { // TODO - Might need to be a bit more clever with this. Will wait and see if this causes any problems. foreach (object item in drp_subtitleTracks.Items) { if (!item.ToString().Contains("Foreign Audio Search")) { drp_subtitleTracks.SelectedItem = item; btn_addSubtitleTrack_Click(this, EventArgs.Empty); } } } /// /// Add all closed caption tracks /// /// /// The Sender /// /// /// The Event Args /// private void mnu_AddAllCC_Click(object sender, EventArgs e) { foreach (object item in drp_subtitleTracks.Items) { if (item.ToString().Contains("Closed")) { drp_subtitleTracks.SelectedItem = item; btn_addSubtitleTrack_Click(this, EventArgs.Empty); } } } /// /// Clear all tracks. /// /// /// The Sender /// /// /// The Event Args /// private void mnu_ClearAll_Click(object sender, EventArgs e) { lv_subList.Items.Clear(); subList.Clear(); } /// /// Remvoe a track /// /// /// The Sender /// /// /// The Event Args /// private void btn_RemoveTrack_Click(object sender, EventArgs e) { this.RemoveSelectedTrack(); } /// /// Import an SRT Subtitle Track /// /// /// The sender. /// /// /// The e. /// private void BtnSrtAddClick(object sender, EventArgs e) { if (openFileDialog.ShowDialog() != DialogResult.OK) return; if (srtFiles.ContainsKey(Path.GetFileName(openFileDialog.FileName))) { MessageBox.Show( "A Subtitle track with the same name has already been imported.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } drp_subtitleTracks.Items.Add(Path.GetFileName(openFileDialog.FileName)); drp_subtitleTracks.SelectedItem = Path.GetFileName(openFileDialog.FileName); srtFiles.Add(Path.GetFileName(openFileDialog.FileName), openFileDialog.FileName); } /// /// The Subtitle List selected index has changed /// /// /// The sender. /// /// /// The e. /// private void LbSubListSelectedIndexChanged(object sender, EventArgs e) { // Set the dropdown controls based on the selected item in the List. if (lv_subList.Items.Count != 0 && lv_subList.SelectedIndices.Count != 0) { SubtitleTrack track = subList[lv_subList.SelectedIndices[0]]; int c = 0; if (lv_subList.Items[lv_subList.SelectedIndices[0]].SubItems[0].Text.ToLower().Contains(".srt")) // We have an SRT { foreach (var item in drp_subtitleTracks.Items) { if (item.ToString() == track.SrtFileName) drp_subtitleTracks.SelectedIndex = c; c++; } srt_lang.SelectedItem = track.SrtLang; srt_offset.Value = track.SrtOffset; srt_charcode.SelectedItem = track.SrtCharCode; check_default.CheckState = track.Default ? CheckState.Checked : CheckState.Unchecked; } else // We have Bitmap/CC { foreach (var item in drp_subtitleTracks.Items) { if (item.ToString() == track.Track) drp_subtitleTracks.SelectedIndex = c; c++; } check_forced.CheckState = track.Forced ? CheckState.Checked : CheckState.Unchecked; check_burned.CheckState = track.Burned ? CheckState.Checked : CheckState.Unchecked; check_default.CheckState = track.Default ? CheckState.Checked : CheckState.Unchecked; } } } #endregion #region Subtitle Controls /// /// Handle the Subtitle track dropdown changed event /// /// /// The sender. /// /// /// The e. /// private void DrpSubtitleTracksSelectedIndexChanged(object sender, EventArgs e) { if (drp_subtitleTracks.SelectedItem.ToString().Contains(".srt")) { check_forced.Enabled = false; check_burned.Enabled = false; srt_lang.Enabled = true; srt_charcode.Enabled = true; srt_offset.Enabled = true; } else { check_forced.Enabled = true; check_burned.Enabled = true; srt_lang.Enabled = false; srt_charcode.Enabled = false; srt_offset.Enabled = false; } // Update an item in the list if required. if (lv_subList.Items.Count == 0 || lv_subList.SelectedIndices.Count == 0) return; if (drp_subtitleTracks.SelectedItem.ToString().Contains(".srt")) { lv_subList.Items[lv_subList.SelectedIndices[0]].SubItems[0].Text = srt_lang.SelectedItem + "(" + drp_subtitleTracks.SelectedItem + ")"; lv_subList.Select(); string srtPath; srtFiles.TryGetValue(drp_subtitleTracks.SelectedItem.ToString(), out srtPath); subList[lv_subList.SelectedIndices[0]].SrtPath = srtPath; } else { lv_subList.Items[lv_subList.SelectedIndices[0]].SubItems[0].Text = drp_subtitleTracks.SelectedItem.ToString(); lv_subList.Select(); } subList[lv_subList.SelectedIndices[0]].Track = drp_subtitleTracks.SelectedItem.ToString(); } /// /// Foreced Subtitles checkbox changed /// /// /// The sender. /// /// /// The e. /// private void CheckForcedCheckedChanged(object sender, EventArgs e) { if (lv_subList.Items.Count == 0 || lv_subList.SelectedIndices.Count == 0) return; lv_subList.Items[lv_subList.SelectedIndices[0]].SubItems[1].Text = check_forced.Checked ? "Yes" : "No"; lv_subList.Select(); subList[lv_subList.SelectedIndices[0]].Forced = check_forced.Checked; // Update SubList List } /// /// Burned in subtitles checkbox changed /// /// /// The sender. /// /// /// The e. /// private void CheckBurnedCheckedChanged(object sender, EventArgs e) { if (lv_subList.Items.Count == 0 || lv_subList.SelectedIndices.Count == 0) return; if (check_burned.Checked) // Make sure we only have 1 burned track this.SetBurnedToOffForAllTracks(); lv_subList.Items[lv_subList.SelectedIndices[0]].SubItems[2].Text = check_burned.Checked ? "Yes" : "No"; lv_subList.Select(); subList[lv_subList.SelectedIndices[0]].Burned = check_burned.Checked; // Update SubList List } /// /// Default Subitle track check changed /// /// /// The sender. /// /// /// The e. /// private void CheckDefaultCheckedChanged(object sender, EventArgs e) { if (lv_subList.Items.Count == 0 || lv_subList.SelectedIndices.Count == 0) return; if (check_default.Checked) // Make sure we only have 1 default track if (lv_subList.Items[lv_subList.SelectedIndices[0]].SubItems[0].Text.Contains(".srt")) this.SetDefaultToOffForAllSRTTracks(); else this.SetDefaultToOffForAllTracks(); lv_subList.Items[lv_subList.SelectedIndices[0]].SubItems[3].Text = check_default.Checked ? "Yes" : "No"; lv_subList.Select(); subList[lv_subList.SelectedIndices[0]].Default = check_default.Checked; // Update SubList List } /// /// Srt Offset Numeric Control Changed /// /// /// The sender. /// /// /// The e. /// private void SrtOffsetValueChanged(object sender, EventArgs e) { // Update an item in the list if required. if (lv_subList.Items.Count == 0 || lv_subList.SelectedIndices.Count == 0) return; lv_subList.Items[lv_subList.SelectedIndices[0]].SubItems[6].Text = srt_offset.Value.ToString(); lv_subList.Select(); subList[lv_subList.SelectedIndices[0]].SrtOffset = (int)srt_offset.Value; // Update SubList List } /// /// The SRT Charcode changed /// /// /// The sender. /// /// /// The e. /// private void SrtCharcodeSelectedIndexChanged(object sender, EventArgs e) { if (lv_subList.Items.Count == 0 || lv_subList.SelectedIndices.Count == 0) return; lv_subList.Items[lv_subList.SelectedIndices[0]].SubItems[5].Text = srt_charcode.SelectedItem.ToString(); lv_subList.Select(); subList[lv_subList.SelectedIndices[0]].SrtCharCode = srt_charcode.SelectedItem.ToString(); // Update SubList List } /// /// The Srt Language code changed /// /// /// The sender. /// /// /// The e. /// private void SrtLangSelectedIndexChanged(object sender, EventArgs e) { if (lv_subList.Items.Count == 0 || lv_subList.SelectedIndices.Count == 0) return; lv_subList.Items[lv_subList.SelectedIndices[0]].SubItems[4].Text = srt_lang.SelectedItem.ToString(); lv_subList.Select(); subList[lv_subList.SelectedIndices[0]].SrtLang = srt_lang.SelectedItem.ToString(); // Update SubList List } #endregion #region Right Click Menu /// /// Move an item in the subtitle list up /// /// /// The sender. /// /// /// The e. /// private void MnuMoveupClick(object sender, EventArgs e) { if (lv_subList.SelectedIndices.Count != 0) { ListViewItem item = lv_subList.SelectedItems[0]; int index = item.Index; index--; if (lv_subList.Items.Count > index && index >= 0) { lv_subList.Items.Remove(item); lv_subList.Items.Insert(index, item); item.Selected = true; lv_subList.Focus(); } } } /// /// Move an item in the subtitle list down /// /// /// The sender. /// /// /// The e. /// private void MnuMovedownClick(object sender, EventArgs e) { if (lv_subList.SelectedIndices.Count != 0) { ListViewItem item = lv_subList.SelectedItems[0]; int index = item.Index; index++; if (index < lv_subList.Items.Count) { lv_subList.Items.Remove(item); lv_subList.Items.Insert(index, item); item.Selected = true; lv_subList.Focus(); } } } /// /// Remove a selected subtitle track. /// /// /// The sender. /// /// /// The e. /// private void MnuRemoveClick(object sender, EventArgs e) { this.RemoveSelectedTrack(); } #endregion #region Helpers /// /// Set all Non SRT tracks to Default = NO /// private void SetDefaultToOffForAllTracks() { int c = 0; foreach (ListViewItem item in lv_subList.Items) { if (subList[c].SrtPath == "-") { if (item.SubItems[3].Text == "Yes") { item.SubItems[3].Text = "No"; subList[c].Default = false; } } c++; } } /// /// Set all subtitle tracks so that they have no default. /// private void SetDefaultToOffForAllSRTTracks() { int c = 0; foreach (ListViewItem item in lv_subList.Items) { if (!subList[c].IsSrtSubtitle) { if (item.SubItems[3].Text == "Yes") { item.SubItems[3].Text = "No"; subList[c].Default = false; } } c++; } } /// /// Set all tracks to Burned = No /// private void SetBurnedToOffForAllTracks() { int c = 0; foreach (ListViewItem item in lv_subList.Items) { if (item.SubItems[2].Text == "Yes") { item.SubItems[2].Text = "No"; subList[c].Burned = false; } c++; } } /// /// Remove a selected track /// private void RemoveSelectedTrack() { // Remove the Item and reselect the control if the following conditions are met. if (lv_subList.SelectedItems.Count != 0) { // Record the current selected index. int currentPosition = lv_subList.SelectedIndices[0]; int selectedInd = lv_subList.SelectedIndices[0]; lv_subList.Items.RemoveAt(selectedInd); subList.RemoveAt(selectedInd); // Now reslect the correct item and give focus to the list. if (lv_subList.Items.Count != 0) { if (currentPosition <= (lv_subList.Items.Count - 1)) lv_subList.Items[currentPosition].Selected = true; else if (currentPosition > (lv_subList.Items.Count - 1)) lv_subList.Items[lv_subList.Items.Count - 1].Selected = true; lv_subList.Select(); } } } #endregion } }