Merge branch 'main' into feature/beautify-timecodes-2023

This commit is contained in:
Nikolaj Olsson 2023-07-16 12:07:52 -04:00 committed by GitHub
commit 0c692e3f85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 1868 additions and 36 deletions

View File

@ -0,0 +1,30 @@
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Nikse.SubtitleEdit.Core.Common;
namespace Test.Core
{
[TestClass]
public class LanguageAutoDetectLanguagesTest
{
[TestMethod]
public void AutoDetectEnglish()
{
var res = LanguageAutoDetect.AutoDetectGoogleLanguageOrNull2(new Subtitle(new List<Paragraph>
{
new Paragraph("In this tutorial, I'll show you how to add", 0,0)
}));
Assert.AreEqual("en", res);
}
[TestMethod]
public void AutoDetectDanish()
{
var res = LanguageAutoDetect.AutoDetectGoogleLanguageOrNull2(new Subtitle(new List<Paragraph>
{
new Paragraph("Jeg kan ikke finde på noget at lave i dag. Kun at være glad.", 0,0)
}));
Assert.AreEqual("da", res);
}
}
}

View File

@ -71,6 +71,7 @@
<Compile Include="LanguageFiles\LanguageFileTest.cs" />
<Compile Include="Logic\BeautifyTimeCodesTest.cs" />
<Compile Include="Logic\ConvertColorsToDialogTest.cs" />
<Compile Include="Core\LanguageAutoDetectLanguagesTest.cs" />
<Compile Include="Logic\NetflixHelperTest.cs" />
<Compile Include="Logic\SubtitleFormats\EbuStlTest.cs" />
<Compile Include="Logic\SubtitleFormats\NetflixTimedTextTest.cs" />

View File

@ -33,8 +33,14 @@ namespace Nikse.SubtitleEdit.Core.AudioToText
new VoskModel
{
TwoLetterLanguageCode = "cn",
LanguageName = "Chinese (very large, 1.5G)",
Url = "https://alphacephei.com/vosk/models/vosk-model-cn-kaldi-multicn-2-lgraph.zip",
LanguageName = "Chinese (small, 42 MB)",
Url = "https://alphacephei.com/vosk/models/vosk-model-cn-0.22.zip",
},
new VoskModel
{
TwoLetterLanguageCode = "cn",
LanguageName = "Chinese (very large, 1.3 GB)",
Url = "https://alphacephei.com/vosk/models/vosk-model-cn-0.22.zip",
},
new VoskModel
{
@ -51,8 +57,14 @@ namespace Nikse.SubtitleEdit.Core.AudioToText
new VoskModel
{
TwoLetterLanguageCode = "es",
LanguageName = "Spanish",
Url = "https://alphacephei.com/vosk/models/vosk-model-small-es-0.3.zip",
LanguageName = "Spanish (small, 39 MB)",
Url = "https://alphacephei.com/vosk/models/vosk-model-small-es-0.42.zip",
},
new VoskModel
{
TwoLetterLanguageCode = "es",
LanguageName = "Spanish (large, 1.4 GB)",
Url = "https://alphacephei.com/vosk/models/vosk-model-es-0.42.zip",
},
new VoskModel
{
@ -75,19 +87,31 @@ namespace Nikse.SubtitleEdit.Core.AudioToText
new VoskModel
{
TwoLetterLanguageCode = "pt",
LanguageName = "Portuguese",
LanguageName = "Portuguese (small, 31 MB)",
Url = "https://alphacephei.com/vosk/models/vosk-model-small-pt-0.3.zip",
},
new VoskModel
{
TwoLetterLanguageCode = "pt",
LanguageName = "Portuguese (large, 1.6 GB)",
Url = "https://alphacephei.com/vosk/models/vosk-model-pt-fb-v0.1.1-20220516_2113.zip",
},
new VoskModel
{
TwoLetterLanguageCode = "it",
LanguageName = "Italian",
Url = "https://alphacephei.com/vosk/models/vosk-model-small-it-0.4.zip",
LanguageName = "Italian (small, 48 MB)",
Url = "https://alphacephei.com/vosk/models/vosk-model-small-it-0.22.zip",
},
new VoskModel
{
TwoLetterLanguageCode = "it",
LanguageName = "Italian (large, 1.2 GB)",
Url = "https://alphacephei.com/vosk/models/vosk-model-it-0.22.zip",
},
new VoskModel
{
TwoLetterLanguageCode = "nl",
LanguageName = "Dutch",
LanguageName = "Dutch (large, 860 MB)",
Url = "https://alphacephei.com/vosk/models/vosk-model-nl-spraakherkenning-0.6-lgraph.zip",
},
new VoskModel
@ -99,10 +123,16 @@ namespace Nikse.SubtitleEdit.Core.AudioToText
new VoskModel
{
TwoLetterLanguageCode = "ru",
LanguageName = "Russian",
LanguageName = "Russian (small, 45 MB)",
Url = "https://alphacephei.com/vosk/models/vosk-model-small-ru-0.22.zip",
},
new VoskModel
{
TwoLetterLanguageCode = "ru",
LanguageName = "Russian (large, 1.8 GB)",
Url = "https://alphacephei.com/vosk/models/vosk-model-ru-0.42.zip",
},
new VoskModel
{
TwoLetterLanguageCode = "fa",
LanguageName = "Farsi",
@ -123,10 +153,16 @@ namespace Nikse.SubtitleEdit.Core.AudioToText
new VoskModel
{
TwoLetterLanguageCode = "ar",
LanguageName = "Arabic",
LanguageName = "Arabic (small, 318 MB)",
Url = "https://alphacephei.com/vosk/models/vosk-model-ar-mgb2-0.4.zip",
},
new VoskModel
{
TwoLetterLanguageCode = "ar",
LanguageName = "Arabic (large, 1.3 GB)",
Url = "https://alphacephei.com/vosk/models/vosk-model-ar-0.22-linto-1.1.0.zip",
},
new VoskModel
{
TwoLetterLanguageCode = "uk",
LanguageName = "Ukrainian (small, 133 MB)",
@ -197,7 +233,13 @@ namespace Nikse.SubtitleEdit.Core.AudioToText
TwoLetterLanguageCode = "pl",
LanguageName = "Polish",
Url = "https://alphacephei.com/vosk/models/vosk-model-small-pl-0.22.zip",
}
},
new VoskModel
{
TwoLetterLanguageCode = "br",
LanguageName = "Breton (small, 70 MB)",
Url = "https://alphacephei.com/vosk/models/vosk-model-br-0.7.zip",
},
};
}
}

View File

