347 lines
9.8 KiB
C#
347 lines
9.8 KiB
C#
using System.Linq;
|
|
using System.Windows.Forms;
|
|
using JD.Timeline.Model;
|
|
using JD.Timeline.Model.Utility;
|
|
using JD.Timeline.Plugin;
|
|
using JD.Timeline.View.ClipView.Widgets;
|
|
using TeaBox;
|
|
using TeaBox.Actions;
|
|
using TeaBox.Gui;
|
|
using TeaBox.ObjectModel;
|
|
using TeaBox.ObjectModel.Pads;
|
|
using TeaBox.ObjectModel.Transactions;
|
|
using JD.Core;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using JD.Application.Model.UndoRedo;
|
|
using JD.Application.Utils;
|
|
using JD.Application.View.PropertyGrid;
|
|
using JD.Kernel.StaticHelpers;
|
|
using JD.Kernel.Structs;
|
|
using JD.LogService;
|
|
using JD.MessageService;
|
|
using JD.Sequencers.Windowlesses;
|
|
using JD.Serialization;
|
|
using Path = System.IO.Path;
|
|
using PropertyChangedEventArgs = System.ComponentModel.PropertyChangedEventArgs;
|
|
|
|
namespace JD.Timeline.Document
|
|
{
|
|
/// <summary>
|
|
/// Track document maps to a single wave with its annotations. A view can be open to allow the edition of the document.
|
|
/// </summary>
|
|
public class TapeDocument : TeaBox.ObjectModel.Document, IGridEditableDocument, IUndoableContent, IDelegatedActionRouter, IDisposable
|
|
{
|
|
#region Variables
|
|
|
|
private Tape _tape;
|
|
private UndoHistory _undoHistory;
|
|
private TapeUndoer _undoer;
|
|
private readonly CommandStack _commandStack = new CommandStack();
|
|
private readonly OperationTaskCollector _taskCollector = new OperationTaskCollector();
|
|
|
|
/// <summary>
|
|
/// Depending if the document was load from a file or from the plugin.
|
|
/// </summary>
|
|
protected bool _fromFile;
|
|
|
|
/// <summary>
|
|
/// Depending if the document was imported from a tpl or open from a tape directly.
|
|
/// </summary>
|
|
protected bool _isImportedFromTpl;
|
|
|
|
#endregion
|
|
|
|
#region Constructor
|
|
|
|
public TapeDocument(DocumentCreationContext context, Tape tape, bool fromFile = false)
|
|
: base(context)
|
|
{
|
|
Tape = tape;
|
|
if (Tape != null && FileUtility.IsValidPath(Tape.ToString()))
|
|
{
|
|
FileName = DataRoot.ToAbsolutePath(Tape.ToString());
|
|
}
|
|
UpdateTitle();
|
|
IsDirty = false;
|
|
_fromFile = fromFile;
|
|
}
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
Tape = null;
|
|
base.Dispose(disposing);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Properties
|
|
|
|
public Tape Tape
|
|
{
|
|
get { return _tape; }
|
|
protected set
|
|
{
|
|
if (_tape != null)
|
|
{
|
|
_tape.PropertyChanged -= Tape_PropertyChanged;
|
|
_undoHistory.Dispose();
|
|
}
|
|
|
|
_tape = value;
|
|
|
|
if (_tape != null)
|
|
{
|
|
_tape.PropertyChanged += Tape_PropertyChanged;
|
|
_undoer = new TapeUndoer(_tape, _commandStack);
|
|
_undoHistory = new UndoHistory(FileName, _undoer);
|
|
}
|
|
}
|
|
}
|
|
|
|
public OperationTaskCollector TaskCollector
|
|
{
|
|
get
|
|
{
|
|
return _taskCollector;
|
|
}
|
|
}
|
|
|
|
public bool IsLoadedFromFile
|
|
{
|
|
get
|
|
{
|
|
return _fromFile;
|
|
}
|
|
}
|
|
|
|
public bool IsImportedFromTpl
|
|
{
|
|
get
|
|
{
|
|
return _isImportedFromTpl;
|
|
}
|
|
}
|
|
|
|
public delegate void ValidationFunc(Tape tape, ILogger logger);
|
|
public event ValidationFunc Validation;
|
|
|
|
#endregion
|
|
|
|
#region Methods
|
|
|
|
private void UpdateTitle()
|
|
{
|
|
TitleName = GetTitleNameImpl();
|
|
}
|
|
|
|
protected override string GetTitleNameImpl()
|
|
{
|
|
string name = _tape == null ? "" : _tape.ToString().NonEmpty("No name");
|
|
|
|
if (FileUtility.IsValidPath(name))
|
|
{
|
|
name = Path.GetFileNameWithoutExtension(name);
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
|
|
public override bool DirectSave()
|
|
{
|
|
if (IsDirty && !_fromFile)
|
|
{
|
|
if (!ValidateTape())
|
|
return false;
|
|
|
|
TimelinePlugin.Instance?.SendSaveTape(_tape.Id);
|
|
IsDirty = false;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
protected override void LoadImpl(string filename, DocumentLoadingContext context, bool isReloading)
|
|
{
|
|
base.LoadImpl(filename, context, isReloading);
|
|
|
|
_fromFile = true;
|
|
string extension = Path.GetExtension(filename) ?? "";
|
|
_isImportedFromTpl = extension.Equals(FileExtensions.Template, StringComparison.OrdinalIgnoreCase);
|
|
|
|
Tape = Tape.CreateTape(filename);
|
|
Tape.IsOffline = true;
|
|
Tape.UpdateTapeReferences();
|
|
}
|
|
|
|
protected override bool SaveImpl(string filePath, Flags<SaveContext> context)
|
|
{
|
|
base.SaveImpl(filePath, context);
|
|
|
|
if (!ValidateTape())
|
|
return false;
|
|
|
|
|
|
if (_isImportedFromTpl && File.Exists(filePath))
|
|
{
|
|
_isImportedFromTpl = false;
|
|
|
|
var dialog = new MessageDialog
|
|
{
|
|
MainIcon = MessageDialogIcon.Warning,
|
|
MainMessage = $"File '{Path.GetFileName(filePath)}' already exists",
|
|
Content = "Tpl files need to be converted in tape files in order to open them in DanceEditor."
|
|
};
|
|
|
|
dialog.AddButton("Overwrite existing file");
|
|
var rename = dialog.AddButton("Save to different location");
|
|
var cancel = dialog.AddCommonButton(MessageDialogCommonButton.Cancel);
|
|
|
|
var result = dialog.ShowDialog();
|
|
if (result == rename)
|
|
{
|
|
var dlgSaveFileDialog = new SaveFileDialog { FileName = filePath, Filter = "Dance Tape files (*.dtape)|*.dtape" };
|
|
if (dlgSaveFileDialog.ShowDialog() == DialogResult.OK)
|
|
{
|
|
filePath = dlgSaveFileDialog.FileName;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
if (result == cancel)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (_fromFile)
|
|
{
|
|
Tape.Save(filePath);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private bool ValidateTape()
|
|
{
|
|
LoggingContext loggingContext;
|
|
using (_taskCollector.OperationScope(out loggingContext))
|
|
{
|
|
Validation?.Invoke(Tape, loggingContext);
|
|
|
|
Tape.SanitizeClipIds(loggingContext);
|
|
}
|
|
|
|
if (loggingContext.Errors.Any() || loggingContext.Warnings.Any())
|
|
{
|
|
TeaBox.ObjectModel.Application.Main.Dispatcher.DispatchAsync(() => TeaBox.ObjectModel.Application.Main.SelectPad(typeof(ErrorListPad), false));
|
|
}
|
|
|
|
if (loggingContext.Errors.Any())
|
|
{
|
|
const string error = "Current tape contains invalid data and cannot be saved in this state.\n\n" +
|
|
"Consult the error list for more details";
|
|
MessageService.MessageService.ShowError(error);
|
|
return false;
|
|
}
|
|
|
|
if (loggingContext.Warnings.Any())
|
|
{
|
|
var dialog = new MessageDialog
|
|
{
|
|
MainIcon = MessageDialogIcon.Warning,
|
|
MainMessage = "Current tape contains suspicious data (warnings). We suggest to fix the data prior of saving."
|
|
};
|
|
|
|
var save = dialog.AddButton("Save anyway");
|
|
dialog.AddCommonButton(MessageDialogCommonButton.Cancel);
|
|
|
|
var result = dialog.ShowDialog();
|
|
return (result == save);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Handlers
|
|
|
|
private void Tape_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
|
{
|
|
UpdateTitle();
|
|
if (!_tape.IsComputedProperty(e.PropertyName))
|
|
{
|
|
OnChanged(e);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IGridEditableDocument
|
|
|
|
object IGridEditableDocument.MainObject
|
|
{
|
|
get
|
|
{
|
|
if (_tape?.EditionContext != null)
|
|
{
|
|
return _tape.EditionContext;
|
|
}
|
|
return _tape;
|
|
}
|
|
}
|
|
|
|
object IGridEditableDocument.ToPropertyGrid(WindowlessControl control)
|
|
{
|
|
return (control as ClipWidget)?.Model;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Undo/Redo
|
|
|
|
int IUndoableContent.UndoCount
|
|
{
|
|
get { return _commandStack.UndoCount; }
|
|
}
|
|
|
|
int IUndoableContent.RedoCount
|
|
{
|
|
get { return _commandStack.RedoCount; }
|
|
}
|
|
|
|
void IUndoableContent.Undo(int count)
|
|
{
|
|
_commandStack.Undo(count);
|
|
}
|
|
|
|
void IUndoableContent.Redo(int count)
|
|
{
|
|
_commandStack.Redo(count);
|
|
}
|
|
|
|
#endregion
|
|
|
|
public IEnumerable<object> GetDelegatedActionTarget()
|
|
{
|
|
yield return _undoHistory;
|
|
}
|
|
|
|
public LoggingContext GetLoggingContext()
|
|
{
|
|
LoggingContext context;
|
|
_taskCollector.OperationScope(out context);
|
|
return context;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_undoHistory?.Dispose();
|
|
_undoer?.Dispose();
|
|
_taskCollector?.Dispose();
|
|
}
|
|
}
|
|
}
|