295 lines
8.9 KiB
C#
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
|
|
}
|
|
}
|