@ -77,14 +77,18 @@ namespace Nikse.SubtitleEdit.Core.Common
private static readonly string[] AutoDetectWordsEnglish =
{
"we", "are", "and", "your", "what", "[TW]hat's", "You're", "(any|some|every)thing", "money", "because", "human", "because", "welcome", "really", "something", "confusing", "about", "know", "people", "other", "which", "would",
"these", "could"
"we", "are", "and", "your", "what", "[TW]hat's", "You're", "(any|some|every)thing", "money", "because", "human", "abandon",
"welcome", "really", "something", "confusing", "about", "know", "people", "other", "which", "would", "these", "could",
"tutorial", "I'll", "show", "you", "about", "absolutely", "accept", "wrong", "thought", "training", "treatment", "successful",
"strong", "simply", "situation","soldier", "somebody", "someone","something", "sometimes", "speak","statement", "perhaps",
"piece", "picture", "police", "popular", "population", "suggest", "weapon", "water", "window", "woman", "talk", "security",
"season", "remain", "relationship", "report" ,"user" ,"result", "this",
};
private static readonly string[] AutoDetectWordsDanish =
{
"vi", "han", "og", "jeg", "var", "men", "gider", "bliver", "virkelig", "kommer", "tilbage", "Hej", "længere", "gjorde", "dig", "havde", "[Uu]ndskyld", "arbejder", "vidste", "troede", "stadigvæk", "[Mm]åske", "første", "gik",
"fortælle", "begyndt", "spørgsmål", "pludselig"
"fortælle", "begyndt", "spørgsmål", "pludselig", "være", "fordi", "været", "sidste","også", "løb", "sagde", "hjem",
};
private static readonly string[] AutoDetectWordsNorwegian =
@ -796,11 +800,275 @@ namespace Nikse.SubtitleEdit.Core.Common
return languageId;
}
public class LanguageForAutoDetect
{
public string LanguageCode { get; set; }
public string[] Words { get; set; }
public int WordCount { get; set; }
}
public static LanguageForAutoDetect[] GetLanguagesWithCount(string text)
{
var list = new LanguageForAutoDetect[]
{
new LanguageForAutoDetect
{
LanguageCode = "en",
Words = AutoDetectWordsEnglish,
},
new LanguageForAutoDetect
{
LanguageCode = "da",
Words = AutoDetectWordsDanish,
},
new LanguageForAutoDetect
{
LanguageCode = "no",
Words = AutoDetectWordsNorwegian,
},
new LanguageForAutoDetect
{
LanguageCode = "sv",
Words = AutoDetectWordsSwedish,
},
new LanguageForAutoDetect
{
LanguageCode = "es",
Words = AutoDetectWordsSpanish,
},
new LanguageForAutoDetect
{
LanguageCode = "it",
Words = AutoDetectWordsItalian,
},
new LanguageForAutoDetect
{
LanguageCode = "fr",
Words = AutoDetectWordsFrench,
},
new LanguageForAutoDetect
{
LanguageCode = "pt",
Words = AutoDetectWordsPortuguese,
},
new LanguageForAutoDetect
{
LanguageCode = "de",
Words = AutoDetectWordsGerman,
},
new LanguageForAutoDetect
{
LanguageCode = "nl",
Words = AutoDetectWordsDutch,
},
new LanguageForAutoDetect
{
LanguageCode = "pl",
Words = AutoDetectWordsPolish,
},
new LanguageForAutoDetect
{
LanguageCode = "el",
Words = AutoDetectWordsGreek,
},
new LanguageForAutoDetect
{
LanguageCode = "ru",
Words = AutoDetectWordsRussian,
},
new LanguageForAutoDetect
{
LanguageCode = "bg",
Words = AutoDetectWordsBulgarian,
},
new LanguageForAutoDetect
{
LanguageCode = "uk",
Words = AutoDetectWordsUkrainian,
},
new LanguageForAutoDetect
{
LanguageCode = "sq",
Words = AutoDetectWordsAlbanian,
},
new LanguageForAutoDetect
{
LanguageCode = "ar",
Words = AutoDetectWordsArabic,
},
new LanguageForAutoDetect
{
LanguageCode = "fa",
Words = AutoDetectWordsFarsi,
},
new LanguageForAutoDetect
{
LanguageCode = "he",
Words = AutoDetectWordsHebrew,
},
new LanguageForAutoDetect
{
LanguageCode = "vi",
Words = AutoDetectWordsVietnamese,
},
new LanguageForAutoDetect
{
LanguageCode = "hu",
Words = AutoDetectWordsHungarian,
},
new LanguageForAutoDetect
{
LanguageCode = "tr",
Words = AutoDetectWordsTurkish,
},
new LanguageForAutoDetect
{
LanguageCode = "-",
Words = AutoDetectWordsCroatianAndSerbian,
},
new LanguageForAutoDetect
{
LanguageCode = "hr",
Words = AutoDetectWordsCroatian,
},
new LanguageForAutoDetect
{
LanguageCode = "sr",
Words = AutoDetectWordsSerbian,
},
new LanguageForAutoDetect
{
LanguageCode = "sr",
Words = AutoDetectWordsSerbianCyrillic,
},
new LanguageForAutoDetect
{
LanguageCode = "sr-cy",
Words = AutoDetectWordsSerbianCyrillicOnly,
},
new LanguageForAutoDetect
{
LanguageCode = "id",
Words = AutoDetectWordsIndonesian,
},
new LanguageForAutoDetect
{
LanguageCode = "th",
Words = AutoDetectWordsThai,
},
new LanguageForAutoDetect
{
LanguageCode = "ko",
Words = AutoDetectWordsKorean,
},
new LanguageForAutoDetect
{
LanguageCode = "mk",
Words = AutoDetectWordsMacedonian,
},
new LanguageForAutoDetect
{
LanguageCode = "fi",
Words = AutoDetectWordsFinnish,
},
new LanguageForAutoDetect
{
LanguageCode = "ro",
Words = AutoDetectWordsRomanian,
},
new LanguageForAutoDetect
{
LanguageCode = "-",
Words = AutoDetectWordsCzechAndSlovak,
},
new LanguageForAutoDetect
{
LanguageCode = "cs",
Words = AutoDetectWordsCzech,
},
new LanguageForAutoDetect
{
LanguageCode = "sk",
Words = AutoDetectWordsSlovak,
},
new LanguageForAutoDetect
{
LanguageCode = "sl",
Words = AutoDetectWordsSlovenian,
},
new LanguageForAutoDetect
{
LanguageCode = "is",
Words = AutoDetectWordsIcelandic,
},
new LanguageForAutoDetect
{
LanguageCode = "lv",
Words = AutoDetectWordsLatvian,
},
new LanguageForAutoDetect
{
LanguageCode = "lt",
Words = AutoDetectWordsLithuanian,
},
new LanguageForAutoDetect
{
LanguageCode = "hi",
Words = AutoDetectWordsHindi,
},
new LanguageForAutoDetect
{
LanguageCode = "ur",
Words = AutoDetectWordsUrdu,
},
new LanguageForAutoDetect
{
LanguageCode = "si",
Words = AutoDetectWordsSinhalese,
},
};
foreach (var item in list)
{
item.WordCount = GetCount(text, item.Words);
}
return list;
}
//TODO: improve... a lot ;)
public static string AutoDetectGoogleLanguageOrNull2(Subtitle subtitle)
{
var s = new Subtitle(subtitle);
s.RemoveEmptyLines();
var allText = s.GetAllTexts(500000).TrimEnd();
var languagesAndWords = GetLanguagesWithCount(allText);
var languageAndWordHitsOrdered = languagesAndWords
.Where(p => p.WordCount > 0)
.OrderByDescending(p => p.WordCount);
var languageIdFromWordCount = languageAndWordHitsOrdered
.FirstOrDefault(p => p.LanguageCode != "-")?.LanguageCode;
var languageIdViaLetters = GetEncodingViaLetter(allText);
if (languageIdFromWordCount != null)
{
return languageIdFromWordCount;
}
if (languageIdViaLetters != null)
{
return languageIdViaLetters;
}
return null;
}
public static string AutoDetectGoogleLanguageOrNull(Subtitle subtitle)
{
var s = new Subtitle(subtitle);
s.RemoveEmptyLines();
var allText = s.GetAllTexts(500000);
var allText = s.GetAllTexts(500000).TrimEnd();
var languageId = AutoDetectGoogleLanguage(allText, s.Paragraphs.Count / 14);
if (string.IsNullOrEmpty(languageId))
{
@ -811,6 +1079,7 @@ namespace Nikse.SubtitleEdit.Core.Common
{
languageId = null;
}
return languageId;
}

View File

@ -0,0 +1,787 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Globalization;
using System.Windows.Forms;
using Nikse.SubtitleEdit.Core.Common;
using Nikse.SubtitleEdit.Logic;
namespace Nikse.SubtitleEdit.Controls
{
[Category("NikseTimeUpDown"), Description("Subtitle time with better support for color theme")]
public sealed class NikseTimeUpDown : Control
{
// ReSharper disable once InconsistentNaming
public EventHandler TimeCodeChanged;
public enum TimeMode
{
HHMMSSMS,
HHMMSSFF
}
private readonly bool _designMode = LicenseManager.UsageMode == LicenseUsageMode.Designtime;
private const int NumericUpDownValue = 50;
private bool _forceHHMMSSFF;
public bool UseVideoOffset { get; set; }
private static readonly char[] SplitChars = GetSplitChars();
private bool _dirty;
double _initialTotalMilliseconds;
internal void ForceHHMMSSFF()
{
_forceHHMMSSFF = true;
_maskedTextBox.Mask = "00:00:00:00";
}
public void SetAutoWidth()
{
using (var g = Graphics.FromHwnd(IntPtr.Zero))
{
var widthOfUpDown = 25;
if (Configuration.IsRunningOnLinux)
{
widthOfUpDown += 20;
}
var actualWidth = g.MeasureString("00:00:00:000", Font).Width;
Width = (int)Math.Round(actualWidth + ButtonsWidth + 6);
}
}
public TimeMode Mode
{
get
{
if (_forceHHMMSSFF || Configuration.Settings?.General.UseTimeFormatHHMMSSFF == true)
{
return TimeMode.HHMMSSFF;
}
return TimeMode.HHMMSSMS;
}
}
[Category("NikseTimeUpDown"), Description("Gets or sets the increment value")]
public decimal Increment { get; set; } = 100;
[Category("NikseTimeUpDown"), Description("Allow arrow keys to set increment/decrement value")]
[DefaultValue(true)]
public bool InterceptArrowKeys { get; set; }
private Color _buttonForeColor;
private Brush _buttonForeColorBrush;
[Category("NikseTimeUpDown"), Description("Gets or sets the button foreground color"),
RefreshProperties(RefreshProperties.Repaint)]
public Color ButtonForeColor
{
get => _buttonForeColor;
set
{
if (value.A == 0)
{
return;
}
_buttonForeColor = value;
_buttonForeColorBrush?.Dispose();
_buttonForeColorBrush = new SolidBrush(_buttonForeColor);
Invalidate();
}
}
private Color _buttonForeColorOver;
private Brush _buttonForeColorOverBrush;
[Category("NikseTimeUpDown"), Description("Gets or sets the button foreground mouse over color"), RefreshProperties(RefreshProperties.Repaint)]
public Color ButtonForeColorOver
{
get => _buttonForeColorOver;
set
{
if (value.A == 0)
{
return;
}
_buttonForeColorOver = value;
_buttonForeColorOverBrush?.Dispose();
_buttonForeColorOverBrush = new SolidBrush(_buttonForeColorOver);
Invalidate();
}
}
private Color _buttonForeColorDown;
private Brush _buttonForeColorDownBrush;
[Category("NikseTimeUpDown"), Description("Gets or sets the button foreground mouse down color"), RefreshProperties(RefreshProperties.Repaint)]
public Color ButtonForeColorDown
{
get => _buttonForeColorDown;
set
{
if (value.A == 0)
{
return;
}
_buttonForeColorDown = value;
_buttonForeColorDownBrush?.Dispose();
_buttonForeColorDownBrush = new SolidBrush(_buttonForeColorDown);
Invalidate();
}
}
private Color _borderColor;
[Category("NikseTimeUpDown"), Description("Gets or sets the border color"), RefreshProperties(RefreshProperties.Repaint)]
public Color BorderColor
{
get => _borderColor;
set
{
if (value.A == 0)
{
return;
}
_borderColor = value;
Invalidate();
}
}
private Color _backColorDisabled;
[Category("NikseTimeUpDown"), Description("Gets or sets the button foreground color"),
RefreshProperties(RefreshProperties.Repaint)]
public Color BackColorDisabled
{
get => _backColorDisabled;
set
{
if (value.A == 0)
{
return;
}
_backColorDisabled = value;
Invalidate();
}
}
private Color _borderColorDisabled;
[Category("NikseTimeUpDown"), Description("Gets or sets the disabled border color"), RefreshProperties(RefreshProperties.Repaint)]
public Color BorderColorDisabled
{
get => _borderColorDisabled;
set
{
if (value.A == 0)
{
return;
}
_borderColorDisabled = value;
Invalidate();
}
}
private readonly MaskedTextBox _maskedTextBox;
public NikseTimeUpDown()
{
_maskedTextBox = new MaskedTextBox();
_maskedTextBox.Font = UiUtil.GetDefaultFont();
_maskedTextBox.KeyPress += TextBox_KeyPress;
_maskedTextBox.KeyDown += (sender, e) =>
{
if (InterceptArrowKeys && e.KeyCode == Keys.Down)
{
AddValue(-Increment);
e.Handled = true;
}
else if (InterceptArrowKeys && e.KeyCode == Keys.Up)
{
AddValue(Increment);
e.Handled = true;
}
else if (e.KeyData == Keys.Enter)
{
TimeCodeChanged?.Invoke(this, e);
e.SuppressKeyPress = true;
}
else if (e.KeyData != (Keys.Tab | Keys.Shift) &&
e.KeyData != Keys.Tab &&
e.KeyData != Keys.Left &&
e.KeyData != Keys.Right)
{
_dirty = true;
}
};
_maskedTextBox.LostFocus += (sender, args) => Invalidate();
_maskedTextBox.GotFocus += (sender, args) => Invalidate();
_maskedTextBox.MouseDown += (sender, e) =>
{
if (e.Button == MouseButtons.Right)
{
_dirty = true;
}
};
_maskedTextBox.BorderStyle = BorderStyle.None;
Controls.Add(_maskedTextBox);
BackColor = new TextBox().BackColor;
ButtonForeColor = DefaultForeColor;
ButtonForeColorOver = Color.FromArgb(0, 120, 215);
ButtonForeColorDown = Color.Orange;
BorderColor = Color.FromArgb(171, 173, 179);
BorderColorDisabled = Color.FromArgb(120, 120, 120);
BackColorDisabled = Color.FromArgb(240, 240, 240);
DoubleBuffered = true;
InterceptArrowKeys = true;
Increment = 100;
_repeatTimer = new Timer();
_repeatTimer.Tick += (sender, args) =>
{
if (_repeatTimerArrowUp)
{
AddValue(Increment);
}
else
{
AddValue(-Increment);
}
_repeatCount++;
_repeatTimer.Interval = _repeatCount < 8 ? 75 : 10;
};
LostFocus += (sender, args) => _repeatTimer.Stop();
_maskedTextBox.InsertKeyMode = InsertKeyMode.Overwrite;
_maskedTextBox.LostFocus += (sender, args) =>
{
AddValue(0);
};
}
public MaskedTextBox MaskedTextBox => _maskedTextBox;
public void SetTotalMilliseconds(double milliseconds)
{
_dirty = false;
_initialTotalMilliseconds = milliseconds;
if (UseVideoOffset)
{
milliseconds += Configuration.Settings.General.CurrentVideoOffsetInMs;
}
if (Mode == TimeMode.HHMMSSMS)
{
_maskedTextBox.Mask = GetMask(milliseconds);
_maskedTextBox.Text = new TimeCode(milliseconds).ToString();
}
else
{
var tc = new TimeCode(milliseconds);
_maskedTextBox.Mask = GetMaskFrames(milliseconds);
_maskedTextBox.Text = tc.ToString().Substring(0, 9) + $"{Core.SubtitleFormats.SubtitleFormat.MillisecondsToFrames(tc.Milliseconds):00}";
}
_dirty = false;
}
public double? GetTotalMilliseconds()
{
return _dirty ? TimeCode?.TotalMilliseconds : _initialTotalMilliseconds;
}
public TimeCode TimeCode
{
get
{
if (_designMode)
{
return new TimeCode();
}
if (string.IsNullOrWhiteSpace(_maskedTextBox.Text.RemoveChar('.').Replace(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator, string.Empty).RemoveChar(',', ':')))
{
return new TimeCode(TimeCode.MaxTimeTotalMilliseconds);
}
if (!_dirty)
{
return new TimeCode(_initialTotalMilliseconds);
}
string startTime = _maskedTextBox.Text;
bool isNegative = startTime.StartsWith('-');
startTime = startTime.TrimStart('-').Replace(' ', '0');
if (Mode == TimeMode.HHMMSSMS)
{
if (startTime.EndsWith(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator, StringComparison.Ordinal))
{
startTime += "000";
}
var times = startTime.Split(SplitChars, StringSplitOptions.RemoveEmptyEntries);
if (times.Length == 4)
{
int.TryParse(times[0], out var hours);
int.TryParse(times[1], out var minutes);
if (minutes > 59)
{
minutes = 59;
}
int.TryParse(times[2], out var seconds);
if (seconds > 59)
{
seconds = 59;
}
int.TryParse(times[3].PadRight(3, '0'), out var milliseconds);
var tc = new TimeCode(hours, minutes, seconds, milliseconds);
if (UseVideoOffset)
{
tc.TotalMilliseconds -= Configuration.Settings.General.CurrentVideoOffsetInMs;
}
if (isNegative)
{
tc.TotalMilliseconds *= -1;
}
return tc;
}
}
else
{
if (startTime.EndsWith(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator, StringComparison.Ordinal) || startTime.EndsWith(':'))
{
startTime += "00";
}
string[] times = startTime.Split(SplitChars, StringSplitOptions.RemoveEmptyEntries);
if (times.Length == 4)
{
int.TryParse(times[0], out var hours);
int.TryParse(times[1], out var minutes);
int.TryParse(times[2], out var seconds);
if (int.TryParse(times[3], out var milliseconds))
{
milliseconds = Core.SubtitleFormats.SubtitleFormat.FramesToMillisecondsMax999(milliseconds);
}
var tc = new TimeCode(hours, minutes, seconds, milliseconds);
if (UseVideoOffset)
{
tc.TotalMilliseconds -= Configuration.Settings.General.CurrentVideoOffsetInMs;
}
if (isNegative)
{
tc.TotalMilliseconds *= -1;
}
return tc;
}
}
return null;
}
set
{
if (_designMode)
{
return;
}
if (value != null)
{
_dirty = false;
_initialTotalMilliseconds = value.TotalMilliseconds;
}
if (value == null || value.TotalMilliseconds >= TimeCode.MaxTimeTotalMilliseconds - 0.1)
{
_maskedTextBox.Text = string.Empty;
return;
}
var v = new TimeCode(value.TotalMilliseconds);
if (UseVideoOffset)
{
v.TotalMilliseconds += Configuration.Settings.General.CurrentVideoOffsetInMs;
}
if (Mode == TimeMode.HHMMSSMS)
{
_maskedTextBox.Mask = GetMask(v.TotalMilliseconds);
_maskedTextBox.Text = v.ToString();
}
else
{
_maskedTextBox.Mask = GetMaskFrames(v.TotalMilliseconds);
_maskedTextBox.Text = v.ToHHMMSSFF();
}
}
}
private static string GetMask(double val) => val >= 0 ? "00:00:00.000" : "-00:00:00.000";
private static string GetMaskFrames(double val) => val >= 0 ? "00:00:00:00" : "-00:00:00:00";
public void Theme()
{
var enabled = Enabled;
Enabled = true;
if (Configuration.Settings.General.UseDarkTheme)
{
BackColor = DarkTheme.BackColor;
MaskedTextBox.BackColor = DarkTheme.BackColor;
BackColor = DarkTheme.BackColor;
}
else
{
BackColor = DefaultBackColor;
using (var tb = new TextBox())
{
MaskedTextBox.BackColor = tb.BackColor;
BackColor = tb.BackColor;
}
}
Enabled = enabled;
}
/// <summary>
/// Allow only digits, Enter and Backspace key.
/// </summary>
private void TextBox_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == (char)Keys.Enter)
{
AddValue(0);
e.Handled = false;
}
else if (char.IsDigit(e.KeyChar) || e.KeyChar == (char)Keys.Back)
{
e.Handled = false;
}
else
{
e.Handled = true;
}
}
/// <summary>
/// Increment or decrement the TextBox value.
/// </summary>
/// <param name="value">Value to increment/decrement</param>
private void AddValue(decimal value)
{
_dirty = true;
var milliseconds = GetTotalMilliseconds();
if (milliseconds.HasValue)
{
if (milliseconds.Value >= TimeCode.MaxTimeTotalMilliseconds - 0.1)
{
milliseconds = 0;
}
if (Mode == TimeMode.HHMMSSMS)
{
SetTotalMilliseconds(milliseconds.Value + (double)value);
}
else
{
if (value > NumericUpDownValue)
{
SetTotalMilliseconds(milliseconds.Value + Core.SubtitleFormats.SubtitleFormat.FramesToMilliseconds(1));
}
else if (value < NumericUpDownValue)
{
SetTotalMilliseconds(milliseconds.Value - Core.SubtitleFormats.SubtitleFormat.FramesToMilliseconds(1));
}
}
TimeCodeChanged?.Invoke(this, null);
}
}
private bool _buttonUpActive;
private bool _buttonDownActive;
private bool _buttonLeftIsDown;
private int _mouseX;
private int _mouseY;
private readonly Timer _repeatTimer;
private bool _repeatTimerArrowUp;
private int _repeatCount;
protected override void OnMouseEnter(EventArgs e)
{
_buttonUpActive = false;
_buttonDownActive = false;
base.OnMouseEnter(e);
Invalidate();
}
protected override void OnMouseLeave(EventArgs e)
{
_buttonUpActive = false;
_buttonDownActive = false;
base.OnMouseLeave(e);
Invalidate();
}
protected override void OnMouseDown(MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
_buttonLeftIsDown = true;
if (_buttonUpActive)
{
AddValue(Increment);
_repeatTimerArrowUp = true;
_repeatTimer.Interval = 300;
_repeatCount = 0;
_repeatTimer.Start();
}
else if (_buttonDownActive)
{
AddValue(-Increment);
_repeatTimerArrowUp = false;
_repeatTimer.Interval = 300;
_repeatCount = 0;
_repeatTimer.Start();
}
Invalidate();
}
base.OnMouseDown(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
_repeatTimer.Stop();
if (_buttonLeftIsDown)
{
_buttonLeftIsDown = false;
Invalidate();
}
base.OnMouseDown(e);
}
protected override void OnMouseMove(MouseEventArgs e)
{
var left = RightToLeft == RightToLeft.Yes ? 0 : Width - ButtonsWidth;
var right = RightToLeft == RightToLeft.Yes ? ButtonsWidth : Width;
var height = Height / 2 - 3;
const int top = 2;
_mouseX = e.X;
_mouseY = e.Y;
if (_mouseX >= left && _mouseX <= right)
{
if (_mouseY > top + height)
{
if (!_buttonDownActive)
{
_buttonUpActive = false;
_buttonDownActive = true;
Invalidate();
}
}
else
{
if (!_buttonUpActive)
{
_buttonUpActive = true;
_buttonDownActive = false;
Invalidate();
}
}
}
else
{
if (_buttonUpActive || _buttonDownActive)
{
_buttonUpActive = false;
_buttonDownActive = false;
Invalidate();
}
_repeatTimer.Stop();
}
base.OnMouseMove(e);
}
private const int ButtonsWidth = 13;
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
_maskedTextBox.BackColor = BackColor;
_maskedTextBox.ForeColor = ButtonForeColor;
_maskedTextBox.Top = 2;
_maskedTextBox.Left = RightToLeft == RightToLeft.Yes ? ButtonsWidth : 3;
_maskedTextBox.Height = Height - 4;
_maskedTextBox.Width = Width - ButtonsWidth - 3;
_maskedTextBox.Invalidate();
if (!Enabled)
{
DrawDisabled(e);
return;
}
base.OnPaint(e);
using (var pen = _maskedTextBox.Focused ? new Pen(_buttonForeColorOver, 1f) : new Pen(BorderColor, 1f))
{
var borderRectangle = new Rectangle(e.ClipRectangle.X, e.ClipRectangle.Y, e.ClipRectangle.Width - 1, e.ClipRectangle.Height - 1);
e.Graphics.DrawRectangle(pen, borderRectangle);
}
var brush = _buttonForeColorBrush;
var left = RightToLeft == RightToLeft.Yes ? 3 : Width - ButtonsWidth;
var height = e.ClipRectangle.Height / 2 - 4;
var top = 2;
if (_buttonUpActive)
{
brush = _buttonLeftIsDown ? _buttonForeColorDownBrush : _buttonForeColorOverBrush;
}
DrawArrowUp(e, brush, left, top, height);
if (_buttonDownActive)
{
brush = _buttonLeftIsDown ? _buttonForeColorDownBrush : _buttonForeColorOverBrush;
}
else
{
brush = _buttonForeColorBrush;
}
top = height + 5;
DrawArrowDown(e, brush, left, top, height);
}
public override RightToLeft RightToLeft
{
get => base.RightToLeft;
set
{
base.RightToLeft = value;
Application.DoEvents();
Invalidate();
}
}
public override Color ForeColor
{
get => base.ForeColor;
set
{
base.ForeColor = value;
_maskedTextBox.ForeColor = value;
Application.DoEvents();
Invalidate();
}
}
public override Color BackColor
{
get => base.BackColor;
set
{
base.BackColor = value;
_maskedTextBox.BackColor = value;
Application.DoEvents();
Invalidate();
}
}
private static void DrawArrowDown(PaintEventArgs e, Brush brush, int left, int top, int height)
{
e.Graphics.FillPolygon(brush,
new[]
{
new Point(left + 5, top + height),
new Point(left + 0, top + 0),
new Point(left + 10, top + 0)
});
}
private static void DrawArrowUp(PaintEventArgs e, Brush brush, int left, int top, int height)
{
e.Graphics.FillPolygon(brush,
new[]
{
new Point(left + 5, top + 0),
new Point(left + 0, top + height),
new Point(left + 10, top + height)
});
}
private void DrawDisabled(PaintEventArgs e)
{
using (var brushBg = new SolidBrush(BackColorDisabled))
{
e.Graphics.FillRectangle(brushBg, e.ClipRectangle);
}
using (var pen = new Pen(BorderColorDisabled, 1f))
{
var borderRectangle = new Rectangle(e.ClipRectangle.X, e.ClipRectangle.Y, e.ClipRectangle.Width - 1, e.ClipRectangle.Height - 1);
e.Graphics.DrawRectangle(pen, borderRectangle);
}
var left = RightToLeft == RightToLeft.Yes ? 3 : e.ClipRectangle.Width - ButtonsWidth;
var height = e.ClipRectangle.Height / 2 - 4;
var top = 2;
using (var brush = new SolidBrush(BorderColorDisabled))
{
DrawArrowUp(e, brush, left, top, height);
top = height + 5;
DrawArrowDown(e, brush, left, top, height);
}
}
private static char[] GetSplitChars()
{
var splitChars = new List<char> { ':', ',', '.' };
string cultureSeparator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
if (cultureSeparator.Length == 1)
{
var ch = Convert.ToChar(cultureSeparator);
if (!splitChars.Contains(ch))
{
splitChars.Add(ch);
}
}
return splitChars.ToArray();
}
}
}

