/* frmQueue.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 { using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.IO; using System.Linq; using System.Windows.Forms; using Functions; using HandBrake.ApplicationServices; using HandBrake.ApplicationServices.Functions; using HandBrake.ApplicationServices.Model; using HandBrake.ApplicationServices.Model.Encoding; using HandBrake.ApplicationServices.Services; using HandBrake.ApplicationServices.Services.Interfaces; using HandBrake.ApplicationServices.Utilities; using HandBrake.Interop.Model.Encoding; using Model; using AudioEncoder = HandBrake.ApplicationServices.Model.Encoding.AudioEncoder; /// /// The Queue Window /// public partial class frmQueue : Form { /// /// Update Handler Delegate /// private delegate void UpdateHandler(); /// /// An instance of the Queue service /// private readonly IQueueProcessor queue; /// /// A reference to the main application window /// private readonly frmMain mainWindow; /// /// The User setting service /// private readonly IUserSettingService userSettingService = new UserSettingService(); /// /// Initializes a new instance of the class. /// /// /// An instance of the queue service. /// /// /// The main window. /// public frmQueue(IQueueProcessor q, frmMain mw) { InitializeComponent(); this.mainWindow = mw; this.queue = q; queue.EncodeService.EncodeStarted += this.QueueOnEncodeStart; queue.QueueCompleted += this.QueueOnQueueFinished; queue.QueuePaused += this.QueueOnPaused; queue.QueueManager.QueueChanged += this.queue_QueueListChanged; queue.EncodeService.EncodeStarted += this.queue_EncodeStarted; queue.EncodeService.EncodeCompleted += this.queue_EncodeEnded; drp_completeOption.Text = userSettingService.GetUserSetting(UserSettingConstants.WhenCompleteAction); } /// /// Queue Changed /// /// /// The sender. /// /// /// The e. /// private void queue_QueueListChanged(object sender, EventArgs e) { UpdateUiElementsOnQueueChange(); } /// /// Encode Ended /// /// /// The sender. /// /// /// The e. /// private void queue_EncodeEnded(object sender, EventArgs e) { queue.EncodeService.EncodeStatusChanged -= EncodeQueue_EncodeStatusChanged; ResetEncodeText(); } /// /// Queue Started /// /// /// The sender. /// /// /// The e. /// private void queue_EncodeStarted(object sender, EventArgs e) { this.SetCurrentEncodeInformation(); queue.EncodeService.EncodeStatusChanged += EncodeQueue_EncodeStatusChanged; } /// /// Display the Encode Status /// /// /// The sender. /// /// /// The e. /// private void EncodeQueue_EncodeStatusChanged(object sender, HandBrake.ApplicationServices.EventArgs.EncodeProgressEventArgs e) { if (this.InvokeRequired) { this.BeginInvoke(new EncodeProgessStatus(EncodeQueue_EncodeStatusChanged), new[] { sender, e }); return; } lbl_encodeStatus.Text = string.Format( "Encoding: Pass {0} of {1}, {2:00.00}%, FPS: {3:000.0}, Avg FPS: {4:000.0}, Time Remaining: {5}, Elapsed: {6:hh\\:mm\\:ss}", e.Task, e.TaskCount, e.PercentComplete, e.CurrentFrameRate, e.AverageFrameRate, e.EstimatedTimeLeft, e.ElapsedTime); UpdateStatusLabel(); } /// /// Handle the Queue Paused event /// /// /// The sender. /// /// /// The EventArgs. /// private void QueueOnPaused(object sender, EventArgs e) { SetUiEncodeFinished(); UpdateUiElementsOnQueueChange(); } /// /// Handle the Queue Finished event. /// /// /// The sender. /// /// /// The EventArgs. /// private void QueueOnQueueFinished(object sender, EventArgs e) { SetUiEncodeFinished(); ResetQueue(); // Reset the Queue Window } /// /// Handle the Encode Started event /// /// /// The sender. /// /// /// The e. /// private void QueueOnEncodeStart(object sender, EventArgs e) { SetUiEncodeStarted(); // make sure the UI is set correctly UpdateUiElementsOnQueueChange(); // Redraw the Queue, a new encode has started. } /// /// Initializes the Queue list with the Arraylist from the Queue class /// public void SetQueue() { UpdateUiElementsOnQueueChange(); } /// /// Initializes the Queue list, then shows and activates the window /// public new void Show() { Show(true); } /// /// Initializes the Queue list only if doSetQueue is true, then shows and activates the window /// /// Indicates whether to call setQueue() before showing the window public void Show(bool doSetQueue) { if (doSetQueue) SetQueue(); base.Show(); } /// /// Handle the Encode button Click event /// /// The sender /// the EventArgs private void BtnEncodeClick(object sender, EventArgs e) { if (!queue.IsProcessing) { SetUiEncodeStarted(); } lbl_encodeStatus.Text = "Encoding ..."; queue.Start(); } /// /// Handle the Pause button click event. /// /// /// The sender. /// /// /// The EventArgs. /// private void BtnPauseClick(object sender, EventArgs e) { queue.Pause(); MessageBox.Show( "No further items on the queue will start. The current encode process will continue until it is finished. \nClick 'Encode' when you wish to continue encoding the queue.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); } // UI Work /// /// Setup the UI to show that an encode has started /// private void SetUiEncodeStarted() { if (InvokeRequired) { BeginInvoke(new UpdateHandler(SetUiEncodeStarted)); return; } btn_encode.Enabled = false; btn_pause.Visible = true; } /// /// Setup the UI to indicate that an encode has finished. /// private void SetUiEncodeFinished() { if (InvokeRequired) { BeginInvoke(new UpdateHandler(SetUiEncodeFinished)); return; } btn_pause.Visible = false; btn_encode.Enabled = true; } /// /// Reset the Queue Window display /// private void ResetQueue() { if (InvokeRequired) { BeginInvoke(new UpdateHandler(ResetQueue)); return; } btn_pause.Visible = false; btn_encode.Enabled = true; this.RedrawQueue(); ResetEncodeText(); } /// /// Reset the current job text /// private void ResetEncodeText() { if (InvokeRequired) { BeginInvoke(new UpdateHandler(ResetEncodeText)); return; } lbl_encodeStatus.Text = "Ready"; lbl_source.Text = "-"; lbl_dest.Text = "-"; lbl_encodeOptions.Text = "-"; UpdateStatusLabel(); } /// /// Redraw the Queue window with the latest information about HandBrakes status /// private void RedrawQueue() { if (InvokeRequired) { BeginInvoke(new UpdateHandler(RedrawQueue)); return; } list_queue.Items.Clear(); ReadOnlyCollection theQueue = queue.QueueManager.Queue; foreach (QueueTask queueItem in theQueue) { string qItem = queueItem.Query; EncodeTask parsed = QueryParserUtility.Parse(qItem); // Get the DVD Title string title = parsed.Title == 0 ? "Auto" : parsed.Title.ToString(); // Get the DVD Chapters string chapters; if (parsed.StartPoint == 0) chapters = "Auto"; else { chapters = parsed.StartPoint.ToString(); if (parsed.EndPoint != 0) chapters = chapters + " - " + parsed.EndPoint; } ListViewItem item = new ListViewItem { Tag = queueItem, Text = EnumHelper.GetDescription(queueItem.Status) }; item.SubItems.Add(title); item.SubItems.Add(chapters); // Chapters item.SubItems.Add(queueItem.Source); // Source item.SubItems.Add(queueItem.Destination); // Destination item.SubItems.Add(EnumHelper.GetDisplayValue(parsed.VideoEncoder)); // Display The Audio Track Information string audio = string.Empty; foreach (AudioTrack track in parsed.AudioTracks) { if (audio != string.Empty) audio += ", " + EnumHelper.GetDescription(track.Encoder); else audio = EnumHelper.GetDescription(track.Encoder); } item.SubItems.Add(audio); // Audio list_queue.Items.Add(item); } } /// /// Update the UI elements /// private void UpdateUiElementsOnQueueChange() { if (InvokeRequired) { BeginInvoke(new UpdateHandler(UpdateUiElementsOnQueueChange)); return; } RedrawQueue(); UpdateStatusLabel(); } /// /// Update the Display /// private void UpdateStatusLabel() { if (InvokeRequired) { BeginInvoke(new UpdateHandler(UpdateStatusLabel)); return; } lbl_encodesPending.Text = string.Format("{0} encodes(s) pending", this.queue.QueueManager.Count); } /// /// Set the window up with the current encode information /// private void SetCurrentEncodeInformation() { try { if (InvokeRequired) { BeginInvoke(new UpdateHandler(SetCurrentEncodeInformation)); } EncodeTask parsed = QueryParserUtility.Parse(queue.QueueManager.LastProcessedJob.Query); // Get title and chapters string title = parsed.Title == 0 ? "Auto" : parsed.Title.ToString(); string chapterlbl; if (Equals(parsed.StartPoint, 0)) chapterlbl = "Auto"; else { string chapters = parsed.StartPoint.ToString(); if (parsed.EndPoint != 0) chapters = chapters + " - " + parsed.EndPoint; chapterlbl = chapters; } // Get audio information string audio = string.Empty; foreach (AudioTrack track in parsed.AudioTracks) { if (audio != string.Empty) audio += ", " + EnumHelper.GetDescription(track.Encoder); else audio = EnumHelper.GetDescription(track.Encoder); } // found query is a global varible lbl_encodeStatus.Text = "Encoding ..."; lbl_source.Text = queue.QueueManager.LastProcessedJob.Source + "(Title: " + title + " Chapters: " + chapterlbl + ")"; lbl_dest.Text = queue.QueueManager.LastProcessedJob.Destination; lbl_encodeOptions.Text = string.Format("Video: {0}, Audio: {1}\nx264 Options: {2}", EnumHelper.GetDisplayValue(parsed.VideoEncoder), audio, parsed.AdvancedEncoderOptions); } catch (Exception) { // Do Nothing } } /* Right Click Menu */ /// /// Handle the Move Up Menu Item /// /// /// The sender. /// /// /// The e. /// private void MnuUpClick(object sender, EventArgs e) { MoveUp(); } /// /// Handle the Move down Menu Item /// /// /// The sender. /// /// /// The e. /// private void MnuDownClick(object sender, EventArgs e) { MoveDown(); } /// /// Edit a job /// /// /// The sender. /// /// /// The e. /// private void MnuEditClick(object sender, EventArgs e) { if (list_queue.SelectedIndices != null && list_queue.SelectedIndices.Count != 0) { lock (queue) { lock (list_queue) { QueueTask index = list_queue.SelectedItems[0].Tag as QueueTask; mainWindow.RecievingJob(index); queue.QueueManager.Remove(index); RedrawQueue(); } } } } /// /// Handle the delete Menu Item /// /// /// The sender. /// /// /// The e. /// private void MnuDeleteClick(object sender, EventArgs e) { DeleteSelectedItems(); } /// /// Clear down the Queue /// /// /// The sender. /// /// /// The EventArgs. /// private void mnuClearAll_Click(object sender, EventArgs e) { DialogResult result = MessageBox.Show( "Are you sure you wish to clear down the queue?", "Clear the Queue", MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (result == DialogResult.Yes) { queue.QueueManager.Clear(); } } /* Keyboard Shortcuts */ /// /// Handle the delete keyboard press /// /// /// The sender. /// /// /// The e. /// private void ListQueueDeleteKey(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Delete && e.Modifiers == Keys.None) DeleteSelectedItems(); } /* Queue Management */ /// /// Move items up in the queue /// private void MoveUp() { // If there are selected items and the first item is not selected if (list_queue.SelectedIndices.Count > 0 && !list_queue.SelectedIndices.Contains(0)) { // Copy the selected indices to preserve them during the movement List selectedIndices = new List(list_queue.SelectedIndices.Count); foreach (int selectedIndex in list_queue.SelectedIndices) selectedIndices.Add(selectedIndex); // Move up each selected item foreach (int selectedIndex in selectedIndices) queue.QueueManager.MoveUp(selectedIndex); // Keep the selected item(s) selected, now moved up one index foreach (int selectedIndex in selectedIndices) if (selectedIndex - 1 > -1) // Defensive programming: ensure index is good list_queue.Items[selectedIndex - 1].Selected = true; } list_queue.Select(); // Activate the control to show the selected items } /// /// Move items down in the queue /// private void MoveDown() { // If there are selected items and the last item is not selected if (list_queue.SelectedIndices.Count > 0 && !list_queue.SelectedIndices.Contains(list_queue.Items[list_queue.Items.Count - 1].Index)) { // Copy the selected indices to preserve them during the movement List selectedIndices = new List(list_queue.SelectedIndices.Count); foreach (int selectedIndex in list_queue.SelectedIndices) selectedIndices.Add(selectedIndex); // Reverse the indices to move the items down from last to first (preserves indices) selectedIndices.Reverse(); // Move down each selected item foreach (int selectedIndex in selectedIndices) queue.QueueManager.MoveDown(selectedIndex); // Keep the selected item(s) selected, now moved down one index foreach (int selectedIndex in selectedIndices) if (selectedIndex + 1 < list_queue.Items.Count) // Defensive programming: ensure index is good list_queue.Items[selectedIndex + 1].Selected = true; } list_queue.Select(); // Activate the control to show the selected items } /// /// Delete the currently selected items on the queue /// private void DeleteSelectedItems() { // If there are selected items if (list_queue.SelectedIndices.Count > 0) { // Remove each selected item foreach (ListViewItem selectedIndex in this.list_queue.SelectedItems) queue.QueueManager.Remove((QueueTask)selectedIndex.Tag); // Select the first item after deletion. if (list_queue.Items.Count > 0) list_queue.Items[0].Selected = true; } list_queue.Select(); // Activate the control to show the selected items } /* Queue Import / Export features */ /// /// Create a batch script /// /// /// The sender. /// /// /// The e. /// private void MnuBatchClick(object sender, EventArgs e) { SaveFile.FileName = string.Empty; SaveFile.Filter = "Batch|.bat"; SaveFile.ShowDialog(); if (SaveFile.FileName != String.Empty) queue.QueueManager.WriteBatchScriptToFile(SaveFile.FileName); } /// /// Export Queue /// /// /// The sender. /// /// /// The e. /// private void MnuExportClick(object sender, EventArgs e) { SaveFile.FileName = string.Empty; SaveFile.Filter = "HandBrake Queue|*.queue"; SaveFile.ShowDialog(); if (SaveFile.FileName != String.Empty) queue.QueueManager.BackupQueue(SaveFile.FileName); } /// /// Import Queue /// /// /// The sender. /// /// /// The e. /// private void MnuImportClick(object sender, EventArgs e) { OpenFile.FileName = string.Empty; OpenFile.ShowDialog(); if (OpenFile.FileName != String.Empty) queue.QueueManager.RestoreQueue(OpenFile.FileName); } /// /// Readd current job to queue /// /// /// The sender. /// /// /// The e. /// private void MnuReaddClick(object sender, EventArgs e) { if (queue.QueueManager.LastProcessedJob != null && !queue.QueueManager.LastProcessedJob.IsEmpty) { queue.QueueManager.Add(queue.QueueManager.LastProcessedJob); } } /* Overrides */ /// /// Hide's the window when the user tries to "x" out of the window instead of closing it. /// /// /// The e. /// protected override void OnClosing(CancelEventArgs e) { e.Cancel = true; this.Hide(); base.OnClosing(e); } /// /// Change the OnComplete option setting. /// /// /// The sender. /// /// /// The EventArgs. /// private void CompleteOptionChanged(object sender, EventArgs e) { userSettingService.SetUserSetting(UserSettingConstants.WhenCompleteAction, drp_completeOption.Text); } /// /// Clear all completed items /// /// /// The sender. /// /// /// The e. /// private void mnuClearCompleted_Click(object sender, EventArgs e) { this.queue.QueueManager.ClearCompleted(); } /// /// Retry Job Menu Item /// /// /// The sender. /// /// /// The e. /// private void mnu_Retry_Click(object sender, EventArgs e) { if (list_queue.SelectedIndices.Count != 0) { lock (queue) { lock (list_queue) { QueueTask index = list_queue.SelectedItems[0].Tag as QueueTask; try { queue.QueueManager.ResetJobStatusToWaiting(index); } catch (Exception) { MessageBox.Show( "Can only retry a job if it is in an Error or Completed state.", "Notice", MessageBoxButtons.OK, MessageBoxIcon.Information); } RedrawQueue(); } } } } } }