├── .vs
└── LiveSplit.Splits
│ └── v15
│ └── Server
│ └── sqlite3
│ ├── db.lock
│ └── storage.ide
├── PublicReleases
├── 1.0.0.zip
└── LiveSplit.GradedSplits.1.0.1.zip
├── UI
├── GradedExtendedGradientType.cs
├── GradedIconState.cs
├── GradedColumnType.cs
├── GradedIconsApplicationState.cs
├── Components
│ ├── GradedSplitsComponentFactory.cs
│ ├── GradedColumnSettings.cs
│ ├── GradedColumnSettings.resx
│ ├── GradedSplitsSettings.resx
│ ├── GradedLabelsComponent.cs
│ ├── GradedColumnSettings.Designer.cs
│ ├── GradedSplitsComponent.cs
│ ├── GradedSplitComponent.cs
│ └── GradedSplitsSettings.cs
└── GradedColumnData.cs
├── .gitattributes
├── TimeFormatters
├── GradedRegularSplitTimeFormatter.cs
└── GradedDeltaSplitTimeFormatter.cs
├── Properties
├── AssemblyInfo.cs
├── Resources.Designer.cs
└── Resources.resx
├── README.md
├── .gitignore
└── LiveSplit.GradedSplits.csproj
/.vs/LiveSplit.Splits/v15/Server/sqlite3/db.lock:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/PublicReleases/1.0.0.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Komarulon/LiveSplit.GradedSplits/HEAD/PublicReleases/1.0.0.zip
--------------------------------------------------------------------------------
/PublicReleases/LiveSplit.GradedSplits.1.0.1.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Komarulon/LiveSplit.GradedSplits/HEAD/PublicReleases/LiveSplit.GradedSplits.1.0.1.zip
--------------------------------------------------------------------------------
/.vs/LiveSplit.Splits/v15/Server/sqlite3/storage.ide:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Komarulon/LiveSplit.GradedSplits/HEAD/.vs/LiveSplit.Splits/v15/Server/sqlite3/storage.ide
--------------------------------------------------------------------------------
/UI/GradedExtendedGradientType.cs:
--------------------------------------------------------------------------------
1 | namespace LiveSplit.UI
2 | {
3 | public enum GradedExtendedGradientType
4 | {
5 | Plain, Vertical, Horizontal, Alternating
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/UI/GradedIconState.cs:
--------------------------------------------------------------------------------
1 | namespace LiveSplit.UI
2 | {
3 | public enum GradedIconState
4 | {
5 | Disabled,
6 | Default,
7 | PercentageSplit
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/UI/GradedColumnType.cs:
--------------------------------------------------------------------------------
1 | namespace LiveSplit.UI
2 | {
3 | public enum GradedColumnType
4 | {
5 | Delta, SplitTime, DeltaorSplitTime, SegmentDelta, SegmentTime, SegmentDeltaorSegmentTime
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/UI/GradedIconsApplicationState.cs:
--------------------------------------------------------------------------------
1 | namespace LiveSplit.UI
2 | {
3 | public enum GradedIconsApplicationState
4 | {
5 | Disabled,
6 | CurrentRun,
7 | Comparison,
8 | ComparisonAndCurrentRun
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 | *.sln merge=union
7 | *.csproj merge=union
8 | *.vbproj merge=union
9 | *.fsproj merge=union
10 | *.dbproj merge=union
11 |
12 | # Standard to msysgit
13 | *.doc diff=astextplain
14 | *.DOC diff=astextplain
15 | *.docx diff=astextplain
16 | *.DOCX diff=astextplain
17 | *.dot diff=astextplain
18 | *.DOT diff=astextplain
19 | *.pdf diff=astextplain
20 | *.PDF diff=astextplain
21 | *.rtf diff=astextplain
22 | *.RTF diff=astextplain
23 |
--------------------------------------------------------------------------------
/TimeFormatters/GradedRegularSplitTimeFormatter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace LiveSplit.TimeFormatters
4 | {
5 | public class GradedRegularSplitTimeFormatter : ITimeFormatter
6 | {
7 | public TimeAccuracy Accuracy { get; set; }
8 |
9 | public GradedRegularSplitTimeFormatter(TimeAccuracy accuracy)
10 | {
11 | Accuracy = accuracy;
12 | }
13 | public string Format(TimeSpan? time)
14 | {
15 | var formatter = new RegularTimeFormatter(Accuracy);
16 | if (time == null)
17 | return TimeFormatConstants.DASH;
18 | else
19 | return formatter.Format(time);
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/UI/Components/GradedSplitsComponentFactory.cs:
--------------------------------------------------------------------------------
1 | using LiveSplit.Model;
2 | using System;
3 |
4 | namespace LiveSplit.UI.Components
5 | {
6 | public class GradedSplitsComponentFactory : IComponentFactory
7 | {
8 | public string ComponentName => "Graded Splits";
9 |
10 | public string Description => "Displays a list of split times and deltas in relation to a comparison. Updates icon based on performance.";
11 |
12 | public ComponentCategory Category => ComponentCategory.List;
13 |
14 | public IComponent Create(LiveSplitState state) => new GradedSplitsComponent(state);
15 |
16 | public string UpdateName => ComponentName;
17 |
18 | public string XMLURL => "";
19 |
20 | public string UpdateURL => "";
21 |
22 | public Version Version => Version.Parse("1.8.11");
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/TimeFormatters/GradedDeltaSplitTimeFormatter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace LiveSplit.TimeFormatters
4 | {
5 | public class GradedDeltaSplitTimeFormatter : ITimeFormatter
6 | {
7 | public TimeAccuracy Accuracy { get; set; }
8 | public bool DropDecimals { get; set; }
9 |
10 | public GradedDeltaSplitTimeFormatter(TimeAccuracy accuracy, bool dropDecimals)
11 | {
12 | Accuracy = accuracy;
13 | DropDecimals = dropDecimals;
14 | }
15 | public string Format(TimeSpan? time)
16 | {
17 | var deltaTime = new DeltaTimeFormatter();
18 | deltaTime.Accuracy = Accuracy;
19 | deltaTime.DropDecimals = DropDecimals;
20 | var formattedTime = deltaTime.Format(time);
21 | if (time == null)
22 | return TimeFormatConstants.DASH;
23 | else
24 | return formattedTime;
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/UI/GradedColumnData.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Xml;
3 |
4 | namespace LiveSplit.UI
5 | {
6 | public class GradedColumnData
7 | {
8 | public string Name { get; set; }
9 | public GradedColumnType Type { get; set; }
10 | public string Comparison { get; set; }
11 | public string TimingMethod { get; set; }
12 |
13 | public GradedColumnData(string name, GradedColumnType type, string comparison, string method)
14 | {
15 | Name = name;
16 | Type = type;
17 | Comparison = comparison;
18 | TimingMethod = method;
19 | }
20 |
21 | public static GradedColumnData FromXml(XmlNode node)
22 | {
23 | var element = (XmlElement)node;
24 | return new GradedColumnData(element["Name"].InnerText,
25 | (GradedColumnType)Enum.Parse(typeof(GradedColumnType), element["Type"].InnerText),
26 | element["Comparison"].InnerText,
27 | element["TimingMethod"].InnerText);
28 | }
29 |
30 | public int CreateElement(XmlDocument document, XmlElement element)
31 | {
32 | return SettingsHelper.CreateSetting(document, element, "Version", "1.5") ^
33 | SettingsHelper.CreateSetting(document, element, "Name", Name) ^
34 | SettingsHelper.CreateSetting(document, element, "Type", Type) ^
35 | SettingsHelper.CreateSetting(document, element, "Comparison", Comparison) ^
36 | SettingsHelper.CreateSetting(document, element, "TimingMethod", TimingMethod);
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using LiveSplit;
2 | using LiveSplit.UI.Components;
3 | using System.Reflection;
4 | using System.Runtime.CompilerServices;
5 | using System.Runtime.InteropServices;
6 |
7 | // Allgemeine Informationen über eine Assembly werden über die folgenden
8 | // Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
9 | // die mit einer Assembly verknüpft sind.
10 | [assembly: AssemblyTitle("LiveSplit.Components.Vertical.Splits")]
11 | [assembly: AssemblyDescription("")]
12 | [assembly: AssemblyConfiguration("")]
13 | [assembly: AssemblyCompany("")]
14 | [assembly: AssemblyProduct("LiveSplit.Components.Vertical.Splits")]
15 | [assembly: AssemblyCopyright("Copyright © 2013")]
16 | [assembly: AssemblyTrademark("")]
17 | [assembly: AssemblyCulture("")]
18 |
19 | // Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar
20 | // für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von
21 | // COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest.
22 | [assembly: ComVisible(false)]
23 |
24 | // Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
25 | [assembly: Guid("6ebdfcae-e775-4c9b-bc32-922371b44d82")]
26 |
27 | // Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
28 | //
29 | // Hauptversion
30 | // Nebenversion
31 | // Buildnummer
32 | // Revision
33 | //
34 | // Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
35 | // übernehmen, indem Sie "*" eingeben:
36 | // [assembly: AssemblyVersion("1.0.*")]
37 | [assembly: AssemblyVersion("1.0.0.0")]
38 | [assembly: AssemblyFileVersion("1.0.0.0")]
39 |
40 | [assembly: ComponentFactory(typeof(GradedSplitsComponentFactory))]
--------------------------------------------------------------------------------
/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace LiveSplit.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LiveSplit.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # LiveSplit Graded Splits
2 | ## Splits List Replacement
3 |
4 | A LiveSplit Component that displays a list of split times and deltas in relation to a comparison.
5 | Automatically replaces the segment icon based on that segment's performance, compared to that segment's best.
6 | Requires LiveSplit 1.7.5.
7 |
8 | General Settings:
9 | * Don't override - Turns this feature on/off
10 | * Override current live run - Updates your split icons as you run (when you split, the icon is updated). Your current split icons remain until you split that segment.
11 | * Override personal best - Updates your split icons based on how well your personal best did against that segment's best time. Note that as you run more, and get better splits, your Personal Bests' become worse in comparision.
12 | * Override current live run AND personal best - Updates the split icons for your PB AND updates icons when you split.
13 | * Known issue - Using Best Seg/On Ahead (Gaining), etc. with "Override personal best" causes the PB to not be updated based on those criteria. For now, it's best to just stick to a % comparison if you want to have icons for your PB
14 |
15 |
16 | Split Settings:
17 | * Icon Image - Uses a local image file when overriding your normal split icon
18 | * Behind Best Seg By % - Use this icon if your segment time is less than the percentage slower your best segment. For example, if your best segment is 100s, and you specify 10%, the icon shows if you split at 110s or faster.
19 | * Don't Use - Disables this split icon, it will never override the normal split icon
20 | * Show on (Best/Ahead(Gaining)/etc.) - When you split, if the split is your Best Segment, Ahead (Gaining), etc., use this icon instead of the standard split icon. (Known issue - only overrides for the current live run)
21 |
22 | Recommended Settings:
23 | 
24 |
25 | ## Installation/Setup
26 |
27 | 1. Unzip
28 | 2. Copy LiveSplit.GradedSplits.dll into the Components folder in the LiveSplit folder.
29 | 3. Run LiveSplit.exe
30 | 4. Right-Click -> Edit Layout.
31 | 5. Click the + -> List -> Graded Splits
32 |
33 | ## Importing Settings from your Splits
34 |
35 | 1. Make a copy of your layout.
36 | 2. In your layout, add Graded Splits. KEEP the original Split component in the layout.
37 | 3. Save the layout and exit the LiveSplit.
38 | 4. With a text editor, open your layout file (MyLayoutFileName.lsl)
39 | 5. Find this section, then copy everything in that section, starting from:
40 | ```
41 |
42 | LiveSplit.Splits.dll
43 |
44 | 1.6
45 |
46 | COPY FROM HERE
47 | FFB601A5
48 |
49 | ... Lots of stuff...
50 |
51 |
52 | TO HERE
53 |
54 |
55 |
56 | ```
57 |
58 | 6. Find this section, then delete everything in the section starting from:
59 | ```
60 |
61 | LiveSplit.GradedSplits.dll
62 |
63 | 1.6
64 |
65 | DELETE FROM HERE
66 | FFB601A5
67 | ... Lots of stuff ...
68 |
69 |
70 | TO HERE
71 |
72 |
73 | ... Lots off stuff ...
74 |
75 |
76 |
77 | ```
78 | 7. Paste the content you copied there.
79 | 8. Save your changes.
80 | 9. Open up LiveSplit again. Your layout should have 2 sets of splits.
81 | 10. Remove the original split component from your splits.
82 |
83 | ## Thanks
84 | In no particular order:
85 |
86 | Chlorate - https://chlorate.ca/ - for putting in effort with ideas+testing+bug reporting, as well as making the icons, https://chlorate.ca/split-icons/
87 |
88 | excogs - https://twitter.com/excogs - for putting in lots of effort ideas+testing+bug reporting, and advertising!
89 |
90 | Dyceron - https://twitter.com/Dyceron - reported a bug and I appreciate that
91 |
92 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.sln.docstates
8 |
9 | # Build results
10 | [Dd]ebug/
11 | [Dd]ebugPublic/
12 | [Rr]elease/
13 | x64/
14 | build/
15 | bld/
16 | [Bb]in/
17 | [Oo]bj/
18 |
19 | # MSTest test Results
20 | [Tt]est[Rr]esult*/
21 | [Bb]uild[Ll]og.*
22 |
23 | #NUNIT
24 | *.VisualState.xml
25 | TestResult.xml
26 |
27 | # Build Results of an ATL Project
28 | [Dd]ebugPS/
29 | [Rr]eleasePS/
30 | dlldata.c
31 |
32 | *_i.c
33 | *_p.c
34 | *_i.h
35 | *.ilk
36 | *.meta
37 | *.obj
38 | *.pch
39 | *.pdb
40 | *.pgc
41 | *.pgd
42 | *.rsp
43 | *.sbr
44 | *.tlb
45 | *.tli
46 | *.tlh
47 | *.tmp
48 | *.tmp_proj
49 | *.log
50 | *.vspscc
51 | *.vssscc
52 | .builds
53 | *.pidb
54 | *.svclog
55 | *.scc
56 |
57 | # Chutzpah Test files
58 | _Chutzpah*
59 |
60 | # Visual C++ cache files
61 | ipch/
62 | *.aps
63 | *.ncb
64 | *.opensdf
65 | *.sdf
66 | *.cachefile
67 |
68 | # Visual Studio profiler
69 | *.psess
70 | *.vsp
71 | *.vspx
72 |
73 | # TFS 2012 Local Workspace
74 | $tf/
75 |
76 | # Guidance Automation Toolkit
77 | *.gpState
78 |
79 | # ReSharper is a .NET coding add-in
80 | _ReSharper*/
81 | *.[Rr]e[Ss]harper
82 | *.DotSettings.user
83 |
84 | # JustCode is a .NET coding addin-in
85 | .JustCode
86 |
87 | # TeamCity is a build add-in
88 | _TeamCity*
89 |
90 | # DotCover is a Code Coverage Tool
91 | *.dotCover
92 |
93 | # NCrunch
94 | *.ncrunch*
95 | _NCrunch_*
96 | .*crunch*.local.xml
97 |
98 | # MightyMoose
99 | *.mm.*
100 | AutoTest.Net/
101 |
102 | # Web workbench (sass)
103 | .sass-cache/
104 |
105 | # Installshield output folder
106 | [Ee]xpress/
107 |
108 | # DocProject is a documentation generator add-in
109 | DocProject/buildhelp/
110 | DocProject/Help/*.HxT
111 | DocProject/Help/*.HxC
112 | DocProject/Help/*.hhc
113 | DocProject/Help/*.hhk
114 | DocProject/Help/*.hhp
115 | DocProject/Help/Html2
116 | DocProject/Help/html
117 |
118 | # Click-Once directory
119 | publish/
120 |
121 | # Publish Web Output
122 | *.[Pp]ublish.xml
123 | *.azurePubxml
124 |
125 | # NuGet Packages Directory
126 | packages/
127 | ## TODO: If the tool you use requires repositories.config uncomment the next line
128 | #!packages/repositories.config
129 |
130 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
131 | # This line needs to be after the ignore of the build folder (and the packages folder if the line above has been uncommented)
132 | !packages/build/
133 |
134 | # Windows Azure Build Output
135 | csx/
136 | *.build.csdef
137 |
138 | # Windows Store app package directory
139 | AppPackages/
140 |
141 | # Others
142 | sql/
143 | *.Cache
144 | ClientBin/
145 | [Ss]tyle[Cc]op.*
146 | ~$*
147 | *~
148 | *.dbmdl
149 | *.dbproj.schemaview
150 | *.pfx
151 | *.publishsettings
152 | node_modules/
153 |
154 | # RIA/Silverlight projects
155 | Generated_Code/
156 |
157 | # Backup & report files from converting an old project file to a newer
158 | # Visual Studio version. Backup files are not needed, because we have git ;-)
159 | _UpgradeReport_Files/
160 | Backup*/
161 | UpgradeLog*.XML
162 | UpgradeLog*.htm
163 |
164 | # SQL Server files
165 | *.mdf
166 | *.ldf
167 |
168 | # Business Intelligence projects
169 | *.rdl.data
170 | *.bim.layout
171 | *.bim_*.settings
172 |
173 | # Microsoft Fakes
174 | FakesAssemblies/
175 |
176 | # =========================
177 | # Operating System Files
178 | # =========================
179 |
180 | # OSX
181 | # =========================
182 |
183 | .DS_Store
184 | .AppleDouble
185 | .LSOverride
186 |
187 | # Icon must ends with two \r.
188 | Icon
189 |
190 | # Thumbnails
191 | ._*
192 |
193 | # Files that might appear on external disk
194 | .Spotlight-V100
195 | .Trashes
196 |
197 | # Windows
198 | # =========================
199 |
200 | # Windows image file caches
201 | Thumbs.db
202 | ehthumbs.db
203 |
204 | # Folder config file
205 | Desktop.ini
206 |
207 | # Recycle Bin used on file shares
208 | $RECYCLE.BIN/
209 |
210 | # Windows Installer files
211 | *.cab
212 | *.msi
213 | *.msm
214 | *.msp
215 |
--------------------------------------------------------------------------------
/UI/Components/GradedColumnSettings.cs:
--------------------------------------------------------------------------------
1 | using LiveSplit.Model;
2 | using LiveSplit.Model.Comparisons;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Windows.Forms;
7 |
8 | namespace LiveSplit.UI.Components
9 | {
10 | public partial class GradedColumnSettings : UserControl
11 | {
12 | public string ColumnName { get { return Data.Name; } set { Data.Name = value; } }
13 | public string Type
14 | {
15 | get { return GetColumnType(Data.Type); }
16 | set
17 | {
18 | Data.Type = ParseColumnType(value);
19 | UpdateComparisonItems();
20 | }
21 | }
22 | public string Comparison { get { return Data.Comparison; } set { Data.Comparison = value; } }
23 | public string TimingMethod { get { return Data.TimingMethod; } set { Data.TimingMethod = value; } }
24 |
25 | public GradedColumnData Data { get; set; }
26 | protected LiveSplitState CurrentState { get; set; }
27 | protected IList ColumnsList { get; set; }
28 |
29 | protected int ColumnIndex => ColumnsList.IndexOf(this);
30 | protected int TotalColumns => ColumnsList.Count;
31 |
32 | public event EventHandler ColumnRemoved;
33 | public event EventHandler MovedUp;
34 | public event EventHandler MovedDown;
35 |
36 | public GradedColumnSettings(LiveSplitState state, string columnName, IList columnsList)
37 | {
38 | InitializeComponent();
39 |
40 | Data = new GradedColumnData(columnName, GradedColumnType.Delta, "Current Comparison", "Current Timing Method");
41 |
42 | CurrentState = state;
43 | ColumnsList = columnsList;
44 | }
45 |
46 | void cmbTimingMethod_SelectedIndexChanged(object sender, EventArgs e)
47 | {
48 | TimingMethod = cmbTimingMethod.SelectedItem.ToString();
49 | }
50 |
51 | void cmbComparison_SelectedIndexChanged(object sender, EventArgs e)
52 | {
53 | Comparison = cmbComparison.SelectedItem.ToString();
54 | }
55 |
56 | void cmbColumnType_SelectedIndexChanged(object sender, EventArgs e)
57 | {
58 | Type = cmbColumnType.SelectedItem.ToString();
59 | }
60 |
61 | void ColumnSettings_Load(object sender, EventArgs e)
62 | {
63 | UpdateComparisonItems();
64 |
65 | txtName.DataBindings.Clear();
66 | cmbColumnType.DataBindings.Clear();
67 | cmbTimingMethod.DataBindings.Clear();
68 | txtName.DataBindings.Add("Text", this, "ColumnName", false, DataSourceUpdateMode.OnPropertyChanged);
69 | cmbColumnType.DataBindings.Add("SelectedItem", this, "Type", false, DataSourceUpdateMode.OnPropertyChanged);
70 | cmbTimingMethod.DataBindings.Add("SelectedItem", this, "TimingMethod", false, DataSourceUpdateMode.OnPropertyChanged);
71 | }
72 |
73 | public void UpdateEnabledButtons()
74 | {
75 | btnMoveDown.Enabled = ColumnIndex < TotalColumns - 1;
76 | btnMoveUp.Enabled = ColumnIndex > 0;
77 | }
78 |
79 | void txtName_TextChanged(object sender, EventArgs e)
80 | {
81 | groupColumn.Text = $"Column: { txtName.Text }";
82 | }
83 |
84 | private void UpdateComparisonItems()
85 | {
86 | cmbComparison.Items.Clear();
87 | cmbComparison.Items.Add("Current Comparison");
88 |
89 | if (Data.Type == GradedColumnType.Delta || Data.Type == GradedColumnType.DeltaorSplitTime || Data.Type == GradedColumnType.SplitTime)
90 | cmbComparison.Items.AddRange(CurrentState.Run.Comparisons.Where(x => x != NoneComparisonGenerator.ComparisonName).ToArray());
91 | else
92 | {
93 | cmbComparison.Items.AddRange(CurrentState.Run.Comparisons.Where(x => x != BestSplitTimesComparisonGenerator.ComparisonName && x != NoneComparisonGenerator.ComparisonName).ToArray());
94 | if (Comparison == BestSplitTimesComparisonGenerator.ComparisonName)
95 | Comparison = "Current Comparison";
96 | }
97 |
98 | if (!cmbComparison.Items.Contains(Comparison))
99 | cmbComparison.Items.Add(Comparison);
100 |
101 | cmbComparison.DataBindings.Clear();
102 | cmbComparison.DataBindings.Add("SelectedItem", this, "Comparison", false, DataSourceUpdateMode.OnPropertyChanged);
103 | }
104 |
105 | private static string GetColumnType(GradedColumnType type)
106 | {
107 | if (type == GradedColumnType.SplitTime)
108 | return "Split Time";
109 | else if (type == GradedColumnType.Delta)
110 | return "Delta";
111 | else if (type == GradedColumnType.DeltaorSplitTime)
112 | return "Delta or Split Time";
113 | else if (type == GradedColumnType.SegmentTime)
114 | return "Segment Time";
115 | else if (type == GradedColumnType.SegmentDelta)
116 | return "Segment Delta";
117 | else
118 | return "Segment Delta or Segment Time";
119 | }
120 |
121 | private static GradedColumnType ParseColumnType(string columnType)
122 | {
123 | return (GradedColumnType)Enum.Parse(typeof(GradedColumnType), columnType.Replace(" ", string.Empty));
124 | }
125 |
126 | private void btnRemoveColumn_Click(object sender, EventArgs e)
127 | {
128 | ColumnRemoved?.Invoke(this, null);
129 | }
130 |
131 | private void btnMoveUp_Click(object sender, EventArgs e)
132 | {
133 | MovedUp?.Invoke(this, null);
134 | }
135 |
136 | private void btnMoveDown_Click(object sender, EventArgs e)
137 | {
138 | MovedDown?.Invoke(this, null);
139 | }
140 |
141 | public void SelectControl()
142 | {
143 | btnRemoveColumn.Select();
144 | }
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/UI/Components/GradedColumnSettings.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/UI/Components/GradedSplitsSettings.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/LiveSplit.GradedSplits.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {5546515A-8BD2-4391-A300-448E277F558D}
8 | Library
9 | Properties
10 | LiveSplit
11 | LiveSplit.GradedSplits
12 | v4.6.1
13 | 512
14 | Svn
15 | Svn
16 | Svn
17 | SubversionScc
18 |
19 |
20 |
21 | true
22 | full
23 | false
24 | ..\..\bin\Debug\Components\
25 | DEBUG;TRACE
26 | prompt
27 | 4
28 | false
29 |
30 |
31 | pdbonly
32 | true
33 | ..\..\bin\Release\Components\
34 | TRACE
35 | prompt
36 | 4
37 | MixedRecommendedRules.ruleset
38 | false
39 |
40 |
41 | ..\..\bin\Release Candidate\Components\
42 | TRACE;RELEASE_CANDIDATE
43 | true
44 | pdbonly
45 | AnyCPU
46 | prompt
47 | MixedRecommendedRules.ruleset
48 | false
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | ..\Libs\WinFormsColor.dll
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | True
66 | True
67 | Resources.resx
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | UserControl
76 |
77 |
78 | GradedSplitsSettings.cs
79 |
80 |
81 | UserControl
82 |
83 |
84 | GradedColumnSettings.cs
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 | ResXFileCodeGenerator
94 | Resources.Designer.cs
95 |
96 |
97 | GradedSplitsSettings.cs
98 |
99 |
100 | GradedColumnSettings.cs
101 |
102 |
103 |
104 |
105 | {6de847db-20a3-4848-aeee-1b4364aecdfb}
106 | LiveSplit.Core
107 |
108 |
109 | {e8047f95-ba79-4e2e-a9bb-5c48d5fa2467}
110 | LiveSplit
111 |
112 |
113 | {56dea3a0-2eb7-493b-b50f-a5e3aa8ae52a}
114 | UpdateManager
115 |
116 |
117 |
118 |
125 |
--------------------------------------------------------------------------------
/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 |
--------------------------------------------------------------------------------
/UI/Components/GradedLabelsComponent.cs:
--------------------------------------------------------------------------------
1 | using LiveSplit.Model;
2 | using LiveSplit.TimeFormatters;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Drawing;
6 | using System.Linq;
7 | using System.Windows.Forms;
8 |
9 | namespace LiveSplit.UI.Components
10 | {
11 | public class GradedLabelsComponent : IComponent
12 | {
13 | public GradedSplitsSettings Settings { get; set; }
14 |
15 | protected SimpleLabel MeasureTimeLabel { get; set; }
16 | protected SimpleLabel MeasureDeltaLabel { get; set; }
17 |
18 | protected ITimeFormatter TimeFormatter { get; set; }
19 | protected ITimeFormatter DeltaTimeFormatter { get; set; }
20 |
21 | protected TimeAccuracy CurrentAccuracy { get; set; }
22 | protected TimeAccuracy CurrentDeltaAccuracy { get; set; }
23 | protected bool CurrentDropDecimals { get; set; }
24 |
25 | protected int FrameCount { get; set; }
26 |
27 | public GraphicsCache Cache { get; set; }
28 |
29 | public IEnumerable ColumnsList { get; set; }
30 | public IList LabelsList { get; set; }
31 |
32 | public float PaddingTop => 0f;
33 | public float PaddingLeft => 0f;
34 | public float PaddingBottom => 0f;
35 | public float PaddingRight => 0f;
36 |
37 | public float VerticalHeight => 25 + Settings.SplitHeight;
38 |
39 | public float MinimumWidth { get; set; }
40 |
41 | public float HorizontalWidth => 10f; /* not available in horizontal mode */
42 |
43 | public float MinimumHeight { get; set; }
44 |
45 | public IDictionary ContextMenuControls => null;
46 | public GradedLabelsComponent(GradedSplitsSettings settings, IEnumerable columns)
47 | {
48 | Settings = settings;
49 | MinimumHeight = 31;
50 |
51 | MeasureTimeLabel = new SimpleLabel();
52 | MeasureDeltaLabel = new SimpleLabel();
53 | TimeFormatter = new GradedRegularSplitTimeFormatter(Settings.SplitTimesAccuracy);
54 | DeltaTimeFormatter = new GradedDeltaSplitTimeFormatter(Settings.DeltasAccuracy, Settings.DropDecimals);
55 |
56 | Cache = new GraphicsCache();
57 | LabelsList = new List();
58 | ColumnsList = columns;
59 | }
60 |
61 | private void DrawGeneral(Graphics g, LiveSplitState state, float width, float height, LayoutMode mode)
62 | {
63 | if (Settings.BackgroundGradient == GradedExtendedGradientType.Alternating)
64 | g.FillRectangle(new SolidBrush(
65 | Settings.BackgroundColor
66 | ), 0, 0, width, height);
67 |
68 | MeasureTimeLabel.Text = TimeFormatter.Format(new TimeSpan(24, 0, 0));
69 | MeasureDeltaLabel.Text = DeltaTimeFormatter.Format(new TimeSpan(0, 9, 0, 0));
70 |
71 | MeasureTimeLabel.Font = state.LayoutSettings.TimesFont;
72 | MeasureTimeLabel.IsMonospaced = true;
73 | MeasureDeltaLabel.Font = state.LayoutSettings.TimesFont;
74 | MeasureDeltaLabel.IsMonospaced = true;
75 |
76 | MeasureTimeLabel.SetActualWidth(g);
77 | MeasureDeltaLabel.SetActualWidth(g);
78 |
79 | if (Settings.SplitTimesAccuracy != CurrentAccuracy)
80 | {
81 | TimeFormatter = new GradedRegularSplitTimeFormatter(Settings.SplitTimesAccuracy);
82 | CurrentAccuracy = Settings.SplitTimesAccuracy;
83 | }
84 | if (Settings.DeltasAccuracy != CurrentDeltaAccuracy || Settings.DropDecimals != CurrentDropDecimals)
85 | {
86 | DeltaTimeFormatter = new GradedDeltaSplitTimeFormatter(Settings.DeltasAccuracy, Settings.DropDecimals);
87 | CurrentDeltaAccuracy = Settings.DeltasAccuracy;
88 | CurrentDropDecimals = Settings.DropDecimals;
89 | }
90 |
91 | foreach (var label in LabelsList)
92 | {
93 | label.ShadowColor = state.LayoutSettings.ShadowsColor;
94 | label.OutlineColor = state.LayoutSettings.TextOutlineColor;
95 | label.Y = 0;
96 | label.Height = height;
97 | }
98 | MinimumWidth = 10f;
99 |
100 | if (ColumnsList.Count() == LabelsList.Count)
101 | {
102 | var curX = width - 7;
103 | foreach (var label in LabelsList.Reverse())
104 | {
105 | var column = ColumnsList.ElementAt(LabelsList.IndexOf(label));
106 |
107 | var labelWidth = 0f;
108 | if (column.Type == GradedColumnType.DeltaorSplitTime || column.Type == GradedColumnType.SegmentDeltaorSegmentTime)
109 | labelWidth = Math.Max(MeasureDeltaLabel.ActualWidth, MeasureTimeLabel.ActualWidth);
110 | else if (column.Type == GradedColumnType.Delta || column.Type == GradedColumnType.SegmentDelta)
111 | labelWidth = MeasureDeltaLabel.ActualWidth;
112 | else
113 | labelWidth = MeasureTimeLabel.ActualWidth;
114 | curX -= labelWidth + 5;
115 | label.Width = labelWidth;
116 | label.X = curX + 5;
117 |
118 | label.Font = state.LayoutSettings.TextFont;
119 | label.HasShadow = state.LayoutSettings.DropShadows;
120 | label.Draw(g);
121 | }
122 | }
123 | }
124 |
125 | public void DrawVertical(Graphics g, LiveSplitState state, float width, Region clipRegion)
126 | {
127 | DrawGeneral(g, state, width, VerticalHeight, LayoutMode.Vertical);
128 | }
129 |
130 | public void DrawHorizontal(Graphics g, LiveSplitState state, float height, Region clipRegion)
131 | {
132 | DrawGeneral(g, state, HorizontalWidth, height, LayoutMode.Horizontal);
133 | }
134 |
135 | public string ComponentName => "Labels";
136 |
137 | public Control GetSettingsControl(LayoutMode mode)
138 | {
139 | throw new NotSupportedException();
140 | }
141 |
142 | public void SetSettings(System.Xml.XmlNode settings)
143 | {
144 | throw new NotSupportedException();
145 | }
146 |
147 |
148 | public System.Xml.XmlNode GetSettings(System.Xml.XmlDocument document)
149 | {
150 | throw new NotSupportedException();
151 | }
152 |
153 | public string UpdateName
154 | {
155 | get { throw new NotSupportedException(); }
156 | }
157 |
158 | public string XMLURL
159 | {
160 | get { throw new NotSupportedException(); }
161 | }
162 |
163 | public string UpdateURL
164 | {
165 | get { throw new NotSupportedException(); }
166 | }
167 |
168 | public Version Version
169 | {
170 | get { throw new NotSupportedException(); }
171 | }
172 |
173 | protected void UpdateAll(LiveSplitState state)
174 | {
175 | RecreateLabels();
176 |
177 | foreach (var label in LabelsList)
178 | {
179 | var column = ColumnsList.ElementAt(LabelsList.IndexOf(label));
180 | label.Text = column.Name;
181 | label.ForeColor = Settings.LabelsColor;
182 | }
183 | }
184 |
185 | protected void RecreateLabels()
186 | {
187 | if (ColumnsList != null && LabelsList.Count != ColumnsList.Count())
188 | {
189 | LabelsList.Clear();
190 | foreach (var column in ColumnsList)
191 | {
192 | LabelsList.Add(new SimpleLabel()
193 | {
194 | HorizontalAlignment = StringAlignment.Far,
195 | VerticalAlignment = StringAlignment.Center
196 | });
197 | }
198 | }
199 | }
200 |
201 | public void Update(IInvalidator invalidator, LiveSplitState state, float width, float height, LayoutMode mode)
202 | {
203 | UpdateAll(state);
204 |
205 | Cache.Restart();
206 | Cache["ColumnsCount"] = ColumnsList.Count();
207 | foreach (var label in LabelsList)
208 | Cache["Columns" + LabelsList.IndexOf(label) + "Text"] = label.Text;
209 |
210 | if (invalidator != null && (Cache.HasChanged || FrameCount > 1))
211 | {
212 | invalidator.Invalidate(0, 0, width, height);
213 | }
214 | }
215 |
216 | public void Dispose()
217 | {
218 | }
219 | }
220 | }
221 |
--------------------------------------------------------------------------------
/UI/Components/GradedColumnSettings.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace LiveSplit.UI.Components
2 | {
3 | partial class GradedColumnSettings
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
32 | this.label1 = new System.Windows.Forms.Label();
33 | this.label2 = new System.Windows.Forms.Label();
34 | this.label3 = new System.Windows.Forms.Label();
35 | this.label4 = new System.Windows.Forms.Label();
36 | this.txtName = new System.Windows.Forms.TextBox();
37 | this.cmbColumnType = new System.Windows.Forms.ComboBox();
38 | this.cmbComparison = new System.Windows.Forms.ComboBox();
39 | this.cmbTimingMethod = new System.Windows.Forms.ComboBox();
40 | this.btnRemoveColumn = new System.Windows.Forms.Button();
41 | this.btnMoveDown = new System.Windows.Forms.Button();
42 | this.btnMoveUp = new System.Windows.Forms.Button();
43 | this.groupColumn = new System.Windows.Forms.GroupBox();
44 | this.tableLayoutPanel1.SuspendLayout();
45 | this.groupColumn.SuspendLayout();
46 | this.SuspendLayout();
47 | //
48 | // tableLayoutPanel1
49 | //
50 | this.tableLayoutPanel1.ColumnCount = 4;
51 | this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 90F));
52 | this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 130F));
53 | this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 81F));
54 | this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 114F));
55 | this.tableLayoutPanel1.Controls.Add(this.label1, 0, 0);
56 | this.tableLayoutPanel1.Controls.Add(this.label2, 0, 3);
57 | this.tableLayoutPanel1.Controls.Add(this.label3, 0, 1);
58 | this.tableLayoutPanel1.Controls.Add(this.label4, 0, 2);
59 | this.tableLayoutPanel1.Controls.Add(this.txtName, 1, 0);
60 | this.tableLayoutPanel1.Controls.Add(this.cmbColumnType, 1, 1);
61 | this.tableLayoutPanel1.Controls.Add(this.cmbComparison, 1, 2);
62 | this.tableLayoutPanel1.Controls.Add(this.cmbTimingMethod, 1, 3);
63 | this.tableLayoutPanel1.Controls.Add(this.btnRemoveColumn, 3, 4);
64 | this.tableLayoutPanel1.Controls.Add(this.btnMoveDown, 2, 4);
65 | this.tableLayoutPanel1.Controls.Add(this.btnMoveUp, 1, 4);
66 | this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
67 | this.tableLayoutPanel1.Location = new System.Drawing.Point(3, 16);
68 | this.tableLayoutPanel1.Name = "tableLayoutPanel1";
69 | this.tableLayoutPanel1.RowCount = 5;
70 | this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 29F));
71 | this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 29F));
72 | this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 29F));
73 | this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 29F));
74 | this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 29F));
75 | this.tableLayoutPanel1.Size = new System.Drawing.Size(421, 146);
76 | this.tableLayoutPanel1.TabIndex = 0;
77 | //
78 | // label1
79 | //
80 | this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
81 | this.label1.AutoSize = true;
82 | this.label1.Location = new System.Drawing.Point(3, 8);
83 | this.label1.Name = "label1";
84 | this.label1.Size = new System.Drawing.Size(84, 13);
85 | this.label1.TabIndex = 42;
86 | this.label1.Text = "Name:";
87 | //
88 | // label2
89 | //
90 | this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
91 | this.label2.AutoSize = true;
92 | this.label2.Location = new System.Drawing.Point(3, 95);
93 | this.label2.Name = "label2";
94 | this.label2.Size = new System.Drawing.Size(84, 13);
95 | this.label2.TabIndex = 41;
96 | this.label2.Text = "Timing Method:";
97 | //
98 | // label3
99 | //
100 | this.label3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
101 | this.label3.AutoSize = true;
102 | this.label3.Location = new System.Drawing.Point(3, 37);
103 | this.label3.Name = "label3";
104 | this.label3.Size = new System.Drawing.Size(84, 13);
105 | this.label3.TabIndex = 44;
106 | this.label3.Text = "Column Type:";
107 | //
108 | // label4
109 | //
110 | this.label4.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
111 | this.label4.AutoSize = true;
112 | this.label4.Location = new System.Drawing.Point(3, 66);
113 | this.label4.Name = "label4";
114 | this.label4.Size = new System.Drawing.Size(84, 13);
115 | this.label4.TabIndex = 45;
116 | this.label4.Text = "Comparison:";
117 | //
118 | // txtName
119 | //
120 | this.txtName.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
121 | this.tableLayoutPanel1.SetColumnSpan(this.txtName, 3);
122 | this.txtName.Location = new System.Drawing.Point(93, 4);
123 | this.txtName.Name = "txtName";
124 | this.txtName.Size = new System.Drawing.Size(325, 20);
125 | this.txtName.TabIndex = 43;
126 | this.txtName.TextChanged += new System.EventHandler(txtName_TextChanged);
127 | //
128 | // cmbColumnType
129 | //
130 | this.cmbColumnType.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
131 | this.tableLayoutPanel1.SetColumnSpan(this.cmbColumnType, 3);
132 | this.cmbColumnType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
133 | this.cmbColumnType.FormattingEnabled = true;
134 | this.cmbColumnType.Items.AddRange(new object[] {
135 | "Delta",
136 | "Split Time",
137 | "Delta or Split Time",
138 | "Segment Delta",
139 | "Segment Time",
140 | "Segment Delta or Segment Time"});
141 | this.cmbColumnType.Location = new System.Drawing.Point(93, 33);
142 | this.cmbColumnType.Name = "cmbColumnType";
143 | this.cmbColumnType.Size = new System.Drawing.Size(325, 21);
144 | this.cmbColumnType.TabIndex = 46;
145 | this.cmbColumnType.SelectedIndexChanged += new System.EventHandler(cmbColumnType_SelectedIndexChanged);
146 | //
147 | // cmbComparison
148 | //
149 | this.cmbComparison.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
150 | this.tableLayoutPanel1.SetColumnSpan(this.cmbComparison, 3);
151 | this.cmbComparison.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
152 | this.cmbComparison.FormattingEnabled = true;
153 | this.cmbComparison.Location = new System.Drawing.Point(93, 62);
154 | this.cmbComparison.Name = "cmbComparison";
155 | this.cmbComparison.Size = new System.Drawing.Size(325, 21);
156 | this.cmbComparison.TabIndex = 47;
157 | this.cmbComparison.SelectedIndexChanged += new System.EventHandler(cmbComparison_SelectedIndexChanged);
158 | //
159 | // cmbTimingMethod
160 | //
161 | this.cmbTimingMethod.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
162 | this.tableLayoutPanel1.SetColumnSpan(this.cmbTimingMethod, 3);
163 | this.cmbTimingMethod.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
164 | this.cmbTimingMethod.FormattingEnabled = true;
165 | this.cmbTimingMethod.Items.AddRange(new object[] {
166 | "Current Timing Method",
167 | "Real Time",
168 | "Game Time"});
169 | this.cmbTimingMethod.Location = new System.Drawing.Point(93, 91);
170 | this.cmbTimingMethod.Name = "cmbTimingMethod";
171 | this.cmbTimingMethod.Size = new System.Drawing.Size(325, 21);
172 | this.cmbTimingMethod.TabIndex = 48;
173 | this.cmbTimingMethod.SelectedIndexChanged += new System.EventHandler(cmbTimingMethod_SelectedIndexChanged);
174 | //
175 | // btnRemoveColumn
176 | //
177 | this.btnRemoveColumn.Anchor = System.Windows.Forms.AnchorStyles.Right;
178 | this.btnRemoveColumn.Location = new System.Drawing.Point(324, 119);
179 | this.btnRemoveColumn.Name = "btnRemoveColumn";
180 | this.btnRemoveColumn.Size = new System.Drawing.Size(94, 23);
181 | this.btnRemoveColumn.TabIndex = 52;
182 | this.btnRemoveColumn.Text = "Remove Column";
183 | this.btnRemoveColumn.UseVisualStyleBackColor = true;
184 | this.btnRemoveColumn.Click += new System.EventHandler(this.btnRemoveColumn_Click);
185 | //
186 | // btnMoveDown
187 | //
188 | this.btnMoveDown.Anchor = System.Windows.Forms.AnchorStyles.Right;
189 | this.btnMoveDown.Location = new System.Drawing.Point(223, 119);
190 | this.btnMoveDown.Name = "btnMoveDown";
191 | this.btnMoveDown.Size = new System.Drawing.Size(75, 23);
192 | this.btnMoveDown.TabIndex = 51;
193 | this.btnMoveDown.Text = "Move Down";
194 | this.btnMoveDown.UseVisualStyleBackColor = true;
195 | this.btnMoveDown.Click += new System.EventHandler(this.btnMoveDown_Click);
196 | //
197 | // btnMoveUp
198 | //
199 | this.btnMoveUp.Anchor = System.Windows.Forms.AnchorStyles.Right;
200 | this.btnMoveUp.Location = new System.Drawing.Point(142, 119);
201 | this.btnMoveUp.Name = "btnMoveUp";
202 | this.btnMoveUp.Size = new System.Drawing.Size(75, 23);
203 | this.btnMoveUp.TabIndex = 50;
204 | this.btnMoveUp.Text = "Move Up";
205 | this.btnMoveUp.UseVisualStyleBackColor = true;
206 | this.btnMoveUp.Click += new System.EventHandler(this.btnMoveUp_Click);
207 | //
208 | // groupColumn
209 | //
210 | this.groupColumn.Controls.Add(this.tableLayoutPanel1);
211 | this.groupColumn.Dock = System.Windows.Forms.DockStyle.Fill;
212 | this.groupColumn.Location = new System.Drawing.Point(0, 0);
213 | this.groupColumn.Name = "groupColumn";
214 | this.groupColumn.Size = new System.Drawing.Size(427, 165);
215 | this.groupColumn.TabIndex = 1;
216 | this.groupColumn.TabStop = false;
217 | this.groupColumn.Text = "Column Name";
218 | //
219 | // ColumnSettings
220 | //
221 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
222 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
223 | this.Controls.Add(this.groupColumn);
224 | this.Name = "ColumnSettings";
225 | this.Size = new System.Drawing.Size(427, 165);
226 | this.Load += new System.EventHandler(ColumnSettings_Load);
227 | this.tableLayoutPanel1.ResumeLayout(false);
228 | this.tableLayoutPanel1.PerformLayout();
229 | this.groupColumn.ResumeLayout(false);
230 | this.ResumeLayout(false);
231 |
232 | }
233 |
234 | #endregion
235 |
236 | private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
237 | private System.Windows.Forms.ComboBox cmbTimingMethod;
238 | private System.Windows.Forms.Label label2;
239 | private System.Windows.Forms.GroupBox groupColumn;
240 | private System.Windows.Forms.Label label1;
241 | private System.Windows.Forms.TextBox txtName;
242 | private System.Windows.Forms.Label label3;
243 | private System.Windows.Forms.Label label4;
244 | private System.Windows.Forms.ComboBox cmbColumnType;
245 | private System.Windows.Forms.ComboBox cmbComparison;
246 | private System.Windows.Forms.Button btnRemoveColumn;
247 | private System.Windows.Forms.Button btnMoveDown;
248 | private System.Windows.Forms.Button btnMoveUp;
249 | }
250 | }
--------------------------------------------------------------------------------
/UI/Components/GradedSplitsComponent.cs:
--------------------------------------------------------------------------------
1 | using LiveSplit.Model;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Drawing;
5 | using System.Drawing.Drawing2D;
6 | using System.Linq;
7 | using System.Windows.Forms;
8 |
9 | namespace LiveSplit.UI.Components
10 | {
11 | public class GradedSplitsComponent : IComponent
12 | {
13 | public ComponentRendererComponent InternalComponent { get; protected set; }
14 |
15 | public float PaddingTop => InternalComponent.PaddingTop;
16 | public float PaddingLeft => InternalComponent.PaddingLeft;
17 | public float PaddingBottom => InternalComponent.PaddingBottom;
18 | public float PaddingRight => InternalComponent.PaddingRight;
19 |
20 | protected IList Components { get; set; }
21 | protected IList SplitComponents { get; set; }
22 |
23 | protected GradedSplitsSettings Settings { get; set; }
24 |
25 | private Dictionary ShadowImages { get; set; }
26 |
27 | private int visualSplitCount;
28 | private int settingsSplitCount;
29 |
30 | protected bool PreviousShowLabels { get; set; }
31 |
32 | protected int ScrollOffset { get; set; }
33 | protected int LastSplitSeparatorIndex { get; set; }
34 |
35 | protected LiveSplitState CurrentState { get; set; }
36 | protected LiveSplitState OldState { get; set; }
37 | protected LayoutMode OldLayoutMode { get; set; }
38 | protected Color OldShadowsColor { get; set; }
39 |
40 | protected IEnumerable ColumnsList => Settings.ColumnsList.Select(x => x.Data);
41 |
42 | public string ComponentName => "Graded Splits";
43 |
44 | public float VerticalHeight => InternalComponent.VerticalHeight;
45 |
46 | public float MinimumWidth => InternalComponent.MinimumWidth;
47 |
48 | public float HorizontalWidth => InternalComponent.HorizontalWidth;
49 |
50 | public float MinimumHeight => InternalComponent.MinimumHeight;
51 |
52 | public IDictionary ContextMenuControls => null;
53 |
54 | public GradedSplitsComponent(LiveSplitState state)
55 | {
56 | CurrentState = state;
57 | Settings = new GradedSplitsSettings(state);
58 | InternalComponent = new ComponentRendererComponent();
59 | ShadowImages = new Dictionary();
60 | visualSplitCount = Settings.VisualSplitCount;
61 | settingsSplitCount = Settings.VisualSplitCount;
62 | Settings.SplitLayoutChanged += Settings_SplitLayoutChanged;
63 | ScrollOffset = 0;
64 | RebuildVisualSplits();
65 | state.ComparisonRenamed += state_ComparisonRenamed;
66 | }
67 |
68 | void state_ComparisonRenamed(object sender, EventArgs e)
69 | {
70 | var args = (RenameEventArgs)e;
71 | foreach (var column in ColumnsList)
72 | {
73 | if (column.Comparison == args.OldName)
74 | {
75 | column.Comparison = args.NewName;
76 | ((LiveSplitState)sender).Layout.HasChanged = true;
77 | }
78 | }
79 | }
80 |
81 | void Settings_SplitLayoutChanged(object sender, EventArgs e)
82 | {
83 | RebuildVisualSplits();
84 | }
85 |
86 | private void RebuildVisualSplits()
87 | {
88 | Components = new List();
89 | SplitComponents = new List();
90 | InternalComponent.VisibleComponents = Components;
91 |
92 | var totalSplits = Settings.ShowBlankSplits ? Math.Max(Settings.VisualSplitCount, visualSplitCount) : visualSplitCount;
93 |
94 | if (Settings.ShowColumnLabels && CurrentState.Layout?.Mode == LayoutMode.Vertical)
95 | {
96 | Components.Add(new GradedLabelsComponent(Settings, ColumnsList));
97 | Components.Add(new SeparatorComponent());
98 | }
99 |
100 | for (var i = 0; i < totalSplits; ++i)
101 | {
102 | if (i == totalSplits - 1 && i > 0)
103 | {
104 | LastSplitSeparatorIndex = Components.Count;
105 | if (Settings.AlwaysShowLastSplit && Settings.SeparatorLastSplit)
106 | Components.Add(new SeparatorComponent());
107 | else if (Settings.ShowThinSeparators)
108 | Components.Add(new ThinSeparatorComponent());
109 | }
110 |
111 | var splitComponent = new GradedSplitComponent(Settings, ColumnsList);
112 | Components.Add(splitComponent);
113 | if (i < visualSplitCount - 1 || i == (Settings.LockLastSplit ? totalSplits - 1 : visualSplitCount - 1))
114 | SplitComponents.Add(splitComponent);
115 |
116 | if (Settings.ShowThinSeparators && i < totalSplits - 2)
117 | Components.Add(new ThinSeparatorComponent());
118 | }
119 | }
120 |
121 | private void Prepare(LiveSplitState state)
122 | {
123 | if (state != OldState)
124 | {
125 | state.OnScrollDown += state_OnScrollDown;
126 | state.OnScrollUp += state_OnScrollUp;
127 | state.OnStart += state_OnStart;
128 | state.OnReset += state_OnReset;
129 | state.OnSplit += state_OnSplit;
130 | state.OnSkipSplit += state_OnSkipSplit;
131 | state.OnUndoSplit += state_OnUndoSplit;
132 | OldState = state;
133 | }
134 |
135 | var previousSplitCount = visualSplitCount;
136 | visualSplitCount = Math.Min(state.Run.Count, Settings.VisualSplitCount);
137 | if (previousSplitCount != visualSplitCount
138 | || (Settings.ShowBlankSplits && settingsSplitCount != Settings.VisualSplitCount)
139 | || Settings.ShowColumnLabels != PreviousShowLabels
140 | || (Settings.ShowColumnLabels && state.Layout.Mode != OldLayoutMode))
141 | {
142 | PreviousShowLabels = Settings.ShowColumnLabels;
143 | OldLayoutMode = state.Layout.Mode;
144 | RebuildVisualSplits();
145 | }
146 | settingsSplitCount = Settings.VisualSplitCount;
147 |
148 | var skipCount = Math.Min(
149 | Math.Max(
150 | 0,
151 | state.CurrentSplitIndex - (visualSplitCount - 2 - Settings.SplitPreviewCount + (Settings.AlwaysShowLastSplit ? 0 : 1))),
152 | state.Run.Count - visualSplitCount);
153 | ScrollOffset = Math.Min(Math.Max(ScrollOffset, -skipCount), state.Run.Count - skipCount - visualSplitCount);
154 | skipCount += ScrollOffset;
155 |
156 | if (OldShadowsColor != state.LayoutSettings.ShadowsColor)
157 | ShadowImages.Clear();
158 |
159 | foreach (var split in state.Run)
160 | {
161 | if (split.Icon != null && (!ShadowImages.ContainsKey(split.Icon) || OldShadowsColor != state.LayoutSettings.ShadowsColor))
162 | {
163 | ShadowImages.Add(split.Icon, IconShadow.Generate(split.Icon, state.LayoutSettings.ShadowsColor));
164 | }
165 | }
166 |
167 | var iconsNotBlank = (state.Run.Where(x => x.Icon != null).Any() || Settings.GradedIconsApplicationState != GradedIconsApplicationState.Disabled);
168 | foreach (var split in SplitComponents)
169 | {
170 | split.DisplayIcon = iconsNotBlank && Settings.DisplayIcons;
171 |
172 | if (split.Split != null && split.Split.Icon != null)
173 | split.ShadowImage = ShadowImages[split.Split.Icon];
174 | else
175 | split.ShadowImage = null;
176 | }
177 | OldShadowsColor = state.LayoutSettings.ShadowsColor;
178 |
179 | foreach (var component in Components)
180 | {
181 | if (component is SeparatorComponent)
182 | {
183 | var separator = (SeparatorComponent)component;
184 | var index = Components.IndexOf(separator);
185 | if (state.CurrentPhase == TimerPhase.Running || state.CurrentPhase == TimerPhase.Paused)
186 | {
187 | if (((GradedSplitComponent)Components[index + 1]).Split == state.CurrentSplit)
188 | separator.LockToBottom = true;
189 | else if (Components[index - 1] is GradedSplitComponent && ((GradedSplitComponent)Components[index - 1]).Split == state.CurrentSplit)
190 | separator.LockToBottom = false;
191 | }
192 | if (Settings.AlwaysShowLastSplit && Settings.SeparatorLastSplit && index == LastSplitSeparatorIndex)
193 | {
194 | if (skipCount >= state.Run.Count - visualSplitCount)
195 | {
196 | if (Settings.ShowThinSeparators)
197 | separator.DisplayedSize = 1f;
198 | else
199 | separator.DisplayedSize = 0f;
200 |
201 | separator.UseSeparatorColor = false;
202 | }
203 | else
204 | {
205 | separator.DisplayedSize = 2f;
206 | separator.UseSeparatorColor = true;
207 | }
208 | }
209 | }
210 | else if (component is ThinSeparatorComponent)
211 | {
212 | var separator = (ThinSeparatorComponent)component;
213 | var index = Components.IndexOf(separator);
214 | if (state.CurrentPhase == TimerPhase.Running || state.CurrentPhase == TimerPhase.Paused)
215 | {
216 | if (((GradedSplitComponent)Components[index + 1]).Split == state.CurrentSplit)
217 | separator.LockToBottom = true;
218 | else if (((GradedSplitComponent)Components[index - 1]).Split == state.CurrentSplit)
219 | separator.LockToBottom = false;
220 | }
221 | }
222 | }
223 | }
224 |
225 | void state_OnUndoSplit(object sender, EventArgs e)
226 | {
227 | ScrollOffset = 0;
228 | }
229 |
230 | void state_OnSkipSplit(object sender, EventArgs e)
231 | {
232 | ScrollOffset = 0;
233 | }
234 |
235 | void state_OnSplit(object sender, EventArgs e)
236 | {
237 | ScrollOffset = 0;
238 | }
239 |
240 | void state_OnReset(object sender, TimerPhase e)
241 | {
242 | ScrollOffset = 0;
243 | }
244 |
245 | void state_OnStart(object sender, EventArgs e)
246 | {
247 | ScrollOffset = 0;
248 | }
249 |
250 | void state_OnScrollUp(object sender, EventArgs e)
251 | {
252 | ScrollOffset--;
253 | }
254 |
255 | void state_OnScrollDown(object sender, EventArgs e)
256 | {
257 | ScrollOffset++;
258 | }
259 |
260 | void DrawBackground(Graphics g, float width, float height)
261 | {
262 | if (Settings.BackgroundGradient != GradedExtendedGradientType.Alternating
263 | && (Settings.BackgroundColor.A > 0
264 | || Settings.BackgroundGradient != GradedExtendedGradientType.Plain
265 | && Settings.BackgroundColor2.A > 0))
266 | {
267 | var gradientBrush = new LinearGradientBrush(
268 | new PointF(0, 0),
269 | Settings.BackgroundGradient == GradedExtendedGradientType.Horizontal
270 | ? new PointF(width, 0)
271 | : new PointF(0, height),
272 | Settings.BackgroundColor,
273 | Settings.BackgroundGradient == GradedExtendedGradientType.Plain
274 | ? Settings.BackgroundColor
275 | : Settings.BackgroundColor2);
276 | g.FillRectangle(gradientBrush, 0, 0, width, height);
277 | }
278 | }
279 |
280 | public void DrawVertical(Graphics g, LiveSplitState state, float width, Region clipRegion)
281 | {
282 | Prepare(state);
283 | DrawBackground(g, width, VerticalHeight);
284 | InternalComponent.DrawVertical(g, state, width, clipRegion);
285 | }
286 |
287 | public void DrawHorizontal(Graphics g, LiveSplitState state, float height, Region clipRegion)
288 | {
289 | Prepare(state);
290 | DrawBackground(g, HorizontalWidth, height);
291 | InternalComponent.DrawHorizontal(g, state, height, clipRegion);
292 | }
293 |
294 | public Control GetSettingsControl(LayoutMode mode)
295 | {
296 | Settings.Mode = mode;
297 | return Settings;
298 | }
299 |
300 | public void SetSettings(System.Xml.XmlNode settings)
301 | {
302 | Settings.SetSettings(settings);
303 | RebuildVisualSplits();
304 | }
305 |
306 | public System.Xml.XmlNode GetSettings(System.Xml.XmlDocument document)
307 | {
308 | return Settings.GetSettings(document);
309 | }
310 |
311 | public void Update(IInvalidator invalidator, LiveSplitState state, float width, float height, LayoutMode mode)
312 | {
313 | var skipCount = Math.Min(
314 | Math.Max(
315 | 0,
316 | state.CurrentSplitIndex - (visualSplitCount - 2 - Settings.SplitPreviewCount + (Settings.AlwaysShowLastSplit ? 0 : 1))),
317 | state.Run.Count - visualSplitCount);
318 | ScrollOffset = Math.Min(Math.Max(ScrollOffset, -skipCount), state.Run.Count - skipCount - visualSplitCount);
319 | skipCount += ScrollOffset;
320 |
321 | var i = 0;
322 | if (SplitComponents.Count >= visualSplitCount)
323 | {
324 | foreach (var split in state.Run.Skip(skipCount).Take(visualSplitCount - 1 + (Settings.AlwaysShowLastSplit ? 0 : 1)))
325 | {
326 | SplitComponents[i].Split = split;
327 | i++;
328 | }
329 | if (Settings.AlwaysShowLastSplit)
330 | SplitComponents[i].Split = state.Run.Last();
331 | }
332 |
333 | if (invalidator != null)
334 | InternalComponent.Update(invalidator, state, width, height, mode);
335 | }
336 |
337 | public void Dispose()
338 | {
339 | }
340 |
341 | public int GetSettingsHashCode() => Settings.GetSettingsHashCode();
342 | }
343 | }
344 |
--------------------------------------------------------------------------------
/UI/Components/GradedSplitComponent.cs:
--------------------------------------------------------------------------------
1 | using LiveSplit.Model;
2 | using LiveSplit.TimeFormatters;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Drawing;
6 | using System.Drawing.Drawing2D;
7 | using System.Drawing.Imaging;
8 | using System.Linq;
9 | using System.Windows.Forms;
10 |
11 | namespace LiveSplit.UI.Components
12 | {
13 | public class GradedSplitComponent : IComponent
14 | {
15 | public ISegment Split { get; set; }
16 |
17 | protected SimpleLabel NameLabel { get; set; }
18 | protected SimpleLabel MeasureTimeLabel { get; set; }
19 | protected SimpleLabel MeasureDeltaLabel { get; set; }
20 | public GradedSplitsSettings Settings { get; set; }
21 |
22 | protected int FrameCount { get; set; }
23 |
24 | public GraphicsCache Cache { get; set; }
25 | private Dictionary GradedIcons = new Dictionary();
26 | protected bool NeedUpdateAll { get; set; }
27 | protected bool IsActive { get; set; }
28 |
29 | protected TimeAccuracy CurrentAccuracy { get; set; }
30 | protected TimeAccuracy CurrentDeltaAccuracy { get; set; }
31 | protected bool CurrentDropDecimals { get; set; }
32 |
33 | protected ITimeFormatter TimeFormatter { get; set; }
34 | protected ITimeFormatter DeltaTimeFormatter { get; set; }
35 |
36 | protected int IconWidth => DisplayIcon ? (int)(Settings.IconSize + 7.5f) : 0;
37 |
38 | public bool DisplayIcon { get; set; }
39 |
40 | public Image ShadowImage { get; set; }
41 | private Dictionary CustomShadowImages = new Dictionary();
42 | protected Image OldImage { get; set; }
43 |
44 | public float PaddingTop => 0f;
45 | public float PaddingLeft => 0f;
46 | public float PaddingBottom => 0f;
47 | public float PaddingRight => 0f;
48 |
49 | public IEnumerable ColumnsList { get; set; }
50 | public IList LabelsList { get; set; }
51 |
52 | public float VerticalHeight { get; set; }
53 |
54 | public float MinimumWidth
55 | => CalculateLabelsWidth() + IconWidth + 10;
56 |
57 | public float HorizontalWidth
58 | => Settings.SplitWidth + CalculateLabelsWidth() + IconWidth;
59 |
60 | public float MinimumHeight { get; set; }
61 |
62 | public IDictionary ContextMenuControls => null;
63 |
64 | public GradedSplitComponent(GradedSplitsSettings settings, IEnumerable columnsList)
65 | {
66 | NameLabel = new SimpleLabel()
67 | {
68 | HorizontalAlignment = StringAlignment.Near,
69 | X = 8,
70 | };
71 | MeasureTimeLabel = new SimpleLabel();
72 | MeasureDeltaLabel = new SimpleLabel();
73 | Settings = settings;
74 | ColumnsList = columnsList;
75 | TimeFormatter = new GradedRegularSplitTimeFormatter(Settings.SplitTimesAccuracy);
76 | DeltaTimeFormatter = new GradedDeltaSplitTimeFormatter(Settings.DeltasAccuracy, Settings.DropDecimals);
77 | MinimumHeight = 25;
78 | VerticalHeight = 31;
79 |
80 | NeedUpdateAll = true;
81 | IsActive = false;
82 |
83 | Cache = new GraphicsCache();
84 | LabelsList = new List();
85 | }
86 |
87 | private void DrawGeneral(Graphics g, LiveSplitState state, float width, float height, LayoutMode mode)
88 | {
89 | if (NeedUpdateAll)
90 | UpdateAll(state);
91 |
92 | var splitIndex = state.Run.IndexOf(Split);
93 |
94 | if (Settings.BackgroundGradient == GradedExtendedGradientType.Alternating)
95 | g.FillRectangle(new SolidBrush(
96 | splitIndex % 2 + (Settings.ShowColumnLabels ? 1 : 0) == 1
97 | ? Settings.BackgroundColor2
98 | : Settings.BackgroundColor
99 | ), 0, 0, width, height);
100 |
101 | MeasureTimeLabel.Text = TimeFormatter.Format(new TimeSpan(24, 0, 0));
102 | MeasureDeltaLabel.Text = DeltaTimeFormatter.Format(new TimeSpan(0, 9, 0, 0));
103 |
104 | MeasureTimeLabel.Font = state.LayoutSettings.TimesFont;
105 | MeasureTimeLabel.IsMonospaced = true;
106 | MeasureDeltaLabel.Font = state.LayoutSettings.TimesFont;
107 | MeasureDeltaLabel.IsMonospaced = true;
108 |
109 | MeasureTimeLabel.SetActualWidth(g);
110 | MeasureDeltaLabel.SetActualWidth(g);
111 |
112 | NameLabel.ShadowColor = state.LayoutSettings.ShadowsColor;
113 | NameLabel.OutlineColor = state.LayoutSettings.TextOutlineColor;
114 | foreach (var label in LabelsList)
115 | {
116 | label.ShadowColor = state.LayoutSettings.ShadowsColor;
117 | label.OutlineColor = state.LayoutSettings.TextOutlineColor;
118 | }
119 |
120 | if (Settings.SplitTimesAccuracy != CurrentAccuracy)
121 | {
122 | TimeFormatter = new GradedRegularSplitTimeFormatter(Settings.SplitTimesAccuracy);
123 | CurrentAccuracy = Settings.SplitTimesAccuracy;
124 | }
125 | if (Settings.DeltasAccuracy != CurrentDeltaAccuracy || Settings.DropDecimals != CurrentDropDecimals)
126 | {
127 | DeltaTimeFormatter = new GradedDeltaSplitTimeFormatter(Settings.DeltasAccuracy, Settings.DropDecimals);
128 | CurrentDeltaAccuracy = Settings.DeltasAccuracy;
129 | CurrentDropDecimals = Settings.DropDecimals;
130 | }
131 |
132 | if (Split != null)
133 | {
134 |
135 | if (mode == LayoutMode.Vertical)
136 | {
137 | NameLabel.VerticalAlignment = StringAlignment.Center;
138 | NameLabel.Y = 0;
139 | NameLabel.Height = height;
140 | foreach (var label in LabelsList)
141 | {
142 | label.VerticalAlignment = StringAlignment.Center;
143 | label.Y = 0;
144 | label.Height = height;
145 | }
146 | }
147 | else
148 | {
149 | NameLabel.VerticalAlignment = StringAlignment.Near;
150 | NameLabel.Y = 0;
151 | NameLabel.Height = 50;
152 | foreach (var label in LabelsList)
153 | {
154 | label.VerticalAlignment = StringAlignment.Far;
155 | label.Y = height - 50;
156 | label.Height = 50;
157 | }
158 | }
159 |
160 | if (IsActive)
161 | {
162 | var currentSplitBrush = new LinearGradientBrush(
163 | new PointF(0, 0),
164 | Settings.CurrentSplitGradient == GradientType.Horizontal
165 | ? new PointF(width, 0)
166 | : new PointF(0, height),
167 | Settings.CurrentSplitTopColor,
168 | Settings.CurrentSplitGradient == GradientType.Plain
169 | ? Settings.CurrentSplitTopColor
170 | : Settings.CurrentSplitBottomColor);
171 | g.FillRectangle(currentSplitBrush, 0, 0, width, height);
172 | }
173 |
174 | bool customizedImage;
175 | var icon = GetIcon(state, splitIndex, out customizedImage);
176 |
177 | if (DisplayIcon && icon != null)
178 | {
179 | Image shadow;
180 | if (customizedImage)
181 | {
182 | if (!CustomShadowImages.TryGetValue(icon, out shadow))
183 | {
184 | shadow = IconShadow.Generate(icon, state.LayoutSettings.ShadowsColor);
185 | CustomShadowImages.Add(icon, shadow);
186 | }
187 | }
188 | else
189 | {
190 | shadow = ShadowImage;
191 | }
192 |
193 | if (OldImage != icon)
194 | {
195 | ImageAnimator.Animate(icon, (s, o) => { });
196 | ImageAnimator.Animate(shadow, (s, o) => { });
197 | OldImage = icon;
198 | }
199 |
200 | var drawWidth = Settings.IconSize;
201 | var drawHeight = Settings.IconSize;
202 | var shadowWidth = Settings.IconSize * (5 / 4f);
203 | var shadowHeight = Settings.IconSize * (5 / 4f);
204 | if (icon.Width > icon.Height)
205 | {
206 | var ratio = icon.Height / (float)icon.Width;
207 | drawHeight *= ratio;
208 | shadowHeight *= ratio;
209 | }
210 | else
211 | {
212 | var ratio = icon.Width / (float)icon.Height;
213 | drawWidth *= ratio;
214 | shadowWidth *= ratio;
215 | }
216 |
217 | ImageAnimator.UpdateFrames(shadow);
218 | if (Settings.IconShadows && shadow != null)
219 | {
220 | g.DrawImage(
221 | shadow,
222 | 7 + (Settings.IconSize * (5 / 4f) - shadowWidth) / 2 - 0.7f,
223 | (height - Settings.IconSize) / 2.0f + (Settings.IconSize * (5 / 4f) - shadowHeight) / 2 - 0.7f,
224 | shadowWidth,
225 | shadowHeight);
226 | }
227 |
228 | ImageAnimator.UpdateFrames(icon);
229 |
230 | g.DrawImage(
231 | icon,
232 | 7 + (Settings.IconSize - drawWidth) / 2,
233 | (height - Settings.IconSize) / 2.0f + (Settings.IconSize - drawHeight) / 2,
234 | drawWidth,
235 | drawHeight);
236 | }
237 |
238 | NameLabel.Font = state.LayoutSettings.TextFont;
239 | NameLabel.X = 5 + IconWidth;
240 | NameLabel.HasShadow = state.LayoutSettings.DropShadows;
241 |
242 | if (ColumnsList.Count() == LabelsList.Count)
243 | {
244 | var curX = width - 7;
245 | var nameX = width - 7;
246 | foreach (var label in LabelsList.Reverse())
247 | {
248 | var column = ColumnsList.ElementAt(LabelsList.IndexOf(label));
249 |
250 | var labelWidth = 0f;
251 | if (column.Type == GradedColumnType.DeltaorSplitTime || column.Type == GradedColumnType.SegmentDeltaorSegmentTime)
252 | labelWidth = Math.Max(MeasureDeltaLabel.ActualWidth, MeasureTimeLabel.ActualWidth);
253 | else if (column.Type == GradedColumnType.Delta || column.Type == GradedColumnType.SegmentDelta)
254 | labelWidth = MeasureDeltaLabel.ActualWidth;
255 | else
256 | labelWidth = MeasureTimeLabel.ActualWidth;
257 | label.Width = labelWidth + 20;
258 | curX -= labelWidth + 5;
259 | label.X = curX - 15;
260 |
261 | label.Font = state.LayoutSettings.TimesFont;
262 | label.HasShadow = state.LayoutSettings.DropShadows;
263 | label.IsMonospaced = true;
264 | label.Draw(g);
265 |
266 | if (!string.IsNullOrEmpty(label.Text))
267 | nameX = curX + labelWidth + 5 - label.ActualWidth;
268 |
269 | }
270 | NameLabel.Width = (mode == LayoutMode.Horizontal ? width - 10 : nameX) - IconWidth;
271 | NameLabel.Draw(g);
272 | }
273 | }
274 | else DisplayIcon = Settings.DisplayIcons;
275 | }
276 |
277 | private Image GetIcon(LiveSplitState state, int splitIndex, out bool customizedIcon)
278 | {
279 | var icon = Split.Icon;
280 | customizedIcon = true;
281 |
282 | if (this.Settings.DisplayIcons)
283 | {
284 | var attemptIconOverride = false;
285 | var currentSegmantHasBeenSplit = (state.CurrentSplitIndex > splitIndex);
286 | var updateBasedOnCurrentRun = (this.Settings.GradedIconsApplicationState == GradedIconsApplicationState.ComparisonAndCurrentRun ||
287 | this.Settings.GradedIconsApplicationState == GradedIconsApplicationState.CurrentRun);
288 |
289 | var updateBasedOnComparison = (this.Settings.GradedIconsApplicationState == GradedIconsApplicationState.ComparisonAndCurrentRun ||
290 | this.Settings.GradedIconsApplicationState == GradedIconsApplicationState.Comparison);
291 |
292 | if (updateBasedOnComparison)
293 | {
294 | attemptIconOverride = true;
295 | }
296 | else if (updateBasedOnCurrentRun && currentSegmantHasBeenSplit)
297 | {
298 | attemptIconOverride = true;
299 | }
300 |
301 | // If this split has already gone past:
302 | if (attemptIconOverride)
303 | {
304 | var splitState = SplitState.Unknown;
305 | var previousSegmentTime = LiveSplitStateHelper.GetPreviousSegmentTime(state, splitIndex, state.CurrentTimingMethod);
306 |
307 | var overrideAsSkippedPBSplit = false;
308 |
309 | // Use the skipped split icon if they specified to override for the current run
310 | // if the segment has been split (and skipped)
311 | if (previousSegmentTime == null &&
312 | currentSegmantHasBeenSplit &&
313 | updateBasedOnCurrentRun)
314 | {
315 | overrideAsSkippedPBSplit = true;
316 | }
317 | // Use the skipped split icon if they specified to override compared to the PB
318 | else if (updateBasedOnComparison)
319 | {
320 | // Don't override whatever's in PB if they've split this segment and specificed to update on teh current run
321 | // We don't want a skipped PB segment to cause a "return" later on here.
322 | if (!(currentSegmantHasBeenSplit && updateBasedOnCurrentRun))
323 | {
324 | // the PB was a skipped split?
325 | var PBSplit = state.Run[splitIndex];
326 | if (PBSplit != null)
327 | {
328 | var PBSplittime = PBSplit.PersonalBestSplitTime;
329 | if (state.CurrentTimingMethod == TimingMethod.GameTime)
330 | {
331 | overrideAsSkippedPBSplit = (PBSplittime.GameTime == null);
332 | }
333 | else if (state.CurrentTimingMethod == TimingMethod.RealTime)
334 | {
335 | overrideAsSkippedPBSplit = (PBSplittime.RealTime == null);
336 | }
337 | }
338 | }
339 | }
340 |
341 | if (overrideAsSkippedPBSplit &&
342 | this.Settings.SkippedSplitIcon != null &&
343 | this.Settings.SkippedSplitIcon.IconState == GradedIconState.Default)
344 | {
345 | if (string.IsNullOrWhiteSpace(this.Settings.SkippedSplitIcon.Base64Bytes))
346 | {
347 | icon = null;
348 | }
349 | else
350 | {
351 | // It was a skipped split
352 | Image cached;
353 | if (!GradedIcons.TryGetValue(this.Settings.SkippedSplitIcon.Base64Bytes + "_ss", out cached))
354 | {
355 | icon = getBitmapFromBase64(this.Settings.SkippedSplitIcon.Base64Bytes);
356 | GradedIcons.Add(this.Settings.SkippedSplitIcon.Base64Bytes + "_ss", icon);
357 | }
358 | else
359 | {
360 | icon = cached;
361 | }
362 | }
363 |
364 | customizedIcon = true;
365 | return icon;
366 | }
367 |
368 |
369 | var getCurrentSplitPercentageBehindBestSegment = new Func(() =>
370 | {
371 | // say your best segment is 100s
372 | // it makes sense to do like < 105 = A
373 | // < 110 = B
374 | // 0.05 = (105-100)/100
375 | double bestMilliseconds = 0;
376 | if (state.CurrentTimingMethod == TimingMethod.GameTime)
377 | {
378 | bestMilliseconds = Split.BestSegmentTime.GameTime?.TotalMilliseconds ?? 0;
379 | }
380 | else if (state.CurrentTimingMethod == TimingMethod.RealTime)
381 | {
382 | bestMilliseconds = Split.BestSegmentTime.RealTime?.TotalMilliseconds ?? 0;
383 | }
384 |
385 | // No current best segment, gold split guaranteed A+?
386 | if (bestMilliseconds <= 0)
387 | {
388 | return 0;
389 | }
390 |
391 | double currentMilliseconds = 0;
392 | if (currentSegmantHasBeenSplit && updateBasedOnCurrentRun)
393 | {
394 | if (previousSegmentTime != null)
395 | {
396 | currentMilliseconds = previousSegmentTime.Value.TotalMilliseconds;
397 | }
398 | }
399 | else
400 | {
401 | double personalBestTimeMilliseconds = 0;
402 | double priorsplitMilliseconds = 0;
403 | if (state.CurrentTimingMethod == TimingMethod.GameTime)
404 | {
405 | personalBestTimeMilliseconds = Split.PersonalBestSplitTime.GameTime?.TotalMilliseconds ?? 0;
406 | if (splitIndex > 0) // At index 0, there is no prior split, and the PB split IS the total ms for it
407 | {
408 | priorsplitMilliseconds = state.Run[splitIndex - 1].PersonalBestSplitTime.GameTime?.TotalMilliseconds ?? 0;
409 | }
410 | }
411 | else if (state.CurrentTimingMethod == TimingMethod.RealTime)
412 | {
413 | personalBestTimeMilliseconds = Split.PersonalBestSplitTime.RealTime?.TotalMilliseconds ?? 0;
414 | if (splitIndex > 0) // At index 0, there is no prior split, and the PB split IS the total ms for it
415 | {
416 | priorsplitMilliseconds = state.Run[splitIndex - 1].PersonalBestSplitTime.RealTime?.TotalMilliseconds ?? 0;
417 | }
418 | }
419 |
420 | currentMilliseconds = (personalBestTimeMilliseconds - priorsplitMilliseconds);
421 | }
422 |
423 | // you were super amazing or i messed up:
424 | if (currentMilliseconds <= 0)
425 | {
426 | return -1;
427 | }
428 |
429 | return Convert.ToDecimal((((currentMilliseconds - bestMilliseconds) / bestMilliseconds) * ((double)100)));
430 | });
431 |
432 | if ((!string.IsNullOrWhiteSpace(this.Settings.BehindLosingTimeIcon.Base64Bytes) &&
433 | this.Settings.BehindLosingTimeIcon.IconState == GradedIconState.Default) ||
434 | (!string.IsNullOrWhiteSpace(this.Settings.BehindGainingTimeIcon.Base64Bytes) &&
435 | this.Settings.BehindGainingTimeIcon.IconState == GradedIconState.Default) ||
436 | (!string.IsNullOrWhiteSpace(this.Settings.AheadLosingTimeIcon.Base64Bytes) &&
437 | this.Settings.AheadLosingTimeIcon.IconState == GradedIconState.Default) ||
438 | (!string.IsNullOrWhiteSpace(this.Settings.AheadGainingTimeIcon.Base64Bytes) &&
439 | this.Settings.AheadGainingTimeIcon.IconState == GradedIconState.Default) ||
440 | (!string.IsNullOrWhiteSpace(this.Settings.BestSegmentIcon.Base64Bytes) &&
441 | this.Settings.BestSegmentIcon.IconState == GradedIconState.Default))
442 | {
443 | var segmentDelta = LiveSplitStateHelper.GetPreviousSegmentDelta(state, splitIndex, state.CurrentComparison, state.CurrentTimingMethod);
444 | splitState = this.GetSplitState(state, segmentDelta, splitIndex, state.CurrentComparison, state.CurrentTimingMethod);
445 | }
446 |
447 | var iconToUse = SplitState.Unknown;
448 |
449 | if (!string.IsNullOrWhiteSpace(this.Settings.BehindLosingTimeIcon.Base64Bytes) &&
450 | this.Settings.BehindLosingTimeIcon.IconState != GradedIconState.Disabled)
451 | {
452 | if (this.Settings.BehindLosingTimeIcon.IconState == GradedIconState.Default && splitState == SplitState.BehindLosing)
453 | {
454 | iconToUse = splitState;
455 | }
456 | else if (this.Settings.BehindLosingTimeIcon.IconState == GradedIconState.PercentageSplit)
457 | {
458 | var percentage = getCurrentSplitPercentageBehindBestSegment();
459 | if (percentage < this.Settings.BehindLosingTimeIcon.PercentageBehind)
460 | {
461 | iconToUse = SplitState.BehindLosing;
462 | }
463 | }
464 | }
465 |
466 | if (!string.IsNullOrWhiteSpace(this.Settings.BehindGainingTimeIcon.Base64Bytes) &&
467 | this.Settings.BehindGainingTimeIcon.IconState != GradedIconState.Disabled)
468 | {
469 | if (this.Settings.BehindGainingTimeIcon.IconState == GradedIconState.Default && splitState == SplitState.BehindGaining)
470 | {
471 | iconToUse = splitState;
472 | }
473 | else if (this.Settings.BehindGainingTimeIcon.IconState == GradedIconState.PercentageSplit)
474 | {
475 | var percentage = getCurrentSplitPercentageBehindBestSegment();
476 | if (percentage < this.Settings.BehindGainingTimeIcon.PercentageBehind)
477 | {
478 | iconToUse = SplitState.BehindGaining;
479 | }
480 | }
481 | }
482 |
483 | if (!string.IsNullOrWhiteSpace(this.Settings.AheadLosingTimeIcon.Base64Bytes) &&
484 | this.Settings.AheadLosingTimeIcon.IconState != GradedIconState.Disabled)
485 | {
486 | if (this.Settings.AheadLosingTimeIcon.IconState == GradedIconState.Default && splitState == SplitState.AheadLosing)
487 | {
488 | iconToUse = splitState;
489 | }
490 | else if (this.Settings.AheadLosingTimeIcon.IconState == GradedIconState.PercentageSplit)
491 | {
492 | var percentage = getCurrentSplitPercentageBehindBestSegment();
493 | if (percentage < this.Settings.AheadLosingTimeIcon.PercentageBehind)
494 | {
495 | iconToUse = SplitState.AheadLosing;
496 | }
497 | }
498 | }
499 |
500 | if (!string.IsNullOrWhiteSpace(this.Settings.AheadGainingTimeIcon.Base64Bytes) &&
501 | this.Settings.AheadGainingTimeIcon.IconState != GradedIconState.Disabled)
502 | {
503 | if (this.Settings.AheadGainingTimeIcon.IconState == GradedIconState.Default && splitState == SplitState.AheadGaining)
504 | {
505 | iconToUse = splitState;
506 | }
507 | else if (this.Settings.AheadGainingTimeIcon.IconState == GradedIconState.PercentageSplit)
508 | {
509 | var percentage = getCurrentSplitPercentageBehindBestSegment();
510 | if (percentage < this.Settings.AheadGainingTimeIcon.PercentageBehind)
511 | {
512 | iconToUse = SplitState.AheadGaining;
513 | }
514 | }
515 | }
516 |
517 | if (!string.IsNullOrWhiteSpace(this.Settings.BestSegmentIcon.Base64Bytes) &&
518 | this.Settings.BestSegmentIcon.IconState != GradedIconState.Disabled)
519 | {
520 | if (this.Settings.BestSegmentIcon.IconState == GradedIconState.Default && splitState == SplitState.BestSegment)
521 | {
522 | iconToUse = splitState;
523 | }
524 | else if (this.Settings.BestSegmentIcon.IconState == GradedIconState.PercentageSplit)
525 | {
526 | var percentage = getCurrentSplitPercentageBehindBestSegment();
527 |
528 | if (percentage < this.Settings.BestSegmentIcon.PercentageBehind)
529 | {
530 | iconToUse = SplitState.BestSegment;
531 | }
532 | }
533 | }
534 |
535 | if (iconToUse == SplitState.BestSegment)
536 | {
537 | Image cached;
538 | if (!GradedIcons.TryGetValue(this.Settings.BestSegmentIcon.Base64Bytes + "_1", out cached))
539 | {
540 | icon = getBitmapFromBase64(this.Settings.BestSegmentIcon.Base64Bytes);
541 | GradedIcons.Add(this.Settings.BestSegmentIcon.Base64Bytes + "_1", icon);
542 | }
543 | else
544 | {
545 | icon = cached;
546 | }
547 |
548 | customizedIcon = true;
549 | }
550 | else if (iconToUse == SplitState.AheadGaining)
551 | {
552 | Image cached;
553 | if (!GradedIcons.TryGetValue(this.Settings.AheadGainingTimeIcon.Base64Bytes + "_2", out cached))
554 | {
555 | icon = getBitmapFromBase64(this.Settings.AheadGainingTimeIcon.Base64Bytes);
556 | GradedIcons.Add(this.Settings.AheadGainingTimeIcon.Base64Bytes + "_2", icon);
557 | }
558 | else
559 | {
560 | icon = cached;
561 | }
562 |
563 | customizedIcon = true;
564 | }
565 | else if (iconToUse == SplitState.AheadLosing)
566 | {
567 | Image cached;
568 | if (!GradedIcons.TryGetValue(this.Settings.AheadLosingTimeIcon.Base64Bytes + "_3", out cached))
569 | {
570 | icon = getBitmapFromBase64(this.Settings.AheadLosingTimeIcon.Base64Bytes);
571 | GradedIcons.Add(this.Settings.AheadLosingTimeIcon.Base64Bytes + "_3", icon);
572 | }
573 | else
574 | {
575 | icon = cached;
576 | }
577 |
578 | customizedIcon = true;
579 | }
580 | else if (iconToUse == SplitState.BehindGaining)
581 | {
582 | Image cached;
583 | if (!GradedIcons.TryGetValue(this.Settings.BehindGainingTimeIcon.Base64Bytes + "_4", out cached))
584 | {
585 | icon = getBitmapFromBase64(this.Settings.BehindGainingTimeIcon.Base64Bytes);
586 | GradedIcons.Add(this.Settings.BehindGainingTimeIcon.Base64Bytes + "_4", icon);
587 | }
588 | else
589 | {
590 | icon = cached;
591 | }
592 |
593 | customizedIcon = true;
594 | }
595 | else if (iconToUse == SplitState.BehindLosing)
596 | {
597 | Image cached;
598 | if (!GradedIcons.TryGetValue(this.Settings.BehindLosingTimeIcon.Base64Bytes + "_5", out cached))
599 | {
600 | icon = getBitmapFromBase64(this.Settings.BehindLosingTimeIcon.Base64Bytes);
601 | GradedIcons.Add(this.Settings.BehindLosingTimeIcon.Base64Bytes + "_5", icon);
602 | }
603 | else
604 | {
605 | icon = cached;
606 | }
607 |
608 | customizedIcon = true;
609 | }
610 | }
611 | }
612 |
613 | return icon;
614 | }
615 |
616 |
617 | private Image getBitmapFromBase64(string imageBytes)
618 | {
619 | var byteArr = Convert.FromBase64String(imageBytes);
620 | using (var ms = new System.IO.MemoryStream(byteArr))
621 | {
622 | return Image.FromStream(ms);
623 | }
624 | }
625 |
626 |
627 |
628 | public void DrawVertical(Graphics g, LiveSplitState state, float width, Region clipRegion)
629 | {
630 | if (Settings.Display2Rows)
631 | {
632 | VerticalHeight = Settings.SplitHeight + 0.85f * (g.MeasureString("A", state.LayoutSettings.TimesFont).Height + g.MeasureString("A", state.LayoutSettings.TextFont).Height);
633 | DrawGeneral(g, state, width, VerticalHeight, LayoutMode.Horizontal);
634 | }
635 | else
636 | {
637 | VerticalHeight = Settings.SplitHeight + 25;
638 | DrawGeneral(g, state, width, VerticalHeight, LayoutMode.Vertical);
639 | }
640 | }
641 |
642 | public void DrawHorizontal(Graphics g, LiveSplitState state, float height, Region clipRegion)
643 | {
644 | MinimumHeight = 0.85f * (g.MeasureString("A", state.LayoutSettings.TimesFont).Height + g.MeasureString("A", state.LayoutSettings.TextFont).Height);
645 | DrawGeneral(g, state, HorizontalWidth, height, LayoutMode.Horizontal);
646 | }
647 |
648 | public string ComponentName => "Split";
649 |
650 |
651 | public Control GetSettingsControl(LayoutMode mode)
652 | {
653 | throw new NotSupportedException();
654 | }
655 |
656 | public void SetSettings(System.Xml.XmlNode settings)
657 | {
658 | throw new NotSupportedException();
659 | }
660 |
661 |
662 | public System.Xml.XmlNode GetSettings(System.Xml.XmlDocument document)
663 | {
664 | throw new NotSupportedException();
665 | }
666 |
667 | public string UpdateName
668 | {
669 | get { throw new NotSupportedException(); }
670 | }
671 |
672 | public string XMLURL
673 | {
674 | get { throw new NotSupportedException(); }
675 | }
676 |
677 | public string UpdateURL
678 | {
679 | get { throw new NotSupportedException(); }
680 | }
681 |
682 | public Version Version
683 | {
684 | get { throw new NotSupportedException(); }
685 | }
686 |
687 | protected void UpdateAll(LiveSplitState state)
688 | {
689 | if (Split != null)
690 | {
691 | RecreateLabels();
692 |
693 | if (Settings.AutomaticAbbreviations)
694 | {
695 | if (NameLabel.Text != Split.Name || NameLabel.AlternateText == null || !NameLabel.AlternateText.Any())
696 | NameLabel.AlternateText = Split.Name.GetAbbreviations().ToList();
697 | }
698 | else if (NameLabel.AlternateText != null && NameLabel.AlternateText.Any())
699 | NameLabel.AlternateText.Clear();
700 |
701 | NameLabel.Text = Split.Name;
702 |
703 | var splitIndex = state.Run.IndexOf(Split);
704 | if (splitIndex < state.CurrentSplitIndex)
705 | {
706 | NameLabel.ForeColor = Settings.OverrideTextColor ? Settings.BeforeNamesColor : state.LayoutSettings.TextColor;
707 | }
708 | else
709 | {
710 | if (Split == state.CurrentSplit)
711 | NameLabel.ForeColor = Settings.OverrideTextColor ? Settings.CurrentNamesColor : state.LayoutSettings.TextColor;
712 | else
713 | NameLabel.ForeColor = Settings.OverrideTextColor ? Settings.AfterNamesColor : state.LayoutSettings.TextColor;
714 | }
715 |
716 | foreach (var label in LabelsList)
717 | {
718 | var column = ColumnsList.ElementAt(LabelsList.IndexOf(label));
719 | UpdateColumn(state, label, column);
720 | }
721 | }
722 | }
723 |
724 | protected void UpdateColumn(LiveSplitState state, SimpleLabel label, GradedColumnData data)
725 | {
726 | var comparison = data.Comparison == "Current Comparison" ? state.CurrentComparison : data.Comparison;
727 | if (!state.Run.Comparisons.Contains(comparison))
728 | comparison = state.CurrentComparison;
729 |
730 | var timingMethod = state.CurrentTimingMethod;
731 | if (data.TimingMethod == "Real Time")
732 | timingMethod = TimingMethod.RealTime;
733 | else if (data.TimingMethod == "Game Time")
734 | timingMethod = TimingMethod.GameTime;
735 |
736 | var type = data.Type;
737 |
738 | var splitIndex = state.Run.IndexOf(Split);
739 | if (splitIndex < state.CurrentSplitIndex)
740 | {
741 | if (type == GradedColumnType.SplitTime || type == GradedColumnType.SegmentTime)
742 | {
743 | label.ForeColor = Settings.OverrideTimesColor ? Settings.BeforeTimesColor : state.LayoutSettings.TextColor;
744 |
745 | if (type == GradedColumnType.SplitTime)
746 | {
747 | label.Text = TimeFormatter.Format(Split.SplitTime[timingMethod]);
748 | }
749 | else //SegmentTime
750 | {
751 | var segmentTime = LiveSplitStateHelper.GetPreviousSegmentTime(state, splitIndex, timingMethod);
752 | label.Text = TimeFormatter.Format(segmentTime);
753 | }
754 | }
755 |
756 | if (type == GradedColumnType.DeltaorSplitTime || type == GradedColumnType.Delta)
757 | {
758 | var deltaTime = Split.SplitTime[timingMethod] - Split.Comparisons[comparison][timingMethod];
759 | var color = LiveSplitStateHelper.GetSplitColor(state, deltaTime, splitIndex, true, true, comparison, timingMethod);
760 | if (color == null)
761 | color = Settings.OverrideTimesColor ? Settings.BeforeTimesColor : state.LayoutSettings.TextColor;
762 | label.ForeColor = color.Value;
763 |
764 | if (type == GradedColumnType.DeltaorSplitTime)
765 | {
766 | if (deltaTime != null)
767 | label.Text = DeltaTimeFormatter.Format(deltaTime);
768 | else
769 | label.Text = TimeFormatter.Format(Split.SplitTime[timingMethod]);
770 | }
771 |
772 | else if (type == GradedColumnType.Delta)
773 | label.Text = DeltaTimeFormatter.Format(deltaTime);
774 | }
775 |
776 | else if (type == GradedColumnType.SegmentDeltaorSegmentTime || type == GradedColumnType.SegmentDelta)
777 | {
778 | var segmentDelta = LiveSplitStateHelper.GetPreviousSegmentDelta(state, splitIndex, comparison, timingMethod);
779 | var color = LiveSplitStateHelper.GetSplitColor(state, segmentDelta, splitIndex, false, true, comparison, timingMethod);
780 | if (color == null)
781 | color = Settings.OverrideTimesColor ? Settings.BeforeTimesColor : state.LayoutSettings.TextColor;
782 | label.ForeColor = color.Value;
783 |
784 | if (type == GradedColumnType.SegmentDeltaorSegmentTime)
785 | {
786 | if (segmentDelta != null)
787 | label.Text = DeltaTimeFormatter.Format(segmentDelta);
788 | else
789 | label.Text = TimeFormatter.Format(LiveSplitStateHelper.GetPreviousSegmentTime(state, splitIndex, timingMethod));
790 | }
791 | else if (type == GradedColumnType.SegmentDelta)
792 | {
793 | label.Text = DeltaTimeFormatter.Format(segmentDelta);
794 | }
795 | }
796 | }
797 | else
798 | {
799 | if (type == GradedColumnType.SplitTime || type == GradedColumnType.SegmentTime || type == GradedColumnType.DeltaorSplitTime || type == GradedColumnType.SegmentDeltaorSegmentTime)
800 | {
801 | if (Split == state.CurrentSplit)
802 | label.ForeColor = Settings.OverrideTimesColor ? Settings.CurrentTimesColor : state.LayoutSettings.TextColor;
803 | else
804 | label.ForeColor = Settings.OverrideTimesColor ? Settings.AfterTimesColor : state.LayoutSettings.TextColor;
805 |
806 | if (type == GradedColumnType.SplitTime || type == GradedColumnType.DeltaorSplitTime)
807 | {
808 | label.Text = TimeFormatter.Format(Split.Comparisons[comparison][timingMethod]);
809 | }
810 | else //SegmentTime or SegmentTimeorSegmentDeltaTime
811 | {
812 | var previousTime = TimeSpan.Zero;
813 | for (var index = splitIndex - 1; index >= 0; index --)
814 | {
815 | var comparisonTime = state.Run[index].Comparisons[comparison][timingMethod];
816 | if (comparisonTime != null)
817 | {
818 | previousTime = comparisonTime.Value;
819 | break;
820 | }
821 | }
822 | label.Text = TimeFormatter.Format(Split.Comparisons[comparison][timingMethod] - previousTime);
823 | }
824 | }
825 |
826 | //Live Delta
827 | var splitDelta = type == GradedColumnType.DeltaorSplitTime || type == GradedColumnType.Delta;
828 | var bestDelta = LiveSplitStateHelper.CheckLiveDelta(state, splitDelta, comparison, timingMethod);
829 | if (bestDelta != null && Split == state.CurrentSplit &&
830 | (type == GradedColumnType.DeltaorSplitTime || type == GradedColumnType.Delta || type == GradedColumnType.SegmentDeltaorSegmentTime || type == GradedColumnType.SegmentDelta))
831 | {
832 | label.Text = DeltaTimeFormatter.Format(bestDelta);
833 | label.ForeColor = Settings.OverrideDeltasColor ? Settings.DeltasColor : state.LayoutSettings.TextColor;
834 | }
835 | else if (type == GradedColumnType.Delta || type == GradedColumnType.SegmentDelta)
836 | {
837 | label.Text = "";
838 | }
839 | }
840 | }
841 |
842 | protected float CalculateLabelsWidth()
843 | {
844 | if (ColumnsList != null)
845 | {
846 | var mixedCount = ColumnsList.Count(x => x.Type == GradedColumnType.DeltaorSplitTime || x.Type == GradedColumnType.SegmentDeltaorSegmentTime);
847 | var deltaCount = ColumnsList.Count(x => x.Type == GradedColumnType.Delta || x.Type == GradedColumnType.SegmentDelta);
848 | var timeCount = ColumnsList.Count(x => x.Type == GradedColumnType.SplitTime || x.Type == GradedColumnType.SegmentTime);
849 | return mixedCount * (Math.Max(MeasureDeltaLabel.ActualWidth, MeasureTimeLabel.ActualWidth) + 5)
850 | + deltaCount * (MeasureDeltaLabel.ActualWidth + 5)
851 | + timeCount * (MeasureTimeLabel.ActualWidth + 5);
852 | }
853 | return 0f;
854 | }
855 |
856 | protected void RecreateLabels()
857 | {
858 | if (ColumnsList != null && LabelsList.Count != ColumnsList.Count())
859 | {
860 | LabelsList.Clear();
861 | foreach (var column in ColumnsList)
862 | {
863 | LabelsList.Add(new SimpleLabel
864 | {
865 | HorizontalAlignment = StringAlignment.Far
866 | });
867 | }
868 | }
869 | }
870 |
871 | public void Update(IInvalidator invalidator, LiveSplitState state, float width, float height, LayoutMode mode)
872 | {
873 | if (Split != null)
874 | {
875 | UpdateAll(state);
876 | NeedUpdateAll = false;
877 |
878 | IsActive = (state.CurrentPhase == TimerPhase.Running
879 | || state.CurrentPhase == TimerPhase.Paused) &&
880 | state.CurrentSplit == Split;
881 |
882 | Cache.Restart();
883 | Cache["Icon"] = this.GetIcon(state, state.Run.IndexOf(Split), out bool _ci);
884 | if (Cache.HasChanged)
885 | {
886 | if (Split.Icon == null)
887 | FrameCount = 0;
888 | else
889 | FrameCount = Split.Icon.GetFrameCount(new FrameDimension(Split.Icon.FrameDimensionsList[0]));
890 | }
891 | Cache["DisplayIcon"] = DisplayIcon;
892 | Cache["SplitName"] = NameLabel.Text;
893 | Cache["IsActive"] = IsActive;
894 | Cache["NameColor"] = NameLabel.ForeColor.ToArgb();
895 | Cache["ColumnsCount"] = ColumnsList.Count();
896 | foreach (var label in LabelsList)
897 | {
898 | Cache["Columns" + LabelsList.IndexOf(label) + "Text"] = label.Text;
899 | Cache["Columns" + LabelsList.IndexOf(label) + "Color"] = label.ForeColor.ToArgb();
900 | }
901 |
902 | if (invalidator != null && (Cache.HasChanged || FrameCount > 1))
903 | {
904 | invalidator.Invalidate(0, 0, width, height);
905 | }
906 | }
907 | }
908 |
909 | public void Dispose()
910 | {
911 | }
912 |
913 | private SplitState GetSplitState(
914 | LiveSplitState state,
915 | TimeSpan? timeDifference,
916 | int splitNumber,
917 | string comparison,
918 | TimingMethod method)
919 | {
920 | SplitState splitState = SplitState.Unknown;
921 | if (splitNumber < 0)
922 | return splitState;
923 |
924 | if (timeDifference != null)
925 | {
926 | if (timeDifference < TimeSpan.Zero)
927 | {
928 | splitState = SplitState.AheadGaining;
929 | var lastDelta = LiveSplitStateHelper.GetLastDelta(state, splitNumber - 1, comparison, method);
930 | if (splitNumber > 0 && lastDelta != null && timeDifference > lastDelta)
931 | splitState = SplitState.AheadLosing;
932 | }
933 | else
934 | {
935 | splitState = SplitState.BehindLosing;
936 | var lastDelta = LiveSplitStateHelper.GetLastDelta(state, splitNumber - 1, comparison, method);
937 | if (splitNumber > 0 && lastDelta != null && timeDifference < lastDelta)
938 | splitState = SplitState.BehindGaining;
939 | }
940 | }
941 |
942 | if (LiveSplitStateHelper.CheckBestSegment(state, splitNumber, method))
943 | {
944 | splitState = SplitState.BestSegment;
945 | }
946 |
947 | return splitState;
948 | }
949 |
950 | private enum SplitState
951 | {
952 | Unknown,
953 | BestSegment,
954 | AheadGaining,
955 | AheadLosing,
956 | BehindGaining,
957 | BehindLosing,
958 | }
959 | }
960 | }
961 |
--------------------------------------------------------------------------------
/UI/Components/GradedSplitsSettings.cs:
--------------------------------------------------------------------------------
1 | using LiveSplit.Model;
2 | using LiveSplit.TimeFormatters;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Drawing;
6 | using System.IO;
7 | using System.Linq;
8 | using System.Windows.Forms;
9 | using System.Xml;
10 |
11 | namespace LiveSplit.UI.Components
12 | {
13 | public partial class GradedSplitsSettings : UserControl
14 | {
15 | private int _VisualSplitCount { get; set; }
16 | public int VisualSplitCount
17 | {
18 | get { return _VisualSplitCount; }
19 | set
20 | {
21 | _VisualSplitCount = value;
22 | var max = Math.Max(0, _VisualSplitCount - (AlwaysShowLastSplit ? 2 : 1));
23 | if (dmnUpcomingSegments.Value > max)
24 | dmnUpcomingSegments.Value = max;
25 | dmnUpcomingSegments.Maximum = max;
26 | }
27 | }
28 | public Color CurrentSplitTopColor { get; set; }
29 | public Color CurrentSplitBottomColor { get; set; }
30 | public int SplitPreviewCount { get; set; }
31 | public float SplitWidth { get; set; }
32 | public float SplitHeight { get; set; }
33 | public float ScaledSplitHeight { get { return SplitHeight * 10f; } set { SplitHeight = value / 10f; } }
34 | public float IconSize { get; set; }
35 |
36 | public bool Display2Rows { get; set; }
37 |
38 | public Color BackgroundColor { get; set; }
39 | public Color BackgroundColor2 { get; set; }
40 |
41 | public GradedExtendedGradientType BackgroundGradient { get; set; }
42 | public string GradientString
43 | {
44 | get { return BackgroundGradient.ToString(); }
45 | set { BackgroundGradient = (GradedExtendedGradientType)Enum.Parse(typeof(GradedExtendedGradientType), value); }
46 | }
47 |
48 | public LiveSplitState CurrentState { get; set; }
49 |
50 | public bool DisplayIcons { get; set; }
51 | public bool IconShadows { get; set; }
52 | public bool ShowThinSeparators { get; set; }
53 | public bool AlwaysShowLastSplit { get; set; }
54 | public bool ShowBlankSplits { get; set; }
55 | public bool LockLastSplit { get; set; }
56 | public bool SeparatorLastSplit { get; set; }
57 |
58 | public bool DropDecimals { get; set; }
59 | public TimeAccuracy DeltasAccuracy { get; set; }
60 |
61 | public bool OverrideDeltasColor { get; set; }
62 | public Color DeltasColor { get; set; }
63 |
64 | public bool ShowColumnLabels { get; set; }
65 | public Color LabelsColor { get; set; }
66 |
67 | public bool AutomaticAbbreviations { get; set; }
68 | public Color BeforeNamesColor { get; set; }
69 | public Color CurrentNamesColor { get; set; }
70 | public Color AfterNamesColor { get; set; }
71 | public bool OverrideTextColor { get; set; }
72 | public Color BeforeTimesColor { get; set; }
73 | public Color CurrentTimesColor { get; set; }
74 | public Color AfterTimesColor { get; set; }
75 | public bool OverrideTimesColor { get; set; }
76 |
77 | public TimeAccuracy SplitTimesAccuracy { get; set; }
78 | public GradientType CurrentSplitGradient { get; set; }
79 | public string SplitGradientString { get { return CurrentSplitGradient.ToString(); }
80 | set { CurrentSplitGradient = (GradientType)Enum.Parse(typeof(GradientType), value); } }
81 |
82 | public event EventHandler SplitLayoutChanged;
83 |
84 | public LayoutMode Mode { get; set; }
85 |
86 | public IList ColumnsList { get; set; }
87 | public Size StartingSize { get; set; }
88 | public Size StartingTableLayoutSize { get; set; }
89 |
90 | public GradedIcon BestSegmentIcon { get; set; }
91 | public GradedIcon AheadGainingTimeIcon { get; set; }
92 | public GradedIcon AheadLosingTimeIcon { get; set; }
93 | public GradedIcon BehindGainingTimeIcon { get; set; }
94 | public GradedIcon BehindLosingTimeIcon { get; set; }
95 | public GradedIcon SkippedSplitIcon { get; set; }
96 |
97 | public GradedIconsApplicationState GradedIconsApplicationState { get; set; }
98 |
99 | public class GradedIcon
100 | {
101 | public string Base64Bytes { get; set; }
102 | public GradedIconState IconState { get; set; }
103 | public decimal PercentageBehind { get; set; }
104 | }
105 |
106 |
107 | public GradedSplitsSettings(LiveSplitState state)
108 | {
109 | InitializeComponent();
110 |
111 | CurrentState = state;
112 |
113 | StartingSize = Size;
114 | StartingTableLayoutSize = tableColumns.Size;
115 |
116 | VisualSplitCount = 8;
117 | SplitPreviewCount = 1;
118 | DisplayIcons = true;
119 | IconShadows = true;
120 | ShowThinSeparators = true;
121 | AlwaysShowLastSplit = true;
122 | ShowBlankSplits = true;
123 | LockLastSplit = true;
124 | SeparatorLastSplit = true;
125 | SplitTimesAccuracy = TimeAccuracy.Seconds;
126 | CurrentSplitTopColor = Color.FromArgb(51, 115, 244);
127 | CurrentSplitBottomColor = Color.FromArgb(21, 53, 116);
128 | SplitWidth = 20;
129 | SplitHeight = 3.6f;
130 | IconSize = 24f;
131 | AutomaticAbbreviations = false;
132 | BeforeNamesColor = Color.FromArgb(255, 255, 255);
133 | CurrentNamesColor = Color.FromArgb(255, 255, 255);
134 | AfterNamesColor = Color.FromArgb(255, 255, 255);
135 | OverrideTextColor = false;
136 | BeforeTimesColor = Color.FromArgb(255, 255, 255);
137 | CurrentTimesColor = Color.FromArgb(255, 255, 255);
138 | AfterTimesColor = Color.FromArgb(255, 255, 255);
139 | OverrideTimesColor = false;
140 | CurrentSplitGradient = GradientType.Vertical;
141 | cmbSplitGradient.SelectedIndexChanged += cmbSplitGradient_SelectedIndexChanged;
142 | BackgroundColor = Color.Transparent;
143 | BackgroundColor2 = Color.FromArgb(1, 255, 255, 255);
144 | BackgroundGradient = GradedExtendedGradientType.Alternating;
145 | DropDecimals = true;
146 | DeltasAccuracy = TimeAccuracy.Tenths;
147 | OverrideDeltasColor = false;
148 | DeltasColor = Color.FromArgb(255, 255, 255);
149 | Display2Rows = false;
150 | ShowColumnLabels = false;
151 | LabelsColor = Color.FromArgb(255, 255, 255);
152 |
153 | dmnTotalSegments.DataBindings.Add("Value", this, "VisualSplitCount", false, DataSourceUpdateMode.OnPropertyChanged);
154 | dmnUpcomingSegments.DataBindings.Add("Value", this, "SplitPreviewCount", false, DataSourceUpdateMode.OnPropertyChanged);
155 | btnTopColor.DataBindings.Add("BackColor", this, "CurrentSplitTopColor", false, DataSourceUpdateMode.OnPropertyChanged);
156 | btnBottomColor.DataBindings.Add("BackColor", this, "CurrentSplitBottomColor", false, DataSourceUpdateMode.OnPropertyChanged);
157 | chkAutomaticAbbreviations.DataBindings.Add("Checked", this, "AutomaticAbbreviations", false, DataSourceUpdateMode.OnPropertyChanged);
158 | btnBeforeNamesColor.DataBindings.Add("BackColor", this, "BeforeNamesColor", false, DataSourceUpdateMode.OnPropertyChanged);
159 | btnCurrentNamesColor.DataBindings.Add("BackColor", this, "CurrentNamesColor", false, DataSourceUpdateMode.OnPropertyChanged);
160 | btnAfterNamesColor.DataBindings.Add("BackColor", this, "AfterNamesColor", false, DataSourceUpdateMode.OnPropertyChanged);
161 | btnBeforeTimesColor.DataBindings.Add("BackColor", this, "BeforeTimesColor", false, DataSourceUpdateMode.OnPropertyChanged);
162 | btnCurrentTimesColor.DataBindings.Add("BackColor", this, "CurrentTimesColor", false, DataSourceUpdateMode.OnPropertyChanged);
163 | btnAfterTimesColor.DataBindings.Add("BackColor", this, "AfterTimesColor", false, DataSourceUpdateMode.OnPropertyChanged);
164 | chkDisplayIcons.DataBindings.Add("Checked", this, "DisplayIcons", false, DataSourceUpdateMode.OnPropertyChanged);
165 | chkIconShadows.DataBindings.Add("Checked", this, "IconShadows", false, DataSourceUpdateMode.OnPropertyChanged);
166 | chkThinSeparators.DataBindings.Add("Checked", this, "ShowThinSeparators", false, DataSourceUpdateMode.OnPropertyChanged);
167 | chkLastSplit.DataBindings.Add("Checked", this, "AlwaysShowLastSplit", false, DataSourceUpdateMode.OnPropertyChanged);
168 | chkOverrideTextColor.DataBindings.Add("Checked", this, "OverrideTextColor", false, DataSourceUpdateMode.OnPropertyChanged);
169 | chkOverrideTimesColor.DataBindings.Add("Checked", this, "OverrideTimesColor", false, DataSourceUpdateMode.OnPropertyChanged);
170 | chkShowBlankSplits.DataBindings.Add("Checked", this, "ShowBlankSplits", false, DataSourceUpdateMode.OnPropertyChanged);
171 | chkLockLastSplit.DataBindings.Add("Checked", this, "LockLastSplit", false, DataSourceUpdateMode.OnPropertyChanged);
172 | chkSeparatorLastSplit.DataBindings.Add("Checked", this, "SeparatorLastSplit", false, DataSourceUpdateMode.OnPropertyChanged);
173 | chkDropDecimals.DataBindings.Add("Checked", this, "DropDecimals", false, DataSourceUpdateMode.OnPropertyChanged);
174 | chkOverrideDeltaColor.DataBindings.Add("Checked", this, "OverrideDeltasColor", false, DataSourceUpdateMode.OnPropertyChanged);
175 | btnDeltaColor.DataBindings.Add("BackColor", this, "DeltasColor", false, DataSourceUpdateMode.OnPropertyChanged);
176 | btnLabelColor.DataBindings.Add("BackColor", this, "LabelsColor", false, DataSourceUpdateMode.OnPropertyChanged);
177 | trkIconSize.DataBindings.Add("Value", this, "IconSize", false, DataSourceUpdateMode.OnPropertyChanged);
178 | cmbSplitGradient.DataBindings.Add("SelectedItem", this, "SplitGradientString", false, DataSourceUpdateMode.OnPropertyChanged);
179 | cmbGradientType.DataBindings.Add("SelectedItem", this, "GradientString", false, DataSourceUpdateMode.OnPropertyChanged);
180 | btnColor1.DataBindings.Add("BackColor", this, "BackgroundColor", false, DataSourceUpdateMode.OnPropertyChanged);
181 | btnColor2.DataBindings.Add("BackColor", this, "BackgroundColor2", false, DataSourceUpdateMode.OnPropertyChanged);
182 |
183 | ColumnsList = new List();
184 | ColumnsList.Add(new GradedColumnSettings(CurrentState, "+/-", ColumnsList) { Data = new GradedColumnData("+/-", GradedColumnType.Delta, "Current Comparison", "Current Timing Method") });
185 | ColumnsList.Add(new GradedColumnSettings(CurrentState, "Time", ColumnsList) { Data = new GradedColumnData("Time", GradedColumnType.SplitTime, "Current Comparison", "Current Timing Method") });
186 |
187 | this.BestSegmentIcon = new GradedIcon { PercentageBehind = 0, IconState = GradedIconState.Disabled, Base64Bytes = null };
188 | this.AheadGainingTimeIcon = new GradedIcon { PercentageBehind = 0, IconState = GradedIconState.Disabled, Base64Bytes = null };
189 | this.AheadLosingTimeIcon = new GradedIcon { PercentageBehind = 0, IconState = GradedIconState.Disabled, Base64Bytes = null };
190 | this.BehindGainingTimeIcon = new GradedIcon { PercentageBehind = 0, IconState = GradedIconState.Disabled, Base64Bytes = null };
191 | this.BehindLosingTimeIcon = new GradedIcon { PercentageBehind = 999999999999, IconState = GradedIconState.Disabled, Base64Bytes = null };
192 | this.SkippedSplitIcon = new GradedIcon { PercentageBehind = 0, IconState = GradedIconState.Disabled, Base64Bytes = null };
193 | this.GradedIconsApplicationState = GradedIconsApplicationState.Disabled;
194 | }
195 |
196 | void chkColumnLabels_CheckedChanged(object sender, EventArgs e)
197 | {
198 | btnLabelColor.Enabled = lblLabelsColor.Enabled = chkColumnLabels.Checked;
199 | }
200 |
201 | void chkDisplayIcons_CheckedChanged(object sender, EventArgs e)
202 | {
203 | trkIconSize.Enabled = label5.Enabled = chkIconShadows.Enabled = chkDisplayIcons.Checked;
204 | }
205 |
206 | void chkOverrideTimesColor_CheckedChanged(object sender, EventArgs e)
207 | {
208 | label6.Enabled = label9.Enabled = label7.Enabled = btnBeforeTimesColor.Enabled
209 | = btnCurrentTimesColor.Enabled = btnAfterTimesColor.Enabled = chkOverrideTimesColor.Checked;
210 | }
211 |
212 | void chkOverrideDeltaColor_CheckedChanged(object sender, EventArgs e)
213 | {
214 | label8.Enabled = btnDeltaColor.Enabled = chkOverrideDeltaColor.Checked;
215 | }
216 |
217 | void chkOverrideTextColor_CheckedChanged(object sender, EventArgs e)
218 | {
219 | label3.Enabled = label10.Enabled = label13.Enabled = btnBeforeNamesColor.Enabled
220 | = btnCurrentNamesColor.Enabled = btnAfterNamesColor.Enabled = chkOverrideTextColor.Checked;
221 | }
222 |
223 | void rdoDeltaTenths_CheckedChanged(object sender, EventArgs e)
224 | {
225 | UpdateDeltaAccuracy();
226 | }
227 |
228 | void rdoDeltaSeconds_CheckedChanged(object sender, EventArgs e)
229 | {
230 | UpdateDeltaAccuracy();
231 | }
232 |
233 | void chkSeparatorLastSplit_CheckedChanged(object sender, EventArgs e)
234 | {
235 | SeparatorLastSplit = chkSeparatorLastSplit.Checked;
236 | SplitLayoutChanged(this, null);
237 | }
238 |
239 | void cmbGradientType_SelectedIndexChanged(object sender, EventArgs e)
240 | {
241 | btnColor1.Visible = cmbGradientType.SelectedItem.ToString() != "Plain";
242 | btnColor2.DataBindings.Clear();
243 | btnColor2.DataBindings.Add("BackColor", this, btnColor1.Visible ? "BackgroundColor2" : "BackgroundColor", false, DataSourceUpdateMode.OnPropertyChanged);
244 | GradientString = cmbGradientType.SelectedItem.ToString();
245 | }
246 |
247 | void cmbSplitGradient_SelectedIndexChanged(object sender, EventArgs e)
248 | {
249 | btnTopColor.Visible = cmbSplitGradient.SelectedItem.ToString() != "Plain";
250 | btnBottomColor.DataBindings.Clear();
251 | btnBottomColor.DataBindings.Add("BackColor", this, btnTopColor.Visible ? "CurrentSplitBottomColor" : "CurrentSplitTopCOlor", false, DataSourceUpdateMode.OnPropertyChanged);
252 | SplitGradientString = cmbSplitGradient.SelectedItem.ToString();
253 | }
254 |
255 | void chkLockLastSplit_CheckedChanged(object sender, EventArgs e)
256 | {
257 | LockLastSplit = chkLockLastSplit.Checked;
258 | SplitLayoutChanged(this, null);
259 | }
260 |
261 | void chkShowBlankSplits_CheckedChanged(object sender, EventArgs e)
262 | {
263 | ShowBlankSplits = chkLockLastSplit.Enabled = chkShowBlankSplits.Checked;
264 | SplitLayoutChanged(this, null);
265 | }
266 |
267 | void rdoTenths_CheckedChanged(object sender, EventArgs e)
268 | {
269 | UpdateAccuracy();
270 | }
271 |
272 | void rdoSeconds_CheckedChanged(object sender, EventArgs e)
273 | {
274 | UpdateAccuracy();
275 | }
276 |
277 | void UpdateAccuracy()
278 | {
279 | if (rdoSeconds.Checked)
280 | SplitTimesAccuracy = TimeAccuracy.Seconds;
281 | else if (rdoTenths.Checked)
282 | SplitTimesAccuracy = TimeAccuracy.Tenths;
283 | else
284 | SplitTimesAccuracy = TimeAccuracy.Hundredths;
285 | }
286 |
287 | void UpdateDeltaAccuracy()
288 | {
289 | if (rdoDeltaSeconds.Checked)
290 | DeltasAccuracy = TimeAccuracy.Seconds;
291 | else if (rdoDeltaTenths.Checked)
292 | DeltasAccuracy = TimeAccuracy.Tenths;
293 | else
294 | DeltasAccuracy = TimeAccuracy.Hundredths;
295 | }
296 |
297 | void chkLastSplit_CheckedChanged(object sender, EventArgs e)
298 | {
299 | AlwaysShowLastSplit = chkLastSplit.Checked;
300 | VisualSplitCount = VisualSplitCount;
301 | SplitLayoutChanged(this, null);
302 | }
303 |
304 | void chkThinSeparators_CheckedChanged(object sender, EventArgs e)
305 | {
306 | ShowThinSeparators = chkThinSeparators.Checked;
307 | SplitLayoutChanged(this, null);
308 | }
309 |
310 | void SplitsSettings_Load(object sender, EventArgs e)
311 | {
312 | ResetColumns();
313 |
314 | chkOverrideDeltaColor_CheckedChanged(null, null);
315 | chkOverrideTextColor_CheckedChanged(null, null);
316 | chkOverrideTimesColor_CheckedChanged(null, null);
317 | chkColumnLabels_CheckedChanged(null, null);
318 | chkDisplayIcons_CheckedChanged(null, null);
319 | chkLockLastSplit.Enabled = chkShowBlankSplits.Checked;
320 |
321 | rdoSeconds.Checked = SplitTimesAccuracy == TimeAccuracy.Seconds;
322 | rdoTenths.Checked = SplitTimesAccuracy == TimeAccuracy.Tenths;
323 | rdoHundredths.Checked = SplitTimesAccuracy == TimeAccuracy.Hundredths;
324 |
325 | rdoDeltaSeconds.Checked = DeltasAccuracy == TimeAccuracy.Seconds;
326 | rdoDeltaTenths.Checked = DeltasAccuracy == TimeAccuracy.Tenths;
327 | rdoDeltaHundredths.Checked = DeltasAccuracy == TimeAccuracy.Hundredths;
328 |
329 | if (Mode == LayoutMode.Horizontal)
330 | {
331 | trkSize.DataBindings.Clear();
332 | trkSize.Minimum = 5;
333 | trkSize.Maximum = 120;
334 | SplitWidth = Math.Min(Math.Max(trkSize.Minimum, SplitWidth), trkSize.Maximum);
335 | trkSize.DataBindings.Add("Value", this, "SplitWidth", false, DataSourceUpdateMode.OnPropertyChanged);
336 | lblSplitSize.Text = "Split Width:";
337 | chkDisplayRows.Enabled = false;
338 | chkDisplayRows.DataBindings.Clear();
339 | chkDisplayRows.Checked = true;
340 | chkColumnLabels.DataBindings.Clear();
341 | chkColumnLabels.Enabled = chkColumnLabels.Checked = false;
342 | }
343 | else
344 | {
345 | trkSize.DataBindings.Clear();
346 | trkSize.Minimum = 0;
347 | trkSize.Maximum = 250;
348 | ScaledSplitHeight = Math.Min(Math.Max(trkSize.Minimum, ScaledSplitHeight), trkSize.Maximum);
349 | trkSize.DataBindings.Add("Value", this, "ScaledSplitHeight", false, DataSourceUpdateMode.OnPropertyChanged);
350 | lblSplitSize.Text = "Split Height:";
351 | chkDisplayRows.Enabled = true;
352 | chkDisplayRows.DataBindings.Clear();
353 | chkDisplayRows.DataBindings.Add("Checked", this, "Display2Rows", false, DataSourceUpdateMode.OnPropertyChanged);
354 | chkColumnLabels.DataBindings.Clear();
355 | chkColumnLabels.Enabled = true;
356 | chkColumnLabels.DataBindings.Add("Checked", this, "ShowColumnLabels", false, DataSourceUpdateMode.OnPropertyChanged);
357 | }
358 | }
359 |
360 | public void SetSettings(XmlNode node)
361 | {
362 | var element = (XmlElement)node;
363 | Version version = SettingsHelper.ParseVersion(element["Version"]);
364 |
365 | CurrentSplitTopColor = SettingsHelper.ParseColor(element["CurrentSplitTopColor"]);
366 | CurrentSplitBottomColor = SettingsHelper.ParseColor(element["CurrentSplitBottomColor"]);
367 | VisualSplitCount = SettingsHelper.ParseInt(element["VisualSplitCount"]);
368 | SplitPreviewCount = SettingsHelper.ParseInt(element["SplitPreviewCount"]);
369 | DisplayIcons = SettingsHelper.ParseBool(element["DisplayIcons"]);
370 | ShowThinSeparators = SettingsHelper.ParseBool(element["ShowThinSeparators"]);
371 | AlwaysShowLastSplit = SettingsHelper.ParseBool(element["AlwaysShowLastSplit"]);
372 | SplitWidth = SettingsHelper.ParseFloat(element["SplitWidth"]);
373 | AutomaticAbbreviations = SettingsHelper.ParseBool(element["AutomaticAbbreviations"], false);
374 | ShowColumnLabels = SettingsHelper.ParseBool(element["ShowColumnLabels"], false);
375 | LabelsColor = SettingsHelper.ParseColor(element["LabelsColor"], Color.FromArgb(255, 255, 255));
376 | OverrideTimesColor = SettingsHelper.ParseBool(element["OverrideTimesColor"], false);
377 | BeforeTimesColor = SettingsHelper.ParseColor(element["BeforeTimesColor"], Color.FromArgb(255, 255, 255));
378 | CurrentTimesColor = SettingsHelper.ParseColor(element["CurrentTimesColor"], Color.FromArgb(255, 255, 255));
379 | AfterTimesColor = SettingsHelper.ParseColor(element["AfterTimesColor"], Color.FromArgb(255, 255, 255));
380 | SplitHeight = SettingsHelper.ParseFloat(element["SplitHeight"], 6);
381 | SplitGradientString = SettingsHelper.ParseString(element["CurrentSplitGradient"], GradientType.Vertical.ToString());
382 | BackgroundColor = SettingsHelper.ParseColor(element["BackgroundColor"], Color.Transparent);
383 | BackgroundColor2 = SettingsHelper.ParseColor(element["BackgroundColor2"], Color.Transparent);
384 | GradientString = SettingsHelper.ParseString(element["BackgroundGradient"], GradedExtendedGradientType.Plain.ToString());
385 | SeparatorLastSplit = SettingsHelper.ParseBool(element["SeparatorLastSplit"], true);
386 | DropDecimals = SettingsHelper.ParseBool(element["DropDecimals"], true);
387 | DeltasAccuracy = SettingsHelper.ParseEnum(element["DeltasAccuracy"], TimeAccuracy.Tenths);
388 | OverrideDeltasColor = SettingsHelper.ParseBool(element["OverrideDeltasColor"], false);
389 | DeltasColor = SettingsHelper.ParseColor(element["DeltasColor"], Color.FromArgb(255, 255, 255));
390 | Display2Rows = SettingsHelper.ParseBool(element["Display2Rows"], false);
391 | SplitTimesAccuracy = SettingsHelper.ParseEnum(element["SplitTimesAccuracy"], TimeAccuracy.Seconds);
392 | ShowBlankSplits = SettingsHelper.ParseBool(element["ShowBlankSplits"], true);
393 | LockLastSplit = SettingsHelper.ParseBool(element["LockLastSplit"], false);
394 | IconSize = SettingsHelper.ParseFloat(element["IconSize"], 24f);
395 | IconShadows = SettingsHelper.ParseBool(element["IconShadows"], true);
396 |
397 | if (version >= new Version(1, 5))
398 | {
399 | var columnsElement = element["Columns"];
400 | ColumnsList.Clear();
401 | foreach (var child in columnsElement.ChildNodes)
402 | {
403 | var columnData = GradedColumnData.FromXml((XmlNode)child);
404 | ColumnsList.Add(new GradedColumnSettings(CurrentState, columnData.Name, ColumnsList) { Data = columnData });
405 | }
406 | }
407 | else
408 | {
409 | ColumnsList.Clear();
410 | var comparison = SettingsHelper.ParseString(element["Comparison"]);
411 | if (SettingsHelper.ParseBool(element["ShowSplitTimes"]))
412 | {
413 | ColumnsList.Add(new GradedColumnSettings(CurrentState, "+/-", ColumnsList) { Data = new GradedColumnData("+/-", GradedColumnType.Delta, comparison, "Current Timing Method")});
414 | ColumnsList.Add(new GradedColumnSettings(CurrentState, "Time", ColumnsList) { Data = new GradedColumnData("Time", GradedColumnType.SplitTime, comparison, "Current Timing Method")});
415 | }
416 | else
417 | {
418 | ColumnsList.Add(new GradedColumnSettings(CurrentState, "+/-", ColumnsList) { Data = new GradedColumnData("+/-", GradedColumnType.DeltaorSplitTime, comparison, "Current Timing Method") });
419 | }
420 | }
421 |
422 | if (version >= new Version(1, 3))
423 | {
424 | BeforeNamesColor = SettingsHelper.ParseColor(element["BeforeNamesColor"]);
425 | CurrentNamesColor = SettingsHelper.ParseColor(element["CurrentNamesColor"]);
426 | AfterNamesColor = SettingsHelper.ParseColor(element["AfterNamesColor"]);
427 | OverrideTextColor = SettingsHelper.ParseBool(element["OverrideTextColor"]);
428 | }
429 | else
430 | {
431 | if (version >= new Version(1, 2))
432 | BeforeNamesColor = CurrentNamesColor = AfterNamesColor = SettingsHelper.ParseColor(element["SplitNamesColor"]);
433 | else
434 | {
435 | BeforeNamesColor = Color.FromArgb(255, 255, 255);
436 | CurrentNamesColor = Color.FromArgb(255, 255, 255);
437 | AfterNamesColor = Color.FromArgb(255, 255, 255);
438 | }
439 | OverrideTextColor = !SettingsHelper.ParseBool(element["UseTextColor"], true);
440 | }
441 |
442 | var gradedIconsElem = element["GradedIcons"];
443 |
444 | foreach (XmlNode child in gradedIconsElem.ChildNodes)
445 | {
446 | if (child.Name == "ApplicationState")
447 | {
448 | var appstate = SettingsHelper.ParseEnum((XmlElement)child, GradedIconsApplicationState.Disabled);
449 | this.GradedIconsApplicationState = appstate;
450 | if (appstate == GradedIconsApplicationState.Disabled)
451 | {
452 | this.IconApplicationState_Radio_Disabled.Checked = true;
453 | }
454 | else if (appstate == GradedIconsApplicationState.CurrentRun)
455 | {
456 | this.IconApplicationState_Radio_CurrentRun.Checked = true;
457 | }
458 | else if (appstate == GradedIconsApplicationState.Comparison)
459 | {
460 | this.IconApplicationState_Radio_Comparison.Checked = true;
461 | }
462 | else if (appstate == GradedIconsApplicationState.ComparisonAndCurrentRun)
463 | {
464 | this.IconApplicationState_Radio_CurrentRunAndComparison.Checked = true;
465 | }
466 | continue;
467 | }
468 |
469 | decimal percentage = 0m;
470 | string iconByte64String = null;
471 | GradedIconState state = GradedIconState.Disabled;
472 |
473 | foreach (XmlElement innerChild in child.ChildNodes)
474 | {
475 | if (innerChild.Name == "PercentBehind")
476 | {
477 | percentage = Convert.ToDecimal(SettingsHelper.ParseString(innerChild, "0.00"));
478 | }
479 | else if (innerChild.Name == "State")
480 | {
481 | state = SettingsHelper.ParseEnum(innerChild, GradedIconState.Disabled);
482 | }
483 | else if (innerChild.Name == "Icon")
484 | {
485 | iconByte64String = innerChild.InnerText;
486 | }
487 | }
488 |
489 | NumericUpDown percentageToUpdate = null;
490 | Button toPlaceIconOn = null;
491 | RadioButton disabled = null;
492 | RadioButton defaultRadio = null;
493 | RadioButton usePercentage = null;
494 |
495 | if (child.Name == "BestSegment")
496 | {
497 | percentageToUpdate = BestSeg_Percent;
498 | toPlaceIconOn = BestSegmentIconButton;
499 | disabled = Radio_BestSeg_Disable;
500 | defaultRadio = Radio_BestSeg_UseDefault;
501 | usePercentage = Radio_BestSeg_UsePercent;
502 | this.BestSegmentIcon.Base64Bytes = iconByte64String;
503 | this.BestSegmentIcon.IconState = state;
504 | this.BestSegmentIcon.PercentageBehind = percentage;
505 | }
506 | else if (child.Name == "AheadGaining")
507 | {
508 | percentageToUpdate = AheadGaining_Percent;
509 | toPlaceIconOn = AheadGainingIconButton;
510 | disabled = Radio_AheadGaining_Disable;
511 | defaultRadio = Radio_AheadGaining_UseDefault;
512 | usePercentage = Radio_AheadGaining_UsePercent;
513 | this.AheadGainingTimeIcon.Base64Bytes = iconByte64String;
514 | this.AheadGainingTimeIcon.IconState = state;
515 | this.AheadGainingTimeIcon.PercentageBehind = percentage;
516 | }
517 | else if (child.Name == "AheadLosing")
518 | {
519 | percentageToUpdate = AheadLosing_Percent;
520 | toPlaceIconOn = AheadLosingIconButton;
521 | disabled = Radio_AheadLosing_Disable;
522 | defaultRadio = Radio_AheadLosing_UseDefault;
523 | usePercentage = Radio_AheadLosing_UsePercent;
524 | this.AheadLosingTimeIcon.Base64Bytes = iconByte64String;
525 | this.AheadLosingTimeIcon.IconState = state;
526 | this.AheadLosingTimeIcon.PercentageBehind = percentage;
527 | }
528 | else if (child.Name == "BehindGaining")
529 | {
530 | percentageToUpdate = BehindGaining_Percent;
531 | toPlaceIconOn = BehindGainingIconButton;
532 | disabled = Radio_BehindGaining_Disable;
533 | defaultRadio = Radio_BehindGaining_UseDefault;
534 | usePercentage = Radio_BehindGaining_UsePercent;
535 | this.BehindGainingTimeIcon.Base64Bytes = iconByte64String;
536 | this.BehindGainingTimeIcon.IconState = state;
537 | this.BehindGainingTimeIcon.PercentageBehind = percentage;
538 | }
539 | else if (child.Name == "BehindLosing")
540 | {
541 | percentage = 999999999999;
542 | //percentageToUpdate = BehindLosing_Percent;
543 | toPlaceIconOn = BehindLosingIconButton;
544 | disabled = Radio_BehindLosing_Disable;
545 | defaultRadio = Radio_BehindLosing_UseDefault;
546 | usePercentage = Radio_BehindLosing_UsePercent;
547 | this.BehindLosingTimeIcon.Base64Bytes = iconByte64String;
548 | this.BehindLosingTimeIcon.IconState = state;
549 | this.BehindLosingTimeIcon.PercentageBehind = percentage;
550 | }
551 | else if (child.Name == "SkippedSplit")
552 | {
553 | percentageToUpdate = null;
554 | toPlaceIconOn = SkippedSplitIconButton;
555 | disabled = Radio_SkippedSplit_Disable;
556 | defaultRadio = Radio_SkippedSplit_Use;
557 | usePercentage = null;
558 | this.SkippedSplitIcon.Base64Bytes = iconByte64String;
559 | this.SkippedSplitIcon.IconState = state;
560 | }
561 |
562 | if (state == GradedIconState.Disabled && disabled != null)
563 | {
564 | disabled.Checked = true;
565 | }
566 | else if (state == GradedIconState.Default && defaultRadio != null)
567 | {
568 | defaultRadio.Checked = true;
569 | }
570 | else if (state == GradedIconState.PercentageSplit && usePercentage != null)
571 | {
572 | usePercentage.Checked = true;
573 | }
574 |
575 | if (percentageToUpdate != null)
576 | {
577 | percentageToUpdate.Value = percentage;
578 | }
579 |
580 |
581 | if (toPlaceIconOn != null && !string.IsNullOrWhiteSpace(iconByte64String))
582 | {
583 | placeImageOnButton(toPlaceIconOn, Convert.FromBase64String(iconByte64String));
584 | }
585 | }
586 | }
587 |
588 | public XmlNode GetSettings(XmlDocument document)
589 | {
590 | var parent = document.CreateElement("Settings");
591 | CreateSettingsNode(document, parent);
592 | return parent;
593 | }
594 |
595 | public int GetSettingsHashCode()
596 | {
597 | return CreateSettingsNode(null, null);
598 | }
599 |
600 | private int CreateSettingsNode(XmlDocument document, XmlElement parent)
601 | {
602 | var hashCode = SettingsHelper.CreateSetting(document, parent, "Version", "1.6") ^
603 | SettingsHelper.CreateSetting(document, parent, "CurrentSplitTopColor", CurrentSplitTopColor) ^
604 | SettingsHelper.CreateSetting(document, parent, "CurrentSplitBottomColor", CurrentSplitBottomColor) ^
605 | SettingsHelper.CreateSetting(document, parent, "VisualSplitCount", VisualSplitCount) ^
606 | SettingsHelper.CreateSetting(document, parent, "SplitPreviewCount", SplitPreviewCount) ^
607 | SettingsHelper.CreateSetting(document, parent, "DisplayIcons", DisplayIcons) ^
608 | SettingsHelper.CreateSetting(document, parent, "ShowThinSeparators", ShowThinSeparators) ^
609 | SettingsHelper.CreateSetting(document, parent, "AlwaysShowLastSplit", AlwaysShowLastSplit) ^
610 | SettingsHelper.CreateSetting(document, parent, "SplitWidth", SplitWidth) ^
611 | SettingsHelper.CreateSetting(document, parent, "SplitTimesAccuracy", SplitTimesAccuracy) ^
612 | SettingsHelper.CreateSetting(document, parent, "AutomaticAbbreviations", AutomaticAbbreviations) ^
613 | SettingsHelper.CreateSetting(document, parent, "BeforeNamesColor", BeforeNamesColor) ^
614 | SettingsHelper.CreateSetting(document, parent, "CurrentNamesColor", CurrentNamesColor) ^
615 | SettingsHelper.CreateSetting(document, parent, "AfterNamesColor", AfterNamesColor) ^
616 | SettingsHelper.CreateSetting(document, parent, "OverrideTextColor", OverrideTextColor) ^
617 | SettingsHelper.CreateSetting(document, parent, "BeforeTimesColor", BeforeTimesColor) ^
618 | SettingsHelper.CreateSetting(document, parent, "CurrentTimesColor", CurrentTimesColor) ^
619 | SettingsHelper.CreateSetting(document, parent, "AfterTimesColor", AfterTimesColor) ^
620 | SettingsHelper.CreateSetting(document, parent, "OverrideTimesColor", OverrideTimesColor) ^
621 | SettingsHelper.CreateSetting(document, parent, "ShowBlankSplits", ShowBlankSplits) ^
622 | SettingsHelper.CreateSetting(document, parent, "LockLastSplit", LockLastSplit) ^
623 | SettingsHelper.CreateSetting(document, parent, "IconSize", IconSize) ^
624 | SettingsHelper.CreateSetting(document, parent, "IconShadows", IconShadows) ^
625 | SettingsHelper.CreateSetting(document, parent, "SplitHeight", SplitHeight) ^
626 | SettingsHelper.CreateSetting(document, parent, "CurrentSplitGradient", CurrentSplitGradient) ^
627 | SettingsHelper.CreateSetting(document, parent, "BackgroundColor", BackgroundColor) ^
628 | SettingsHelper.CreateSetting(document, parent, "BackgroundColor2", BackgroundColor2) ^
629 | SettingsHelper.CreateSetting(document, parent, "BackgroundGradient", BackgroundGradient) ^
630 | SettingsHelper.CreateSetting(document, parent, "SeparatorLastSplit", SeparatorLastSplit) ^
631 | SettingsHelper.CreateSetting(document, parent, "DeltasAccuracy", DeltasAccuracy) ^
632 | SettingsHelper.CreateSetting(document, parent, "DropDecimals", DropDecimals) ^
633 | SettingsHelper.CreateSetting(document, parent, "OverrideDeltasColor", OverrideDeltasColor) ^
634 | SettingsHelper.CreateSetting(document, parent, "DeltasColor", DeltasColor) ^
635 | SettingsHelper.CreateSetting(document, parent, "Display2Rows", Display2Rows) ^
636 | SettingsHelper.CreateSetting(document, parent, "ShowColumnLabels", ShowColumnLabels) ^
637 | SettingsHelper.CreateSetting(document, parent, "LabelsColor", LabelsColor);
638 |
639 | XmlElement columnsElement = null;
640 | if (document != null)
641 | {
642 | columnsElement = document.CreateElement("Columns");
643 | parent.AppendChild(columnsElement);
644 | }
645 |
646 | var count = 1;
647 | foreach (var columnData in ColumnsList.Select(x => x.Data))
648 | {
649 | XmlElement settings = null;
650 | if (document != null)
651 | {
652 | settings = document.CreateElement("Settings");
653 | columnsElement.AppendChild(settings);
654 | }
655 | hashCode ^= columnData.CreateElement(document, settings) * count;
656 | count++;
657 | }
658 |
659 |
660 | XmlElement gradedIconsElement = null;
661 | if (document != null)
662 | {
663 | gradedIconsElement = document.CreateElement("GradedIcons");
664 | parent.AppendChild(gradedIconsElement);
665 |
666 | GradedIconsApplicationState applicationState =
667 | (this.IconApplicationState_Radio_Disabled.Checked ? GradedIconsApplicationState.Disabled :
668 | (this.IconApplicationState_Radio_CurrentRun.Checked ? GradedIconsApplicationState.CurrentRun :
669 | (this.IconApplicationState_Radio_Comparison.Checked ? GradedIconsApplicationState.Comparison :
670 | (this.IconApplicationState_Radio_CurrentRunAndComparison.Checked ? GradedIconsApplicationState.ComparisonAndCurrentRun :
671 | GradedIconsApplicationState.Disabled
672 | )
673 | )
674 | )
675 | );
676 |
677 | var elem = document.CreateElement("ApplicationState");
678 | hashCode ^= SettingsHelper.CreateSetting(document, elem, "ApplicationState", applicationState.ToString());
679 | gradedIconsElement.AppendChild(elem);
680 |
681 | var bestSegElem = document.CreateElement("BestSegment");
682 | GradedIconState state = (this.Radio_BestSeg_Disable.Checked ? GradedIconState.Disabled :
683 | (this.Radio_BestSeg_UseDefault.Checked ? GradedIconState.Default :
684 | (this.Radio_BestSeg_UsePercent.Checked ? GradedIconState.PercentageSplit : GradedIconState.Disabled)
685 | ));
686 | hashCode ^= SettingsHelper.CreateSetting(document, bestSegElem, "PercentBehind", this.BestSeg_Percent.Value);
687 | hashCode ^= SettingsHelper.CreateSetting(document, bestSegElem, "Icon", this.BestSegmentIcon.Base64Bytes);
688 | hashCode ^= SettingsHelper.CreateSetting(document, bestSegElem, "State", state.ToString());
689 | gradedIconsElement.AppendChild(bestSegElem);
690 |
691 | var aheadGainingElem = document.CreateElement("AheadGaining");
692 | state = (this.Radio_AheadGaining_Disable.Checked ? GradedIconState.Disabled :
693 | (this.Radio_AheadGaining_UseDefault.Checked ? GradedIconState.Default :
694 | (this.Radio_AheadGaining_UsePercent.Checked ? GradedIconState.PercentageSplit : GradedIconState.Disabled)
695 | ));
696 | hashCode ^= SettingsHelper.CreateSetting(document, aheadGainingElem, "PercentBehind", this.AheadGaining_Percent.Value);
697 | hashCode ^= SettingsHelper.CreateSetting(document, aheadGainingElem, "Icon", this.AheadGainingTimeIcon.Base64Bytes);
698 | hashCode ^= SettingsHelper.CreateSetting(document, aheadGainingElem, "State", state.ToString());
699 | gradedIconsElement.AppendChild(aheadGainingElem);
700 |
701 | var aheadLosingElem = document.CreateElement("AheadLosing");
702 | state = (this.Radio_AheadLosing_Disable.Checked ? GradedIconState.Disabled :
703 | (this.Radio_AheadLosing_UseDefault.Checked ? GradedIconState.Default :
704 | (this.Radio_AheadLosing_UsePercent.Checked ? GradedIconState.PercentageSplit : GradedIconState.Disabled)
705 | ));
706 | hashCode ^= SettingsHelper.CreateSetting(document, aheadLosingElem, "PercentBehind", this.AheadLosing_Percent.Value);
707 | hashCode ^= SettingsHelper.CreateSetting(document, aheadLosingElem, "Icon", this.AheadLosingTimeIcon.Base64Bytes);
708 | hashCode ^= SettingsHelper.CreateSetting(document, aheadLosingElem, "State", state.ToString());
709 | gradedIconsElement.AppendChild(aheadLosingElem);
710 |
711 | var behindGainingElem = document.CreateElement("BehindGaining");
712 | state = (this.Radio_BehindGaining_Disable.Checked ? GradedIconState.Disabled :
713 | (this.Radio_BehindGaining_UseDefault.Checked ? GradedIconState.Default :
714 | (this.Radio_BehindGaining_UsePercent.Checked ? GradedIconState.PercentageSplit : GradedIconState.Disabled)
715 | ));
716 | hashCode ^= SettingsHelper.CreateSetting(document, behindGainingElem, "PercentBehind", this.BehindGaining_Percent.Value);
717 | hashCode ^= SettingsHelper.CreateSetting(document, behindGainingElem, "Icon", this.BehindGainingTimeIcon.Base64Bytes);
718 | hashCode ^= SettingsHelper.CreateSetting(document, behindGainingElem, "State", state.ToString());
719 | gradedIconsElement.AppendChild(behindGainingElem);
720 |
721 | var behindLosingElem = document.CreateElement("BehindLosing");
722 | state = (this.Radio_BehindLosing_Disable.Checked ? GradedIconState.Disabled :
723 | (this.Radio_BehindLosing_UseDefault.Checked ? GradedIconState.Default :
724 | (this.Radio_BehindLosing_UsePercent.Checked ? GradedIconState.PercentageSplit : GradedIconState.Disabled)
725 | ));
726 | hashCode ^= SettingsHelper.CreateSetting(document, behindLosingElem, "PercentBehind", 999999999999);// this.BehindLosing_Percent.Value);
727 | hashCode ^= SettingsHelper.CreateSetting(document, behindLosingElem, "Icon", this.BehindLosingTimeIcon.Base64Bytes);
728 | hashCode ^= SettingsHelper.CreateSetting(document, behindLosingElem, "State", state.ToString());
729 | gradedIconsElement.AppendChild(behindLosingElem);
730 |
731 | var skippedSplitElem = document.CreateElement("SkippedSplit");
732 | state = (this.Radio_SkippedSplit_Disable.Checked ? GradedIconState.Disabled :
733 | (this.Radio_SkippedSplit_Use.Checked ? GradedIconState.Default :
734 | GradedIconState.Disabled
735 | ));
736 | hashCode ^= SettingsHelper.CreateSetting(document, skippedSplitElem, "Icon", this.SkippedSplitIcon.Base64Bytes);
737 | hashCode ^= SettingsHelper.CreateSetting(document, skippedSplitElem, "State", state.ToString());
738 | gradedIconsElement.AppendChild(skippedSplitElem);
739 | }
740 |
741 | return hashCode;
742 | }
743 |
744 | private void ColorButtonClick(object sender, EventArgs e)
745 | {
746 | SettingsHelper.ColorButtonClick((Button)sender, this);
747 | }
748 |
749 | private void ResetColumns()
750 | {
751 | ClearLayout();
752 | var index = 1;
753 | foreach (var column in ColumnsList)
754 | {
755 | UpdateLayoutForColumn();
756 | AddColumnToLayout(column, index);
757 | column.UpdateEnabledButtons();
758 | index++;
759 | }
760 | }
761 |
762 | private void AddColumnToLayout(GradedColumnSettings column, int index)
763 | {
764 | tableColumns.Controls.Add(column, 0, index);
765 | tableColumns.SetColumnSpan(column, 4);
766 | column.ColumnRemoved -= column_ColumnRemoved;
767 | column.MovedUp -= column_MovedUp;
768 | column.MovedDown -= column_MovedDown;
769 | column.ColumnRemoved += column_ColumnRemoved;
770 | column.MovedUp += column_MovedUp;
771 | column.MovedDown += column_MovedDown;
772 | }
773 |
774 | void column_MovedDown(object sender, EventArgs e)
775 | {
776 | var column = (GradedColumnSettings)sender;
777 | var index = ColumnsList.IndexOf(column);
778 | ColumnsList.Remove(column);
779 | ColumnsList.Insert(index + 1, column);
780 | ResetColumns();
781 | column.SelectControl();
782 | }
783 |
784 | void column_MovedUp(object sender, EventArgs e)
785 | {
786 | var column = (GradedColumnSettings)sender;
787 | var index = ColumnsList.IndexOf(column);
788 | ColumnsList.Remove(column);
789 | ColumnsList.Insert(index - 1, column);
790 | ResetColumns();
791 | column.SelectControl();
792 | }
793 |
794 | void column_ColumnRemoved(object sender, EventArgs e)
795 | {
796 | var column = (GradedColumnSettings)sender;
797 | var index = ColumnsList.IndexOf(column);
798 | ColumnsList.Remove(column);
799 | ResetColumns();
800 | if (ColumnsList.Count > 0)
801 | ColumnsList.Last().SelectControl();
802 | else
803 | chkColumnLabels.Select();
804 | }
805 |
806 | private void ClearLayout()
807 | {
808 | tableColumns.RowCount = 1;
809 | tableColumns.RowStyles.Clear();
810 | tableColumns.RowStyles.Add(new RowStyle(SizeType.Absolute, 29f));
811 | tableColumns.Size = StartingTableLayoutSize;
812 | foreach (var control in tableColumns.Controls.OfType().ToList())
813 | {
814 | tableColumns.Controls.Remove(control);
815 | }
816 | Size = StartingSize;
817 | }
818 |
819 | private void UpdateLayoutForColumn()
820 | {
821 | tableColumns.RowCount++;
822 | tableColumns.RowStyles.Add(new RowStyle(SizeType.Absolute, 179f));
823 | tableColumns.Size = new Size(tableColumns.Size.Width, tableColumns.Size.Height + 179);
824 | Size = new Size(Size.Width, Size.Height + 179);
825 | groupColumns.Size = new Size(groupColumns.Size.Width, groupColumns.Size.Height + 179);
826 | }
827 |
828 | private void btnAddColumn_Click(object sender, EventArgs e)
829 | {
830 | UpdateLayoutForColumn();
831 |
832 | var columnControl = new GradedColumnSettings(CurrentState, "#" + (ColumnsList.Count + 1), ColumnsList);
833 | ColumnsList.Add(columnControl);
834 | AddColumnToLayout(columnControl, ColumnsList.Count);
835 |
836 | foreach (var column in ColumnsList)
837 | column.UpdateEnabledButtons();
838 | }
839 |
840 |
841 | private void button1_Click(object sender, EventArgs e)
842 | {
843 | this.BestSegmentIcon.Base64Bytes = setImageOnButton((Button)sender);
844 | }
845 |
846 | private void button2_Click(object sender, EventArgs e)
847 | {
848 | this.AheadGainingTimeIcon.Base64Bytes = setImageOnButton((Button)sender);
849 | }
850 |
851 | private void button3_Click(object sender, EventArgs e)
852 | {
853 | this.AheadLosingTimeIcon.Base64Bytes = setImageOnButton((Button)sender);
854 | }
855 |
856 | private void button4_Click(object sender, EventArgs e)
857 | {
858 | this.BehindGainingTimeIcon.Base64Bytes = setImageOnButton((Button)sender);
859 | }
860 |
861 | private void button5_Click(object sender, EventArgs e)
862 | {
863 | this.BehindLosingTimeIcon.Base64Bytes = setImageOnButton((Button)sender);
864 | }
865 |
866 | private void SkippedSplitIconButton_Click(object sender, EventArgs e)
867 | {
868 | this.SkippedSplitIcon.Base64Bytes = setImageOnButton((Button)sender);
869 | }
870 |
871 | private string setImageOnButton(Button button)
872 | {
873 | try
874 | {
875 | // Wrap the creation of the OpenFileDialog instance in a using statement,
876 | // rather than manually calling the Dispose method to ensure proper disposal
877 | using (OpenFileDialog dlg = new OpenFileDialog())
878 | {
879 | dlg.Title = "Open Image";
880 | dlg.Filter =
881 | "All Files|*.*";
882 | dlg.Multiselect = false;
883 |
884 | if (dlg.ShowDialog() == DialogResult.OK)
885 | {
886 | var bytes = placeImageOnButton(button, dlg.FileName);
887 | return Convert.ToBase64String(bytes);
888 | }
889 | }
890 | return null;
891 | }
892 | catch
893 | {
894 |
895 | return null;
896 | }
897 | }
898 |
899 | private byte[] placeImageOnButton(Button button, string location)
900 | {
901 | var bmp = new Bitmap(location);
902 | button.BackgroundImageLayout = ImageLayout.Stretch;
903 | button.BackgroundImage = bmp;
904 | return File.ReadAllBytes(location);
905 | }
906 |
907 | private void placeImageOnButton(Button button, byte[] imageBytes)
908 | {
909 | using (var ms = new MemoryStream(imageBytes))
910 | {
911 | button.BackgroundImageLayout = ImageLayout.Stretch;
912 | button.BackgroundImage = Image.FromStream(ms);
913 | }
914 | }
915 |
916 | private void tableLayoutPanel12_Paint(object sender, PaintEventArgs e)
917 | {
918 |
919 | }
920 |
921 | private void Radio_BestSeg_UsePercent_CheckedChanged(object sender, EventArgs e)
922 | {
923 | if (((RadioButton)sender).Checked)
924 | {
925 | this.BestSegmentIcon.IconState = GradedIconState.PercentageSplit;
926 | }
927 | }
928 |
929 | private void IconApplicationState_Radio_Disabled_CheckedChanged(object sender, EventArgs e)
930 | {
931 | if (((RadioButton)sender).Checked)
932 | {
933 | this.GradedIconsApplicationState = GradedIconsApplicationState.Disabled;
934 | }
935 | }
936 |
937 | private void IconApplicationState_Radio_CurrentRun_CheckedChanged(object sender, EventArgs e)
938 | {
939 | if (((RadioButton)sender).Checked)
940 | {
941 | this.GradedIconsApplicationState = GradedIconsApplicationState.CurrentRun;
942 | }
943 | }
944 |
945 | private void IconApplicationState_Radio_Comparison_CheckedChanged(object sender, EventArgs e)
946 | {
947 | if (((RadioButton)sender).Checked)
948 | {
949 | this.GradedIconsApplicationState = GradedIconsApplicationState.Comparison;
950 | }
951 | }
952 |
953 | private void IconApplicationState_Radio_CurrentRunAndComparison_CheckedChanged(object sender, EventArgs e)
954 | {
955 | if (((RadioButton)sender).Checked)
956 | {
957 | this.GradedIconsApplicationState = GradedIconsApplicationState.ComparisonAndCurrentRun;
958 | }
959 | }
960 |
961 | private void Radio_BestSeg_Disable_CheckedChanged(object sender, EventArgs e)
962 | {
963 | if (((RadioButton)sender).Checked)
964 | {
965 | this.BestSegmentIcon.IconState = GradedIconState.Disabled;
966 | }
967 | }
968 |
969 | private void Radio_BestSeg_UseDefault_CheckedChanged(object sender, EventArgs e)
970 | {
971 | if (((RadioButton)sender).Checked)
972 | {
973 | this.BestSegmentIcon.IconState = GradedIconState.Default;
974 | }
975 | }
976 |
977 | private void BestSeg_Percent_ValueChanged(object sender, EventArgs e)
978 | {
979 | this.BestSegmentIcon.PercentageBehind = ((NumericUpDown)sender).Value;
980 | }
981 |
982 | private void Radio_AheadGaining_Disable_CheckedChanged(object sender, EventArgs e)
983 | {
984 | if (((RadioButton)sender).Checked)
985 | {
986 | this.AheadGainingTimeIcon.IconState = GradedIconState.Disabled;
987 | }
988 | }
989 |
990 | private void Radio_AheadGaining_UseDefault_CheckedChanged(object sender, EventArgs e)
991 | {
992 | if (((RadioButton)sender).Checked)
993 | {
994 | this.AheadGainingTimeIcon.IconState = GradedIconState.Default;
995 | }
996 | }
997 |
998 | private void Radio_AheadGaining_UsePercent_CheckedChanged(object sender, EventArgs e)
999 | {
1000 | if (((RadioButton)sender).Checked)
1001 | {
1002 | this.AheadGainingTimeIcon.IconState = GradedIconState.PercentageSplit;
1003 | }
1004 | }
1005 |
1006 | private void AheadGaining_Percent_ValueChanged(object sender, EventArgs e)
1007 | {
1008 | this.AheadGainingTimeIcon.PercentageBehind = ((NumericUpDown)sender).Value;
1009 | }
1010 |
1011 | private void Radio_AheadLosing_Disable_CheckedChanged(object sender, EventArgs e)
1012 | {
1013 | if (((RadioButton)sender).Checked)
1014 | {
1015 | this.AheadLosingTimeIcon.IconState = GradedIconState.Disabled;
1016 | }
1017 | }
1018 |
1019 | private void Radio_AheadLosing_UseDefault_CheckedChanged(object sender, EventArgs e)
1020 | {
1021 | if (((RadioButton)sender).Checked)
1022 | {
1023 | this.AheadLosingTimeIcon.IconState = GradedIconState.Default;
1024 | }
1025 | }
1026 |
1027 | private void Radio_AheadLosing_UsePercent_CheckedChanged(object sender, EventArgs e)
1028 | {
1029 | if (((RadioButton)sender).Checked)
1030 | {
1031 | this.AheadLosingTimeIcon.IconState = GradedIconState.PercentageSplit;
1032 | }
1033 | }
1034 |
1035 | private void AheadLosing_Percent_ValueChanged(object sender, EventArgs e)
1036 | {
1037 | this.AheadLosingTimeIcon.PercentageBehind = ((NumericUpDown)sender).Value;
1038 | }
1039 |
1040 | private void Radio_BehindGaining_Disable_CheckedChanged(object sender, EventArgs e)
1041 | {
1042 | if (((RadioButton)sender).Checked)
1043 | {
1044 | this.BehindGainingTimeIcon.IconState = GradedIconState.Disabled;
1045 | }
1046 | }
1047 |
1048 | private void Radio_BehindGaining_UseDefault_CheckedChanged(object sender, EventArgs e)
1049 | {
1050 | if (((RadioButton)sender).Checked)
1051 | {
1052 | this.BehindGainingTimeIcon.IconState = GradedIconState.Default;
1053 | }
1054 | }
1055 |
1056 | private void Radio_BehindGaining_UsePercent_CheckedChanged(object sender, EventArgs e)
1057 | {
1058 | if (((RadioButton)sender).Checked)
1059 | {
1060 | this.BehindGainingTimeIcon.IconState = GradedIconState.PercentageSplit;
1061 | }
1062 | }
1063 |
1064 | private void BehindGaining_Percent_ValueChanged(object sender, EventArgs e)
1065 | {
1066 | this.BehindGainingTimeIcon.PercentageBehind = ((NumericUpDown)sender).Value;
1067 | }
1068 |
1069 |
1070 |
1071 | private void Radio_BehindLosing_UseDefault_CheckedChanged(object sender, EventArgs e)
1072 | {
1073 | if (((RadioButton)sender).Checked)
1074 | {
1075 | this.BehindLosingTimeIcon.IconState = GradedIconState.Default;
1076 | }
1077 | }
1078 |
1079 | private void Radio_BehindLosing_UsePercent_CheckedChanged(object sender, EventArgs e)
1080 | {
1081 | if (((RadioButton)sender).Checked)
1082 | {
1083 | this.BehindLosingTimeIcon.IconState = GradedIconState.PercentageSplit;
1084 | }
1085 | }
1086 |
1087 | private void BehindLosing_Percent_ValueChanged(object sender, EventArgs e)
1088 | {
1089 | //this.BehindLosingTimeIcon.PercentageBehind = ((NumericUpDown)sender).Value;
1090 | }
1091 |
1092 |
1093 |
1094 | private void Radio_BehindLosing_Disable_CheckedChanged(object sender, EventArgs e)
1095 | {
1096 | if (((RadioButton)sender).Checked)
1097 | {
1098 | this.BehindLosingTimeIcon.IconState = GradedIconState.Disabled;
1099 | }
1100 | }
1101 |
1102 |
1103 | private void Radio_SkippedSplit_UseDefault_CheckedChanged(object sender, EventArgs e)
1104 | {
1105 | if (((RadioButton)sender).Checked)
1106 | {
1107 | this.SkippedSplitIcon.IconState = GradedIconState.Default;
1108 | }
1109 | }
1110 |
1111 | private void Radio_SkippedSplit_Disable_CheckedChanged(object sender, EventArgs e)
1112 | {
1113 | if (((RadioButton)sender).Checked)
1114 | {
1115 | this.SkippedSplitIcon.IconState = GradedIconState.Disabled;
1116 | }
1117 | }
1118 | }
1119 | }
1120 |
--------------------------------------------------------------------------------