View File

@ -0,0 +1,615 @@
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace Nikse.SubtitleEdit.Controls
{
[Category("NikseUpDown"), Description("Numeric Up/Down with better support for color theme")]
public sealed class NikseUpDown : Control
{
// ReSharper disable once InconsistentNaming
public event EventHandler ValueChanged;
private decimal _value;
[Category("NikseUpDown"), Description("Gets or sets the default value in textBox"), RefreshProperties(RefreshProperties.Repaint)]
public decimal Value
{
get => _value;
set
{
if (DecimalPlaces == 0)
{
_value = value;
}
else if (DecimalPlaces > 0)
{
_value = Math.Round(value, DecimalPlaces);
}
Invalidate();
}
}
private int _decimalPlaces;
[Category("NikseUpDown"), Description("Gets or sets the decimal places (max 4)")]
public int DecimalPlaces
{
get => _decimalPlaces;
set
{
if (value <= 0)
{
_decimalPlaces = 0;
}
else if (value > 3)
{
_decimalPlaces = 4;
}
else
{
_decimalPlaces = value;
}
Invalidate();
}
}
private bool _thousandsSeparator;
[Category("NikseUpDown"), Description("Gets or sets the thousand seperator")]
public bool ThousandsSeparator
{
get => _thousandsSeparator;
set
{
_thousandsSeparator = value;
Invalidate();
}
}
[Category("NikseUpDown"), Description("Gets or sets the increment value")]
public decimal Increment { get; set; } = 1;
[Category("NikseUpDown"), Description("Gets or sets the Maximum value (max 25 significant digits)")]
public decimal Maximum { get; set; } = 100;
[Category("NikseUpDown"), Description("Gets or sets the Minimum value")]
public decimal Minimum { get; set; } = 0;
[Category("NikseUpDown"), Description("Allow arrow keys to set increment/decrement value")]
[DefaultValue(true)]
public bool InterceptArrowKeys { get; set; }
private Color _buttonForeColor;
private Brush _buttonForeColorBrush;
[Category("NikseUpDown"), Description("Gets or sets the button foreground color"),
RefreshProperties(RefreshProperties.Repaint)]
public Color ButtonForeColor
{
get => _buttonForeColor;
set
{
if (value.A == 0)
{
return;
}
_buttonForeColor = value;
_buttonForeColorBrush?.Dispose();
_buttonForeColorBrush = new SolidBrush(_buttonForeColor);
Invalidate();
}
}
private Color _buttonForeColorOver;
private Brush _buttonForeColorOverBrush;
[Category("NikseUpDown"), Description("Gets or sets the button foreground mouse over color"), RefreshProperties(RefreshProperties.Repaint)]
public Color ButtonForeColorOver
{
get => _buttonForeColorOver;
set
{
if (value.A == 0)
{
return;
}
_buttonForeColorOver = value;
_buttonForeColorOverBrush?.Dispose();
_buttonForeColorOverBrush = new SolidBrush(_buttonForeColorOver);
Invalidate();
}
}
private Color _buttonForeColorDown;
private Brush _buttonForeColorDownBrush;
[Category("NikseUpDown"), Description("Gets or sets the button foreground mouse down color"), RefreshProperties(RefreshProperties.Repaint)]
public Color ButtonForeColorDown
{
get => _buttonForeColorDown;
set
{
if (value.A == 0)
{
return;
}
_buttonForeColorDown = value;
_buttonForeColorDownBrush?.Dispose();
_buttonForeColorDownBrush = new SolidBrush(_buttonForeColorDown);
Invalidate();
}
}
private Color _borderColor;
[Category("NikseUpDown"), Description("Gets or sets the border color"), RefreshProperties(RefreshProperties.Repaint)]
public Color BorderColor
{
get => _borderColor;
set
{
if (value.A == 0)
{
return;
}
_borderColor = value;
Invalidate();
}
}
private Color _backColorDisabled;
[Category("NikseUpDown"), Description("Gets or sets the button foreground color"),
RefreshProperties(RefreshProperties.Repaint)]
public Color BackColorDisabled
{
get => _backColorDisabled;
set
{
if (value.A == 0)
{
return;
}
_backColorDisabled = value;
Invalidate();
}
}
private Color _borderColorDisabled;
[Category("NikseUpDown"), Description("Gets or sets the disabled border color"), RefreshProperties(RefreshProperties.Repaint)]
public Color BorderColorDisabled
{
get => _borderColorDisabled;
set
{
if (value.A == 0)
{
return;
}
_borderColorDisabled = value;
Invalidate();
}
}
private readonly TextBox _textBox;
public NikseUpDown()
{
_textBox = new TextBox();
_textBox.KeyPress += TextBox_KeyPress;
_textBox.KeyDown += (sender, e) =>
{
if (InterceptArrowKeys && e.KeyCode == Keys.Down)
{
AddValue(-Increment);
e.Handled = true;
}
else if (InterceptArrowKeys && e.KeyCode == Keys.Up)
{
AddValue(Increment);
e.Handled = true;
}
};
_textBox.LostFocus += (sender, args) => Invalidate();
_textBox.GotFocus += (sender, args) => Invalidate();
_textBox.TextChanged += _textBox_TextChanged;
_textBox.BorderStyle = BorderStyle.None;
Controls.Add(_textBox);
BackColor = new TextBox().BackColor;
ButtonForeColor = DefaultForeColor;
ButtonForeColorOver = Color.FromArgb(0, 120, 215);
ButtonForeColorDown = Color.Orange;
BorderColor = Color.FromArgb(171, 173, 179);
BorderColorDisabled = Color.FromArgb(120, 120, 120);
BackColorDisabled = Color.FromArgb(240, 240, 240);
DoubleBuffered = true;
InterceptArrowKeys = true;
_repeatTimer = new Timer();
_repeatTimer.Tick += (sender, args) =>
{
if (_repeatTimerArrowUp)
{
AddValue(Increment);
}
else
{
AddValue(-Increment);
}
_repeatCount++;
_repeatTimer.Interval = _repeatCount < 8 ? 75 : 10;
};
LostFocus += (sender, args) => _repeatTimer.Stop();
}
private void _textBox_TextChanged(object sender, EventArgs e)
{
if (decimal.TryParse(_textBox.Text, out var result))
{
Value = Math.Round(result, DecimalPlaces);
if (Value < Minimum)
{
Value = Minimum;
Invalidate();
}
else if (Value > Maximum)
{
Value = Maximum;
Invalidate();
}
}
}
/// <summary>
/// Allow only digits, Enter and Backspace key.
/// </summary>
private void TextBox_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == (char)Keys.Enter)
{
AddValue(0);
e.Handled = false;
}
else if (char.IsDigit(e.KeyChar) || e.KeyChar == (char)Keys.Back)
{
e.Handled = false;
}
else if (e.KeyChar == '.' || e.KeyChar == ',')
{
e.Handled = !(DecimalPlaces > 0);
}
else
{
e.Handled = true;
}
}
/// <summary>
/// Increment or decrement the TextBox value.
/// </summary>
/// <param name="value">Value to increment/decrement</param>
private void AddValue(decimal value)
{
if (string.IsNullOrEmpty(_textBox.Text))
{
Value = 0 >= Minimum && 0 <= Maximum ? 0 : Minimum;
SetText();
ValueChanged?.Invoke(this, null);
return;
}
if (_textBox.TextLength > 25)
{
Value = Maximum;
SetText();
ValueChanged?.Invoke(this, null);
return;
}
if (decimal.TryParse(_textBox.Text, out var result))
{
Value = Math.Round(result + value, DecimalPlaces);
if (Value < Minimum)
{
Value = Minimum;
}
else if (Value > Maximum)
{
Value = Maximum;
}
else
{
SetText();
ValueChanged?.Invoke(this, null);
return;
}
}
SetText();
ValueChanged?.Invoke(this, null);
}
private bool _buttonUpActive;
private bool _buttonDownActive;
private bool _buttonLeftIsDown;
private int _mouseX;
private int _mouseY;
private readonly Timer _repeatTimer;
private bool _repeatTimerArrowUp;
private int _repeatCount;
protected override void OnMouseEnter(EventArgs e)
{
_buttonUpActive = false;
_buttonDownActive = false;
base.OnMouseEnter(e);
Invalidate();
}
protected override void OnMouseLeave(EventArgs e)
{
_buttonUpActive = false;
_buttonDownActive = false;
base.OnMouseLeave(e);
Invalidate();
}
protected override void OnMouseDown(MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
_buttonLeftIsDown = true;
if (_buttonUpActive)
{
AddValue(Increment);
_repeatTimerArrowUp = true;
_repeatTimer.Interval = 300;
_repeatCount = 0;
_repeatTimer.Start();
}
else if (_buttonDownActive)
{
AddValue(-Increment);
_repeatTimerArrowUp = false;
_repeatTimer.Interval = 300;
_repeatCount = 0;
_repeatTimer.Start();
}
Invalidate();
}
base.OnMouseDown(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
_repeatTimer.Stop();
if (_buttonLeftIsDown)
{
_buttonLeftIsDown = false;
Invalidate();
}
base.OnMouseDown(e);
}
protected override void OnMouseMove(MouseEventArgs e)
{
var left = RightToLeft == RightToLeft.Yes ? 0 : Width - ButtonsWidth;
var right = RightToLeft == RightToLeft.Yes ? ButtonsWidth : Width;
var height = Height / 2 - 3;
const int top = 2;
_mouseX = e.X;
_mouseY = e.Y;
if (_mouseX >= left && _mouseX <= right)
{
if (_mouseY > top + height)
{
if (!_buttonDownActive)
{
_buttonUpActive = false;
_buttonDownActive = true;
Invalidate();
}
}
else
{
if (!_buttonUpActive)
{
_buttonUpActive = true;
_buttonDownActive = false;
Invalidate();
}
}
}
else
{
if (_buttonUpActive || _buttonDownActive)
{
_buttonUpActive = false;
_buttonDownActive = false;
Invalidate();
}
_repeatTimer.Stop();
}
base.OnMouseMove(e);
}
private const int ButtonsWidth = 13;
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
_textBox.BackColor = BackColor;
_textBox.ForeColor = ButtonForeColor;
_textBox.Top = 2;
_textBox.Left = RightToLeft == RightToLeft.Yes ? ButtonsWidth : 3;
_textBox.Height = Height - 4;
_textBox.Width = Width - ButtonsWidth - 3;
SetText();
if (!Enabled)
{
DrawDisabled(e);
return;
}
base.OnPaint(e);
using (var pen = _textBox.Focused ? new Pen(_buttonForeColorOver, 1f) : new Pen(BorderColor, 1f))
{
var borderRectangle = new Rectangle(e.ClipRectangle.X, e.ClipRectangle.Y, e.ClipRectangle.Width - 1, e.ClipRectangle.Height - 1);
e.Graphics.DrawRectangle(pen, borderRectangle);
}
var brush = _buttonForeColorBrush;
var left = RightToLeft == RightToLeft.Yes ? 3 : Width - ButtonsWidth;
var height = e.ClipRectangle.Height / 2 - 4;
var top = 2;
if (_buttonUpActive)
{
brush = _buttonLeftIsDown ? _buttonForeColorDownBrush : _buttonForeColorOverBrush;
}
DrawArrowUp(e, brush, left, top, height);
if (_buttonDownActive)
{
brush = _buttonLeftIsDown ? _buttonForeColorDownBrush : _buttonForeColorOverBrush;
}
else
{
brush = _buttonForeColorBrush;
}
top = height + 5;
DrawArrowDown(e, brush, left, top, height);
}
public override RightToLeft RightToLeft
{
get => base.RightToLeft;
set
{
base.RightToLeft = value;
Application.DoEvents();
Invalidate();
}
}
public override Color ForeColor
{
get => base.ForeColor;
set
{
base.ForeColor = value;
_textBox.ForeColor = value;
Application.DoEvents();
Invalidate();
}
}
public override Color BackColor
{
get => base.BackColor;
set
{
base.BackColor = value;
_textBox.BackColor = value;
Application.DoEvents();
Invalidate();
}
}
private void SetText()
{
if (DecimalPlaces <= 0)
{
_textBox.Text = ThousandsSeparator ? $"{Value:#,###,##0}" : $"{Value:########0}";
}
else if (DecimalPlaces == 1)
{
_textBox.Text = ThousandsSeparator ? $"{Value:#,###,##0.0}" : $"{Value:########0.0}";
}
else if (DecimalPlaces == 2)
{
_textBox.Text = ThousandsSeparator ? $"{Value:#,###,##0.00}" : $"{Value:#########0.00}";
}
else if (DecimalPlaces == 3)
{
_textBox.Text = ThousandsSeparator ? $"{Value:#,###,##0.000}" : $"{Value:#########0.000}";
}
else
{
_textBox.Text = ThousandsSeparator ? $"{Value:#,###,##0.0000}" : $"{Value:#########0.0000}";
}
}
private static void DrawArrowDown(PaintEventArgs e, Brush brush, int left, int top, int height)
{
e.Graphics.FillPolygon(brush,
new[]
{
new Point(left + 5, top + height),
new Point(left + 0, top + 0),
new Point(left + 10, top + 0)
});
}
private static void DrawArrowUp(PaintEventArgs e, Brush brush, int left, int top, int height)
{
e.Graphics.FillPolygon(brush,
new[]
{
new Point(left + 5, top + 0),
new Point(left + 0, top + height),
new Point(left + 10, top + height)
});
}
private void DrawDisabled(PaintEventArgs e)
{
using (var brushBg = new SolidBrush(BackColorDisabled))
{
e.Graphics.FillRectangle(brushBg, e.ClipRectangle);
}
using (var pen = new Pen(BorderColorDisabled, 1f))
{
var borderRectangle = new Rectangle(e.ClipRectangle.X, e.ClipRectangle.Y, e.ClipRectangle.Width - 1, e.ClipRectangle.Height - 1);
e.Graphics.DrawRectangle(pen, borderRectangle);
}
var left = RightToLeft == RightToLeft.Yes ? 3 : e.ClipRectangle.Width - ButtonsWidth;
var height = e.ClipRectangle.Height / 2 - 4;
var top = 2;
using (var brush = new SolidBrush(BorderColorDisabled))
{
DrawArrowUp(e, brush, left, top, height);
top = height + 5;
DrawArrowDown(e, brush, left, top, height);
}
}
}
}

