├── .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 | ![alt text](https://i.imgur.com/qiJaYDp.png) 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 | --------------------------------------------------------------------------------