mirror of
https://github.com/chylex/TweetDuck.git
synced 2025-04-13 18:15:48 +02:00
Update from master
This commit is contained in:
commit
dbb2f10754
Configuration
Core
Controls
FormBrowser.csHandling
Notification
Other/Settings
Utils
Data
CombinedFileStream.csCommandLineArgs.csInjectedHTML.cs
Serialization
TwoKeyDictionary.csWindowState.csPlugins
Program.csReporter.csResources/Scripts
TweetDuck.csprojUpdates
lib/TweetLib.Audio
tests
@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using TweetDuck.Core.Utils;
|
||||
using TweetDuck.Data;
|
||||
|
||||
namespace TweetDuck.Configuration{
|
||||
static class Arguments{
|
||||
|
@ -1,217 +1,140 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using TweetDuck.Core;
|
||||
using TweetDuck.Core.Controls;
|
||||
using TweetDuck.Core.Notification;
|
||||
using TweetDuck.Core.Utils;
|
||||
using TweetDuck.Data;
|
||||
using TweetDuck.Data.Serialization;
|
||||
|
||||
namespace TweetDuck.Configuration{
|
||||
[Serializable]
|
||||
sealed class UserConfig{
|
||||
private static readonly IFormatter Formatter = new BinaryFormatter{ Binder = new LegacyBinder() };
|
||||
sealed class UserConfig : ISerializedObject{
|
||||
private static readonly FileSerializer<UserConfig> Serializer = new FileSerializer<UserConfig>();
|
||||
|
||||
private class LegacyBinder : SerializationBinder{
|
||||
public override Type BindToType(string assemblyName, string typeName){
|
||||
return Type.GetType(string.Format("{0}, {1}", typeName.Replace("TweetDck", "TweetDuck"), assemblyName.Replace("TweetDck", "TweetDuck")));
|
||||
}
|
||||
static UserConfig(){
|
||||
Serializer.RegisterTypeConverter(typeof(WindowState), WindowState.Converter);
|
||||
|
||||
Serializer.RegisterTypeConverter(typeof(Point), new SingleTypeConverter<Point>{
|
||||
ConvertToString = value => $"{value.X} {value.Y}",
|
||||
ConvertToObject = value => {
|
||||
int[] elements = StringUtils.ParseInts(value, ' ');
|
||||
return new Point(elements[0], elements[1]);
|
||||
}
|
||||
});
|
||||
|
||||
Serializer.RegisterTypeConverter(typeof(Size), new SingleTypeConverter<Size>{
|
||||
ConvertToString = value => $"{value.Width} {value.Height}",
|
||||
ConvertToObject = value => {
|
||||
int[] elements = StringUtils.ParseInts(value, ' ');
|
||||
return new Size(elements[0], elements[1]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// CONFIGURATION DATA
|
||||
|
||||
private const int CurrentFileVersion = 12;
|
||||
public WindowState BrowserWindow { get; set; } = new WindowState();
|
||||
public WindowState PluginsWindow { get; set; } = new WindowState();
|
||||
|
||||
// START OF CONFIGURATION
|
||||
public bool ExpandLinksOnHover { get; set; } = true;
|
||||
public bool SwitchAccountSelectors { get; set; } = true;
|
||||
public bool EnableSpellCheck { get; set; } = false;
|
||||
private int _zoomLevel = 100;
|
||||
private bool _muteNotifications;
|
||||
|
||||
private TrayIcon.Behavior _trayBehavior = TrayIcon.Behavior.Disabled;
|
||||
public bool EnableTrayHighlight { get; set; } = true;
|
||||
|
||||
public WindowState BrowserWindow { get; set; }
|
||||
public WindowState PluginsWindow { get; set; }
|
||||
public bool EnableUpdateCheck { get; set; } = true;
|
||||
public string DismissedUpdate { get; set; } = null;
|
||||
|
||||
public bool DisplayNotificationColumn { get; set; }
|
||||
public bool DisplayNotificationTimer { get; set; }
|
||||
public bool NotificationTimerCountDown { get; set; }
|
||||
public bool NotificationSkipOnLinkClick { get; set; }
|
||||
public bool NotificationNonIntrusiveMode { get; set; }
|
||||
public bool DisplayNotificationColumn { get; set; } = false;
|
||||
public bool NotificationSkipOnLinkClick { get; set; } = false;
|
||||
public bool NotificationNonIntrusiveMode { get; set; } = true;
|
||||
public int NotificationIdlePauseSeconds { get; set; } = 0;
|
||||
|
||||
public int NotificationIdlePauseSeconds { get; set; }
|
||||
public int NotificationDurationValue { get; set; }
|
||||
public int NotificationScrollSpeed { get; set; }
|
||||
public bool DisplayNotificationTimer { get; set; } = true;
|
||||
public bool NotificationTimerCountDown { get; set; } = false;
|
||||
public int NotificationDurationValue { get; set; } = 25;
|
||||
|
||||
public TweetNotification.Position NotificationPosition { get; set; }
|
||||
public Point CustomNotificationPosition { get; set; }
|
||||
public int NotificationEdgeDistance { get; set; }
|
||||
public int NotificationDisplay { get; set; }
|
||||
public TweetNotification.Position NotificationPosition { get; set; } = TweetNotification.Position.TopRight;
|
||||
public Point CustomNotificationPosition { get; set; } = ControlExtensions.InvisibleLocation;
|
||||
public int NotificationDisplay { get; set; } = 0;
|
||||
public int NotificationEdgeDistance { get; set; } = 8;
|
||||
|
||||
public TweetNotification.Size NotificationSize { get; set; }
|
||||
public Size CustomNotificationSize { get; set; }
|
||||
public TweetNotification.Size NotificationSize { get; set; } = TweetNotification.Size.Auto;
|
||||
public Size CustomNotificationSize { get; set; } = Size.Empty;
|
||||
public int NotificationScrollSpeed { get; set; } = 10;
|
||||
|
||||
public bool EnableSpellCheck { get; set; }
|
||||
public bool ExpandLinksOnHover { get; set; }
|
||||
public bool SwitchAccountSelectors { get; set; }
|
||||
public bool EnableTrayHighlight { get; set; }
|
||||
private string _notificationSoundPath;
|
||||
|
||||
public bool EnableUpdateCheck { get; set; }
|
||||
public string DismissedUpdate { get; set; }
|
||||
public string CustomCefArgs { get; set; } = null;
|
||||
public string CustomBrowserCSS { get; set; } = null;
|
||||
public string CustomNotificationCSS { get; set; } = null;
|
||||
|
||||
public string CustomCefArgs { get; set; }
|
||||
public string CustomBrowserCSS { get; set; }
|
||||
public string CustomNotificationCSS { get; set; }
|
||||
|
||||
public bool EnableBrowserGCReload { get; set; }
|
||||
public int BrowserMemoryThreshold { get; set; }
|
||||
public bool EnableBrowserGCReload { get; set; } = false;
|
||||
public int BrowserMemoryThreshold { get; set; } = 350;
|
||||
|
||||
// SPECIAL PROPERTIES
|
||||
|
||||
public bool IsCustomNotificationPositionSet => CustomNotificationPosition != ControlExtensions.InvisibleLocation;
|
||||
public bool IsCustomNotificationSizeSet => CustomNotificationSize != Size.Empty;
|
||||
|
||||
public string NotificationSoundPath{
|
||||
get => string.IsNullOrEmpty(notificationSoundPath) ? string.Empty : notificationSoundPath;
|
||||
set => notificationSoundPath = value;
|
||||
get => string.IsNullOrEmpty(_notificationSoundPath) ? string.Empty : _notificationSoundPath;
|
||||
set => _notificationSoundPath = value;
|
||||
}
|
||||
|
||||
public bool MuteNotifications{
|
||||
get => muteNotifications;
|
||||
get => _muteNotifications;
|
||||
|
||||
set{
|
||||
if (muteNotifications != value){
|
||||
muteNotifications = value;
|
||||
if (_muteNotifications != value){
|
||||
_muteNotifications = value;
|
||||
MuteToggled?.Invoke(this, new EventArgs());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int ZoomLevel{
|
||||
get => zoomLevel;
|
||||
get => _zoomLevel;
|
||||
|
||||
set{
|
||||
if (zoomLevel != value){
|
||||
zoomLevel = value;
|
||||
if (_zoomLevel != value){
|
||||
_zoomLevel = value;
|
||||
ZoomLevelChanged?.Invoke(this, new EventArgs());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public double ZoomMultiplier => zoomLevel/100.0;
|
||||
public double ZoomMultiplier => _zoomLevel/100.0;
|
||||
|
||||
public TrayIcon.Behavior TrayBehavior{
|
||||
get => trayBehavior;
|
||||
get => _trayBehavior;
|
||||
|
||||
set{
|
||||
if (trayBehavior != value){
|
||||
trayBehavior = value;
|
||||
if (_trayBehavior != value){
|
||||
_trayBehavior = value;
|
||||
TrayBehaviorChanged?.Invoke(this, new EventArgs());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// END OF CONFIGURATION
|
||||
// EVENTS
|
||||
|
||||
[field:NonSerialized]
|
||||
public event EventHandler MuteToggled;
|
||||
|
||||
[field:NonSerialized]
|
||||
public event EventHandler ZoomLevelChanged;
|
||||
|
||||
[field:NonSerialized]
|
||||
public event EventHandler TrayBehaviorChanged;
|
||||
|
||||
private readonly string file;
|
||||
|
||||
[NonSerialized]
|
||||
private string file;
|
||||
|
||||
private int fileVersion;
|
||||
private bool muteNotifications;
|
||||
private int zoomLevel;
|
||||
private string notificationSoundPath;
|
||||
private TrayIcon.Behavior trayBehavior;
|
||||
|
||||
private UserConfig(string file){
|
||||
public UserConfig(string file){ // TODO make private after removing UserConfigLegacy
|
||||
this.file = file;
|
||||
|
||||
BrowserWindow = new WindowState();
|
||||
ZoomLevel = 100;
|
||||
DisplayNotificationTimer = true;
|
||||
NotificationNonIntrusiveMode = true;
|
||||
NotificationPosition = TweetNotification.Position.TopRight;
|
||||
CustomNotificationPosition = ControlExtensions.InvisibleLocation;
|
||||
NotificationSize = TweetNotification.Size.Auto;
|
||||
NotificationEdgeDistance = 8;
|
||||
NotificationDurationValue = 25;
|
||||
NotificationScrollSpeed = 100;
|
||||
BrowserMemoryThreshold = 350;
|
||||
EnableUpdateCheck = true;
|
||||
ExpandLinksOnHover = true;
|
||||
SwitchAccountSelectors = true;
|
||||
EnableTrayHighlight = true;
|
||||
PluginsWindow = new WindowState();
|
||||
}
|
||||
|
||||
private void UpgradeFile(){
|
||||
if (fileVersion == CurrentFileVersion){
|
||||
return;
|
||||
}
|
||||
|
||||
// if outdated, cycle through all versions
|
||||
if (fileVersion == 0){
|
||||
DisplayNotificationTimer = true;
|
||||
EnableUpdateCheck = true;
|
||||
++fileVersion;
|
||||
}
|
||||
|
||||
if (fileVersion == 1){
|
||||
ExpandLinksOnHover = true;
|
||||
++fileVersion;
|
||||
}
|
||||
|
||||
if (fileVersion == 2){
|
||||
BrowserWindow = new WindowState();
|
||||
PluginsWindow = new WindowState();
|
||||
++fileVersion;
|
||||
}
|
||||
|
||||
if (fileVersion == 3){
|
||||
EnableTrayHighlight = true;
|
||||
NotificationDurationValue = 25;
|
||||
++fileVersion;
|
||||
}
|
||||
|
||||
if (fileVersion == 4){
|
||||
++fileVersion;
|
||||
}
|
||||
|
||||
if (fileVersion == 5){
|
||||
++fileVersion;
|
||||
}
|
||||
|
||||
if (fileVersion == 6){
|
||||
NotificationNonIntrusiveMode = true;
|
||||
++fileVersion;
|
||||
}
|
||||
|
||||
if (fileVersion == 7){
|
||||
ZoomLevel = 100;
|
||||
++fileVersion;
|
||||
}
|
||||
|
||||
if (fileVersion == 8){
|
||||
SwitchAccountSelectors = true;
|
||||
++fileVersion;
|
||||
}
|
||||
|
||||
if (fileVersion == 9){
|
||||
NotificationScrollSpeed = 100;
|
||||
++fileVersion;
|
||||
}
|
||||
|
||||
if (fileVersion == 10){
|
||||
NotificationSize = TweetNotification.Size.Auto;
|
||||
}
|
||||
|
||||
if (fileVersion == 11){
|
||||
BrowserMemoryThreshold = 350;
|
||||
++fileVersion;
|
||||
}
|
||||
|
||||
// update the version
|
||||
fileVersion = CurrentFileVersion;
|
||||
Save();
|
||||
bool ISerializedObject.OnReadUnknownProperty(string property, string value){
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Save(){
|
||||
@ -227,10 +150,7 @@ public bool Save(){
|
||||
File.Move(file, backupFile);
|
||||
}
|
||||
|
||||
using(Stream stream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None)){
|
||||
Formatter.Serialize(stream, this);
|
||||
}
|
||||
|
||||
Serializer.Write(file, this);
|
||||
return true;
|
||||
}catch(Exception e){
|
||||
Program.Reporter.HandleException("Configuration Error", "Could not save the configuration file.", true, e);
|
||||
@ -239,22 +159,20 @@ public bool Save(){
|
||||
}
|
||||
|
||||
public static UserConfig Load(string file){
|
||||
UserConfig config = null;
|
||||
Exception firstException = null;
|
||||
|
||||
for(int attempt = 0; attempt < 2; attempt++){
|
||||
try{
|
||||
using(Stream stream = new FileStream(attempt == 0 ? file : GetBackupFile(file), FileMode.Open, FileAccess.Read, FileShare.Read)){
|
||||
if ((config = Formatter.Deserialize(stream) as UserConfig) != null){
|
||||
config.file = file;
|
||||
}
|
||||
}
|
||||
|
||||
config?.UpgradeFile();
|
||||
break;
|
||||
UserConfig config = new UserConfig(file);
|
||||
Serializer.Read(attempt == 0 ? file : GetBackupFile(file), config);
|
||||
return config;
|
||||
}catch(FileNotFoundException){
|
||||
}catch(DirectoryNotFoundException){
|
||||
break;
|
||||
}catch(FormatException){
|
||||
UserConfig config = UserConfigLegacy.Load(file);
|
||||
config.Save();
|
||||
return config;
|
||||
}catch(Exception e){
|
||||
if (attempt == 0){
|
||||
firstException = e;
|
||||
@ -266,11 +184,11 @@ public static UserConfig Load(string file){
|
||||
}
|
||||
}
|
||||
|
||||
if (firstException != null && config == null){
|
||||
if (firstException != null){
|
||||
Program.Reporter.HandleException("Configuration Error", "Could not open the configuration file.", true, firstException);
|
||||
}
|
||||
|
||||
return config ?? new UserConfig(file);
|
||||
return new UserConfig(file);
|
||||
}
|
||||
|
||||
public static string GetBackupFile(string file){
|
||||
|
210
Configuration/UserConfigLegacy.cs
Normal file
210
Configuration/UserConfigLegacy.cs
Normal file
@ -0,0 +1,210 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using TweetDuck.Core;
|
||||
using TweetDuck.Core.Controls;
|
||||
using TweetDuck.Core.Notification;
|
||||
using TweetDuck.Data;
|
||||
|
||||
namespace TweetDuck.Configuration{
|
||||
[Serializable]
|
||||
sealed class UserConfigLegacy{ // TODO remove eventually
|
||||
private static readonly IFormatter Formatter = new BinaryFormatter{ Binder = new LegacyBinder() };
|
||||
|
||||
private class LegacyBinder : SerializationBinder{
|
||||
public override Type BindToType(string assemblyName, string typeName){
|
||||
return Type.GetType(string.Format("{0}, {1}", typeName.Replace("TweetDck", "TweetDuck").Replace(".UserConfig", ".UserConfigLegacy").Replace("Core.Utils.WindowState", "Data.WindowState"), assemblyName.Replace("TweetDck", "TweetDuck")));
|
||||
}
|
||||
}
|
||||
|
||||
private const int CurrentFileVersion = 11;
|
||||
|
||||
// START OF CONFIGURATION
|
||||
|
||||
public WindowState BrowserWindow { get; set; }
|
||||
public WindowState PluginsWindow { get; set; }
|
||||
|
||||
public bool DisplayNotificationColumn { get; set; }
|
||||
public bool DisplayNotificationTimer { get; set; }
|
||||
public bool NotificationTimerCountDown { get; set; }
|
||||
public bool NotificationSkipOnLinkClick { get; set; }
|
||||
public bool NotificationNonIntrusiveMode { get; set; }
|
||||
|
||||
public int NotificationIdlePauseSeconds { get; set; }
|
||||
public int NotificationDurationValue { get; set; }
|
||||
public int NotificationScrollSpeed { get; set; }
|
||||
|
||||
public TweetNotification.Position NotificationPosition { get; set; }
|
||||
public Point CustomNotificationPosition { get; set; }
|
||||
public int NotificationEdgeDistance { get; set; }
|
||||
public int NotificationDisplay { get; set; }
|
||||
|
||||
public TweetNotification.Size NotificationSize { get; set; }
|
||||
public Size CustomNotificationSize { get; set; }
|
||||
|
||||
public bool EnableSpellCheck { get; set; }
|
||||
public bool ExpandLinksOnHover { get; set; }
|
||||
public bool SwitchAccountSelectors { get; set; }
|
||||
public bool EnableTrayHighlight { get; set; }
|
||||
|
||||
public bool EnableUpdateCheck { get; set; }
|
||||
public string DismissedUpdate { get; set; }
|
||||
|
||||
public string CustomCefArgs { get; set; }
|
||||
public string CustomBrowserCSS { get; set; }
|
||||
public string CustomNotificationCSS { get; set; }
|
||||
|
||||
public string NotificationSoundPath{
|
||||
get => string.IsNullOrEmpty(notificationSoundPath) ? string.Empty : notificationSoundPath;
|
||||
set => notificationSoundPath = value;
|
||||
}
|
||||
|
||||
public bool MuteNotifications{
|
||||
get => muteNotifications;
|
||||
set => muteNotifications = value;
|
||||
}
|
||||
|
||||
public int ZoomLevel{
|
||||
get => zoomLevel;
|
||||
set => zoomLevel = value;
|
||||
}
|
||||
|
||||
public TrayIcon.Behavior TrayBehavior{
|
||||
get => trayBehavior;
|
||||
set => trayBehavior = value;
|
||||
}
|
||||
|
||||
// END OF CONFIGURATION
|
||||
|
||||
[NonSerialized]
|
||||
private string file;
|
||||
|
||||
private int fileVersion;
|
||||
private bool muteNotifications;
|
||||
private int zoomLevel;
|
||||
private string notificationSoundPath;
|
||||
private TrayIcon.Behavior trayBehavior;
|
||||
|
||||
private UserConfigLegacy(string file){
|
||||
this.file = file;
|
||||
|
||||
BrowserWindow = new WindowState();
|
||||
ZoomLevel = 100;
|
||||
DisplayNotificationTimer = true;
|
||||
NotificationNonIntrusiveMode = true;
|
||||
NotificationPosition = TweetNotification.Position.TopRight;
|
||||
CustomNotificationPosition = ControlExtensions.InvisibleLocation;
|
||||
NotificationSize = TweetNotification.Size.Auto;
|
||||
NotificationEdgeDistance = 8;
|
||||
NotificationDurationValue = 25;
|
||||
NotificationScrollSpeed = 100;
|
||||
EnableUpdateCheck = true;
|
||||
ExpandLinksOnHover = true;
|
||||
SwitchAccountSelectors = true;
|
||||
EnableTrayHighlight = true;
|
||||
PluginsWindow = new WindowState();
|
||||
}
|
||||
|
||||
private void UpgradeFile(){
|
||||
if (fileVersion == CurrentFileVersion){
|
||||
return;
|
||||
}
|
||||
|
||||
// if outdated, cycle through all versions
|
||||
if (fileVersion <= 5){
|
||||
DisplayNotificationTimer = true;
|
||||
EnableUpdateCheck = true;
|
||||
ExpandLinksOnHover = true;
|
||||
BrowserWindow = new WindowState();
|
||||
PluginsWindow = new WindowState();
|
||||
EnableTrayHighlight = true;
|
||||
NotificationDurationValue = 25;
|
||||
fileVersion = 6;
|
||||
}
|
||||
|
||||
if (fileVersion == 6){
|
||||
NotificationNonIntrusiveMode = true;
|
||||
++fileVersion;
|
||||
}
|
||||
|
||||
if (fileVersion == 7){
|
||||
ZoomLevel = 100;
|
||||
++fileVersion;
|
||||
}
|
||||
|
||||
if (fileVersion == 8){
|
||||
SwitchAccountSelectors = true;
|
||||
++fileVersion;
|
||||
}
|
||||
|
||||
if (fileVersion == 9){
|
||||
NotificationScrollSpeed = 100;
|
||||
++fileVersion;
|
||||
}
|
||||
|
||||
if (fileVersion == 10){
|
||||
NotificationSize = TweetNotification.Size.Auto;
|
||||
++fileVersion;
|
||||
}
|
||||
|
||||
// update the version
|
||||
fileVersion = CurrentFileVersion;
|
||||
}
|
||||
|
||||
public UserConfig ConvertLegacy(){
|
||||
return new UserConfig(file){
|
||||
BrowserWindow = BrowserWindow,
|
||||
PluginsWindow = PluginsWindow,
|
||||
DisplayNotificationColumn = DisplayNotificationColumn,
|
||||
DisplayNotificationTimer = DisplayNotificationTimer,
|
||||
NotificationTimerCountDown = NotificationTimerCountDown,
|
||||
NotificationSkipOnLinkClick = NotificationSkipOnLinkClick,
|
||||
NotificationNonIntrusiveMode = NotificationNonIntrusiveMode,
|
||||
NotificationIdlePauseSeconds = NotificationIdlePauseSeconds,
|
||||
NotificationDurationValue = NotificationDurationValue,
|
||||
NotificationScrollSpeed = NotificationScrollSpeed,
|
||||
NotificationPosition = NotificationPosition,
|
||||
CustomNotificationPosition = CustomNotificationPosition,
|
||||
NotificationEdgeDistance = NotificationEdgeDistance,
|
||||
NotificationDisplay = NotificationDisplay,
|
||||
NotificationSize = NotificationSize,
|
||||
CustomNotificationSize = CustomNotificationSize,
|
||||
EnableSpellCheck = EnableSpellCheck,
|
||||
ExpandLinksOnHover = ExpandLinksOnHover,
|
||||
SwitchAccountSelectors = SwitchAccountSelectors,
|
||||
EnableTrayHighlight = EnableTrayHighlight,
|
||||
EnableUpdateCheck = EnableUpdateCheck,
|
||||
DismissedUpdate = DismissedUpdate,
|
||||
CustomCefArgs = CustomCefArgs,
|
||||
CustomBrowserCSS = CustomBrowserCSS,
|
||||
CustomNotificationCSS = CustomNotificationCSS,
|
||||
NotificationSoundPath = NotificationSoundPath,
|
||||
MuteNotifications = MuteNotifications,
|
||||
ZoomLevel = ZoomLevel,
|
||||
TrayBehavior = TrayBehavior
|
||||
};
|
||||
}
|
||||
|
||||
public static UserConfig Load(string file){
|
||||
UserConfigLegacy config = null;
|
||||
|
||||
try{
|
||||
using(Stream stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read)){
|
||||
if ((config = Formatter.Deserialize(stream) as UserConfigLegacy) != null){
|
||||
config.file = file;
|
||||
}
|
||||
}
|
||||
|
||||
config?.UpgradeFile();
|
||||
}catch(FileNotFoundException){
|
||||
}catch(DirectoryNotFoundException){
|
||||
}catch(Exception e){
|
||||
Program.Reporter.HandleException("Configuration Error", "Could not open the configuration file.", true, e);
|
||||
}
|
||||
|
||||
return (config ?? new UserConfigLegacy(file)).ConvertLegacy();
|
||||
}
|
||||
}
|
||||
}
|
@ -70,7 +70,7 @@ public static bool AlignValueToTick(this TrackBar trackBar){
|
||||
public static void SetElevated(this Button button){
|
||||
button.Text = " "+button.Text;
|
||||
button.FlatStyle = FlatStyle.System;
|
||||
NativeMethods.SendMessage(button.Handle, NativeMethods.BCM_SETSHIELD, 0, new IntPtr(1));
|
||||
NativeMethods.SendMessage(button.Handle, NativeMethods.BCM_SETSHIELD, new UIntPtr(0), new IntPtr(1));
|
||||
}
|
||||
|
||||
public static void EnableMultilineShortcuts(this TextBox textBox){
|
||||
|
@ -19,7 +19,7 @@
|
||||
using TweetDuck.Resources;
|
||||
using TweetDuck.Updates;
|
||||
using TweetDuck.Updates.Events;
|
||||
using TweetLib.Audio.Utils;
|
||||
using TweetLib.Audio;
|
||||
|
||||
namespace TweetDuck.Core{
|
||||
sealed partial class FormBrowser : Form{
|
||||
@ -208,7 +208,7 @@ private void browser_LoadError(object sender, LoadErrorEventArgs e){
|
||||
return;
|
||||
}
|
||||
|
||||
if (!e.FailedUrl.StartsWith("http://td/")){
|
||||
if (!e.FailedUrl.StartsWith("http://td/", StringComparison.Ordinal)){
|
||||
string errorPage = ScriptLoader.LoadResource("pages/error.html", true);
|
||||
|
||||
if (errorPage != null){
|
||||
@ -406,7 +406,7 @@ public void UpdateProperties(PropertyBridge.Properties properties){
|
||||
}
|
||||
|
||||
public void ReloadToTweetDeck(){
|
||||
browser.ExecuteScriptAsync("gc&&gc();window.location.href='https://tweetdeck.twitter.com'");
|
||||
browser.ExecuteScriptAsync($"gc&&gc();window.location.href='{BrowserUtils.TweetDeckURL}'");
|
||||
}
|
||||
|
||||
// callback handlers
|
||||
@ -485,7 +485,7 @@ public void PlayNotificationSound(){
|
||||
|
||||
public void OnTweetScreenshotReady(string html, int width, int height){
|
||||
if (notificationScreenshotManager == null){
|
||||
notificationScreenshotManager = new TweetScreenshotManager(this);
|
||||
notificationScreenshotManager = new TweetScreenshotManager(this, plugins);
|
||||
}
|
||||
|
||||
notificationScreenshotManager.Trigger(html, width, height);
|
||||
|
@ -113,7 +113,7 @@ protected void SetClipboardText(string text){
|
||||
form.InvokeAsyncSafe(() => WindowsUtils.SetClipboard(text, TextDataFormat.UnicodeText));
|
||||
}
|
||||
|
||||
protected void AddDebugMenuItems(IMenuModel model){
|
||||
protected static void AddDebugMenuItems(IMenuModel model){
|
||||
model.AddItem((CefMenuCommand)MenuOpenDevTools, "Open dev tools");
|
||||
}
|
||||
|
||||
@ -134,11 +134,7 @@ private static string GetImageFileName(string url){
|
||||
int dot = url.LastIndexOf('.');
|
||||
|
||||
if (dot != -1){
|
||||
int colon = url.IndexOf(':', dot);
|
||||
|
||||
if (colon != -1){
|
||||
url = url.Substring(0, colon);
|
||||
}
|
||||
url = StringUtils.ExtractBefore(url, ':', dot);
|
||||
}
|
||||
|
||||
// return file name
|
||||
|
@ -111,7 +111,7 @@ public FormNotificationBase(Form owner, bool enableContextMenu){
|
||||
this.dpiScale = this.GetDPIScale();
|
||||
|
||||
DefaultResourceHandlerFactory handlerFactory = (DefaultResourceHandlerFactory)browser.ResourceHandlerFactory;
|
||||
handlerFactory.RegisterHandler("https://tweetdeck.twitter.com", this.resourceHandler);
|
||||
handlerFactory.RegisterHandler(BrowserUtils.TweetDeckURL, this.resourceHandler);
|
||||
|
||||
Controls.Add(browser);
|
||||
|
||||
@ -183,7 +183,7 @@ protected virtual void LoadTweet(TweetNotification tweet){
|
||||
currentColumn = tweet.Column;
|
||||
|
||||
resourceHandler.SetHTML(GetTweetHTML(tweet));
|
||||
browser.Load("https://tweetdeck.twitter.com");
|
||||
browser.Load(BrowserUtils.TweetDeckURL);
|
||||
}
|
||||
|
||||
protected virtual void SetNotificationSize(int width, int height){
|
||||
|
@ -5,6 +5,7 @@
|
||||
using TweetDuck.Core.Bridge;
|
||||
using TweetDuck.Core.Controls;
|
||||
using TweetDuck.Core.Utils;
|
||||
using TweetDuck.Data;
|
||||
using TweetDuck.Plugins;
|
||||
using TweetDuck.Plugins.Enums;
|
||||
using TweetDuck.Resources;
|
||||
@ -17,12 +18,8 @@ partial class FormNotificationMain : FormNotificationBase{
|
||||
private static readonly string NotificationScriptIdentifier = ScriptLoader.GetRootIdentifier(NotificationScriptFile);
|
||||
private static readonly string PluginScriptIdentifier = ScriptLoader.GetRootIdentifier(PluginManager.PluginNotificationScriptFile);
|
||||
|
||||
private static readonly string NotificationJS, PluginJS;
|
||||
|
||||
static FormNotificationMain(){
|
||||
NotificationJS = ScriptLoader.LoadResource(NotificationScriptFile);
|
||||
PluginJS = ScriptLoader.LoadResource(PluginManager.PluginNotificationScriptFile);
|
||||
}
|
||||
private static readonly string NotificationJS = ScriptLoader.LoadResource(NotificationScriptFile);
|
||||
private static readonly string PluginJS = ScriptLoader.LoadResource(PluginManager.PluginNotificationScriptFile);
|
||||
|
||||
private readonly PluginManager plugins;
|
||||
|
||||
|
@ -4,11 +4,17 @@
|
||||
using System.Windows.Forms;
|
||||
using TweetDuck.Core.Bridge;
|
||||
using TweetDuck.Core.Utils;
|
||||
using TweetDuck.Data;
|
||||
using TweetDuck.Plugins;
|
||||
using TweetDuck.Resources;
|
||||
|
||||
namespace TweetDuck.Core.Notification.Screenshot{
|
||||
sealed class FormNotificationScreenshotable : FormNotificationBase{
|
||||
public FormNotificationScreenshotable(Action callback, Form owner) : base(owner, false){
|
||||
private readonly PluginManager plugins;
|
||||
|
||||
public FormNotificationScreenshotable(Action callback, Form owner, PluginManager pluginManager) : base(owner, false){
|
||||
this.plugins = pluginManager;
|
||||
|
||||
browser.RegisterAsyncJsObject("$TD_NotificationScreenshot", new CallbackBridge(this, callback));
|
||||
|
||||
browser.FrameLoadEnd += (sender, args) => {
|
||||
@ -17,9 +23,15 @@ public FormNotificationScreenshotable(Action callback, Form owner) : base(owner,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
protected override string GetTweetHTML(TweetNotification tweet){
|
||||
return tweet.GenerateHtml(enableCustomCSS: false);
|
||||
string html = tweet.GenerateHtml("td-screenshot", false);
|
||||
|
||||
foreach(InjectedHTML injection in plugins.Bridge.NotificationInjections){
|
||||
html = injection.Inject(html);
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
public void LoadNotificationForScreenshot(TweetNotification tweet, int width, int height){
|
||||
|
@ -4,17 +4,20 @@
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using TweetDuck.Core.Controls;
|
||||
using TweetDuck.Plugins;
|
||||
|
||||
namespace TweetDuck.Core.Notification.Screenshot{
|
||||
sealed class TweetScreenshotManager : IDisposable{
|
||||
private readonly Form owner;
|
||||
private readonly PluginManager plugins;
|
||||
private readonly Timer timeout;
|
||||
private readonly Timer disposer;
|
||||
|
||||
private FormNotificationScreenshotable screenshot;
|
||||
|
||||
public TweetScreenshotManager(Form owner){
|
||||
public TweetScreenshotManager(Form owner, PluginManager pluginManager){
|
||||
this.owner = owner;
|
||||
this.plugins = pluginManager;
|
||||
|
||||
this.timeout = new Timer{ Interval = 8000 };
|
||||
this.timeout.Tick += timeout_Tick;
|
||||
@ -40,7 +43,7 @@ public void Trigger(string html, int width, int height){
|
||||
return;
|
||||
}
|
||||
|
||||
screenshot = new FormNotificationScreenshotable(Callback, owner){
|
||||
screenshot = new FormNotificationScreenshotable(Callback, owner, plugins){
|
||||
CanMoveWindow = () => false
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using TweetLib.Audio;
|
||||
using TweetLib.Audio.Utils;
|
||||
|
||||
namespace TweetDuck.Core.Notification{
|
||||
sealed class SoundNotification : IDisposable{
|
||||
|
@ -3,7 +3,7 @@
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using TweetDuck.Configuration;
|
||||
using TweetDuck.Core.Utils;
|
||||
using TweetDuck.Data;
|
||||
|
||||
namespace TweetDuck.Core.Other.Settings.Dialogs{
|
||||
sealed partial class DialogSettingsRestart : Form{
|
||||
|
@ -4,6 +4,7 @@
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using TweetDuck.Configuration;
|
||||
using TweetDuck.Data;
|
||||
using TweetDuck.Plugins;
|
||||
using TweetDuck.Plugins.Enums;
|
||||
|
||||
|
@ -68,10 +68,10 @@ public TabSettingsNotifications(FormNotificationMain notification){
|
||||
checkNonIntrusive.Checked = Config.NotificationNonIntrusiveMode;
|
||||
|
||||
trackBarScrollSpeed.SetValueSafe(Config.NotificationScrollSpeed);
|
||||
labelScrollSpeedValue.Text = trackBarScrollSpeed.Value.ToString(CultureInfo.InvariantCulture)+"%";
|
||||
labelScrollSpeedValue.Text = trackBarScrollSpeed.Value+"%";
|
||||
|
||||
trackBarEdgeDistance.SetValueSafe(Config.NotificationEdgeDistance);
|
||||
labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value.ToString(CultureInfo.InvariantCulture)+" px";
|
||||
labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value+" px";
|
||||
|
||||
this.notification.CanMoveWindow = () => radioLocCustom.Checked;
|
||||
this.notification.CanResizeWindow = radioSizeCustom.Checked;
|
||||
@ -233,7 +233,7 @@ private void comboBoxIdlePause_SelectedValueChanged(object sender, EventArgs e){
|
||||
|
||||
private void trackBarScrollSpeed_ValueChanged(object sender, EventArgs e){
|
||||
if (trackBarScrollSpeed.AlignValueToTick()){
|
||||
labelScrollSpeedValue.Text = trackBarScrollSpeed.Value.ToString(CultureInfo.InvariantCulture)+"%";
|
||||
labelScrollSpeedValue.Text = trackBarScrollSpeed.Value+"%";
|
||||
Config.NotificationScrollSpeed = trackBarScrollSpeed.Value;
|
||||
}
|
||||
}
|
||||
@ -244,7 +244,7 @@ private void comboBoxDisplay_SelectedValueChanged(object sender, EventArgs e){
|
||||
}
|
||||
|
||||
private void trackBarEdgeDistance_ValueChanged(object sender, EventArgs e){
|
||||
labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value.ToString(CultureInfo.InvariantCulture)+" px";
|
||||
labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value+" px";
|
||||
Config.NotificationEdgeDistance = trackBarEdgeDistance.Value;
|
||||
notification.ShowNotificationForSettings(false);
|
||||
}
|
||||
|
@ -77,6 +77,7 @@ private void InitializeComponent() {
|
||||
this.tbCustomSound.Name = "tbCustomSound";
|
||||
this.tbCustomSound.Size = new System.Drawing.Size(316, 20);
|
||||
this.tbCustomSound.TabIndex = 0;
|
||||
this.toolTip.SetToolTip(this.tbCustomSound, "When empty, the default TweetDeck sound notification is used.");
|
||||
//
|
||||
// labelSoundNotification
|
||||
//
|
||||
|
@ -3,7 +3,7 @@
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
using TweetDuck.Core.Notification;
|
||||
using TweetLib.Audio.Utils;
|
||||
using TweetLib.Audio;
|
||||
|
||||
namespace TweetDuck.Core.Other.Settings{
|
||||
partial class TabSettingsSounds : BaseTabSettings{
|
||||
|
@ -3,23 +3,23 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace TweetDuck.Core.Utils{
|
||||
static class BrowserUtils{
|
||||
public const string TweetDeckURL = "https://tweetdeck.twitter.com";
|
||||
|
||||
public static string HeaderAcceptLanguage{
|
||||
get{
|
||||
string culture = CultureInfo.CurrentCulture.Name;
|
||||
string culture = Program.Culture.Name;
|
||||
|
||||
if (culture == "en"){
|
||||
return "en-us,en";
|
||||
}
|
||||
else{
|
||||
return culture.ToLowerInvariant()+",en;q=0.9";
|
||||
return culture.ToLower()+",en;q=0.9";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -80,12 +80,8 @@ public static string GetFileNameFromUrl(string url){
|
||||
return string.IsNullOrEmpty(file) ? null : file;
|
||||
}
|
||||
|
||||
public static string ConvertPascalCaseToScreamingSnakeCase(string str){
|
||||
return Regex.Replace(str, @"(\p{Ll})(\P{Ll})|(\P{Ll})(\P{Ll}\p{Ll})", "$1$3_$2$4").ToUpperInvariant();
|
||||
}
|
||||
|
||||
public static string GetErrorName(CefErrorCode code){
|
||||
return ConvertPascalCaseToScreamingSnakeCase(Enum.GetName(typeof(CefErrorCode), code) ?? string.Empty);
|
||||
return StringUtils.ConvertPascalCaseToScreamingSnakeCase(Enum.GetName(typeof(CefErrorCode), code) ?? string.Empty);
|
||||
}
|
||||
|
||||
public static WebClient DownloadFileAsync(string url, string target, Action onSuccess, Action<Exception> onFailure){
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using TweetDuck.Data;
|
||||
|
||||
namespace TweetDuck.Core.Utils{
|
||||
static class CommandLineArgsParser{
|
||||
|
@ -67,10 +67,10 @@ private struct MSLLHOOKSTRUCT{
|
||||
private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam, IntPtr lParam);
|
||||
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, UIntPtr wParam, IntPtr lParam);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool PostMessage(IntPtr hWnd, uint msg, int wParam, IntPtr lParam);
|
||||
public static extern bool PostMessage(IntPtr hWnd, uint msg, UIntPtr wParam, IntPtr lParam);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern uint RegisterWindowMessage(string messageName);
|
||||
|
20
Core/Utils/StringUtils.cs
Normal file
20
Core/Utils/StringUtils.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace TweetDuck.Core.Utils{
|
||||
static class StringUtils{
|
||||
public static string ExtractBefore(string str, char search, int startIndex = 0){
|
||||
int index = str.IndexOf(search, startIndex);
|
||||
return index == -1 ? str : str.Substring(0, index);
|
||||
}
|
||||
|
||||
public static int[] ParseInts(string str, char separator){
|
||||
return str.Split(new char[]{ separator }, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse).ToArray();
|
||||
}
|
||||
|
||||
public static string ConvertPascalCaseToScreamingSnakeCase(string str){
|
||||
return Regex.Replace(str, @"(\p{Ll})(\P{Ll})|(\P{Ll})(\P{Ll}\p{Ll})", "$1$3_$2$4").ToUpper();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +1,10 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using TweetDuck.Core.Utils;
|
||||
|
||||
namespace TweetDuck.Core.Other.Settings.Export{
|
||||
class CombinedFileStream : IDisposable{
|
||||
namespace TweetDuck.Data{
|
||||
sealed class CombinedFileStream : IDisposable{
|
||||
public const char KeySeparator = '|';
|
||||
|
||||
private readonly Stream stream;
|
||||
@ -79,8 +80,7 @@ public string SkipFile(){
|
||||
stream.Position += BitConverter.ToInt32(contentLength, 0);
|
||||
|
||||
string keyName = Encoding.UTF8.GetString(name);
|
||||
int separatorIndex = keyName.IndexOf(KeySeparator);
|
||||
return separatorIndex == -1 ? keyName : keyName.Substring(0, separatorIndex);
|
||||
return StringUtils.ExtractBefore(keyName, KeySeparator);
|
||||
}
|
||||
|
||||
public void Flush(){
|
||||
@ -96,8 +96,7 @@ public class Entry{
|
||||
|
||||
public string KeyName{
|
||||
get{
|
||||
int index = Identifier.IndexOf(KeySeparator);
|
||||
return index == -1 ? Identifier : Identifier.Substring(0, index);
|
||||
return StringUtils.ExtractBefore(Identifier, KeySeparator);
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace TweetDuck.Core.Utils{
|
||||
class CommandLineArgs{
|
||||
namespace TweetDuck.Data{
|
||||
sealed class CommandLineArgs{
|
||||
public static CommandLineArgs FromStringArray(char entryChar, string[] array){
|
||||
CommandLineArgs args = new CommandLineArgs();
|
||||
ReadStringArray(entryChar, array, args);
|
||||
@ -38,31 +38,31 @@ public static void ReadStringArray(char entryChar, string[] array, CommandLineAr
|
||||
public int Count => flags.Count+values.Count;
|
||||
|
||||
public void AddFlag(string flag){
|
||||
flags.Add(flag.ToLowerInvariant());
|
||||
flags.Add(flag.ToLower());
|
||||
}
|
||||
|
||||
public bool HasFlag(string flag){
|
||||
return flags.Contains(flag.ToLowerInvariant());
|
||||
return flags.Contains(flag.ToLower());
|
||||
}
|
||||
|
||||
public void RemoveFlag(string flag){
|
||||
flags.Remove(flag.ToLowerInvariant());
|
||||
flags.Remove(flag.ToLower());
|
||||
}
|
||||
|
||||
public void SetValue(string key, string value){
|
||||
values[key.ToLowerInvariant()] = value;
|
||||
values[key.ToLower()] = value;
|
||||
}
|
||||
|
||||
public bool HasValue(string key){
|
||||
return values.ContainsKey(key.ToLowerInvariant());
|
||||
return values.ContainsKey(key.ToLower());
|
||||
}
|
||||
|
||||
public string GetValue(string key, string defaultValue){
|
||||
return values.TryGetValue(key.ToLowerInvariant(), out string val) ? val : defaultValue;
|
||||
return values.TryGetValue(key.ToLower(), out string val) ? val : defaultValue;
|
||||
}
|
||||
|
||||
public void RemoveValue(string key){
|
||||
values.Remove(key.ToLowerInvariant());
|
||||
values.Remove(key.ToLower());
|
||||
}
|
||||
|
||||
public CommandLineArgs Clone(){
|
@ -1,7 +1,7 @@
|
||||
using System;
|
||||
|
||||
namespace TweetDuck.Core.Utils{
|
||||
class InjectedHTML{
|
||||
namespace TweetDuck.Data{
|
||||
sealed class InjectedHTML{
|
||||
public enum Position{
|
||||
Before, After
|
||||
}
|
133
Data/Serialization/FileSerializer.cs
Normal file
133
Data/Serialization/FileSerializer.cs
Normal file
@ -0,0 +1,133 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace TweetDuck.Data.Serialization{
|
||||
sealed class FileSerializer<T> where T : ISerializedObject{
|
||||
private const string NewLineReal = "\r\n";
|
||||
private const string NewLineCustom = "\r~\n";
|
||||
|
||||
private static readonly ITypeConverter BasicSerializerObj = new BasicTypeConverter();
|
||||
|
||||
private readonly Dictionary<string, PropertyInfo> props;
|
||||
private readonly Dictionary<Type, ITypeConverter> converters;
|
||||
|
||||
public FileSerializer(){
|
||||
this.props = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(prop => prop.CanWrite).ToDictionary(prop => prop.Name);
|
||||
this.converters = new Dictionary<Type, ITypeConverter>();
|
||||
}
|
||||
|
||||
public void RegisterTypeConverter(Type type, ITypeConverter converter){
|
||||
converters[type] = converter;
|
||||
}
|
||||
|
||||
public void Write(string file, T obj){
|
||||
using(StreamWriter writer = new StreamWriter(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))){
|
||||
foreach(KeyValuePair<string, PropertyInfo> prop in props){
|
||||
Type type = prop.Value.PropertyType;
|
||||
object value = prop.Value.GetValue(obj);
|
||||
|
||||
if (!converters.TryGetValue(type, out ITypeConverter serializer)) {
|
||||
serializer = BasicSerializerObj;
|
||||
}
|
||||
|
||||
if (serializer.TryWriteType(type, value, out string converted)){
|
||||
if (converted != null){
|
||||
writer.Write($"{prop.Key} {converted.Replace(Environment.NewLine, NewLineCustom)}");
|
||||
writer.Write(NewLineReal);
|
||||
}
|
||||
}
|
||||
else{
|
||||
throw new SerializationException($"Invalid serialization type, conversion failed for: {type}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Read(string file, T obj){
|
||||
using(StreamReader reader = new StreamReader(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))){
|
||||
if (reader.Peek() == 0){
|
||||
throw new FormatException("Input appears to be a binary file.");
|
||||
}
|
||||
|
||||
foreach(string line in reader.ReadToEnd().Split(new string[]{ NewLineReal }, StringSplitOptions.RemoveEmptyEntries)){
|
||||
int space = line.IndexOf(' ');
|
||||
|
||||
if (space == -1){
|
||||
throw new SerializationException($"Invalid file format, missing separator: {line}");
|
||||
}
|
||||
|
||||
string property = line.Substring(0, space);
|
||||
string value = line.Substring(space+1).Replace(NewLineCustom, Environment.NewLine);
|
||||
|
||||
if (props.TryGetValue(property, out PropertyInfo info)){
|
||||
if (!converters.TryGetValue(info.PropertyType, out ITypeConverter serializer)) {
|
||||
serializer = BasicSerializerObj;
|
||||
}
|
||||
|
||||
if (serializer.TryReadType(info.PropertyType, value, out object converted)){
|
||||
info.SetValue(obj, converted);
|
||||
}
|
||||
else{
|
||||
throw new SerializationException($"Invalid file format, cannot convert value: {value} (property: {property})");
|
||||
}
|
||||
}
|
||||
else if (!obj.OnReadUnknownProperty(property, value)){
|
||||
throw new SerializationException($"Invalid file format, unknown property: {property}+");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class BasicTypeConverter : ITypeConverter{
|
||||
bool ITypeConverter.TryWriteType(Type type, object value, out string converted){
|
||||
switch(Type.GetTypeCode(type)){
|
||||
case TypeCode.Boolean:
|
||||
converted = value.ToString();
|
||||
return true;
|
||||
|
||||
case TypeCode.Int32:
|
||||
converted = ((int)value).ToString(); // cast required for enums
|
||||
return true;
|
||||
|
||||
case TypeCode.String:
|
||||
converted = value?.ToString();
|
||||
return true;
|
||||
|
||||
default:
|
||||
converted = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ITypeConverter.TryReadType(Type type, string value, out object converted){
|
||||
switch(Type.GetTypeCode(type)){
|
||||
case TypeCode.Boolean:
|
||||
if (bool.TryParse(value, out bool b)){
|
||||
converted = b;
|
||||
return true;
|
||||
}
|
||||
else goto default;
|
||||
|
||||
case TypeCode.Int32:
|
||||
if (int.TryParse(value, out int i)){
|
||||
converted = i;
|
||||
return true;
|
||||
}
|
||||
else goto default;
|
||||
|
||||
case TypeCode.String:
|
||||
converted = value;
|
||||
return true;
|
||||
|
||||
default:
|
||||
converted = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
5
Data/Serialization/ISerializedObject.cs
Normal file
5
Data/Serialization/ISerializedObject.cs
Normal file
@ -0,0 +1,5 @@
|
||||
namespace TweetDuck.Data.Serialization{
|
||||
interface ISerializedObject{
|
||||
bool OnReadUnknownProperty(string property, string value);
|
||||
}
|
||||
}
|
8
Data/Serialization/ITypeConverter.cs
Normal file
8
Data/Serialization/ITypeConverter.cs
Normal file
@ -0,0 +1,8 @@
|
||||
using System;
|
||||
|
||||
namespace TweetDuck.Data.Serialization{
|
||||
interface ITypeConverter{
|
||||
bool TryWriteType(Type type, object value, out string converted);
|
||||
bool TryReadType(Type type, string value, out object converted);
|
||||
}
|
||||
}
|
31
Data/Serialization/SingleTypeConverter.cs
Normal file
31
Data/Serialization/SingleTypeConverter.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using System;
|
||||
|
||||
namespace TweetDuck.Data.Serialization{
|
||||
sealed class SingleTypeConverter<T> : ITypeConverter{
|
||||
public delegate string FuncConvertToString<U>(U value);
|
||||
public delegate U FuncConvertToObject<U>(string value);
|
||||
|
||||
public FuncConvertToString<T> ConvertToString { get; set; }
|
||||
public FuncConvertToObject<T> ConvertToObject { get; set; }
|
||||
|
||||
bool ITypeConverter.TryWriteType(Type type, object value, out string converted){
|
||||
try{
|
||||
converted = ConvertToString((T)value);
|
||||
return true;
|
||||
}catch{
|
||||
converted = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ITypeConverter.TryReadType(Type type, string value, out object converted){
|
||||
try{
|
||||
converted = ConvertToObject(value);
|
||||
return true;
|
||||
}catch{
|
||||
converted = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace TweetDuck.Core.Utils{
|
||||
class TwoKeyDictionary<K1, K2, V>{
|
||||
namespace TweetDuck.Data{
|
||||
sealed class TwoKeyDictionary<K1, K2, V>{
|
||||
private readonly Dictionary<K1, Dictionary<K2, V>> dict;
|
||||
private readonly int innerCapacity;
|
||||
|
@ -2,10 +2,12 @@
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
using TweetDuck.Core.Controls;
|
||||
using TweetDuck.Core.Utils;
|
||||
using TweetDuck.Data.Serialization;
|
||||
|
||||
namespace TweetDuck.Core.Utils{
|
||||
[Serializable]
|
||||
class WindowState{
|
||||
namespace TweetDuck.Data{
|
||||
[Serializable] // TODO remove attribute with UserConfigLegacy
|
||||
sealed class WindowState{
|
||||
private Rectangle rect;
|
||||
private bool isMaximized;
|
||||
|
||||
@ -26,5 +28,17 @@ public void Restore(Form form, bool firstTimeFullscreen){
|
||||
Save(form);
|
||||
}
|
||||
}
|
||||
|
||||
public static readonly SingleTypeConverter<WindowState> Converter = new SingleTypeConverter<WindowState>{
|
||||
ConvertToString = value => $"{(value.isMaximized ? 'M' : '_')}{value.rect.X} {value.rect.Y} {value.rect.Width} {value.rect.Height}",
|
||||
ConvertToObject = value => {
|
||||
int[] elements = StringUtils.ParseInts(value.Substring(1), ' ');
|
||||
|
||||
return new WindowState{
|
||||
rect = new Rectangle(elements[0], elements[1], elements[2], elements[3]),
|
||||
isMaximized = value[0] == 'M'
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@
|
||||
using TweetDuck.Plugins.Enums;
|
||||
|
||||
namespace TweetDuck.Plugins{
|
||||
class Plugin{
|
||||
sealed class Plugin{
|
||||
public string Identifier { get; }
|
||||
public PluginGroup Group { get; }
|
||||
public PluginEnvironment Environments { get; private set; }
|
||||
@ -132,8 +132,7 @@ public override int GetHashCode(){
|
||||
}
|
||||
|
||||
public override bool Equals(object obj){
|
||||
Plugin plugin = obj as Plugin;
|
||||
return plugin != null && plugin.Identifier.Equals(Identifier);
|
||||
return obj is Plugin plugin && plugin.Identifier.Equals(Identifier);
|
||||
}
|
||||
|
||||
public static Plugin CreateFromFolder(string path, PluginGroup group, out string error){
|
||||
@ -184,7 +183,7 @@ private static bool LoadMetadata(string path, Plugin plugin, out string error){
|
||||
plugin.metadata[currentTag] = currentContents;
|
||||
}
|
||||
|
||||
currentTag = line.Substring(1, line.Length-2).ToUpperInvariant();
|
||||
currentTag = line.Substring(1, line.Length-2).ToUpper();
|
||||
currentContents = "";
|
||||
|
||||
if (line.Equals(endTag[0])){
|
||||
|
@ -2,12 +2,12 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using TweetDuck.Core.Utils;
|
||||
using TweetDuck.Data;
|
||||
using TweetDuck.Plugins.Enums;
|
||||
using TweetDuck.Plugins.Events;
|
||||
|
||||
namespace TweetDuck.Plugins{
|
||||
class PluginBridge{
|
||||
sealed class PluginBridge{
|
||||
private static string SanitizeCacheKey(string key){
|
||||
return key.Replace('\\', '/').Trim();
|
||||
}
|
||||
@ -47,9 +47,9 @@ private string GetFullPathOrThrow(int token, PluginFolder folder, string path){
|
||||
|
||||
if (fullPath.Length == 0){
|
||||
switch(folder){
|
||||
case PluginFolder.Data: throw new Exception("File path has to be relative to the plugin data folder.");
|
||||
case PluginFolder.Root: throw new Exception("File path has to be relative to the plugin root folder.");
|
||||
default: throw new Exception("Invalid folder type "+folder+", this is a "+Program.BrandName+" error.");
|
||||
case PluginFolder.Data: throw new ArgumentException("File path has to be relative to the plugin data folder.");
|
||||
case PluginFolder.Root: throw new ArgumentException("File path has to be relative to the plugin root folder.");
|
||||
default: throw new ArgumentException("Invalid folder type "+folder+", this is a "+Program.BrandName+" error.");
|
||||
}
|
||||
}
|
||||
else{
|
||||
@ -67,9 +67,9 @@ private string ReadFileUnsafe(int token, string cacheKey, string fullPath, bool
|
||||
try{
|
||||
return fileCache[token, cacheKey] = File.ReadAllText(fullPath, Encoding.UTF8);
|
||||
}catch(FileNotFoundException){
|
||||
throw new Exception("File not found.");
|
||||
throw new FileNotFoundException("File not found.");
|
||||
}catch(DirectoryNotFoundException){
|
||||
throw new Exception("Directory not found.");
|
||||
throw new DirectoryNotFoundException("Directory not found.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,8 +28,7 @@ public bool IsEnabled(Plugin plugin){
|
||||
|
||||
public void Load(string file){
|
||||
try{
|
||||
using(FileStream stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
using(StreamReader reader = new StreamReader(stream, Encoding.UTF8)){
|
||||
using(StreamReader reader = new StreamReader(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read), Encoding.UTF8)){
|
||||
string line = reader.ReadLine();
|
||||
|
||||
if (line == "#Disabled"){
|
||||
@ -49,8 +48,7 @@ public void Load(string file){
|
||||
|
||||
public void Save(string file){
|
||||
try{
|
||||
using(FileStream stream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))
|
||||
using(StreamWriter writer = new StreamWriter(stream, Encoding.UTF8)){
|
||||
using(StreamWriter writer = new StreamWriter(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None), Encoding.UTF8)){
|
||||
writer.WriteLine("#Disabled");
|
||||
|
||||
foreach(string disabled in Disabled){
|
||||
|
@ -1,5 +1,4 @@
|
||||
using System.Globalization;
|
||||
using TweetDuck.Plugins.Enums;
|
||||
using TweetDuck.Plugins.Enums;
|
||||
|
||||
namespace TweetDuck.Plugins{
|
||||
static class PluginScriptGenerator{
|
||||
@ -11,7 +10,7 @@ public static string GeneratePlugin(string pluginIdentifier, string pluginConten
|
||||
return PluginGen
|
||||
.Replace("%params", environment.GetScriptVariables())
|
||||
.Replace("%id", pluginIdentifier)
|
||||
.Replace("%token", pluginToken.ToString(CultureInfo.InvariantCulture))
|
||||
.Replace("%token", pluginToken.ToString())
|
||||
.Replace("%contents", pluginContents);
|
||||
}
|
||||
|
||||
@ -19,7 +18,7 @@ public static string GeneratePlugin(string pluginIdentifier, string pluginConten
|
||||
|
||||
/* PluginGen
|
||||
|
||||
(function(%params, $i, $d){
|
||||
(function(%params, $d){
|
||||
let tmp = {
|
||||
id: '%id',
|
||||
obj: new class extends PluginBase{%contents}
|
||||
|
23
Program.cs
23
Program.cs
@ -1,6 +1,7 @@
|
||||
using CefSharp;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
@ -11,6 +12,7 @@
|
||||
using TweetDuck.Core.Other;
|
||||
using TweetDuck.Core.Other.Settings.Export;
|
||||
using TweetDuck.Core.Utils;
|
||||
using TweetDuck.Data;
|
||||
using TweetDuck.Plugins;
|
||||
using TweetDuck.Plugins.Events;
|
||||
using TweetDuck.Updates;
|
||||
@ -50,15 +52,25 @@ static class Program{
|
||||
|
||||
public static UserConfig UserConfig { get; private set; }
|
||||
public static SystemConfig SystemConfig { get; private set; }
|
||||
public static Reporter Reporter { get; private set; }
|
||||
public static Reporter Reporter { get; }
|
||||
public static CultureInfo Culture { get; }
|
||||
|
||||
public static event EventHandler UserConfigReplaced;
|
||||
|
||||
static Program(){
|
||||
Culture = CultureInfo.CurrentCulture;
|
||||
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
|
||||
CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture;
|
||||
|
||||
Reporter = new Reporter(ErrorLogFilePath);
|
||||
Reporter.SetupUnhandledExceptionHandler(BrandName+" Has Failed :(");
|
||||
}
|
||||
|
||||
[STAThread]
|
||||
private static void Main(){
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
|
||||
|
||||
WindowRestoreMessage = NativeMethods.RegisterWindowMessage("TweetDuckRestore");
|
||||
SubProcessMessage = NativeMethods.RegisterWindowMessage("TweetDuckSubProcess");
|
||||
|
||||
@ -66,10 +78,7 @@ private static void Main(){
|
||||
MessageBox.Show(BrandName+" does not have write permissions to the storage folder: "+StoragePath, "Permission Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
Reporter = new Reporter(ErrorLogFilePath);
|
||||
Reporter.SetupUnhandledExceptionHandler(BrandName+" Has Failed :(");
|
||||
|
||||
|
||||
if (Arguments.HasFlag(Arguments.ArgRestart)){
|
||||
for(int attempt = 0; attempt < 21; attempt++){
|
||||
LockManager.Result lockResult = LockManager.Lock();
|
||||
@ -102,7 +111,7 @@ private static void Main(){
|
||||
|
||||
if (lockResult == LockManager.Result.HasProcess){
|
||||
if (LockManager.LockingProcess.MainWindowHandle == IntPtr.Zero){ // restore if the original process is in tray
|
||||
NativeMethods.PostMessage(NativeMethods.HWND_BROADCAST, WindowRestoreMessage, LockManager.LockingProcess.Id, IntPtr.Zero);
|
||||
NativeMethods.PostMessage(NativeMethods.HWND_BROADCAST, WindowRestoreMessage, new UIntPtr((uint)LockManager.LockingProcess.Id), IntPtr.Zero);
|
||||
|
||||
if (WindowsUtils.TrySleepUntil(() => {
|
||||
LockManager.LockingProcess.Refresh();
|
||||
|
16
Reporter.cs
16
Reporter.cs
@ -1,14 +1,13 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using TweetDuck.Core.Other;
|
||||
|
||||
namespace TweetDuck{
|
||||
class Reporter{
|
||||
sealed class Reporter{
|
||||
private readonly string logFile;
|
||||
|
||||
public Reporter(string logFile){
|
||||
@ -17,9 +16,7 @@ public Reporter(string logFile){
|
||||
|
||||
public void SetupUnhandledExceptionHandler(string caption){
|
||||
AppDomain.CurrentDomain.UnhandledException += (sender, args) => {
|
||||
Exception ex = args.ExceptionObject as Exception;
|
||||
|
||||
if (ex != null){
|
||||
if (args.ExceptionObject is Exception ex) {
|
||||
HandleException(caption, "An unhandled exception has occurred.", false, ex);
|
||||
}
|
||||
};
|
||||
@ -32,7 +29,7 @@ public bool Log(string data){
|
||||
build.Append("Please, report all issues to: https://github.com/chylex/TweetDuck/issues\r\n\r\n");
|
||||
}
|
||||
|
||||
build.Append("[").Append(DateTime.Now.ToString("G", CultureInfo.CurrentCulture)).Append("]\r\n");
|
||||
build.Append("[").Append(DateTime.Now.ToString("G", Program.Culture)).Append("]\r\n");
|
||||
build.Append(data).Append("\r\n\r\n");
|
||||
|
||||
try{
|
||||
@ -87,9 +84,10 @@ public static void HandleEarlyFailure(string caption, string message){
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
|
||||
FormMessage form = new FormMessage(caption, message, MessageBoxIcon.Error);
|
||||
form.ActiveControl = form.AddButton("Exit");
|
||||
form.ShowDialog();
|
||||
using(FormMessage form = new FormMessage(caption, message, MessageBoxIcon.Error)){
|
||||
form.ActiveControl = form.AddButton("Exit");
|
||||
form.ShowDialog();
|
||||
}
|
||||
|
||||
try{
|
||||
Process.GetCurrentProcess().Kill();
|
||||
|
@ -498,7 +498,7 @@
|
||||
|
||||
var tryCloseModal1 = function(){
|
||||
var modal = $("#open-modal");
|
||||
return modal.is(":visible") && tryClickSelector("a[rel=dismiss]", modal);
|
||||
return modal.is(":visible") && tryClickSelector("a.mdl-dismiss", modal);
|
||||
};
|
||||
|
||||
var tryCloseModal2 = function(){
|
||||
|
@ -73,6 +73,7 @@
|
||||
<Compile Include="Configuration\LockManager.cs" />
|
||||
<Compile Include="Configuration\SystemConfig.cs" />
|
||||
<Compile Include="Configuration\UserConfig.cs" />
|
||||
<Compile Include="Configuration\UserConfigLegacy.cs" />
|
||||
<Compile Include="Core\Bridge\PropertyBridge.cs" />
|
||||
<Compile Include="Core\Controls\ControlExtensions.cs" />
|
||||
<Compile Include="Core\Controls\FlatButton.cs">
|
||||
@ -164,7 +165,8 @@
|
||||
<Compile Include="Core\Other\Settings\Dialogs\DialogSettingsRestart.Designer.cs">
|
||||
<DependentUpon>DialogSettingsRestart.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Core\Other\Settings\Export\CombinedFileStream.cs" />
|
||||
<Compile Include="Core\Utils\StringUtils.cs" />
|
||||
<Compile Include="Data\CombinedFileStream.cs" />
|
||||
<Compile Include="Core\Other\Settings\Export\ExportFileFlags.cs" />
|
||||
<Compile Include="Core\Other\Settings\Export\ExportManager.cs" />
|
||||
<Compile Include="Core\Other\Settings\TabSettingsAdvanced.cs">
|
||||
@ -195,17 +197,20 @@
|
||||
<DependentUpon>TabSettingsNotifications.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Core\Bridge\CallbackBridge.cs" />
|
||||
<Compile Include="Core\Utils\BrowserProcesses.cs" />
|
||||
<Compile Include="Core\Utils\CommandLineArgs.cs" />
|
||||
<Compile Include="Data\CommandLineArgs.cs" />
|
||||
<Compile Include="Core\Utils\CommandLineArgsParser.cs" />
|
||||
<Compile Include="Core\Notification\Screenshot\FormNotificationScreenshotable.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Core\Notification\Screenshot\TweetScreenshotManager.cs" />
|
||||
<Compile Include="Core\Utils\InjectedHTML.cs" />
|
||||
<Compile Include="Data\Serialization\FileSerializer.cs" />
|
||||
<Compile Include="Data\InjectedHTML.cs" />
|
||||
<Compile Include="Data\Serialization\ISerializedObject.cs" />
|
||||
<Compile Include="Data\Serialization\ITypeConverter.cs" />
|
||||
<Compile Include="Data\Serialization\SingleTypeConverter.cs" />
|
||||
<Compile Include="Data\TwoKeyDictionary.cs" />
|
||||
<Compile Include="Data\WindowState.cs" />
|
||||
<Compile Include="Core\Utils\MemoryUsageTracker.cs" />
|
||||
<Compile Include="Core\Utils\TwoKeyDictionary.cs" />
|
||||
<Compile Include="Core\Utils\WindowState.cs" />
|
||||
<Compile Include="Core\Utils\WindowsUtils.cs" />
|
||||
<Compile Include="Core\Bridge\TweetDeckBridge.cs" />
|
||||
<Compile Include="Core\Other\FormSettings.cs">
|
||||
|
@ -4,7 +4,7 @@
|
||||
using TweetDuck.Core.Utils;
|
||||
|
||||
namespace TweetDuck.Updates{
|
||||
class UpdateInfo{
|
||||
sealed class UpdateInfo{
|
||||
public string VersionTag { get; }
|
||||
public string InstallerPath { get; }
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
namespace TweetDuck.Updates{
|
||||
class UpdaterSettings{
|
||||
sealed class UpdaterSettings{
|
||||
public bool AllowPreReleases { get; set; }
|
||||
public string DismissedUpdate { get; set; }
|
||||
public string InstallerDownloadFolder { get; set; }
|
||||
|
@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using TweetLib.Audio.Impl;
|
||||
using TweetLib.Audio.Utils;
|
||||
|
||||
namespace TweetLib.Audio{
|
||||
public abstract class AudioPlayer : IDisposable{
|
||||
@ -32,6 +31,11 @@ public static AudioPlayer New(){
|
||||
|
||||
public abstract void Play(string file);
|
||||
public abstract void Stop();
|
||||
public abstract void Dispose();
|
||||
protected abstract void Dispose(bool disposing);
|
||||
|
||||
public void Dispose(){
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Media;
|
||||
using TweetLib.Audio.Utils;
|
||||
|
||||
namespace TweetLib.Audio.Impl{
|
||||
sealed class SoundPlayerImplFallback : AudioPlayer{
|
||||
@ -39,7 +38,7 @@ public override void Stop(){
|
||||
player.Stop();
|
||||
}
|
||||
|
||||
public override void Dispose(){
|
||||
protected override void Dispose(bool disposing){
|
||||
player.Dispose();
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,7 @@ public override void Stop(){
|
||||
}
|
||||
}
|
||||
|
||||
public override void Dispose(){
|
||||
protected override void Dispose(bool disposing){
|
||||
player.close();
|
||||
Marshal.ReleaseComObject(player);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace TweetLib.Audio.Utils{
|
||||
namespace TweetLib.Audio{
|
||||
public sealed class PlaybackErrorEventArgs : EventArgs{
|
||||
public string Message { get; }
|
||||
public bool Ignore { get; set; }
|
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
@ -30,7 +30,7 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="AudioPlayer.cs" />
|
||||
<Compile Include="Utils\NativeCoreAudio.cs" />
|
||||
<Compile Include="Utils\PlaybackErrorEventArgs.cs" />
|
||||
<Compile Include="PlaybackErrorEventArgs.cs" />
|
||||
<Compile Include="Impl\SoundPlayerImplFallback.cs" />
|
||||
<Compile Include="Impl\SoundPlayerImplWMP.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
|
@ -82,9 +82,7 @@ private static ISimpleAudioVolume GetVolumeObject(string name){
|
||||
ISimpleAudioVolume volumeObj = null;
|
||||
|
||||
for(int index = sessions.GetCount()-1; index >= 0; index--){
|
||||
IAudioSessionControl2 ctl = sessions.GetSession(index) as IAudioSessionControl2;
|
||||
|
||||
if (ctl != null){
|
||||
if (sessions.GetSession(index) is IAudioSessionControl2 ctl){
|
||||
string identifier = ctl.GetSessionIdentifier();
|
||||
|
||||
if (identifier != null && identifier.Contains(name)){
|
||||
|
@ -1,7 +1,7 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using TweetDuck.Core.Utils;
|
||||
|
||||
namespace UnitTests.Core.Utils{
|
||||
namespace UnitTests.Core{
|
||||
[TestClass]
|
||||
public class TestBrowserUtils{
|
||||
[TestMethod]
|
||||
@ -46,15 +46,5 @@ public void TestGetFileNameFromUrl(){
|
||||
|
||||
Assert.IsNull(BrowserUtils.GetFileNameFromUrl("http://test.com/"));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestConvertPascalCaseToScreamingSnakeCase(){
|
||||
Assert.AreEqual("HELP", BrowserUtils.ConvertPascalCaseToScreamingSnakeCase("Help"));
|
||||
Assert.AreEqual("HELP_ME", BrowserUtils.ConvertPascalCaseToScreamingSnakeCase("HelpMe"));
|
||||
Assert.AreEqual("HELP_ME_PLEASE", BrowserUtils.ConvertPascalCaseToScreamingSnakeCase("HelpMePlease"));
|
||||
|
||||
Assert.AreEqual("HTML_CODE", BrowserUtils.ConvertPascalCaseToScreamingSnakeCase("HTMLCode"));
|
||||
Assert.AreEqual("CHECK_OUT_MY_HTML_CODE", BrowserUtils.ConvertPascalCaseToScreamingSnakeCase("CheckOutMyHTMLCode"));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using TweetDuck.Core.Utils;
|
||||
using TweetDuck.Data;
|
||||
|
||||
namespace UnitTests.Core.Utils{
|
||||
namespace UnitTests.Core{
|
||||
[TestClass]
|
||||
public class TestCommandLineArgsParser{
|
||||
[TestMethod]
|
36
tests/Core/TestStringUtils.cs
Normal file
36
tests/Core/TestStringUtils.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using TweetDuck.Core.Utils;
|
||||
|
||||
namespace UnitTests.Core{
|
||||
[TestClass]
|
||||
public class TestStringUtils{
|
||||
[TestMethod]
|
||||
public void TestExtractBefore(){
|
||||
Assert.AreEqual("missing", StringUtils.ExtractBefore("missing", '_'));
|
||||
Assert.AreEqual("", StringUtils.ExtractBefore("_empty", '_'));
|
||||
Assert.AreEqual("some", StringUtils.ExtractBefore("some_text", '_'));
|
||||
Assert.AreEqual("first", StringUtils.ExtractBefore("first_separator_only", '_'));
|
||||
Assert.AreEqual("start_index", StringUtils.ExtractBefore("start_index_test", '_', 8));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestParseInts(){
|
||||
CollectionAssert.AreEqual(new int[0], StringUtils.ParseInts("", ','));
|
||||
CollectionAssert.AreEqual(new int[]{ 1 }, StringUtils.ParseInts("1", ','));
|
||||
CollectionAssert.AreEqual(new int[]{ 1, 2, 3 }, StringUtils.ParseInts("1,2,3", ','));
|
||||
CollectionAssert.AreEqual(new int[]{ 1, 2, 3 }, StringUtils.ParseInts("1,2,3,", ','));
|
||||
CollectionAssert.AreEqual(new int[]{ 1, 2, 3 }, StringUtils.ParseInts(",1,2,,3,", ','));
|
||||
CollectionAssert.AreEqual(new int[]{ -50, 50 }, StringUtils.ParseInts("-50,50", ','));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestConvertPascalCaseToScreamingSnakeCase(){
|
||||
Assert.AreEqual("HELP", StringUtils.ConvertPascalCaseToScreamingSnakeCase("Help"));
|
||||
Assert.AreEqual("HELP_ME", StringUtils.ConvertPascalCaseToScreamingSnakeCase("HelpMe"));
|
||||
Assert.AreEqual("HELP_ME_PLEASE", StringUtils.ConvertPascalCaseToScreamingSnakeCase("HelpMePlease"));
|
||||
|
||||
Assert.AreEqual("HTML_CODE", StringUtils.ConvertPascalCaseToScreamingSnakeCase("HTMLCode"));
|
||||
Assert.AreEqual("CHECK_OUT_MY_HTML_CODE", StringUtils.ConvertPascalCaseToScreamingSnakeCase("CheckOutMyHTMLCode"));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System;
|
||||
using System;
|
||||
using System.IO;
|
||||
using TweetDuck.Core.Other.Settings.Export;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using TweetDuck.Data;
|
||||
|
||||
namespace UnitTests.Core.Settings{
|
||||
namespace UnitTests.Data{
|
||||
[TestClass]
|
||||
public class TestCombinedFileStream{
|
||||
[TestMethod]
|
@ -1,8 +1,8 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System.Collections.Generic;
|
||||
using TweetDuck.Core.Utils;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using TweetDuck.Data;
|
||||
|
||||
namespace UnitTests.Core.Utils{
|
||||
namespace UnitTests.Data{
|
||||
[TestClass]
|
||||
public class TestCommandLineArgs{
|
||||
[TestMethod]
|
54
tests/Data/TestFileSerializer.cs
Normal file
54
tests/Data/TestFileSerializer.cs
Normal file
@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using TweetDuck.Data.Serialization;
|
||||
|
||||
namespace UnitTests.Data{
|
||||
[TestClass]
|
||||
public class TestFileSerializer{
|
||||
private enum TestEnum{
|
||||
A, B, C, D, E
|
||||
}
|
||||
|
||||
private class SerializationTestBasic : ISerializedObject{
|
||||
public bool TestBool { get; set; }
|
||||
public int TestInt { get; set; }
|
||||
public string TestString { get; set; }
|
||||
public string TestStringNull { get; set; }
|
||||
public TestEnum TestEnum { get; set; }
|
||||
|
||||
bool ISerializedObject.OnReadUnknownProperty(string property, string value){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestBasicWriteRead(){
|
||||
FileSerializer<SerializationTestBasic> serializer = new FileSerializer<SerializationTestBasic>();
|
||||
|
||||
SerializationTestBasic write = new SerializationTestBasic{
|
||||
TestBool = true,
|
||||
TestInt = -100,
|
||||
TestString = "abc"+Environment.NewLine+"def",
|
||||
TestStringNull = null,
|
||||
TestEnum = TestEnum.D
|
||||
};
|
||||
|
||||
serializer.Write("serialized_basic", write);
|
||||
|
||||
Assert.IsTrue(File.Exists("serialized_basic"));
|
||||
TestUtils.DeleteFileOnExit("serialized_basic");
|
||||
|
||||
SerializationTestBasic read = new SerializationTestBasic();
|
||||
serializer.Read("serialized_basic", read);
|
||||
|
||||
Assert.IsTrue(read.TestBool);
|
||||
Assert.AreEqual(-100, read.TestInt);
|
||||
Assert.AreEqual("abc"+Environment.NewLine+"def", read.TestString);
|
||||
Assert.IsNull(read.TestStringNull);
|
||||
Assert.AreEqual(TestEnum.D, read.TestEnum);
|
||||
}
|
||||
|
||||
// TODO more complex tests
|
||||
}
|
||||
}
|
@ -2,9 +2,9 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using TweetDuck.Core.Utils;
|
||||
using TweetDuck.Data;
|
||||
|
||||
namespace UnitTests.Core.Utils{
|
||||
namespace UnitTests.Data{
|
||||
[TestClass]
|
||||
public class TestInjectedHTML{
|
||||
private static IEnumerable<InjectedHTML.Position> Positions => Enum.GetValues(typeof(InjectedHTML.Position)).Cast<InjectedHTML.Position>();
|
@ -1,10 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using TweetDuck.Core.Utils;
|
||||
using System.Collections.Generic;
|
||||
using TweetDuck.Data;
|
||||
|
||||
namespace UnitTests.Core.Utils{
|
||||
namespace UnitTests.Data{
|
||||
[TestClass]
|
||||
public class TestTwoKeyDictionary{
|
||||
private static TwoKeyDictionary<string, int, string> CreateDict(){
|
@ -47,12 +47,14 @@
|
||||
</Otherwise>
|
||||
</Choose>
|
||||
<ItemGroup>
|
||||
<Compile Include="Core\Settings\TestCombinedFileStream.cs" />
|
||||
<Compile Include="Core\Utils\TestBrowserUtils.cs" />
|
||||
<Compile Include="Core\Utils\TestCommandLineArgs.cs" />
|
||||
<Compile Include="Core\Utils\TestCommandLineArgsParser.cs" />
|
||||
<Compile Include="Core\Utils\TestInjectedHTML.cs" />
|
||||
<Compile Include="Core\Utils\TestTwoKeyDictionary.cs" />
|
||||
<Compile Include="Core\TestStringUtils.cs" />
|
||||
<Compile Include="Data\TestCombinedFileStream.cs" />
|
||||
<Compile Include="Core\TestBrowserUtils.cs" />
|
||||
<Compile Include="Data\TestCommandLineArgs.cs" />
|
||||
<Compile Include="Core\TestCommandLineArgsParser.cs" />
|
||||
<Compile Include="Data\TestFileSerializer.cs" />
|
||||
<Compile Include="Data\TestInjectedHTML.cs" />
|
||||
<Compile Include="Data\TestTwoKeyDictionary.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="TestUtils.cs" />
|
||||
</ItemGroup>
|
||||
|
Loading…
Reference in New Issue
Block a user