View File

@ -542,8 +542,8 @@ namespace Nikse.SubtitleEdit.Forms
this.labelTextLineTotal = new System.Windows.Forms.Label();
this.labelCharactersPerSecond = new System.Windows.Forms.Label();
this.buttonUnBreak = new System.Windows.Forms.Button();
this.timeUpDownStartTime = new Nikse.SubtitleEdit.Controls.TimeUpDown();
this.numericUpDownDuration = new System.Windows.Forms.NumericUpDown();
this.timeUpDownStartTime = new Nikse.SubtitleEdit.Controls.NikseTimeUpDown();
this.numericUpDownDuration = new Nikse.SubtitleEdit.Controls.NikseUpDown();
this.buttonPrevious = new System.Windows.Forms.Button();
this.buttonNext = new System.Windows.Forms.Button();
this.labelStartTime = new System.Windows.Forms.Label();
@ -616,7 +616,6 @@ namespace Nikse.SubtitleEdit.Forms
this.panelBookmark.SuspendLayout();
this.contextMenuStripTextBoxListView.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.pictureBoxBookmark)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.numericUpDownDuration)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.pictureBoxRecord)).BeginInit();
this.contextMenuStripTextBoxSourceView.SuspendLayout();
this.panelVideoPlayer.SuspendLayout();
@ -5302,15 +5301,24 @@ namespace Nikse.SubtitleEdit.Forms
//
// timeUpDownStartTime
//
this.timeUpDownStartTime.AutoSize = true;
this.timeUpDownStartTime.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.timeUpDownStartTime.BackColor = System.Drawing.SystemColors.Control;
this.timeUpDownStartTime.BackColorDisabled = System.Drawing.Color.FromArgb(((int)(((byte)(240)))), ((int)(((byte)(240)))), ((int)(((byte)(240)))));
this.timeUpDownStartTime.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(171)))), ((int)(((byte)(173)))), ((int)(((byte)(179)))));
this.timeUpDownStartTime.BorderColorDisabled = System.Drawing.Color.FromArgb(((int)(((byte)(120)))), ((int)(((byte)(120)))), ((int)(((byte)(120)))));
this.timeUpDownStartTime.ButtonForeColor = System.Drawing.SystemColors.ControlText;
this.timeUpDownStartTime.ButtonForeColorDown = System.Drawing.Color.Orange;
this.timeUpDownStartTime.ButtonForeColorOver = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(120)))), ((int)(((byte)(215)))));
this.timeUpDownStartTime.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F);
this.timeUpDownStartTime.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(155)))), ((int)(((byte)(155)))), ((int)(((byte)(155)))));
this.timeUpDownStartTime.Increment = new decimal(new int[] {
100,
0,
0,
0});
this.timeUpDownStartTime.Location = new System.Drawing.Point(8, 26);
this.timeUpDownStartTime.Margin = new System.Windows.Forms.Padding(4);
this.timeUpDownStartTime.Name = "timeUpDownStartTime";
this.timeUpDownStartTime.Size = new System.Drawing.Size(113, 27);
this.timeUpDownStartTime.Size = new System.Drawing.Size(113, 21);
this.timeUpDownStartTime.TabIndex = 0;
timeCode3.Hours = 0;
timeCode3.Milliseconds = 0;
@ -5324,13 +5332,20 @@ namespace Nikse.SubtitleEdit.Forms
//
// numericUpDownDuration
//
this.numericUpDownDuration.BackColor = System.Drawing.SystemColors.Window;
this.numericUpDownDuration.BackColorDisabled = System.Drawing.Color.FromArgb(((int)(((byte)(240)))), ((int)(((byte)(240)))), ((int)(((byte)(240)))));
this.numericUpDownDuration.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(171)))), ((int)(((byte)(173)))), ((int)(((byte)(179)))));
this.numericUpDownDuration.BorderColorDisabled = System.Drawing.Color.FromArgb(((int)(((byte)(120)))), ((int)(((byte)(120)))), ((int)(((byte)(120)))));
this.numericUpDownDuration.ButtonForeColor = System.Drawing.SystemColors.ControlText;
this.numericUpDownDuration.ButtonForeColorDown = System.Drawing.Color.Orange;
this.numericUpDownDuration.ButtonForeColorOver = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(120)))), ((int)(((byte)(215)))));
this.numericUpDownDuration.DecimalPlaces = 3;
this.numericUpDownDuration.Increment = new decimal(new int[] {
1,
0,
0,
65536});
this.numericUpDownDuration.Location = new System.Drawing.Point(122, 27);
this.numericUpDownDuration.Location = new System.Drawing.Point(122, 26);
this.numericUpDownDuration.Maximum = new decimal(new int[] {
999999999,
0,
@ -5342,8 +5357,14 @@ namespace Nikse.SubtitleEdit.Forms
0,
-2147483648});
this.numericUpDownDuration.Name = "numericUpDownDuration";
this.numericUpDownDuration.Size = new System.Drawing.Size(56, 20);
this.numericUpDownDuration.Size = new System.Drawing.Size(56, 21);
this.numericUpDownDuration.TabIndex = 1;
this.numericUpDownDuration.ThousandsSeparator = false;
this.numericUpDownDuration.Value = new decimal(new int[] {
0,
0,
0,
196608});
this.numericUpDownDuration.ValueChanged += new System.EventHandler(this.NumericUpDownDurationValueChanged);
//
// buttonPrevious
@ -5733,7 +5754,6 @@ namespace Nikse.SubtitleEdit.Forms
this.panelBookmark.PerformLayout();
this.contextMenuStripTextBoxListView.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.pictureBoxBookmark)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.numericUpDownDuration)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.pictureBoxRecord)).EndInit();
this.contextMenuStripTextBoxSourceView.ResumeLayout(false);
this.panelVideoPlayer.ResumeLayout(false);
@ -5816,14 +5836,14 @@ namespace Nikse.SubtitleEdit.Forms
private System.Windows.Forms.Button buttonAutoBreak;
private System.Windows.Forms.ToolStripMenuItem removeFormattinglToolStripMenuItem;
private System.Windows.Forms.Label labelTextLineLengths;
private System.Windows.Forms.NumericUpDown numericUpDownDuration;
private Nikse.SubtitleEdit.Controls.NikseUpDown numericUpDownDuration;
private System.Windows.Forms.Label labelStartTimeWarning;
private System.Windows.Forms.Button buttonUnBreak;
private System.Windows.Forms.ToolStripMenuItem colorToolStripMenuItem;
private System.Windows.Forms.Label labelTextLineTotal;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator2;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator10;
private Nikse.SubtitleEdit.Controls.TimeUpDown timeUpDownStartTime;
private Nikse.SubtitleEdit.Controls.NikseTimeUpDown timeUpDownStartTime;
private System.Windows.Forms.ToolStripMenuItem ChangeCasingToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem toolStripMenuItemMergeLines;
private System.Windows.Forms.ToolStripMenuItem toolStripMenuItemSortBy;

