JD2022-TU1/main/tools/framework/JD.Application.TeaBox/View/TrackItems/AnnotationTrackItem.cs

295 lines
8.9 KiB
C#

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using JD.Application.View.Misc;
using JD.Application.View.PropertyGrid;
using JD.Application.View.Timeline;
using JD.Application.View.WindowLess;
using JD.Audio.Annotations;
using JD.Audio.Sound;
using JD.Kernel.Interfaces;
using JD.Kernel.StaticHelpers;
using JD.Sequencers.Times;
using JD.Sequencers.Windowlesses;
using TeaBox;
namespace JD.Application.View.TrackItems
{
/// <summary>
/// A visual representation of an annotation in a track.
/// </summary>
public class AnnotationTrackItem : LocalizedTrackItem, ICloneable<WindowlessControl>
{
#region Constants
private const int MaxTextPixelWidth = 140;
private const int PaddingImageText = 0;
private const int LabelPadding = 2; // Padding between the label and the text.
private static readonly SolidBrush _labelBrush = new SolidBrush(Color.FromArgb(100, Color.Black));
private static readonly SolidBrush _selectedLabelBrush = new SolidBrush(Color.FromArgb(175, Color.WhiteSmoke));
private static readonly SolidBrush _textBrush = new SolidBrush(Color.WhiteSmoke);
private static readonly SolidBrush _selectedTextBrush = new SolidBrush(Color.FromArgb(50, 50, 50));
private static readonly StringFormat _stringFormat = new StringFormat() { Trimming = StringTrimming.EllipsisCharacter };
private static readonly Font _font = new Font(FontFamily.GenericSansSerif, 8f);
#endregion
#region Variables
private readonly global::JD.Sequencers.Times.Timeline _timeline;
private readonly EditableAnnotation _model;
private readonly Size _imageSize;
private readonly Image _image;
#endregion
#region Constructor
protected AnnotationTrackItem(EditableAnnotation model, global::JD.Sequencers.Times.Timeline timeline, Image image)
{
Throw.IfNull(image, "image");
_timeline = timeline;
_model = model;
IsMoveAllowed = true;
Time = model.Time;
_image = image;
_imageSize = _image.Size;
}
#endregion
#region Properties
/// <summary>
/// The collection containing the list of model (allows to add/remove items).
/// </summary>
public IAnnotationList ParentCollection { get; set; }
public EditableAnnotation Model
{
get { return _model; }
}
public new BeatUnit Time
{
get { return _timeline.BeatFromTimeUnit(base.Time); }
set { base.Time = _timeline.TimeUnitFromBeat(value); }
}
public virtual SnappingUnit SnappingUnit
{
get { return EditionOptions.CurrentSnappingUnit; }
}
public override string ToolTipText
{
get
{
if (Timeline == null)
{
// Happens when the control has been removed.
return string.Empty;
}
var wave = Timeline.Wave();
var beat = wave.Annotations.Markers[Time];
var time = beat.Time;
string measure = string.Concat(beat.MeasurePosition, ".", beat.PositionInMeasure);
string comment = Model.Comment;
if (!string.IsNullOrEmpty(comment))
{
comment = "\n" + comment;
}
return $"{measure}\n{time.Minutes:00}m {time.Seconds:00}s{comment}";
}
}
public override string Text
{
get { return Model.ToString(); }
}
public override bool SupportAutoLoop
{
get { return true; }
}
#endregion
#region Layout
private Size ImageSize
{
get { return _imageSize; }
}
public int RowHeight
{
get { return ImageSize.Height; }
}
/// <summary>
/// Bounds relative to parent track bottom.
/// </summary>
public Rectangle RelativeBounds { get; set; }
public bool ShowLabel
{
get; set;
}
public void UpdatePosition(Graphics graphics, List<Rectangle> unavailableSpace)
{
var rowHeight = RowHeight;
var relativeBounds = new Rectangle
{
Width = ImageSize.Width + PaddingImageText + (int)graphics.MeasureString(Text, _font, MaxTextPixelWidth).Width + LabelPadding * 2,
Height = rowHeight,
X = Timeline.TimeToPixel(Time) - ImageSize.Width / 2,
Y = -RowHeight,
};
while (unavailableSpace.Any(rectangle => rectangle.Contains(relativeBounds.Location)))
{
relativeBounds.Y -= rowHeight + 1;
}
RelativeBounds = relativeBounds;
}
#endregion
#region Painting / Layout
protected override void OnPaintSelf(PaintEventArgs e)
{
var graphics = e.Graphics;
var text = Text;
var drawBounds = DrawMarker(_image, graphics);
if (!string.IsNullOrEmpty(text) && ShowLabel)
{
drawBounds = Rectangle.Union(drawBounds, DrawText(text, graphics));
}
if (Selected)
{
ControlPaint.DrawFocusRectangle(graphics, drawBounds);
}
}
private Rectangle DrawText(string text, Graphics graphics)
{
// Draw label.
Rectangle labelBounds = Bounds;
int offset = ImageSize.Width + PaddingImageText;
labelBounds.X += offset;
labelBounds.Width -= offset;
graphics.FillRectangle(Selected ? _selectedLabelBrush : _labelBrush, labelBounds);
// Draw text.
var textBounds = labelBounds;
textBounds.Height = _font.Height;
textBounds.X++;
textBounds.Y++; // Offset the text otherwise it would appear too close with the edge
graphics.DrawString(text, _font, Selected ? _selectedTextBrush : _textBrush, textBounds, _stringFormat);
return labelBounds;
}
protected Rectangle DrawMarker(Image image, Graphics graphics)
{
var rect = new Rectangle(Bounds.Location, image.Size);
graphics.DrawImage(image, rect);
return rect;
}
/// <summary>
/// Sets the same style as the current one to the text box.
/// </summary>
/// <param name="textBox"></param>
public void CloneDisplayStyle(TextBox textBox)
{
if (Selected)
{
textBox.BackColor = _selectedLabelBrush.Color;
textBox.ForeColor = _selectedTextBrush.Color;
}
else
{
textBox.BackColor = _labelBrush.Color;
textBox.ForeColor = _textBrush.Color;
}
textBox.Font = _font;
}
#endregion
#region Edition
/// <summary>
/// Returns a property grid representation of the current model.
/// </summary>
public virtual object ToGridView(Wave parentWave)
{
return new AnnotationGridView(Model, parentWave);
}
/// <summary>
/// Call the remove method so the remove handle can remove the corresponding object.
/// </summary>
public void Remove()
{
ParentCollection.Remove(Model);
}
/// <summary>
/// Update internal model when the track item is moved.
/// </summary>
protected override void OnTimeChanged(System.EventArgs e)
{
base.OnTimeChanged(e);
Model.Time = Time;
}
#endregion
#region Snap
public override void Move(float delta)
{
base.Time += delta;
}
public override void Snap_Self(float originalDelta, ref float snappedDelta, ref float diff)
{
BeatUnit newValue = Timeline.Wave().UnitConverter.Snap(new BeatUnit(Time.Value + originalDelta), SnappingUnit);
snappedDelta = newValue.Value - Time.Value;
}
#endregion
#region ICloneable
/// <summary>
/// Implements the clone on drag.
/// </summary>
WindowlessControl ICloneable<WindowlessControl>.Clone()
{
var clone = Model.Clone();
ParentCollection.Add(clone);
return Track.TrackView.ControlsRecursive().OfType<AnnotationTrackItem>().First(control => control.Model == clone);
}
#endregion
}
}