Aegisub/SSATool/Line.cs

306 lines
12 KiB
C#

/*
SSATool - A collection of utilities for Advanced Substation Alpha
Copyright (C) 2007 Dan Donovan
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; ONLY under version 2
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
namespace SSATool {
public enum LineType {
dialogue,
comment,
style,
other,
error
}
public class DialogueLine : ICloneable {
public TimeSpan start, end;
public Style style;
public string actor, effect;
public string text;
public int marginl, marginr, margint, marginb;
public int layer;
public bool marked;
public override string ToString() {
string retStr = String.Format("Dialogue: {0},{1},{2},{3},{4},{5:D4},{6:D4},{7:D4},{8}{9},{10}",
(Form1.sver==4)?("Marked="+(this.marked?"1":"0")):this.layer.ToString(Util.nfi),
Util.TimeSpanSSA(this.start,false,1),
Util.TimeSpanSSA(this.end,true,1),
this.style.name, this.actor,
this.marginl, this.marginr, this.margint,
((Form1.sver == 6) ? this.marginb.ToString(Util.nfi).PadLeft(4, '0') + "," : String.Empty),
this.effect,this.text);
return retStr;
}
public Object Clone() {
return this.MemberwiseClone(); // A shallow memberwise-clone is fine
}
/// <summary>
/// Return just the text of the line without any SSA
/// </summary>
/// <returns></returns>
public string JustText {
get {
string justtext = string.Empty;
char[] linechar = this.text.ToCharArray();
bool inCode = false;
for (int charindex = 0; charindex != this.text.Length; charindex+=1) {
if (linechar[charindex] == '{') inCode = true;
else if (linechar[charindex] == '}') inCode = false;
else if (!inCode) justtext = justtext + linechar[charindex];
}
return justtext;
}
}
/// <summary>
/// Return the margin of this line, taking overrides into account
/// </summary>
/// <returns>The margin as a rectangle</returns>
public RectangleF GetMargin() {
int Marginl = (this.marginl==0)?this.style.marginl:this.marginl;
int Margint = (this.margint==0)?this.style.margint:this.margint;
int Marginb = (this.marginb==0)?this.style.marginb:this.marginb;
int Marginr = (this.marginr==0)?this.style.marginr:this.marginr;
int Height = Form1.ResY - (Margint+Marginb);
int Width = Form1.ResX - (Marginl+Marginr);
return new RectangleF(Marginl, Margint, Width, Height);
}
/// <summary>
/// Find the bounding rectangle of an area of the line
/// </summary>
/// <param name="startIndex"></param>
/// <param name="length"></param>
/// <returns>The bounding rectangle</returns>
public RectangleF GetRect(int startIndex, int length) {
Graphics graphics = Form1.listSSAg;
StringFormat format = new System.Drawing.StringFormat();
CharacterRange[] ranges = { new CharacterRange(startIndex, length) };
Region[] regions = new Region[1];
RectangleF margin, rect;
margin = this.GetMargin();
format.SetMeasurableCharacterRanges(ranges);
int align = this.style.scrAlignment;
switch (align&3) {
case 1:
format.LineAlignment = StringAlignment.Near;
break;
case 2:
format.LineAlignment = StringAlignment.Center;
break;
default:
format.LineAlignment = StringAlignment.Far;
break;
}
switch (align&12) {
case 0:
format.Alignment = StringAlignment.Far;
break;
case 4:
format.Alignment = StringAlignment.Near;
break;
default:
format.Alignment = StringAlignment.Center;
break;
}
regions = graphics.MeasureCharacterRanges(this.JustText, this.style.GetFont(), margin, format);
rect = regions[0].GetBounds(graphics);
rect = new RectangleF(margin.X+rect.X, margin.Y+rect.Y, rect.Width, rect.Height);
return rect;
}
public static DialogueLine ParseLine(string theLine, LineType lt) {
try {
DialogueLine retLine = new DialogueLine();
char[] linechar = theLine.ToCharArray();
string buff = String.Empty;
int pos = 0;
bool neg = false;
for (int index = (Form1.sver == 4) ? 17 : 9; index != linechar.Length; index+=1) {
if ((pos<9||pos==9&&Form1.sver==6) && linechar[index].Equals(',')) {
switch (pos) {
case 0:
if (Form1.sver == 4) retLine.marked = linechar[index - 1].Equals('1');
else retLine.layer = int.Parse(buff, Util.cfi);
break;
case 1:
if (neg) retLine.start = TimeSpan.Parse(buff.Replace("-", String.Empty)).Negate();
else retLine.start = TimeSpan.Parse(buff);
break;
case 2:
if (neg) retLine.end = TimeSpan.Parse(buff.Replace("-", String.Empty)).Negate();
else retLine.end = TimeSpan.Parse(buff);
break;
case 3:
retLine.style = Form1.styleColl.Find(delegate(Style s) { return s.Equals(buff); });
if (retLine.style == null) retLine.style = new Style(buff);
break;
case 4:
retLine.actor = buff;
break;
case 5:
retLine.marginl = int.Parse(buff, Util.cfi);
break;
case 6:
retLine.marginr = int.Parse(buff, Util.cfi);
break;
case 7:
retLine.margint = int.Parse(buff, Util.cfi);
break;
case 8:
if (Form1.sver < 6) retLine.effect = buff;
else retLine.marginb = int.Parse(buff, Util.cfi);
break;
case 9:
retLine.effect = buff;
break;
}
pos += 1;
buff = string.Empty;
neg = false;
}
else if ((pos != 0 || char.IsDigit(linechar[index])) && (pos == 3 || pos == 4 || pos >= 8 || !char.IsWhiteSpace(linechar[index]))) {
buff += linechar[index];
neg = neg || (linechar[index] == '-');
}
}
retLine.text = buff;
return retLine;
} catch {
throw new FormatException();
}
}
public DialogueLine() { }
public DialogueLine(TimeSpan StartTime, TimeSpan EndTime, Style Style) {
start = StartTime;
end = EndTime;
style = Style;
}
public DialogueLine(string Text) {
text = Text;
}
public DialogueLine(Style Style, string Text) : this(Text) {
style = Style;
}
}
public class Line : IEquatable<Line>, ICloneable {
public LineType lineType;
public object line;
public bool enabled, selected;
public Line() {
this.enabled = true;
}
public static LineType getLineType(string theLine) {
if (theLine.Length > 20 && String.Compare(theLine.Substring(0, 9), "dialogue:", true, Util.cfi) == 0)
return LineType.dialogue;
else if (theLine.Length > 8 && String.Compare(theLine.Substring(0, 8), "comment:", true, Util.cfi) == 0)
return LineType.comment;
else if (theLine.Length > 20 && String.Compare(theLine.Substring(0, 6), "style:", true, Util.cfi) == 0)
return LineType.style;
else
return LineType.other;
}
public static Line ParseLine(string inStr) {
Line l = new Line();
l.lineType = getLineType(inStr);
switch (l.lineType) {
case LineType.dialogue:
try {
l.line = DialogueLine.ParseLine(inStr, LineType.dialogue);
} catch {
l.lineType = LineType.error;
l.line = inStr;
}
break;
case LineType.style:
try {
Style s = Style.ParseStyle(inStr);
l.line = s;
Form1.styleColl.Add(s);
} catch {
l.lineType = LineType.error;
l.line = inStr;
}
break;
default:
if (inStr.Length > 8 && String.Compare(inStr.Substring(0, 7), "playres", true, Util.cfi) == 0) {
switch (inStr.ToCharArray()[7]) {
case 'x':
case 'X':
Form1.ResX = int.Parse(inStr.Substring(9, inStr.Length - 9), Util.cfi);
break;
case 'y':
case 'Y':
Form1.ResY = int.Parse(inStr.Substring(9, inStr.Length - 9), Util.cfi);
break;
}
}
else if (inStr.Length > 10 && String.Compare(inStr.Substring(0, 10), "scripttype", true, Util.cfi) == 0) {
//TODO: Improve this detection
inStr = inStr.TrimEnd();
if (inStr.EndsWith("+=1")) Form1.sver = 6;
else if (inStr.EndsWith("+")) Form1.sver = 5;
else Form1.sver = 4;
}
l.line = inStr;
break;
}
return l;
}
bool System.IEquatable<Line>.Equals(Line line) {
return this.line==line.line;
}
public override int GetHashCode() {
return base.GetHashCode();
}
public Object Clone() {
Line retLine = new Line();
retLine.enabled = this.enabled;
retLine.lineType = this.lineType;
if (this.lineType == LineType.dialogue)
retLine.line = ((DialogueLine)line).Clone();
else if (this.lineType == LineType.style)
retLine.line = ((Style)line).Clone();
else
retLine.line = this.line;
return retLine;
}
public override string ToString() {
return line.ToString();
}
}
}