mirror of
https://github.com/chylex/TweetDuck.git
synced 2025-09-14 19:32:10 +02:00
Compare commits
92 Commits
Author | SHA1 | Date | |
---|---|---|---|
52f1f4c4eb | |||
6c1782a038 | |||
8b8f5f5473 | |||
61d3ed891a | |||
b1abf87320 | |||
9aedfc2799 | |||
ad6240a067 | |||
9539eb076a | |||
c808e7bd83 | |||
13ea388f5e | |||
c46dc0f1a3 | |||
2ae311007d | |||
9344e02bff | |||
40ad836fc3 | |||
e8604a261d | |||
2a41d21a29 | |||
4c62aa067b | |||
49db3074c6 | |||
f5e3b34f30 | |||
f0affa4aec | |||
4f5075ac54 | |||
20f0445b10 | |||
c77c974455 | |||
44397b2d45 | |||
943d4d4d72 | |||
6468c03465 | |||
8141a5a5c5 | |||
26a1779310 | |||
45d18ffafe | |||
5f1c30609c | |||
7266d705d3 | |||
ee6bb782d6 | |||
8ae6e2c886 | |||
dd3a0d3890 | |||
8d8e2da57e | |||
e60d204302 | |||
3d642d8ad2 | |||
8db6e8a090 | |||
8153fcde85 | |||
96469cfca5 | |||
7601645c12 | |||
c28615d548 | |||
b515add94e | |||
9fd5e9443d | |||
b2ddb1fab2 | |||
fdac42947c | |||
eeaf6949c5 | |||
d7ad62d476 | |||
cd87a329fc | |||
8c0d306823 | |||
d5c3ea0862 | |||
83c962a7a4 | |||
40ef9a42dd | |||
868af5ac6a | |||
625227d0ce | |||
064627961e | |||
de0321cb2d | |||
0d71a33b28 | |||
6d779f17b3 | |||
05510d7bc1 | |||
8e162fe031 | |||
7ea7366a43 | |||
445e6fcec0 | |||
42f4d97d5d | |||
6357708533 | |||
59c9801437 | |||
d691bef1fb | |||
442d74d0cb | |||
588bb9a093 | |||
380e580d65 | |||
4e306661f8 | |||
9f3f33da93 | |||
69cd96a37c | |||
1293a2a533 | |||
d24b7bbcb9 | |||
b55b47b689 | |||
c4c032b4d5 | |||
970cd21964 | |||
8ca9d242b2 | |||
6f0518edcc | |||
e2d15dd7e3 | |||
5c310e8647 | |||
01dca0bc66 | |||
8b54fbdb2f | |||
663d0a633e | |||
ccd5edb0e4 | |||
c6190db918 | |||
3d4cec3b22 | |||
5ed970b5a0 | |||
c22934336b | |||
a3a52e0a1c | |||
b2be530f6b |
@@ -12,6 +12,7 @@ namespace TweetDuck.Configuration{
|
|||||||
// internal args
|
// internal args
|
||||||
public const string ArgRestart = "-restart";
|
public const string ArgRestart = "-restart";
|
||||||
public const string ArgImportCookies = "-importcookies";
|
public const string ArgImportCookies = "-importcookies";
|
||||||
|
public const string ArgDeleteCookies = "-deletecookies";
|
||||||
public const string ArgUpdated = "-updated";
|
public const string ArgUpdated = "-updated";
|
||||||
|
|
||||||
// class data and methods
|
// class data and methods
|
||||||
@@ -29,6 +30,7 @@ namespace TweetDuck.Configuration{
|
|||||||
CommandLineArgs args = Current.Clone();
|
CommandLineArgs args = Current.Clone();
|
||||||
args.RemoveFlag(ArgRestart);
|
args.RemoveFlag(ArgRestart);
|
||||||
args.RemoveFlag(ArgImportCookies);
|
args.RemoveFlag(ArgImportCookies);
|
||||||
|
args.RemoveFlag(ArgDeleteCookies);
|
||||||
args.RemoveFlag(ArgUpdated);
|
args.RemoveFlag(ArgUpdated);
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
@@ -5,9 +5,7 @@ using TweetDuck.Data.Serialization;
|
|||||||
|
|
||||||
namespace TweetDuck.Configuration{
|
namespace TweetDuck.Configuration{
|
||||||
sealed class SystemConfig{
|
sealed class SystemConfig{
|
||||||
private static readonly FileSerializer<SystemConfig> Serializer = new FileSerializer<SystemConfig>{
|
private static readonly FileSerializer<SystemConfig> Serializer = new FileSerializer<SystemConfig>();
|
||||||
// HandleUnknownProperties = (obj, data) => {}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static readonly bool IsHardwareAccelerationSupported = File.Exists(Path.Combine(Program.ProgramPath, "libEGL.dll")) &&
|
public static readonly bool IsHardwareAccelerationSupported = File.Exists(Path.Combine(Program.ProgramPath, "libEGL.dll")) &&
|
||||||
File.Exists(Path.Combine(Program.ProgramPath, "libGLESv2.dll"));
|
File.Exists(Path.Combine(Program.ProgramPath, "libGLESv2.dll"));
|
||||||
@@ -50,19 +48,8 @@ namespace TweetDuck.Configuration{
|
|||||||
|
|
||||||
try{
|
try{
|
||||||
Serializer.Read(file, config);
|
Serializer.Read(file, config);
|
||||||
return config;
|
|
||||||
}catch(FileNotFoundException){
|
}catch(FileNotFoundException){
|
||||||
}catch(DirectoryNotFoundException){
|
}catch(DirectoryNotFoundException){
|
||||||
}catch(FormatException){
|
|
||||||
try{
|
|
||||||
using(Stream stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read)){
|
|
||||||
config.HardwareAcceleration = stream.ReadByte() > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
config.Save();
|
|
||||||
}catch(Exception e){
|
|
||||||
Program.Reporter.HandleException("Configuration Error", "Could not update the system configuration file.", true, e);
|
|
||||||
}
|
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
Program.Reporter.HandleException("Configuration Error", "Could not open the system configuration file. If you continue, you will lose system specific configuration such as Hardware Acceleration.", true, e);
|
Program.Reporter.HandleException("Configuration Error", "Could not open the system configuration file. If you continue, you will lose system specific configuration such as Hardware Acceleration.", true, e);
|
||||||
}
|
}
|
||||||
|
@@ -14,16 +14,6 @@ namespace TweetDuck.Configuration{
|
|||||||
private static readonly FileSerializer<UserConfig> Serializer = new FileSerializer<UserConfig>{ HandleUnknownProperties = HandleUnknownProperties };
|
private static readonly FileSerializer<UserConfig> Serializer = new FileSerializer<UserConfig>{ HandleUnknownProperties = HandleUnknownProperties };
|
||||||
|
|
||||||
private static void HandleUnknownProperties(UserConfig obj, Dictionary<string, string> data){
|
private static void HandleUnknownProperties(UserConfig obj, Dictionary<string, string> data){
|
||||||
if (data.TryGetValue("EnableBrowserGCReload", out string propGCReload) && data.TryGetValue("BrowserMemoryThreshold", out string propMemThreshold)){
|
|
||||||
if (bool.TryParse(propGCReload, out bool isGCReloadEnabled) && isGCReloadEnabled && int.TryParse(propMemThreshold, out int memThreshold)){
|
|
||||||
// SystemConfig initialization was moved before UserConfig to allow for this
|
|
||||||
// TODO remove the migration soon
|
|
||||||
Program.SystemConfig.EnableBrowserGCReload = true;
|
|
||||||
Program.SystemConfig.BrowserMemoryThreshold = memThreshold;
|
|
||||||
Program.SystemConfig.Save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data.Remove("EnableBrowserGCReload");
|
data.Remove("EnableBrowserGCReload");
|
||||||
data.Remove("BrowserMemoryThreshold");
|
data.Remove("BrowserMemoryThreshold");
|
||||||
|
|
||||||
@@ -90,6 +80,7 @@ namespace TweetDuck.Configuration{
|
|||||||
public Size CustomNotificationSize { get; set; } = Size.Empty;
|
public Size CustomNotificationSize { get; set; } = Size.Empty;
|
||||||
public int NotificationScrollSpeed { get; set; } = 10;
|
public int NotificationScrollSpeed { get; set; } = 10;
|
||||||
|
|
||||||
|
public int NotificationSoundVolume { get; set; } = 100;
|
||||||
private string _notificationSoundPath;
|
private string _notificationSoundPath;
|
||||||
|
|
||||||
public string CustomCefArgs { get; set; } = null;
|
public string CustomCefArgs { get; set; } = null;
|
||||||
@@ -114,7 +105,7 @@ namespace TweetDuck.Configuration{
|
|||||||
set{
|
set{
|
||||||
if (_muteNotifications != value){
|
if (_muteNotifications != value){
|
||||||
_muteNotifications = value;
|
_muteNotifications = value;
|
||||||
MuteToggled?.Invoke(this, new EventArgs());
|
MuteToggled?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -125,7 +116,7 @@ namespace TweetDuck.Configuration{
|
|||||||
set{
|
set{
|
||||||
if (_zoomLevel != value){
|
if (_zoomLevel != value){
|
||||||
_zoomLevel = value;
|
_zoomLevel = value;
|
||||||
ZoomLevelChanged?.Invoke(this, new EventArgs());
|
ZoomLevelChanged?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,7 +129,7 @@ namespace TweetDuck.Configuration{
|
|||||||
set{
|
set{
|
||||||
if (_trayBehavior != value){
|
if (_trayBehavior != value){
|
||||||
_trayBehavior = value;
|
_trayBehavior = value;
|
||||||
TrayBehaviorChanged?.Invoke(this, new EventArgs());
|
TrayBehaviorChanged?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -153,7 +144,7 @@ namespace TweetDuck.Configuration{
|
|||||||
|
|
||||||
private readonly string file;
|
private readonly string file;
|
||||||
|
|
||||||
public UserConfig(string file){ // TODO make private after removing UserConfigLegacy
|
private UserConfig(string file){
|
||||||
this.file = file;
|
this.file = file;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,28 +166,52 @@ namespace TweetDuck.Configuration{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool Reload(){
|
||||||
|
try{
|
||||||
|
LoadInternal(false);
|
||||||
|
return true;
|
||||||
|
}catch(FileNotFoundException){
|
||||||
|
try{
|
||||||
|
Serializer.Write(file, new UserConfig(file));
|
||||||
|
LoadInternal(false);
|
||||||
|
return true;
|
||||||
|
}catch(Exception e){
|
||||||
|
Program.Reporter.HandleException("Configuration Error", "Could not regenerate configuration file.", true, e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}catch(Exception e){
|
||||||
|
Program.Reporter.HandleException("Configuration Error", "Could not reload configuration file.", true, e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadInternal(bool backup){
|
||||||
|
Serializer.Read(backup ? GetBackupFile(file) : file, this);
|
||||||
|
}
|
||||||
|
|
||||||
public static UserConfig Load(string file){
|
public static UserConfig Load(string file){
|
||||||
Exception firstException = null;
|
Exception firstException = null;
|
||||||
|
|
||||||
for(int attempt = 0; attempt < 2; attempt++){
|
for(int attempt = 0; attempt < 2; attempt++){
|
||||||
try{
|
try{
|
||||||
UserConfig config = new UserConfig(file);
|
UserConfig config = new UserConfig(file);
|
||||||
Serializer.Read(attempt == 0 ? file : GetBackupFile(file), config);
|
config.LoadInternal(attempt > 0);
|
||||||
return config;
|
return config;
|
||||||
}catch(FileNotFoundException){
|
}catch(FileNotFoundException){
|
||||||
}catch(DirectoryNotFoundException){
|
}catch(DirectoryNotFoundException){
|
||||||
break;
|
break;
|
||||||
}catch(FormatException){
|
|
||||||
UserConfig config = UserConfigLegacy.Load(file);
|
|
||||||
config.Save();
|
|
||||||
return config;
|
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
if (attempt == 0){
|
if (attempt == 0){
|
||||||
firstException = e;
|
firstException = e;
|
||||||
Program.Reporter.Log(e.ToString());
|
Program.Reporter.Log(e.ToString());
|
||||||
}
|
}
|
||||||
|
else if (firstException is FormatException){
|
||||||
|
Program.Reporter.HandleException("Configuration Error", "The configuration file is outdated or corrupted. If you continue, your program options will be reset.", true, e);
|
||||||
|
return new UserConfig(file);
|
||||||
|
}
|
||||||
else if (firstException != null){
|
else if (firstException != null){
|
||||||
Program.Reporter.HandleException("Configuration Error", "Could not open the backup configuration file. If you continue, your program options will be reset.", true, e);
|
Program.Reporter.HandleException("Configuration Error", "Could not open the backup configuration file. If you continue, your program options will be reset.", true, e);
|
||||||
|
return new UserConfig(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,210 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -3,6 +3,7 @@ using System.Text;
|
|||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using CefSharp;
|
using CefSharp;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
|
using TweetDuck.Core.Handling;
|
||||||
using TweetDuck.Core.Notification;
|
using TweetDuck.Core.Notification;
|
||||||
using TweetDuck.Core.Other;
|
using TweetDuck.Core.Other;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
@@ -10,17 +11,18 @@ using TweetDuck.Resources;
|
|||||||
|
|
||||||
namespace TweetDuck.Core.Bridge{
|
namespace TweetDuck.Core.Bridge{
|
||||||
sealed class TweetDeckBridge{
|
sealed class TweetDeckBridge{
|
||||||
public static string LastRightClickedLink = string.Empty;
|
public static string LastHighlightedTweetUrl = string.Empty;
|
||||||
public static string LastRightClickedImage = string.Empty;
|
public static string LastHighlightedQuoteUrl = string.Empty;
|
||||||
public static string LastHighlightedTweet = string.Empty;
|
private static string LastHighlightedTweetAuthors = string.Empty;
|
||||||
public static string LastHighlightedQuotedTweet = string.Empty;
|
private static string LastHighlightedTweetImages = string.Empty;
|
||||||
public static string LastHighlightedTweetAuthor = string.Empty;
|
|
||||||
public static string[] LastHighlightedTweetImages = StringUtils.EmptyArray;
|
public static string[] LastHighlightedTweetAuthorsArray => LastHighlightedTweetAuthors.Split(';');
|
||||||
public static Dictionary<string, string> SessionData = new Dictionary<string, string>(2);
|
public static string[] LastHighlightedTweetImagesArray => LastHighlightedTweetImages.Split(';');
|
||||||
|
|
||||||
|
private static readonly Dictionary<string, string> SessionData = new Dictionary<string, string>(2);
|
||||||
|
|
||||||
public static void ResetStaticProperties(){
|
public static void ResetStaticProperties(){
|
||||||
LastRightClickedLink = LastRightClickedImage = LastHighlightedTweet = LastHighlightedQuotedTweet = LastHighlightedTweetAuthor = string.Empty;
|
LastHighlightedTweetUrl = LastHighlightedQuoteUrl = LastHighlightedTweetAuthors = LastHighlightedTweetImages = string.Empty;
|
||||||
LastHighlightedTweetImages = StringUtils.EmptyArray;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RestoreSessionData(IFrame frame){
|
public static void RestoreSessionData(IFrame frame){
|
||||||
@@ -56,20 +58,16 @@ namespace TweetDuck.Core.Bridge{
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetLastRightClickedLink(string link){
|
public void SetLastRightClickInfo(string type, string link){
|
||||||
form.InvokeAsyncSafe(() => LastRightClickedLink = link);
|
form.InvokeAsyncSafe(() => ContextMenuBase.SetContextInfo(type, link));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetLastRightClickedImage(string link){
|
public void SetLastHighlightedTweet(string tweetUrl, string quoteUrl, string authors, string imageList){
|
||||||
form.InvokeAsyncSafe(() => LastRightClickedImage = link);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetLastHighlightedTweet(string link, string quotedLink, string author, string imageList){
|
|
||||||
form.InvokeAsyncSafe(() => {
|
form.InvokeAsyncSafe(() => {
|
||||||
LastHighlightedTweet = link;
|
LastHighlightedTweetUrl = tweetUrl;
|
||||||
LastHighlightedQuotedTweet = quotedLink;
|
LastHighlightedQuoteUrl = quoteUrl;
|
||||||
LastHighlightedTweetAuthor = author;
|
LastHighlightedTweetAuthors = authors;
|
||||||
LastHighlightedTweetImages = imageList.Split(';');
|
LastHighlightedTweetImages = imageList;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,10 +75,10 @@ namespace TweetDuck.Core.Bridge{
|
|||||||
form.InvokeAsyncSafe(form.OpenContextMenu);
|
form.InvokeAsyncSafe(form.OpenContextMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnTweetPopup(string columnName, string tweetHtml, int tweetCharacters, string tweetUrl, string quoteUrl){
|
public void OnTweetPopup(string columnId, string chirpId, string columnName, string tweetHtml, int tweetCharacters, string tweetUrl, string quoteUrl){
|
||||||
notification.InvokeAsyncSafe(() => {
|
notification.InvokeAsyncSafe(() => {
|
||||||
form.OnTweetNotification();
|
form.OnTweetNotification();
|
||||||
notification.ShowNotification(new TweetNotification(columnName, tweetHtml, tweetCharacters, tweetUrl, quoteUrl));
|
notification.ShowNotification(new TweetNotification(columnId, chirpId, columnName, tweetHtml, tweetCharacters, tweetUrl, quoteUrl));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,12 +120,12 @@ namespace TweetDuck.Core.Bridge{
|
|||||||
form.InvokeAsyncSafe(WindowsUtils.ClipboardStripHtmlStyles);
|
form.InvokeAsyncSafe(WindowsUtils.ClipboardStripHtmlStyles);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetIdleSeconds(){
|
public void OpenBrowser(string url){
|
||||||
return NativeMethods.GetIdleSeconds();
|
form.InvokeAsyncSafe(() => BrowserUtils.OpenExternalBrowser(url));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OpenBrowser(string url){
|
public int GetIdleSeconds(){
|
||||||
BrowserUtils.OpenExternalBrowser(url);
|
return NativeMethods.GetIdleSeconds();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Alert(string type, string contents){
|
public void Alert(string type, string contents){
|
||||||
|
@@ -2,8 +2,6 @@ using System;
|
|||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Core.Utils;
|
|
||||||
using TweetLib.Communication;
|
|
||||||
|
|
||||||
namespace TweetDuck.Core.Controls{
|
namespace TweetDuck.Core.Controls{
|
||||||
static class ControlExtensions{
|
static class ControlExtensions{
|
||||||
@@ -68,12 +66,6 @@ namespace TweetDuck.Core.Controls{
|
|||||||
else return true;
|
else return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SetElevated(this Button button){
|
|
||||||
button.Text = " "+button.Text;
|
|
||||||
button.FlatStyle = FlatStyle.System;
|
|
||||||
Comms.SendMessage(button.Handle, NativeMethods.BCM_SETSHIELD, 0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void EnableMultilineShortcuts(this TextBox textBox){
|
public static void EnableMultilineShortcuts(this TextBox textBox){
|
||||||
textBox.KeyDown += (sender, args) => {
|
textBox.KeyDown += (sender, args) => {
|
||||||
if (args.Control && args.KeyCode == Keys.A){
|
if (args.Control && args.KeyCode == Keys.A){
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Controls{
|
namespace TweetDuck.Core.Controls{
|
||||||
class FlatButton : Button{
|
sealed class FlatButton : Button{
|
||||||
protected override bool ShowFocusCues => false;
|
protected override bool ShowFocusCues => false;
|
||||||
|
|
||||||
public FlatButton(){
|
public FlatButton(){
|
||||||
|
4
Core/FormBrowser.Designer.cs
generated
4
Core/FormBrowser.Designer.cs
generated
@@ -39,10 +39,10 @@
|
|||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
this.BackColor = TweetDuck.Core.Utils.TwitterUtils.BackgroundColor;
|
this.BackColor = TweetDuck.Core.Utils.TwitterUtils.BackgroundColor;
|
||||||
this.ClientSize = new System.Drawing.Size(324, 386);
|
this.ClientSize = new System.Drawing.Size(400, 386);
|
||||||
this.Icon = Properties.Resources.icon;
|
this.Icon = Properties.Resources.icon;
|
||||||
this.Location = TweetDuck.Core.Controls.ControlExtensions.InvisibleLocation;
|
this.Location = TweetDuck.Core.Controls.ControlExtensions.InvisibleLocation;
|
||||||
this.MinimumSize = new System.Drawing.Size(340, 424);
|
this.MinimumSize = new System.Drawing.Size(416, 424);
|
||||||
this.Name = "FormBrowser";
|
this.Name = "FormBrowser";
|
||||||
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
|
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
|
||||||
this.Activated += new System.EventHandler(this.FormBrowser_Activated);
|
this.Activated += new System.EventHandler(this.FormBrowser_Activated);
|
||||||
|
@@ -19,7 +19,6 @@ using TweetDuck.Plugins.Enums;
|
|||||||
using TweetDuck.Plugins.Events;
|
using TweetDuck.Plugins.Events;
|
||||||
using TweetDuck.Resources;
|
using TweetDuck.Resources;
|
||||||
using TweetDuck.Updates;
|
using TweetDuck.Updates;
|
||||||
using TweetDuck.Updates.Events;
|
|
||||||
using TweetLib.Audio;
|
using TweetLib.Audio;
|
||||||
|
|
||||||
namespace TweetDuck.Core{
|
namespace TweetDuck.Core{
|
||||||
@@ -85,6 +84,7 @@ namespace TweetDuck.Core{
|
|||||||
this.notification.Show();
|
this.notification.Show();
|
||||||
|
|
||||||
this.browser = new ChromiumWebBrowser("https://tweetdeck.twitter.com/"){
|
this.browser = new ChromiumWebBrowser("https://tweetdeck.twitter.com/"){
|
||||||
|
DialogHandler = new FileDialogHandler(),
|
||||||
MenuHandler = new ContextMenuBrowser(this),
|
MenuHandler = new ContextMenuBrowser(this),
|
||||||
JsDialogHandler = new JavaScriptDialogHandler(),
|
JsDialogHandler = new JavaScriptDialogHandler(),
|
||||||
KeyboardHandler = new KeyboardHandlerBrowser(this),
|
KeyboardHandler = new KeyboardHandlerBrowser(this),
|
||||||
@@ -337,7 +337,7 @@ namespace TweetDuck.Core{
|
|||||||
browser.ExecuteScriptAsync("window.TDPF_setPluginState", e.Plugin, e.IsEnabled);
|
browser.ExecuteScriptAsync("window.TDPF_setPluginState", e.Plugin, e.IsEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updates_UpdateAccepted(object sender, UpdateAcceptedEventArgs e){
|
private void updates_UpdateAccepted(object sender, UpdateEventArgs e){
|
||||||
this.InvokeAsyncSafe(() => {
|
this.InvokeAsyncSafe(() => {
|
||||||
FormManager.CloseAllDialogs();
|
FormManager.CloseAllDialogs();
|
||||||
|
|
||||||
@@ -351,9 +351,9 @@ namespace TweetDuck.Core{
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updates_UpdateDismissed(object sender, UpdateDismissedEventArgs e){
|
private void updates_UpdateDismissed(object sender, UpdateEventArgs e){
|
||||||
this.InvokeAsyncSafe(() => {
|
this.InvokeAsyncSafe(() => {
|
||||||
Config.DismissedUpdate = e.VersionTag;
|
Config.DismissedUpdate = e.UpdateInfo.VersionTag;
|
||||||
Config.Save();
|
Config.Save();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -378,7 +378,7 @@ namespace TweetDuck.Core{
|
|||||||
if (isLoaded){
|
if (isLoaded){
|
||||||
if (m.Msg == Program.WindowRestoreMessage){
|
if (m.Msg == Program.WindowRestoreMessage){
|
||||||
if (WindowsUtils.CurrentProcessID == m.WParam.ToInt32()){
|
if (WindowsUtils.CurrentProcessID == m.WParam.ToInt32()){
|
||||||
trayIcon_ClickRestore(trayIcon, new EventArgs());
|
trayIcon_ClickRestore(trayIcon, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -454,8 +454,10 @@ namespace TweetDuck.Core{
|
|||||||
|
|
||||||
form.FormClosed += (sender, args) => {
|
form.FormClosed += (sender, args) => {
|
||||||
if (!prevEnableUpdateCheck && Config.EnableUpdateCheck){
|
if (!prevEnableUpdateCheck && Config.EnableUpdateCheck){
|
||||||
updates.DismissUpdate(string.Empty);
|
Config.DismissedUpdate = null;
|
||||||
updates.Check(false);
|
Config.Save();
|
||||||
|
|
||||||
|
updates.Check(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Config.EnableTrayHighlight){
|
if (!Config.EnableTrayHighlight){
|
||||||
@@ -469,7 +471,13 @@ namespace TweetDuck.Core{
|
|||||||
memoryUsageTracker.Stop();
|
memoryUsageTracker.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (form.ShouldReloadBrowser){
|
||||||
|
FormManager.TryFind<FormPlugins>()?.Close();
|
||||||
|
plugins.Reload(); // also reloads the browser
|
||||||
|
}
|
||||||
|
else{
|
||||||
UpdateProperties(PropertyBridge.Environment.Browser);
|
UpdateProperties(PropertyBridge.Environment.Browser);
|
||||||
|
}
|
||||||
|
|
||||||
notification.RequiresResize = true;
|
notification.RequiresResize = true;
|
||||||
form.Dispose();
|
form.Dispose();
|
||||||
@@ -507,6 +515,7 @@ namespace TweetDuck.Core{
|
|||||||
soundNotification.PlaybackError += soundNotification_PlaybackError;
|
soundNotification.PlaybackError += soundNotification_PlaybackError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
soundNotification.SetVolume(Config.NotificationSoundVolume);
|
||||||
soundNotification.Play(Config.NotificationSoundPath);
|
soundNotification.Play(Config.NotificationSoundPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -528,17 +537,31 @@ namespace TweetDuck.Core{
|
|||||||
videoPlayer.Launch(url);
|
videoPlayer.Launch(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ToggleVideoPause(){
|
public void HideVideoOverlay(){
|
||||||
|
browser.ExecuteScriptAsync("$('#td-video-player-overlay').remove()");
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ProcessBrowserKey(Keys key){
|
||||||
if (videoPlayer != null && videoPlayer.Running){
|
if (videoPlayer != null && videoPlayer.Running){
|
||||||
videoPlayer.TogglePause();
|
videoPlayer.SendKeyEvent(key);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void HideVideoOverlay(){
|
public void ShowTweetDetail(string columnId, string chirpId, string fallbackUrl){
|
||||||
browser.ExecuteScriptAsync("$('#td-video-player-overlay').remove()");
|
Activate();
|
||||||
|
|
||||||
|
using(IFrame frame = browser.GetBrowser().MainFrame){
|
||||||
|
if (!TwitterUtils.IsTweetDeckWebsite(frame)){
|
||||||
|
FormMessage.Error("View Tweet Detail", "TweetDeck is not currently loaded.", FormMessage.OK);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
notification.FinishCurrentNotification();
|
||||||
|
browser.ExecuteScriptAsync("window.TDGF_showTweetDetail", columnId, chirpId, fallbackUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnTweetScreenshotReady(string html, int width, int height){
|
public void OnTweetScreenshotReady(string html, int width, int height){
|
||||||
|
@@ -4,8 +4,12 @@ using TweetDuck.Core.Other;
|
|||||||
|
|
||||||
namespace TweetDuck.Core{
|
namespace TweetDuck.Core{
|
||||||
static class FormManager{
|
static class FormManager{
|
||||||
|
public static T TryFind<T>() where T : Form{
|
||||||
|
return Application.OpenForms.OfType<T>().FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
public static bool TryBringToFront<T>() where T : Form{
|
public static bool TryBringToFront<T>() where T : Form{
|
||||||
T form = Application.OpenForms.OfType<T>().FirstOrDefault();
|
T form = TryFind<T>();
|
||||||
|
|
||||||
if (form != null){
|
if (form != null){
|
||||||
form.BringToFront();
|
form.BringToFront();
|
||||||
|
@@ -6,6 +6,8 @@ using CefSharp;
|
|||||||
using TweetDuck.Core.Bridge;
|
using TweetDuck.Core.Bridge;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Handling{
|
namespace TweetDuck.Core.Handling{
|
||||||
abstract class ContextMenuBase : IContextMenuHandler{
|
abstract class ContextMenuBase : IContextMenuHandler{
|
||||||
@@ -13,63 +15,76 @@ namespace TweetDuck.Core.Handling{
|
|||||||
|
|
||||||
private static TwitterUtils.ImageQuality ImageQuality => Program.UserConfig.TwitterImageQuality;
|
private static TwitterUtils.ImageQuality ImageQuality => Program.UserConfig.TwitterImageQuality;
|
||||||
|
|
||||||
private static string GetLink(IContextMenuParams parameters){
|
private static KeyValuePair<string, string> ContextInfo;
|
||||||
return string.IsNullOrEmpty(TweetDeckBridge.LastRightClickedLink) ? parameters.UnfilteredLinkUrl : TweetDeckBridge.LastRightClickedLink;
|
private static bool IsLink => ContextInfo.Key == "link";
|
||||||
|
private static bool IsImage => ContextInfo.Key == "image";
|
||||||
|
private static bool IsVideo => ContextInfo.Key == "video";
|
||||||
|
|
||||||
|
public static void SetContextInfo(string type, string link){
|
||||||
|
ContextInfo = new KeyValuePair<string, string>(string.IsNullOrEmpty(link) ? null : type, link);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetImage(IContextMenuParams parameters){
|
private static string GetMediaLink(IContextMenuParams parameters){
|
||||||
return string.IsNullOrEmpty(TweetDeckBridge.LastRightClickedImage) ? parameters.SourceUrl : TweetDeckBridge.LastRightClickedImage;
|
return IsImage || IsVideo ? ContextInfo.Value : parameters.SourceUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
private const int MenuOpenLinkUrl = 26500;
|
private const CefMenuCommand MenuOpenLinkUrl = (CefMenuCommand)26500;
|
||||||
private const int MenuCopyLinkUrl = 26501;
|
private const CefMenuCommand MenuCopyLinkUrl = (CefMenuCommand)26501;
|
||||||
private const int MenuCopyUsername = 26502;
|
private const CefMenuCommand MenuCopyUsername = (CefMenuCommand)26502;
|
||||||
private const int MenuOpenImageUrl = 26503;
|
private const CefMenuCommand MenuOpenMediaUrl = (CefMenuCommand)26503;
|
||||||
private const int MenuCopyImageUrl = 26504;
|
private const CefMenuCommand MenuCopyMediaUrl = (CefMenuCommand)26504;
|
||||||
private const int MenuSaveImage = 26505;
|
private const CefMenuCommand MenuSaveMedia = (CefMenuCommand)26505;
|
||||||
private const int MenuSaveAllImages = 26506;
|
private const CefMenuCommand MenuSaveTweetImages = (CefMenuCommand)26506;
|
||||||
private const int MenuOpenDevTools = 26599;
|
private const CefMenuCommand MenuOpenDevTools = (CefMenuCommand)26599;
|
||||||
|
|
||||||
private readonly Form form;
|
private string[] lastHighlightedTweetAuthors;
|
||||||
|
|
||||||
private string lastHighlightedTweetAuthor;
|
|
||||||
private string[] lastHighlightedTweetImageList;
|
private string[] lastHighlightedTweetImageList;
|
||||||
|
|
||||||
protected ContextMenuBase(Form form){
|
|
||||||
this.form = form;
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
|
public virtual void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
|
||||||
bool hasTweetImage = !string.IsNullOrEmpty(TweetDeckBridge.LastRightClickedImage);
|
|
||||||
lastHighlightedTweetAuthor = TweetDeckBridge.LastHighlightedTweetAuthor;
|
|
||||||
lastHighlightedTweetImageList = TweetDeckBridge.LastHighlightedTweetImages;
|
|
||||||
|
|
||||||
if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){
|
if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){
|
||||||
lastHighlightedTweetAuthor = string.Empty;
|
lastHighlightedTweetAuthors = StringUtils.EmptyArray;
|
||||||
lastHighlightedTweetImageList = StringUtils.EmptyArray;
|
lastHighlightedTweetImageList = StringUtils.EmptyArray;
|
||||||
}
|
ContextInfo = default(KeyValuePair<string, string>);
|
||||||
|
|
||||||
if (parameters.TypeFlags.HasFlag(ContextMenuType.Link) && !parameters.UnfilteredLinkUrl.EndsWith("tweetdeck.twitter.com/#", StringComparison.Ordinal) && !hasTweetImage){
|
|
||||||
if (TwitterUtils.RegexAccount.IsMatch(parameters.UnfilteredLinkUrl)){
|
|
||||||
model.AddItem((CefMenuCommand)MenuOpenLinkUrl, "Open account in browser");
|
|
||||||
model.AddItem((CefMenuCommand)MenuCopyLinkUrl, "Copy account address");
|
|
||||||
model.AddItem((CefMenuCommand)MenuCopyUsername, "Copy account username");
|
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
model.AddItem((CefMenuCommand)MenuOpenLinkUrl, "Open link in browser");
|
lastHighlightedTweetAuthors = TweetDeckBridge.LastHighlightedTweetAuthorsArray;
|
||||||
model.AddItem((CefMenuCommand)MenuCopyLinkUrl, "Copy link address");
|
lastHighlightedTweetImageList = TweetDeckBridge.LastHighlightedTweetImagesArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasTweetImage = IsImage;
|
||||||
|
bool hasTweetVideo = IsVideo;
|
||||||
|
|
||||||
|
string TextOpen(string name) => "Open "+name+" in browser";
|
||||||
|
string TextCopy(string name) => "Copy "+name+" address";
|
||||||
|
string TextSave(string name) => "Save "+name+" as...";
|
||||||
|
|
||||||
|
if (parameters.TypeFlags.HasFlag(ContextMenuType.Link) && !parameters.UnfilteredLinkUrl.EndsWith("tweetdeck.twitter.com/#", StringComparison.Ordinal) && !hasTweetImage && !hasTweetVideo){
|
||||||
|
if (TwitterUtils.RegexAccount.IsMatch(parameters.UnfilteredLinkUrl)){
|
||||||
|
model.AddItem(MenuOpenLinkUrl, TextOpen("account"));
|
||||||
|
model.AddItem(MenuCopyLinkUrl, TextCopy("account"));
|
||||||
|
model.AddItem(MenuCopyUsername, "Copy account username");
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
model.AddItem(MenuOpenLinkUrl, TextOpen("link"));
|
||||||
|
model.AddItem(MenuCopyLinkUrl, TextCopy("link"));
|
||||||
}
|
}
|
||||||
|
|
||||||
model.AddSeparator();
|
model.AddSeparator();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((parameters.TypeFlags.HasFlag(ContextMenuType.Media) && parameters.HasImageContents) || hasTweetImage){
|
if (hasTweetVideo){
|
||||||
model.AddItem((CefMenuCommand)MenuOpenImageUrl, "Open image in browser");
|
model.AddItem(MenuOpenMediaUrl, TextOpen("video"));
|
||||||
model.AddItem((CefMenuCommand)MenuCopyImageUrl, "Copy image address");
|
model.AddItem(MenuCopyMediaUrl, TextCopy("video"));
|
||||||
model.AddItem((CefMenuCommand)MenuSaveImage, "Save image as...");
|
model.AddItem(MenuSaveMedia, TextSave("video"));
|
||||||
|
model.AddSeparator();
|
||||||
|
}
|
||||||
|
else if ((parameters.TypeFlags.HasFlag(ContextMenuType.Media) && parameters.HasImageContents) || hasTweetImage){
|
||||||
|
model.AddItem(MenuOpenMediaUrl, TextOpen("image"));
|
||||||
|
model.AddItem(MenuCopyMediaUrl, TextCopy("image"));
|
||||||
|
model.AddItem(MenuSaveMedia, TextSave("image"));
|
||||||
|
|
||||||
if (lastHighlightedTweetImageList.Length > 1){
|
if (lastHighlightedTweetImageList.Length > 1){
|
||||||
model.AddItem((CefMenuCommand)MenuSaveAllImages, "Save all images as...");
|
model.AddItem(MenuSaveTweetImages, TextSave("all images"));
|
||||||
}
|
}
|
||||||
|
|
||||||
model.AddSeparator();
|
model.AddSeparator();
|
||||||
@@ -77,34 +92,40 @@ namespace TweetDuck.Core.Handling{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public virtual bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){
|
public virtual bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){
|
||||||
switch((int)commandId){
|
switch(commandId){
|
||||||
case MenuOpenLinkUrl:
|
case MenuOpenLinkUrl:
|
||||||
BrowserUtils.OpenExternalBrowser(parameters.LinkUrl);
|
OpenBrowser(browserControl.AsControl(), IsLink ? ContextInfo.Value : parameters.LinkUrl);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MenuCopyLinkUrl:
|
case MenuCopyLinkUrl:
|
||||||
SetClipboardText(GetLink(parameters));
|
SetClipboardText(browserControl.AsControl(), IsLink ? ContextInfo.Value : parameters.UnfilteredLinkUrl);
|
||||||
break;
|
|
||||||
|
|
||||||
case MenuOpenImageUrl:
|
|
||||||
BrowserUtils.OpenExternalBrowser(TwitterUtils.GetImageLink(GetImage(parameters), ImageQuality));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MenuSaveImage:
|
|
||||||
TwitterUtils.DownloadImage(GetImage(parameters), lastHighlightedTweetAuthor, ImageQuality);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MenuSaveAllImages:
|
|
||||||
TwitterUtils.DownloadImages(lastHighlightedTweetImageList, lastHighlightedTweetAuthor, ImageQuality);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MenuCopyImageUrl:
|
|
||||||
SetClipboardText(TwitterUtils.GetImageLink(GetImage(parameters), ImageQuality));
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MenuCopyUsername:
|
case MenuCopyUsername:
|
||||||
Match match = TwitterUtils.RegexAccount.Match(parameters.UnfilteredLinkUrl);
|
Match match = TwitterUtils.RegexAccount.Match(parameters.UnfilteredLinkUrl);
|
||||||
SetClipboardText(match.Success ? match.Groups[1].Value : parameters.UnfilteredLinkUrl);
|
SetClipboardText(browserControl.AsControl(), match.Success ? match.Groups[1].Value : parameters.UnfilteredLinkUrl);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MenuOpenMediaUrl:
|
||||||
|
OpenBrowser(browserControl.AsControl(), TwitterUtils.GetMediaLink(GetMediaLink(parameters), ImageQuality));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MenuCopyMediaUrl:
|
||||||
|
SetClipboardText(browserControl.AsControl(), TwitterUtils.GetMediaLink(GetMediaLink(parameters), ImageQuality));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MenuSaveMedia:
|
||||||
|
if (IsVideo){
|
||||||
|
TwitterUtils.DownloadVideo(GetMediaLink(parameters));
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
TwitterUtils.DownloadImage(GetMediaLink(parameters), lastHighlightedTweetAuthors.LastOrDefault(), ImageQuality);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MenuSaveTweetImages:
|
||||||
|
TwitterUtils.DownloadImages(lastHighlightedTweetImageList, lastHighlightedTweetAuthors.LastOrDefault(), ImageQuality);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MenuOpenDevTools:
|
case MenuOpenDevTools:
|
||||||
@@ -116,20 +137,23 @@ namespace TweetDuck.Core.Handling{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public virtual void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame){
|
public virtual void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame){
|
||||||
TweetDeckBridge.LastRightClickedLink = string.Empty;
|
ContextInfo = default(KeyValuePair<string, string>);
|
||||||
TweetDeckBridge.LastRightClickedImage = string.Empty;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual bool RunContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback){
|
public virtual bool RunContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void SetClipboardText(string text){
|
protected void OpenBrowser(Control control, string url){
|
||||||
form.InvokeAsyncSafe(() => WindowsUtils.SetClipboard(text, TextDataFormat.UnicodeText));
|
control.InvokeAsyncSafe(() => BrowserUtils.OpenExternalBrowser(url));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void SetClipboardText(Control control, string text){
|
||||||
|
control.InvokeAsyncSafe(() => WindowsUtils.SetClipboard(text, TextDataFormat.UnicodeText));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static void AddDebugMenuItems(IMenuModel model){
|
protected static void AddDebugMenuItems(IMenuModel model){
|
||||||
model.AddItem((CefMenuCommand)MenuOpenDevTools, "Open dev tools");
|
model.AddItem(MenuOpenDevTools, "Open dev tools");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static void RemoveSeparatorIfLast(IMenuModel model){
|
protected static void RemoveSeparatorIfLast(IMenuModel model){
|
||||||
|
@@ -5,18 +5,18 @@ using TweetDuck.Core.Controls;
|
|||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Handling{
|
namespace TweetDuck.Core.Handling{
|
||||||
class ContextMenuBrowser : ContextMenuBase{
|
sealed class ContextMenuBrowser : ContextMenuBase{
|
||||||
private const int MenuGlobal = 26600;
|
private const CefMenuCommand MenuGlobal = (CefMenuCommand)26600;
|
||||||
private const int MenuMute = 26601;
|
private const CefMenuCommand MenuMute = (CefMenuCommand)26601;
|
||||||
private const int MenuSettings = 26602;
|
private const CefMenuCommand MenuSettings = (CefMenuCommand)26602;
|
||||||
private const int MenuPlugins = 26003;
|
private const CefMenuCommand MenuPlugins = (CefMenuCommand)26003;
|
||||||
private const int MenuAbout = 26604;
|
private const CefMenuCommand MenuAbout = (CefMenuCommand)26604;
|
||||||
|
|
||||||
private const int MenuOpenTweetUrl = 26610;
|
private const CefMenuCommand MenuOpenTweetUrl = (CefMenuCommand)26610;
|
||||||
private const int MenuCopyTweetUrl = 26611;
|
private const CefMenuCommand MenuCopyTweetUrl = (CefMenuCommand)26611;
|
||||||
private const int MenuOpenQuotedTweetUrl = 26612;
|
private const CefMenuCommand MenuOpenQuotedTweetUrl = (CefMenuCommand)26612;
|
||||||
private const int MenuCopyQuotedTweetUrl = 26613;
|
private const CefMenuCommand MenuCopyQuotedTweetUrl = (CefMenuCommand)26613;
|
||||||
private const int MenuScreenshotTweet = 26614;
|
private const CefMenuCommand MenuScreenshotTweet = (CefMenuCommand)26614;
|
||||||
|
|
||||||
private const string TitleReloadBrowser = "Reload browser";
|
private const string TitleReloadBrowser = "Reload browser";
|
||||||
private const string TitleMuteNotifications = "Mute notifications";
|
private const string TitleMuteNotifications = "Mute notifications";
|
||||||
@@ -26,10 +26,10 @@ namespace TweetDuck.Core.Handling{
|
|||||||
|
|
||||||
private readonly FormBrowser form;
|
private readonly FormBrowser form;
|
||||||
|
|
||||||
private string lastHighlightedTweet;
|
private string lastHighlightedTweetUrl;
|
||||||
private string lastHighlightedQuotedTweet;
|
private string lastHighlightedQuoteUrl;
|
||||||
|
|
||||||
public ContextMenuBrowser(FormBrowser form) : base(form){
|
public ContextMenuBrowser(FormBrowser form){
|
||||||
this.form = form;
|
this.form = form;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,23 +46,23 @@ namespace TweetDuck.Core.Handling{
|
|||||||
|
|
||||||
base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);
|
base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);
|
||||||
|
|
||||||
lastHighlightedTweet = TweetDeckBridge.LastHighlightedTweet;
|
lastHighlightedTweetUrl = TweetDeckBridge.LastHighlightedTweetUrl;
|
||||||
lastHighlightedQuotedTweet = TweetDeckBridge.LastHighlightedQuotedTweet;
|
lastHighlightedQuoteUrl = TweetDeckBridge.LastHighlightedQuoteUrl;
|
||||||
|
|
||||||
if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){
|
if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){
|
||||||
lastHighlightedTweet = string.Empty;
|
lastHighlightedTweetUrl = string.Empty;
|
||||||
lastHighlightedQuotedTweet = string.Empty;
|
lastHighlightedQuoteUrl = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(lastHighlightedTweet) && (parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){
|
if (!string.IsNullOrEmpty(lastHighlightedTweetUrl) && (parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){
|
||||||
model.AddItem((CefMenuCommand)MenuOpenTweetUrl, "Open tweet in browser");
|
model.AddItem(MenuOpenTweetUrl, "Open tweet in browser");
|
||||||
model.AddItem((CefMenuCommand)MenuCopyTweetUrl, "Copy tweet address");
|
model.AddItem(MenuCopyTweetUrl, "Copy tweet address");
|
||||||
model.AddItem((CefMenuCommand)MenuScreenshotTweet, "Screenshot tweet to clipboard");
|
model.AddItem(MenuScreenshotTweet, "Screenshot tweet to clipboard");
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(lastHighlightedQuotedTweet)){
|
if (!string.IsNullOrEmpty(lastHighlightedQuoteUrl)){
|
||||||
model.AddSeparator();
|
model.AddSeparator();
|
||||||
model.AddItem((CefMenuCommand)MenuOpenQuotedTweetUrl, "Open quoted tweet in browser");
|
model.AddItem(MenuOpenQuotedTweetUrl, "Open quoted tweet in browser");
|
||||||
model.AddItem((CefMenuCommand)MenuCopyQuotedTweetUrl, "Copy quoted tweet address");
|
model.AddItem(MenuCopyQuotedTweetUrl, "Copy quoted tweet address");
|
||||||
}
|
}
|
||||||
|
|
||||||
model.AddSeparator();
|
model.AddSeparator();
|
||||||
@@ -71,16 +71,16 @@ namespace TweetDuck.Core.Handling{
|
|||||||
if ((parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){
|
if ((parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){
|
||||||
AddSeparator(model);
|
AddSeparator(model);
|
||||||
|
|
||||||
IMenuModel globalMenu = model.Count == 0 ? model : model.AddSubMenu((CefMenuCommand)MenuGlobal, Program.BrandName);
|
IMenuModel globalMenu = model.Count == 0 ? model : model.AddSubMenu(MenuGlobal, Program.BrandName);
|
||||||
|
|
||||||
globalMenu.AddItem(CefMenuCommand.Reload, TitleReloadBrowser);
|
globalMenu.AddItem(CefMenuCommand.Reload, TitleReloadBrowser);
|
||||||
globalMenu.AddCheckItem((CefMenuCommand)MenuMute, TitleMuteNotifications);
|
globalMenu.AddCheckItem(MenuMute, TitleMuteNotifications);
|
||||||
globalMenu.SetChecked((CefMenuCommand)MenuMute, Program.UserConfig.MuteNotifications);
|
globalMenu.SetChecked(MenuMute, Program.UserConfig.MuteNotifications);
|
||||||
globalMenu.AddSeparator();
|
globalMenu.AddSeparator();
|
||||||
|
|
||||||
globalMenu.AddItem((CefMenuCommand)MenuSettings, TitleSettings);
|
globalMenu.AddItem(MenuSettings, TitleSettings);
|
||||||
globalMenu.AddItem((CefMenuCommand)MenuPlugins, TitlePlugins);
|
globalMenu.AddItem(MenuPlugins, TitlePlugins);
|
||||||
globalMenu.AddItem((CefMenuCommand)MenuAbout, TitleAboutProgram);
|
globalMenu.AddItem(MenuAbout, TitleAboutProgram);
|
||||||
|
|
||||||
if (HasDevTools){
|
if (HasDevTools){
|
||||||
globalMenu.AddSeparator();
|
globalMenu.AddSeparator();
|
||||||
@@ -96,8 +96,8 @@ namespace TweetDuck.Core.Handling{
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch((int)commandId){
|
switch(commandId){
|
||||||
case (int)CefMenuCommand.Reload:
|
case CefMenuCommand.Reload:
|
||||||
form.InvokeAsyncSafe(form.ReloadToTweetDeck);
|
form.InvokeAsyncSafe(form.ReloadToTweetDeck);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@@ -118,11 +118,11 @@ namespace TweetDuck.Core.Handling{
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MenuOpenTweetUrl:
|
case MenuOpenTweetUrl:
|
||||||
BrowserUtils.OpenExternalBrowser(lastHighlightedTweet);
|
OpenBrowser(form, lastHighlightedTweetUrl);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MenuCopyTweetUrl:
|
case MenuCopyTweetUrl:
|
||||||
SetClipboardText(lastHighlightedTweet);
|
SetClipboardText(form, lastHighlightedTweetUrl);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MenuScreenshotTweet:
|
case MenuScreenshotTweet:
|
||||||
@@ -130,11 +130,11 @@ namespace TweetDuck.Core.Handling{
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MenuOpenQuotedTweetUrl:
|
case MenuOpenQuotedTweetUrl:
|
||||||
BrowserUtils.OpenExternalBrowser(lastHighlightedQuotedTweet);
|
OpenBrowser(form, lastHighlightedQuoteUrl);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MenuCopyQuotedTweetUrl:
|
case MenuCopyQuotedTweetUrl:
|
||||||
SetClipboardText(lastHighlightedQuotedTweet);
|
SetClipboardText(form, lastHighlightedQuoteUrl);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -3,16 +3,17 @@ using TweetDuck.Core.Controls;
|
|||||||
using TweetDuck.Core.Notification;
|
using TweetDuck.Core.Notification;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Handling{
|
namespace TweetDuck.Core.Handling{
|
||||||
class ContextMenuNotification : ContextMenuBase{
|
sealed class ContextMenuNotification : ContextMenuBase{
|
||||||
private const int MenuSkipTweet = 26600;
|
private const CefMenuCommand MenuViewDetail = (CefMenuCommand)26600;
|
||||||
private const int MenuFreeze = 26601;
|
private const CefMenuCommand MenuSkipTweet = (CefMenuCommand)26601;
|
||||||
private const int MenuCopyTweetUrl = 26602;
|
private const CefMenuCommand MenuFreeze = (CefMenuCommand)26602;
|
||||||
private const int MenuCopyQuotedTweetUrl = 26603;
|
private const CefMenuCommand MenuCopyTweetUrl = (CefMenuCommand)26603;
|
||||||
|
private const CefMenuCommand MenuCopyQuotedTweetUrl = (CefMenuCommand)26604;
|
||||||
|
|
||||||
private readonly FormNotificationBase form;
|
private readonly FormNotificationBase form;
|
||||||
private readonly bool enableCustomMenu;
|
private readonly bool enableCustomMenu;
|
||||||
|
|
||||||
public ContextMenuNotification(FormNotificationBase form, bool enableCustomMenu) : base(form){
|
public ContextMenuNotification(FormNotificationBase form, bool enableCustomMenu){
|
||||||
this.form = form;
|
this.form = form;
|
||||||
this.enableCustomMenu = enableCustomMenu;
|
this.enableCustomMenu = enableCustomMenu;
|
||||||
}
|
}
|
||||||
@@ -28,23 +29,26 @@ namespace TweetDuck.Core.Handling{
|
|||||||
base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);
|
base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);
|
||||||
|
|
||||||
if (enableCustomMenu){
|
if (enableCustomMenu){
|
||||||
model.AddItem((CefMenuCommand)MenuSkipTweet, "Skip tweet");
|
if (form.CanViewDetail){
|
||||||
model.AddCheckItem((CefMenuCommand)MenuFreeze, "Freeze");
|
model.AddItem(MenuViewDetail, "View detail");
|
||||||
model.SetChecked((CefMenuCommand)MenuFreeze, form.FreezeTimer);
|
|
||||||
model.AddSeparator();
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(form.CurrentTweetUrl)){
|
|
||||||
model.AddItem((CefMenuCommand)MenuCopyTweetUrl, "Copy tweet address");
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(form.CurrentQuoteUrl)){
|
|
||||||
model.AddItem((CefMenuCommand)MenuCopyQuotedTweetUrl, "Copy quoted tweet address");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model.AddItem(MenuSkipTweet, "Skip tweet");
|
||||||
|
model.AddCheckItem(MenuFreeze, "Freeze");
|
||||||
|
model.SetChecked(MenuFreeze, form.FreezeTimer);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(form.CurrentTweetUrl)){
|
||||||
model.AddSeparator();
|
model.AddSeparator();
|
||||||
|
model.AddItem(MenuCopyTweetUrl, "Copy tweet address");
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(form.CurrentQuoteUrl)){
|
||||||
|
model.AddItem(MenuCopyQuotedTweetUrl, "Copy quoted tweet address");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HasDevTools){
|
if (HasDevTools){
|
||||||
|
model.AddSeparator();
|
||||||
AddDebugMenuItems(model);
|
AddDebugMenuItems(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +62,7 @@ namespace TweetDuck.Core.Handling{
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch((int)commandId){
|
switch(commandId){
|
||||||
case MenuSkipTweet:
|
case MenuSkipTweet:
|
||||||
form.InvokeAsyncSafe(form.FinishCurrentNotification);
|
form.InvokeAsyncSafe(form.FinishCurrentNotification);
|
||||||
return true;
|
return true;
|
||||||
@@ -67,12 +71,16 @@ namespace TweetDuck.Core.Handling{
|
|||||||
form.InvokeAsyncSafe(() => form.FreezeTimer = !form.FreezeTimer);
|
form.InvokeAsyncSafe(() => form.FreezeTimer = !form.FreezeTimer);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case MenuViewDetail:
|
||||||
|
form.InvokeSafe(form.ShowTweetDetail);
|
||||||
|
return true;
|
||||||
|
|
||||||
case MenuCopyTweetUrl:
|
case MenuCopyTweetUrl:
|
||||||
SetClipboardText(form.CurrentTweetUrl);
|
SetClipboardText(form, form.CurrentTweetUrl);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MenuCopyQuotedTweetUrl:
|
case MenuCopyQuotedTweetUrl:
|
||||||
SetClipboardText(form.CurrentQuoteUrl);
|
SetClipboardText(form, form.CurrentQuoteUrl);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
using CefSharp;
|
using CefSharp;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Handling.General{
|
namespace TweetDuck.Core.Handling.General{
|
||||||
class BrowserProcessHandler : IBrowserProcessHandler{
|
sealed class BrowserProcessHandler : IBrowserProcessHandler{
|
||||||
void IBrowserProcessHandler.OnContextInitialized(){
|
void IBrowserProcessHandler.OnContextInitialized(){
|
||||||
using(IRequestContext ctx = Cef.GetGlobalRequestContext()){
|
using(IRequestContext ctx = Cef.GetGlobalRequestContext()){
|
||||||
ctx.SetPreference("browser.enable_spellchecking", Program.UserConfig.EnableSpellCheck, out string _);
|
ctx.SetPreference("browser.enable_spellchecking", Program.UserConfig.EnableSpellCheck, out string _);
|
||||||
|
40
Core/Handling/General/FileDialogHandler.cs
Normal file
40
Core/Handling/General/FileDialogHandler.cs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using CefSharp;
|
||||||
|
|
||||||
|
namespace TweetDuck.Core.Handling.General{
|
||||||
|
sealed class FileDialogHandler : IDialogHandler{
|
||||||
|
public bool OnFileDialog(IWebBrowser browserControl, IBrowser browser, CefFileDialogMode mode, string title, string defaultFilePath, List<string> acceptFilters, int selectedAcceptFilter, IFileDialogCallback callback){
|
||||||
|
CefFileDialogMode dialogType = mode & CefFileDialogMode.TypeMask;
|
||||||
|
|
||||||
|
if (dialogType == CefFileDialogMode.Open || dialogType == CefFileDialogMode.OpenMultiple){
|
||||||
|
string allFilters = string.Join(";", acceptFilters.Select(filter => "*"+filter));
|
||||||
|
|
||||||
|
using(OpenFileDialog dialog = new OpenFileDialog{
|
||||||
|
AutoUpgradeEnabled = true,
|
||||||
|
DereferenceLinks = true,
|
||||||
|
Multiselect = dialogType == CefFileDialogMode.OpenMultiple,
|
||||||
|
Title = "Open Files",
|
||||||
|
Filter = $"All Supported Formats ({allFilters})|{allFilters}|All Files (*.*)|*.*"
|
||||||
|
}){
|
||||||
|
if (dialog.ShowDialog() == DialogResult.OK){
|
||||||
|
callback.Continue(acceptFilters.FindIndex(filter => filter == Path.GetExtension(dialog.FileName)), dialog.FileNames.ToList());
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
callback.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
callback.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
callback.Dispose();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -7,32 +7,52 @@ using TweetDuck.Core.Other;
|
|||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Handling.General{
|
namespace TweetDuck.Core.Handling.General{
|
||||||
class JavaScriptDialogHandler : IJsDialogHandler{
|
sealed class JavaScriptDialogHandler : IJsDialogHandler{
|
||||||
|
private static FormMessage CreateMessageForm(string caption, string text){
|
||||||
|
MessageBoxIcon icon = MessageBoxIcon.None;
|
||||||
|
int pipe = text.IndexOf('|');
|
||||||
|
|
||||||
|
if (pipe != -1){
|
||||||
|
switch(text.Substring(0, pipe)){
|
||||||
|
case "error": icon = MessageBoxIcon.Error; break;
|
||||||
|
case "warning": icon = MessageBoxIcon.Warning; break;
|
||||||
|
case "info": icon = MessageBoxIcon.Information; break;
|
||||||
|
case "question": icon = MessageBoxIcon.Question; break;
|
||||||
|
default: return new FormMessage(caption, text, icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
text = text.Substring(pipe+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new FormMessage(caption, text, icon);
|
||||||
|
}
|
||||||
|
|
||||||
bool IJsDialogHandler.OnJSDialog(IWebBrowser browserControl, IBrowser browser, string originUrl, CefJsDialogType dialogType, string messageText, string defaultPromptText, IJsDialogCallback callback, ref bool suppressMessage){
|
bool IJsDialogHandler.OnJSDialog(IWebBrowser browserControl, IBrowser browser, string originUrl, CefJsDialogType dialogType, string messageText, string defaultPromptText, IJsDialogCallback callback, ref bool suppressMessage){
|
||||||
((ChromiumWebBrowser)browserControl).InvokeSafe(() => {
|
((ChromiumWebBrowser)browserControl).InvokeSafe(() => {
|
||||||
FormMessage form;
|
FormMessage form;
|
||||||
TextBox input = null;
|
TextBox input = null;
|
||||||
|
|
||||||
if (dialogType == CefJsDialogType.Alert){
|
if (dialogType == CefJsDialogType.Alert){
|
||||||
form = new FormMessage("Browser Message", messageText, MessageBoxIcon.None);
|
form = CreateMessageForm("Browser Message", messageText);
|
||||||
form.AddButton(FormMessage.OK, ControlType.Accept | ControlType.Focused);
|
form.AddButton(FormMessage.OK, ControlType.Accept | ControlType.Focused);
|
||||||
}
|
}
|
||||||
else if (dialogType == CefJsDialogType.Confirm){
|
else if (dialogType == CefJsDialogType.Confirm){
|
||||||
form = new FormMessage("Browser Confirmation", messageText, MessageBoxIcon.None);
|
form = CreateMessageForm("Browser Confirmation", messageText);
|
||||||
form.AddButton(FormMessage.No, DialogResult.No, ControlType.Cancel);
|
form.AddButton(FormMessage.No, DialogResult.No, ControlType.Cancel);
|
||||||
form.AddButton(FormMessage.Yes, ControlType.Focused);
|
form.AddButton(FormMessage.Yes, ControlType.Focused);
|
||||||
}
|
}
|
||||||
else if (dialogType == CefJsDialogType.Prompt){
|
else if (dialogType == CefJsDialogType.Prompt){
|
||||||
form = new FormMessage("Browser Prompt", messageText, MessageBoxIcon.None);
|
form = CreateMessageForm("Browser Prompt", messageText);
|
||||||
form.AddButton(FormMessage.Cancel, DialogResult.Cancel, ControlType.Cancel);
|
form.AddButton(FormMessage.Cancel, DialogResult.Cancel, ControlType.Cancel);
|
||||||
form.AddButton(FormMessage.OK, ControlType.Accept | ControlType.Focused);
|
form.AddButton(FormMessage.OK, ControlType.Accept | ControlType.Focused);
|
||||||
|
|
||||||
float dpiScale = form.GetDPIScale();
|
float dpiScale = form.GetDPIScale();
|
||||||
|
int inputPad = form.HasIcon ? 43 : 0;
|
||||||
|
|
||||||
input = new TextBox{
|
input = new TextBox{
|
||||||
Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom,
|
Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom,
|
||||||
Location = new Point(BrowserUtils.Scale(22, dpiScale), form.ActionPanelY-BrowserUtils.Scale(46, dpiScale)),
|
Location = new Point(BrowserUtils.Scale(22+inputPad, dpiScale), form.ActionPanelY-BrowserUtils.Scale(46, dpiScale)),
|
||||||
Size = new Size(form.ClientSize.Width-BrowserUtils.Scale(44, dpiScale), 20)
|
Size = new Size(form.ClientSize.Width-BrowserUtils.Scale(44+inputPad, dpiScale), 20)
|
||||||
};
|
};
|
||||||
|
|
||||||
form.Controls.Add(input);
|
form.Controls.Add(input);
|
||||||
|
@@ -1,8 +1,9 @@
|
|||||||
using CefSharp;
|
using CefSharp;
|
||||||
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Handling.General{
|
namespace TweetDuck.Core.Handling.General{
|
||||||
class LifeSpanHandler : ILifeSpanHandler{
|
sealed class LifeSpanHandler : ILifeSpanHandler{
|
||||||
public bool OnBeforePopup(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser){
|
public bool OnBeforePopup(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser){
|
||||||
newBrowser = null;
|
newBrowser = null;
|
||||||
|
|
||||||
@@ -11,7 +12,7 @@ namespace TweetDuck.Core.Handling.General{
|
|||||||
case WindowOpenDisposition.NewForegroundTab:
|
case WindowOpenDisposition.NewForegroundTab:
|
||||||
case WindowOpenDisposition.NewPopup:
|
case WindowOpenDisposition.NewPopup:
|
||||||
case WindowOpenDisposition.NewWindow:
|
case WindowOpenDisposition.NewWindow:
|
||||||
BrowserUtils.OpenExternalBrowser(targetUrl);
|
browserControl.AsControl().InvokeAsyncSafe(() => BrowserUtils.OpenExternalBrowser(targetUrl));
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@@ -10,11 +10,7 @@ namespace TweetDuck.Core.Handling{
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool IKeyboardHandler.OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut){
|
bool IKeyboardHandler.OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut){
|
||||||
if (type == KeyType.RawKeyDown && (Keys)windowsKeyCode == Keys.Space){
|
return type == KeyType.RawKeyDown && form.ProcessBrowserKey((Keys)windowsKeyCode);
|
||||||
return form.ToggleVideoPause();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IKeyboardHandler.OnKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey){
|
bool IKeyboardHandler.OnKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey){
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
using TweetDuck.Core.Handling.General;
|
using TweetDuck.Core.Handling.General;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Handling{
|
namespace TweetDuck.Core.Handling{
|
||||||
class RequestHandlerBrowser : RequestHandlerBase{
|
sealed class RequestHandlerBrowser : RequestHandlerBase{
|
||||||
public override void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status){
|
public override void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status){
|
||||||
browser.Reload();
|
browser.Reload();
|
||||||
}
|
}
|
||||||
|
@@ -4,7 +4,7 @@ using System.IO;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Handling{
|
namespace TweetDuck.Core.Handling{
|
||||||
class ResourceHandlerNotification : IResourceHandler{
|
sealed class ResourceHandlerNotification : IResourceHandler{
|
||||||
private readonly NameValueCollection headers = new NameValueCollection(0);
|
private readonly NameValueCollection headers = new NameValueCollection(0);
|
||||||
private MemoryStream dataIn;
|
private MemoryStream dataIn;
|
||||||
|
|
||||||
|
@@ -73,25 +73,27 @@ namespace TweetDuck.Core.Notification{
|
|||||||
|
|
||||||
protected double SizeScale => dpiScale*Program.UserConfig.ZoomMultiplier;
|
protected double SizeScale => dpiScale*Program.UserConfig.ZoomMultiplier;
|
||||||
|
|
||||||
protected readonly Form owner;
|
protected readonly FormBrowser owner;
|
||||||
protected readonly ChromiumWebBrowser browser;
|
protected readonly ChromiumWebBrowser browser;
|
||||||
|
|
||||||
private readonly ResourceHandlerNotification resourceHandler = new ResourceHandlerNotification();
|
private readonly ResourceHandlerNotification resourceHandler = new ResourceHandlerNotification();
|
||||||
private readonly float dpiScale;
|
private readonly float dpiScale;
|
||||||
|
|
||||||
private string currentColumn;
|
private TweetNotification currentNotification;
|
||||||
private int pauseCounter;
|
private int pauseCounter;
|
||||||
|
|
||||||
|
public string CurrentTweetUrl => currentNotification?.TweetUrl;
|
||||||
|
public string CurrentQuoteUrl => currentNotification?.QuoteUrl;
|
||||||
|
|
||||||
|
public bool CanViewDetail => currentNotification != null && !string.IsNullOrEmpty(currentNotification.ColumnId) && !string.IsNullOrEmpty(currentNotification.ChirpId);
|
||||||
public bool IsPaused => pauseCounter > 0;
|
public bool IsPaused => pauseCounter > 0;
|
||||||
|
|
||||||
public bool FreezeTimer { get; set; }
|
public bool FreezeTimer { get; set; }
|
||||||
public bool ContextMenuOpen { get; set; }
|
public bool ContextMenuOpen { get; set; }
|
||||||
public string CurrentTweetUrl { get; private set; }
|
|
||||||
public string CurrentQuoteUrl { get; private set; }
|
|
||||||
|
|
||||||
public event EventHandler Initialized;
|
public event EventHandler Initialized;
|
||||||
|
|
||||||
public FormNotificationBase(Form owner, bool enableContextMenu){
|
protected FormNotificationBase(FormBrowser owner, bool enableContextMenu){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
@@ -143,7 +145,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
|
|
||||||
private void Browser_IsBrowserInitializedChanged(object sender, IsBrowserInitializedChangedEventArgs e){
|
private void Browser_IsBrowserInitializedChanged(object sender, IsBrowserInitializedChangedEventArgs e){
|
||||||
if (e.IsBrowserInitialized){
|
if (e.IsBrowserInitialized){
|
||||||
Initialized?.Invoke(this, new EventArgs());
|
Initialized?.Invoke(this, EventArgs.Empty);
|
||||||
|
|
||||||
int identifier = browser.GetBrowser().Identifier;
|
int identifier = browser.GetBrowser().Identifier;
|
||||||
Disposed += (sender2, args2) => BrowserProcesses.Forget(identifier);
|
Disposed += (sender2, args2) => BrowserProcesses.Forget(identifier);
|
||||||
@@ -158,7 +160,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
}
|
}
|
||||||
|
|
||||||
Location = ControlExtensions.InvisibleLocation;
|
Location = ControlExtensions.InvisibleLocation;
|
||||||
currentColumn = null;
|
currentNotification = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void FinishCurrentNotification(){}
|
public virtual void FinishCurrentNotification(){}
|
||||||
@@ -181,10 +183,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void LoadTweet(TweetNotification tweet){
|
protected virtual void LoadTweet(TweetNotification tweet){
|
||||||
CurrentTweetUrl = tweet.TweetUrl;
|
currentNotification = tweet;
|
||||||
CurrentQuoteUrl = tweet.QuoteUrl;
|
|
||||||
currentColumn = tweet.Column;
|
|
||||||
|
|
||||||
resourceHandler.SetHTML(GetTweetHTML(tweet));
|
resourceHandler.SetHTML(GetTweetHTML(tweet));
|
||||||
browser.Load(TwitterUtils.TweetDeckURL);
|
browser.Load(TwitterUtils.TweetDeckURL);
|
||||||
}
|
}
|
||||||
@@ -193,12 +192,13 @@ namespace TweetDuck.Core.Notification{
|
|||||||
browser.ClientSize = ClientSize = new Size(BrowserUtils.Scale(width, SizeScale), BrowserUtils.Scale(height, SizeScale));
|
browser.ClientSize = ClientSize = new Size(BrowserUtils.Scale(width, SizeScale), BrowserUtils.Scale(height, SizeScale));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnNotificationReady(){
|
protected virtual void UpdateTitle(){
|
||||||
MoveToVisibleLocation();
|
string title = currentNotification?.ColumnTitle;
|
||||||
|
Text = string.IsNullOrEmpty(title) || !Program.UserConfig.DisplayNotificationColumn ? Program.BrandName : Program.BrandName+" - "+title;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void UpdateTitle(){
|
public void ShowTweetDetail(){
|
||||||
Text = string.IsNullOrEmpty(currentColumn) || !Program.UserConfig.DisplayNotificationColumn ? Program.BrandName : Program.BrandName+" - "+currentColumn;
|
owner.ShowTweetDetail(currentNotification.ColumnId, currentNotification.ChirpId, currentNotification.TweetUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void MoveToVisibleLocation(){
|
public void MoveToVisibleLocation(){
|
||||||
|
@@ -279,7 +279,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
StartMouseHook();
|
StartMouseHook();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnNotificationReady(){
|
protected virtual void OnNotificationReady(){
|
||||||
PrepareAndDisplayWindow();
|
PrepareAndDisplayWindow();
|
||||||
timerProgress.Start();
|
timerProgress.Start();
|
||||||
}
|
}
|
||||||
|
@@ -14,7 +14,7 @@ namespace TweetDuck.Core.Notification.Screenshot{
|
|||||||
sealed class FormNotificationScreenshotable : FormNotificationBase{
|
sealed class FormNotificationScreenshotable : FormNotificationBase{
|
||||||
private readonly PluginManager plugins;
|
private readonly PluginManager plugins;
|
||||||
|
|
||||||
public FormNotificationScreenshotable(Action callback, Form owner, PluginManager pluginManager) : base(owner, false){
|
public FormNotificationScreenshotable(Action callback, FormBrowser owner, PluginManager pluginManager) : base(owner, false){
|
||||||
this.plugins = pluginManager;
|
this.plugins = pluginManager;
|
||||||
|
|
||||||
browser.RegisterAsyncJsObject("$TD_NotificationScreenshot", new CallbackBridge(this, callback));
|
browser.RegisterAsyncJsObject("$TD_NotificationScreenshot", new CallbackBridge(this, callback));
|
||||||
|
@@ -46,7 +46,7 @@ namespace TweetDuck.Core.Notification.Screenshot{
|
|||||||
CanMoveWindow = () => false
|
CanMoveWindow = () => false
|
||||||
};
|
};
|
||||||
|
|
||||||
screenshot.LoadNotificationForScreenshot(new TweetNotification(string.Empty, html, 0, string.Empty, string.Empty), width, height);
|
screenshot.LoadNotificationForScreenshot(new TweetNotification(string.Empty, string.Empty, string.Empty, html, 0, string.Empty, string.Empty), width, height);
|
||||||
screenshot.Show();
|
screenshot.Show();
|
||||||
timeout.Start();
|
timeout.Start();
|
||||||
|
|
||||||
|
@@ -17,6 +17,10 @@ namespace TweetDuck.Core.Notification{
|
|||||||
player.Play(file);
|
player.Play(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool SetVolume(int volume){
|
||||||
|
return player.SetVolume(volume);
|
||||||
|
}
|
||||||
|
|
||||||
private void Player_PlaybackError(object sender, PlaybackErrorEventArgs e){
|
private void Player_PlaybackError(object sender, PlaybackErrorEventArgs e){
|
||||||
PlaybackError?.Invoke(this, e);
|
PlaybackError?.Invoke(this, e);
|
||||||
}
|
}
|
||||||
|
@@ -35,7 +35,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
return new TweetNotification("Home", ExampleTweetHTML, 95, string.Empty, string.Empty, true);
|
return new TweetNotification(string.Empty, string.Empty, "Home", ExampleTweetHTML, 95, string.Empty, string.Empty, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +55,10 @@ namespace TweetDuck.Core.Notification{
|
|||||||
Auto, Custom
|
Auto, Custom
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Column { get; }
|
public string ColumnId { get; }
|
||||||
|
public string ChirpId { get; }
|
||||||
|
|
||||||
|
public string ColumnTitle { get; }
|
||||||
public string TweetUrl { get; }
|
public string TweetUrl { get; }
|
||||||
public string QuoteUrl { get; }
|
public string QuoteUrl { get; }
|
||||||
|
|
||||||
@@ -63,10 +66,13 @@ namespace TweetDuck.Core.Notification{
|
|||||||
private readonly int characters;
|
private readonly int characters;
|
||||||
private readonly bool isExample;
|
private readonly bool isExample;
|
||||||
|
|
||||||
public TweetNotification(string column, string html, int characters, string tweetUrl, string quoteUrl) : this(column, html, characters, tweetUrl, quoteUrl, false){}
|
public TweetNotification(string columnId, string chirpId, string title, string html, int characters, string tweetUrl, string quoteUrl) : this(columnId, chirpId, title, html, characters, tweetUrl, quoteUrl, false){}
|
||||||
|
|
||||||
private TweetNotification(string column, string html, int characters, string tweetUrl, string quoteUrl, bool isExample){
|
private TweetNotification(string columnId, string chirpId, string title, string html, int characters, string tweetUrl, string quoteUrl, bool isExample){
|
||||||
this.Column = column;
|
this.ColumnId = columnId;
|
||||||
|
this.ChirpId = chirpId;
|
||||||
|
|
||||||
|
this.ColumnTitle = title;
|
||||||
this.TweetUrl = tweetUrl;
|
this.TweetUrl = tweetUrl;
|
||||||
this.QuoteUrl = quoteUrl;
|
this.QuoteUrl = quoteUrl;
|
||||||
|
|
||||||
|
@@ -60,6 +60,7 @@ namespace TweetDuck.Core.Other{
|
|||||||
|
|
||||||
public Button ClickedButton { get; private set; }
|
public Button ClickedButton { get; private set; }
|
||||||
|
|
||||||
|
public bool HasIcon => icon != null;
|
||||||
public int ActionPanelY => panelActions.Location.Y;
|
public int ActionPanelY => panelActions.Location.Y;
|
||||||
|
|
||||||
private int ClientWidth{
|
private int ClientWidth{
|
||||||
|
@@ -19,6 +19,8 @@ namespace TweetDuck.Core.Other{
|
|||||||
private readonly Dictionary<Type, SettingsTab> tabs = new Dictionary<Type, SettingsTab>(4);
|
private readonly Dictionary<Type, SettingsTab> tabs = new Dictionary<Type, SettingsTab>(4);
|
||||||
private SettingsTab currentTab;
|
private SettingsTab currentTab;
|
||||||
|
|
||||||
|
public bool ShouldReloadBrowser { get; private set; }
|
||||||
|
|
||||||
public FormSettings(FormBrowser browser, PluginManager plugins, UpdateHandler updates, Type startTab){
|
public FormSettings(FormBrowser browser, PluginManager plugins, UpdateHandler updates, Type startTab){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
@@ -52,13 +54,19 @@ namespace TweetDuck.Core.Other{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void btnManageOptions_Click(object sender, EventArgs e){
|
private void btnManageOptions_Click(object sender, EventArgs e){
|
||||||
using(DialogSettingsManage dialog = new DialogSettingsManage(plugins)){
|
|
||||||
if (dialog.ShowDialog() == DialogResult.OK && dialog.ShouldReloadUI){
|
|
||||||
foreach(SettingsTab tab in tabs.Values){
|
foreach(SettingsTab tab in tabs.Values){
|
||||||
tab.Control = null;
|
if (tab.IsInitialized){
|
||||||
|
tab.Control.OnClosing();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectTab(currentTab);
|
using(DialogSettingsManage dialog = new DialogSettingsManage(plugins)){
|
||||||
|
if (dialog.ShowDialog() == DialogResult.OK){
|
||||||
|
FormClosing -= FormSettings_FormClosing;
|
||||||
|
browser.ResumeNotification();
|
||||||
|
|
||||||
|
ShouldReloadBrowser = dialog.ShouldReloadBrowser;
|
||||||
|
Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,11 +128,16 @@ namespace TweetDuck.Core.Other{
|
|||||||
tab.Control.OnReady();
|
tab.Control.OnReady();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
panelContents.VerticalScroll.Enabled = false; // required to stop animation that would otherwise break everything
|
||||||
|
panelContents.PerformLayout();
|
||||||
|
|
||||||
panelContents.SuspendLayout();
|
panelContents.SuspendLayout();
|
||||||
panelContents.VerticalScroll.Value = 0; // https://gfycat.com/GrotesqueTastyAstarte
|
panelContents.VerticalScroll.Value = 0; // https://gfycat.com/GrotesqueTastyAstarte
|
||||||
panelContents.Controls.Clear();
|
panelContents.Controls.Clear();
|
||||||
panelContents.Controls.Add(tab.Control);
|
panelContents.Controls.Add(tab.Control);
|
||||||
panelContents.ResumeLayout(true);
|
panelContents.ResumeLayout(true);
|
||||||
|
|
||||||
|
panelContents.VerticalScroll.Enabled = true;
|
||||||
panelContents.Focus();
|
panelContents.Focus();
|
||||||
|
|
||||||
currentTab = tab;
|
currentTab = tab;
|
||||||
@@ -139,7 +152,7 @@ namespace TweetDuck.Core.Other{
|
|||||||
panelContents.Focus();
|
panelContents.Focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SettingsTab{
|
private sealed class SettingsTab{
|
||||||
public Button Button { get; }
|
public Button Button { get; }
|
||||||
|
|
||||||
public BaseTabSettings Control{
|
public BaseTabSettings Control{
|
||||||
|
@@ -68,8 +68,8 @@ namespace TweetDuck.Core.Other.Management{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void TogglePause(){
|
public void SendKeyEvent(Keys key){
|
||||||
currentPipe?.Write("pause");
|
currentPipe?.Write("key", ((int)key).ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void currentPipe_DataIn(object sender, DuplexPipe.PipeReadEventArgs e){
|
private void currentPipe_DataIn(object sender, DuplexPipe.PipeReadEventArgs e){
|
||||||
@@ -83,6 +83,10 @@ namespace TweetDuck.Core.Other.Management{
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "download":
|
||||||
|
TwitterUtils.DownloadVideo(lastUrl);
|
||||||
|
break;
|
||||||
|
|
||||||
case "rip":
|
case "rip":
|
||||||
currentPipe.Dispose();
|
currentPipe.Dispose();
|
||||||
currentPipe = null;
|
currentPipe = null;
|
||||||
@@ -169,7 +173,7 @@ namespace TweetDuck.Core.Other.Management{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void TriggerProcessExitEventUnsafe(){
|
private void TriggerProcessExitEventUnsafe(){
|
||||||
ProcessExited?.Invoke(this, new EventArgs());
|
ProcessExited?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -17,7 +17,7 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public BaseTabSettings(){
|
protected BaseTabSettings(){
|
||||||
Padding = new Padding(6);
|
Padding = new Padding(6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,12 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using TweetDuck.Configuration;
|
||||||
using TweetDuck.Core.Other.Settings.Export;
|
using TweetDuck.Core.Other.Settings.Export;
|
||||||
using TweetDuck.Plugins;
|
using TweetDuck.Plugins;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Other.Settings.Dialogs{
|
namespace TweetDuck.Core.Other.Settings.Dialogs{
|
||||||
sealed partial class DialogSettingsManage : Form{
|
sealed partial class DialogSettingsManage : Form{
|
||||||
private enum State{
|
private enum State{
|
||||||
Deciding, Import, Export
|
Deciding, Reset, Import, Export
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExportFileFlags Flags{
|
public ExportFileFlags Flags{
|
||||||
@@ -20,7 +22,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ShouldReloadUI { get; private set; }
|
public bool ShouldReloadBrowser { get; private set; }
|
||||||
|
|
||||||
private readonly PluginManager plugins;
|
private readonly PluginManager plugins;
|
||||||
private State currentState;
|
private State currentState;
|
||||||
@@ -58,15 +60,10 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
case State.Deciding:
|
case State.Deciding:
|
||||||
// Reset
|
// Reset
|
||||||
if (radioReset.Checked){
|
if (radioReset.Checked){
|
||||||
if (FormMessage.Warning("Reset TweetDuck Options", "This will reset all of your program options. Plugins will not be affected. Do you want to proceed?", FormMessage.Yes, FormMessage.No)){
|
currentState = State.Reset;
|
||||||
Program.ResetConfig();
|
|
||||||
|
|
||||||
ShouldReloadUI = true;
|
Text = "Restore Defaults";
|
||||||
DialogResult = DialogResult.OK;
|
Flags = ExportFileFlags.Config;
|
||||||
Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import
|
// Import
|
||||||
@@ -109,10 +106,43 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
panelExport.Visible = true;
|
panelExport.Visible = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case State.Reset:
|
||||||
|
if (FormMessage.Warning("Reset TweetDuck Options", "This will reset the selected items. Are you sure you want to proceed?", FormMessage.Yes, FormMessage.No)){
|
||||||
|
if (Flags.HasFlag(ExportFileFlags.Config)){
|
||||||
|
Program.ResetConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Flags.HasFlag(ExportFileFlags.PluginData)){
|
||||||
|
try{
|
||||||
|
File.Delete(Program.PluginConfigFilePath);
|
||||||
|
Directory.Delete(Program.PluginDataPath, true);
|
||||||
|
}catch(Exception ex){
|
||||||
|
Program.Reporter.HandleException("Plugin Data Reset Error", "Could not delete plugin data.", true, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Flags.HasFlag(ExportFileFlags.Session)){
|
||||||
|
Program.Restart(Arguments.ArgDeleteCookies);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
ShouldReloadBrowser = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DialogResult = DialogResult.OK;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
case State.Import:
|
case State.Import:
|
||||||
if (importManager.Import(Flags)){
|
if (importManager.Import(Flags)){
|
||||||
if (!importManager.IsRestarting){
|
Program.UserConfig.Reload();
|
||||||
ShouldReloadUI = true;
|
|
||||||
|
if (importManager.IsRestarting){
|
||||||
|
Program.Restart(Arguments.ArgImportCookies);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
ShouldReloadBrowser = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
@@ -165,6 +195,9 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
if (currentState == State.Import){
|
if (currentState == State.Import){
|
||||||
btnContinue.Text = selectedFlags.HasFlag(ExportFileFlags.Session) ? "Import && Restart" : "Import Profile";
|
btnContinue.Text = selectedFlags.HasFlag(ExportFileFlags.Session) ? "Import && Restart" : "Import Profile";
|
||||||
}
|
}
|
||||||
|
else if (currentState == State.Reset){
|
||||||
|
btnContinue.Text = selectedFlags.HasFlag(ExportFileFlags.Session) ? "Restore && Restart" : "Restore Defaults";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -29,20 +29,22 @@
|
|||||||
this.cbLogging = new System.Windows.Forms.CheckBox();
|
this.cbLogging = new System.Windows.Forms.CheckBox();
|
||||||
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
||||||
this.cbDebugUpdates = new System.Windows.Forms.CheckBox();
|
this.cbDebugUpdates = new System.Windows.Forms.CheckBox();
|
||||||
this.labelLocale = new System.Windows.Forms.Label();
|
|
||||||
this.comboLocale = new System.Windows.Forms.ComboBox();
|
this.comboLocale = new System.Windows.Forms.ComboBox();
|
||||||
this.labelDataFolder = new System.Windows.Forms.Label();
|
|
||||||
this.tbDataFolder = new System.Windows.Forms.TextBox();
|
this.tbDataFolder = new System.Windows.Forms.TextBox();
|
||||||
|
this.tbShortcutTarget = new System.Windows.Forms.TextBox();
|
||||||
|
this.labelLocale = new System.Windows.Forms.Label();
|
||||||
|
this.labelDataFolder = new System.Windows.Forms.Label();
|
||||||
|
this.labelShortcutTarget = new System.Windows.Forms.Label();
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
//
|
//
|
||||||
// btnCancel
|
// btnCancel
|
||||||
//
|
//
|
||||||
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
this.btnCancel.Location = new System.Drawing.Point(160, 171);
|
this.btnCancel.Location = new System.Drawing.Point(216, 217);
|
||||||
this.btnCancel.Name = "btnCancel";
|
this.btnCancel.Name = "btnCancel";
|
||||||
this.btnCancel.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
this.btnCancel.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||||
this.btnCancel.Size = new System.Drawing.Size(56, 23);
|
this.btnCancel.Size = new System.Drawing.Size(56, 23);
|
||||||
this.btnCancel.TabIndex = 7;
|
this.btnCancel.TabIndex = 9;
|
||||||
this.btnCancel.Text = "Cancel";
|
this.btnCancel.Text = "Cancel";
|
||||||
this.btnCancel.UseVisualStyleBackColor = true;
|
this.btnCancel.UseVisualStyleBackColor = true;
|
||||||
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
|
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
|
||||||
@@ -50,11 +52,11 @@
|
|||||||
// btnRestart
|
// btnRestart
|
||||||
//
|
//
|
||||||
this.btnRestart.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
this.btnRestart.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
this.btnRestart.Location = new System.Drawing.Point(97, 171);
|
this.btnRestart.Location = new System.Drawing.Point(153, 217);
|
||||||
this.btnRestart.Name = "btnRestart";
|
this.btnRestart.Name = "btnRestart";
|
||||||
this.btnRestart.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
this.btnRestart.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||||
this.btnRestart.Size = new System.Drawing.Size(57, 23);
|
this.btnRestart.Size = new System.Drawing.Size(57, 23);
|
||||||
this.btnRestart.TabIndex = 6;
|
this.btnRestart.TabIndex = 8;
|
||||||
this.btnRestart.Text = "Restart";
|
this.btnRestart.Text = "Restart";
|
||||||
this.btnRestart.UseVisualStyleBackColor = true;
|
this.btnRestart.UseVisualStyleBackColor = true;
|
||||||
this.btnRestart.Click += new System.EventHandler(this.btnRestart_Click);
|
this.btnRestart.Click += new System.EventHandler(this.btnRestart_Click);
|
||||||
@@ -67,7 +69,7 @@
|
|||||||
this.cbLogging.Size = new System.Drawing.Size(64, 17);
|
this.cbLogging.Size = new System.Drawing.Size(64, 17);
|
||||||
this.cbLogging.TabIndex = 0;
|
this.cbLogging.TabIndex = 0;
|
||||||
this.cbLogging.Text = "Logging";
|
this.cbLogging.Text = "Logging";
|
||||||
this.toolTip.SetToolTip(this.cbLogging, "Logging JavaScript output into a\r\ndebug.txt file in the data folder.");
|
this.toolTip.SetToolTip(this.cbLogging, "Logging JavaScript output into TD_Console.txt file in the data folder.");
|
||||||
this.cbLogging.UseVisualStyleBackColor = true;
|
this.cbLogging.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
// cbDebugUpdates
|
// cbDebugUpdates
|
||||||
@@ -81,6 +83,40 @@
|
|||||||
this.toolTip.SetToolTip(this.cbDebugUpdates, "Allows updating to pre-releases.");
|
this.toolTip.SetToolTip(this.cbDebugUpdates, "Allows updating to pre-releases.");
|
||||||
this.cbDebugUpdates.UseVisualStyleBackColor = true;
|
this.cbDebugUpdates.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
|
// comboLocale
|
||||||
|
//
|
||||||
|
this.comboLocale.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.comboLocale.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||||
|
this.comboLocale.FormattingEnabled = true;
|
||||||
|
this.comboLocale.Location = new System.Drawing.Point(15, 83);
|
||||||
|
this.comboLocale.Name = "comboLocale";
|
||||||
|
this.comboLocale.Size = new System.Drawing.Size(257, 21);
|
||||||
|
this.comboLocale.TabIndex = 3;
|
||||||
|
this.toolTip.SetToolTip(this.comboLocale, "Language used for spell checking.");
|
||||||
|
//
|
||||||
|
// tbDataFolder
|
||||||
|
//
|
||||||
|
this.tbDataFolder.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.tbDataFolder.Location = new System.Drawing.Point(15, 135);
|
||||||
|
this.tbDataFolder.Name = "tbDataFolder";
|
||||||
|
this.tbDataFolder.Size = new System.Drawing.Size(257, 20);
|
||||||
|
this.tbDataFolder.TabIndex = 5;
|
||||||
|
this.toolTip.SetToolTip(this.tbDataFolder, "Path to the data folder. Must be either an absolute path,\r\nor a simple folder name that will be created in LocalAppData.");
|
||||||
|
//
|
||||||
|
// tbShortcutTarget
|
||||||
|
//
|
||||||
|
this.tbShortcutTarget.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.tbShortcutTarget.Cursor = System.Windows.Forms.Cursors.Hand;
|
||||||
|
this.tbShortcutTarget.Location = new System.Drawing.Point(15, 186);
|
||||||
|
this.tbShortcutTarget.Name = "tbShortcutTarget";
|
||||||
|
this.tbShortcutTarget.ReadOnly = true;
|
||||||
|
this.tbShortcutTarget.Size = new System.Drawing.Size(257, 20);
|
||||||
|
this.tbShortcutTarget.TabIndex = 7;
|
||||||
|
this.tbShortcutTarget.Click += new System.EventHandler(this.tbShortcutTarget_Click);
|
||||||
|
//
|
||||||
// labelLocale
|
// labelLocale
|
||||||
//
|
//
|
||||||
this.labelLocale.AutoSize = true;
|
this.labelLocale.AutoSize = true;
|
||||||
@@ -91,16 +127,6 @@
|
|||||||
this.labelLocale.TabIndex = 2;
|
this.labelLocale.TabIndex = 2;
|
||||||
this.labelLocale.Text = "Locale";
|
this.labelLocale.Text = "Locale";
|
||||||
//
|
//
|
||||||
// comboLocale
|
|
||||||
//
|
|
||||||
this.comboLocale.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
|
||||||
| System.Windows.Forms.AnchorStyles.Right)));
|
|
||||||
this.comboLocale.FormattingEnabled = true;
|
|
||||||
this.comboLocale.Location = new System.Drawing.Point(15, 83);
|
|
||||||
this.comboLocale.Name = "comboLocale";
|
|
||||||
this.comboLocale.Size = new System.Drawing.Size(201, 21);
|
|
||||||
this.comboLocale.TabIndex = 3;
|
|
||||||
//
|
|
||||||
// labelDataFolder
|
// labelDataFolder
|
||||||
//
|
//
|
||||||
this.labelDataFolder.AutoSize = true;
|
this.labelDataFolder.AutoSize = true;
|
||||||
@@ -111,20 +137,23 @@
|
|||||||
this.labelDataFolder.TabIndex = 4;
|
this.labelDataFolder.TabIndex = 4;
|
||||||
this.labelDataFolder.Text = "Data Folder";
|
this.labelDataFolder.Text = "Data Folder";
|
||||||
//
|
//
|
||||||
// tbDataFolder
|
// labelShortcutTarget
|
||||||
//
|
//
|
||||||
this.tbDataFolder.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
this.labelShortcutTarget.AutoSize = true;
|
||||||
| System.Windows.Forms.AnchorStyles.Right)));
|
this.labelShortcutTarget.Location = new System.Drawing.Point(12, 170);
|
||||||
this.tbDataFolder.Location = new System.Drawing.Point(15, 135);
|
this.labelShortcutTarget.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
|
||||||
this.tbDataFolder.Name = "tbDataFolder";
|
this.labelShortcutTarget.Name = "labelShortcutTarget";
|
||||||
this.tbDataFolder.Size = new System.Drawing.Size(201, 20);
|
this.labelShortcutTarget.Size = new System.Drawing.Size(155, 13);
|
||||||
this.tbDataFolder.TabIndex = 5;
|
this.labelShortcutTarget.TabIndex = 6;
|
||||||
|
this.labelShortcutTarget.Text = "Shortcut Target (click to select)";
|
||||||
//
|
//
|
||||||
// DialogSettingsRestart
|
// DialogSettingsRestart
|
||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
this.ClientSize = new System.Drawing.Size(228, 206);
|
this.ClientSize = new System.Drawing.Size(284, 252);
|
||||||
|
this.Controls.Add(this.tbShortcutTarget);
|
||||||
|
this.Controls.Add(this.labelShortcutTarget);
|
||||||
this.Controls.Add(this.tbDataFolder);
|
this.Controls.Add(this.tbDataFolder);
|
||||||
this.Controls.Add(this.labelDataFolder);
|
this.Controls.Add(this.labelDataFolder);
|
||||||
this.Controls.Add(this.comboLocale);
|
this.Controls.Add(this.comboLocale);
|
||||||
@@ -155,5 +184,7 @@
|
|||||||
private System.Windows.Forms.ComboBox comboLocale;
|
private System.Windows.Forms.ComboBox comboLocale;
|
||||||
private System.Windows.Forms.Label labelDataFolder;
|
private System.Windows.Forms.Label labelDataFolder;
|
||||||
private System.Windows.Forms.TextBox tbDataFolder;
|
private System.Windows.Forms.TextBox tbDataFolder;
|
||||||
|
private System.Windows.Forms.TextBox tbShortcutTarget;
|
||||||
|
private System.Windows.Forms.Label labelShortcutTarget;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -24,12 +24,26 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
cbLogging.Checked = currentArgs.HasFlag(Arguments.ArgLogging);
|
cbLogging.Checked = currentArgs.HasFlag(Arguments.ArgLogging);
|
||||||
cbDebugUpdates.Checked = currentArgs.HasFlag(Arguments.ArgDebugUpdates);
|
cbDebugUpdates.Checked = currentArgs.HasFlag(Arguments.ArgDebugUpdates);
|
||||||
comboLocale.SelectedItem = currentArgs.GetValue(Arguments.ArgLocale, DefaultLocale);
|
comboLocale.SelectedItem = currentArgs.GetValue(Arguments.ArgLocale, DefaultLocale);
|
||||||
|
|
||||||
|
cbLogging.CheckedChanged += control_Change;
|
||||||
|
cbDebugUpdates.CheckedChanged += control_Change;
|
||||||
|
comboLocale.SelectedValueChanged += control_Change;
|
||||||
|
|
||||||
|
if (Program.IsPortable){
|
||||||
|
tbDataFolder.Text = "Not available in portable version";
|
||||||
|
tbDataFolder.Enabled = false;
|
||||||
|
}
|
||||||
|
else{
|
||||||
tbDataFolder.Text = currentArgs.GetValue(Arguments.ArgDataFolder, string.Empty);
|
tbDataFolder.Text = currentArgs.GetValue(Arguments.ArgDataFolder, string.Empty);
|
||||||
|
tbDataFolder.TextChanged += control_Change;
|
||||||
|
}
|
||||||
|
|
||||||
|
control_Change(this, EventArgs.Empty);
|
||||||
|
|
||||||
Text = Program.BrandName+" Arguments";
|
Text = Program.BrandName+" Arguments";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void btnRestart_Click(object sender, EventArgs e){
|
private void control_Change(object sender, EventArgs e){
|
||||||
Args = new CommandLineArgs();
|
Args = new CommandLineArgs();
|
||||||
|
|
||||||
if (cbLogging.Checked){
|
if (cbLogging.Checked){
|
||||||
@@ -46,10 +60,21 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
Args.SetValue(Arguments.ArgLocale, locale);
|
Args.SetValue(Arguments.ArgLocale, locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(tbDataFolder.Text)){
|
if (!string.IsNullOrWhiteSpace(tbDataFolder.Text) && tbDataFolder.Enabled){
|
||||||
Args.SetValue(Arguments.ArgDataFolder, tbDataFolder.Text);
|
Args.SetValue(Arguments.ArgDataFolder, tbDataFolder.Text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tbShortcutTarget.Text = $@"""{Application.ExecutablePath}""{(Args.Count > 0 ? " " : "")}{Args}";
|
||||||
|
tbShortcutTarget.Select(tbShortcutTarget.Text.Length, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tbShortcutTarget_Click(object sender, EventArgs e){
|
||||||
|
if (tbShortcutTarget.SelectionLength == 0){
|
||||||
|
tbShortcutTarget.SelectAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btnRestart_Click(object sender, EventArgs e){
|
||||||
DialogResult = DialogResult.OK;
|
DialogResult = DialogResult.OK;
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
|
@@ -2,7 +2,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using TweetDuck.Configuration;
|
|
||||||
using TweetDuck.Data;
|
using TweetDuck.Data;
|
||||||
using TweetDuck.Plugins;
|
using TweetDuck.Plugins;
|
||||||
using TweetDuck.Plugins.Enums;
|
using TweetDuck.Plugins.Enums;
|
||||||
@@ -141,13 +140,6 @@ namespace TweetDuck.Core.Other.Settings.Export{
|
|||||||
FormMessage.Information("Importing TweetDuck Profile", "Detected missing plugins when importing plugin data:\n"+string.Join("\n", missingPlugins), FormMessage.OK);
|
FormMessage.Information("Importing TweetDuck Profile", "Detected missing plugins when importing plugin data:\n"+string.Join("\n", missingPlugins), FormMessage.OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsRestarting){
|
|
||||||
Program.Restart(Arguments.ArgImportCookies);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
Program.ReloadConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
LastException = e;
|
LastException = e;
|
||||||
@@ -169,6 +161,16 @@ namespace TweetDuck.Core.Other.Settings.Export{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void DeleteCookies(){
|
||||||
|
try{
|
||||||
|
if (File.Exists(CookiesPath)){
|
||||||
|
File.Delete(CookiesPath);
|
||||||
|
}
|
||||||
|
}catch(Exception e){
|
||||||
|
Program.Reporter.HandleException("Session Reset Error", "Could not remove the cookie file to reset the login session.", true, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static IEnumerable<PathInfo> EnumerateFilesRelative(string root){
|
private static IEnumerable<PathInfo> EnumerateFilesRelative(string root){
|
||||||
return Directory.Exists(root) ? Directory.EnumerateFiles(root, "*.*", SearchOption.AllDirectories).Select(fullPath => new PathInfo{
|
return Directory.Exists(root) ? Directory.EnumerateFiles(root, "*.*", SearchOption.AllDirectories).Select(fullPath => new PathInfo{
|
||||||
Full = fullPath,
|
Full = fullPath,
|
||||||
@@ -176,7 +178,7 @@ namespace TweetDuck.Core.Other.Settings.Export{
|
|||||||
}) : Enumerable.Empty<PathInfo>();
|
}) : Enumerable.Empty<PathInfo>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class PathInfo{
|
private sealed class PathInfo{
|
||||||
public string Full { get; set; }
|
public string Full { get; set; }
|
||||||
public string Relative { get; set; }
|
public string Relative { get; set; }
|
||||||
}
|
}
|
||||||
|
@@ -7,7 +7,7 @@ using TweetDuck.Core.Other.Settings.Dialogs;
|
|||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Other.Settings{
|
namespace TweetDuck.Core.Other.Settings{
|
||||||
partial class TabSettingsAdvanced : BaseTabSettings{
|
sealed partial class TabSettingsAdvanced : BaseTabSettings{
|
||||||
private static SystemConfig SysConfig => Program.SystemConfig;
|
private static SystemConfig SysConfig => Program.SystemConfig;
|
||||||
|
|
||||||
private readonly Action<string> reinjectBrowserCSS;
|
private readonly Action<string> reinjectBrowserCSS;
|
||||||
|
@@ -123,7 +123,7 @@
|
|||||||
// labelZoomValue
|
// labelZoomValue
|
||||||
//
|
//
|
||||||
this.labelZoomValue.BackColor = System.Drawing.Color.Transparent;
|
this.labelZoomValue.BackColor = System.Drawing.Color.Transparent;
|
||||||
this.labelZoomValue.Location = new System.Drawing.Point(141, 123);
|
this.labelZoomValue.Location = new System.Drawing.Point(147, 123);
|
||||||
this.labelZoomValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
|
this.labelZoomValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
|
||||||
this.labelZoomValue.Name = "labelZoomValue";
|
this.labelZoomValue.Name = "labelZoomValue";
|
||||||
this.labelZoomValue.Size = new System.Drawing.Size(38, 13);
|
this.labelZoomValue.Size = new System.Drawing.Size(38, 13);
|
||||||
@@ -204,8 +204,8 @@
|
|||||||
this.panelUI.Controls.Add(this.checkSwitchAccountSelectors);
|
this.panelUI.Controls.Add(this.checkSwitchAccountSelectors);
|
||||||
this.panelUI.Controls.Add(this.checkSpellCheck);
|
this.panelUI.Controls.Add(this.checkSpellCheck);
|
||||||
this.panelUI.Controls.Add(this.labelZoom);
|
this.panelUI.Controls.Add(this.labelZoom);
|
||||||
this.panelUI.Controls.Add(this.labelZoomValue);
|
|
||||||
this.panelUI.Controls.Add(this.trackBarZoom);
|
this.panelUI.Controls.Add(this.trackBarZoom);
|
||||||
|
this.panelUI.Controls.Add(this.labelZoomValue);
|
||||||
this.panelUI.Location = new System.Drawing.Point(9, 31);
|
this.panelUI.Location = new System.Drawing.Point(9, 31);
|
||||||
this.panelUI.Name = "panelUI";
|
this.panelUI.Name = "panelUI";
|
||||||
this.panelUI.Size = new System.Drawing.Size(322, 157);
|
this.panelUI.Size = new System.Drawing.Size(322, 157);
|
||||||
|
@@ -1,10 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Updates;
|
using TweetDuck.Updates;
|
||||||
using TweetDuck.Updates.Events;
|
|
||||||
|
|
||||||
namespace TweetDuck.Core.Other.Settings{
|
namespace TweetDuck.Core.Other.Settings{
|
||||||
partial class TabSettingsGeneral : BaseTabSettings{
|
sealed partial class TabSettingsGeneral : BaseTabSettings{
|
||||||
private readonly UpdateHandler updates;
|
private readonly UpdateHandler updates;
|
||||||
private int updateCheckEventId = -1;
|
private int updateCheckEventId = -1;
|
||||||
|
|
||||||
@@ -30,6 +29,8 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
checkSwitchAccountSelectors.Checked = Config.SwitchAccountSelectors;
|
checkSwitchAccountSelectors.Checked = Config.SwitchAccountSelectors;
|
||||||
checkBestImageQuality.Checked = Config.BestImageQuality;
|
checkBestImageQuality.Checked = Config.BestImageQuality;
|
||||||
checkSpellCheck.Checked = Config.EnableSpellCheck;
|
checkSpellCheck.Checked = Config.EnableSpellCheck;
|
||||||
|
|
||||||
|
checkTrayHighlight.Enabled = Config.TrayBehavior.ShouldDisplayIcon();
|
||||||
checkTrayHighlight.Checked = Config.EnableTrayHighlight;
|
checkTrayHighlight.Checked = Config.EnableTrayHighlight;
|
||||||
|
|
||||||
checkUpdateNotifications.Checked = Config.EnableUpdateCheck;
|
checkUpdateNotifications.Checked = Config.EnableUpdateCheck;
|
||||||
@@ -80,6 +81,7 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
|
|
||||||
private void comboBoxTrayType_SelectedIndexChanged(object sender, EventArgs e){
|
private void comboBoxTrayType_SelectedIndexChanged(object sender, EventArgs e){
|
||||||
Config.TrayBehavior = (TrayIcon.Behavior)comboBoxTrayType.SelectedIndex;
|
Config.TrayBehavior = (TrayIcon.Behavior)comboBoxTrayType.SelectedIndex;
|
||||||
|
checkTrayHighlight.Enabled = Config.TrayBehavior.ShouldDisplayIcon();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkTrayHighlight_CheckedChanged(object sender, EventArgs e){
|
private void checkTrayHighlight_CheckedChanged(object sender, EventArgs e){
|
||||||
@@ -91,23 +93,18 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void btnCheckUpdates_Click(object sender, EventArgs e){
|
private void btnCheckUpdates_Click(object sender, EventArgs e){
|
||||||
updateCheckEventId = updates.Check(true);
|
Config.DismissedUpdate = null;
|
||||||
|
|
||||||
if (updateCheckEventId == -1){
|
|
||||||
FormMessage.Warning("Unsupported System", "Sorry, your system is no longer supported.", FormMessage.OK);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
btnCheckUpdates.Enabled = false;
|
btnCheckUpdates.Enabled = false;
|
||||||
updates.DismissUpdate(string.Empty);
|
updateCheckEventId = updates.Check(true);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updates_CheckFinished(object sender, UpdateCheckEventArgs e){
|
private void updates_CheckFinished(object sender, UpdateEventArgs e){
|
||||||
this.InvokeAsyncSafe(() => {
|
this.InvokeAsyncSafe(() => {
|
||||||
if (e.EventId == updateCheckEventId){
|
if (e.EventId == updateCheckEventId){
|
||||||
btnCheckUpdates.Enabled = true;
|
btnCheckUpdates.Enabled = true;
|
||||||
|
|
||||||
if (!e.UpdateAvailable){
|
if (!e.IsUpdateAvailable){
|
||||||
FormMessage.Information("No Updates Available", "Your version of TweetDuck is up to date.", FormMessage.OK);
|
FormMessage.Information("No Updates Available", "Your version of TweetDuck is up to date.", FormMessage.OK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -79,7 +79,7 @@
|
|||||||
this.labelEdgeDistanceValue.Location = new System.Drawing.Point(147, 129);
|
this.labelEdgeDistanceValue.Location = new System.Drawing.Point(147, 129);
|
||||||
this.labelEdgeDistanceValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
|
this.labelEdgeDistanceValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
|
||||||
this.labelEdgeDistanceValue.Name = "labelEdgeDistanceValue";
|
this.labelEdgeDistanceValue.Name = "labelEdgeDistanceValue";
|
||||||
this.labelEdgeDistanceValue.Size = new System.Drawing.Size(34, 13);
|
this.labelEdgeDistanceValue.Size = new System.Drawing.Size(40, 13);
|
||||||
this.labelEdgeDistanceValue.TabIndex = 9;
|
this.labelEdgeDistanceValue.TabIndex = 9;
|
||||||
this.labelEdgeDistanceValue.Text = "0 px";
|
this.labelEdgeDistanceValue.Text = "0 px";
|
||||||
this.labelEdgeDistanceValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
|
this.labelEdgeDistanceValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
|
||||||
@@ -253,7 +253,7 @@
|
|||||||
this.labelDurationValue.Location = new System.Drawing.Point(147, 77);
|
this.labelDurationValue.Location = new System.Drawing.Point(147, 77);
|
||||||
this.labelDurationValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
|
this.labelDurationValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
|
||||||
this.labelDurationValue.Name = "labelDurationValue";
|
this.labelDurationValue.Name = "labelDurationValue";
|
||||||
this.labelDurationValue.Size = new System.Drawing.Size(48, 13);
|
this.labelDurationValue.Size = new System.Drawing.Size(52, 13);
|
||||||
this.labelDurationValue.TabIndex = 4;
|
this.labelDurationValue.TabIndex = 4;
|
||||||
this.labelDurationValue.Text = "0 ms/c";
|
this.labelDurationValue.Text = "0 ms/c";
|
||||||
this.labelDurationValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
|
this.labelDurationValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
|
||||||
@@ -406,7 +406,7 @@
|
|||||||
this.labelScrollSpeedValue.Location = new System.Drawing.Point(147, 53);
|
this.labelScrollSpeedValue.Location = new System.Drawing.Point(147, 53);
|
||||||
this.labelScrollSpeedValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
|
this.labelScrollSpeedValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
|
||||||
this.labelScrollSpeedValue.Name = "labelScrollSpeedValue";
|
this.labelScrollSpeedValue.Name = "labelScrollSpeedValue";
|
||||||
this.labelScrollSpeedValue.Size = new System.Drawing.Size(34, 13);
|
this.labelScrollSpeedValue.Size = new System.Drawing.Size(38, 13);
|
||||||
this.labelScrollSpeedValue.TabIndex = 4;
|
this.labelScrollSpeedValue.TabIndex = 4;
|
||||||
this.labelScrollSpeedValue.Text = "100%";
|
this.labelScrollSpeedValue.Text = "100%";
|
||||||
this.labelScrollSpeedValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
|
this.labelScrollSpeedValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
|
||||||
@@ -450,7 +450,6 @@
|
|||||||
//
|
//
|
||||||
this.panelLocation.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
this.panelLocation.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||||
| System.Windows.Forms.AnchorStyles.Right)));
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
this.panelLocation.Controls.Add(this.labelEdgeDistanceValue);
|
|
||||||
this.panelLocation.Controls.Add(this.radioLocTL);
|
this.panelLocation.Controls.Add(this.radioLocTL);
|
||||||
this.panelLocation.Controls.Add(this.labelDisplay);
|
this.panelLocation.Controls.Add(this.labelDisplay);
|
||||||
this.panelLocation.Controls.Add(this.trackBarEdgeDistance);
|
this.panelLocation.Controls.Add(this.trackBarEdgeDistance);
|
||||||
@@ -460,6 +459,7 @@
|
|||||||
this.panelLocation.Controls.Add(this.radioLocBL);
|
this.panelLocation.Controls.Add(this.radioLocBL);
|
||||||
this.panelLocation.Controls.Add(this.radioLocCustom);
|
this.panelLocation.Controls.Add(this.radioLocCustom);
|
||||||
this.panelLocation.Controls.Add(this.radioLocBR);
|
this.panelLocation.Controls.Add(this.radioLocBR);
|
||||||
|
this.panelLocation.Controls.Add(this.labelEdgeDistanceValue);
|
||||||
this.panelLocation.Location = new System.Drawing.Point(9, 418);
|
this.panelLocation.Location = new System.Drawing.Point(9, 418);
|
||||||
this.panelLocation.Name = "panelLocation";
|
this.panelLocation.Name = "panelLocation";
|
||||||
this.panelLocation.Size = new System.Drawing.Size(322, 165);
|
this.panelLocation.Size = new System.Drawing.Size(322, 165);
|
||||||
|
@@ -4,7 +4,7 @@ using TweetDuck.Core.Controls;
|
|||||||
using TweetDuck.Core.Notification;
|
using TweetDuck.Core.Notification;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Other.Settings{
|
namespace TweetDuck.Core.Other.Settings{
|
||||||
partial class TabSettingsNotifications : BaseTabSettings{
|
sealed partial class TabSettingsNotifications : BaseTabSettings{
|
||||||
private static readonly int[] IdlePauseSeconds = { 0, 30, 60, 120, 300 };
|
private static readonly int[] IdlePauseSeconds = { 0, 30, 60, 120, 300 };
|
||||||
|
|
||||||
private readonly FormNotificationMain notification;
|
private readonly FormNotificationMain notification;
|
||||||
|
72
Core/Other/Settings/TabSettingsSounds.Designer.cs
generated
72
Core/Other/Settings/TabSettingsSounds.Designer.cs
generated
@@ -25,15 +25,40 @@
|
|||||||
private void InitializeComponent() {
|
private void InitializeComponent() {
|
||||||
this.components = new System.ComponentModel.Container();
|
this.components = new System.ComponentModel.Container();
|
||||||
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
||||||
|
this.tbCustomSound = new System.Windows.Forms.TextBox();
|
||||||
|
this.labelVolumeValue = new System.Windows.Forms.Label();
|
||||||
this.btnPlaySound = new System.Windows.Forms.Button();
|
this.btnPlaySound = new System.Windows.Forms.Button();
|
||||||
this.btnResetSound = new System.Windows.Forms.Button();
|
this.btnResetSound = new System.Windows.Forms.Button();
|
||||||
this.btnBrowseSound = new System.Windows.Forms.Button();
|
this.btnBrowseSound = new System.Windows.Forms.Button();
|
||||||
this.tbCustomSound = new System.Windows.Forms.TextBox();
|
|
||||||
this.labelSoundNotification = new System.Windows.Forms.Label();
|
this.labelSoundNotification = new System.Windows.Forms.Label();
|
||||||
this.panelSoundNotification = new System.Windows.Forms.Panel();
|
this.panelSoundNotification = new System.Windows.Forms.Panel();
|
||||||
|
this.labelVolume = new System.Windows.Forms.Label();
|
||||||
|
this.trackBarVolume = new System.Windows.Forms.TrackBar();
|
||||||
this.panelSoundNotification.SuspendLayout();
|
this.panelSoundNotification.SuspendLayout();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.trackBarVolume)).BeginInit();
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
//
|
//
|
||||||
|
// tbCustomSound
|
||||||
|
//
|
||||||
|
this.tbCustomSound.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.tbCustomSound.Location = new System.Drawing.Point(3, 3);
|
||||||
|
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.");
|
||||||
|
//
|
||||||
|
// labelVolumeValue
|
||||||
|
//
|
||||||
|
this.labelVolumeValue.BackColor = System.Drawing.Color.Transparent;
|
||||||
|
this.labelVolumeValue.Location = new System.Drawing.Point(147, 84);
|
||||||
|
this.labelVolumeValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
|
||||||
|
this.labelVolumeValue.Name = "labelVolumeValue";
|
||||||
|
this.labelVolumeValue.Size = new System.Drawing.Size(38, 13);
|
||||||
|
this.labelVolumeValue.TabIndex = 6;
|
||||||
|
this.labelVolumeValue.Text = "100%";
|
||||||
|
this.labelVolumeValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
|
||||||
|
//
|
||||||
// btnPlaySound
|
// btnPlaySound
|
||||||
//
|
//
|
||||||
this.btnPlaySound.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
this.btnPlaySound.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
@@ -69,16 +94,6 @@
|
|||||||
this.btnBrowseSound.Text = "Browse...";
|
this.btnBrowseSound.Text = "Browse...";
|
||||||
this.btnBrowseSound.UseVisualStyleBackColor = true;
|
this.btnBrowseSound.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
// tbCustomSound
|
|
||||||
//
|
|
||||||
this.tbCustomSound.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
|
||||||
| System.Windows.Forms.AnchorStyles.Right)));
|
|
||||||
this.tbCustomSound.Location = new System.Drawing.Point(3, 3);
|
|
||||||
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
|
// labelSoundNotification
|
||||||
//
|
//
|
||||||
this.labelSoundNotification.AutoSize = true;
|
this.labelSoundNotification.AutoSize = true;
|
||||||
@@ -94,15 +109,42 @@
|
|||||||
//
|
//
|
||||||
this.panelSoundNotification.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
this.panelSoundNotification.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||||
| System.Windows.Forms.AnchorStyles.Right)));
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.panelSoundNotification.Controls.Add(this.labelVolume);
|
||||||
|
this.panelSoundNotification.Controls.Add(this.trackBarVolume);
|
||||||
this.panelSoundNotification.Controls.Add(this.btnPlaySound);
|
this.panelSoundNotification.Controls.Add(this.btnPlaySound);
|
||||||
this.panelSoundNotification.Controls.Add(this.tbCustomSound);
|
this.panelSoundNotification.Controls.Add(this.tbCustomSound);
|
||||||
this.panelSoundNotification.Controls.Add(this.btnResetSound);
|
this.panelSoundNotification.Controls.Add(this.btnResetSound);
|
||||||
this.panelSoundNotification.Controls.Add(this.btnBrowseSound);
|
this.panelSoundNotification.Controls.Add(this.btnBrowseSound);
|
||||||
|
this.panelSoundNotification.Controls.Add(this.labelVolumeValue);
|
||||||
this.panelSoundNotification.Location = new System.Drawing.Point(9, 31);
|
this.panelSoundNotification.Location = new System.Drawing.Point(9, 31);
|
||||||
this.panelSoundNotification.Name = "panelSoundNotification";
|
this.panelSoundNotification.Name = "panelSoundNotification";
|
||||||
this.panelSoundNotification.Size = new System.Drawing.Size(322, 56);
|
this.panelSoundNotification.Size = new System.Drawing.Size(322, 119);
|
||||||
this.panelSoundNotification.TabIndex = 2;
|
this.panelSoundNotification.TabIndex = 2;
|
||||||
//
|
//
|
||||||
|
// labelVolume
|
||||||
|
//
|
||||||
|
this.labelVolume.AutoSize = true;
|
||||||
|
this.labelVolume.Location = new System.Drawing.Point(3, 67);
|
||||||
|
this.labelVolume.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
|
||||||
|
this.labelVolume.Name = "labelVolume";
|
||||||
|
this.labelVolume.Size = new System.Drawing.Size(42, 13);
|
||||||
|
this.labelVolume.TabIndex = 4;
|
||||||
|
this.labelVolume.Text = "Volume";
|
||||||
|
//
|
||||||
|
// trackBarVolume
|
||||||
|
//
|
||||||
|
this.trackBarVolume.AutoSize = false;
|
||||||
|
this.trackBarVolume.BackColor = System.Drawing.SystemColors.Control;
|
||||||
|
this.trackBarVolume.Location = new System.Drawing.Point(3, 83);
|
||||||
|
this.trackBarVolume.Maximum = 100;
|
||||||
|
this.trackBarVolume.Name = "trackBarVolume";
|
||||||
|
this.trackBarVolume.Size = new System.Drawing.Size(148, 30);
|
||||||
|
this.trackBarVolume.SmallChange = 1;
|
||||||
|
this.trackBarVolume.TabIndex = 5;
|
||||||
|
this.trackBarVolume.TickFrequency = 10;
|
||||||
|
this.trackBarVolume.Value = 100;
|
||||||
|
this.trackBarVolume.ValueChanged += new System.EventHandler(this.trackBarVolume_ValueChanged);
|
||||||
|
//
|
||||||
// TabSettingsSounds
|
// TabSettingsSounds
|
||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
@@ -110,9 +152,10 @@
|
|||||||
this.Controls.Add(this.panelSoundNotification);
|
this.Controls.Add(this.panelSoundNotification);
|
||||||
this.Controls.Add(this.labelSoundNotification);
|
this.Controls.Add(this.labelSoundNotification);
|
||||||
this.Name = "TabSettingsSounds";
|
this.Name = "TabSettingsSounds";
|
||||||
this.Size = new System.Drawing.Size(340, 97);
|
this.Size = new System.Drawing.Size(340, 160);
|
||||||
this.panelSoundNotification.ResumeLayout(false);
|
this.panelSoundNotification.ResumeLayout(false);
|
||||||
this.panelSoundNotification.PerformLayout();
|
this.panelSoundNotification.PerformLayout();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.trackBarVolume)).EndInit();
|
||||||
this.ResumeLayout(false);
|
this.ResumeLayout(false);
|
||||||
this.PerformLayout();
|
this.PerformLayout();
|
||||||
|
|
||||||
@@ -127,5 +170,8 @@
|
|||||||
private System.Windows.Forms.Button btnPlaySound;
|
private System.Windows.Forms.Button btnPlaySound;
|
||||||
private System.Windows.Forms.Label labelSoundNotification;
|
private System.Windows.Forms.Label labelSoundNotification;
|
||||||
private System.Windows.Forms.Panel panelSoundNotification;
|
private System.Windows.Forms.Panel panelSoundNotification;
|
||||||
|
private System.Windows.Forms.Label labelVolume;
|
||||||
|
private System.Windows.Forms.Label labelVolumeValue;
|
||||||
|
private System.Windows.Forms.TrackBar trackBarVolume;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,12 +2,14 @@
|
|||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Notification;
|
using TweetDuck.Core.Notification;
|
||||||
using TweetLib.Audio;
|
using TweetLib.Audio;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Other.Settings{
|
namespace TweetDuck.Core.Other.Settings{
|
||||||
partial class TabSettingsSounds : BaseTabSettings{
|
sealed partial class TabSettingsSounds : BaseTabSettings{
|
||||||
private readonly SoundNotification soundNotification;
|
private readonly SoundNotification soundNotification;
|
||||||
|
private readonly bool supportsChangingVolume;
|
||||||
|
|
||||||
public TabSettingsSounds(){
|
public TabSettingsSounds(){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@@ -15,8 +17,14 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
soundNotification = new SoundNotification();
|
soundNotification = new SoundNotification();
|
||||||
soundNotification.PlaybackError += sound_PlaybackError;
|
soundNotification.PlaybackError += sound_PlaybackError;
|
||||||
|
|
||||||
|
supportsChangingVolume = soundNotification.SetVolume(Config.NotificationSoundVolume);
|
||||||
|
|
||||||
|
trackBarVolume.Enabled = supportsChangingVolume && !string.IsNullOrEmpty(Config.NotificationSoundPath);
|
||||||
|
trackBarVolume.SetValueSafe(Config.NotificationSoundVolume);
|
||||||
|
labelVolumeValue.Text = trackBarVolume.Value+"%";
|
||||||
|
|
||||||
tbCustomSound.Text = Config.NotificationSoundPath;
|
tbCustomSound.Text = Config.NotificationSoundPath;
|
||||||
tbCustomSound_TextChanged(tbCustomSound, new EventArgs());
|
tbCustomSound_TextChanged(tbCustomSound, EventArgs.Empty);
|
||||||
|
|
||||||
Disposed += (sender, args) => soundNotification.Dispose();
|
Disposed += (sender, args) => soundNotification.Dispose();
|
||||||
}
|
}
|
||||||
@@ -37,6 +45,7 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
tbCustomSound.ForeColor = isEmpty || File.Exists(tbCustomSound.Text) ? SystemColors.WindowText : Color.Red;
|
tbCustomSound.ForeColor = isEmpty || File.Exists(tbCustomSound.Text) ? SystemColors.WindowText : Color.Red;
|
||||||
btnPlaySound.Enabled = !isEmpty;
|
btnPlaySound.Enabled = !isEmpty;
|
||||||
btnResetSound.Enabled = !isEmpty;
|
btnResetSound.Enabled = !isEmpty;
|
||||||
|
trackBarVolume.Enabled = supportsChangingVolume && !isEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void btnPlaySound_Click(object sender, EventArgs e){
|
private void btnPlaySound_Click(object sender, EventArgs e){
|
||||||
@@ -63,5 +72,11 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
private void btnResetSound_Click(object sender, EventArgs e){
|
private void btnResetSound_Click(object sender, EventArgs e){
|
||||||
tbCustomSound.Text = string.Empty;
|
tbCustomSound.Text = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void trackBarVolume_ValueChanged(object sender, EventArgs e){
|
||||||
|
Config.NotificationSoundVolume = trackBarVolume.Value;
|
||||||
|
soundNotification.SetVolume(Config.NotificationSoundVolume);
|
||||||
|
labelVolumeValue.Text = Config.NotificationSoundVolume+"%";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,7 @@ using System.ComponentModel;
|
|||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
namespace TweetDuck.Core{
|
namespace TweetDuck.Core{
|
||||||
partial class TrayIcon : Component{
|
sealed partial class TrayIcon : Component{
|
||||||
public enum Behavior{ // keep order
|
public enum Behavior{ // keep order
|
||||||
Disabled, DisplayOnly, MinimizeToTray, CloseToTray, Combined
|
Disabled, DisplayOnly, MinimizeToTray, CloseToTray, Combined
|
||||||
}
|
}
|
||||||
|
@@ -5,6 +5,7 @@ using System.Diagnostics;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using CefSharp.WinForms;
|
||||||
using TweetDuck.Core.Other;
|
using TweetDuck.Core.Other;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Utils{
|
namespace TweetDuck.Core.Utils{
|
||||||
@@ -42,23 +43,46 @@ namespace TweetDuck.Core.Utils{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsValidUrl(string url){
|
public static ChromiumWebBrowser AsControl(this IWebBrowser browserControl){
|
||||||
if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri)){
|
return (ChromiumWebBrowser)browserControl;
|
||||||
string scheme = uri.Scheme;
|
|
||||||
return scheme == Uri.UriSchemeHttp || scheme == Uri.UriSchemeHttps || scheme == Uri.UriSchemeFtp || scheme == Uri.UriSchemeMailto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
private const string TwitterTrackingUrl = "t.co";
|
||||||
|
|
||||||
|
public enum UrlCheckResult{
|
||||||
|
Invalid, Tracking, Fine
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UrlCheckResult CheckUrl(string url){
|
||||||
|
if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri)){
|
||||||
|
string scheme = uri.Scheme;
|
||||||
|
|
||||||
|
if (scheme == Uri.UriSchemeHttps || scheme == Uri.UriSchemeHttp || scheme == Uri.UriSchemeFtp || scheme == Uri.UriSchemeMailto){
|
||||||
|
return uri.Host == TwitterTrackingUrl ? UrlCheckResult.Tracking : UrlCheckResult.Fine;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return UrlCheckResult.Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OpenExternalBrowser(string url){
|
public static void OpenExternalBrowser(string url){
|
||||||
if (string.IsNullOrWhiteSpace(url))return;
|
if (string.IsNullOrWhiteSpace(url))return;
|
||||||
|
|
||||||
if (IsValidUrl(url)){
|
switch(CheckUrl(url)){
|
||||||
|
case UrlCheckResult.Fine:
|
||||||
|
OpenExternalBrowserUnsafe(url);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UrlCheckResult.Tracking:
|
||||||
|
if (FormMessage.Warning("Blocked URL", "TweetDuck has blocked a tracking url due to privacy concerns. Do you want to visit it anyway?\n"+url, FormMessage.Yes, FormMessage.No)){
|
||||||
OpenExternalBrowserUnsafe(url);
|
OpenExternalBrowserUnsafe(url);
|
||||||
}
|
}
|
||||||
else{
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UrlCheckResult.Invalid:
|
||||||
FormMessage.Warning("Blocked URL", "A potentially malicious URL was blocked from opening:\n"+url, FormMessage.OK);
|
FormMessage.Warning("Blocked URL", "A potentially malicious URL was blocked from opening:\n"+url, FormMessage.OK);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -16,7 +16,6 @@ namespace TweetDuck.Core.Utils{
|
|||||||
public const int GWL_STYLE = -16;
|
public const int GWL_STYLE = -16;
|
||||||
|
|
||||||
public const int SB_HORZ = 0;
|
public const int SB_HORZ = 0;
|
||||||
public const int BCM_SETSHIELD = 0x160C;
|
|
||||||
|
|
||||||
public const int WM_MOUSE_LL = 14;
|
public const int WM_MOUSE_LL = 14;
|
||||||
public const int WM_MOUSEWHEEL = 0x020A;
|
public const int WM_MOUSEWHEEL = 0x020A;
|
||||||
|
@@ -14,7 +14,7 @@ namespace TweetDuck.Core.Utils{
|
|||||||
public static readonly Color BackgroundColor = Color.FromArgb(28, 99, 153);
|
public static readonly Color BackgroundColor = Color.FromArgb(28, 99, 153);
|
||||||
public const string BackgroundColorFix = "let e=document.createElement('style');document.head.appendChild(e);e.innerHTML='body::before{background:#1c6399!important}'";
|
public const string BackgroundColorFix = "let e=document.createElement('style');document.head.appendChild(e);e.innerHTML='body::before{background:#1c6399!important}'";
|
||||||
|
|
||||||
private static readonly Lazy<Regex> RegexAccountLazy = new Lazy<Regex>(() => new Regex(@"^https?://twitter\.com/([^/]+)/?$", RegexOptions.Compiled), false);
|
private static readonly Lazy<Regex> RegexAccountLazy = new Lazy<Regex>(() => new Regex(@"^https?://twitter\.com/(?!signup$|tos$|privacy$)([^/]+)/?$", RegexOptions.Compiled), false);
|
||||||
public static Regex RegexAccount => RegexAccountLazy.Value;
|
public static Regex RegexAccount => RegexAccountLazy.Value;
|
||||||
|
|
||||||
public static readonly string[] DictionaryWords = {
|
public static readonly string[] DictionaryWords = {
|
||||||
@@ -33,14 +33,14 @@ namespace TweetDuck.Core.Utils{
|
|||||||
return frame.Url.Contains("//twitter.com/");
|
return frame.Url.Contains("//twitter.com/");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string ExtractImageBaseLink(string url){
|
private static string ExtractMediaBaseLink(string url){
|
||||||
int dot = url.LastIndexOf('/');
|
int dot = url.LastIndexOf('/');
|
||||||
return dot == -1 ? url : StringUtils.ExtractBefore(url, ':', dot);
|
return dot == -1 ? url : StringUtils.ExtractBefore(url, ':', dot);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetImageLink(string url, ImageQuality quality){
|
public static string GetMediaLink(string url, ImageQuality quality){
|
||||||
if (quality == ImageQuality.Orig){
|
if (quality == ImageQuality.Orig){
|
||||||
string result = ExtractImageBaseLink(url);
|
string result = ExtractMediaBaseLink(url);
|
||||||
|
|
||||||
if (result != url || url.Contains("//pbs.twimg.com/media/")){
|
if (result != url || url.Contains("//pbs.twimg.com/media/")){
|
||||||
result += ":orig";
|
result += ":orig";
|
||||||
@@ -62,10 +62,10 @@ namespace TweetDuck.Core.Utils{
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
string firstImageLink = GetImageLink(urls[0], quality);
|
string firstImageLink = GetMediaLink(urls[0], quality);
|
||||||
int qualityIndex = firstImageLink.IndexOf(':', firstImageLink.LastIndexOf('/'));
|
int qualityIndex = firstImageLink.IndexOf(':', firstImageLink.LastIndexOf('/'));
|
||||||
|
|
||||||
string file = BrowserUtils.GetFileNameFromUrl(ExtractImageBaseLink(firstImageLink));
|
string file = BrowserUtils.GetFileNameFromUrl(ExtractMediaBaseLink(firstImageLink));
|
||||||
string ext = Path.GetExtension(file); // includes dot
|
string ext = Path.GetExtension(file); // includes dot
|
||||||
|
|
||||||
string[] fileNameParts = qualityIndex == -1 ? new string[]{
|
string[] fileNameParts = qualityIndex == -1 ? new string[]{
|
||||||
@@ -96,11 +96,30 @@ namespace TweetDuck.Core.Utils{
|
|||||||
string pathExt = Path.GetExtension(dialog.FileName);
|
string pathExt = Path.GetExtension(dialog.FileName);
|
||||||
|
|
||||||
for(int index = 0; index < urls.Length; index++){
|
for(int index = 0; index < urls.Length; index++){
|
||||||
BrowserUtils.DownloadFileAsync(GetImageLink(urls[index], quality), $"{pathBase} {index+1}{pathExt}", null, OnFailure);
|
BrowserUtils.DownloadFileAsync(GetMediaLink(urls[index], quality), $"{pathBase} {index+1}{pathExt}", null, OnFailure);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void DownloadVideo(string url){
|
||||||
|
string filename = BrowserUtils.GetFileNameFromUrl(url);
|
||||||
|
string ext = Path.GetExtension(filename);
|
||||||
|
|
||||||
|
using(SaveFileDialog dialog = new SaveFileDialog{
|
||||||
|
AutoUpgradeEnabled = true,
|
||||||
|
OverwritePrompt = true,
|
||||||
|
Title = "Save video",
|
||||||
|
FileName = filename,
|
||||||
|
Filter = "Video"+(string.IsNullOrEmpty(ext) ? " (unknown)|*.*" : $" (*{ext})|*{ext}")
|
||||||
|
}){
|
||||||
|
if (dialog.ShowDialog() == DialogResult.OK){
|
||||||
|
BrowserUtils.DownloadFileAsync(url, dialog.FileName, null, ex => {
|
||||||
|
FormMessage.Error("Image Download", "An error occurred while downloading the image: "+ex.Message, FormMessage.OK);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -91,7 +91,7 @@ namespace TweetDuck.Data{
|
|||||||
stream.Dispose();
|
stream.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Entry{
|
public sealed class Entry{
|
||||||
public string Identifier { get; }
|
public string Identifier { get; }
|
||||||
|
|
||||||
public string KeyName{
|
public string KeyName{
|
||||||
|
@@ -90,12 +90,12 @@ namespace TweetDuck.Data.Serialization{
|
|||||||
HandleUnknownProperties?.Invoke(obj, unknownProperties);
|
HandleUnknownProperties?.Invoke(obj, unknownProperties);
|
||||||
|
|
||||||
if (unknownProperties.Count > 0){
|
if (unknownProperties.Count > 0){
|
||||||
throw new SerializationException($"Invalid file format, unknown properties: {string.Join(", ", unknownProperties.Keys)}+");
|
throw new SerializationException($"Invalid file format, unknown properties: {string.Join(", ", unknownProperties.Keys)}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class BasicTypeConverter : ITypeConverter{
|
private sealed class BasicTypeConverter : ITypeConverter{
|
||||||
bool ITypeConverter.TryWriteType(Type type, object value, out string converted){
|
bool ITypeConverter.TryWriteType(Type type, object value, out string converted){
|
||||||
switch(Type.GetTypeCode(type)){
|
switch(Type.GetTypeCode(type)){
|
||||||
case TypeCode.Boolean:
|
case TypeCode.Boolean:
|
||||||
|
@@ -1,12 +1,10 @@
|
|||||||
using System;
|
using System.Drawing;
|
||||||
using System.Drawing;
|
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetDuck.Data.Serialization;
|
using TweetDuck.Data.Serialization;
|
||||||
|
|
||||||
namespace TweetDuck.Data{
|
namespace TweetDuck.Data{
|
||||||
[Serializable] // TODO remove attribute with UserConfigLegacy
|
|
||||||
sealed class WindowState{
|
sealed class WindowState{
|
||||||
private Rectangle rect;
|
private Rectangle rect;
|
||||||
private bool isMaximized;
|
private bool isMaximized;
|
||||||
|
@@ -8,7 +8,7 @@ using TweetDuck.Core.Utils;
|
|||||||
using TweetDuck.Plugins.Enums;
|
using TweetDuck.Plugins.Enums;
|
||||||
|
|
||||||
namespace TweetDuck.Plugins.Controls{
|
namespace TweetDuck.Plugins.Controls{
|
||||||
partial class PluginControl : UserControl{
|
sealed partial class PluginControl : UserControl{
|
||||||
private readonly PluginManager pluginManager;
|
private readonly PluginManager pluginManager;
|
||||||
private readonly Plugin plugin;
|
private readonly Plugin plugin;
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace TweetDuck.Plugins.Events{
|
namespace TweetDuck.Plugins.Events{
|
||||||
class PluginChangedStateEventArgs : EventArgs{
|
sealed class PluginChangedStateEventArgs : EventArgs{
|
||||||
public Plugin Plugin { get; }
|
public Plugin Plugin { get; }
|
||||||
public bool IsEnabled { get; }
|
public bool IsEnabled { get; }
|
||||||
|
|
||||||
|
@@ -2,10 +2,10 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace TweetDuck.Plugins.Events{
|
namespace TweetDuck.Plugins.Events{
|
||||||
class PluginErrorEventArgs : EventArgs{
|
sealed class PluginErrorEventArgs : EventArgs{
|
||||||
public bool HasErrors => Errors.Count > 0;
|
public bool HasErrors => Errors.Count > 0;
|
||||||
|
|
||||||
public IList<string> Errors;
|
public IList<string> Errors { get; }
|
||||||
|
|
||||||
public PluginErrorEventArgs(IList<string> errors){
|
public PluginErrorEventArgs(IList<string> errors){
|
||||||
this.Errors = errors;
|
this.Errors = errors;
|
||||||
|
@@ -7,6 +7,9 @@ using TweetDuck.Plugins.Enums;
|
|||||||
|
|
||||||
namespace TweetDuck.Plugins{
|
namespace TweetDuck.Plugins{
|
||||||
sealed class Plugin{
|
sealed class Plugin{
|
||||||
|
private static readonly Version AppVersion = new Version(Program.VersionTag);
|
||||||
|
private const string VersionWildcard = "*";
|
||||||
|
|
||||||
public string Identifier { get; }
|
public string Identifier { get; }
|
||||||
public PluginGroup Group { get; }
|
public PluginGroup Group { get; }
|
||||||
public PluginEnvironment Environments { get; private set; }
|
public PluginEnvironment Environments { get; private set; }
|
||||||
@@ -50,7 +53,7 @@ namespace TweetDuck.Plugins{
|
|||||||
{ "WEBSITE", "" },
|
{ "WEBSITE", "" },
|
||||||
{ "CONFIGFILE", "" },
|
{ "CONFIGFILE", "" },
|
||||||
{ "CONFIGDEFAULT", "" },
|
{ "CONFIGDEFAULT", "" },
|
||||||
{ "REQUIRES", "*" }
|
{ "REQUIRES", VersionWildcard }
|
||||||
};
|
};
|
||||||
|
|
||||||
private bool? canRun;
|
private bool? canRun;
|
||||||
@@ -209,7 +212,7 @@ namespace TweetDuck.Plugins{
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (plugin.RequiredVersion.Length == 0 || !(plugin.RequiredVersion.Equals("*") || System.Version.TryParse(plugin.RequiredVersion, out Version _))){
|
if (plugin.RequiredVersion.Length == 0 || !(plugin.RequiredVersion == VersionWildcard || System.Version.TryParse(plugin.RequiredVersion, out Version _))){
|
||||||
error = "Plugin contains invalid version: "+plugin.RequiredVersion;
|
error = "Plugin contains invalid version: "+plugin.RequiredVersion;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -221,7 +224,7 @@ namespace TweetDuck.Plugins{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static bool CheckRequiredVersion(string requires){
|
private static bool CheckRequiredVersion(string requires){
|
||||||
return requires.Equals("*", StringComparison.Ordinal) || Program.Version >= new Version(requires);
|
return requires == VersionWildcard || AppVersion >= new Version(requires);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -8,22 +8,24 @@ namespace TweetDuck.Plugins{
|
|||||||
sealed class PluginConfig{
|
sealed class PluginConfig{
|
||||||
public event EventHandler<PluginChangedStateEventArgs> InternalPluginChangedState; // should only be accessed from PluginManager
|
public event EventHandler<PluginChangedStateEventArgs> InternalPluginChangedState; // should only be accessed from PluginManager
|
||||||
|
|
||||||
public IEnumerable<string> DisabledPlugins => Disabled;
|
public IEnumerable<string> DisabledPlugins => disabled;
|
||||||
public bool AnyDisabled => Disabled.Count > 0;
|
public bool AnyDisabled => disabled.Count > 0;
|
||||||
|
|
||||||
private readonly HashSet<string> Disabled = new HashSet<string>{
|
private static readonly string[] DefaultDisabled = {
|
||||||
"official/clear-columns",
|
"official/clear-columns",
|
||||||
"official/reply-account"
|
"official/reply-account"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private readonly HashSet<string> disabled = new HashSet<string>();
|
||||||
|
|
||||||
public void SetEnabled(Plugin plugin, bool enabled){
|
public void SetEnabled(Plugin plugin, bool enabled){
|
||||||
if ((enabled && Disabled.Remove(plugin.Identifier)) || (!enabled && Disabled.Add(plugin.Identifier))){
|
if ((enabled && disabled.Remove(plugin.Identifier)) || (!enabled && disabled.Add(plugin.Identifier))){
|
||||||
InternalPluginChangedState?.Invoke(this, new PluginChangedStateEventArgs(plugin, enabled));
|
InternalPluginChangedState?.Invoke(this, new PluginChangedStateEventArgs(plugin, enabled));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsEnabled(Plugin plugin){
|
public bool IsEnabled(Plugin plugin){
|
||||||
return !Disabled.Contains(plugin.Identifier);
|
return !disabled.Contains(plugin.Identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Load(string file){
|
public void Load(string file){
|
||||||
@@ -32,14 +34,20 @@ namespace TweetDuck.Plugins{
|
|||||||
string line = reader.ReadLine();
|
string line = reader.ReadLine();
|
||||||
|
|
||||||
if (line == "#Disabled"){
|
if (line == "#Disabled"){
|
||||||
Disabled.Clear();
|
disabled.Clear();
|
||||||
|
|
||||||
while((line = reader.ReadLine()) != null){
|
while((line = reader.ReadLine()) != null){
|
||||||
Disabled.Add(line);
|
disabled.Add(line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}catch(FileNotFoundException){
|
}catch(FileNotFoundException){
|
||||||
|
disabled.Clear();
|
||||||
|
|
||||||
|
foreach(string identifier in DefaultDisabled){
|
||||||
|
disabled.Add(identifier);
|
||||||
|
}
|
||||||
|
|
||||||
Save(file);
|
Save(file);
|
||||||
}catch(DirectoryNotFoundException){
|
}catch(DirectoryNotFoundException){
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
@@ -52,8 +60,8 @@ namespace TweetDuck.Plugins{
|
|||||||
using(StreamWriter writer = new StreamWriter(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None), Encoding.UTF8)){
|
using(StreamWriter writer = new StreamWriter(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None), Encoding.UTF8)){
|
||||||
writer.WriteLine("#Disabled");
|
writer.WriteLine("#Disabled");
|
||||||
|
|
||||||
foreach(string disabled in Disabled){
|
foreach(string identifier in disabled){
|
||||||
writer.WriteLine(disabled);
|
writer.WriteLine(identifier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
|
@@ -45,14 +45,7 @@ namespace TweetDuck.Plugins{
|
|||||||
this.Bridge = new PluginBridge(this);
|
this.Bridge = new PluginBridge(this);
|
||||||
|
|
||||||
Config.Load(configPath);
|
Config.Load(configPath);
|
||||||
|
|
||||||
Config.InternalPluginChangedState += Config_InternalPluginChangedState;
|
Config.InternalPluginChangedState += Config_InternalPluginChangedState;
|
||||||
Program.UserConfigReplaced += Program_UserConfigReplaced;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Program_UserConfigReplaced(object sender, EventArgs e){
|
|
||||||
Config.Load(configPath);
|
|
||||||
Reload();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Config_InternalPluginChangedState(object sender, PluginChangedStateEventArgs e){
|
private void Config_InternalPluginChangedState(object sender, PluginChangedStateEventArgs e){
|
||||||
@@ -83,6 +76,8 @@ namespace TweetDuck.Plugins{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void Reload(){
|
public void Reload(){
|
||||||
|
Config.Load(configPath);
|
||||||
|
|
||||||
plugins.Clear();
|
plugins.Clear();
|
||||||
tokens.Clear();
|
tokens.Clear();
|
||||||
|
|
||||||
|
18
Program.cs
18
Program.cs
@@ -21,10 +21,8 @@ namespace TweetDuck{
|
|||||||
public const string BrandName = "TweetDuck";
|
public const string BrandName = "TweetDuck";
|
||||||
public const string Website = "https://tweetduck.chylex.com";
|
public const string Website = "https://tweetduck.chylex.com";
|
||||||
|
|
||||||
public const string VersionTag = "1.8.6";
|
public const string VersionTag = "1.9.1";
|
||||||
public const string VersionFull = "1.8.6";
|
|
||||||
|
|
||||||
public static readonly Version Version = new Version(VersionTag);
|
|
||||||
public static readonly bool IsPortable = File.Exists("makeportable");
|
public static readonly bool IsPortable = File.Exists("makeportable");
|
||||||
|
|
||||||
public static readonly string ProgramPath = AppDomain.CurrentDomain.BaseDirectory;
|
public static readonly string ProgramPath = AppDomain.CurrentDomain.BaseDirectory;
|
||||||
@@ -54,8 +52,6 @@ namespace TweetDuck{
|
|||||||
public static Reporter Reporter { get; }
|
public static Reporter Reporter { get; }
|
||||||
public static CultureInfo Culture { get; }
|
public static CultureInfo Culture { get; }
|
||||||
|
|
||||||
public static event EventHandler UserConfigReplaced;
|
|
||||||
|
|
||||||
static Program(){
|
static Program(){
|
||||||
Culture = CultureInfo.CurrentCulture;
|
Culture = CultureInfo.CurrentCulture;
|
||||||
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
|
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
|
||||||
@@ -118,12 +114,15 @@ namespace TweetDuck{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UserConfig = UserConfig.Load(UserConfigFilePath);
|
||||||
SystemConfig = SystemConfig.Load(SystemConfigFilePath);
|
SystemConfig = SystemConfig.Load(SystemConfigFilePath);
|
||||||
ReloadConfig();
|
|
||||||
|
|
||||||
if (Arguments.HasFlag(Arguments.ArgImportCookies)){
|
if (Arguments.HasFlag(Arguments.ArgImportCookies)){
|
||||||
ExportManager.ImportCookies();
|
ExportManager.ImportCookies();
|
||||||
}
|
}
|
||||||
|
else if (Arguments.HasFlag(Arguments.ArgDeleteCookies)){
|
||||||
|
ExportManager.DeleteCookies();
|
||||||
|
}
|
||||||
|
|
||||||
if (Arguments.HasFlag(Arguments.ArgUpdated)){
|
if (Arguments.HasFlag(Arguments.ArgUpdated)){
|
||||||
WindowsUtils.TryDeleteFolderWhenAble(InstallerPath, 8000);
|
WindowsUtils.TryDeleteFolderWhenAble(InstallerPath, 8000);
|
||||||
@@ -190,11 +189,6 @@ namespace TweetDuck{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ReloadConfig(){
|
|
||||||
UserConfig = UserConfig.Load(UserConfigFilePath);
|
|
||||||
UserConfigReplaced?.Invoke(UserConfig, new EventArgs());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ResetConfig(){
|
public static void ResetConfig(){
|
||||||
try{
|
try{
|
||||||
File.Delete(UserConfigFilePath);
|
File.Delete(UserConfigFilePath);
|
||||||
@@ -204,7 +198,7 @@ namespace TweetDuck{
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReloadConfig();
|
UserConfig.Reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Restart(params string[] extraArgs){
|
public static void Restart(params string[] extraArgs){
|
||||||
|
@@ -34,8 +34,8 @@ using TweetDuck;
|
|||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion(Program.VersionFull)]
|
[assembly: AssemblyVersion(Program.VersionTag)]
|
||||||
[assembly: AssemblyFileVersion(Program.VersionFull)]
|
[assembly: AssemblyFileVersion(Program.VersionTag)]
|
||||||
|
|
||||||
[assembly: NeutralResourcesLanguage("en")]
|
[assembly: NeutralResourcesLanguage("en")]
|
||||||
|
|
||||||
|
@@ -53,9 +53,7 @@ enabled(){
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.eventKeyDown = function(e){
|
this.eventKeyDown = function(e){
|
||||||
if (!(document.activeElement === null || document.activeElement === document.body)){
|
return if !(document.activeElement === null || document.activeElement === document.body);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateShiftState(e.shiftKey);
|
updateShiftState(e.shiftKey);
|
||||||
|
|
||||||
|
@@ -102,16 +102,14 @@ enabled(){
|
|||||||
|
|
||||||
// settings click event
|
// settings click event
|
||||||
this.onSettingsMenuClickedEvent = () => {
|
this.onSettingsMenuClickedEvent = () => {
|
||||||
if (this.htmlModal === null || this.config === null){
|
return if this.htmlModal === null || this.config === null;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
let menu = $(".js-dropdown-content").children("ul").first();
|
let menu = $(".js-dropdown-content").children("ul").first();
|
||||||
if (menu.length === 0)return;
|
return if menu.length === 0;
|
||||||
|
|
||||||
let itemTD = menu.children("[data-std]").first();
|
let itemTD = menu.children("[data-std]").first();
|
||||||
if (itemTD.length === 0)return;
|
return if itemTD.length === 0;
|
||||||
|
|
||||||
if (!itemTD.prev().hasClass("drp-h-divider")){
|
if (!itemTD.prev().hasClass("drp-h-divider")){
|
||||||
itemTD.before('<li class="drp-h-divider"></li>');
|
itemTD.before('<li class="drp-h-divider"></li>');
|
||||||
@@ -336,17 +334,22 @@ enabled(){
|
|||||||
this.css.insert(".txt-base-smallest:not(.icon), .txt-base-largest:not(.icon) { font-size: "+this.config.fontSize+" !important }");
|
this.css.insert(".txt-base-smallest:not(.icon), .txt-base-largest:not(.icon) { font-size: "+this.config.fontSize+" !important }");
|
||||||
this.css.insert(".avatar { border-radius: "+this.config.avatarRadius+"% !important }");
|
this.css.insert(".avatar { border-radius: "+this.config.avatarRadius+"% !important }");
|
||||||
|
|
||||||
|
let notificationScrollbarColor = null;
|
||||||
|
|
||||||
if (this.config.themeColorTweaks){
|
if (this.config.themeColorTweaks){
|
||||||
switch(TD.settings.getTheme()){
|
switch(TD.settings.getTheme()){
|
||||||
case "dark":
|
case "dark":
|
||||||
this.css.insert(".app-content, .app-columns-container { background-color: #444448 }");
|
this.css.insert(".app-content, .app-columns-container { background-color: #444448 }");
|
||||||
this.css.insert(".column-drag-handle { opacity: 0.5 }");
|
this.css.insert(".column-drag-handle { opacity: 0.5 }");
|
||||||
this.css.insert(".column-drag-handle:hover { opacity: 1 }");
|
this.css.insert(".column-drag-handle:hover { opacity: 1 }");
|
||||||
|
this.css.insert(".scroll-styled-v::-webkit-scrollbar-thumb, .scroll-styled-h::-webkit-scrollbar-thumb { background-color: #666 }");
|
||||||
|
notificationScrollbarColor = "666";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "light":
|
case "light":
|
||||||
this.css.insert(".scroll-styled-v::-webkit-scrollbar-thumb, .scroll-styled-h::-webkit-scrollbar-thumb { background-color: #d2d6da }");
|
this.css.insert(".scroll-styled-v::-webkit-scrollbar-thumb, .scroll-styled-h::-webkit-scrollbar-thumb { background-color: #d2d6da }");
|
||||||
this.css.insert(".app-columns-container.scroll-styled-h::-webkit-scrollbar-thumb:not(:hover) { background-color: #a5aeb5 }");
|
this.css.insert(".app-columns-container.scroll-styled-h::-webkit-scrollbar-thumb:not(:hover) { background-color: #a5aeb5 }");
|
||||||
|
notificationScrollbarColor = "a5aeb5";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -462,6 +465,9 @@ enabled(){
|
|||||||
.drawer .btn .icon, .app-header .btn .icon { line-height: 1em !important }
|
.drawer .btn .icon, .app-header .btn .icon { line-height: 1em !important }
|
||||||
.column-header .column-type-icon { bottom: 26px !important }
|
.column-header .column-type-icon { bottom: 26px !important }
|
||||||
.is-options-open .column-type-icon { bottom: 25px !important }
|
.is-options-open .column-type-icon { bottom: 25px !important }
|
||||||
|
|
||||||
|
.tweet-action-item .icon-favorite-toggle { font-size: 16px !important; }
|
||||||
|
.tweet-action-item .heartsprite { top: -260% !important; left: -260% !important; transform: scale(0.4, 0.39) translateY(0.5px) !important; }
|
||||||
.tweet-footer { margin-top: 6px !important }`;
|
.tweet-footer { margin-top: 6px !important }`;
|
||||||
|
|
||||||
document.head.appendChild(this.icons);
|
document.head.appendChild(this.icons);
|
||||||
@@ -513,6 +519,10 @@ ${this.config.revertIcons ? `
|
|||||||
.icon-user-filled:before{content:"\\f035";font-family:tweetdeckold}
|
.icon-user-filled:before{content:"\\f035";font-family:tweetdeckold}
|
||||||
.icon-user-dd:before{content:"\\f01a";font-family:tweetdeckold}
|
.icon-user-dd:before{content:"\\f01a";font-family:tweetdeckold}
|
||||||
` : ``}
|
` : ``}
|
||||||
|
|
||||||
|
${notificationScrollbarColor ? `
|
||||||
|
.scroll-styled-v::-webkit-scrollbar-thumb, .scroll-styled-h::-webkit-scrollbar-thumb { background-color: #${notificationScrollbarColor} }
|
||||||
|
` : ``}
|
||||||
</style>`);
|
</style>`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -9,7 +9,7 @@ Emoji keyboard
|
|||||||
chylex
|
chylex
|
||||||
|
|
||||||
[version]
|
[version]
|
||||||
1.3
|
1.3.1
|
||||||
|
|
||||||
[website]
|
[website]
|
||||||
https://tweetduck.chylex.com
|
https://tweetduck.chylex.com
|
||||||
|
@@ -45,13 +45,6 @@ enabled(){
|
|||||||
this.css.insert(".emoji-keyboard-skintones .sel { border: 2px solid rgba(0, 0, 0, 0.35); box-shadow: 0 0 2px 0 rgba(255, 255, 255, 0.65), 0 0 1px 0 rgba(255, 255, 255, 0.4) inset }");
|
this.css.insert(".emoji-keyboard-skintones .sel { border: 2px solid rgba(0, 0, 0, 0.35); box-shadow: 0 0 2px 0 rgba(255, 255, 255, 0.65), 0 0 1px 0 rgba(255, 255, 255, 0.4) inset }");
|
||||||
this.css.insert(".emoji-keyboard-skintones :hover { border: 2px solid rgba(0, 0, 0, 0.25); box-shadow: 0 0 1px 0 rgba(255, 255, 255, 0.65), 0 0 1px 0 rgba(255, 255, 255, 0.25) inset }");
|
this.css.insert(".emoji-keyboard-skintones :hover { border: 2px solid rgba(0, 0, 0, 0.25); box-shadow: 0 0 1px 0 rgba(255, 255, 255, 0.65), 0 0 1px 0 rgba(255, 255, 255, 0.25) inset }");
|
||||||
|
|
||||||
this.css.insert("#emoji-keyboard-tweet-input { padding: 0 !important; line-height: 18px }");
|
|
||||||
this.css.insert("#emoji-keyboard-tweet-input img { padding: 0.1em !important; width: 1em; height: 1em; vertical-align: -0.25em }");
|
|
||||||
this.css.insert("#emoji-keyboard-tweet-input:empty::before { content: \"What's happening?\"; display: inline-block; color: #ced8de }");
|
|
||||||
|
|
||||||
this.css.insert(".js-docked-compose .compose-text-container.td-emoji-keyboard-swap .js-compose-text { position: absolute; z-index: -9999; left: 0; opacity: 0 }");
|
|
||||||
this.css.insert(".compose-text-container:not(.td-emoji-keyboard-swap) #emoji-keyboard-tweet-input { display: none; }");
|
|
||||||
|
|
||||||
this.css.insert(".js-compose-text { font-family: \"Twitter Color Emoji\", Helvetica, Arial, Verdana, sans-serif; }");
|
this.css.insert(".js-compose-text { font-family: \"Twitter Color Emoji\", Helvetica, Arial, Verdana, sans-serif; }");
|
||||||
|
|
||||||
// layout
|
// layout
|
||||||
@@ -72,6 +65,8 @@ enabled(){
|
|||||||
this.currentKeyboard = null;
|
this.currentKeyboard = null;
|
||||||
this.currentSpanner = null;
|
this.currentSpanner = null;
|
||||||
|
|
||||||
|
var wasSearchFocused = false;
|
||||||
|
|
||||||
var hideKeyboard = (refocus) => {
|
var hideKeyboard = (refocus) => {
|
||||||
$(this.currentKeyboard).remove();
|
$(this.currentKeyboard).remove();
|
||||||
this.currentKeyboard = null;
|
this.currentKeyboard = null;
|
||||||
@@ -86,18 +81,13 @@ enabled(){
|
|||||||
$(".emoji-keyboard-popup-btn").removeClass("is-selected");
|
$(".emoji-keyboard-popup-btn").removeClass("is-selected");
|
||||||
|
|
||||||
if (refocus){
|
if (refocus){
|
||||||
if ($(".compose-text-container", ".js-docked-compose").hasClass("td-emoji-keyboard-swap")){
|
|
||||||
$("#emoji-keyboard-tweet-input").focus();
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
$(".js-compose-text", ".js-docked-compose").focus();
|
$(".js-compose-text", ".js-docked-compose").focus();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var generateEmojiHTML = skinTone => {
|
var generateEmojiHTML = skinTone => {
|
||||||
let index = 0;
|
let index = 0;
|
||||||
let html = [ "<p style='font-size:13px;color:#444;margin:4px;text-align:center'>Please, note that most emoji will not show up properly in the text box above, but they will display in the tweet.</p>" ];
|
let html = [ "<p style='font-size:13px;color:#444;margin:4px;text-align:center'>Please, note that some emoji may not show up correctly in the text box above, but they will display in the tweet.</p>" ];
|
||||||
|
|
||||||
for(let array of [ this.emojiData1, this.emojiData2[skinTone], this.emojiData3 ]){
|
for(let array of [ this.emojiData1, this.emojiData2[skinTone], this.emojiData3 ]){
|
||||||
for(let emoji of array){
|
for(let emoji of array){
|
||||||
@@ -193,16 +183,36 @@ enabled(){
|
|||||||
var searchInput = search.children[0];
|
var searchInput = search.children[0];
|
||||||
searchInput.focus();
|
searchInput.focus();
|
||||||
|
|
||||||
|
wasSearchFocused = false;
|
||||||
|
|
||||||
searchInput.addEventListener("input", function(e){
|
searchInput.addEventListener("input", function(e){
|
||||||
me.currentKeywords = e.target.value.split(" ").filter(kw => kw.length > 0).map(kw => kw.toLowerCase());
|
me.currentKeywords = e.target.value.split(" ").filter(kw => kw.length > 0).map(kw => kw.toLowerCase());
|
||||||
updateFilters();
|
updateFilters();
|
||||||
|
|
||||||
|
wasSearchFocused = $(this).val().length > 0;
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
});
|
});
|
||||||
|
|
||||||
searchInput.addEventListener("focus", function(){
|
searchInput.addEventListener("keydown", function(e){
|
||||||
|
if (e.keyCode === 13 && $(this).val().length){ // enter
|
||||||
|
let ele = $(".emoji-keyboard-list").children("img").filter(":visible").first();
|
||||||
|
|
||||||
|
if (ele.length > 0){
|
||||||
|
insertEmoji(ele[0].getAttribute("src"), ele[0].getAttribute("alt"));
|
||||||
|
}
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
searchInput.addEventListener("click", function(){
|
||||||
$(this).select();
|
$(this).select();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
searchInput.addEventListener("focus", function(){
|
||||||
|
wasSearchFocused = true;
|
||||||
|
});
|
||||||
|
|
||||||
this.currentKeyboard = outer;
|
this.currentKeyboard = outer;
|
||||||
selectSkinTone(this.selectedSkinTone);
|
selectSkinTone(this.selectedSkinTone);
|
||||||
|
|
||||||
@@ -218,27 +228,7 @@ enabled(){
|
|||||||
return button.offset().top+button.outerHeight()+me.composePanelScroller.scrollTop()+8;
|
return button.offset().top+button.outerHeight()+me.composePanelScroller.scrollTop()+8;
|
||||||
};
|
};
|
||||||
|
|
||||||
var focusWithCaretAtEnd = () => {
|
|
||||||
let range = document.createRange();
|
|
||||||
range.selectNodeContents(me.composeInputNew[0]);
|
|
||||||
range.collapse(false);
|
|
||||||
|
|
||||||
let sel = window.getSelection();
|
|
||||||
sel.removeAllRanges();
|
|
||||||
sel.addRange(range);
|
|
||||||
};
|
|
||||||
|
|
||||||
var insertEmoji = (src, alt) => {
|
var insertEmoji = (src, alt) => {
|
||||||
if (this.ENABLE_CUSTOM_KEYBOARD){
|
|
||||||
replaceEditor(true);
|
|
||||||
|
|
||||||
if (!hasSelectionInEditor()){
|
|
||||||
focusWithCaretAtEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
document.execCommand("insertHTML", false, `<img src="${src}" alt="${alt}">`);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
let input = $(".js-compose-text", ".js-docked-compose");
|
let input = $(".js-compose-text", ".js-docked-compose");
|
||||||
|
|
||||||
let val = input.val();
|
let val = input.val();
|
||||||
@@ -247,10 +237,15 @@ enabled(){
|
|||||||
|
|
||||||
input.val(val.slice(0, posStart)+alt+val.slice(posEnd));
|
input.val(val.slice(0, posStart)+alt+val.slice(posEnd));
|
||||||
input.trigger("change");
|
input.trigger("change");
|
||||||
input.focus();
|
|
||||||
|
|
||||||
input[0].selectionStart = posStart+alt.length;
|
input[0].selectionStart = posStart+alt.length;
|
||||||
input[0].selectionEnd = posStart+alt.length;
|
input[0].selectionEnd = posStart+alt.length;
|
||||||
|
|
||||||
|
if (wasSearchFocused){
|
||||||
|
$(".emoji-keyboard-search").children("input").focus();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
input.focus();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -258,8 +253,8 @@ enabled(){
|
|||||||
|
|
||||||
this.emojiKeyboardButtonClickEvent = function(e){
|
this.emojiKeyboardButtonClickEvent = function(e){
|
||||||
if (me.currentKeyboard){
|
if (me.currentKeyboard){
|
||||||
hideKeyboard();
|
|
||||||
$(this).blur();
|
$(this).blur();
|
||||||
|
hideKeyboard(true);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
me.generateKeyboard($(this).offset().left, getKeyboardTop());
|
me.generateKeyboard($(this).offset().left, getKeyboardTop());
|
||||||
@@ -275,6 +270,10 @@ enabled(){
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.composeInputFocusEvent = function(e){
|
||||||
|
wasSearchFocused = false;
|
||||||
|
};
|
||||||
|
|
||||||
this.composerSendingEvent = function(e){
|
this.composerSendingEvent = function(e){
|
||||||
hideKeyboard();
|
hideKeyboard();
|
||||||
};
|
};
|
||||||
@@ -297,148 +296,6 @@ enabled(){
|
|||||||
me.currentKeyboard.style.top = getKeyboardTop()+"px";
|
me.currentKeyboard.style.top = getKeyboardTop()+"px";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// new editor event handlers
|
|
||||||
|
|
||||||
var prevOldInputVal = "";
|
|
||||||
var isEditorActive = false;
|
|
||||||
|
|
||||||
var hasSelectionInEditor = function(){
|
|
||||||
let sel = window.getSelection();
|
|
||||||
return sel.anchorNode && $(sel.anchorNode).closest("#emoji-keyboard-tweet-input").length;
|
|
||||||
};
|
|
||||||
|
|
||||||
var migrateEditorText = function(){
|
|
||||||
let selStart = me.composeInputOrig[0].selectionStart;
|
|
||||||
let selEnd = me.composeInputOrig[0].selectionEnd;
|
|
||||||
|
|
||||||
let val = me.composeInputOrig.val();
|
|
||||||
let splitStart = val.substring(0, selStart).split("\n");
|
|
||||||
let splitEnd = val.substring(0, selEnd).split("\n");
|
|
||||||
|
|
||||||
me.composeInputNew.text(val);
|
|
||||||
me.composeInputNew.html(me.composeInputNew.text().replace(/^(.*?)$/gm, "<div>$1</div>").replace(/<div><\/div>/g, "<div><br></div>"));
|
|
||||||
|
|
||||||
let sel = window.getSelection();
|
|
||||||
let range = document.createRange();
|
|
||||||
|
|
||||||
if (me.composeInputNew[0].children.length === 0){
|
|
||||||
focusWithCaretAtEnd();
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
me.composeInputNew.focus();
|
|
||||||
range.setStart(me.composeInputNew[0].children[splitStart.length-1].firstChild, splitStart.length > 1 ? selStart-val.lastIndexOf("\n", selStart)-1 : selStart);
|
|
||||||
range.setEnd(me.composeInputNew[0].children[splitEnd.length-1].firstChild, splitEnd.length > 1 ? selEnd-val.lastIndexOf("\n", selEnd)-1 : selEnd);
|
|
||||||
}
|
|
||||||
|
|
||||||
sel.removeAllRanges();
|
|
||||||
sel.addRange(range);
|
|
||||||
};
|
|
||||||
|
|
||||||
var replaceEditor = function(useCustom){
|
|
||||||
if (useCustom && !isEditorActive){
|
|
||||||
isEditorActive = true;
|
|
||||||
}
|
|
||||||
else if (!useCustom && isEditorActive){
|
|
||||||
isEditorActive = false;
|
|
||||||
}
|
|
||||||
else return;
|
|
||||||
|
|
||||||
$(".compose-text-container", ".js-docked-compose").toggleClass("td-emoji-keyboard-swap", isEditorActive);
|
|
||||||
|
|
||||||
if (isEditorActive){
|
|
||||||
migrateEditorText();
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
me.composeInputOrig.focus();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.composeOldInputFocusEvent = function(){
|
|
||||||
if (!isEditorActive){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let val = $(this).val();
|
|
||||||
|
|
||||||
if (val.length === 0){
|
|
||||||
replaceEditor(false);
|
|
||||||
}
|
|
||||||
else if (val != prevOldInputVal){
|
|
||||||
setTimeout(migrateEditorText, 1);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
focusWithCaretAtEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
prevOldInputVal = val;
|
|
||||||
};
|
|
||||||
|
|
||||||
var allowedShortcuts = [ 65 /* A */, 67 /* C */, 86 /* V */, 89 /* Y */, 90 /* Z */ ];
|
|
||||||
|
|
||||||
this.composeInputKeyEvent = function(e){
|
|
||||||
if (e.type === "keydown" && (e.ctrlKey || e.metaKey)){
|
|
||||||
if (e.keyCode === 13){ // enter
|
|
||||||
$(".js-send-button", ".js-docked-compose").click();
|
|
||||||
}
|
|
||||||
else if (e.keyCode >= 48 && !allowedShortcuts.includes(e.keyCode)){
|
|
||||||
e.preventDefault();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.keyCode !== 27){ // escape
|
|
||||||
e.stopPropagation();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.composeInputUpdateEvent = function(){
|
|
||||||
let clone = $(this).clone();
|
|
||||||
|
|
||||||
clone.children("div").each(function(){
|
|
||||||
let ele = $(this)[0];
|
|
||||||
ele.outerHTML = "\n"+ele.innerHTML;
|
|
||||||
});
|
|
||||||
|
|
||||||
clone.children("img").each(function(){
|
|
||||||
let ele = $(this)[0];
|
|
||||||
ele.outerHTML = ele.getAttribute("alt");
|
|
||||||
});
|
|
||||||
|
|
||||||
me.composeInputOrig.val(prevOldInputVal = clone.text());
|
|
||||||
me.composeInputOrig.trigger("change");
|
|
||||||
|
|
||||||
if (prevOldInputVal.length === 0){
|
|
||||||
replaceEditor(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO if (!emoji.length){
|
|
||||||
let sel = window.getSelection();
|
|
||||||
let selStart = -1, selEnd = -1;
|
|
||||||
|
|
||||||
if ($(sel.anchorNode).closest("#emoji-keyboard-tweet-input").length && sel.rangeCount === 1){
|
|
||||||
let range = sel.getRangeAt(0);
|
|
||||||
// TODO figure out offset
|
|
||||||
}
|
|
||||||
|
|
||||||
replaceEditor(false);
|
|
||||||
me.composeInputOrig.focus();
|
|
||||||
|
|
||||||
if (selStart !== -1){
|
|
||||||
me.composeInputOrig[0].selectionStart = selStart;
|
|
||||||
me.composeInputOrig[0].selectionEnd = selEnd;
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
};
|
|
||||||
|
|
||||||
this.composeInputPasteEvent = function(e){ // contenteditable with <img alt> handles copying just fine
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
let text = e.originalEvent.clipboardData.getData("text/plain");
|
|
||||||
text && document.execCommand("insertText", false, text);
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO handle @ and hashtags
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ready(){
|
ready(){
|
||||||
@@ -448,24 +305,12 @@ ready(){
|
|||||||
this.composePanelScroller.on("scroll", this.composerScrollEvent);
|
this.composePanelScroller.on("scroll", this.composerScrollEvent);
|
||||||
|
|
||||||
$(".emoji-keyboard-popup-btn").on("click", this.emojiKeyboardButtonClickEvent);
|
$(".emoji-keyboard-popup-btn").on("click", this.emojiKeyboardButtonClickEvent);
|
||||||
|
$(".js-compose-text", ".js-docked-compose").on("focus", this.composeInputFocusEvent);
|
||||||
$(document).on("click", this.documentClickEvent);
|
$(document).on("click", this.documentClickEvent);
|
||||||
$(document).on("keydown", this.documentKeyEvent);
|
$(document).on("keydown", this.documentKeyEvent);
|
||||||
$(document).on("uiComposeImageAdded", this.uploadFilesEvent);
|
$(document).on("uiComposeImageAdded", this.uploadFilesEvent);
|
||||||
this.composeDrawer.on("uiComposeTweetSending", this.composerSendingEvent);
|
this.composeDrawer.on("uiComposeTweetSending", this.composerSendingEvent);
|
||||||
|
|
||||||
// Editor
|
|
||||||
|
|
||||||
this.composeInputOrig = $(".js-compose-text", ".js-docked-compose").first();
|
|
||||||
this.composeInputNew = $('<div id="emoji-keyboard-tweet-input" contenteditable="true" class="compose-text txt-size--14 scroll-v scroll-styled-v scroll-styled-h scroll-alt td-detect-image-paste"></div>').insertAfter(this.composeInputOrig);
|
|
||||||
|
|
||||||
if (this.ENABLE_CUSTOM_KEYBOARD){
|
|
||||||
this.composeInputOrig.on("focus", this.composeOldInputFocusEvent);
|
|
||||||
|
|
||||||
this.composeInputNew.on("keydown keypress keyup", this.composeInputKeyEvent);
|
|
||||||
this.composeInputNew.on("input", this.composeInputUpdateEvent);
|
|
||||||
this.composeInputNew.on("paste", this.composeInputPasteEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTML generation
|
// HTML generation
|
||||||
|
|
||||||
var convUnicode = function(codePt){
|
var convUnicode = function(codePt){
|
||||||
@@ -579,14 +424,12 @@ disabled(){
|
|||||||
$(this.currentSpanner).remove();
|
$(this.currentSpanner).remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.composeInputNew.remove();
|
|
||||||
|
|
||||||
this.composeInputOrig.off("focus", this.composeOldInputFocusEvent);
|
|
||||||
this.composePanelScroller.off("scroll", this.composerScrollEvent);
|
this.composePanelScroller.off("scroll", this.composerScrollEvent);
|
||||||
|
|
||||||
$(".emoji-keyboard-popup-btn").off("click", this.emojiKeyboardButtonClickEvent);
|
$(".emoji-keyboard-popup-btn").off("click", this.emojiKeyboardButtonClickEvent);
|
||||||
$(".emoji-keyboard-popup-btn").remove();
|
$(".emoji-keyboard-popup-btn").remove();
|
||||||
|
|
||||||
|
$(".js-compose-text", ".js-docked-compose").off("focus", this.composeInputFocusEvent);
|
||||||
$(document).off("click", this.documentClickEvent);
|
$(document).off("click", this.documentClickEvent);
|
||||||
$(document).off("keydown", this.documentKeyEvent);
|
$(document).off("keydown", this.documentKeyEvent);
|
||||||
$(document).off("uiComposeImageAdded", this.uploadFilesEvent);
|
$(document).off("uiComposeImageAdded", this.uploadFilesEvent);
|
||||||
|
@@ -6,9 +6,7 @@ enabled(){
|
|||||||
this.lastSelectedAccount = null;
|
this.lastSelectedAccount = null;
|
||||||
|
|
||||||
this.uiComposeTweetEvent = (e, data) => {
|
this.uiComposeTweetEvent = (e, data) => {
|
||||||
if (data.type !== "reply" || data.popFromInline || !("element" in data)){
|
return if data.type !== "reply" || data.popFromInline || !("element" in data);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var query;
|
var query;
|
||||||
|
|
||||||
@@ -83,9 +81,7 @@ enabled(){
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case "#last":
|
case "#last":
|
||||||
if (this.lastSelectedAccount === null){
|
return if this.lastSelectedAccount === null;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
identifier = this.lastSelectedAccount;
|
identifier = this.lastSelectedAccount;
|
||||||
break;
|
break;
|
||||||
|
@@ -177,7 +177,7 @@ enabled(){
|
|||||||
|
|
||||||
var useTemplate = (contents, append) => {
|
var useTemplate = (contents, append) => {
|
||||||
let ele = $(".js-compose-text");
|
let ele = $(".js-compose-text");
|
||||||
if (ele.length === 0)return;
|
return if ele.length === 0;
|
||||||
|
|
||||||
let value = append ? ele.val()+contents : contents;
|
let value = append ? ele.val()+contents : contents;
|
||||||
let prevLength = value.length;
|
let prevLength = value.length;
|
||||||
|
@@ -3,10 +3,21 @@ $ErrorActionPreference = "Stop"
|
|||||||
|
|
||||||
Set-Location $dir
|
Set-Location $dir
|
||||||
|
|
||||||
ForEach($file in Get-ChildItem -Include *.js, *.html -Recurse){
|
function Rewrite-File{
|
||||||
$lines = Get-Content -Path $file.FullName
|
[CmdletBinding()]
|
||||||
$lines = ($lines | % { $_.TrimStart() }) -Replace '^(.*?)((?<=^|[;{}()] )//\s.*)?$', '$1'
|
Param([Parameter(Mandatory = $True, ValueFromPipeline = $True)][array] $lines, [Parameter(Mandatory = $True, Position = 1)] $file)
|
||||||
$lines | Where { $_ -ne '' } | Set-Content -Path $file.FullName
|
|
||||||
|
|
||||||
|
$lines | Where { $_ -ne '' } | Set-Content -Path $file.FullName
|
||||||
Write-Host "Processed" $file.FullName.Substring($dir.Length)
|
Write-Host "Processed" $file.FullName.Substring($dir.Length)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ForEach($file in Get-ChildItem -Include *.js -Recurse){
|
||||||
|
$lines = Get-Content -Path $file.FullName
|
||||||
|
$lines = ($lines | % { $_.TrimStart() }) -Replace '^(.*?)((?<=^|[;{}()])\s?//(?:\s.*|$))?$', '$1' -Replace '(?<!\w)return(\s.*?)? if (.*?);', 'if ($2)return$1;'
|
||||||
|
,$lines | Rewrite-File $file
|
||||||
|
}
|
||||||
|
|
||||||
|
ForEach($file in Get-ChildItem -Include *.html -Recurse){
|
||||||
|
$lines = Get-Content -Path $file.FullName
|
||||||
|
,($lines | % { $_.TrimStart() }) | Rewrite-File $file
|
||||||
|
}
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
using CefSharp;
|
using CefSharp;
|
||||||
using CefSharp.WinForms;
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -21,27 +20,15 @@ namespace TweetDuck.Resources{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ExecuteFile(ChromiumWebBrowser browser, string file){
|
|
||||||
ExecuteScript(browser, LoadResource(file), GetRootIdentifier(file));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ExecuteFile(IFrame frame, string file){
|
public static void ExecuteFile(IFrame frame, string file){
|
||||||
ExecuteScript(frame, LoadResource(file), GetRootIdentifier(file));
|
ExecuteScript(frame, LoadResource(file), GetRootIdentifier(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ExecuteScript(ChromiumWebBrowser browser, string script, string identifier){
|
|
||||||
if (script == null)return;
|
|
||||||
|
|
||||||
using(IFrame frame = browser.GetMainFrame()){
|
|
||||||
frame.ExecuteJavaScriptAsync(script, UrlPrefix+identifier, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ExecuteScript(IFrame frame, string script, string identifier){
|
public static void ExecuteScript(IFrame frame, string script, string identifier){
|
||||||
if (script == null)return;
|
if (script != null){
|
||||||
|
|
||||||
frame.ExecuteJavaScriptAsync(script, UrlPrefix+identifier, 1);
|
frame.ExecuteJavaScriptAsync(script, UrlPrefix+identifier, 1);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static string GetRootIdentifier(string file){
|
public static string GetRootIdentifier(string file){
|
||||||
return "root:"+Path.GetFileNameWithoutExtension(file);
|
return "root:"+Path.GetFileNameWithoutExtension(file);
|
||||||
|
@@ -112,9 +112,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
let checkTweetCache = (set, id) => {
|
let checkTweetCache = (set, id) => {
|
||||||
if (set.has(id)){
|
return true if set.has(id);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (set.size > 50){
|
if (set.size > 50){
|
||||||
set.clear();
|
set.clear();
|
||||||
@@ -130,19 +128,17 @@
|
|||||||
|
|
||||||
return function(column, tweet){
|
return function(column, tweet){
|
||||||
if (tweet instanceof TD.services.TwitterConversation || tweet instanceof TD.services.TwitterConversationMessageEvent){
|
if (tweet instanceof TD.services.TwitterConversation || tweet instanceof TD.services.TwitterConversationMessageEvent){
|
||||||
if (checkTweetCache(recentMessages, tweet.id)){
|
return if checkTweetCache(recentMessages, tweet.id);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
else{
|
||||||
else if (checkTweetCache(recentTweets, tweet.id)){
|
return if checkTweetCache(recentTweets, tweet.id);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
startRecentTweetTimer();
|
startRecentTweetTimer();
|
||||||
|
|
||||||
if (column.model.getHasNotification()){
|
if (column.model.getHasNotification()){
|
||||||
let sensitive = (tweet.getRelatedTweet() && tweet.getRelatedTweet().possiblySensitive || (tweet.quotedTweet && tweet.quotedTweet.possiblySensitive));
|
let sensitive = (tweet.getRelatedTweet() && tweet.getRelatedTweet().possiblySensitive || (tweet.quotedTweet && tweet.quotedTweet.possiblySensitive));
|
||||||
let previews = $TDX.notificationMediaPreviews && !sensitive;
|
let previews = $TDX.notificationMediaPreviews && (!sensitive || TD.settings.getDisplaySensitiveMedia());
|
||||||
|
|
||||||
let html = $(tweet.render({
|
let html = $(tweet.render({
|
||||||
withFooter: false,
|
withFooter: false,
|
||||||
@@ -200,10 +196,12 @@
|
|||||||
|
|
||||||
let source = tweet.getRelatedTweet();
|
let source = tweet.getRelatedTweet();
|
||||||
let duration = source ? source.text.length+(source.quotedTweet ? source.quotedTweet.text.length : 0) : tweet.text.length;
|
let duration = source ? source.text.length+(source.quotedTweet ? source.quotedTweet.text.length : 0) : tweet.text.length;
|
||||||
|
|
||||||
|
let chirpId = source ? source.id : "";
|
||||||
let tweetUrl = source ? source.getChirpURL() : "";
|
let tweetUrl = source ? source.getChirpURL() : "";
|
||||||
let quoteUrl = source && source.quotedTweet ? source.quotedTweet.getChirpURL() : "";
|
let quoteUrl = source && source.quotedTweet ? source.quotedTweet.getChirpURL() : "";
|
||||||
|
|
||||||
$TD.onTweetPopup(columnTypes[column.getColumnType()] || "", html.html(), duration, tweetUrl, quoteUrl);
|
$TD.onTweetPopup(column.model.privateState.apiid, chirpId, columnTypes[column.getColumnType()] || "", html.html(), duration, tweetUrl, quoteUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (column.model.getHasSound()){
|
if (column.model.getHasSound()){
|
||||||
@@ -212,6 +210,53 @@
|
|||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Function: Shows tweet detail, used in notification context menu.
|
||||||
|
//
|
||||||
|
(function(){
|
||||||
|
var showTweetDetailInternal = function(column, chirp){
|
||||||
|
TD.ui.updates.showDetailView(column, chirp, column.findChirp(chirp) || chirp);
|
||||||
|
TD.controller.columnManager.showColumn(column.model.privateState.key);
|
||||||
|
|
||||||
|
$(document).trigger("uiGridClearSelection");
|
||||||
|
};
|
||||||
|
|
||||||
|
window.TDGF_showTweetDetail = function(columnId, chirpId, fallbackUrl){
|
||||||
|
if (!TD.ready){
|
||||||
|
onAppReady.push(function(){
|
||||||
|
window.TDGF_showTweetDetail(columnId, chirpId, fallbackUrl);
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let column = TD.controller.columnManager.getByApiid(columnId);
|
||||||
|
|
||||||
|
if (!column){
|
||||||
|
if (confirm("error|The column which contained the tweet no longer exists. Would you like to open the tweet in your browser instead?")){
|
||||||
|
$TD.openBrowser(fallbackUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let chirp = column.findMostInterestingChirp(chirpId);
|
||||||
|
|
||||||
|
if (chirp){
|
||||||
|
showTweetDetailInternal(column, chirp);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
TD.controller.clients.getPreferredClient().show(chirpId, function(chirp){
|
||||||
|
showTweetDetailInternal(column, chirp);
|
||||||
|
}, function(){
|
||||||
|
if (confirm("error|Could not retrieve the requested tweet. Would you like to open the tweet in your browser instead?")){
|
||||||
|
$TD.openBrowser(fallbackUrl);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
//
|
//
|
||||||
// Function: Retrieves the tags to be put into <head> for notification HTML code.
|
// Function: Retrieves the tags to be put into <head> for notification HTML code.
|
||||||
//
|
//
|
||||||
@@ -225,7 +270,7 @@
|
|||||||
tags.push("<style type='text/css'>");
|
tags.push("<style type='text/css'>");
|
||||||
tags.push("body { background: "+getClassStyleProperty("column", "background-color")+" }"); // set background color
|
tags.push("body { background: "+getClassStyleProperty("column", "background-color")+" }"); // set background color
|
||||||
tags.push("a[data-full-url] { word-break: break-all }"); // break long urls
|
tags.push("a[data-full-url] { word-break: break-all }"); // break long urls
|
||||||
tags.push(".txt-base-smallest .badge-verified:before { height: 13px !important }"); // fix cut off badge icon
|
tags.push(".txt-base-smallest .badge-verified:before { width: 13px !important; height: 13px !important; background-position: -223px -98px !important; }"); // fix cut off badge icon
|
||||||
tags.push(".activity-header { align-items: center !important; margin-bottom: 4px }"); // tweak alignment of avatar and text in notifications
|
tags.push(".activity-header { align-items: center !important; margin-bottom: 4px }"); // tweak alignment of avatar and text in notifications
|
||||||
tags.push(".activity-header .tweet-timestamp { line-height: unset }"); // fix timestamp position in notifications
|
tags.push(".activity-header .tweet-timestamp { line-height: unset }"); // fix timestamp position in notifications
|
||||||
tags.push("</style>");
|
tags.push("</style>");
|
||||||
@@ -281,7 +326,7 @@
|
|||||||
$("[data-action='settings-menu']").click(function(){
|
$("[data-action='settings-menu']").click(function(){
|
||||||
setTimeout(function(){
|
setTimeout(function(){
|
||||||
let menu = $(".js-dropdown-content").children("ul").first();
|
let menu = $(".js-dropdown-content").children("ul").first();
|
||||||
if (menu.length === 0)return;
|
return if menu.length === 0;
|
||||||
|
|
||||||
menu.children(".drp-h-divider").last().before('<li class="is-selectable" data-std><a href="#" data-action="tweetduck">TweetDuck</a></li>');
|
menu.children(".drp-h-divider").last().before('<li class="is-selectable" data-std><a href="#" data-action="tweetduck">TweetDuck</a></li>');
|
||||||
|
|
||||||
@@ -311,15 +356,11 @@
|
|||||||
var prevMouseX = -1, prevMouseY = -1;
|
var prevMouseX = -1, prevMouseY = -1;
|
||||||
var tooltipTimer, tooltipDisplayed;
|
var tooltipTimer, tooltipDisplayed;
|
||||||
|
|
||||||
$(document.body).delegate("a[data-full-url]", "mouseenter mouseleave mousemove", function(e){
|
$(document.body).delegate("a[data-full-url]", {
|
||||||
var me = $(this);
|
mouseenter: function(){
|
||||||
|
let me = $(this);
|
||||||
if (e.type === "mouseenter"){
|
|
||||||
let text = me.text();
|
let text = me.text();
|
||||||
|
return if text.charCodeAt(text.length-1) !== 8230; // horizontal ellipsis
|
||||||
if (text.charCodeAt(text.length-1) !== 8230){ // horizontal ellipsis
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($TDX.expandLinksOnHover){
|
if ($TDX.expandLinksOnHover){
|
||||||
tooltipTimer = window.setTimeout(function(){
|
tooltipTimer = window.setTimeout(function(){
|
||||||
@@ -338,14 +379,13 @@
|
|||||||
tooltipDisplayed = true;
|
tooltipDisplayed = true;
|
||||||
}, 400);
|
}, 400);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
else if (e.type === "mouseleave"){
|
|
||||||
if ($TDX.expandLinksOnHover){
|
|
||||||
let prevText = me.attr("td-prev-text");
|
|
||||||
|
|
||||||
if (prevText){
|
mouseleave: function(){
|
||||||
me.text(prevText);
|
let me = $(this);
|
||||||
}
|
|
||||||
|
if (me[0].hasAttribute("td-prev-text")){
|
||||||
|
me.text(me.attr("td-prev-text"));
|
||||||
}
|
}
|
||||||
|
|
||||||
window.clearTimeout(tooltipTimer);
|
window.clearTimeout(tooltipTimer);
|
||||||
@@ -354,10 +394,11 @@
|
|||||||
tooltipDisplayed = false;
|
tooltipDisplayed = false;
|
||||||
$TD.displayTooltip(null, false);
|
$TD.displayTooltip(null, false);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
else if (e.type === "mousemove"){
|
|
||||||
|
mousemove: function(e){
|
||||||
if (tooltipDisplayed && (prevMouseX !== e.clientX || prevMouseY !== e.clientY)){
|
if (tooltipDisplayed && (prevMouseX !== e.clientX || prevMouseY !== e.clientY)){
|
||||||
$TD.displayTooltip(me.attr("data-full-url"), false);
|
$TD.displayTooltip($(this).attr("data-full-url"), false);
|
||||||
prevMouseX = e.clientX;
|
prevMouseX = e.clientX;
|
||||||
prevMouseY = e.clientY;
|
prevMouseY = e.clientY;
|
||||||
}
|
}
|
||||||
@@ -366,33 +407,77 @@
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Allow bypassing of t.co and include media previews in context menus.
|
// Block: Bypass t.co when clicking links and media.
|
||||||
//
|
//
|
||||||
$(document.body).delegate("a", "contextmenu", function(){
|
$(document.body).delegate("a[data-full-url]", "click", function(e){
|
||||||
$TD.setLastRightClickedLink($(this).attr("data-full-url") || "");
|
$TD.openBrowser($(this).attr("data-full-url"));
|
||||||
|
e.preventDefault();
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document.body).delegate("a.js-media-image-link", "contextmenu", function(){
|
if (ensurePropertyExists(TD, "services", "TwitterUser", "prototype", "fromJSONObject")){
|
||||||
|
let prevFunc = TD.services.TwitterUser.prototype.fromJSONObject;
|
||||||
|
|
||||||
|
TD.services.TwitterUser.prototype.fromJSONObject = function(){
|
||||||
|
let obj = prevFunc.apply(this, arguments);
|
||||||
|
let e = arguments[0].entities;
|
||||||
|
|
||||||
|
if (e && e.url && e.url.urls && e.url.urls.length && e.url.urls[0].expanded_url){
|
||||||
|
obj.url = e.url.urls[0].expanded_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ensurePropertyExists(TD, "services", "TwitterMedia", "prototype", "fromMediaEntity")){
|
||||||
|
let prevFunc = TD.services.TwitterMedia.prototype.fromMediaEntity;
|
||||||
|
|
||||||
|
TD.services.TwitterMedia.prototype.fromMediaEntity = function(){
|
||||||
|
let obj = prevFunc.apply(this, arguments);
|
||||||
|
let e = arguments[0];
|
||||||
|
|
||||||
|
if (e.expanded_url){
|
||||||
|
if (obj.url === obj.shortUrl){
|
||||||
|
obj.shortUrl = e.expanded_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.url = e.expanded_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Include additional information in context menus.
|
||||||
|
//
|
||||||
|
$(document.body).delegate("a", "contextmenu", function(){
|
||||||
let me = $(this)[0];
|
let me = $(this)[0];
|
||||||
|
|
||||||
if (me.firstElementChild){
|
if (me.classList.contains("js-media-image-link") && highlightedTweetObj){
|
||||||
$TD.setLastRightClickedImage(me.firstElementChild.getAttribute("src"));
|
let media = (highlightedTweetObj.quotedTweet || highlightedTweetObj).getMedia().find(media => media.mediaId === me.getAttribute("data-media-entity-id"));
|
||||||
|
|
||||||
|
if ((media.isVideo && media.service === "twitter") || media.isAnimatedGif){
|
||||||
|
$TD.setLastRightClickInfo("video", media.chooseVideoVariant().url);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
$TD.setLastRightClickedImage(me.style.backgroundImage.replace(/url\(['"]?(.*?)['"]?\)/, "$1"));
|
$TD.setLastRightClickInfo("image", media.large());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (me.classList.contains("js-gif-play")){
|
||||||
|
$TD.setLastRightClickInfo("video", $(this).closest(".js-media-gif-container").find("video").attr("src"));
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
$TD.setLastRightClickInfo("link", me.getAttribute("data-full-url"));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Hook into the notification sound effect.
|
// Block: Hook into the notification sound effect.
|
||||||
//
|
//
|
||||||
(function(){
|
HTMLAudioElement.prototype.play = prependToFunction(HTMLAudioElement.prototype.play, function(){
|
||||||
let soundEle = document.getElementById("update-sound");
|
|
||||||
|
|
||||||
soundEle.play = prependToFunction(soundEle.play, function(){
|
|
||||||
return $TDX.muteNotifications || $TDX.hasCustomNotificationSound;
|
return $TDX.muteNotifications || $TDX.hasCustomNotificationSound;
|
||||||
});
|
});
|
||||||
})();
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Update highlighted column and tweet for context menu and other functionality.
|
// Block: Update highlighted column and tweet for context menu and other functionality.
|
||||||
@@ -406,53 +491,54 @@
|
|||||||
return !!highlightedColumnObj;
|
return !!highlightedColumnObj;
|
||||||
};
|
};
|
||||||
|
|
||||||
var updateHighlightedTweet = function(ele, obj, link, embeddedLink, author, imageList){
|
var updateHighlightedTweet = function(ele, obj, tweetUrl, quoteUrl, authors, imageList){
|
||||||
highlightedTweetEle = ele;
|
highlightedTweetEle = ele;
|
||||||
highlightedTweetObj = obj;
|
highlightedTweetObj = obj;
|
||||||
|
|
||||||
if (lastTweet !== link){
|
if (lastTweet !== tweetUrl){
|
||||||
$TD.setLastHighlightedTweet(link, embeddedLink, author, imageList);
|
$TD.setLastHighlightedTweet(tweetUrl, quoteUrl, authors, imageList);
|
||||||
lastTweet = link;
|
lastTweet = tweetUrl;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
app.delegate("section.js-column", "mouseenter mouseleave", function(e){
|
var processMedia = function(media){
|
||||||
if (e.type === "mouseenter"){
|
return media.filter(item => !item.isAnimatedGif).map(item => item.entity.media_url_https+":small").join(";");
|
||||||
|
};
|
||||||
|
|
||||||
|
app.delegate("section.js-column", {
|
||||||
|
mouseenter: function(){
|
||||||
if (!highlightedColumnObj){
|
if (!highlightedColumnObj){
|
||||||
updateHighlightedColumn($(this));
|
updateHighlightedColumn($(this));
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
else if (e.type === "mouseleave"){
|
|
||||||
|
mouseleave: function(){
|
||||||
updateHighlightedColumn(null);
|
updateHighlightedColumn(null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.delegate("article.js-stream-item", "mouseenter mouseleave", function(e){
|
app.delegate("article.js-stream-item", {
|
||||||
if (e.type === "mouseenter"){
|
mouseenter: function(){
|
||||||
let me = $(this);
|
let me = $(this);
|
||||||
|
return if !me[0].hasAttribute("data-account-key") || (!highlightedColumnObj && !updateHighlightedColumn(me.closest("section.js-column")));
|
||||||
if (!me[0].hasAttribute("data-account-key") || (!highlightedColumnObj && !updateHighlightedColumn(me.closest("section.js-column")))){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let tweet = highlightedColumnObj.findChirp(me.attr("data-tweet-id")) || highlightedColumnObj.findChirp(me.attr("data-key"));
|
let tweet = highlightedColumnObj.findChirp(me.attr("data-tweet-id")) || highlightedColumnObj.findChirp(me.attr("data-key"));
|
||||||
|
return if !tweet;
|
||||||
|
|
||||||
if (tweet){
|
|
||||||
if (tweet.chirpType === TD.services.ChirpBase.TWEET){
|
if (tweet.chirpType === TD.services.ChirpBase.TWEET){
|
||||||
let link = tweet.getChirpURL();
|
let tweetUrl = tweet.getChirpURL();
|
||||||
let embedded = tweet.quotedTweet ? tweet.quotedTweet.getChirpURL() : "";
|
let quoteUrl = tweet.quotedTweet ? tweet.quotedTweet.getChirpURL() : "";
|
||||||
let username = tweet.getMainUser().screenName;
|
let authors = tweet.quotedTweet ? [ tweet.getMainUser().screenName, tweet.quotedTweet.getMainUser().screenName ].join(";") : tweet.getMainUser().screenName;
|
||||||
let images = tweet.hasImage() ? tweet.getMedia().filter(item => !item.isAnimatedGif).map(item => item.entity.media_url_https+":small").join(";") : "";
|
let imageList = tweet.quotedTweet ? processMedia(tweet.quotedTweet.getMedia()) : tweet.hasImage() ? processMedia(tweet.getMedia()) : "";
|
||||||
// TODO maybe handle embedded images too?
|
|
||||||
|
|
||||||
updateHighlightedTweet(me, tweet, link || "", embedded || "", username, images);
|
updateHighlightedTweet(me, tweet, tweetUrl || "", quoteUrl || "", authors, imageList);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
updateHighlightedTweet(me, tweet, "", "", "", "");
|
updateHighlightedTweet(me, tweet, "", "", "", "");
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
|
||||||
else if (e.type === "mouseleave"){
|
mouseleave: function(){
|
||||||
updateHighlightedTweet(null, null, "", "", "", "");
|
updateHighlightedTweet(null, null, "", "", "", "");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -538,9 +624,7 @@
|
|||||||
for(let item of e.originalEvent.clipboardData.items){
|
for(let item of e.originalEvent.clipboardData.items){
|
||||||
if (item.type.startsWith("image/")){
|
if (item.type.startsWith("image/")){
|
||||||
if (!$(this).closest(".rpl").find(".js-reply-popout").click().length){ // popout direct messages
|
if (!$(this).closest(".rpl").find(".js-reply-popout").click().length){ // popout direct messages
|
||||||
if ($(".js-add-image-button").is(".is-disabled")){ // tweetdeck does not check upload count properly
|
return if $(".js-add-image-button").is(".is-disabled"); // tweetdeck does not check upload count properly
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uploader.addFilesToUpload([ item.getAsFile() ]);
|
uploader.addFilesToUpload([ item.getAsFile() ]);
|
||||||
@@ -639,9 +723,7 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!ensurePropertyExists(TD, "vo", "Column", "prototype", "clear")){
|
return if !ensurePropertyExists(TD, "vo", "Column", "prototype", "clear");
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
TD.vo.Column.prototype.clear = prependToFunction(TD.vo.Column.prototype.clear, function(){
|
TD.vo.Column.prototype.clear = prependToFunction(TD.vo.Column.prototype.clear, function(){
|
||||||
window.setTimeout(resetActiveFocus, 0); // unfocuses the Clear button, otherwise it steals keyboard input
|
window.setTimeout(resetActiveFocus, 0); // unfocuses the Clear button, otherwise it steals keyboard input
|
||||||
@@ -668,9 +750,7 @@
|
|||||||
|
|
||||||
$(".js-drawer[data-drawer='compose']").delegate(".js-account-list > .js-account-item", "click", onAccountClick);
|
$(".js-drawer[data-drawer='compose']").delegate(".js-account-list > .js-account-item", "click", onAccountClick);
|
||||||
|
|
||||||
if (!ensurePropertyExists(TD, "components", "AccountSelector", "prototype", "refreshPostingAccounts")){
|
return if !ensurePropertyExists(TD, "components", "AccountSelector", "prototype", "refreshPostingAccounts");
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
TD.components.AccountSelector.prototype.refreshPostingAccounts = appendToFunction(TD.components.AccountSelector.prototype.refreshPostingAccounts, function(){
|
TD.components.AccountSelector.prototype.refreshPostingAccounts = appendToFunction(TD.components.AccountSelector.prototype.refreshPostingAccounts, function(){
|
||||||
if (!this.$node.attr("td-account-selector-hook")){
|
if (!this.$node.attr("td-account-selector-hook")){
|
||||||
@@ -726,9 +806,10 @@
|
|||||||
|
|
||||||
addRule(".column-nav-link .attribution { position: absolute; }"); // fix cut off account names
|
addRule(".column-nav-link .attribution { position: absolute; }"); // fix cut off account names
|
||||||
addRule(".txt-base-smallest .sprite-verified-mini { width: 13px !important; height: 13px !important; background-position: -223px -99px !important; }"); // fix cut off badge icon when zoomed in
|
addRule(".txt-base-smallest .sprite-verified-mini { width: 13px !important; height: 13px !important; background-position: -223px -99px !important; }"); // fix cut off badge icon when zoomed in
|
||||||
|
addRule(".txt-base-smallest .badge-verified:before { width: 13px !important; height: 13px !important; background-position: -223px -98px !important; }"); // fix cut off badge icon in notifications
|
||||||
|
|
||||||
addRule(".btn, .mdl, .mdl-content, .app-search-fake, .app-search-input, .popover, .lst-modal, .media-item, .media-preview, .tooltip-inner { border-radius: 1px !important; }"); // square-ify buttons, inputs, dialogs, menus, media previews
|
addRule(".btn, .mdl, .mdl-content, .app-search-fake, .app-search-input, .popover, .lst-modal, .media-item, .media-preview, .tooltip-inner { border-radius: 1px !important; }"); // square-ify buttons, dialogs, menus, media previews
|
||||||
addRule(".compose-text-container, .dropdown-menu, .list-item-last, .quoted-tweet { border-radius: 0 !important; }"); // square-ify dropdowns, quoted tweets, and account selectors
|
addRule(".compose-text-container, .dropdown-menu, .list-item-last, .quoted-tweet, .input-group-button, input, textarea, select { border-radius: 0 !important; }"); // square-ify dropdowns, inputs, quoted tweets, and account selectors
|
||||||
addRule(".prf-header { border-radius: 0; }"); // fix user account header border
|
addRule(".prf-header { border-radius: 0; }"); // fix user account header border
|
||||||
|
|
||||||
addRule(".accs li, .accs img { border-radius: 0 !important; }"); // square-ify retweet account selector
|
addRule(".accs li, .accs img { border-radius: 0 !important; }"); // square-ify retweet account selector
|
||||||
@@ -839,7 +920,7 @@
|
|||||||
TD.components.MediaGallery.prototype._loadTweet = appendToFunction(TD.components.MediaGallery.prototype._loadTweet, function(){
|
TD.components.MediaGallery.prototype._loadTweet = appendToFunction(TD.components.MediaGallery.prototype._loadTweet, function(){
|
||||||
let media = this.chirp.getMedia().find(media => media.mediaId === this.clickedMediaEntityId);
|
let media = this.chirp.getMedia().find(media => media.mediaId === this.clickedMediaEntityId);
|
||||||
|
|
||||||
if (media && media.isVideo && media.service !== "youtube"){
|
if (media && media.isVideo && media.service === "twitter"){
|
||||||
playVideo(media.chooseVideoVariant().url);
|
playVideo(media.chooseVideoVariant().url);
|
||||||
cancelModal = true;
|
cancelModal = true;
|
||||||
}
|
}
|
||||||
@@ -923,6 +1004,44 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Detect and notify about connection issues.
|
||||||
|
//
|
||||||
|
(function(){
|
||||||
|
let onConnectionError = function(){
|
||||||
|
return if $("#tweetduck-conn-issues").length;
|
||||||
|
|
||||||
|
let ele = $(`
|
||||||
|
<div id="tweetduck-conn-issues" class="Layer NotificationListLayer">
|
||||||
|
<ul class="NotificationList">
|
||||||
|
<li class="Notification Notification--red" style="height:63px;">
|
||||||
|
<div class="Notification-inner">
|
||||||
|
<div class="Notification-icon"><span class="Icon Icon--medium Icon--circleError"></span></div>
|
||||||
|
<div class="Notification-content"><div class="Notification-body">Experiencing connection issues</div></div>
|
||||||
|
<button type="button" class="Notification-closeButton" aria-label="Close"><span class="Icon Icon--smallest Icon--close" aria-hidden="true"></span></button>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
`).appendTo(document.body);
|
||||||
|
|
||||||
|
ele.find("button").click(function(){
|
||||||
|
ele.fadeOut(200);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
let onConnectionFine = function(){
|
||||||
|
let ele = $("#tweetduck-conn-issues");
|
||||||
|
|
||||||
|
ele.fadeOut(200, function(){
|
||||||
|
ele.remove();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("offline", onConnectionError);
|
||||||
|
window.addEventListener("online", onConnectionFine);
|
||||||
|
})();
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Custom reload function with memory cleanup.
|
// Block: Custom reload function with memory cleanup.
|
||||||
//
|
//
|
||||||
@@ -938,6 +1057,12 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
window.TDGF_tryRunCleanup = function(){
|
window.TDGF_tryRunCleanup = function(){
|
||||||
|
// no modals are visible
|
||||||
|
return false if $("#open-modal").is(":visible") || !$(".js-modals-container").is(":empty");
|
||||||
|
|
||||||
|
// all columns are in a default state
|
||||||
|
return false if $("section.js-column").is(".is-shifted-1,.is-shifted-2");
|
||||||
|
|
||||||
// all textareas are empty
|
// all textareas are empty
|
||||||
if ($("textarea").is(function(){
|
if ($("textarea").is(function(){
|
||||||
return $(this).val().length > 0;
|
return $(this).val().length > 0;
|
||||||
@@ -945,16 +1070,6 @@
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// no modals are visible
|
|
||||||
if ($("#open-modal").is(":visible") || !$(".js-modals-container").is(":empty")){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// all columns are in a default state
|
|
||||||
if ($("section.js-column").is(".is-shifted-1,.is-shifted-2")){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// all columns are scrolled to top
|
// all columns are scrolled to top
|
||||||
if ($(".js-column-scroller").is(function(){
|
if ($(".js-column-scroller").is(function(){
|
||||||
return $(this).scrollTop() > 0;
|
return $(this).scrollTop() > 0;
|
||||||
|
@@ -14,14 +14,16 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Hook into links to bypass default open function.
|
// Block: Hook into links to bypass default open function and t.co.
|
||||||
//
|
//
|
||||||
addEventListener(links, "click", function(e){
|
addEventListener(links, "click", function(e){
|
||||||
$TD.openBrowser(e.currentTarget.getAttribute("href"));
|
let ele = e.currentTarget;
|
||||||
|
|
||||||
|
$TD.openBrowser(ele.hasAttribute("data-full-url") ? ele.getAttribute("data-full-url") : ele.getAttribute("href"));
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
if ($TDX.skipOnLinkClick){
|
if ($TDX.skipOnLinkClick){
|
||||||
let parentClasses = e.currentTarget.parentNode.classList;
|
let parentClasses = ele.parentNode.classList;
|
||||||
|
|
||||||
if (parentClasses.contains("js-tweet-text") || parentClasses.contains("js-quoted-tweet-text") || parentClasses.contains("js-timestamp")){
|
if (parentClasses.contains("js-tweet-text") || parentClasses.contains("js-quoted-tweet-text") || parentClasses.contains("js-timestamp")){
|
||||||
$TD.loadNextNotification();
|
$TD.loadNextNotification();
|
||||||
@@ -33,7 +35,7 @@
|
|||||||
// Block: Allow bypassing of t.co in context menus.
|
// Block: Allow bypassing of t.co in context menus.
|
||||||
//
|
//
|
||||||
addEventListener(links, "contextmenu", function(e){
|
addEventListener(links, "contextmenu", function(e){
|
||||||
$TD.setLastRightClickedLink(e.currentTarget.getAttribute("data-full-url") || "");
|
$TD.setLastRightClickInfo("link", e.currentTarget.getAttribute("data-full-url"));
|
||||||
});
|
});
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -51,13 +53,10 @@
|
|||||||
var me = e.currentTarget;
|
var me = e.currentTarget;
|
||||||
|
|
||||||
var url = me.getAttribute("data-full-url");
|
var url = me.getAttribute("data-full-url");
|
||||||
if (!url)return;
|
return if !url;
|
||||||
|
|
||||||
var text = me.textContent;
|
var text = me.textContent;
|
||||||
|
return if text.charCodeAt(text.length-1) !== 8230; // horizontal ellipsis
|
||||||
if (text.charCodeAt(text.length-1) !== 8230){ // horizontal ellipsis
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($TDX.expandLinksOnHover){
|
if ($TDX.expandLinksOnHover){
|
||||||
tooltipTimer = window.setTimeout(function(){
|
tooltipTimer = window.setTimeout(function(){
|
||||||
@@ -79,7 +78,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
addEventListener(links, "mouseleave", function(e){
|
addEventListener(links, "mouseleave", function(e){
|
||||||
if (!e.currentTarget.hasAttribute("data-full-url"))return;
|
return if !e.currentTarget.hasAttribute("data-full-url");
|
||||||
|
|
||||||
if ($TDX.expandLinksOnHover){
|
if ($TDX.expandLinksOnHover){
|
||||||
var prevText = e.currentTarget.getAttribute("td-prev-text");
|
var prevText = e.currentTarget.getAttribute("td-prev-text");
|
||||||
@@ -100,7 +99,7 @@
|
|||||||
addEventListener(links, "mousemove", function(e){
|
addEventListener(links, "mousemove", function(e){
|
||||||
if (tooltipDisplayed && (prevMouseX !== e.clientX || prevMouseY !== e.clientY)){
|
if (tooltipDisplayed && (prevMouseX !== e.clientX || prevMouseY !== e.clientY)){
|
||||||
var url = e.currentTarget.getAttribute("data-full-url");
|
var url = e.currentTarget.getAttribute("data-full-url");
|
||||||
if (!url)return;
|
return if !url;
|
||||||
|
|
||||||
$TD.displayTooltip(url, true);
|
$TD.displayTooltip(url, true);
|
||||||
prevMouseX = e.clientX;
|
prevMouseX = e.clientX;
|
||||||
@@ -112,11 +111,7 @@
|
|||||||
//
|
//
|
||||||
// Block: Setup a skip button.
|
// Block: Setup a skip button.
|
||||||
//
|
//
|
||||||
(function(){
|
if (!document.body.hasAttribute("td-example-notification")){
|
||||||
if (document.body.hasAttribute("td-example-notification")){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.body.insertAdjacentHTML("afterbegin", [
|
document.body.insertAdjacentHTML("afterbegin", [
|
||||||
'<svg id="td-skip" xmlns="http://www.w3.org/2000/svg" width="10" height="17" viewBox="0 0 350 600" style="position:fixed;left:30px;bottom:10px;z-index:1000">',
|
'<svg id="td-skip" xmlns="http://www.w3.org/2000/svg" width="10" height="17" viewBox="0 0 350 600" style="position:fixed;left:30px;bottom:10px;z-index:1000">',
|
||||||
'<path fill="#888" d="M0,151.656l102.208-102.22l247.777,247.775L102.208,544.986L0,442.758l145.546-145.547">',
|
'<path fill="#888" d="M0,151.656l102.208-102.22l247.777,247.775L102.208,544.986L0,442.758l145.546-145.547">',
|
||||||
@@ -126,7 +121,7 @@
|
|||||||
document.getElementById("td-skip").addEventListener("click", function(){
|
document.getElementById("td-skip").addEventListener("click", function(){
|
||||||
$TD.loadNextNotification();
|
$TD.loadNextNotification();
|
||||||
});
|
});
|
||||||
})();
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Setup a hover class on body.
|
// Block: Setup a hover class on body.
|
||||||
|
@@ -39,4 +39,25 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
setTimeout(injectCSS, 1);
|
setTimeout(injectCSS, 1);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Make login page links external.
|
||||||
|
//
|
||||||
|
if (location.pathname === "/login"){
|
||||||
|
document.addEventListener("DOMContentLoaded", function(){
|
||||||
|
let openLinkExternally = function(e){
|
||||||
|
let href = e.currentTarget.getAttribute("href");
|
||||||
|
$TD.openBrowser(href[0] === '/' ? location.origin+href : href);
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
};
|
||||||
|
|
||||||
|
let links = document.getElementsByTagName("A");
|
||||||
|
|
||||||
|
for(let index = 0; index < links.length; index++){
|
||||||
|
links[index].addEventListener("click", openLinkExternally);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
|
@@ -28,8 +28,6 @@
|
|||||||
// Function: Creates the update notification element. Removes the old one if already exists.
|
// Function: Creates the update notification element. Removes the old one if already exists.
|
||||||
//
|
//
|
||||||
var displayNotification = function(version, download, changelog){
|
var displayNotification = function(version, download, changelog){
|
||||||
var outdated = version === "unsupported";
|
|
||||||
|
|
||||||
// styles
|
// styles
|
||||||
var css = $("#tweetduck-update-css");
|
var css = $("#tweetduck-update-css");
|
||||||
|
|
||||||
@@ -183,16 +181,7 @@
|
|||||||
ele.remove();
|
ele.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
ele = $(outdated ? `
|
ele = $(`
|
||||||
<div id='tweetduck-update'>
|
|
||||||
<p class='tdu-title'>Unsupported System</p>
|
|
||||||
<p class='tdu-info'>You will not receive updates</p>
|
|
||||||
<div class='tdu-buttons'>
|
|
||||||
<button class='tdu-btn-unsupported'>Read more</button>
|
|
||||||
<button class='tdu-btn-ignore'>Dismiss</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
` : `
|
|
||||||
<div id='tweetduck-update'>
|
<div id='tweetduck-update'>
|
||||||
<p class='tdu-title'>T weetDuck Update ${version}</p>
|
<p class='tdu-title'>T weetDuck Update ${version}</p>
|
||||||
<p class='tdu-info tdu-showlog'>View update information</p>
|
<p class='tdu-info tdu-showlog'>View update information</p>
|
||||||
@@ -244,11 +233,7 @@
|
|||||||
slide();
|
slide();
|
||||||
});
|
});
|
||||||
|
|
||||||
buttonDiv.children(".tdu-btn-unsupported").click(function(){
|
buttonDiv.children(".tdu-btn-ignore").click(function(){
|
||||||
$TDU.openBrowser("https://github.com/chylex/TweetDuck/wiki/Supported-Systems");
|
|
||||||
});
|
|
||||||
|
|
||||||
buttonDiv.children(".tdu-btn-ignore,.tdu-btn-unsupported").click(function(){
|
|
||||||
$TDU.onUpdateDismissed();
|
$TDU.onUpdateDismissed();
|
||||||
slide();
|
slide();
|
||||||
});
|
});
|
||||||
@@ -318,6 +303,5 @@
|
|||||||
//
|
//
|
||||||
// Block: Setup global functions.
|
// Block: Setup global functions.
|
||||||
//
|
//
|
||||||
window.TDUF_displayNotification = displayNotification;
|
|
||||||
window.TDUF_runUpdateCheck = runUpdateCheck;
|
window.TDUF_runUpdateCheck = runUpdateCheck;
|
||||||
})($, $TDU);
|
})($, $TDU);
|
||||||
|
@@ -73,7 +73,6 @@
|
|||||||
<Compile Include="Configuration\LockManager.cs" />
|
<Compile Include="Configuration\LockManager.cs" />
|
||||||
<Compile Include="Configuration\SystemConfig.cs" />
|
<Compile Include="Configuration\SystemConfig.cs" />
|
||||||
<Compile Include="Configuration\UserConfig.cs" />
|
<Compile Include="Configuration\UserConfig.cs" />
|
||||||
<Compile Include="Configuration\UserConfigLegacy.cs" />
|
|
||||||
<Compile Include="Core\Bridge\PropertyBridge.cs" />
|
<Compile Include="Core\Bridge\PropertyBridge.cs" />
|
||||||
<Compile Include="Core\Controls\ControlExtensions.cs" />
|
<Compile Include="Core\Controls\ControlExtensions.cs" />
|
||||||
<Compile Include="Core\Controls\FlatButton.cs">
|
<Compile Include="Core\Controls\FlatButton.cs">
|
||||||
@@ -98,6 +97,7 @@
|
|||||||
<Compile Include="Core\FormBrowser.Designer.cs">
|
<Compile Include="Core\FormBrowser.Designer.cs">
|
||||||
<DependentUpon>FormBrowser.cs</DependentUpon>
|
<DependentUpon>FormBrowser.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Core\Handling\General\FileDialogHandler.cs" />
|
||||||
<Compile Include="Core\Handling\KeyboardHandlerBrowser.cs" />
|
<Compile Include="Core\Handling\KeyboardHandlerBrowser.cs" />
|
||||||
<Compile Include="Core\Handling\KeyboardHandlerNotification.cs" />
|
<Compile Include="Core\Handling\KeyboardHandlerNotification.cs" />
|
||||||
<Compile Include="Core\Handling\RequestHandlerBrowser.cs" />
|
<Compile Include="Core\Handling\RequestHandlerBrowser.cs" />
|
||||||
@@ -243,7 +243,6 @@
|
|||||||
<Compile Include="Plugins\PluginManager.cs" />
|
<Compile Include="Plugins\PluginManager.cs" />
|
||||||
<Compile Include="Plugins\PluginScriptGenerator.cs" />
|
<Compile Include="Plugins\PluginScriptGenerator.cs" />
|
||||||
<Compile Include="Reporter.cs" />
|
<Compile Include="Reporter.cs" />
|
||||||
<Compile Include="Updates\Events\UpdateDismissedEventArgs.cs" />
|
|
||||||
<Compile Include="Updates\FormUpdateDownload.cs">
|
<Compile Include="Updates\FormUpdateDownload.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -259,8 +258,6 @@
|
|||||||
<Compile Include="Core\Utils\BrowserCache.cs" />
|
<Compile Include="Core\Utils\BrowserCache.cs" />
|
||||||
<Compile Include="Core\Utils\BrowserUtils.cs" />
|
<Compile Include="Core\Utils\BrowserUtils.cs" />
|
||||||
<Compile Include="Core\Utils\NativeMethods.cs" />
|
<Compile Include="Core\Utils\NativeMethods.cs" />
|
||||||
<Compile Include="Updates\Events\UpdateAcceptedEventArgs.cs" />
|
|
||||||
<Compile Include="Updates\Events\UpdateCheckEventArgs.cs" />
|
|
||||||
<Compile Include="Updates\UpdateDownloadStatus.cs" />
|
<Compile Include="Updates\UpdateDownloadStatus.cs" />
|
||||||
<Compile Include="Updates\UpdateHandler.cs" />
|
<Compile Include="Updates\UpdateHandler.cs" />
|
||||||
<Compile Include="Updates\UpdateInfo.cs" />
|
<Compile Include="Updates\UpdateInfo.cs" />
|
||||||
@@ -272,6 +269,7 @@
|
|||||||
<DependentUpon>Resources.resx</DependentUpon>
|
<DependentUpon>Resources.resx</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Resources\ScriptLoader.cs" />
|
<Compile Include="Resources\ScriptLoader.cs" />
|
||||||
|
<Compile Include="Updates\UpdateEventArgs.cs" />
|
||||||
<Compile Include="Updates\UpdaterSettings.cs" />
|
<Compile Include="Updates\UpdaterSettings.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -305,9 +303,6 @@
|
|||||||
<DependentUpon>FormAbout.cs</DependentUpon>
|
<DependentUpon>FormAbout.cs</DependentUpon>
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Include="Updates\FormUpdateDownload.resx">
|
|
||||||
<DependentUpon>FormUpdateDownload.cs</DependentUpon>
|
|
||||||
</EmbeddedResource>
|
|
||||||
<EmbeddedResource Include="Properties\Resources.resx">
|
<EmbeddedResource Include="Properties\Resources.resx">
|
||||||
<Generator>ResXFileCodeGenerator</Generator>
|
<Generator>ResXFileCodeGenerator</Generator>
|
||||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||||
@@ -349,7 +344,7 @@
|
|||||||
<Project>{b10b0017-819e-4f71-870f-8256b36a26aa}</Project>
|
<Project>{b10b0017-819e-4f71-870f-8256b36a26aa}</Project>
|
||||||
<Name>TweetDuck.Browser</Name>
|
<Name>TweetDuck.Browser</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
<ProjectReference Include="video\TweetDuck.Video\TweetDuck.Video.csproj">
|
<ProjectReference Include="video\TweetDuck.Video.csproj">
|
||||||
<Project>{278b2d11-402d-44b6-b6a1-8fa67db65565}</Project>
|
<Project>{278b2d11-402d-44b6-b6a1-8fa67db65565}</Project>
|
||||||
<Name>TweetDuck.Video</Name>
|
<Name>TweetDuck.Video</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
@@ -10,7 +10,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "tests\UnitTest
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetLib.Audio", "lib\TweetLib.Audio\TweetLib.Audio.csproj", "{E9E1FD1B-F480-45B7-9970-BE2ECFD309AC}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetLib.Audio", "lib\TweetLib.Audio\TweetLib.Audio.csproj", "{E9E1FD1B-F480-45B7-9970-BE2ECFD309AC}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetDuck.Video", "video\TweetDuck.Video\TweetDuck.Video.csproj", "{278B2D11-402D-44B6-B6A1-8FA67DB65565}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetDuck.Video", "video\TweetDuck.Video.csproj", "{278B2D11-402D-44B6-B6A1-8FA67DB65565}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetLib.Communication", "lib\TweetLib.Communication\TweetLib.Communication.csproj", "{72473763-4B9D-4FB6-A923-9364B2680F06}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetLib.Communication", "lib\TweetLib.Communication\TweetLib.Communication.csproj", "{72473763-4B9D-4FB6-A923-9364B2680F06}"
|
||||||
EndProject
|
EndProject
|
||||||
|
@@ -1,11 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace TweetDuck.Updates.Events{
|
|
||||||
class UpdateAcceptedEventArgs : EventArgs{
|
|
||||||
public readonly UpdateInfo UpdateInfo;
|
|
||||||
|
|
||||||
public UpdateAcceptedEventArgs(UpdateInfo info){
|
|
||||||
this.UpdateInfo = info;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,15 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace TweetDuck.Updates.Events{
|
|
||||||
class UpdateCheckEventArgs : EventArgs{
|
|
||||||
public int EventId { get; }
|
|
||||||
public UpdateInfo UpdateInfo { get; }
|
|
||||||
|
|
||||||
public bool UpdateAvailable => UpdateInfo != null;
|
|
||||||
|
|
||||||
public UpdateCheckEventArgs(int eventId, UpdateInfo updateInfo){
|
|
||||||
EventId = eventId;
|
|
||||||
UpdateInfo = updateInfo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,11 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace TweetDuck.Updates.Events{
|
|
||||||
class UpdateDismissedEventArgs : EventArgs{
|
|
||||||
public readonly string VersionTag;
|
|
||||||
|
|
||||||
public UpdateDismissedEventArgs(string versionTag){
|
|
||||||
this.VersionTag = versionTag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
3
Updates/FormUpdateDownload.Designer.cs
generated
3
Updates/FormUpdateDownload.Designer.cs
generated
@@ -24,7 +24,6 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void InitializeComponent() {
|
private void InitializeComponent() {
|
||||||
this.components = new System.ComponentModel.Container();
|
this.components = new System.ComponentModel.Container();
|
||||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FormUpdateDownload));
|
|
||||||
this.btnCancel = new System.Windows.Forms.Button();
|
this.btnCancel = new System.Windows.Forms.Button();
|
||||||
this.labelDescription = new System.Windows.Forms.Label();
|
this.labelDescription = new System.Windows.Forms.Label();
|
||||||
this.timerDownloadCheck = new System.Windows.Forms.Timer(this.components);
|
this.timerDownloadCheck = new System.Windows.Forms.Timer(this.components);
|
||||||
@@ -65,7 +64,7 @@
|
|||||||
this.Controls.Add(this.btnCancel);
|
this.Controls.Add(this.btnCancel);
|
||||||
this.DoubleBuffered = true;
|
this.DoubleBuffered = true;
|
||||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
|
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
|
||||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
this.Icon = Properties.Resources.icon;
|
||||||
this.MaximizeBox = false;
|
this.MaximizeBox = false;
|
||||||
this.MinimizeBox = false;
|
this.MinimizeBox = false;
|
||||||
this.Name = "FormUpdateDownload";
|
this.Name = "FormUpdateDownload";
|
||||||
|
@@ -1,380 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<root>
|
|
||||||
<!--
|
|
||||||
Microsoft ResX Schema
|
|
||||||
|
|
||||||
Version 2.0
|
|
||||||
|
|
||||||
The primary goals of this format is to allow a simple XML format
|
|
||||||
that is mostly human readable. The generation and parsing of the
|
|
||||||
various data types are done through the TypeConverter classes
|
|
||||||
associated with the data types.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
... ado.net/XML headers & schema ...
|
|
||||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
|
||||||
<resheader name="version">2.0</resheader>
|
|
||||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
|
||||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
|
||||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
|
||||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
|
||||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
|
||||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
|
||||||
</data>
|
|
||||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
|
||||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
|
||||||
<comment>This is a comment</comment>
|
|
||||||
</data>
|
|
||||||
|
|
||||||
There are any number of "resheader" rows that contain simple
|
|
||||||
name/value pairs.
|
|
||||||
|
|
||||||
Each data row contains a name, and value. The row also contains a
|
|
||||||
type or mimetype. Type corresponds to a .NET class that support
|
|
||||||
text/value conversion through the TypeConverter architecture.
|
|
||||||
Classes that don't support this are serialized and stored with the
|
|
||||||
mimetype set.
|
|
||||||
|
|
||||||
The mimetype is used for serialized objects, and tells the
|
|
||||||
ResXResourceReader how to depersist the object. This is currently not
|
|
||||||
extensible. For a given mimetype the value must be set accordingly:
|
|
||||||
|
|
||||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
|
||||||
that the ResXResourceWriter will generate, however the reader can
|
|
||||||
read any of the formats listed below.
|
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.binary.base64
|
|
||||||
value : The object must be serialized with
|
|
||||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
|
||||||
: and then encoded with base64 encoding.
|
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.soap.base64
|
|
||||||
value : The object must be serialized with
|
|
||||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
|
||||||
: and then encoded with base64 encoding.
|
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
|
||||||
value : The object must be serialized into a byte array
|
|
||||||
: using a System.ComponentModel.TypeConverter
|
|
||||||
: and then encoded with base64 encoding.
|
|
||||||
-->
|
|
||||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
|
||||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
|
||||||
<xsd:element name="root" msdata:IsDataSet="true">
|
|
||||||
<xsd:complexType>
|
|
||||||
<xsd:choice maxOccurs="unbounded">
|
|
||||||
<xsd:element name="metadata">
|
|
||||||
<xsd:complexType>
|
|
||||||
<xsd:sequence>
|
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
|
||||||
</xsd:sequence>
|
|
||||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
|
||||||
<xsd:attribute name="type" type="xsd:string" />
|
|
||||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
|
||||||
<xsd:attribute ref="xml:space" />
|
|
||||||
</xsd:complexType>
|
|
||||||
</xsd:element>
|
|
||||||
<xsd:element name="assembly">
|
|
||||||
<xsd:complexType>
|
|
||||||
<xsd:attribute name="alias" type="xsd:string" />
|
|
||||||
<xsd:attribute name="name" type="xsd:string" />
|
|
||||||
</xsd:complexType>
|
|
||||||
</xsd:element>
|
|
||||||
<xsd:element name="data">
|
|
||||||
<xsd:complexType>
|
|
||||||
<xsd:sequence>
|
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
|
||||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
|
||||||
</xsd:sequence>
|
|
||||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
|
||||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
|
||||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
|
||||||
<xsd:attribute ref="xml:space" />
|
|
||||||
</xsd:complexType>
|
|
||||||
</xsd:element>
|
|
||||||
<xsd:element name="resheader">
|
|
||||||
<xsd:complexType>
|
|
||||||
<xsd:sequence>
|
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
|
||||||
</xsd:sequence>
|
|
||||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
|
||||||
</xsd:complexType>
|
|
||||||
</xsd:element>
|
|
||||||
</xsd:choice>
|
|
||||||
</xsd:complexType>
|
|
||||||
</xsd:element>
|
|
||||||
</xsd:schema>
|
|
||||||
<resheader name="resmimetype">
|
|
||||||
<value>text/microsoft-resx</value>
|
|
||||||
</resheader>
|
|
||||||
<resheader name="version">
|
|
||||||
<value>2.0</value>
|
|
||||||
</resheader>
|
|
||||||
<resheader name="reader">
|
|
||||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
|
||||||
</resheader>
|
|
||||||
<resheader name="writer">
|
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
|
||||||
</resheader>
|
|
||||||
<metadata name="timerDownloadCheck.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
|
||||||
<value>17, 17</value>
|
|
||||||
</metadata>
|
|
||||||
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
|
||||||
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
|
||||||
<value>
|
|
||||||
AAABAAMAMDAAAAEAIACoJQAANgAAACAgAAABACAAqBAAAN4lAAAQEAAAAQAgAGgEAACGNgAAKAAAADAA
|
|
||||||
AABgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqHg5IrF+OlS+hjyMw4g7tcmM
|
|
||||||
PM/MjTzezY485s2OPObMjjzfyIw80MOJPLe8hDyNs387WKx8OyQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApHU4FrB8OGXGijzAzo889dKR
|
|
||||||
PP/SkTz/0pE8/9GRPP/RkDz/0JA8/9CQPP/RkDz/0ZE8/9KRPP/TkTz/0pE8/86PPPbGijzCt4E8bKp6
|
|
||||||
OxoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKN1OB+9hTuLzI486NOR
|
|
||||||
PP/SkTz/0ZA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9GQ
|
|
||||||
PP/SkTz/0pE8/82OPOu9hTuOq3w8IgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACidTkFuoM8csyN
|
|
||||||
POrTkjz/0ZE8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
|
||||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9GRPP/Tkjz/zY887bqEPHimeDsHAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKp6
|
|
||||||
OynDiDvA05I8/9GRPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
|
||||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0ZA8/9ORPP/GijzFr308LgAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAArXw7UM2OPOvTkTz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
|
||||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
|
||||||
PP/SkTz/zo887rOAPFYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAACxfjxm0ZA8/NGRPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
|
||||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
|
||||||
PP/QkDz/0JA8/9CQPP/QkDz/0ZE8/9GQPP64gjxtAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAALaBPGXRkTz/0ZE8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
|
||||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
|
||||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9GQPP/SkTz/uIM8bAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqnk6TtCQPPzRkTz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
|
||||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
|
||||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/RkDz/0ZA8/rSA
|
|
||||||
PFUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACtfDwmzo887dKRPP/QkDz/0JA8/9CQ
|
|
||||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
|
||||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
|
||||||
PP/QkDz/0ZE8/86PPPGtfDwrAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKZ4PAXFijzC0pE8/9CQ
|
|
||||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CURf/TpGT/2baJ/9/GpP/l0bf/59W//+fVvf/kz7P/3sOg/9mz
|
|
||||||
hP/ToWH/0JND/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
|
||||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9KRPP/EiTvJo3Y7CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALiC
|
|
||||||
PG/Tkjz/0JA8/9CQPP/QkDz/0JA8/9CSQf/Vq3P/5tO7//j07v//////////////////////////////
|
|
||||||
////////////////////////9vHq/+TQtf/UqG7/0JA+/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
|
||||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/Tkjz/uYM8dgAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAArXw8Gc2OPOzRkTz/0JA8/9CQPP/QlEX/27qP//fz7P//////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////8ObZ/9auev/QkDz/0JA8/9CQ
|
|
||||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/RkTz/zI488J1x
|
|
||||||
OB8AAAAAAAAAAAAAAAAAAAAAuoM7h9OSPP/QkDz/0JA8/9GXTf/TpGb/27uS/9u6kP/ewZz/59W9//Xv
|
|
||||||
5///////////////////////////////////////////////////////////////////////////////
|
|
||||||
///r3cr/0ZxV/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
|
||||||
PP/QkDz/1JI8/7mDO5AAAAAAAAAAAAAAAADFj0cR0pNB6s+PO//Ojjv/0JA8/9CQPP/QkDz/0JA8/9CQ
|
|
||||||
PP/QkDz/0JA8/9CRQP/TpGf/5NC2//38+v//////////////////////////////////////////////
|
|
||||||
////////////////////////+vf0/9auev/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
|
||||||
PP/QkDz/0JA8/9CQPP/QkDz/0ZE8/8uOPO6SajYVAAAAAAAAAADzr1df7qxV/+WkT//ZmUT/0JA9/86O
|
|
||||||
O//Pjzv/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9GaUv/l07r/////////////////////////
|
|
||||||
///////////////////////////////////////////////////buo//0JA8/9CQPP/QkDz/0JA8/9CQ
|
|
||||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9OSPP+ldjdoAAAAAAAAAADurFW67qxV//Ct
|
|
||||||
Vv/vrVb/6adR/92dSP/Skz//zo47/8+PO//QkDz/0JA8/9CQPP/QkDz/0JJB/9Smaf/dv5j/+fXw////
|
|
||||||
////////////////////////////////////////////////////////////////////////2riM/9CQ
|
|
||||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9KRPP/FiTzGoHQ6Ae6s
|
|
||||||
VRrurFXz7qxV/+6sVf/urFX/761W//CuVv/sqlT/4qFM/9aVQv/Pjzv/zo47/9CQPP/VqnH/8+vh////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
/////////////9aueP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9GQ
|
|
||||||
PP/Ojzz4oHM5Iu6sVU3urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/vrVX/8K5W/+6sVf/mpU//2plF/9q6
|
|
||||||
j///////////////////////////////////////////////////////////////////////////////
|
|
||||||
//////////////////////////////n18P/Rm1P/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
|
||||||
PP/QkDz/0JA8/9CQPP/SkTz/rXs6Vu6sVYPurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
|
||||||
Vf/wrVb/6r2B////////////////////////////////////////////////////////////////////
|
|
||||||
///////////////////////////////////////////////////o18L/0JA8/9CQPP/QkDz/0JA8/9CQ
|
|
||||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/SkTz/v4Y8j+6sVa3urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
|
||||||
Vf/urFX/7qxV/+6sVf/urFb/9Onb///////+/v3/////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////1qx2/9CQ
|
|
||||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/SkTz/wYc7uO6sVcrurFX/7qxV/+6s
|
|
||||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/trlr/67Zy/+m+h//y5NP/////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////7+TW/9CQPf/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/RkTz/yYw80e6s
|
|
||||||
VdvurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/6sme//v49P//////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
/////////////////////////////9arc//QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
|
||||||
PP/RkDz/zI083+6sVeLurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/u2b3/////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
/////////////////////////////////////////////+fVvv/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
|
||||||
PP/QkDz/0JA8/9CQPP/QkDz/zo885u6sVeLurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+vL
|
|
||||||
of//////////////////////////////////////////////////////////////////////////////
|
|
||||||
//////////////////////////////////////////////////////////////r28v/Rl0r/0JA8/9CQ
|
|
||||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/zo885u6sVdrurFX/7qxV/+6sVf/urFX/7qxV/+6s
|
|
||||||
Vf/urFX/7LBj//r28f//////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
///Vq3P/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/RkDz/zI083+6sVcrurFX/7qxV/+6s
|
|
||||||
Vf/urFX/7qxV/+6sVf/urFX/6cKO////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
///////////////////ewp7/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/RkTz/yYw80O6s
|
|
||||||
VazurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7NOy//z59v/u2b7/+fPr////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
///////////////////////////////////n1sD/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
|
||||||
PP/SkTz/xIo8uO6sVYHurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/67Zx/+2uWf/qy6H/////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
///////////////////////////////////////////////////v5dj/0JA8/9CQPP/QkDz/0JA8/9CQ
|
|
||||||
PP/QkDz/0JA8/9CQPP/SkTz/vYU8je6sVUvurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+nC
|
|
||||||
kP///////////////////////////////////////////////////////fz6//fu5P/w4Mv/793F//7+
|
|
||||||
/v/////////////////////////////////////////////////////////////////17eT/0JA//86O
|
|
||||||
Ov/Pjzv/0JA8/9CQPP/QkDz/0JA8/9CQPP/SkTz/tYA8Vu6sVRnurFXy7qxV/+6sVf/urFX/7qxV/+6s
|
|
||||||
Vf/urFX/7a5b//fw5///////////////////////////////////////+/jz/+7av//pwIr/67Jn/+6t
|
|
||||||
Vv/urFX/6rd1////////////////////////////////////////////////////////////////////
|
|
||||||
///48ej/6KhU/92cR//Tkj7/zo47/8+PO//QkDz/0JA8/9GQPP/Pjzz3rXw8IAAAAADurFW37qxV/+6s
|
|
||||||
Vf/urFX/7qxV/+6sVf/urFX/6b2D//////////////////////////////////nz6//qyZ3/7K9h/+6s
|
|
||||||
Vf/urFX/7qxV/+6sVf/urFX/6rl6////////////////////////////////////////////////////
|
|
||||||
///////////////////9/Pr/6r6C//CuV//sqlT/4aBL/9aWQv/Pjzv/zo47/9GRPP/GijzCp3g8AQAA
|
|
||||||
AADurFVb7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/68yk///////////////////////+/fz/68+q/+2v
|
|
||||||
XP/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/6rNr//7+/f//////////////////////////////
|
|
||||||
/////////////////////////////////////////////+nEk//vrVX/8K5W/+6sVf/lpE//2ZlF/9KR
|
|
||||||
Pf+zfzpoAAAAAAAAAADurFUP7qxV5u6sVf/urFX/7qxV/+6sVf/urFX/7NGu//////////////////fu
|
|
||||||
5P/quHb/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxW//Xr3///////////////
|
|
||||||
///////////////////////////////////////////////////7+PP/+vXu///////pxJL/7qxV/+6s
|
|
||||||
Vf/wrVb/8K1W/+mnUevUmUsUAAAAAAAAAAAAAAAA7qxVgO6sVf/urFX/7qxV/+6sVf/urFX/6sqg////
|
|
||||||
////////8eLO/+yvX//urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+nF
|
|
||||||
lf/////////////////////////////////////////////////////////////////8+vf/6cWV/+q5
|
|
||||||
ev/s07H/6rd1/+6sVf/urFX/7qxV/++tVogAAAAAAAAAAAAAAAAAAAAA7qxVFe6sVejurFX/7qxV/+6s
|
|
||||||
Vf/urFX/6bp8///////w4Mn/7a1Z/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
|
||||||
Vf/urFX/7qxV/+6sVf/x4cz////////////////////////////////////////////////////+//7+
|
|
||||||
/v/////////+/+m8gP/urFX/7a1Y/+6sVf/urFX/7qxV7O6sVRoAAAAAAAAAAAAAAAAAAAAAAAAAAO6s
|
|
||||||
VWburFX/7qxV/+6sVf/urFX/7a5b/+zTs//sr1//7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
|
||||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/sr1//8uPQ////////////////////////////////////
|
|
||||||
////////6suj/+qzav/pxpj/9Onb//n07f/rr2D/7qxV/+6sVf/urFX/7qxVbwAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAO6sVQPurFW67qxV/+6sVf/urFX/7qxV/+6sVv/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
|
||||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7axY/+nGl//27uP/////////
|
|
||||||
/////////fz6//Lj0P/qunv/7qxV/+6sVf/urFX/7qxV/+m9gv/qt3X/7qxV/+6sVf/urFXB7qxVBQAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADurFUg7qxV6O6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
|
||||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
|
||||||
Vf/qtnP/6sqg/+3WuP/s07P/6cSS/+uyZf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVv/trVr/7qxV/+6s
|
|
||||||
VezurFUlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7qxVRu6sVfjurFX/7qxV/+6s
|
|
||||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
|
||||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
|
||||||
Vf/urFX/7qxV++6sVUwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO6s
|
|
||||||
VVzurFX97qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
|
||||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
|
||||||
Vf/urFX/7qxV/+6sVf/urFX/7qxVYwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAADurFVd7qxV+O6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
|
||||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
|
||||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVfvurFVjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7qxVR+6sVeXurFX/7qxV/+6sVf/urFX/7qxV/+6s
|
|
||||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
|
||||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV6O6sVU0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO6sVSPurFW37qxV/+6s
|
|
||||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
|
||||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFW77qxVJgAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AADurFUB7qxVae6sVeTurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
|
||||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV5+6sVW3urFUDAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAO6sVRjurFWA7qxV4u6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
|
||||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVeTurFWE7qxVGwAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7qxVEu6sVV7urFW17qxV7+6s
|
|
||||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVfDurFW47qxVX+6s
|
|
||||||
VRMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAA7qxVG+6sVUzurFWA7qxVrO6sVcnurFXa7qxV4u6sVeLurFXa7qxVye6sVa3urFWC7qxVTe6s
|
|
||||||
VR0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAP//gAH//6gr//wAAD//qCv/8AAAD/+oK//AAAAD/6gr/4AAAAH/qCv/AAAAAP+oK/4A
|
|
||||||
AAAAf6gr/AAAAAA/qCv4AAAAAB+oK/AAAAAAD6gr4AAAAAAHqCvgAAAAAAeoK8AAAAAAA6grwAAAAAAD
|
|
||||||
qCuAAAAAAAGoK4AAAAAAAagrgAAAAAAAqCsAAAAAAACoKwAAAAAAAKgrAAAAAAAAqCsAAAAAAACoKwAA
|
|
||||||
AAAAAKgrAAAAAAAAqCsAAAAAAACoKwAAAAAAAKgrAAAAAAAAqCsAAAAAAACoKwAAAAAAAKgrAAAAAAAA
|
|
||||||
qCsAAAAAAACoKwAAAAAAAKgrgAAAAAAAqCuAAAAAAAGoK4AAAAAAAagrwAAAAAADqCvAAAAAAAOoK+AA
|
|
||||||
AAAAB6gr4AAAAAAHqCvwAAAAAA+oK/gAAAAAH6gr/AAAAAA/qCv+AAAAAH+oK/8AAAAA/6gr/4AAAAH/
|
|
||||||
qCv/wAAAA/+oK//wAAAP/6gr//wAAD//qCv//4AB//+oKygAAAAgAAAAQAAAAAEAIAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApHY4FrR/
|
|
||||||
Ole+hjuVxos7w8uNPNvOjzznzo8858uNPNzGijzEvoU7l7WBO1mpejsYAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqHg5Kr6F
|
|
||||||
O5XLjTvo0pE8/9ORPP/SkTz/0ZE8/9CQPP/QkDz/0ZE8/9KRPP/TkTz/0pE8/82OPOq/hjyZrXw7LAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAo3U6CbuE
|
|
||||||
O4bPjzz005I8/9GRPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0ZE8/9OS
|
|
||||||
PP/PkDz1vYU8iah6PAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKR3
|
|
||||||
OibGizvG05E8/9GQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
|
||||||
PP/QkDz/0JA8/9GQPP/TkTz/yIs8yqx8PCkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AACsezsxy40849KRPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
|
||||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/SkTz/zI485rKAPTUAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAApXc6JMuNPOTSkTz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
|
||||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/SkTz/zI485659PCcAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAKd4OwnHizzJ0pE8/9CQPP/QkDz/0JA9/9OgYP/YtIX/38Sh/+LMr//iy67/3sOf/9iy
|
|
||||||
gv/Tn13/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/SkTz/x4s8zaV4
|
|
||||||
OwsAAAAAAAAAAAAAAAAAAAAAu4Q8hNOSPP/QkDz/0JA8/9GXS//YtIX/6NfC//Xv5//8+vj/////////
|
|
||||||
///8+vf/9O3k/+fVvv/YsYH/0JRF/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
|
||||||
PP/Tkjz/u4Q7iQAAAAAAAAAAAAAAAKx7OyTPjzz10ZA8/9GXS//Ztoj/8une////////////////////
|
|
||||||
///////////////////////////////////38uz/3L2V/9CRP//QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
|
||||||
PP/QkDz/0JA8/9GRPP/Ojzz3onU5KQAAAAAAAAAAxo1BlNGQO//Ojjv/0JNC/9GWSP/QlET/0ZpR/9au
|
|
||||||
eP/n1b3//v79////////////////////////////////////////////7+TV/9GdWP/QkDz/0JA8/9CQ
|
|
||||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9SSPP+4gjqaAAAAAPGuVhHvrVXq5qRO/9qZRP/QkT3/zo46/8+P
|
|
||||||
O//QkDz/0JA8/9CQPP/To2P/8+vh////////////////////////////////////////////+fXw/9Oh
|
|
||||||
Yf/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0pE8/8qMO+2WbTYV7qxVUe6sVf/wrVb/761W/+mo
|
|
||||||
Uv/enUj/05M//86PO//PkD7/3cCZ//Lp3v/7+PT/////////////////////////////////////////
|
|
||||||
////////+PTv/9GcVf/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/05I8/699OljurFWR7qxV/+6s
|
|
||||||
Vf/urFX/761W//CuV//sqlT/4qJQ/+zdyf//////////////////////////////////////////////
|
|
||||||
////////////////////////7+TV/9CRP//QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/Tkjz/vIQ7mO6s
|
|
||||||
Vb/urFX/7qxV/+6sVf/urFX/7qxV/++tVf/ry5///v79//7+/f//////////////////////////////
|
|
||||||
////////////////////////////////////////2rqO/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9KR
|
|
||||||
PP/GijvF7qxV2u6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+u1bv/t1bb//fv4////////////////////
|
|
||||||
///////////////////////////////////////////////////48+3/0ZRG/9CQPP/QkDz/0JA8/9CQ
|
|
||||||
PP/QkDz/0ZA8/8uNPNzurFXl7qxV/+6sVf/urFX/7qxV/+6sVf/rsWP/9OjZ////////////////////
|
|
||||||
///////////////////////////////////////////////////////////////////YtIX/0JA8/9CQ
|
|
||||||
PP/QkDz/0JA8/9CQPP/QkDz/zo886O6sVeXurFX/7qxV/+6sVf/urFX/7qxW//Hiz///////////////
|
|
||||||
/////////////////////////////////////////////////////////////////////////////+rb
|
|
||||||
yP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/Ojzzo7qxV2e6sVf/urFX/7qxV/+6sVf/quHb/////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////+fXw/9CURf/QkDz/0JA8/9CQPP/QkDz/0ZA8/8yOPNzurFW/7qxV/+6sVf/urFX/7qxV/+nA
|
|
||||||
if/s1LT/9Onb////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////0Z5b/9CQPP/QkDz/0JA8/9CQPP/SkTz/x4s8xe6sVY/urFX/7qxV/+6s
|
|
||||||
Vf/urFX/7qxV/+nEk//+/fz////////////////////////////+/v3/+PHo//To2f/+/v7/////////
|
|
||||||
///////////////////////////////////VqGz/zo46/8+PO//QkDz/0JA8/9KRPP+/hjyX7qxVUO6s
|
|
||||||
Vf/urFX/7qxV/+6sVf/ssGH//Pr2///////////////////////y49D/6cOR/+qyaf/urVb/6rJp////
|
|
||||||
/////////////////////////////////////////////+e7f//goEr/1ZVB/8+PO//Ojzv/0pE8/7WB
|
|
||||||
PFfurFUQ7qxV6O6sVf/urFX/7qxV/+m+hv/////////////////z5tX/6rl6/+6sVf/urFX/7qxV/+6s
|
|
||||||
Vf/qs2r////+////////////////////////////////////////////+PLp/+y4c//urFX/5aRO/9qZ
|
|
||||||
RP/Mjjztp3g6FQAAAADurFWQ7qxV/+6sVf/urFX/6cKP////////////682m/+6sVf/urFX/7qxV/+6s
|
|
||||||
Vf/urFX/7qxV/+2sV//17N////////////////////////////////////////7+/v/06Nn/8uTT/+qz
|
|
||||||
a//wrVb/761W/+alUZYAAAAAAAAAAO6sVSHurFX07qxV/+6sVf/quXj//////+nDkf/urFX/7qxV/+6s
|
|
||||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+m+hP////////////////////////////////////////////jy
|
|
||||||
6v/qtnH/67Fj/+6sVf/urFX2761WJQAAAAAAAAAAAAAAAO6sVX7urFX/7qxV/+2vW//pvID/7qxV/+6s
|
|
||||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+nEkv/7+PT//////////////////fz6/+rJ
|
|
||||||
n//rtW7/682n/+zTs//urFb/7qxV/+6sVYQAAAAAAAAAAAAAAAAAAAAA7qxVB+6sVcTurFX/7qxV/+6s
|
|
||||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+u0bP/qy6H/7da4/+vN
|
|
||||||
pv/qtnP/7qxV/+6sVf/urFX/7K9f/+2sWP/urFXI7qxVCQAAAAAAAAAAAAAAAAAAAAAAAAAA7qxVIO6s
|
|
||||||
VeDurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
|
||||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV4+6sVSMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAA7qxVLO6sVd/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
|
||||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVeLurFUwAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAA7qxVIu6sVcHurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
|
||||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFXE7qxVJAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7qxVBu6sVX/urFXw7qxV/+6sVf/urFX/7qxV/+6s
|
|
||||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFXy7qxVgu6sVQgAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO6sVSXurFWP7qxV5e6s
|
|
||||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFXl7qxVke6sVScAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AADurFUS7qxVUO6sVY7urFW97qxV2O6sVeXurFXl7qxV2O6sVb7urFWP7qxVUu6sVRMAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8AD//8AAP/8AAA/+AAAH/AAAA/gAAAHwAAAA8AA
|
|
||||||
AAOAAAABgAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAA
|
|
||||||
AAGAAAABwAAAA8AAAAPgAAAH8AAAD/gAAB/8AAA//wAA///AA/8oAAAAEAAAACAAAAABACAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKx7Oja9hTqTyYw8zs6PPOfOjzznyYw8zr6G
|
|
||||||
PJSvfjw3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAo3Y7CbyEO5PRkDz805I8/9GRPP/QkDz/0JA8/9GR
|
|
||||||
PP/TkTz/0ZA8/b+GPJWrfDwKAAAAAAAAAAAAAAAAp3g8CcSJPLfTkjz/0ZA8/9CQPP/QkDz/0JA8/9CQ
|
|
||||||
PP/QkDz/0JA8/9CQPP/Tkjz/xoo8uqx8PQoAAAAAAAAAAL2FPJPUk0D/06Rm/92/mf/izK//4sut/9u6
|
|
||||||
j//Snlr/0JA8/9CQPP/QkDz/0JA8/9OSPP+9hTyWAAAAALiEQDLQkDz90JpS/93Amf/u4tP/////////
|
|
||||||
/////////fv5/+DIqP/QkkH/0JA8/9CQPP/RkDz/0JA8/qN1OTTtq1WS56VP/9qaRf/RkT3/0JdO/+rc
|
|
||||||
yP//////////////////////693L/9CSQf/QkDz/0JA8/9SSPP+3gTqW761Vz++tVv/wrVb/6K5h//fy
|
|
||||||
6v/////////////////////////////////gx6b/0JA8/9CQPP/RkTz/yYw80e6sVefurFX/7qxV/+q+
|
|
||||||
hP/69vD//////////////////////////////////fz7/9GbU//QkDz/0JA8/8+PPOnurFXn7qxV/+yw
|
|
||||||
Y//79/P////////////////////////////////////////////ZuIz/0JA8/9CQPP/Pjzzp7qxVz+6s
|
|
||||||
Vf/sr2H/8eLO/////////////v7+//n07f/+/v7/////////////////48yu/8+PO//Qjzv/yYs80e6s
|
|
||||||
VZLurFX/67Jm//7+/f/8+fb/68yl/+qza//rsmf//v37//////////////////Pn2P/ip1n/2phC/8CH
|
|
||||||
PJXurFUw7qxV/eq2c//169//6rNr/+6sVf/urFX/7q1W//Pm1f/////////////////8+fX/67+G/++t
|
|
||||||
Vf3lpVEyAAAAAO6sVZHtrVr/7a9c/+6sVf/urFX/7qxV/+6sVf/rsWP/7de5//Lj0P/pxJL/67Zy/+qz
|
|
||||||
av/urFWTAAAAAAAAAADurFUI7qxVtO6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
|
||||||
Vf/urFW37qxVCQAAAAAAAAAAAAAAAO6sVQjurFWQ7qxV++6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
|
||||||
VfvurFWS7qxVCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO6sVTPurFWQ7qxVzO6sVeburFXm7qxVzO6s
|
|
||||||
VZDurFU0AAAAAAAAAAAAAAAAAAAAAPAPrEHAA6xBgAGsQYABrEEAAKxBAACsQQAArEEAAKxBAACsQQAA
|
|
||||||
rEEAAKxBAACsQYABrEGAAaxBwAOsQfAPrEE=
|
|
||||||
</value>
|
|
||||||
</data>
|
|
||||||
</root>
|
|
20
Updates/UpdateEventArgs.cs
Normal file
20
Updates/UpdateEventArgs.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace TweetDuck.Updates{
|
||||||
|
sealed class UpdateEventArgs : EventArgs{
|
||||||
|
public int EventId { get; }
|
||||||
|
public UpdateInfo UpdateInfo { get; }
|
||||||
|
|
||||||
|
public bool IsUpdateAvailable => UpdateInfo != null;
|
||||||
|
|
||||||
|
public UpdateEventArgs(int eventId, UpdateInfo updateInfo){
|
||||||
|
this.EventId = eventId;
|
||||||
|
this.UpdateInfo = updateInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UpdateEventArgs(UpdateInfo updateInfo){
|
||||||
|
this.EventId = updateInfo.EventId;
|
||||||
|
this.UpdateInfo = updateInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -5,18 +5,15 @@ using System.Windows.Forms;
|
|||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetDuck.Resources;
|
using TweetDuck.Resources;
|
||||||
using TweetDuck.Updates.Events;
|
|
||||||
|
|
||||||
namespace TweetDuck.Updates{
|
namespace TweetDuck.Updates{
|
||||||
sealed class UpdateHandler{
|
sealed class UpdateHandler{
|
||||||
private static bool IsSystemSupported => true; // Environment.OSVersion.Version >= new Version("6.1"); // 6.1 NT version = Windows 7
|
|
||||||
|
|
||||||
private readonly ChromiumWebBrowser browser;
|
private readonly ChromiumWebBrowser browser;
|
||||||
private readonly UpdaterSettings settings;
|
private readonly UpdaterSettings settings;
|
||||||
|
|
||||||
public event EventHandler<UpdateAcceptedEventArgs> UpdateAccepted;
|
public event EventHandler<UpdateEventArgs> UpdateAccepted;
|
||||||
public event EventHandler<UpdateDismissedEventArgs> UpdateDismissed;
|
public event EventHandler<UpdateEventArgs> UpdateDismissed;
|
||||||
public event EventHandler<UpdateCheckEventArgs> CheckFinished;
|
public event EventHandler<UpdateEventArgs> CheckFinished;
|
||||||
|
|
||||||
private int lastEventId;
|
private int lastEventId;
|
||||||
private UpdateInfo lastUpdateInfo;
|
private UpdateInfo lastUpdateInfo;
|
||||||
@@ -37,22 +34,17 @@ namespace TweetDuck.Updates{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int Check(bool force){
|
public int Check(bool force){
|
||||||
if (IsSystemSupported){
|
|
||||||
if (Program.UserConfig.EnableUpdateCheck || force){
|
if (Program.UserConfig.EnableUpdateCheck || force){
|
||||||
string dismissedUpdate = force || settings.DismissedUpdate == null ? string.Empty : settings.DismissedUpdate;
|
if (force){
|
||||||
|
settings.DismissedUpdate = null;
|
||||||
|
}
|
||||||
|
|
||||||
browser.ExecuteScriptAsync("TDUF_runUpdateCheck", ++lastEventId, Program.VersionTag, dismissedUpdate, settings.AllowPreReleases);
|
browser.ExecuteScriptAsync("TDUF_runUpdateCheck", ++lastEventId, Program.VersionTag, settings.DismissedUpdate ?? string.Empty, settings.AllowPreReleases);
|
||||||
return lastEventId;
|
return lastEventId;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
else if (settings.DismissedUpdate != "unsupported"){
|
|
||||||
browser.ExecuteScriptAsync("TDUF_displayNotification", "unsupported");
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void BeginUpdateDownload(Form ownerForm, UpdateInfo updateInfo, Action<UpdateInfo> onSuccess){
|
public void BeginUpdateDownload(Form ownerForm, UpdateInfo updateInfo, Action<UpdateInfo> onSuccess){
|
||||||
if (updateInfo.DownloadStatus == UpdateDownloadStatus.Done){
|
if (updateInfo.DownloadStatus == UpdateDownloadStatus.Done){
|
||||||
@@ -88,24 +80,20 @@ namespace TweetDuck.Updates{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DismissUpdate(string tag){
|
private void TriggerUpdateAcceptedEvent(UpdateEventArgs args){
|
||||||
TriggerUpdateDismissedEvent(new UpdateDismissedEventArgs(tag));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void TriggerUpdateAcceptedEvent(UpdateAcceptedEventArgs args){
|
|
||||||
UpdateAccepted?.Invoke(this, args);
|
UpdateAccepted?.Invoke(this, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TriggerUpdateDismissedEvent(UpdateDismissedEventArgs args){
|
private void TriggerUpdateDismissedEvent(UpdateEventArgs args){
|
||||||
settings.DismissedUpdate = args.VersionTag;
|
settings.DismissedUpdate = args.UpdateInfo.VersionTag;
|
||||||
UpdateDismissed?.Invoke(this, args);
|
UpdateDismissed?.Invoke(this, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TriggerCheckFinishedEvent(UpdateCheckEventArgs args){
|
private void TriggerCheckFinishedEvent(UpdateEventArgs args){
|
||||||
CheckFinished?.Invoke(this, args);
|
CheckFinished?.Invoke(this, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Bridge{
|
public sealed class Bridge{
|
||||||
private readonly UpdateHandler owner;
|
private readonly UpdateHandler owner;
|
||||||
|
|
||||||
public Bridge(UpdateHandler owner){
|
public Bridge(UpdateHandler owner){
|
||||||
@@ -119,22 +107,22 @@ namespace TweetDuck.Updates{
|
|||||||
public void OnUpdateCheckFinished(int eventId, string versionTag, string downloadUrl){
|
public void OnUpdateCheckFinished(int eventId, string versionTag, string downloadUrl){
|
||||||
if (versionTag != null && (owner.lastUpdateInfo == null || owner.lastUpdateInfo.VersionTag != versionTag)){
|
if (versionTag != null && (owner.lastUpdateInfo == null || owner.lastUpdateInfo.VersionTag != versionTag)){
|
||||||
owner.CleanupDownload();
|
owner.CleanupDownload();
|
||||||
owner.lastUpdateInfo = new UpdateInfo(owner.settings, versionTag, downloadUrl);
|
owner.lastUpdateInfo = new UpdateInfo(owner.settings, eventId, versionTag, downloadUrl);
|
||||||
owner.lastUpdateInfo.BeginSilentDownload();
|
owner.lastUpdateInfo.BeginSilentDownload();
|
||||||
}
|
}
|
||||||
|
|
||||||
owner.TriggerCheckFinishedEvent(new UpdateCheckEventArgs(eventId, owner.lastUpdateInfo));
|
owner.TriggerCheckFinishedEvent(new UpdateEventArgs(eventId, owner.lastUpdateInfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnUpdateAccepted(){
|
public void OnUpdateAccepted(){
|
||||||
if (owner.lastUpdateInfo != null){
|
if (owner.lastUpdateInfo != null){
|
||||||
owner.TriggerUpdateAcceptedEvent(new UpdateAcceptedEventArgs(owner.lastUpdateInfo));
|
owner.TriggerUpdateAcceptedEvent(new UpdateEventArgs(owner.lastUpdateInfo));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnUpdateDismissed(){
|
public void OnUpdateDismissed(){
|
||||||
if (owner.lastUpdateInfo != null){
|
if (owner.lastUpdateInfo != null){
|
||||||
owner.TriggerUpdateDismissedEvent(new UpdateDismissedEventArgs(owner.lastUpdateInfo.VersionTag));
|
owner.TriggerUpdateDismissedEvent(new UpdateEventArgs(owner.lastUpdateInfo));
|
||||||
owner.CleanupDownload();
|
owner.CleanupDownload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,6 +5,7 @@ using TweetDuck.Core.Utils;
|
|||||||
|
|
||||||
namespace TweetDuck.Updates{
|
namespace TweetDuck.Updates{
|
||||||
sealed class UpdateInfo{
|
sealed class UpdateInfo{
|
||||||
|
public int EventId { get; }
|
||||||
public string VersionTag { get; }
|
public string VersionTag { get; }
|
||||||
public string InstallerPath { get; }
|
public string InstallerPath { get; }
|
||||||
|
|
||||||
@@ -15,10 +16,11 @@ namespace TweetDuck.Updates{
|
|||||||
private readonly string downloadUrl;
|
private readonly string downloadUrl;
|
||||||
private WebClient currentDownload;
|
private WebClient currentDownload;
|
||||||
|
|
||||||
public UpdateInfo(UpdaterSettings settings, string versionTag, string downloadUrl){
|
public UpdateInfo(UpdaterSettings settings, int eventId, string versionTag, string downloadUrl){
|
||||||
this.installerFolder = settings.InstallerDownloadFolder;
|
this.installerFolder = settings.InstallerDownloadFolder;
|
||||||
this.downloadUrl = downloadUrl;
|
this.downloadUrl = downloadUrl;
|
||||||
|
|
||||||
|
this.EventId = eventId;
|
||||||
this.VersionTag = versionTag;
|
this.VersionTag = versionTag;
|
||||||
this.InstallerPath = Path.Combine(installerFolder, "TweetDuck."+versionTag+".exe");
|
this.InstallerPath = Path.Combine(installerFolder, "TweetDuck."+versionTag+".exe");
|
||||||
}
|
}
|
||||||
|
@@ -30,7 +30,7 @@ namespace TweetLib.Audio{
|
|||||||
public abstract event EventHandler<PlaybackErrorEventArgs> PlaybackError;
|
public abstract event EventHandler<PlaybackErrorEventArgs> PlaybackError;
|
||||||
|
|
||||||
public abstract void Play(string file);
|
public abstract void Play(string file);
|
||||||
public abstract void Stop();
|
public abstract bool SetVolume(int volume);
|
||||||
protected abstract void Dispose(bool disposing);
|
protected abstract void Dispose(bool disposing);
|
||||||
|
|
||||||
public void Dispose(){
|
public void Dispose(){
|
||||||
|
@@ -34,8 +34,8 @@ namespace TweetLib.Audio.Impl{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Stop(){
|
public override bool SetVolume(int volume){
|
||||||
player.Stop();
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool disposing){
|
protected override void Dispose(bool disposing){
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using TweetLib.Audio.Utils;
|
using System.Windows.Forms;
|
||||||
using WMPLib;
|
using WMPLib;
|
||||||
|
|
||||||
namespace TweetLib.Audio.Impl{
|
namespace TweetLib.Audio.Impl{
|
||||||
@@ -9,56 +9,56 @@ namespace TweetLib.Audio.Impl{
|
|||||||
|
|
||||||
public override event EventHandler<PlaybackErrorEventArgs> PlaybackError;
|
public override event EventHandler<PlaybackErrorEventArgs> PlaybackError;
|
||||||
|
|
||||||
private readonly WindowsMediaPlayer player;
|
private readonly Form owner;
|
||||||
|
private readonly ControlWMP wmp;
|
||||||
private bool wasTryingToPlay;
|
private bool wasTryingToPlay;
|
||||||
private bool ignorePlaybackError;
|
private bool ignorePlaybackError;
|
||||||
|
|
||||||
// changing the player volume also affects the value in the Windows mixer
|
private WindowsMediaPlayer Player => wmp.Ocx;
|
||||||
// however, the initial value is always 50 or some other stupid shit
|
|
||||||
// so we have to tell the player to set its volume to whatever the mixer is set to
|
|
||||||
// using the most code required for the least functionality with a sorry excuse for an API
|
|
||||||
// thanks, Microsoft
|
|
||||||
|
|
||||||
public SoundPlayerImplWMP(){
|
public SoundPlayerImplWMP(){
|
||||||
player = new WindowsMediaPlayer();
|
owner = new Form();
|
||||||
player.settings.autoStart = false;
|
wmp = new ControlWMP();
|
||||||
player.settings.enableErrorDialogs = false;
|
wmp.BeginInit();
|
||||||
player.settings.invokeURLs = false;
|
owner.Controls.Add(wmp);
|
||||||
player.settings.volume = (int)Math.Floor(100.0*NativeCoreAudio.GetMixerVolumeForCurrentProcess());
|
wmp.EndInit();
|
||||||
player.MediaChange += player_MediaChange;
|
|
||||||
player.MediaError += player_MediaError;
|
Player.uiMode = "none";
|
||||||
|
Player.settings.autoStart = false;
|
||||||
|
Player.settings.enableErrorDialogs = false;
|
||||||
|
Player.settings.invokeURLs = false;
|
||||||
|
Player.settings.volume = 0;
|
||||||
|
Player.MediaChange += player_MediaChange;
|
||||||
|
Player.MediaError += player_MediaError;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Play(string file){
|
public override void Play(string file){
|
||||||
wasTryingToPlay = true;
|
wasTryingToPlay = true;
|
||||||
|
|
||||||
try{
|
try{
|
||||||
if (player.URL != file){
|
if (Player.URL != file){
|
||||||
player.close();
|
Player.close();
|
||||||
player.URL = file;
|
Player.URL = file;
|
||||||
ignorePlaybackError = false;
|
ignorePlaybackError = false;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
player.controls.stop();
|
Player.controls.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
player.controls.play();
|
Player.controls.play();
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
OnNotificationSoundError("An error occurred in Windows Media Player: "+e.Message);
|
OnNotificationSoundError("An error occurred in Windows Media Player: "+e.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Stop(){
|
public override bool SetVolume(int volume){
|
||||||
try{
|
Player.settings.volume = volume;
|
||||||
player.controls.stop();
|
return true;
|
||||||
}catch{
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool disposing){
|
protected override void Dispose(bool disposing){
|
||||||
player.close();
|
wmp.Dispose();
|
||||||
Marshal.ReleaseComObject(player);
|
owner.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void player_MediaChange(object item){
|
private void player_MediaChange(object item){
|
||||||
@@ -109,5 +109,16 @@ namespace TweetLib.Audio.Impl{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Clsid("{6bf52a52-394a-11d3-b153-00c04f79faa6}")]
|
||||||
|
private sealed class ControlWMP : AxHost{
|
||||||
|
public WindowsMediaPlayer Ocx { get; private set; }
|
||||||
|
|
||||||
|
public ControlWMP() : base("6bf52a52-394a-11d3-b153-00c04f79faa6"){}
|
||||||
|
|
||||||
|
protected override void AttachInterfaces(){
|
||||||
|
Ocx = (WindowsMediaPlayer)GetOcx();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
|
|||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion("1.0.0.0")]
|
[assembly: AssemblyVersion("1.1.0.0")]
|
||||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
[assembly: AssemblyFileVersion("1.1.0.0")]
|
||||||
|
@@ -26,10 +26,10 @@
|
|||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Windows.Forms" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="AudioPlayer.cs" />
|
<Compile Include="AudioPlayer.cs" />
|
||||||
<Compile Include="Utils\NativeCoreAudio.cs" />
|
|
||||||
<Compile Include="PlaybackErrorEventArgs.cs" />
|
<Compile Include="PlaybackErrorEventArgs.cs" />
|
||||||
<Compile Include="Impl\SoundPlayerImplFallback.cs" />
|
<Compile Include="Impl\SoundPlayerImplFallback.cs" />
|
||||||
<Compile Include="Impl\SoundPlayerImplWMP.cs" />
|
<Compile Include="Impl\SoundPlayerImplWMP.cs" />
|
||||||
|
@@ -1,127 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.IO;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace TweetLib.Audio.Utils{
|
|
||||||
static class NativeCoreAudio{
|
|
||||||
private const int EDATAFLOW_RENDER = 0;
|
|
||||||
private const int EROLE_MULTIMEDIA = 1;
|
|
||||||
|
|
||||||
[ComImport]
|
|
||||||
[Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")]
|
|
||||||
private class MMDeviceEnumerator{}
|
|
||||||
|
|
||||||
[Guid("A95664D2-9614-4F35-A746-DE8DB63617E6")]
|
|
||||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
|
||||||
private interface IMMDeviceEnumerator{
|
|
||||||
int Unimpl_EnumAudioEndpoints();
|
|
||||||
IMMDevice GetDefaultAudioEndpoint(int dataFlow, int role);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Guid("D666063F-1587-4E43-81F1-B948E807363F")]
|
|
||||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
|
||||||
private interface IMMDevice{
|
|
||||||
[return:MarshalAs(UnmanagedType.IUnknown)]
|
|
||||||
object Activate(ref Guid id, int clsCtx, IntPtr activationParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Guid("77AA99A0-1BD6-484F-8BC7-2C654C9A9B6F")]
|
|
||||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
|
||||||
private interface IAudioSessionManager2{
|
|
||||||
int Unimpl_FindWillToLive();
|
|
||||||
int Unimpl_HelloDarknessMyOldFriend();
|
|
||||||
IAudioSessionEnumerator GetSessionEnumerator();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Guid("E2F5BB11-0570-40CA-ACDD-3AA01277DEE8")]
|
|
||||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
|
||||||
private interface IAudioSessionEnumerator{
|
|
||||||
int GetCount();
|
|
||||||
IAudioSessionControl GetSession(int sessionIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Guid("F4B1A599-7266-4319-A8CA-E70ACB11E8CD")]
|
|
||||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
|
||||||
private interface IAudioSessionControl{}
|
|
||||||
|
|
||||||
[Guid("BFB7FF88-7239-4FC9-8FA2-07C950BE9C6D")]
|
|
||||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
|
||||||
private interface IAudioSessionControl2{
|
|
||||||
int Unimpl_GetState();
|
|
||||||
int Unimpl_GetDisplayName();
|
|
||||||
int Unimpl_SetDisplayName();
|
|
||||||
int Unimpl_GetIconPath();
|
|
||||||
int Unimpl_SetIconPath();
|
|
||||||
int Unimpl_GetGroupingParam();
|
|
||||||
int Unimpl_SetGroupingParam();
|
|
||||||
int Unimpl_RegisterAudioSessionNotification();
|
|
||||||
int Unimpl_UnregisterAudioSessionNotification();
|
|
||||||
|
|
||||||
[return:MarshalAs(UnmanagedType.LPWStr)]
|
|
||||||
string GetSessionIdentifier();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Guid("87CE5498-68D6-44E5-9215-6DA47EF883D8")]
|
|
||||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
|
||||||
private interface ISimpleAudioVolume{
|
|
||||||
void SetMasterVolume(float level, ref Guid eventContext);
|
|
||||||
float GetMasterVolume();
|
|
||||||
}
|
|
||||||
|
|
||||||
[SuppressMessage("ReSharper", "SuspiciousTypeConversion.Global")]
|
|
||||||
private static ISimpleAudioVolume GetVolumeObject(string name){
|
|
||||||
IMMDeviceEnumerator devices = (IMMDeviceEnumerator)new MMDeviceEnumerator();
|
|
||||||
IMMDevice device = devices.GetDefaultAudioEndpoint(EDATAFLOW_RENDER, EROLE_MULTIMEDIA);
|
|
||||||
|
|
||||||
Guid sessionManagerGUID = typeof(IAudioSessionManager2).GUID;
|
|
||||||
IAudioSessionManager2 manager = (IAudioSessionManager2)device.Activate(ref sessionManagerGUID, 0, IntPtr.Zero);
|
|
||||||
IAudioSessionEnumerator sessions = manager.GetSessionEnumerator();
|
|
||||||
|
|
||||||
ISimpleAudioVolume volumeObj = null;
|
|
||||||
|
|
||||||
for(int index = sessions.GetCount()-1; index >= 0; index--){
|
|
||||||
if (sessions.GetSession(index) is IAudioSessionControl2 ctl){
|
|
||||||
string identifier = ctl.GetSessionIdentifier();
|
|
||||||
|
|
||||||
if (identifier != null && identifier.Contains(name)){
|
|
||||||
volumeObj = ctl as ISimpleAudioVolume;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Marshal.ReleaseComObject(ctl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Marshal.ReleaseComObject(devices);
|
|
||||||
Marshal.ReleaseComObject(device);
|
|
||||||
Marshal.ReleaseComObject(manager);
|
|
||||||
Marshal.ReleaseComObject(sessions);
|
|
||||||
return volumeObj;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static double GetMixerVolume(string appPath){
|
|
||||||
ISimpleAudioVolume obj = GetVolumeObject(appPath);
|
|
||||||
float level = 1F;
|
|
||||||
|
|
||||||
if (obj != null){
|
|
||||||
level = obj.GetMasterVolume();
|
|
||||||
Marshal.ReleaseComObject(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Math.Round(level, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static double GetMixerVolumeForCurrentProcess(){
|
|
||||||
string path;
|
|
||||||
|
|
||||||
using(Process process = Process.GetCurrentProcess()){
|
|
||||||
path = process.MainModule.FileName;
|
|
||||||
path = path.Substring(Path.GetPathRoot(path).Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetMixerVolume(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -3,30 +3,10 @@ using TweetLib.Communication.Utils;
|
|||||||
|
|
||||||
namespace TweetLib.Communication{
|
namespace TweetLib.Communication{
|
||||||
public static class Comms{
|
public static class Comms{
|
||||||
public static void SendMessage(IntPtr hWnd, uint msg, uint wParam, int lParam){
|
|
||||||
NativeMethods.SendMessage(hWnd, msg, new UIntPtr(wParam), new IntPtr(lParam));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SendMessage(IntPtr hWnd, uint msg, UIntPtr wParam, IntPtr lParam){
|
|
||||||
NativeMethods.SendMessage(hWnd, msg, wParam, lParam);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void PostMessage(IntPtr hWnd, uint msg, uint wParam, int lParam){
|
|
||||||
NativeMethods.PostMessage(hWnd, msg, new UIntPtr(wParam), new IntPtr(lParam));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void PostMessage(IntPtr hWnd, uint msg, UIntPtr wParam, IntPtr lParam){
|
|
||||||
NativeMethods.PostMessage(hWnd, msg, wParam, lParam);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void BroadcastMessage(uint msg, uint wParam, int lParam){
|
public static void BroadcastMessage(uint msg, uint wParam, int lParam){
|
||||||
NativeMethods.PostMessage(NativeMethods.HWND_BROADCAST, msg, new UIntPtr(wParam), new IntPtr(lParam));
|
NativeMethods.PostMessage(NativeMethods.HWND_BROADCAST, msg, new UIntPtr(wParam), new IntPtr(lParam));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void BroadcastMessage(uint msg, UIntPtr wParam, IntPtr lParam){
|
|
||||||
NativeMethods.PostMessage(NativeMethods.HWND_BROADCAST, msg, wParam, lParam);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static uint RegisterMessage(string name){
|
public static uint RegisterMessage(string name){
|
||||||
return NativeMethods.RegisterWindowMessage(name);
|
return NativeMethods.RegisterWindowMessage(name);
|
||||||
}
|
}
|
||||||
|
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
|
|||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion("1.0.0.0")]
|
[assembly: AssemblyVersion("1.1.0.0")]
|
||||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
[assembly: AssemblyFileVersion("1.1.0.0")]
|
||||||
|
@@ -5,9 +5,6 @@ namespace TweetLib.Communication.Utils{
|
|||||||
static class NativeMethods{
|
static class NativeMethods{
|
||||||
public static readonly IntPtr HWND_BROADCAST = new IntPtr(0xFFFF);
|
public static readonly IntPtr HWND_BROADCAST = new IntPtr(0xFFFF);
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
|
||||||
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, UIntPtr wParam, IntPtr lParam);
|
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
[DllImport("user32.dll")]
|
||||||
public static extern bool PostMessage(IntPtr hWnd, uint msg, UIntPtr wParam, IntPtr lParam);
|
public static extern bool PostMessage(IntPtr hWnd, uint msg, UIntPtr wParam, IntPtr lParam);
|
||||||
|
|
||||||
|
@@ -6,6 +6,8 @@ using TweetLib.Communication;
|
|||||||
|
|
||||||
namespace TweetDuck.Browser{
|
namespace TweetDuck.Browser{
|
||||||
static class Program{
|
static class Program{
|
||||||
|
internal const string Version = "1.2.0.0";
|
||||||
|
|
||||||
private static int Main(string[] args){
|
private static int Main(string[] args){
|
||||||
SubProcess.EnableHighDPISupport();
|
SubProcess.EnableHighDPISupport();
|
||||||
|
|
||||||
@@ -20,7 +22,8 @@ namespace TweetDuck.Browser{
|
|||||||
else return SubProcess.ExecuteProcess();
|
else return SubProcess.ExecuteProcess();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class RendererProcess : SubProcess{
|
private sealed class RendererProcess : SubProcess{
|
||||||
|
// ReSharper disable once ParameterTypeCanBeEnumerable.Local
|
||||||
public RendererProcess(string[] args) : base(args){}
|
public RendererProcess(string[] args) : base(args){}
|
||||||
|
|
||||||
public override void OnBrowserCreated(CefBrowserWrapper wrapper){
|
public override void OnBrowserCreated(CefBrowserWrapper wrapper){
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using TweetDuck.Browser;
|
||||||
|
|
||||||
// General Information about an assembly is controlled through the following
|
// General Information about an assembly is controlled through the following
|
||||||
// set of attributes. Change these attribute values to modify the information
|
// set of attributes. Change these attribute values to modify the information
|
||||||
@@ -31,5 +32,5 @@ using System.Runtime.InteropServices;
|
|||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion("1.1.0.0")]
|
[assembly: AssemblyVersion(Program.Version)]
|
||||||
[assembly: AssemblyFileVersion("1.1.0.0")]
|
[assembly: AssemblyFileVersion(Program.Version)]
|
||||||
|
138
tests/Configuration/TestUserConfig.cs
Normal file
138
tests/Configuration/TestUserConfig.cs
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics.Contracts;
|
||||||
|
using System.IO;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using TweetDuck.Configuration;
|
||||||
|
using TweetDuck.Core;
|
||||||
|
|
||||||
|
namespace UnitTests.Configuration{
|
||||||
|
[TestClass]
|
||||||
|
public class TestUserConfig : UnitTestIO{
|
||||||
|
private static void WriteTestConfig(string file, bool withBackup){
|
||||||
|
UserConfig cfg = UserConfig.Load(file);
|
||||||
|
cfg.ZoomLevel = 123;
|
||||||
|
cfg.Save();
|
||||||
|
|
||||||
|
if (withBackup){
|
||||||
|
cfg.Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Pure] // used to display a warning when not using the return value
|
||||||
|
private static bool CheckTestConfig(string file){
|
||||||
|
return UserConfig.Load(file).ZoomLevel == 123;
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestMissing(){
|
||||||
|
Assert.IsNotNull(UserConfig.Load("missing"));
|
||||||
|
Assert.IsFalse(File.Exists("missing"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestBasic(){
|
||||||
|
Assert.IsFalse(CheckTestConfig("basic"));
|
||||||
|
WriteTestConfig("basic", false);
|
||||||
|
Assert.IsTrue(CheckTestConfig("basic"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestBackupName(){
|
||||||
|
Assert.AreEqual("name.bak", UserConfig.GetBackupFile("name"));
|
||||||
|
Assert.AreEqual("name.cfg.bak", UserConfig.GetBackupFile("name.cfg"));
|
||||||
|
Assert.AreEqual("name.bak.bak", UserConfig.GetBackupFile("name.bak"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestBackupCreate(){
|
||||||
|
WriteTestConfig("nobackup", false);
|
||||||
|
Assert.IsTrue(File.Exists("nobackup"));
|
||||||
|
Assert.IsFalse(File.Exists(UserConfig.GetBackupFile("nobackup")));
|
||||||
|
|
||||||
|
WriteTestConfig("withbackup", true);
|
||||||
|
Assert.IsTrue(File.Exists("withbackup"));
|
||||||
|
Assert.IsTrue(File.Exists(UserConfig.GetBackupFile("withbackup")));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestBackupRestore(){
|
||||||
|
WriteTestConfig("gone", true);
|
||||||
|
Assert.IsTrue(File.Exists("gone"));
|
||||||
|
Assert.IsTrue(File.Exists(UserConfig.GetBackupFile("gone")));
|
||||||
|
File.Delete("gone");
|
||||||
|
Assert.IsTrue(CheckTestConfig("gone"));
|
||||||
|
|
||||||
|
WriteTestConfig("corrupted", true);
|
||||||
|
Assert.IsTrue(File.Exists("corrupted"));
|
||||||
|
Assert.IsTrue(File.Exists(UserConfig.GetBackupFile("corrupted")));
|
||||||
|
File.WriteAllText("corrupted", "oh no corrupt");
|
||||||
|
Assert.IsTrue(CheckTestConfig("corrupted"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestReload(){
|
||||||
|
UserConfig cfg = UserConfig.Load("reloaded");
|
||||||
|
cfg.ZoomLevel = 123;
|
||||||
|
cfg.Save();
|
||||||
|
|
||||||
|
cfg.ZoomLevel = 200;
|
||||||
|
Assert.IsTrue(cfg.Reload());
|
||||||
|
Assert.AreEqual(123, cfg.ZoomLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestReset(){
|
||||||
|
UserConfig cfg = UserConfig.Load("reset");
|
||||||
|
cfg.ZoomLevel = 123;
|
||||||
|
cfg.Save();
|
||||||
|
|
||||||
|
File.Delete("reset");
|
||||||
|
Assert.IsTrue(cfg.Reload());
|
||||||
|
Assert.AreEqual(100, cfg.ZoomLevel);
|
||||||
|
Assert.IsTrue(File.Exists("reset"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestEventsNoTrigger(){
|
||||||
|
void Fail(object sender, EventArgs args) => Assert.Fail();
|
||||||
|
|
||||||
|
UserConfig cfg = UserConfig.Load("events");
|
||||||
|
cfg.MuteNotifications = true;
|
||||||
|
cfg.TrayBehavior = TrayIcon.Behavior.Combined;
|
||||||
|
cfg.ZoomLevel = 99;
|
||||||
|
|
||||||
|
cfg.MuteToggled += Fail;
|
||||||
|
cfg.TrayBehaviorChanged += Fail;
|
||||||
|
cfg.ZoomLevelChanged += Fail;
|
||||||
|
|
||||||
|
cfg.MuteNotifications = true;
|
||||||
|
cfg.TrayBehavior = TrayIcon.Behavior.Combined;
|
||||||
|
cfg.ZoomLevel = 99;
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestEventsTrigger(){
|
||||||
|
int triggers = 0;
|
||||||
|
void Trigger(object sender, EventArgs args) => ++triggers;
|
||||||
|
|
||||||
|
UserConfig cfg = UserConfig.Load("events");
|
||||||
|
cfg.MuteNotifications = false;
|
||||||
|
cfg.TrayBehavior = TrayIcon.Behavior.Disabled;
|
||||||
|
cfg.ZoomLevel = 100;
|
||||||
|
|
||||||
|
cfg.MuteToggled += Trigger;
|
||||||
|
cfg.TrayBehaviorChanged += Trigger;
|
||||||
|
cfg.ZoomLevelChanged += Trigger;
|
||||||
|
|
||||||
|
cfg.MuteNotifications = true;
|
||||||
|
cfg.TrayBehavior = TrayIcon.Behavior.Combined;
|
||||||
|
cfg.ZoomLevel = 99;
|
||||||
|
|
||||||
|
cfg.MuteNotifications = false;
|
||||||
|
cfg.TrayBehavior = TrayIcon.Behavior.Disabled;
|
||||||
|
cfg.ZoomLevel = 100;
|
||||||
|
|
||||||
|
Assert.AreEqual(6, triggers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -6,33 +6,36 @@ namespace UnitTests.Core{
|
|||||||
public class TestBrowserUtils{
|
public class TestBrowserUtils{
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void TestIsValidUrl(){
|
public void TestIsValidUrl(){
|
||||||
Assert.IsTrue(BrowserUtils.IsValidUrl("http://google.com")); // base
|
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("http://google.com")); // base
|
||||||
Assert.IsTrue(BrowserUtils.IsValidUrl("http://www.google.com")); // www.
|
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("http://www.google.com")); // www.
|
||||||
Assert.IsTrue(BrowserUtils.IsValidUrl("http://google.co.uk")); // co.uk
|
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("http://google.co.uk")); // co.uk
|
||||||
|
|
||||||
Assert.IsTrue(BrowserUtils.IsValidUrl("https://google.com")); // https
|
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("https://google.com")); // https
|
||||||
Assert.IsTrue(BrowserUtils.IsValidUrl("ftp://google.com")); // ftp
|
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("ftp://google.com")); // ftp
|
||||||
Assert.IsTrue(BrowserUtils.IsValidUrl("mailto:someone@google.com")); // mailto
|
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("mailto:someone@google.com")); // mailto
|
||||||
|
|
||||||
Assert.IsTrue(BrowserUtils.IsValidUrl("http://google.com/")); // trailing slash
|
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("http://google.com/")); // trailing slash
|
||||||
Assert.IsTrue(BrowserUtils.IsValidUrl("http://google.com/?")); // trailing question mark
|
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("http://google.com/?")); // trailing question mark
|
||||||
Assert.IsTrue(BrowserUtils.IsValidUrl("http://google.com/?a=5&b=x")); // parameters
|
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("http://google.com/?a=5&b=x")); // parameters
|
||||||
Assert.IsTrue(BrowserUtils.IsValidUrl("http://google.com/#hash")); // parameters + hash
|
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("http://google.com/#hash")); // parameters + hash
|
||||||
Assert.IsTrue(BrowserUtils.IsValidUrl("http://google.com/?a=5&b=x#hash")); // parameters + hash
|
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("http://google.com/?a=5&b=x#hash")); // parameters + hash
|
||||||
|
|
||||||
foreach(string tld in new string[]{ "accountants", "blackfriday", "cancerresearch", "coffee", "cool", "foo", "travelersinsurance" }){
|
foreach(string tld in new string[]{ "accountants", "blackfriday", "cancerresearch", "coffee", "cool", "foo", "travelersinsurance" }){
|
||||||
Assert.IsTrue(BrowserUtils.IsValidUrl("http://test."+tld)); // long and unusual TLDs
|
Assert.AreEqual(BrowserUtils.UrlCheckResult.Fine, BrowserUtils.CheckUrl("http://test."+tld)); // long and unusual TLDs
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.IsFalse(BrowserUtils.IsValidUrl("explorer")); // file
|
Assert.AreEqual(BrowserUtils.UrlCheckResult.Tracking, BrowserUtils.CheckUrl("http://t.co/abc")); // tracking
|
||||||
Assert.IsFalse(BrowserUtils.IsValidUrl("explorer.exe")); // file
|
Assert.AreEqual(BrowserUtils.UrlCheckResult.Tracking, BrowserUtils.CheckUrl("https://t.co/abc")); // tracking
|
||||||
Assert.IsFalse(BrowserUtils.IsValidUrl("://explorer.exe")); // file-sorta
|
|
||||||
Assert.IsFalse(BrowserUtils.IsValidUrl("file://explorer.exe")); // file-proper
|
|
||||||
|
|
||||||
Assert.IsFalse(BrowserUtils.IsValidUrl("")); // empty
|
Assert.AreEqual(BrowserUtils.UrlCheckResult.Invalid, BrowserUtils.CheckUrl("explorer")); // file
|
||||||
Assert.IsFalse(BrowserUtils.IsValidUrl("lol")); // random
|
Assert.AreEqual(BrowserUtils.UrlCheckResult.Invalid, BrowserUtils.CheckUrl("explorer.exe")); // file
|
||||||
|
Assert.AreEqual(BrowserUtils.UrlCheckResult.Invalid, BrowserUtils.CheckUrl("://explorer.exe")); // file-sorta
|
||||||
|
Assert.AreEqual(BrowserUtils.UrlCheckResult.Invalid, BrowserUtils.CheckUrl("file://explorer.exe")); // file-proper
|
||||||
|
|
||||||
Assert.IsFalse(BrowserUtils.IsValidUrl("gopher://nobody.cares")); // lmao rekt
|
Assert.AreEqual(BrowserUtils.UrlCheckResult.Invalid, BrowserUtils.CheckUrl("")); // empty
|
||||||
|
Assert.AreEqual(BrowserUtils.UrlCheckResult.Invalid, BrowserUtils.CheckUrl("lol")); // random
|
||||||
|
|
||||||
|
Assert.AreEqual(BrowserUtils.UrlCheckResult.Invalid, BrowserUtils.CheckUrl("gopher://nobody.cares")); // lmao rekt
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
|
@@ -26,20 +26,20 @@ namespace UnitTests.Core{
|
|||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void TestImageQualityLink(){
|
public void TestImageQualityLink(){
|
||||||
Assert.AreEqual("https://pbs.twimg.com/profile_images/123", TwitterUtils.GetImageLink("https://pbs.twimg.com/profile_images/123", TwitterUtils.ImageQuality.Default));
|
Assert.AreEqual("https://pbs.twimg.com/profile_images/123", TwitterUtils.GetMediaLink("https://pbs.twimg.com/profile_images/123", TwitterUtils.ImageQuality.Default));
|
||||||
Assert.AreEqual("https://pbs.twimg.com/profile_images/123", TwitterUtils.GetImageLink("https://pbs.twimg.com/profile_images/123", TwitterUtils.ImageQuality.Orig));
|
Assert.AreEqual("https://pbs.twimg.com/profile_images/123", TwitterUtils.GetMediaLink("https://pbs.twimg.com/profile_images/123", TwitterUtils.ImageQuality.Orig));
|
||||||
|
|
||||||
Assert.AreEqual("https://pbs.twimg.com/profile_images/123.jpg", TwitterUtils.GetImageLink("https://pbs.twimg.com/profile_images/123.jpg", TwitterUtils.ImageQuality.Default));
|
Assert.AreEqual("https://pbs.twimg.com/profile_images/123.jpg", TwitterUtils.GetMediaLink("https://pbs.twimg.com/profile_images/123.jpg", TwitterUtils.ImageQuality.Default));
|
||||||
Assert.AreEqual("https://pbs.twimg.com/profile_images/123.jpg", TwitterUtils.GetImageLink("https://pbs.twimg.com/profile_images/123.jpg", TwitterUtils.ImageQuality.Orig));
|
Assert.AreEqual("https://pbs.twimg.com/profile_images/123.jpg", TwitterUtils.GetMediaLink("https://pbs.twimg.com/profile_images/123.jpg", TwitterUtils.ImageQuality.Orig));
|
||||||
|
|
||||||
Assert.AreEqual("https://pbs.twimg.com/media/123.jpg", TwitterUtils.GetImageLink("https://pbs.twimg.com/media/123.jpg", TwitterUtils.ImageQuality.Default));
|
Assert.AreEqual("https://pbs.twimg.com/media/123.jpg", TwitterUtils.GetMediaLink("https://pbs.twimg.com/media/123.jpg", TwitterUtils.ImageQuality.Default));
|
||||||
Assert.AreEqual("https://pbs.twimg.com/media/123.jpg:orig", TwitterUtils.GetImageLink("https://pbs.twimg.com/media/123.jpg", TwitterUtils.ImageQuality.Orig));
|
Assert.AreEqual("https://pbs.twimg.com/media/123.jpg:orig", TwitterUtils.GetMediaLink("https://pbs.twimg.com/media/123.jpg", TwitterUtils.ImageQuality.Orig));
|
||||||
|
|
||||||
Assert.AreEqual("https://pbs.twimg.com/media/123.jpg:small", TwitterUtils.GetImageLink("https://pbs.twimg.com/media/123.jpg:small", TwitterUtils.ImageQuality.Default));
|
Assert.AreEqual("https://pbs.twimg.com/media/123.jpg:small", TwitterUtils.GetMediaLink("https://pbs.twimg.com/media/123.jpg:small", TwitterUtils.ImageQuality.Default));
|
||||||
Assert.AreEqual("https://pbs.twimg.com/media/123.jpg:orig", TwitterUtils.GetImageLink("https://pbs.twimg.com/media/123.jpg:small", TwitterUtils.ImageQuality.Orig));
|
Assert.AreEqual("https://pbs.twimg.com/media/123.jpg:orig", TwitterUtils.GetMediaLink("https://pbs.twimg.com/media/123.jpg:small", TwitterUtils.ImageQuality.Orig));
|
||||||
|
|
||||||
Assert.AreEqual("https://pbs.twimg.com/media/123.jpg:large", TwitterUtils.GetImageLink("https://pbs.twimg.com/media/123.jpg:large", TwitterUtils.ImageQuality.Default));
|
Assert.AreEqual("https://pbs.twimg.com/media/123.jpg:large", TwitterUtils.GetMediaLink("https://pbs.twimg.com/media/123.jpg:large", TwitterUtils.ImageQuality.Default));
|
||||||
Assert.AreEqual("https://pbs.twimg.com/media/123.jpg:orig", TwitterUtils.GetImageLink("https://pbs.twimg.com/media/123.jpg:large", TwitterUtils.ImageQuality.Orig));
|
Assert.AreEqual("https://pbs.twimg.com/media/123.jpg:orig", TwitterUtils.GetMediaLink("https://pbs.twimg.com/media/123.jpg:large", TwitterUtils.ImageQuality.Orig));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,39 +6,39 @@ using TweetDuck.Data;
|
|||||||
|
|
||||||
namespace UnitTests.Data{
|
namespace UnitTests.Data{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public class TestCombinedFileStream{
|
public class TestCombinedFileStream : UnitTestIO{
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void TestNoFiles(){
|
public void TestNoFiles(){
|
||||||
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.WriteFile("cfs_empty"))){
|
using(CombinedFileStream cfs = new CombinedFileStream(File.OpenWrite("empty"))){
|
||||||
cfs.Flush();
|
cfs.Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.IsTrue(File.Exists("cfs_empty"));
|
Assert.IsTrue(File.Exists("empty"));
|
||||||
|
|
||||||
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.ReadFile("cfs_empty"))){
|
using(CombinedFileStream cfs = new CombinedFileStream(File.OpenRead("empty"))){
|
||||||
Assert.IsNull(cfs.ReadFile());
|
Assert.IsNull(cfs.ReadFile());
|
||||||
}
|
}
|
||||||
|
|
||||||
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.ReadFile("cfs_empty"))){
|
using(CombinedFileStream cfs = new CombinedFileStream(File.OpenRead("empty"))){
|
||||||
Assert.IsNull(cfs.SkipFile());
|
Assert.IsNull(cfs.SkipFile());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void TestEmptyFiles(){
|
public void TestEmptyFiles(){
|
||||||
TestUtils.WriteText("cfs_input_empty_1", string.Empty);
|
File.WriteAllText("input_empty_1", string.Empty);
|
||||||
TestUtils.WriteText("cfs_input_empty_2", string.Empty);
|
File.WriteAllText("input_empty_2", string.Empty);
|
||||||
|
|
||||||
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.WriteFile("cfs_blank_files"))){
|
using(CombinedFileStream cfs = new CombinedFileStream(File.OpenWrite("blank_files"))){
|
||||||
cfs.WriteFile("id1", "cfs_input_empty_1");
|
cfs.WriteFile("id1", "input_empty_1");
|
||||||
cfs.WriteFile("id2", "cfs_input_empty_2");
|
cfs.WriteFile("id2", "input_empty_2");
|
||||||
cfs.WriteFile("id2_clone", "cfs_input_empty_2");
|
cfs.WriteFile("id2_clone", "input_empty_2");
|
||||||
cfs.Flush();
|
cfs.Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.IsTrue(File.Exists("cfs_blank_files"));
|
Assert.IsTrue(File.Exists("blank_files"));
|
||||||
|
|
||||||
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.ReadFile("cfs_blank_files"))){
|
using(CombinedFileStream cfs = new CombinedFileStream(File.OpenRead("blank_files"))){
|
||||||
CombinedFileStream.Entry entry1 = cfs.ReadFile();
|
CombinedFileStream.Entry entry1 = cfs.ReadFile();
|
||||||
string entry2key = cfs.SkipFile();
|
string entry2key = cfs.SkipFile();
|
||||||
CombinedFileStream.Entry entry3 = cfs.ReadFile();
|
CombinedFileStream.Entry entry3 = cfs.ReadFile();
|
||||||
@@ -56,31 +56,29 @@ namespace UnitTests.Data{
|
|||||||
Assert.AreEqual("id2_clone", entry3.Identifier);
|
Assert.AreEqual("id2_clone", entry3.Identifier);
|
||||||
CollectionAssert.AreEqual(new string[0], entry3.KeyValue);
|
CollectionAssert.AreEqual(new string[0], entry3.KeyValue);
|
||||||
|
|
||||||
entry1.WriteToFile("cfs_blank_file_1");
|
entry1.WriteToFile("blank_file_1");
|
||||||
entry3.WriteToFile("cfs_blank_file_2");
|
entry3.WriteToFile("blank_file_2");
|
||||||
TestUtils.DeleteFileOnExit("cfs_blank_file_1");
|
|
||||||
TestUtils.DeleteFileOnExit("cfs_blank_file_2");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.IsTrue(File.Exists("cfs_blank_file_1"));
|
Assert.IsTrue(File.Exists("blank_file_1"));
|
||||||
Assert.IsTrue(File.Exists("cfs_blank_file_2"));
|
Assert.IsTrue(File.Exists("blank_file_2"));
|
||||||
Assert.AreEqual(string.Empty, TestUtils.ReadText("cfs_blank_file_1"));
|
Assert.AreEqual(string.Empty, File.ReadAllText("blank_file_1"));
|
||||||
Assert.AreEqual(string.Empty, TestUtils.ReadText("cfs_blank_file_2"));
|
Assert.AreEqual(string.Empty, File.ReadAllText("blank_file_2"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void TestTextFilesAndComplexKeys(){
|
public void TestTextFilesAndComplexKeys(){
|
||||||
TestUtils.WriteText("cfs_input_text_1", "Hello World!"+Environment.NewLine);
|
File.WriteAllText("input_text_1", "Hello World!"+Environment.NewLine);
|
||||||
|
|
||||||
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.WriteFile("cfs_text_files"))){
|
using(CombinedFileStream cfs = new CombinedFileStream(File.OpenWrite("text_files"))){
|
||||||
cfs.WriteFile(new string[]{ "key1", "a", "bb", "ccc", "dddd" }, "cfs_input_text_1");
|
cfs.WriteFile(new string[]{ "key1", "a", "bb", "ccc", "dddd" }, "input_text_1");
|
||||||
cfs.WriteFile(new string[]{ "key2", "a", "bb", "ccc", "dddd" }, "cfs_input_text_1");
|
cfs.WriteFile(new string[]{ "key2", "a", "bb", "ccc", "dddd" }, "input_text_1");
|
||||||
cfs.Flush();
|
cfs.Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.IsTrue(File.Exists("cfs_text_files"));
|
Assert.IsTrue(File.Exists("text_files"));
|
||||||
|
|
||||||
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.ReadFile("cfs_text_files"))){
|
using(CombinedFileStream cfs = new CombinedFileStream(File.OpenRead("text_files"))){
|
||||||
CombinedFileStream.Entry entry = cfs.ReadFile();
|
CombinedFileStream.Entry entry = cfs.ReadFile();
|
||||||
|
|
||||||
Assert.AreEqual("key2", cfs.SkipFile());
|
Assert.AreEqual("key2", cfs.SkipFile());
|
||||||
@@ -91,68 +89,65 @@ namespace UnitTests.Data{
|
|||||||
Assert.AreEqual("key1", entry.KeyName);
|
Assert.AreEqual("key1", entry.KeyName);
|
||||||
CollectionAssert.AreEqual(new string[]{ "a", "bb", "ccc", "dddd" }, entry.KeyValue);
|
CollectionAssert.AreEqual(new string[]{ "a", "bb", "ccc", "dddd" }, entry.KeyValue);
|
||||||
|
|
||||||
entry.WriteToFile("cfs_text_file_1");
|
entry.WriteToFile("text_file_1");
|
||||||
TestUtils.DeleteFileOnExit("cfs_text_file_1");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.IsTrue(File.Exists("cfs_text_file_1"));
|
Assert.IsTrue(File.Exists("text_file_1"));
|
||||||
Assert.AreEqual("Hello World!"+Environment.NewLine, TestUtils.ReadText("cfs_text_file_1"));
|
Assert.AreEqual("Hello World!"+Environment.NewLine, File.ReadAllText("text_file_1"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void TestEntryWriteWithDirectory(){
|
public void TestEntryWriteWithDirectory(){
|
||||||
if (Directory.Exists("cfs_directory")){
|
if (Directory.Exists("directory")){
|
||||||
Directory.Delete("cfs_directory", true);
|
Directory.Delete("directory", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
TestUtils.WriteText("cfs_input_dir_1", "test");
|
File.WriteAllText("input_dir_1", "test");
|
||||||
|
|
||||||
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.WriteFile("cfs_dir_test"))){
|
using(CombinedFileStream cfs = new CombinedFileStream(File.OpenWrite("dir_test"))){
|
||||||
cfs.WriteFile("key1", "cfs_input_dir_1");
|
cfs.WriteFile("key1", "input_dir_1");
|
||||||
cfs.WriteFile("key2", "cfs_input_dir_1");
|
cfs.WriteFile("key2", "input_dir_1");
|
||||||
cfs.WriteFile("key3", "cfs_input_dir_1");
|
cfs.WriteFile("key3", "input_dir_1");
|
||||||
cfs.WriteFile("key4", "cfs_input_dir_1");
|
cfs.WriteFile("key4", "input_dir_1");
|
||||||
cfs.Flush();
|
cfs.Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.IsTrue(File.Exists("cfs_dir_test"));
|
Assert.IsTrue(File.Exists("dir_test"));
|
||||||
|
|
||||||
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.ReadFile("cfs_dir_test"))){
|
using(CombinedFileStream cfs = new CombinedFileStream(File.OpenRead("dir_test"))){
|
||||||
try{
|
try{
|
||||||
cfs.ReadFile().WriteToFile("cfs_directory/cfs_dir_test_file", false);
|
cfs.ReadFile().WriteToFile("directory/dir_test_file", false);
|
||||||
Assert.Fail("WriteToFile did not trigger an exception.");
|
Assert.Fail("WriteToFile did not trigger an exception.");
|
||||||
}catch(DirectoryNotFoundException){}
|
}catch(DirectoryNotFoundException){}
|
||||||
|
|
||||||
cfs.ReadFile().WriteToFile("cfs_directory/cfs_dir_test_file", true);
|
cfs.ReadFile().WriteToFile("directory/dir_test_file", true);
|
||||||
cfs.ReadFile().WriteToFile("cfs_dir_test_file", true);
|
cfs.ReadFile().WriteToFile("dir_test_file", true);
|
||||||
cfs.ReadFile().WriteToFile("cfs_dir_test_file.txt", true);
|
cfs.ReadFile().WriteToFile("dir_test_file.txt", true);
|
||||||
TestUtils.DeleteFileOnExit("cfs_dir_test_file");
|
|
||||||
TestUtils.DeleteFileOnExit("cfs_dir_test_file.txt");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.IsTrue(Directory.Exists("cfs_directory"));
|
Assert.IsTrue(Directory.Exists("directory"));
|
||||||
Assert.IsTrue(File.Exists("cfs_directory/cfs_dir_test_file"));
|
Assert.IsTrue(File.Exists("directory/dir_test_file"));
|
||||||
Assert.IsTrue(File.Exists("cfs_dir_test_file"));
|
Assert.IsTrue(File.Exists("dir_test_file"));
|
||||||
Assert.IsTrue(File.Exists("cfs_dir_test_file.txt"));
|
Assert.IsTrue(File.Exists("dir_test_file.txt"));
|
||||||
Assert.AreEqual("test", TestUtils.ReadText("cfs_directory/cfs_dir_test_file"));
|
Assert.AreEqual("test", File.ReadAllText("directory/dir_test_file"));
|
||||||
Assert.AreEqual("test", TestUtils.ReadText("cfs_dir_test_file"));
|
Assert.AreEqual("test", File.ReadAllText("dir_test_file"));
|
||||||
Assert.AreEqual("test", TestUtils.ReadText("cfs_dir_test_file.txt"));
|
Assert.AreEqual("test", File.ReadAllText("dir_test_file.txt"));
|
||||||
|
|
||||||
Directory.Delete("cfs_directory", true);
|
Directory.Delete("directory", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void TestLongIdentifierSuccess(){
|
public void TestLongIdentifierSuccess(){
|
||||||
TestUtils.WriteText("cfs_long_identifier_fail_in", "test");
|
File.WriteAllText("long_identifier_fail_in", "test");
|
||||||
|
|
||||||
string identifier = string.Join("", Enumerable.Repeat("x", 255));
|
string identifier = string.Join("", Enumerable.Repeat("x", 255));
|
||||||
|
|
||||||
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.WriteFile("cfs_long_identifier_success"))){
|
using(CombinedFileStream cfs = new CombinedFileStream(File.OpenWrite("long_identifier_success"))){
|
||||||
cfs.WriteFile(identifier, "cfs_long_identifier_fail_in");
|
cfs.WriteFile(identifier, "long_identifier_fail_in");
|
||||||
cfs.Flush();
|
cfs.Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.ReadFile("cfs_long_identifier_success"))){
|
using(CombinedFileStream cfs = new CombinedFileStream(File.OpenRead("long_identifier_success"))){
|
||||||
Assert.AreEqual(identifier, cfs.ReadFile().Identifier);
|
Assert.AreEqual(identifier, cfs.ReadFile().Identifier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,10 +155,10 @@ namespace UnitTests.Data{
|
|||||||
[TestMethod]
|
[TestMethod]
|
||||||
[ExpectedException(typeof(ArgumentOutOfRangeException))]
|
[ExpectedException(typeof(ArgumentOutOfRangeException))]
|
||||||
public void TestLongIdentifierFail(){
|
public void TestLongIdentifierFail(){
|
||||||
TestUtils.WriteText("cfs_long_identifier_fail_in", "test");
|
File.WriteAllText("long_identifier_fail_in", "test");
|
||||||
|
|
||||||
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.WriteFile("cfs_long_identifier_fail"))){
|
using(CombinedFileStream cfs = new CombinedFileStream(File.OpenWrite("long_identifier_fail"))){
|
||||||
cfs.WriteFile(string.Join("", Enumerable.Repeat("x", 256)), "cfs_long_identifier_fail_in");
|
cfs.WriteFile(string.Join("", Enumerable.Repeat("x", 256)), "long_identifier_fail_in");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,7 +5,7 @@ using TweetDuck.Data.Serialization;
|
|||||||
|
|
||||||
namespace UnitTests.Data{
|
namespace UnitTests.Data{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public class TestFileSerializer{
|
public class TestFileSerializer : UnitTestIO{
|
||||||
private enum TestEnum{
|
private enum TestEnum{
|
||||||
A, B, C, D, E
|
A, B, C, D, E
|
||||||
}
|
}
|
||||||
@@ -30,13 +30,11 @@ namespace UnitTests.Data{
|
|||||||
TestEnum = TestEnum.D
|
TestEnum = TestEnum.D
|
||||||
};
|
};
|
||||||
|
|
||||||
serializer.Write("serialized_basic", write);
|
serializer.Write("basic_wr", write);
|
||||||
|
Assert.IsTrue(File.Exists("basic_wr"));
|
||||||
Assert.IsTrue(File.Exists("serialized_basic"));
|
|
||||||
TestUtils.DeleteFileOnExit("serialized_basic");
|
|
||||||
|
|
||||||
SerializationTestBasic read = new SerializationTestBasic();
|
SerializationTestBasic read = new SerializationTestBasic();
|
||||||
serializer.Read("serialized_basic", read);
|
serializer.Read("basic_wr", read);
|
||||||
|
|
||||||
Assert.IsTrue(read.TestBool);
|
Assert.IsTrue(read.TestBool);
|
||||||
Assert.AreEqual(-100, read.TestInt);
|
Assert.AreEqual(-100, read.TestInt);
|
||||||
|
@@ -1,65 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
using System.Linq;
|
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
||||||
|
|
||||||
namespace UnitTests{
|
|
||||||
public static class TestUtils{
|
|
||||||
private static readonly HashSet<string> CreatedFiles = new HashSet<string>();
|
|
||||||
|
|
||||||
public static void WriteText(string file, string text){
|
|
||||||
DeleteFileOnExit(file);
|
|
||||||
File.WriteAllText(file, text, Encoding.UTF8);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void WriteLines(string file, IEnumerable<string> lines){
|
|
||||||
DeleteFileOnExit(file);
|
|
||||||
File.WriteAllLines(file, lines, Encoding.UTF8);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static FileStream WriteFile(string file){
|
|
||||||
DeleteFileOnExit(file);
|
|
||||||
return new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string ReadText(string file){
|
|
||||||
try{
|
|
||||||
return File.ReadAllText(file, Encoding.UTF8);
|
|
||||||
}catch(Exception){
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<string> ReadLines(string file){
|
|
||||||
try{
|
|
||||||
return File.ReadLines(file, Encoding.UTF8);
|
|
||||||
}catch(Exception){
|
|
||||||
return Enumerable.Empty<string>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static FileStream ReadFile(string file){
|
|
||||||
return new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.None);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void DeleteFileOnExit(string file){
|
|
||||||
CreatedFiles.Add(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestClass]
|
|
||||||
public static class Cleanup{
|
|
||||||
[AssemblyCleanup]
|
|
||||||
public static void DeleteFilesOnExit(){
|
|
||||||
foreach(string file in CreatedFiles){
|
|
||||||
try{
|
|
||||||
File.Delete(file);
|
|
||||||
}catch(Exception){
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
32
tests/UnitTestIO.cs
Normal file
32
tests/UnitTestIO.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
|
||||||
|
namespace UnitTests{
|
||||||
|
[TestClass]
|
||||||
|
public class UnitTestIO{
|
||||||
|
private static readonly HashSet<string> CreatedFolders = new HashSet<string>();
|
||||||
|
|
||||||
|
[TestInitialize]
|
||||||
|
public void InitTest(){
|
||||||
|
string folder = Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, GetType().Name);
|
||||||
|
CreatedFolders.Add(folder);
|
||||||
|
Directory.CreateDirectory(folder);
|
||||||
|
Directory.SetCurrentDirectory(folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
[AssemblyCleanup]
|
||||||
|
public static void DeleteFilesOnExit(){
|
||||||
|
Directory.SetCurrentDirectory(AppDomain.CurrentDomain.SetupInformation.ApplicationBase);
|
||||||
|
|
||||||
|
foreach(string folder in CreatedFolders){
|
||||||
|
try{
|
||||||
|
Directory.Delete(folder, true);
|
||||||
|
}catch(Exception){
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -47,6 +47,7 @@
|
|||||||
</Otherwise>
|
</Otherwise>
|
||||||
</Choose>
|
</Choose>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="Configuration\TestUserConfig.cs" />
|
||||||
<Compile Include="Core\TestStringUtils.cs" />
|
<Compile Include="Core\TestStringUtils.cs" />
|
||||||
<Compile Include="Core\TestTwitterUtils.cs" />
|
<Compile Include="Core\TestTwitterUtils.cs" />
|
||||||
<Compile Include="Data\TestCombinedFileStream.cs" />
|
<Compile Include="Data\TestCombinedFileStream.cs" />
|
||||||
@@ -56,7 +57,7 @@
|
|||||||
<Compile Include="Data\TestInjectedHTML.cs" />
|
<Compile Include="Data\TestInjectedHTML.cs" />
|
||||||
<Compile Include="Data\TestTwoKeyDictionary.cs" />
|
<Compile Include="Data\TestTwoKeyDictionary.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="TestUtils.cs" />
|
<Compile Include="UnitTestIO.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\TweetDuck.csproj">
|
<ProjectReference Include="..\TweetDuck.csproj">
|
||||||
|
@@ -5,7 +5,7 @@ using WMPLib;
|
|||||||
namespace TweetDuck.Video.Controls{
|
namespace TweetDuck.Video.Controls{
|
||||||
[DesignTimeVisible(true)]
|
[DesignTimeVisible(true)]
|
||||||
[Clsid("{6bf52a52-394a-11d3-b153-00c04f79faa6}")]
|
[Clsid("{6bf52a52-394a-11d3-b153-00c04f79faa6}")]
|
||||||
class ControlWMP : AxHost{
|
sealed class ControlWMP : AxHost{
|
||||||
public WindowsMediaPlayer Ocx { get; private set; }
|
public WindowsMediaPlayer Ocx { get; private set; }
|
||||||
|
|
||||||
public ControlWMP() : base("6bf52a52-394a-11d3-b153-00c04f79faa6"){}
|
public ControlWMP() : base("6bf52a52-394a-11d3-b153-00c04f79faa6"){}
|
40
video/Controls/LabelTooltip.cs
Normal file
40
video/Controls/LabelTooltip.cs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace TweetDuck.Video.Controls{
|
||||||
|
sealed class LabelTooltip : Label{
|
||||||
|
public LabelTooltip(){
|
||||||
|
Visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AttachTooltip(Control control, bool followCursor, string tooltip){
|
||||||
|
AttachTooltip(control, followCursor, args => tooltip);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AttachTooltip(Control control, bool followCursor, Func<MouseEventArgs, string> tooltipFunc){
|
||||||
|
control.MouseMove += (sender, args) => {
|
||||||
|
SuspendLayout();
|
||||||
|
|
||||||
|
Form form = control.FindForm();
|
||||||
|
System.Diagnostics.Debug.Assert(form != null);
|
||||||
|
|
||||||
|
Text = tooltipFunc(args);
|
||||||
|
|
||||||
|
Point loc = form.PointToClient(control.Parent.PointToScreen(new Point(control.Location.X+(followCursor ? args.X : control.Width/2), 0)));
|
||||||
|
loc.X = Math.Max(0, Math.Min(form.Width-Width, loc.X-Width/2));
|
||||||
|
loc.Y -= Height-Margin.Top+Margin.Bottom;
|
||||||
|
Location = loc;
|
||||||
|
|
||||||
|
ResumeLayout();
|
||||||
|
Visible = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
control.MouseLeave += control_MouseLeave;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void control_MouseLeave(object sender, EventArgs e){
|
||||||
|
Visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -6,29 +6,37 @@ namespace TweetDuck.Video.Controls{
|
|||||||
private readonly SolidBrush brushFore;
|
private readonly SolidBrush brushFore;
|
||||||
private readonly SolidBrush brushHover;
|
private readonly SolidBrush brushHover;
|
||||||
private readonly SolidBrush brushOverlap;
|
private readonly SolidBrush brushOverlap;
|
||||||
|
private readonly SolidBrush brushBack;
|
||||||
|
|
||||||
public SeekBar(){
|
public SeekBar(){
|
||||||
brushFore = new SolidBrush(Color.White);
|
brushFore = new SolidBrush(Color.White);
|
||||||
brushHover = new SolidBrush(Color.White);
|
brushHover = new SolidBrush(Color.White);
|
||||||
brushOverlap = new SolidBrush(Color.White);
|
brushOverlap = new SolidBrush(Color.White);
|
||||||
|
brushBack = new SolidBrush(Color.White);
|
||||||
|
|
||||||
SetStyle(ControlStyles.UserPaint, true);
|
SetStyle(ControlStyles.UserPaint, true);
|
||||||
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
|
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double GetProgress(int clientX){
|
||||||
|
return clientX/(Width-1.0);
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnPaint(PaintEventArgs e){
|
protected override void OnPaint(PaintEventArgs e){
|
||||||
if (brushFore.Color != ForeColor){
|
if (brushFore.Color != ForeColor){
|
||||||
brushFore.Color = ForeColor;
|
brushFore.Color = ForeColor;
|
||||||
brushHover.Color = Color.FromArgb(128, ForeColor);
|
brushHover.Color = Color.FromArgb(128, ForeColor);
|
||||||
brushOverlap.Color = Color.FromArgb(80+ForeColor.R*11/16, 80+ForeColor.G*11/16, 80+ForeColor.B*11/16);
|
brushOverlap.Color = Color.FromArgb(80+ForeColor.R*11/16, 80+ForeColor.G*11/16, 80+ForeColor.B*11/16);
|
||||||
|
brushBack.Color = Parent.BackColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle rect = e.ClipRectangle;
|
Rectangle rect = e.ClipRectangle;
|
||||||
Point cursor = PointToClient(Cursor.Position);
|
Point cursor = PointToClient(Cursor.Position);
|
||||||
int width = rect.Width;
|
int width = rect.Width-1;
|
||||||
int progress = (int)(width*((double)Value/Maximum));
|
int progress = (int)(width*((double)Value/Maximum));
|
||||||
|
|
||||||
rect.Width = progress;
|
rect.Width = progress;
|
||||||
|
rect.Height -= 1;
|
||||||
e.Graphics.FillRectangle(brushFore, rect);
|
e.Graphics.FillRectangle(brushFore, rect);
|
||||||
|
|
||||||
if (cursor.X >= 0 && cursor.Y >= 0 && cursor.X <= width && cursor.Y <= rect.Height){
|
if (cursor.X >= 0 && cursor.Y >= 0 && cursor.X <= width && cursor.Y <= rect.Height){
|
||||||
@@ -42,6 +50,17 @@ namespace TweetDuck.Video.Controls{
|
|||||||
e.Graphics.FillRectangle(brushHover, rect);
|
e.Graphics.FillRectangle(brushHover, rect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rect.X = width;
|
||||||
|
rect.Width = 1;
|
||||||
|
rect.Height += 1;
|
||||||
|
e.Graphics.FillRectangle(brushBack, rect);
|
||||||
|
|
||||||
|
rect.X = 0;
|
||||||
|
rect.Y = rect.Height-1;
|
||||||
|
rect.Width = width;
|
||||||
|
rect.Height = 1;
|
||||||
|
e.Graphics.FillRectangle(brushBack, rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool disposing){
|
protected override void Dispose(bool disposing){
|
||||||
@@ -51,6 +70,7 @@ namespace TweetDuck.Video.Controls{
|
|||||||
brushFore.Dispose();
|
brushFore.Dispose();
|
||||||
brushHover.Dispose();
|
brushHover.Dispose();
|
||||||
brushOverlap.Dispose();
|
brushOverlap.Dispose();
|
||||||
|
brushBack.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user