/* 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 } /// /// Return just the text of the line without any SSA /// /// 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; } } /// /// Return the margin of this line, taking overrides into account /// /// The margin as a rectangle 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); } /// /// Find the bounding rectangle of an area of the line /// /// /// /// The bounding rectangle 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, 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.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(); } } }