View File

@ -4689,9 +4689,9 @@
this.groupBoxGraphicsButtons.Controls.Add(this.pictureBoxPreview1);
this.groupBoxGraphicsButtons.Controls.Add(this.labelToolbarIconTheme);
this.groupBoxGraphicsButtons.Controls.Add(this.comboBoxToolbarIconTheme);
this.groupBoxGraphicsButtons.Location = new System.Drawing.Point(383, 310);
this.groupBoxGraphicsButtons.Location = new System.Drawing.Point(383, 307);
this.groupBoxGraphicsButtons.Name = "groupBoxGraphicsButtons";
this.groupBoxGraphicsButtons.Size = new System.Drawing.Size(461, 109);
this.groupBoxGraphicsButtons.Size = new System.Drawing.Size(461, 114);
this.groupBoxGraphicsButtons.TabIndex = 41;
this.groupBoxGraphicsButtons.TabStop = false;
this.groupBoxGraphicsButtons.Text = "Graphics buttons";
@ -5414,8 +5414,8 @@
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(1092, 574);
this.Controls.Add(this.labelUpdateFileTypeAssociationsStatus);
this.Controls.Add(this.panelVideoPlayer);
this.Controls.Add(this.panelFont);
this.Controls.Add(this.panelVideoPlayer);
this.Controls.Add(this.panelToolBar);
this.Controls.Add(this.panelTools);
this.Controls.Add(this.panelWaveform);

