// -------------------------------------------------------------------------------------------------------------------- // // This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License. // // // The Preview View Model // // -------------------------------------------------------------------------------------------------------------------- namespace HandBrakeWPF.ViewModels { using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Linq; using System.Windows; using Caliburn.Micro; using HandBrakeWPF.EventArgs; using HandBrakeWPF.Properties; using HandBrakeWPF.Services.Interfaces; using HandBrakeWPF.Services.Queue.Interfaces; using HandBrakeWPF.Services.Queue.Model; using HandBrakeWPF.ViewModels.Interfaces; using Microsoft.Win32; using EncodeCompletedEventArgs = HandBrakeWPF.Services.Encode.EventArgs.EncodeCompletedEventArgs; using EncodeProgressEventArgs = HandBrakeWPF.Services.Encode.EventArgs.EncodeProgressEventArgs; using EncodeTask = HandBrakeWPF.Services.Encode.Model.EncodeTask; /// /// The Preview View Model /// public class QueueViewModel : ViewModelBase, IQueueViewModel { #region Constants and Fields private readonly IErrorService errorService; private readonly IUserSettingService userSettingService; private readonly IQueueProcessor queueProcessor; private bool isEncoding; private string jobStatus; private string jobsPending; private string whenDoneAction; private bool isQueueRunning; #endregion #region Constructors and Destructors /// /// Initializes a new instance of the class. /// /// /// The user Setting Service. /// /// /// The Queue Processor Service /// /// /// The Error Service /// public QueueViewModel(IUserSettingService userSettingService, IQueueProcessor queueProcessor, IErrorService errorService) { this.userSettingService = userSettingService; this.queueProcessor = queueProcessor; this.errorService = errorService; this.Title = Resources.QueueViewModel_Queue; this.JobsPending = Resources.QueueViewModel_NoEncodesPending; this.JobStatus = Resources.QueueViewModel_NoJobsPending; this.SelectedItems = new BindingList(); this.DisplayName = "Queue"; this.IsQueueRunning = false; this.WhenDoneAction = this.userSettingService.GetUserSetting(UserSettingConstants.WhenCompleteAction); } #endregion #region Properties /// /// Gets or sets a value indicating whether the Queue is paused or not.. /// public bool IsQueueRunning { get { return this.isQueueRunning; } set { if (value == this.isQueueRunning) return; this.isQueueRunning = value; this.NotifyOfPropertyChange(() => this.IsQueueRunning); } } /// /// Gets or sets JobStatus. /// public string JobStatus { get { return this.jobStatus; } set { this.jobStatus = value; this.NotifyOfPropertyChange(() => this.JobStatus); } } /// /// Gets or sets JobsPending. /// public string JobsPending { get { return this.jobsPending; } set { this.jobsPending = value; this.NotifyOfPropertyChange(() => this.JobsPending); } } /// /// Gets or sets WhenDoneAction. /// public string WhenDoneAction { get { return this.whenDoneAction; } set { this.whenDoneAction = value; this.NotifyOfPropertyChange(() => this.WhenDoneAction); } } /// /// Gets the queue tasks. /// public BindingList QueueTasks { get { return this.queueProcessor.Queue; } } /// /// Gets or sets the selected items. /// public BindingList SelectedItems { get; set; } #endregion #region Public Methods /// /// Update the When Done Setting /// /// /// The action. /// public void WhenDone(string action) { this.WhenDone(action, true); } /// /// Update the When Done Setting /// /// /// The action. /// /// /// Save the change to the setting. Use false when updating UI. /// public void WhenDone(string action, bool saveChange) { this.WhenDoneAction = action; if (saveChange) { this.userSettingService.SetUserSetting(UserSettingConstants.WhenCompleteAction, action); } IOptionsViewModel ovm = IoC.Get(); ovm.UpdateSettings(); } /// /// Clear the Queue /// public void Clear() { MessageBoxResult result = this.errorService.ShowMessageBox( Resources.QueueViewModel_ClearQueueConfrimation, Resources.Confirm, MessageBoxButton.YesNo, MessageBoxImage.Warning); if (result == MessageBoxResult.Yes) { this.queueProcessor.Clear(); } } /// /// Clear Completed Items /// public void ClearCompleted() { this.queueProcessor.ClearCompleted(); } /// /// Close this window. /// public void Close() { this.TryClose(); } /// /// Handle the On Window Load /// public override void OnLoad() { // Setup the window to the correct state. this.IsQueueRunning = this.queueProcessor.EncodeService.IsEncoding; this.JobsPending = string.Format(Resources.QueueViewModel_JobsPending, this.queueProcessor.Count); base.OnLoad(); } /// /// Can Pause the Queue. /// Used by Caliburn Micro to enable/disable the context menu item. /// /// /// True when we can pause the queue. /// public bool CanPauseQueue() { return this.IsQueueRunning; } /// /// Pause the Queue /// public void PauseQueue() { this.queueProcessor.Pause(); this.JobStatus = Resources.QueueViewModel_QueuePending; this.JobsPending = string.Format(Resources.QueueViewModel_JobsPending, this.queueProcessor.Count); this.IsQueueRunning = false; MessageBox.Show(Resources.QueueViewModel_QueuePauseNotice, Resources.QueueViewModel_Queue, MessageBoxButton.OK, MessageBoxImage.Information); } /// /// Pause the Queue /// /// /// Prevents evaluation of CanPauseQueue /// public void PauseQueueToolbar() { this.PauseQueue(); } /// /// The remove selected jobs. /// public void RemoveSelectedJobs() { MessageBoxResult result = this.errorService.ShowMessageBox( Resources.QueueViewModel_DelSelectedJobConfirmation, Resources.Question, MessageBoxButton.YesNo, MessageBoxImage.Question); if (result == MessageBoxResult.No) { return; } List tasksToRemove = this.SelectedItems.ToList(); foreach (QueueTask job in tasksToRemove) { this.RemoveJob(job); } } /// /// Remove a Job from the queue /// /// /// The Job to remove from the queue /// public void RemoveJob(object queueTask) { QueueTask task = queueTask as QueueTask; if (task == null) { return; } if (task.Status == QueueItemStatus.InProgress) { MessageBoxResult result = this.errorService.ShowMessageBox( Resources.QueueViewModel_JobCurrentlyRunningWarning, Resources.Warning, MessageBoxButton.YesNo, MessageBoxImage.Question); if (result == MessageBoxResult.Yes) { this.queueProcessor.EncodeService.Stop(); this.queueProcessor.Remove(task); } } else { this.queueProcessor.Remove(task); } } /// /// Reset the job state to waiting. /// /// /// The task. /// public void RetryJob(QueueTask task) { task.Status = QueueItemStatus.Waiting; this.queueProcessor.BackupQueue(null); this.JobsPending = string.Format(Resources.QueueViewModel_JobsPending, this.queueProcessor.Count); } /// /// Can Start Encoding. /// Used by Caliburn Micro to enable/disable the context menu item. /// /// /// True when we can start encoding. /// public bool CanStartQueue() { return !this.IsQueueRunning; } /// /// Start Encode /// public void StartQueue() { if (this.queueProcessor.Count == 0) { this.errorService.ShowMessageBox( Resources.QueueViewModel_NoPendingJobs, Resources.Error, MessageBoxButton.OK, MessageBoxImage.Error); return; } this.JobStatus = Resources.QueueViewModel_QueueStarted; this.JobsPending = string.Format(Resources.QueueViewModel_JobsPending, this.queueProcessor.Count); this.IsQueueRunning = true; this.queueProcessor.Start(userSettingService.GetUserSetting(UserSettingConstants.ClearCompletedFromQueue)); } /// /// Export the Queue to a file. /// public void Export() { SaveFileDialog dialog = new SaveFileDialog { Filter = "Legacy Queue Files (*.hbq)|*.hbq|Json for CLI (*.json)|*.json", OverwritePrompt = true, DefaultExt = ".hbq", AddExtension = true }; if (dialog.ShowDialog() == true) { if (Path.GetExtension(dialog.FileName).ToLower().Trim() == ".json") { this.queueProcessor.ExportJson(dialog.FileName); } else { this.queueProcessor.BackupQueue(dialog.FileName); } } } /// /// Import a saved queue /// public void Import() { OpenFileDialog dialog = new OpenFileDialog { Filter = "Legacy Queue Files (*.hbq)|*.hbq", CheckFileExists = true }; if (dialog.ShowDialog() == true) { this.queueProcessor.RestoreQueue(dialog.FileName); } } /// /// Edit this Job /// /// /// The task. /// public void EditJob(QueueTask task) { MessageBoxResult result = this.errorService.ShowMessageBox( Resources.QueueViewModel_EditConfrimation, "Modify Job?", MessageBoxButton.YesNo, MessageBoxImage.Question); if (result != MessageBoxResult.Yes) { return; } // Remove the job if it is not already encoding. Let the user decide if they want to cancel or not. this.RemoveJob(task); // Pass a copy of the job back to the Main Screen IMainViewModel mvm = IoC.Get(); mvm.EditQueueJob(new EncodeTask(task.Task)); } #endregion #region Methods public void Activate() { this.queueProcessor.QueueCompleted += this.queueProcessor_QueueCompleted; this.queueProcessor.QueueChanged += this.QueueManager_QueueChanged; this.queueProcessor.JobProcessingStarted += this.QueueProcessorJobProcessingStarted; } public void Deactivate() { this.queueProcessor.QueueCompleted -= this.queueProcessor_QueueCompleted; this.queueProcessor.QueueChanged -= this.QueueManager_QueueChanged; this.queueProcessor.JobProcessingStarted -= this.QueueProcessorJobProcessingStarted; } /// /// Override the OnActive to run the Screen Loading code in the view model base. /// protected override void OnActivate() { this.Load(); this.queueProcessor.QueueCompleted += this.queueProcessor_QueueCompleted; this.queueProcessor.QueueChanged += this.QueueManager_QueueChanged; this.queueProcessor.EncodeService.EncodeStatusChanged += this.EncodeService_EncodeStatusChanged; this.queueProcessor.EncodeService.EncodeCompleted += this.EncodeService_EncodeCompleted; this.queueProcessor.JobProcessingStarted += this.QueueProcessorJobProcessingStarted; this.queueProcessor.LowDiskspaceDetected += this.QueueProcessor_LowDiskspaceDetected; this.JobsPending = string.Format(Resources.QueueViewModel_JobsPending, this.queueProcessor.Count); this.JobStatus = Resources.QueueViewModel_QueueReady; base.OnActivate(); } /// /// Override the Deactivate /// /// /// The close. /// protected override void OnDeactivate(bool close) { this.queueProcessor.QueueCompleted -= this.queueProcessor_QueueCompleted; this.queueProcessor.QueueChanged -= this.QueueManager_QueueChanged; this.queueProcessor.EncodeService.EncodeStatusChanged -= this.EncodeService_EncodeStatusChanged; this.queueProcessor.EncodeService.EncodeCompleted -= this.EncodeService_EncodeCompleted; this.queueProcessor.JobProcessingStarted -= this.QueueProcessorJobProcessingStarted; this.queueProcessor.LowDiskspaceDetected -= this.QueueProcessor_LowDiskspaceDetected; base.OnDeactivate(close); } /// /// Handle the Encode Status Changed Event. /// /// /// The sender. /// /// /// The EncodeProgressEventArgs. /// private void EncodeService_EncodeStatusChanged(object sender, EncodeProgressEventArgs e) { Execute.OnUIThread(() => { this.JobStatus = string.Format( Resources.QueueViewModel_QueueStatusDisplay, e.Task, e.TaskCount, e.PercentComplete, e.CurrentFrameRate, e.AverageFrameRate, e.EstimatedTimeLeft, e.ElapsedTime); }); } /// /// Detect Low Disk Space before starting new queue tasks. /// /// Event invoker. /// Event Args. private void QueueProcessor_LowDiskspaceDetected(object sender, EventArgs e) { Execute.OnUIThreadAsync( () => { this.queueProcessor.Pause(); this.JobStatus = Resources.QueueViewModel_QueuePending; this.JobsPending = string.Format(Resources.QueueViewModel_JobsPending, this.queueProcessor.Count); this.IsQueueRunning = false; this.errorService.ShowMessageBox( Resources.MainViewModel_LowDiskSpaceWarning, Resources.MainViewModel_LowDiskSpace, MessageBoxButton.OK, MessageBoxImage.Warning); }); } /// /// Handle the Queue Changed Event. /// /// /// The sender. /// /// /// The e. /// private void QueueManager_QueueChanged(object sender, EventArgs e) { this.JobsPending = string.Format(Resources.QueueViewModel_JobsPending, this.queueProcessor.Count); if (!queueProcessor.IsProcessing) { this.JobStatus = Resources.QueueViewModel_QueueNotRunning; this.IsQueueRunning = false; } } /// /// Handle the Queue Completed Event /// /// /// The sender. /// /// /// The EventArgs. /// private void queueProcessor_QueueCompleted(object sender, EventArgs e) { this.JobStatus = Resources.QueueViewModel_QueueCompleted; this.JobsPending = string.Format(Resources.QueueViewModel_JobsPending, this.queueProcessor.Count); this.IsQueueRunning = false; } /// /// The encode service_ encode completed. /// /// /// The sender. /// /// /// The e. /// private void EncodeService_EncodeCompleted(object sender, EncodeCompletedEventArgs e) { if (!this.queueProcessor.IsProcessing) { this.JobStatus = Resources.QueueViewModel_LastJobFinished; } } /// /// The queue processor job processing started. /// /// /// The sender. /// /// /// The QueueProgressEventArgs. /// private void QueueProcessorJobProcessingStarted(object sender, QueueProgressEventArgs e) { this.JobStatus = Resources.QueueViewModel_QueueStarted; this.JobsPending = string.Format(Resources.QueueViewModel_JobsPending, this.queueProcessor.Count); this.IsQueueRunning = true; } #endregion } }