View File

@ -3,7 +3,7 @@
<General>
<Title>Subtitle Edit</Title>
<Version>3.6.13</Version>
<TranslatedBy>Tradotto da NAMP e bovirus - Data traduzione: 13.07.2023</TranslatedBy>
<TranslatedBy>Tradotto da NAMP e bovirus - Data traduzione: 15.07.2023</TranslatedBy>
<CultureName>it-IT</CultureName>
<HelpFile />
<Ok>OK</Ok>
@ -2840,7 +2840,7 @@ Vuoi continuare?</RestoreDefaultSettingsMsg>
<NumberOfBoldTags>Numero tag grassetto: {0}</NumberOfBoldTags>
<NumberOfUnderlineTags>Numero tag sottolineatura: {0}</NumberOfUnderlineTags>
<NumberOfFontTags>Numero tag per font: {0}</NumberOfFontTags>
<NumberOfAlignmentTags>Numero di tag allineamento: {0}</NumberOfAlignmentTags>
<NumberOfAlignmentTags>Numero tag allineamento: {0}</NumberOfAlignmentTags>
<LineLengthMinimum>Lunghezza sottotitolo - minima: {0}</LineLengthMinimum>
<LineLengthMaximum>Lunghezza sottotitolo - massima: {0}</LineLengthMaximum>
<LineLengthAverage>Lunghezza sottotitolo - media: {0}</LineLengthAverage>
@ -2848,18 +2848,29 @@ Vuoi continuare?</RestoreDefaultSettingsMsg>
<SingleLineLengthMinimum>Lunghezza riga singola - minimo: {0}</SingleLineLengthMinimum>
<SingleLineLengthMaximum>Lunghezza riga singola - massima: {0}</SingleLineLengthMaximum>
<SingleLineLengthAverage>Lunghezza riga singola - media: {0}</SingleLineLengthAverage>
<SingleLineWidthMinimum>Larghezza riga singola - minima: {0} pixels</SingleLineWidthMinimum>
<SingleLineWidthMaximum>Larghezza riga singola - massima: {0} pixels</SingleLineWidthMaximum>
<SingleLineWidthAverage>Larghezza riga singola - media: {0} pixels</SingleLineWidthAverage>
<SingleLineLengthExceedingMaximum>Lunghezza riga singola - superiore al massimo ({0} caratteri): {1} ({2:0.00}%)</SingleLineLengthExceedingMaximum>
<SingleLineWidthMinimum>Larghezza riga singola - minima: {0} pixel</SingleLineWidthMinimum>
<SingleLineWidthMaximum>Larghezza riga singola - massima: {0} pixel</SingleLineWidthMaximum>
<SingleLineWidthExceedingMaximum>Larghezza riga singola - superiore al massimo ({0} pixel): {1} ({2:0.00}%)</SingleLineWidthExceedingMaximum>
<SingleLineWidthAverage>Larghezza riga singola - media: {0} pixel</SingleLineWidthAverage>
<DurationMinimum>Durata - minima: {0:0.000} secondi</DurationMinimum>
<DurationMaximum>Durata - massima: {0:0.000} secondi</DurationMaximum>
<DurationAverage>Durata - media: {0:0.000} secondi</DurationAverage>
<DurationExceedingMinimum>Durata - inferiore al minimo ({0:0.###} sec): {1} ({2:0.00}%)</DurationExceedingMinimum>
<DurationExceedingMaximum>Durata - superiore al massimo ({0:0.###} sec): {1} ({2:0.00}%)</DurationExceedingMaximum>
<CharactersPerSecondMinimum>Caratteri/sec - minimuo: {0:0.000}</CharactersPerSecondMinimum>
<CharactersPerSecondMaximum>Caratteri/sec - massimo: {0:0.000}</CharactersPerSecondMaximum>
<CharactersPerSecondAverage>Caratteri/sec - media: {0:0.000}</CharactersPerSecondAverage>
<CharactersPerSecondExceedingOptimal>Caratteri/sec - superiore ad ottimale ({0:0.##} cps): {1} ({2:0.00}%)</CharactersPerSecondExceedingOptimal>
<CharactersPerSecondExceedingMaximum>Caratteri/sec - superiore al massimo ({0:0.##} cps): {1} ({2:0.00}%)</CharactersPerSecondExceedingMaximum>
<WordsPerMinuteMinimum>Parole/min - minimo: {0:0.000}</WordsPerMinuteMinimum>
<WordsPerMinuteMaximum>Parole/min - massimo: {0:0.000}</WordsPerMinuteMaximum>
<WordsPerMinuteAverage>Parole/min - media: {0:0.000}</WordsPerMinuteAverage>
<WordsPerMinuteExceedingMaximum>Words/min - exceeding maximum ({0} wpm): {1} ({2:0.00}%)</WordsPerMinuteExceedingMaximum>
<GapMinimum>Gap - minimo: {0:#,##0} ms</GapMinimum>
<GapMaximum>Gap - massimo: {0:#,##0} ms</GapMaximum>
<GapAverage>Gap - media: {0:#,##0.##} ms</GapAverage>
<GapExceedingMinimum>Gap - inferiore al minimo ({0:#,##0} ms): {1} ({2:0.00}%)</GapExceedingMinimum>
<Export>Esporta...</Export>
</Statistics>
<SubStationAlphaProperties>

View File

@ -2411,6 +2411,7 @@ FFmpeg를 다운로드하고 사용하시겠습니까?</FfmpegNotFound>
<DarkTheme>어두운 테마</DarkTheme>
<DarkThemeEnabled>어두운 테마 사용</DarkThemeEnabled>
<DarkThemeShowGridViewLines>목록 보기 그리드 선 표시</DarkThemeShowGridViewLines>
<GraphicsButtons>그래픽 버튼</GraphicsButtons>
<UpdateShortcut>업데이트</UpdateShortcut>
<FoucsSetVideoPosition>초점에 비디오 위치 설정</FoucsSetVideoPosition>
<ToggleDockUndockOfVideoControls>비디오 컨트롤 연결 | 해제</ToggleDockUndockOfVideoControls>
@ -2846,18 +2847,29 @@ FFmpeg를 다운로드하고 사용하시겠습니까?</FfmpegNotFound>
<SingleLineLengthMinimum>한 줄의 길이 - 최소: {0}</SingleLineLengthMinimum>
<SingleLineLengthMaximum>한 줄의 길이 - 최대: {0}</SingleLineLengthMaximum>
<SingleLineLengthAverage>한 줄의 길이 - 평균: {0}</SingleLineLengthAverage>
<SingleLineLengthExceedingMaximum>한 줄의 길이 - 최대값 초과 ({0} 문자): {1} ({2:0.00}%)</SingleLineLengthExceedingMaximum>
<SingleLineWidthMinimum>한 줄의 너비 - 최소: {0} 픽셀</SingleLineWidthMinimum>
<SingleLineWidthMaximum>한 줄의 너비 - 최대: {0} 픽셀</SingleLineWidthMaximum>
<SingleLineWidthAverage>한 줄의 너비 - 평균: {0} 픽셀</SingleLineWidthAverage>
<SingleLineWidthExceedingMaximum>한 줄의 너비 - 최대값 초과 ({0} 픽셀): {1} ({2:0.00}%)</SingleLineWidthExceedingMaximum>
<DurationMinimum>표시시간 - 최소: {0:0.000}초</DurationMinimum>
<DurationMaximum>표시시간 - 최대: {0:0.000}초</DurationMaximum>
<DurationAverage>표시시간 - 평균: {0:0.000}초</DurationAverage>
<DurationExceedingMinimum>표시시간 - 최소값 미만 ({0:0.###} 초): {1} ({2:0.00}%)</DurationExceedingMinimum>
<DurationExceedingMaximum>표시시간 - 최대값 초과 ({0:0.###} 초): {1} ({2:0.00}%)</DurationExceedingMaximum>
<CharactersPerSecondMinimum>문자/초 - 최소: {0:0.000}</CharactersPerSecondMinimum>
<CharactersPerSecondMaximum>문자/초 - 최대: {0:0.000}</CharactersPerSecondMaximum>
<CharactersPerSecondAverage>문자/초 - 평균: {0:0.000}</CharactersPerSecondAverage>
<CharactersPerSecondExceedingOptimal>문자/초 - 최적값 초과 ({0:0.##} cps): {1} ({2:0.00}%)</CharactersPerSecondExceedingOptimal>
<CharactersPerSecondExceedingMaximum>문자/초 - 최대값 초과 ({0:0.##} cps): {1} ({2:0.00}%)</CharactersPerSecondExceedingMaximum>
<WordsPerMinuteMinimum>단어/분 - 최소: {0:0.000}</WordsPerMinuteMinimum>
<WordsPerMinuteMaximum>단어/분 - 최대: {0:0.000}</WordsPerMinuteMaximum>
<WordsPerMinuteAverage>단어/분 - 평균: {0:0.000}</WordsPerMinuteAverage>
<WordsPerMinuteExceedingMaximum>단어/분 - 최대값 초과 ({0} wpm): {1} ({2:0.00}%)</WordsPerMinuteExceedingMaximum>
<GapMinimum>간격 - 최소: {0:#,##0} 밀리초</GapMinimum>
<GapMaximum>간격 - 최대: {0:#,##0} 밀리초</GapMaximum>
<GapAverage>간격 - 평균: {0:#,##0.##} 밀리초</GapAverage>
<GapExceedingMinimum>간격 - 최소값 미만 ({0:#,##0} ms): {1} ({2:0.00}%)</GapExceedingMinimum>
<Export>내보내기</Export>
</Statistics>
<SubStationAlphaProperties>
@ -3219,4 +3231,9 @@ FFmpeg를 다운로드하고 사용하시겠습니까?</FfmpegNotFound>
<WebVttStyleManager>
<Title>WebVTT 스타일</Title>
</WebVttStyleManager>
<WhisperAdvanced>
<Title>Whisper Advanced - 추가 명령줄 인수</Title>
<CommandLineArguments>Whisper 명령줄에 대한 추가 매개변수:</CommandLineArguments>
<Info>참고: Whisper 구현에 따라 명령줄 매개 변수는 다릅니다!</Info>
</WhisperAdvanced>
</Language>

View File

@ -11,8 +11,8 @@ namespace Nikse.SubtitleEdit.Logic
{
public static class DarkTheme
{
internal static readonly Color BackColor = Configuration.Settings.General.DarkThemeBackColor;
internal static readonly Color ForeColor = Configuration.Settings.General.DarkThemeForeColor;
public static Color BackColor => Configuration.Settings.General.DarkThemeBackColor;
public static Color ForeColor => Configuration.Settings.General.DarkThemeForeColor;
private const int DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19;
private const int DWMWA_USE_IMMERSIVE_DARK_MODE = 20;
@ -264,11 +264,16 @@ namespace Nikse.SubtitleEdit.Logic
c.BackColor = Control.DefaultBackColor;
c.ForeColor = Control.DefaultForeColor;
var newButton = new TextBox();
var buttonBackColor = newButton.BackColor;
newButton.Dispose();
if (c is Button b)
{
b.FlatStyle = FlatStyle.Standard;
b.EnabledChanged -= Button_EnabledChanged;
b.Paint -= Button_Paint;
b.BackColor = buttonBackColor;
}
if (c is CheckBox cb)
@ -354,6 +359,21 @@ namespace Nikse.SubtitleEdit.Logic
lv.EnabledChanged -= ListView_EnabledChanged;
lv.HandleCreated -= ListView_HandleCreated;
}
else if (c is NikseUpDown)
{
c.BackColor = buttonBackColor;
c.ForeColor = Control.DefaultForeColor;
}
else if (c is NikseTimeUpDown)
{
c.BackColor = buttonBackColor;
c.ForeColor = Control.DefaultForeColor;
}
if (c is Button bu)
{
bu.BackColor = buttonBackColor;
}
}
private static void FixControl(Control c)
@ -451,6 +471,20 @@ namespace Nikse.SubtitleEdit.Logic
lv.EnabledChanged += ListView_EnabledChanged;
lv.HandleCreated += ListView_HandleCreated;
}
else if (c is NikseUpDown ud)
{
ud.BackColor = BackColor;
ud.ForeColor = ForeColor;
ud.ButtonForeColor = ForeColor;
ud.BackColorDisabled = BackColor;
}
else if (c is NikseTimeUpDown tud)
{
tud.BackColor = BackColor;
tud.ForeColor = ForeColor;
tud.ButtonForeColor = ForeColor;
tud.BackColorDisabled = BackColor;
}
}
private static void Button_EnabledChanged(object sender, EventArgs e)

View File

@ -109,6 +109,12 @@
<Compile Include="Controls\CuesPreviewView.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Controls\NikseTimeUpDown.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Controls\NikseUpDown.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Controls\SETextBox.cs">
<SubType>Component</SubType>
</Compile>