Compare commits
234 Commits
Author | SHA1 | Date | |
---|---|---|---|
98799734c5 | |||
96f491a666 | |||
29e541dbef | |||
1343b9c113 | |||
94920fd459 | |||
b2f3b245b7 | |||
15bc6c1d73 | |||
2c175b8d3a | |||
a48c17a769 | |||
03465c4ab0 | |||
b4e936c530 | |||
fb1482370a | |||
e831bc2bea | |||
c74c168c96 | |||
40b53fa40c | |||
3481cc0349 | |||
09abd889e9 | |||
330bbfbb31 | |||
6b7b690476 | |||
cea72801a7 | |||
04369e22a7 | |||
f1b16eab9a | |||
13646d9c90 | |||
17d762ce91 | |||
edb40adaa1 | |||
bc0809994a | |||
a3e3d517b0 | |||
d8b63a54ca | |||
b81e7583eb | |||
51f9ba3642 | |||
296626f7c7 | |||
5b2daf9746 | |||
9a6b615174 | |||
18f8d5b269 | |||
2867a875c9 | |||
ee2f5ae8cb | |||
bd5c301fb9 | |||
6df68629f7 | |||
be08fd4445 | |||
6f1afb94fb | |||
7401b8a52d | |||
c83b62ebaa | |||
108cf8923e | |||
4e26fd9d56 | |||
8c9168a4bf | |||
97da0b79e4 | |||
d7e5f6876b | |||
1b92b112e2 | |||
ca55119531 | |||
d9da14b5dc | |||
512b5666ac | |||
64977964e8 | |||
2bc13e0de6 | |||
b90c5f17cf | |||
7d8d0bd43b | |||
![]() |
54c1137927 | ||
e6655219ee | |||
5896f8e35a | |||
934cba7251 | |||
9cc1a11bef | |||
c1bc956d6d | |||
351b174b86 | |||
0b4aaf80dc | |||
c10c185817 | |||
327ef1cbee | |||
15eb823c7f | |||
54613e5242 | |||
df1352cbe3 | |||
0559afd972 | |||
afffca020e | |||
d663cc3f64 | |||
110d41e393 | |||
1a8823f592 | |||
6374a852b0 | |||
a10c7dd7c3 | |||
547c7ea417 | |||
760607995a | |||
4704197c09 | |||
093ac1ac40 | |||
9ed8b0d904 | |||
7346ce370d | |||
adefdadc19 | |||
703bce2d00 | |||
97928ecd84 | |||
be9ea7f64a | |||
ec2aaa8789 | |||
ab14b72526 | |||
d8e304f3c1 | |||
ea53ce361f | |||
2fce80b347 | |||
373c0b1cc3 | |||
e5e1b7e608 | |||
7e9221c9e0 | |||
6b849f854e | |||
831f6bc744 | |||
d282a7a537 | |||
fb2f1e3031 | |||
00a0da3df3 | |||
8c447b1ffb | |||
a4841175e8 | |||
9b139132a1 | |||
4a404ecabc | |||
aee758b559 | |||
be060d0386 | |||
0195378c10 | |||
bc804c6a53 | |||
76b15f1971 | |||
c4d43c9d5b | |||
e8d3e530de | |||
e145adec58 | |||
e2dad3e477 | |||
27bdbde171 | |||
e9ec27169c | |||
2e24cb634c | |||
beb9046055 | |||
e57301952c | |||
7411279e48 | |||
16acfa85b5 | |||
41ef37f3f0 | |||
00d8538726 | |||
6eeb3f9895 | |||
d19dca6ea5 | |||
2008ccdaa4 | |||
ba2e62de3a | |||
2b62eb254d | |||
31f72b7957 | |||
fdc4616875 | |||
b7de261d25 | |||
ae78a5a026 | |||
fd2cf5d4d7 | |||
9f0997be1a | |||
dbade7f854 | |||
3cdc1e190a | |||
36bede7211 | |||
46689bb700 | |||
13e1a6543c | |||
820ce9e845 | |||
f17806f4e8 | |||
3f5ffc9e10 | |||
aeb0842ab4 | |||
38837ae84c | |||
a4eb6935af | |||
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 |
5
.gitattributes
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Auto detect text files and perform LF normalization
|
||||||
|
* text=auto
|
||||||
|
|
||||||
|
*.txt text eof=lf
|
||||||
|
*.cs diff=csharp
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
@@ -4,7 +4,6 @@ using System.Diagnostics;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetLib.Communication;
|
|
||||||
|
|
||||||
namespace TweetDuck.Configuration{
|
namespace TweetDuck.Configuration{
|
||||||
sealed class LockManager{
|
sealed class LockManager{
|
||||||
@@ -136,15 +135,13 @@ namespace TweetDuck.Configuration{
|
|||||||
// Locking process
|
// Locking process
|
||||||
|
|
||||||
public bool RestoreLockingProcess(int failTimeout){
|
public bool RestoreLockingProcess(int failTimeout){
|
||||||
if (lockingProcess != null){
|
if (lockingProcess != null && lockingProcess.MainWindowHandle == IntPtr.Zero){ // restore if the original process is in tray
|
||||||
if (lockingProcess.MainWindowHandle == IntPtr.Zero){ // restore if the original process is in tray
|
NativeMethods.BroadcastMessage(Program.WindowRestoreMessage, (uint)lockingProcess.Id, 0);
|
||||||
Comms.BroadcastMessage(Program.WindowRestoreMessage, (uint)lockingProcess.Id, 0);
|
|
||||||
|
|
||||||
if (WindowsUtils.TrySleepUntil(() => CheckLockingProcessExited() || (lockingProcess.MainWindowHandle != IntPtr.Zero && lockingProcess.Responding), failTimeout, RetryDelay)){
|
if (WindowsUtils.TrySleepUntil(() => CheckLockingProcessExited() || (lockingProcess.MainWindowHandle != IntPtr.Zero && lockingProcess.Responding), failTimeout, RetryDelay)){
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -166,16 +163,13 @@ namespace TweetDuck.Configuration{
|
|||||||
lockingProcess = null;
|
lockingProcess = null;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}catch(Exception ex){
|
}catch(Exception ex) when (ex is InvalidOperationException || ex is Win32Exception){
|
||||||
if (ex is InvalidOperationException || ex is Win32Exception){
|
|
||||||
if (lockingProcess != null){
|
if (lockingProcess != null){
|
||||||
bool hasExited = CheckLockingProcessExited();
|
bool hasExited = CheckLockingProcessExited();
|
||||||
lockingProcess.Dispose();
|
lockingProcess.Dispose();
|
||||||
return hasExited;
|
return hasExited;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else throw;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@@ -1,13 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using TweetDuck.Core.Utils;
|
|
||||||
using TweetDuck.Data.Serialization;
|
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"));
|
||||||
@@ -34,14 +31,11 @@ namespace TweetDuck.Configuration{
|
|||||||
this.file = file;
|
this.file = file;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Save(){
|
public void Save(){
|
||||||
try{
|
try{
|
||||||
WindowsUtils.CreateDirectoryForFile(file);
|
|
||||||
Serializer.Write(file, this);
|
Serializer.Write(file, this);
|
||||||
return true;
|
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
Program.Reporter.HandleException("Configuration Error", "Could not save the system configuration file.", true, e);
|
Program.Reporter.HandleException("Configuration Error", "Could not save the system configuration file.", true, e);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,20 +43,7 @@ namespace TweetDuck.Configuration{
|
|||||||
SystemConfig config = new SystemConfig(file);
|
SystemConfig config = new SystemConfig(file);
|
||||||
|
|
||||||
try{
|
try{
|
||||||
Serializer.Read(file, config);
|
Serializer.ReadIfExists(file, config);
|
||||||
return config;
|
|
||||||
}catch(FileNotFoundException){
|
|
||||||
}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);
|
||||||
}
|
}
|
||||||
|
@@ -1,36 +1,16 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using TweetDuck.Core;
|
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Notification;
|
using TweetDuck.Core.Notification;
|
||||||
|
using TweetDuck.Core.Other;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetDuck.Data;
|
using TweetDuck.Data;
|
||||||
using TweetDuck.Data.Serialization;
|
using TweetDuck.Data.Serialization;
|
||||||
|
|
||||||
namespace TweetDuck.Configuration{
|
namespace TweetDuck.Configuration{
|
||||||
sealed class UserConfig{
|
sealed class UserConfig{
|
||||||
private static readonly FileSerializer<UserConfig> Serializer = new FileSerializer<UserConfig>{ HandleUnknownProperties = HandleUnknownProperties };
|
private static readonly FileSerializer<UserConfig> Serializer = new FileSerializer<UserConfig>();
|
||||||
|
|
||||||
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("BrowserMemoryThreshold");
|
|
||||||
|
|
||||||
if (data.Count == 0){
|
|
||||||
obj.Save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static UserConfig(){
|
static UserConfig(){
|
||||||
Serializer.RegisterTypeConverter(typeof(WindowState), WindowState.Converter);
|
Serializer.RegisterTypeConverter(typeof(WindowState), WindowState.Converter);
|
||||||
@@ -54,12 +34,18 @@ namespace TweetDuck.Configuration{
|
|||||||
|
|
||||||
// CONFIGURATION DATA
|
// CONFIGURATION DATA
|
||||||
|
|
||||||
|
public bool FirstRun { get; set; } = true;
|
||||||
|
public bool AllowDataCollection { get; set; } = false;
|
||||||
|
public bool ShowDataCollectionNotification { get; set; } = true;
|
||||||
|
|
||||||
public WindowState BrowserWindow { get; set; } = new WindowState();
|
public WindowState BrowserWindow { get; set; } = new WindowState();
|
||||||
public WindowState PluginsWindow { get; set; } = new WindowState();
|
public WindowState PluginsWindow { get; set; } = new WindowState();
|
||||||
|
|
||||||
public bool ExpandLinksOnHover { get; set; } = true;
|
public bool ExpandLinksOnHover { get; set; } = true;
|
||||||
public bool SwitchAccountSelectors { get; set; } = true;
|
public bool SwitchAccountSelectors { get; set; } = true;
|
||||||
|
public bool OpenSearchInFirstColumn { get; set; } = true;
|
||||||
public bool BestImageQuality { get; set; } = true;
|
public bool BestImageQuality { get; set; } = true;
|
||||||
|
public bool EnableAnimatedImages { get; set; } = true;
|
||||||
public bool EnableSpellCheck { get; set; } = false;
|
public bool EnableSpellCheck { get; set; } = false;
|
||||||
public int VideoPlayerVolume { get; set; } = 50;
|
public int VideoPlayerVolume { get; set; } = 50;
|
||||||
private int _zoomLevel = 100;
|
private int _zoomLevel = 100;
|
||||||
@@ -88,8 +74,9 @@ namespace TweetDuck.Configuration{
|
|||||||
|
|
||||||
public TweetNotification.Size NotificationSize { get; set; } = TweetNotification.Size.Auto;
|
public TweetNotification.Size NotificationSize { get; set; } = TweetNotification.Size.Auto;
|
||||||
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; } = 100;
|
||||||
|
|
||||||
|
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 +101,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,20 +112,18 @@ 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public double ZoomMultiplier => _zoomLevel/100.0;
|
|
||||||
|
|
||||||
public TrayIcon.Behavior TrayBehavior{
|
public TrayIcon.Behavior TrayBehavior{
|
||||||
get => _trayBehavior;
|
get => _trayBehavior;
|
||||||
|
|
||||||
set{
|
set{
|
||||||
if (_trayBehavior != value){
|
if (_trayBehavior != value){
|
||||||
_trayBehavior = value;
|
_trayBehavior = value;
|
||||||
TrayBehaviorChanged?.Invoke(this, new EventArgs());
|
TrayBehaviorChanged?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -153,14 +138,12 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Save(){
|
public void Save(){
|
||||||
try{
|
try{
|
||||||
WindowsUtils.CreateDirectoryForFile(file);
|
|
||||||
|
|
||||||
if (File.Exists(file)){
|
if (File.Exists(file)){
|
||||||
string backupFile = GetBackupFile(file);
|
string backupFile = GetBackupFile(file);
|
||||||
File.Delete(backupFile);
|
File.Delete(backupFile);
|
||||||
@@ -168,10 +151,32 @@ namespace TweetDuck.Configuration{
|
|||||||
}
|
}
|
||||||
|
|
||||||
Serializer.Write(file, this);
|
Serializer.Write(file, this);
|
||||||
return true;
|
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
Program.Reporter.HandleException("Configuration Error", "Could not save the configuration file.", true, e);
|
Program.Reporter.HandleException("Configuration Error", "Could not save the configuration file.", true, e);
|
||||||
return false;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reload(){
|
||||||
|
try{
|
||||||
|
LoadInternal(false);
|
||||||
|
}catch(FileNotFoundException){
|
||||||
|
try{
|
||||||
|
Serializer.Write(file, new UserConfig(file));
|
||||||
|
LoadInternal(false);
|
||||||
|
}catch(Exception e){
|
||||||
|
Program.Reporter.HandleException("Configuration Error", "Could not regenerate configuration file.", true, e);
|
||||||
|
}
|
||||||
|
}catch(Exception e){
|
||||||
|
Program.Reporter.HandleException("Configuration Error", "Could not reload configuration file.", true, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadInternal(bool backup){
|
||||||
|
Serializer.Read(backup ? GetBackupFile(file) : file, this);
|
||||||
|
|
||||||
|
if (NotificationScrollSpeed == 10){ // incorrect initial value
|
||||||
|
NotificationScrollSpeed = 100;
|
||||||
|
Save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,22 +186,23 @@ namespace TweetDuck.Configuration{
|
|||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -17,6 +17,7 @@ namespace TweetDuck.Core.Bridge{
|
|||||||
|
|
||||||
if (environment == Environment.Browser){
|
if (environment == Environment.Browser){
|
||||||
build.Append("x.switchAccountSelectors=").Append(Bool(Program.UserConfig.SwitchAccountSelectors));
|
build.Append("x.switchAccountSelectors=").Append(Bool(Program.UserConfig.SwitchAccountSelectors));
|
||||||
|
build.Append("x.openSearchInFirstColumn=").Append(Bool(Program.UserConfig.OpenSearchInFirstColumn));
|
||||||
build.Append("x.muteNotifications=").Append(Bool(Program.UserConfig.MuteNotifications));
|
build.Append("x.muteNotifications=").Append(Bool(Program.UserConfig.MuteNotifications));
|
||||||
build.Append("x.hasCustomNotificationSound=").Append(Bool(Program.UserConfig.NotificationSoundPath.Length > 0));
|
build.Append("x.hasCustomNotificationSound=").Append(Bool(Program.UserConfig.NotificationSoundPath.Length > 0));
|
||||||
build.Append("x.notificationMediaPreviews=").Append(Bool(Program.UserConfig.NotificationMediaPreviews));
|
build.Append("x.notificationMediaPreviews=").Append(Bool(Program.UserConfig.NotificationMediaPreviews));
|
||||||
|
@@ -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,22 @@ 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 FontSize { get; private set; }
|
||||||
public static string LastRightClickedImage = string.Empty;
|
public static string NotificationHeadLayout { get; private set; }
|
||||||
public static string LastHighlightedTweet = string.Empty;
|
|
||||||
public static string LastHighlightedQuotedTweet = string.Empty;
|
public static string LastHighlightedTweetUrl = string.Empty;
|
||||||
public static string LastHighlightedTweetAuthor = string.Empty;
|
public static string LastHighlightedQuoteUrl = string.Empty;
|
||||||
public static string[] LastHighlightedTweetImages = StringUtils.EmptyArray;
|
private static string LastHighlightedTweetAuthors = string.Empty;
|
||||||
public static Dictionary<string, string> SessionData = new Dictionary<string, string>(2);
|
private static string LastHighlightedTweetImages = string.Empty;
|
||||||
|
|
||||||
|
public static string[] LastHighlightedTweetAuthorsArray => LastHighlightedTweetAuthors.Split(';');
|
||||||
|
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;
|
FontSize = NotificationHeadLayout = null;
|
||||||
LastHighlightedTweetImages = StringUtils.EmptyArray;
|
LastHighlightedTweetUrl = LastHighlightedQuoteUrl = LastHighlightedTweetAuthors = LastHighlightedTweetImages = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RestoreSessionData(IFrame frame){
|
public static void RestoreSessionData(IFrame frame){
|
||||||
@@ -44,32 +50,29 @@ namespace TweetDuck.Core.Bridge{
|
|||||||
this.notification = notification;
|
this.notification = notification;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadFontSizeClass(string fsClass){
|
public void OnIntroductionClosed(bool showGuide, bool allowDataCollection){
|
||||||
form.InvokeAsyncSafe(() => {
|
form.InvokeAsyncSafe(() => {
|
||||||
TweetNotification.SetFontSizeClass(fsClass);
|
form.OnIntroductionClosed(showGuide, allowDataCollection);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadNotificationHeadContents(string headContents){
|
public void LoadNotificationLayout(string fontSize, string headLayout){
|
||||||
form.InvokeAsyncSafe(() => {
|
form.InvokeAsyncSafe(() => {
|
||||||
TweetNotification.SetHeadTag(headContents);
|
FontSize = fontSize;
|
||||||
|
NotificationHeadLayout = headLayout;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
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 +80,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));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,20 +117,20 @@ namespace TweetDuck.Core.Bridge{
|
|||||||
form.InvokeAsyncSafe(() => form.OnTweetScreenshotReady(html, width, height));
|
form.InvokeAsyncSafe(() => form.OnTweetScreenshotReady(html, width, height));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PlayVideo(string url){
|
public void PlayVideo(string url, string username){
|
||||||
form.InvokeAsyncSafe(() => form.PlayVideo(url));
|
form.InvokeAsyncSafe(() => form.PlayVideo(url, username));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void FixClipboard(){
|
public void FixClipboard(){
|
||||||
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){
|
||||||
@@ -145,13 +148,9 @@ namespace TweetDuck.Core.Bridge{
|
|||||||
|
|
||||||
public void CrashDebug(string message){
|
public void CrashDebug(string message){
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
Log(message);
|
System.Diagnostics.Debug.WriteLine(message);
|
||||||
System.Diagnostics.Debugger.Break();
|
System.Diagnostics.Debugger.Break();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Log(string data){
|
|
||||||
System.Diagnostics.Debug.WriteLine(data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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(){
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using System.Drawing;
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Controls{
|
namespace TweetDuck.Core.Controls{
|
||||||
@@ -13,7 +14,7 @@ namespace TweetDuck.Core.Controls{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void SetValueInstant(int value){
|
public void SetValueInstant(int value){
|
||||||
ControlExtensions.SetValueInstant(this, value);
|
ControlExtensions.SetValueInstant(this, Math.Max(Minimum, Math.Min(Maximum, value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnPaint(PaintEventArgs e){
|
protected override void OnPaint(PaintEventArgs e){
|
||||||
|
8
Core/FormBrowser.Designer.cs
generated
@@ -24,7 +24,7 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void InitializeComponent() {
|
private void InitializeComponent() {
|
||||||
this.components = new System.ComponentModel.Container();
|
this.components = new System.ComponentModel.Container();
|
||||||
this.trayIcon = new TweetDuck.Core.TrayIcon(this.components);
|
this.trayIcon = new TweetDuck.Core.Other.TrayIcon(this.components);
|
||||||
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
||||||
this.timerResize = new System.Windows.Forms.Timer(this.components);
|
this.timerResize = new System.Windows.Forms.Timer(this.components);
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
@@ -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);
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private TrayIcon trayIcon;
|
private TweetDuck.Core.Other.TrayIcon trayIcon;
|
||||||
private System.Windows.Forms.ToolTip toolTip;
|
private System.Windows.Forms.ToolTip toolTip;
|
||||||
private System.Windows.Forms.Timer timerResize;
|
private System.Windows.Forms.Timer timerResize;
|
||||||
}
|
}
|
||||||
|
@@ -1,25 +1,20 @@
|
|||||||
using CefSharp;
|
using System;
|
||||||
using CefSharp.WinForms;
|
|
||||||
using System;
|
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Configuration;
|
using TweetDuck.Configuration;
|
||||||
using TweetDuck.Core.Bridge;
|
using TweetDuck.Core.Bridge;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Handling;
|
using TweetDuck.Core.Handling;
|
||||||
using TweetDuck.Core.Handling.General;
|
|
||||||
using TweetDuck.Core.Notification;
|
using TweetDuck.Core.Notification;
|
||||||
using TweetDuck.Core.Notification.Screenshot;
|
using TweetDuck.Core.Notification.Screenshot;
|
||||||
using TweetDuck.Core.Other;
|
using TweetDuck.Core.Other;
|
||||||
|
using TweetDuck.Core.Other.Analytics;
|
||||||
using TweetDuck.Core.Other.Management;
|
using TweetDuck.Core.Other.Management;
|
||||||
using TweetDuck.Core.Other.Settings;
|
using TweetDuck.Core.Other.Settings;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetDuck.Plugins;
|
using TweetDuck.Plugins;
|
||||||
using TweetDuck.Plugins.Enums;
|
|
||||||
using TweetDuck.Plugins.Events;
|
using TweetDuck.Plugins.Events;
|
||||||
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{
|
||||||
@@ -45,20 +40,19 @@ namespace TweetDuck.Core{
|
|||||||
|
|
||||||
public string UpdateInstallerPath { get; private set; }
|
public string UpdateInstallerPath { get; private set; }
|
||||||
|
|
||||||
private readonly ChromiumWebBrowser browser;
|
private readonly TweetDeckBrowser browser;
|
||||||
private readonly PluginManager plugins;
|
private readonly PluginManager plugins;
|
||||||
private readonly UpdateHandler updates;
|
private readonly UpdateHandler updates;
|
||||||
private readonly FormNotificationTweet notification;
|
private readonly FormNotificationTweet notification;
|
||||||
private readonly ContextMenu contextMenu;
|
private readonly ContextMenu contextMenu;
|
||||||
private readonly MemoryUsageTracker memoryUsageTracker;
|
|
||||||
|
|
||||||
private bool isLoaded;
|
private bool isLoaded;
|
||||||
private bool isBrowserReady;
|
|
||||||
private FormWindowState prevState;
|
private FormWindowState prevState;
|
||||||
|
|
||||||
private TweetScreenshotManager notificationScreenshotManager;
|
private TweetScreenshotManager notificationScreenshotManager;
|
||||||
private SoundNotification soundNotification;
|
private SoundNotification soundNotification;
|
||||||
private VideoPlayer videoPlayer;
|
private VideoPlayer videoPlayer;
|
||||||
|
private AnalyticsManager analytics;
|
||||||
|
|
||||||
public FormBrowser(UpdaterSettings updaterSettings){
|
public FormBrowser(UpdaterSettings updaterSettings){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@@ -68,50 +62,21 @@ namespace TweetDuck.Core{
|
|||||||
this.plugins = new PluginManager(Program.PluginPath, Program.PluginConfigFilePath);
|
this.plugins = new PluginManager(Program.PluginPath, Program.PluginConfigFilePath);
|
||||||
this.plugins.Reloaded += plugins_Reloaded;
|
this.plugins.Reloaded += plugins_Reloaded;
|
||||||
this.plugins.Executed += plugins_Executed;
|
this.plugins.Executed += plugins_Executed;
|
||||||
this.plugins.PluginChangedState += plugins_PluginChangedState;
|
|
||||||
this.plugins.Reload();
|
this.plugins.Reload();
|
||||||
|
|
||||||
this.contextMenu = ContextMenuBrowser.CreateMenu(this);
|
this.notification = new FormNotificationTweet(this, plugins);
|
||||||
this.memoryUsageTracker = new MemoryUsageTracker("TDGF_tryRunCleanup");
|
|
||||||
|
|
||||||
this.notification = new FormNotificationTweet(this, plugins){
|
|
||||||
#if DEBUG
|
|
||||||
CanMoveWindow = () => (ModifierKeys & Keys.Alt) == Keys.Alt
|
|
||||||
#else
|
|
||||||
CanMoveWindow = () => false
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
this.notification.Show();
|
this.notification.Show();
|
||||||
|
|
||||||
this.browser = new ChromiumWebBrowser("https://tweetdeck.twitter.com/"){
|
this.browser = new TweetDeckBrowser(this, plugins, new TweetDeckBridge(this, notification));
|
||||||
MenuHandler = new ContextMenuBrowser(this),
|
this.browser.PageLoaded += browser_PageLoaded;
|
||||||
JsDialogHandler = new JavaScriptDialogHandler(),
|
|
||||||
KeyboardHandler = new KeyboardHandlerBrowser(this),
|
|
||||||
LifeSpanHandler = new LifeSpanHandler(),
|
|
||||||
RequestHandler = new RequestHandlerBrowser()
|
|
||||||
};
|
|
||||||
|
|
||||||
#if DEBUG
|
this.contextMenu = ContextMenuBrowser.CreateMenu(this);
|
||||||
this.browser.ConsoleMessage += BrowserUtils.HandleConsoleMessage;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
this.browser.LoadingStateChanged += browser_LoadingStateChanged;
|
|
||||||
this.browser.FrameLoadStart += browser_FrameLoadStart;
|
|
||||||
this.browser.FrameLoadEnd += browser_FrameLoadEnd;
|
|
||||||
this.browser.LoadError += browser_LoadError;
|
|
||||||
this.browser.RegisterAsyncJsObject("$TD", new TweetDeckBridge(this, notification));
|
|
||||||
this.browser.RegisterAsyncJsObject("$TDP", plugins.Bridge);
|
|
||||||
|
|
||||||
browser.BrowserSettings.BackgroundColor = (uint)TwitterUtils.BackgroundColor.ToArgb();
|
|
||||||
browser.Dock = DockStyle.None;
|
|
||||||
browser.Location = ControlExtensions.InvisibleLocation;
|
|
||||||
Controls.Add(browser);
|
|
||||||
|
|
||||||
Controls.Add(new MenuStrip{ Visible = false }); // fixes Alt freezing the program in Win 10 Anniversary Update
|
Controls.Add(new MenuStrip{ Visible = false }); // fixes Alt freezing the program in Win 10 Anniversary Update
|
||||||
|
|
||||||
Disposed += (sender, args) => {
|
Disposed += (sender, args) => {
|
||||||
memoryUsageTracker.Dispose();
|
Config.MuteToggled -= Config_MuteToggled;
|
||||||
|
Config.TrayBehaviorChanged -= Config_TrayBehaviorChanged;
|
||||||
|
|
||||||
browser.Dispose();
|
browser.Dispose();
|
||||||
contextMenu.Dispose();
|
contextMenu.Dispose();
|
||||||
@@ -119,18 +84,18 @@ namespace TweetDuck.Core{
|
|||||||
notificationScreenshotManager?.Dispose();
|
notificationScreenshotManager?.Dispose();
|
||||||
soundNotification?.Dispose();
|
soundNotification?.Dispose();
|
||||||
videoPlayer?.Dispose();
|
videoPlayer?.Dispose();
|
||||||
|
analytics?.Dispose();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Config.MuteToggled += Config_MuteToggled;
|
||||||
|
|
||||||
this.trayIcon.ClickRestore += trayIcon_ClickRestore;
|
this.trayIcon.ClickRestore += trayIcon_ClickRestore;
|
||||||
this.trayIcon.ClickClose += trayIcon_ClickClose;
|
this.trayIcon.ClickClose += trayIcon_ClickClose;
|
||||||
Config.TrayBehaviorChanged += Config_TrayBehaviorChanged;
|
Config.TrayBehaviorChanged += Config_TrayBehaviorChanged;
|
||||||
|
|
||||||
UpdateTrayIcon();
|
UpdateTrayIcon();
|
||||||
|
|
||||||
Config.MuteToggled += Config_MuteToggled;
|
this.updates = browser.CreateUpdateHandler(updaterSettings);
|
||||||
Config.ZoomLevelChanged += Config_ZoomLevelChanged;
|
|
||||||
|
|
||||||
this.updates = new UpdateHandler(browser, updaterSettings);
|
|
||||||
this.updates.UpdateAccepted += updates_UpdateAccepted;
|
this.updates.UpdateAccepted += updates_UpdateAccepted;
|
||||||
this.updates.UpdateDismissed += updates_UpdateDismissed;
|
this.updates.UpdateDismissed += updates_UpdateDismissed;
|
||||||
|
|
||||||
@@ -153,13 +118,9 @@ namespace TweetDuck.Core{
|
|||||||
Config.BrowserWindow.Restore(this, true);
|
Config.BrowserWindow.Restore(this, true);
|
||||||
prevState = WindowState;
|
prevState = WindowState;
|
||||||
isLoaded = true;
|
isLoaded = true;
|
||||||
}
|
|
||||||
|
|
||||||
private void OnBrowserReady(){
|
if (Config.AllowDataCollection){
|
||||||
if (!isBrowserReady){
|
analytics = new AnalyticsManager(this, plugins, Program.AnalyticsFilePath);
|
||||||
browser.Location = Point.Empty;
|
|
||||||
browser.Dock = DockStyle.Fill;
|
|
||||||
isBrowserReady = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,64 +128,7 @@ namespace TweetDuck.Core{
|
|||||||
trayIcon.Visible = Config.TrayBehavior.ShouldDisplayIcon();
|
trayIcon.Visible = Config.TrayBehavior.ShouldDisplayIcon();
|
||||||
}
|
}
|
||||||
|
|
||||||
// active event handlers
|
// event handlers
|
||||||
|
|
||||||
private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
|
|
||||||
if (!e.IsLoading){
|
|
||||||
foreach(string word in TwitterUtils.DictionaryWords){
|
|
||||||
browser.AddWordToDictionary(word);
|
|
||||||
}
|
|
||||||
|
|
||||||
BeginInvoke(new Action(OnBrowserReady));
|
|
||||||
browser.LoadingStateChanged -= browser_LoadingStateChanged;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void browser_FrameLoadStart(object sender, FrameLoadStartEventArgs e){
|
|
||||||
if (e.Frame.IsMain){
|
|
||||||
memoryUsageTracker.Stop();
|
|
||||||
|
|
||||||
if (Config.ZoomLevel != 100){
|
|
||||||
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Config.ZoomLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TwitterUtils.IsTwitterWebsite(e.Frame)){
|
|
||||||
ScriptLoader.ExecuteFile(e.Frame, "twitter.js");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
|
|
||||||
if (e.Frame.IsMain && TwitterUtils.IsTweetDeckWebsite(e.Frame)){
|
|
||||||
e.Frame.ExecuteJavaScriptAsync(TwitterUtils.BackgroundColorFix);
|
|
||||||
|
|
||||||
UpdateProperties(PropertyBridge.Environment.Browser);
|
|
||||||
TweetDeckBridge.RestoreSessionData(e.Frame);
|
|
||||||
ScriptLoader.ExecuteFile(e.Frame, "code.js");
|
|
||||||
ReinjectCustomCSS(Config.CustomBrowserCSS);
|
|
||||||
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Browser);
|
|
||||||
|
|
||||||
TweetDeckBridge.ResetStaticProperties();
|
|
||||||
|
|
||||||
if (Program.SystemConfig.EnableBrowserGCReload){
|
|
||||||
memoryUsageTracker.Start(this, e.Browser, Program.SystemConfig.BrowserMemoryThreshold);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void browser_LoadError(object sender, LoadErrorEventArgs e){
|
|
||||||
if (e.ErrorCode == CefErrorCode.Aborted){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!e.FailedUrl.StartsWith("http://td/", StringComparison.Ordinal)){
|
|
||||||
string errorPage = ScriptLoader.LoadResource("pages/error.html", true);
|
|
||||||
|
|
||||||
if (errorPage != null){
|
|
||||||
browser.LoadHtml(errorPage.Replace("{err}", BrowserUtils.GetErrorName(e.ErrorCode)), "http://td/error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void timerResize_Tick(object sender, EventArgs e){
|
private void timerResize_Tick(object sender, EventArgs e){
|
||||||
FormBrowser_ResizeEnd(this, e); // also stops timer
|
FormBrowser_ResizeEnd(this, e); // also stops timer
|
||||||
@@ -295,11 +199,7 @@ namespace TweetDuck.Core{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void Config_MuteToggled(object sender, EventArgs e){
|
private void Config_MuteToggled(object sender, EventArgs e){
|
||||||
UpdateProperties(PropertyBridge.Environment.Browser);
|
TriggerAnalyticsEvent(AnalyticsFile.Event.MuteNotification);
|
||||||
}
|
|
||||||
|
|
||||||
private void Config_ZoomLevelChanged(object sender, EventArgs e){
|
|
||||||
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Config.ZoomLevel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Config_TrayBehaviorChanged(object sender, EventArgs e){
|
private void Config_TrayBehaviorChanged(object sender, EventArgs e){
|
||||||
@@ -317,6 +217,38 @@ namespace TweetDuck.Core{
|
|||||||
ForceClose();
|
ForceClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void browser_PageLoaded(object sender, EventArgs e){
|
||||||
|
if (Config.ShowDataCollectionNotification){
|
||||||
|
this.InvokeAsyncSafe(() => {
|
||||||
|
if (!Config.FirstRun && Config.AllowDataCollection){
|
||||||
|
FormMessage form = new FormMessage("Anonymous Data Update", "Hi! You can now review your anonymous data report, and opt-out if you've changed your mind. Collected data will be used to focus development on most commonly used features. If you want to opt-out but still support the project, any feedback and donations are appreciated.", MessageBoxIcon.Information);
|
||||||
|
form.AddButton("OK", ControlType.Accept | ControlType.Focused);
|
||||||
|
|
||||||
|
Button btnReviewSettings = new Button{
|
||||||
|
Anchor = AnchorStyles.Bottom | AnchorStyles.Left,
|
||||||
|
Font = SystemFonts.MessageBoxFont,
|
||||||
|
Location = new Point(9, 12),
|
||||||
|
Margin = new Padding(0, 0, 48, 0),
|
||||||
|
Size = new Size(160, 26),
|
||||||
|
Text = "Review Feedback Options",
|
||||||
|
UseVisualStyleBackColor = true
|
||||||
|
};
|
||||||
|
|
||||||
|
btnReviewSettings.Click += (sender2, args2) => {
|
||||||
|
form.Close();
|
||||||
|
OpenSettings(typeof(TabSettingsFeedback));
|
||||||
|
};
|
||||||
|
|
||||||
|
form.AddActionControl(btnReviewSettings);
|
||||||
|
ShowChildForm(form);
|
||||||
|
}
|
||||||
|
|
||||||
|
Config.ShowDataCollectionNotification = false;
|
||||||
|
Config.Save();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void plugins_Reloaded(object sender, PluginErrorEventArgs e){
|
private void plugins_Reloaded(object sender, PluginErrorEventArgs e){
|
||||||
if (e.HasErrors){
|
if (e.HasErrors){
|
||||||
FormMessage.Error("Error Loading Plugins", "The following plugins will not be available until the issues are resolved:\n\n"+string.Join("\n\n", e.Errors), FormMessage.OK);
|
FormMessage.Error("Error Loading Plugins", "The following plugins will not be available until the issues are resolved:\n\n"+string.Join("\n\n", e.Errors), FormMessage.OK);
|
||||||
@@ -333,14 +265,15 @@ namespace TweetDuck.Core{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void plugins_PluginChangedState(object sender, PluginChangedStateEventArgs e){
|
private void updates_UpdateAccepted(object sender, UpdateEventArgs e){
|
||||||
browser.ExecuteScriptAsync("window.TDPF_setPluginState", e.Plugin, e.IsEnabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updates_UpdateAccepted(object sender, UpdateAcceptedEventArgs e){
|
|
||||||
this.InvokeAsyncSafe(() => {
|
this.InvokeAsyncSafe(() => {
|
||||||
FormManager.CloseAllDialogs();
|
FormManager.CloseAllDialogs();
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(Config.DismissedUpdate)){
|
||||||
|
Config.DismissedUpdate = null;
|
||||||
|
Config.Save();
|
||||||
|
}
|
||||||
|
|
||||||
updates.BeginUpdateDownload(this, e.UpdateInfo, update => {
|
updates.BeginUpdateDownload(this, e.UpdateInfo, update => {
|
||||||
if (update.DownloadStatus == UpdateDownloadStatus.Done){
|
if (update.DownloadStatus == UpdateDownloadStatus.Done){
|
||||||
UpdateInstallerPath = update.InstallerPath;
|
UpdateInstallerPath = update.InstallerPath;
|
||||||
@@ -351,9 +284,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 +311,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;
|
||||||
@@ -394,12 +327,13 @@ namespace TweetDuck.Core{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isBrowserReady && m.Msg == NativeMethods.WM_PARENTNOTIFY && (m.WParam.ToInt32() & 0xFFFF) == NativeMethods.WM_XBUTTONDOWN){
|
if (browser.Ready && m.Msg == NativeMethods.WM_PARENTNOTIFY && (m.WParam.ToInt32() & 0xFFFF) == NativeMethods.WM_XBUTTONDOWN){
|
||||||
if (videoPlayer != null && videoPlayer.Running){
|
if (videoPlayer != null && videoPlayer.Running){
|
||||||
videoPlayer.Close();
|
videoPlayer.Close();
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
browser.ExecuteScriptAsync("TDGF_onMouseClickExtra", (m.WParam.ToInt32() >> 16) & 0xFFFF);
|
browser.OnMouseClickExtra(m.WParam);
|
||||||
|
TriggerAnalyticsEvent(AnalyticsFile.Event.BrowserExtraMouseButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -408,11 +342,7 @@ namespace TweetDuck.Core{
|
|||||||
base.WndProc(ref m);
|
base.WndProc(ref m);
|
||||||
}
|
}
|
||||||
|
|
||||||
// notification helpers
|
// bridge methods
|
||||||
|
|
||||||
public FormNotificationMain CreateNotificationForm(bool enableContextMenu){
|
|
||||||
return new FormNotificationMain(this, plugins, enableContextMenu);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PauseNotification(){
|
public void PauseNotification(){
|
||||||
notification.PauseNotification();
|
notification.PauseNotification();
|
||||||
@@ -422,22 +352,49 @@ namespace TweetDuck.Core{
|
|||||||
notification.ResumeNotification();
|
notification.ResumeNotification();
|
||||||
}
|
}
|
||||||
|
|
||||||
// javascript calls
|
|
||||||
|
|
||||||
public void ReinjectCustomCSS(string css){
|
public void ReinjectCustomCSS(string css){
|
||||||
browser.ExecuteScriptAsync("TDGF_reinjectCustomCSS", css?.Replace(Environment.NewLine, " ") ?? string.Empty);
|
browser.ReinjectCustomCSS(css);
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateProperties(PropertyBridge.Environment environment){
|
|
||||||
browser.ExecuteScriptAsync(PropertyBridge.GenerateScript(environment));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ReloadToTweetDeck(){
|
public void ReloadToTweetDeck(){
|
||||||
browser.ExecuteScriptAsync($"if(window.TDGF_reload)window.TDGF_reload();else window.location.href='{TwitterUtils.TweetDeckURL}'");
|
browser.ReloadToTweetDeck();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TriggerTweetScreenshot(){
|
||||||
|
browser.TriggerTweetScreenshot();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReloadColumns(){
|
||||||
|
browser.ReloadColumns();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplyROT13(){
|
||||||
|
browser.ApplyROT13();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TriggerAnalyticsEvent(AnalyticsFile.Event e){
|
||||||
|
analytics?.TriggerEvent(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// callback handlers
|
// callback handlers
|
||||||
|
|
||||||
|
public void OnIntroductionClosed(bool showGuide, bool allowDataCollection){
|
||||||
|
if (Config.FirstRun){
|
||||||
|
Config.FirstRun = false;
|
||||||
|
Config.AllowDataCollection = allowDataCollection;
|
||||||
|
Config.ShowDataCollectionNotification = false;
|
||||||
|
Config.Save();
|
||||||
|
|
||||||
|
if (allowDataCollection && analytics == null){
|
||||||
|
analytics = new AnalyticsManager(this, plugins, Program.AnalyticsFilePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showGuide){
|
||||||
|
FormGuide.Show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void OpenContextMenu(){
|
public void OpenContextMenu(){
|
||||||
contextMenu.Show(this, PointToClient(Cursor.Position));
|
contextMenu.Show(this, PointToClient(Cursor.Position));
|
||||||
}
|
}
|
||||||
@@ -450,43 +407,59 @@ namespace TweetDuck.Core{
|
|||||||
if (!FormManager.TryBringToFront<FormSettings>()){
|
if (!FormManager.TryBringToFront<FormSettings>()){
|
||||||
bool prevEnableUpdateCheck = Config.EnableUpdateCheck;
|
bool prevEnableUpdateCheck = Config.EnableUpdateCheck;
|
||||||
|
|
||||||
FormSettings form = new FormSettings(this, plugins, updates, startTab);
|
FormSettings form = new FormSettings(this, plugins, updates, analytics, startTab);
|
||||||
|
|
||||||
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){
|
||||||
trayIcon.HasNotifications = false;
|
trayIcon.HasNotifications = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Program.SystemConfig.EnableBrowserGCReload){
|
browser.RefreshMemoryTracker();
|
||||||
memoryUsageTracker.Start(this, browser.GetBrowser(), Program.SystemConfig.BrowserMemoryThreshold);
|
|
||||||
|
if (Config.AllowDataCollection){
|
||||||
|
if (analytics == null){
|
||||||
|
analytics = new AnalyticsManager(this, plugins, Program.AnalyticsFilePath);
|
||||||
}
|
}
|
||||||
else{
|
}
|
||||||
memoryUsageTracker.Stop();
|
else if (analytics != null){
|
||||||
|
analytics.Dispose();
|
||||||
|
analytics = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateProperties(PropertyBridge.Environment.Browser);
|
if (form.ShouldReloadBrowser){
|
||||||
|
FormManager.TryFind<FormPlugins>()?.Close();
|
||||||
|
plugins.Reload(); // also reloads the browser
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
browser.UpdateProperties();
|
||||||
|
}
|
||||||
|
|
||||||
notification.RequiresResize = true;
|
notification.RequiresResize = true;
|
||||||
form.Dispose();
|
form.Dispose();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
TriggerAnalyticsEvent(AnalyticsFile.Event.OpenOptions);
|
||||||
ShowChildForm(form);
|
ShowChildForm(form);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OpenAbout(){
|
public void OpenAbout(){
|
||||||
if (!FormManager.TryBringToFront<FormAbout>()){
|
if (!FormManager.TryBringToFront<FormAbout>()){
|
||||||
|
TriggerAnalyticsEvent(AnalyticsFile.Event.OpenAbout);
|
||||||
ShowChildForm(new FormAbout());
|
ShowChildForm(new FormAbout());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OpenPlugins(){
|
public void OpenPlugins(){
|
||||||
if (!FormManager.TryBringToFront<FormPlugins>()){
|
if (!FormManager.TryBringToFront<FormPlugins>()){
|
||||||
|
TriggerAnalyticsEvent(AnalyticsFile.Event.OpenPlugins);
|
||||||
ShowChildForm(new FormPlugins(plugins));
|
ShowChildForm(new FormPlugins(plugins));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -507,10 +480,13 @@ namespace TweetDuck.Core{
|
|||||||
soundNotification.PlaybackError += soundNotification_PlaybackError;
|
soundNotification.PlaybackError += soundNotification_PlaybackError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
soundNotification.SetVolume(Config.NotificationSoundVolume);
|
||||||
soundNotification.Play(Config.NotificationSoundPath);
|
soundNotification.Play(Config.NotificationSoundPath);
|
||||||
|
|
||||||
|
TriggerAnalyticsEvent(AnalyticsFile.Event.SoundNotification);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PlayVideo(string url){
|
public void PlayVideo(string url, string username){
|
||||||
if (string.IsNullOrEmpty(url)){
|
if (string.IsNullOrEmpty(url)){
|
||||||
videoPlayer?.Close();
|
videoPlayer?.Close();
|
||||||
return;
|
return;
|
||||||
@@ -520,25 +496,34 @@ namespace TweetDuck.Core{
|
|||||||
videoPlayer = new VideoPlayer(this);
|
videoPlayer = new VideoPlayer(this);
|
||||||
|
|
||||||
videoPlayer.ProcessExited += (sender, args) => {
|
videoPlayer.ProcessExited += (sender, args) => {
|
||||||
browser.GetBrowser().GetHost().SendFocusEvent(true);
|
browser.HideVideoOverlay(true);
|
||||||
HideVideoOverlay();
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
videoPlayer.Launch(url);
|
videoPlayer.Launch(url, username);
|
||||||
|
TriggerAnalyticsEvent(AnalyticsFile.Event.VideoPlay);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ToggleVideoPause(){
|
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();
|
||||||
|
|
||||||
|
if (!browser.IsTweetDeckWebsite){
|
||||||
|
FormMessage.Error("View Tweet Detail", "TweetDeck is not currently loaded.", FormMessage.OK);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
notification.FinishCurrentNotification();
|
||||||
|
browser.ShowTweetDetail(columnId, chirpId, fallbackUrl);
|
||||||
|
TriggerAnalyticsEvent(AnalyticsFile.Event.TweetDetail);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnTweetScreenshotReady(string html, int width, int height){
|
public void OnTweetScreenshotReady(string html, int width, int height){
|
||||||
@@ -547,6 +532,7 @@ namespace TweetDuck.Core{
|
|||||||
}
|
}
|
||||||
|
|
||||||
notificationScreenshotManager.Trigger(html, width, height);
|
notificationScreenshotManager.Trigger(html, width, height);
|
||||||
|
TriggerAnalyticsEvent(AnalyticsFile.Event.TweetScreenshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DisplayTooltip(string text){
|
public void DisplayTooltip(string text){
|
||||||
@@ -559,9 +545,5 @@ namespace TweetDuck.Core{
|
|||||||
toolTip.Show(text, this, position);
|
toolTip.Show(text, this, position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void TriggerTweetScreenshot(){
|
|
||||||
browser.ExecuteScriptAsync("TDGF_triggerScreenshot()");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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();
|
||||||
@@ -16,7 +20,7 @@ namespace TweetDuck.Core{
|
|||||||
|
|
||||||
public static void CloseAllDialogs(){
|
public static void CloseAllDialogs(){
|
||||||
foreach(Form form in Application.OpenForms.Cast<Form>().Reverse()){
|
foreach(Form form in Application.OpenForms.Cast<Form>().Reverse()){
|
||||||
if (form is FormSettings || form is FormPlugins || form is FormAbout){
|
if (form is FormSettings || form is FormPlugins || form is FormAbout || form is FormGuide){
|
||||||
form.Close();
|
form.Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,70 +6,88 @@ 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;
|
||||||
|
using TweetDuck.Core.Other;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Handling{
|
namespace TweetDuck.Core.Handling{
|
||||||
abstract class ContextMenuBase : IContextMenuHandler{
|
abstract class ContextMenuBase : IContextMenuHandler{
|
||||||
protected static readonly bool HasDevTools = File.Exists(Path.Combine(Program.ProgramPath, "devtools_resources.pak"));
|
public static readonly bool HasDevTools = File.Exists(Path.Combine(Program.ProgramPath, "devtools_resources.pak"));
|
||||||
|
|
||||||
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 MenuViewImage = (CefMenuCommand)26503;
|
||||||
private const int MenuCopyImageUrl = 26504;
|
private const CefMenuCommand MenuOpenMediaUrl = (CefMenuCommand)26504;
|
||||||
private const int MenuSaveImage = 26505;
|
private const CefMenuCommand MenuCopyMediaUrl = (CefMenuCommand)26505;
|
||||||
private const int MenuSaveAllImages = 26506;
|
private const CefMenuCommand MenuSaveMedia = (CefMenuCommand)26506;
|
||||||
private const int MenuOpenDevTools = 26599;
|
private const CefMenuCommand MenuSaveTweetImages = (CefMenuCommand)26507;
|
||||||
|
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(MenuViewImage, "View image in photo viewer");
|
||||||
|
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 +95,66 @@ 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 MenuViewImage:
|
||||||
|
string url = GetMediaLink(parameters);
|
||||||
|
string file = Path.Combine(BrowserCache.CacheFolder, TwitterUtils.GetImageFileName(url));
|
||||||
|
|
||||||
|
void ViewFile(){
|
||||||
|
string ext = Path.GetExtension(file);
|
||||||
|
|
||||||
|
if (TwitterUtils.ValidImageExtensions.Contains(ext)){
|
||||||
|
WindowsUtils.OpenAssociatedProgram(file);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
FormMessage.Error("Image Download", "Invalid file extension "+ext, FormMessage.OK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (File.Exists(file)){
|
||||||
|
ViewFile();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
BrowserUtils.DownloadFileAsync(TwitterUtils.GetMediaLink(url, ImageQuality), file, ViewFile, ex => {
|
||||||
|
FormMessage.Error("Image Download", "An error occurred while downloading the image: "+ex.Message, FormMessage.OK);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MenuSaveMedia:
|
||||||
|
if (IsVideo){
|
||||||
|
TwitterUtils.DownloadVideo(GetMediaLink(parameters), lastHighlightedTweetAuthors.LastOrDefault());
|
||||||
|
}
|
||||||
|
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 +166,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){
|
||||||
|
@@ -2,21 +2,23 @@
|
|||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Core.Bridge;
|
using TweetDuck.Core.Bridge;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
|
using TweetDuck.Core.Other.Analytics;
|
||||||
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 CefMenuCommand MenuInputApplyROT13 = (CefMenuCommand)26615;
|
||||||
|
|
||||||
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 +28,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,28 +43,33 @@ namespace TweetDuck.Core.Handling{
|
|||||||
RemoveSeparatorIfLast(model);
|
RemoveSeparatorIfLast(model);
|
||||||
|
|
||||||
if (parameters.TypeFlags.HasFlag(ContextMenuType.Selection)){
|
if (parameters.TypeFlags.HasFlag(ContextMenuType.Selection)){
|
||||||
|
if (parameters.TypeFlags.HasFlag(ContextMenuType.Editable)){
|
||||||
|
model.AddSeparator();
|
||||||
|
model.AddItem(MenuInputApplyROT13, "Apply ROT13");
|
||||||
|
}
|
||||||
|
|
||||||
model.AddSeparator();
|
model.AddSeparator();
|
||||||
}
|
}
|
||||||
|
|
||||||
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 +78,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();
|
||||||
@@ -89,6 +96,8 @@ namespace TweetDuck.Core.Handling{
|
|||||||
}
|
}
|
||||||
|
|
||||||
RemoveSeparatorIfLast(model);
|
RemoveSeparatorIfLast(model);
|
||||||
|
|
||||||
|
form.InvokeAsyncSafe(() => form.TriggerAnalyticsEvent(AnalyticsFile.Event.BrowserContextMenu));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){
|
public override bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){
|
||||||
@@ -96,8 +105,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 +127,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 +139,15 @@ 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;
|
||||||
|
|
||||||
|
case MenuInputApplyROT13:
|
||||||
|
form.InvokeAsyncSafe(form.ApplyROT13);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,6 +166,7 @@ namespace TweetDuck.Core.Handling{
|
|||||||
|
|
||||||
menu.Popup += (sender, args) => {
|
menu.Popup += (sender, args) => {
|
||||||
menu.MenuItems[1].Checked = Program.UserConfig.MuteNotifications;
|
menu.MenuItems[1].Checked = Program.UserConfig.MuteNotifications;
|
||||||
|
form.TriggerAnalyticsEvent(AnalyticsFile.Event.BrowserContextMenu);
|
||||||
};
|
};
|
||||||
|
|
||||||
return menu;
|
return menu;
|
||||||
|
15
Core/Handling/ContextMenuGuide.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using CefSharp;
|
||||||
|
|
||||||
|
namespace TweetDuck.Core.Handling{
|
||||||
|
sealed class ContextMenuGuide : ContextMenuBase{
|
||||||
|
public override void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
|
||||||
|
model.Clear();
|
||||||
|
base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);
|
||||||
|
|
||||||
|
if (HasDevTools){
|
||||||
|
AddSeparator(model);
|
||||||
|
AddDebugMenuItems(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,18 +1,20 @@
|
|||||||
using CefSharp;
|
using CefSharp;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Notification;
|
using TweetDuck.Core.Notification;
|
||||||
|
using TweetDuck.Core.Other.Analytics;
|
||||||
|
|
||||||
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,29 +30,35 @@ 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){
|
||||||
|
AddSeparator(model);
|
||||||
AddDebugMenuItems(model);
|
AddDebugMenuItems(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
RemoveSeparatorIfLast(model);
|
RemoveSeparatorIfLast(model);
|
||||||
|
|
||||||
form.InvokeAsyncSafe(() => form.ContextMenuOpen = true);
|
form.InvokeAsyncSafe(() => {
|
||||||
|
form.ContextMenuOpen = true;
|
||||||
|
form.TriggerAnalyticsEvent(AnalyticsFile.Event.NotificationContextMenu);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){
|
public override bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){
|
||||||
@@ -58,7 +66,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 +75,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
26
Core/Handling/DragHandlerBrowser.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using CefSharp;
|
||||||
|
|
||||||
|
namespace TweetDuck.Core.Handling{
|
||||||
|
sealed class DragHandlerBrowser : IDragHandler{
|
||||||
|
public bool OnDragEnter(IWebBrowser browserControl, IBrowser browser, IDragData dragData, DragOperationsMask mask){
|
||||||
|
void TriggerDragStart(string type, string data = null){
|
||||||
|
browserControl.ExecuteScriptAsync("window.TDGF_onGlobalDragStart", type, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dragData.IsLink){
|
||||||
|
TriggerDragStart("link", dragData.LinkUrl);
|
||||||
|
}
|
||||||
|
else if (dragData.IsFragment){
|
||||||
|
TriggerDragStart("text", dragData.FragmentText.Trim());
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
TriggerDragStart("unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnDraggableRegionsChanged(IWebBrowser browserControl, IBrowser browser, IList<DraggableRegion> regions){}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,14 +1,24 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using CefSharp;
|
using CefSharp;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Handling.General{
|
namespace TweetDuck.Core.Handling.General{
|
||||||
class BrowserProcessHandler : IBrowserProcessHandler{
|
sealed class BrowserProcessHandler : IBrowserProcessHandler{
|
||||||
void IBrowserProcessHandler.OnContextInitialized(){
|
public static Task UpdatePrefs(){
|
||||||
|
return Cef.UIThreadTaskFactory.StartNew(UpdatePrefsInternal);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void UpdatePrefsInternal(){
|
||||||
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 _);
|
||||||
|
ctx.SetPreference("settings.a11y.animation_policy", Program.UserConfig.EnableAnimatedImages ? "allowed" : "none", out string _);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IBrowserProcessHandler.OnContextInitialized(){
|
||||||
|
UpdatePrefsInternal();
|
||||||
|
}
|
||||||
|
|
||||||
void IBrowserProcessHandler.OnScheduleMessagePumpWork(long delay){}
|
void IBrowserProcessHandler.OnScheduleMessagePumpWork(long delay){}
|
||||||
void IDisposable.Dispose(){}
|
void IDisposable.Dispose(){}
|
||||||
}
|
}
|
||||||
|
42
Core/Handling/General/FileDialogHandler.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
using System;
|
||||||
|
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){
|
||||||
|
string ext = Path.GetExtension(dialog.FileName);
|
||||||
|
callback.Continue(acceptFilters.FindIndex(filter => filter.Equals(ext, StringComparison.OrdinalIgnoreCase)), 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,6 +2,7 @@
|
|||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Notification;
|
using TweetDuck.Core.Notification;
|
||||||
|
using TweetDuck.Core.Other.Analytics;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Handling {
|
namespace TweetDuck.Core.Handling {
|
||||||
sealed class KeyboardHandlerNotification : IKeyboardHandler{
|
sealed class KeyboardHandlerNotification : IKeyboardHandler{
|
||||||
@@ -11,19 +12,26 @@ namespace TweetDuck.Core.Handling {
|
|||||||
this.notification = notification;
|
this.notification = notification;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TriggerKeyboardShortcutAnalytics(){
|
||||||
|
notification.InvokeAsyncSafe(() => notification.TriggerAnalyticsEvent(AnalyticsFile.Event.NotificationKeyboardShortcut));
|
||||||
|
}
|
||||||
|
|
||||||
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 && !browser.FocusedFrame.Url.StartsWith("chrome-devtools://")){
|
if (type == KeyType.RawKeyDown && !browser.FocusedFrame.Url.StartsWith("chrome-devtools://")){
|
||||||
switch((Keys)windowsKeyCode){
|
switch((Keys)windowsKeyCode){
|
||||||
case Keys.Enter:
|
case Keys.Enter:
|
||||||
notification.InvokeAsyncSafe(notification.FinishCurrentNotification);
|
notification.InvokeAsyncSafe(notification.FinishCurrentNotification);
|
||||||
|
TriggerKeyboardShortcutAnalytics();
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case Keys.Escape:
|
case Keys.Escape:
|
||||||
notification.InvokeAsyncSafe(() => notification.HideNotification(true));
|
notification.InvokeAsyncSafe(notification.HideNotification);
|
||||||
|
TriggerKeyboardShortcutAnalytics();
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case Keys.Space:
|
case Keys.Space:
|
||||||
notification.InvokeAsyncSafe(() => notification.FreezeTimer = !notification.FreezeTimer);
|
notification.InvokeAsyncSafe(() => notification.FreezeTimer = !notification.FreezeTimer);
|
||||||
|
TriggerKeyboardShortcutAnalytics();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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;
|
||||||
|
|
||||||
|
46
Core/Notification/Example/FormNotificationExample.cs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
using System.Windows.Forms;
|
||||||
|
using TweetDuck.Plugins;
|
||||||
|
using TweetDuck.Resources;
|
||||||
|
|
||||||
|
namespace TweetDuck.Core.Notification.Example{
|
||||||
|
sealed class FormNotificationExample : FormNotificationMain{
|
||||||
|
public override bool RequiresResize => true;
|
||||||
|
protected override bool CanDragWindow => Program.UserConfig.NotificationPosition == TweetNotification.Position.Custom;
|
||||||
|
|
||||||
|
protected override FormBorderStyle NotificationBorderStyle{
|
||||||
|
get{
|
||||||
|
if (Program.UserConfig.NotificationSize == TweetNotification.Size.Custom){
|
||||||
|
switch(base.NotificationBorderStyle){
|
||||||
|
case FormBorderStyle.FixedSingle: return FormBorderStyle.Sizable;
|
||||||
|
case FormBorderStyle.FixedToolWindow: return FormBorderStyle.SizableToolWindow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.NotificationBorderStyle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly TweetNotification exampleNotification;
|
||||||
|
|
||||||
|
public FormNotificationExample(FormBrowser owner, PluginManager pluginManager) : base(owner, pluginManager, false){
|
||||||
|
string exampleTweetHTML = ScriptLoader.LoadResource("pages/example.html", true);
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
exampleTweetHTML = exampleTweetHTML.Replace("</p>", @"</p><div style='margin-top:256px'>Scrollbar test padding...</div>");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
exampleNotification = TweetNotification.Example(exampleTweetHTML, 95);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowExampleNotification(bool reset){
|
||||||
|
if (reset){
|
||||||
|
LoadTweet(exampleNotification);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
PrepareAndDisplayWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateTitle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -4,15 +4,29 @@ using System;
|
|||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Configuration;
|
using TweetDuck.Configuration;
|
||||||
|
using TweetDuck.Core.Bridge;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Handling;
|
using TweetDuck.Core.Handling;
|
||||||
using TweetDuck.Core.Handling.General;
|
using TweetDuck.Core.Handling.General;
|
||||||
|
using TweetDuck.Core.Other.Analytics;
|
||||||
using TweetDuck.Core.Other.Management;
|
using TweetDuck.Core.Other.Management;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Notification{
|
namespace TweetDuck.Core.Notification{
|
||||||
partial class FormNotificationBase : Form{
|
partial class FormNotificationBase : Form{
|
||||||
protected Point PrimaryLocation{
|
protected static int FontSizeLevel{
|
||||||
|
get{
|
||||||
|
switch(TweetDeckBridge.FontSize){
|
||||||
|
case "largest": return 4;
|
||||||
|
case "large": return 3;
|
||||||
|
case "small": return 1;
|
||||||
|
case "smallest": return 0;
|
||||||
|
default: return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual Point PrimaryLocation{
|
||||||
get{
|
get{
|
||||||
UserConfig config = Program.UserConfig;
|
UserConfig config = Program.UserConfig;
|
||||||
Screen screen;
|
Screen screen;
|
||||||
@@ -53,45 +67,57 @@ namespace TweetDuck.Core.Notification{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public bool IsNotificationVisible => Location != ControlExtensions.InvisibleLocation;
|
public bool IsNotificationVisible => Location != ControlExtensions.InvisibleLocation;
|
||||||
|
protected virtual bool CanDragWindow => true;
|
||||||
|
|
||||||
public new Point Location{
|
public new Point Location{
|
||||||
get => base.Location;
|
get{
|
||||||
|
return base.Location;
|
||||||
|
}
|
||||||
|
|
||||||
set{
|
set{
|
||||||
Visible = (base.Location = value) != ControlExtensions.InvisibleLocation;
|
Visible = (base.Location = value) != ControlExtensions.InvisibleLocation;
|
||||||
FormBorderStyle = GetBorderStyle(CanResizeWindow);
|
FormBorderStyle = NotificationBorderStyle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CanResizeWindow{
|
protected virtual FormBorderStyle NotificationBorderStyle{
|
||||||
get => FormBorderStyle == FormBorderStyle.Sizable || FormBorderStyle == FormBorderStyle.SizableToolWindow;
|
get{
|
||||||
set => FormBorderStyle = GetBorderStyle(value);
|
if (WindowsUtils.ShouldAvoidToolWindow && Visible){ // Visible = workaround for alt+tab
|
||||||
|
return FormBorderStyle.FixedSingle;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return FormBorderStyle.FixedToolWindow;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Func<bool> CanMoveWindow { get; set; } = () => true;
|
|
||||||
protected override bool ShowWithoutActivation => true;
|
protected override bool ShowWithoutActivation => true;
|
||||||
|
|
||||||
protected double SizeScale => dpiScale*Program.UserConfig.ZoomMultiplier;
|
protected float DpiScale { get; }
|
||||||
|
protected double SizeScale => DpiScale*Program.UserConfig.ZoomLevel/100.0;
|
||||||
|
|
||||||
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 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;
|
||||||
|
|
||||||
|
protected bool IsCursorOverBrowser => browser.Bounds.Contains(PointToClient(Cursor.Position));
|
||||||
|
|
||||||
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;
|
||||||
@@ -105,13 +131,13 @@ namespace TweetDuck.Core.Notification{
|
|||||||
|
|
||||||
this.browser.Dock = DockStyle.None;
|
this.browser.Dock = DockStyle.None;
|
||||||
this.browser.ClientSize = ClientSize;
|
this.browser.ClientSize = ClientSize;
|
||||||
this.browser.IsBrowserInitializedChanged += Browser_IsBrowserInitializedChanged;
|
this.browser.IsBrowserInitializedChanged += browser_IsBrowserInitializedChanged;
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
this.browser.ConsoleMessage += BrowserUtils.HandleConsoleMessage;
|
this.browser.ConsoleMessage += BrowserUtils.HandleConsoleMessage;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
this.dpiScale = this.GetDPIScale();
|
DpiScale = this.GetDPIScale();
|
||||||
|
|
||||||
DefaultResourceHandlerFactory handlerFactory = (DefaultResourceHandlerFactory)browser.ResourceHandlerFactory;
|
DefaultResourceHandlerFactory handlerFactory = (DefaultResourceHandlerFactory)browser.ResourceHandlerFactory;
|
||||||
handlerFactory.RegisterHandler(TwitterUtils.TweetDeckURL, this.resourceHandler);
|
handlerFactory.RegisterHandler(TwitterUtils.TweetDeckURL, this.resourceHandler);
|
||||||
@@ -128,22 +154,26 @@ namespace TweetDuck.Core.Notification{
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override void WndProc(ref Message m){
|
protected override void WndProc(ref Message m){
|
||||||
if (m.Msg == 0x0112 && (m.WParam.ToInt32() & 0xFFF0) == 0xF010 && !CanMoveWindow()){ // WM_SYSCOMMAND, SC_MOVE
|
if (m.Msg == 0x0112 && (m.WParam.ToInt32() & 0xFFF0) == 0xF010 && !CanDragWindow){ // WM_SYSCOMMAND, SC_MOVE
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
base.WndProc(ref m);
|
base.WndProc(ref m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void TriggerAnalyticsEvent(AnalyticsFile.Event e){
|
||||||
|
owner.TriggerAnalyticsEvent(e);
|
||||||
|
}
|
||||||
|
|
||||||
// event handlers
|
// event handlers
|
||||||
|
|
||||||
private void owner_FormClosed(object sender, FormClosedEventArgs e){
|
private void owner_FormClosed(object sender, FormClosedEventArgs e){
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
@@ -152,13 +182,10 @@ namespace TweetDuck.Core.Notification{
|
|||||||
|
|
||||||
// notification methods
|
// notification methods
|
||||||
|
|
||||||
public virtual void HideNotification(bool loadBlank){
|
public virtual void HideNotification(){
|
||||||
if (loadBlank){
|
|
||||||
browser.Load("about:blank");
|
browser.Load("about:blank");
|
||||||
}
|
|
||||||
|
|
||||||
Location = ControlExtensions.InvisibleLocation;
|
Location = ControlExtensions.InvisibleLocation;
|
||||||
currentColumn = null;
|
currentNotification = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void FinishCurrentNotification(){}
|
public virtual void FinishCurrentNotification(){}
|
||||||
@@ -176,15 +203,11 @@ namespace TweetDuck.Core.Notification{
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected virtual string GetTweetHTML(TweetNotification tweet){
|
protected virtual string GetTweetHTML(TweetNotification tweet){
|
||||||
string bodyClasses = browser.Bounds.Contains(PointToClient(Cursor.Position)) ? "td-hover" : string.Empty;
|
return tweet.GenerateHtml(IsCursorOverBrowser ? "td-hover" : string.Empty);
|
||||||
return tweet.GenerateHtml(bodyClasses);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 +216,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(){
|
||||||
@@ -220,14 +244,5 @@ namespace TweetDuck.Core.Notification{
|
|||||||
toolTip.Show(text, this, position);
|
toolTip.Show(text, this, position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private FormBorderStyle GetBorderStyle(bool sizable){
|
|
||||||
if (WindowsUtils.ShouldAvoidToolWindow && Visible){ // Visible = workaround for alt+tab
|
|
||||||
return sizable ? FormBorderStyle.Sizable : FormBorderStyle.FixedSingle;
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
return sizable ? FormBorderStyle.SizableToolWindow : FormBorderStyle.FixedToolWindow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -49,7 +49,7 @@
|
|||||||
this.progressBarTimer.Margin = new System.Windows.Forms.Padding(0);
|
this.progressBarTimer.Margin = new System.Windows.Forms.Padding(0);
|
||||||
this.progressBarTimer.Maximum = 1000;
|
this.progressBarTimer.Maximum = 1000;
|
||||||
this.progressBarTimer.Name = "progressBarTimer";
|
this.progressBarTimer.Name = "progressBarTimer";
|
||||||
this.progressBarTimer.Size = new System.Drawing.Size(284, TimerBarHeight);
|
this.progressBarTimer.Size = new System.Drawing.Size(284, 4);
|
||||||
this.progressBarTimer.TabIndex = 1;
|
this.progressBarTimer.TabIndex = 1;
|
||||||
//
|
//
|
||||||
// FormNotification
|
// FormNotification
|
||||||
|
@@ -5,6 +5,7 @@ using System.Windows.Forms;
|
|||||||
using TweetDuck.Core.Bridge;
|
using TweetDuck.Core.Bridge;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Handling;
|
using TweetDuck.Core.Handling;
|
||||||
|
using TweetDuck.Core.Other.Analytics;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetDuck.Data;
|
using TweetDuck.Data;
|
||||||
using TweetDuck.Plugins;
|
using TweetDuck.Plugins;
|
||||||
@@ -12,14 +13,14 @@ using TweetDuck.Plugins.Enums;
|
|||||||
using TweetDuck.Resources;
|
using TweetDuck.Resources;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Notification{
|
namespace TweetDuck.Core.Notification{
|
||||||
partial class FormNotificationMain : FormNotificationBase{
|
abstract partial class FormNotificationMain : FormNotificationBase{
|
||||||
private const string NotificationScriptFile = "notification.js";
|
private const string NotificationScriptFile = "notification.js";
|
||||||
private const int TimerBarHeight = 4;
|
|
||||||
|
|
||||||
private static readonly string NotificationScriptIdentifier = ScriptLoader.GetRootIdentifier(NotificationScriptFile);
|
private static readonly string NotificationScriptIdentifier = ScriptLoader.GetRootIdentifier(NotificationScriptFile);
|
||||||
private static readonly string NotificationJS = ScriptLoader.LoadResource(NotificationScriptFile);
|
private static readonly string NotificationJS = ScriptLoader.LoadResource(NotificationScriptFile);
|
||||||
|
|
||||||
private readonly PluginManager plugins;
|
private readonly PluginManager plugins;
|
||||||
|
private readonly int timerBarHeight;
|
||||||
|
|
||||||
protected int timeLeft, totalTime;
|
protected int timeLeft, totalTime;
|
||||||
protected bool pausedDuringNotification;
|
protected bool pausedDuringNotification;
|
||||||
@@ -31,9 +32,9 @@ namespace TweetDuck.Core.Notification{
|
|||||||
private bool? prevDisplayTimer;
|
private bool? prevDisplayTimer;
|
||||||
private int? prevFontSize;
|
private int? prevFontSize;
|
||||||
|
|
||||||
public bool RequiresResize{
|
public virtual bool RequiresResize{
|
||||||
get{
|
get{
|
||||||
return !prevDisplayTimer.HasValue || !prevFontSize.HasValue || prevDisplayTimer != Program.UserConfig.DisplayNotificationTimer || prevFontSize != TweetNotification.FontSizeLevel || CanResizeWindow;
|
return !prevDisplayTimer.HasValue || !prevFontSize.HasValue || prevDisplayTimer != Program.UserConfig.DisplayNotificationTimer || prevFontSize != FontSizeLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
set{
|
set{
|
||||||
@@ -43,7 +44,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
prevDisplayTimer = Program.UserConfig.DisplayNotificationTimer;
|
prevDisplayTimer = Program.UserConfig.DisplayNotificationTimer;
|
||||||
prevFontSize = TweetNotification.FontSizeLevel;
|
prevFontSize = FontSizeLevel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,7 +53,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
get{
|
get{
|
||||||
switch(Program.UserConfig.NotificationSize){
|
switch(Program.UserConfig.NotificationSize){
|
||||||
default:
|
default:
|
||||||
return BrowserUtils.Scale(284, SizeScale*(1.0+0.05*TweetNotification.FontSizeLevel));
|
return BrowserUtils.Scale(284, SizeScale*(1.0+0.05*FontSizeLevel));
|
||||||
|
|
||||||
case TweetNotification.Size.Custom:
|
case TweetNotification.Size.Custom:
|
||||||
return Program.UserConfig.CustomNotificationSize.Width;
|
return Program.UserConfig.CustomNotificationSize.Width;
|
||||||
@@ -64,7 +65,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
get{
|
get{
|
||||||
switch(Program.UserConfig.NotificationSize){
|
switch(Program.UserConfig.NotificationSize){
|
||||||
default:
|
default:
|
||||||
return BrowserUtils.Scale(122, SizeScale*(1.0+0.075*TweetNotification.FontSizeLevel));
|
return BrowserUtils.Scale(122, SizeScale*(1.0+0.075*FontSizeLevel));
|
||||||
|
|
||||||
case TweetNotification.Size.Custom:
|
case TweetNotification.Size.Custom:
|
||||||
return Program.UserConfig.CustomNotificationSize.Height;
|
return Program.UserConfig.CustomNotificationSize.Height;
|
||||||
@@ -72,14 +73,13 @@ namespace TweetDuck.Core.Notification{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Size BrowserSize{
|
public Size BrowserSize => Program.UserConfig.DisplayNotificationTimer ? new Size(ClientSize.Width, ClientSize.Height-timerBarHeight) : ClientSize;
|
||||||
get => Program.UserConfig.DisplayNotificationTimer ? new Size(ClientSize.Width, ClientSize.Height-TimerBarHeight) : ClientSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FormNotificationMain(FormBrowser owner, PluginManager pluginManager, bool enableContextMenu) : base(owner, enableContextMenu){
|
protected FormNotificationMain(FormBrowser owner, PluginManager pluginManager, bool enableContextMenu) : base(owner, enableContextMenu){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
this.plugins = pluginManager;
|
this.plugins = pluginManager;
|
||||||
|
this.timerBarHeight = BrowserUtils.Scale(4, DpiScale);
|
||||||
|
|
||||||
browser.KeyboardHandler = new KeyboardHandlerNotification(this);
|
browser.KeyboardHandler = new KeyboardHandlerNotification(this);
|
||||||
|
|
||||||
@@ -113,8 +113,8 @@ namespace TweetDuck.Core.Notification{
|
|||||||
if (nCode == 0){
|
if (nCode == 0){
|
||||||
int eventType = wParam.ToInt32();
|
int eventType = wParam.ToInt32();
|
||||||
|
|
||||||
if (eventType == NativeMethods.WM_MOUSEWHEEL && browser.Bounds.Contains(PointToClient(Cursor.Position))){
|
if (eventType == NativeMethods.WM_MOUSEWHEEL && IsCursorOverBrowser){
|
||||||
browser.SendMouseWheelEvent(0, 0, 0, BrowserUtils.Scale(NativeMethods.GetMouseHookData(lParam), Program.UserConfig.NotificationScrollSpeed/100.0), CefEventFlags.None);
|
browser.SendMouseWheelEvent(0, 0, 0, BrowserUtils.Scale(NativeMethods.GetMouseHookData(lParam), Program.UserConfig.NotificationScrollSpeed*0.01), CefEventFlags.None);
|
||||||
return NativeMethods.HOOK_HANDLED;
|
return NativeMethods.HOOK_HANDLED;
|
||||||
}
|
}
|
||||||
else if (eventType == NativeMethods.WM_XBUTTONDOWN && DesktopBounds.Contains(Cursor.Position)){
|
else if (eventType == NativeMethods.WM_XBUTTONDOWN && DesktopBounds.Contains(Cursor.Position)){
|
||||||
@@ -128,6 +128,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
}
|
}
|
||||||
|
|
||||||
blockXButtonUp = true;
|
blockXButtonUp = true;
|
||||||
|
this.InvokeAsyncSafe(() => TriggerAnalyticsEvent(AnalyticsFile.Event.NotificationExtraMouseButton));
|
||||||
return NativeMethods.HOOK_HANDLED;
|
return NativeMethods.HOOK_HANDLED;
|
||||||
}
|
}
|
||||||
else if (eventType == NativeMethods.WM_XBUTTONUP && blockXButtonUp){
|
else if (eventType == NativeMethods.WM_XBUTTONUP && blockXButtonUp){
|
||||||
@@ -148,7 +149,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
|
|
||||||
private void FormNotification_FormClosing(object sender, FormClosingEventArgs e){
|
private void FormNotification_FormClosing(object sender, FormClosingEventArgs e){
|
||||||
if (e.CloseReason == CloseReason.UserClosing){
|
if (e.CloseReason == CloseReason.UserClosing){
|
||||||
HideNotification(true);
|
HideNotification();
|
||||||
e.Cancel = true;
|
e.Cancel = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -176,12 +177,14 @@ namespace TweetDuck.Core.Notification{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void timerHideProgress_Tick(object sender, EventArgs e){
|
private void timerHideProgress_Tick(object sender, EventArgs e){
|
||||||
if (Bounds.Contains(Cursor.Position) || FreezeTimer || ContextMenuOpen)return;
|
if (Bounds.Contains(Cursor.Position) || FreezeTimer || ContextMenuOpen){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
timeLeft -= timerProgress.Interval;
|
timeLeft -= timerProgress.Interval;
|
||||||
|
|
||||||
int value = BrowserUtils.Scale(1025, (totalTime-timeLeft)/(double)totalTime);
|
int value = BrowserUtils.Scale(progressBarTimer.Maximum+25, (totalTime-timeLeft)/(double)totalTime);
|
||||||
progressBarTimer.SetValueInstant(Math.Min(1000, Math.Max(0, Program.UserConfig.NotificationTimerCountDown ? 1000-value : value)));
|
progressBarTimer.SetValueInstant(Program.UserConfig.NotificationTimerCountDown ? progressBarTimer.Maximum-value : value);
|
||||||
|
|
||||||
if (timeLeft <= 0){
|
if (timeLeft <= 0){
|
||||||
FinishCurrentNotification();
|
FinishCurrentNotification();
|
||||||
@@ -194,21 +197,10 @@ namespace TweetDuck.Core.Notification{
|
|||||||
LoadTweet(notification);
|
LoadTweet(notification);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ShowNotificationForSettings(bool reset){
|
public override void HideNotification(){
|
||||||
if (reset){
|
base.HideNotification();
|
||||||
LoadTweet(TweetNotification.ExampleTweet);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
PrepareAndDisplayWindow();
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateTitle();
|
progressBarTimer.Value = Program.UserConfig.NotificationTimerCountDown ? progressBarTimer.Maximum : progressBarTimer.Minimum;
|
||||||
}
|
|
||||||
|
|
||||||
public override void HideNotification(bool loadBlank){
|
|
||||||
base.HideNotification(loadBlank);
|
|
||||||
|
|
||||||
progressBarTimer.Value = Program.UserConfig.NotificationTimerCountDown ? 1000 : 0;
|
|
||||||
timerProgress.Stop();
|
timerProgress.Stop();
|
||||||
totalTime = 0;
|
totalTime = 0;
|
||||||
|
|
||||||
@@ -251,14 +243,14 @@ namespace TweetDuck.Core.Notification{
|
|||||||
protected override void LoadTweet(TweetNotification tweet){
|
protected override void LoadTweet(TweetNotification tweet){
|
||||||
timerProgress.Stop();
|
timerProgress.Stop();
|
||||||
totalTime = timeLeft = tweet.GetDisplayDuration(Program.UserConfig.NotificationDurationValue);
|
totalTime = timeLeft = tweet.GetDisplayDuration(Program.UserConfig.NotificationDurationValue);
|
||||||
progressBarTimer.Value = Program.UserConfig.NotificationTimerCountDown ? 1000 : 0;
|
progressBarTimer.Value = Program.UserConfig.NotificationTimerCountDown ? progressBarTimer.Maximum : progressBarTimer.Minimum;
|
||||||
|
|
||||||
base.LoadTweet(tweet);
|
base.LoadTweet(tweet);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void SetNotificationSize(int width, int height){
|
protected override void SetNotificationSize(int width, int height){
|
||||||
if (Program.UserConfig.DisplayNotificationTimer){
|
if (Program.UserConfig.DisplayNotificationTimer){
|
||||||
ClientSize = new Size(width, height+TimerBarHeight);
|
ClientSize = new Size(width, height+timerBarHeight);
|
||||||
progressBarTimer.Visible = true;
|
progressBarTimer.Visible = true;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
@@ -269,7 +261,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
browser.ClientSize = new Size(width, height);
|
browser.ClientSize = new Size(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PrepareAndDisplayWindow(){
|
protected void PrepareAndDisplayWindow(){
|
||||||
if (RequiresResize){
|
if (RequiresResize){
|
||||||
RequiresResize = false;
|
RequiresResize = false;
|
||||||
SetNotificationSize(BaseClientWidth, BaseClientHeight);
|
SetNotificationSize(BaseClientWidth, BaseClientHeight);
|
||||||
@@ -279,7 +271,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
StartMouseHook();
|
StartMouseHook();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnNotificationReady(){
|
protected virtual void OnNotificationReady(){
|
||||||
PrepareAndDisplayWindow();
|
PrepareAndDisplayWindow();
|
||||||
timerProgress.Start();
|
timerProgress.Start();
|
||||||
}
|
}
|
||||||
|
@@ -40,7 +40,6 @@ namespace TweetDuck.Core.Notification {
|
|||||||
//
|
//
|
||||||
// FormNotificationTweet
|
// FormNotificationTweet
|
||||||
//
|
//
|
||||||
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FormNotificationTweet_FormClosing);
|
|
||||||
this.ResumeLayout(true);
|
this.ResumeLayout(true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using TweetDuck.Plugins;
|
using TweetDuck.Plugins;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using TweetDuck.Core.Other.Analytics;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Notification{
|
namespace TweetDuck.Core.Notification{
|
||||||
@@ -10,10 +11,24 @@ namespace TweetDuck.Core.Notification{
|
|||||||
private const int NonIntrusiveIdleLimit = 30;
|
private const int NonIntrusiveIdleLimit = 30;
|
||||||
private const int TrimMinimum = 32;
|
private const int TrimMinimum = 32;
|
||||||
|
|
||||||
|
protected override Point PrimaryLocation => hasTemporarilyMoved && IsNotificationVisible ? Location : base.PrimaryLocation;
|
||||||
private bool IsCursorOverNotificationArea => new Rectangle(PrimaryLocation, Size).Contains(Cursor.Position);
|
private bool IsCursorOverNotificationArea => new Rectangle(PrimaryLocation, Size).Contains(Cursor.Position);
|
||||||
|
|
||||||
|
protected override bool CanDragWindow{
|
||||||
|
get{
|
||||||
|
if (ModifierKeys.HasFlag(Keys.Alt)){
|
||||||
|
hasTemporarilyMoved = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private readonly Queue<TweetNotification> tweetQueue = new Queue<TweetNotification>(4);
|
private readonly Queue<TweetNotification> tweetQueue = new Queue<TweetNotification>(4);
|
||||||
private bool needsTrim;
|
private bool needsTrim;
|
||||||
|
private bool hasTemporarilyMoved;
|
||||||
|
|
||||||
public FormNotificationTweet(FormBrowser owner, PluginManager pluginManager) : base(owner, pluginManager, true){
|
public FormNotificationTweet(FormBrowser owner, PluginManager pluginManager) : base(owner, pluginManager, true){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@@ -26,18 +41,18 @@ namespace TweetDuck.Core.Notification{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FormNotificationTweet_FormClosing(object sender, FormClosingEventArgs e){
|
protected override void WndProc(ref Message m){
|
||||||
if (e.CloseReason == CloseReason.UserClosing){
|
if (m.Msg == 0x00A7){ // WM_NCMBUTTONDOWN
|
||||||
tweetQueue.Clear(); // already canceled
|
int hitTest = m.WParam.ToInt32();
|
||||||
TrimQueue();
|
|
||||||
|
if (hitTest == 2 || hitTest == 20){ // HTCAPTION, HTCLOSE
|
||||||
|
hasTemporarilyMoved = false;
|
||||||
|
MoveToVisibleLocation();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TrimQueue(){
|
base.WndProc(ref m);
|
||||||
if (needsTrim){
|
|
||||||
tweetQueue.TrimExcess();
|
|
||||||
needsTrim = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// event handlers
|
// event handlers
|
||||||
@@ -68,11 +83,9 @@ namespace TweetDuck.Core.Notification{
|
|||||||
// notification methods
|
// notification methods
|
||||||
|
|
||||||
public override void ShowNotification(TweetNotification notification){
|
public override void ShowNotification(TweetNotification notification){
|
||||||
if (IsPaused){
|
|
||||||
tweetQueue.Enqueue(notification);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
tweetQueue.Enqueue(notification);
|
tweetQueue.Enqueue(notification);
|
||||||
|
|
||||||
|
if (!IsPaused){
|
||||||
UpdateTitle();
|
UpdateTitle();
|
||||||
|
|
||||||
if (totalTime == 0){
|
if (totalTime == 0){
|
||||||
@@ -81,6 +94,19 @@ namespace TweetDuck.Core.Notification{
|
|||||||
}
|
}
|
||||||
|
|
||||||
needsTrim |= tweetQueue.Count >= TrimMinimum;
|
needsTrim |= tweetQueue.Count >= TrimMinimum;
|
||||||
|
TriggerAnalyticsEvent(AnalyticsFile.Event.DesktopNotification);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void HideNotification(){
|
||||||
|
base.HideNotification();
|
||||||
|
tweetQueue.Clear();
|
||||||
|
|
||||||
|
if (needsTrim){
|
||||||
|
tweetQueue.TrimExcess();
|
||||||
|
needsTrim = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasTemporarilyMoved = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void FinishCurrentNotification(){
|
public override void FinishCurrentNotification(){
|
||||||
@@ -88,8 +114,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
LoadNextNotification();
|
LoadNextNotification();
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
HideNotification(true);
|
HideNotification();
|
||||||
TrimQueue();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -12,9 +12,11 @@ using TweetDuck.Resources;
|
|||||||
|
|
||||||
namespace TweetDuck.Core.Notification.Screenshot{
|
namespace TweetDuck.Core.Notification.Screenshot{
|
||||||
sealed class FormNotificationScreenshotable : FormNotificationBase{
|
sealed class FormNotificationScreenshotable : FormNotificationBase{
|
||||||
|
protected override bool CanDragWindow => false;
|
||||||
|
|
||||||
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));
|
||||||
@@ -22,7 +24,7 @@ namespace TweetDuck.Core.Notification.Screenshot{
|
|||||||
browser.LoadingStateChanged += (sender, args) => {
|
browser.LoadingStateChanged += (sender, args) => {
|
||||||
if (!args.IsLoading){
|
if (!args.IsLoading){
|
||||||
using(IFrame frame = args.Browser.MainFrame){
|
using(IFrame frame = args.Browser.MainFrame){
|
||||||
ScriptLoader.ExecuteScript(frame, "window.setTimeout($TD_NotificationScreenshot.trigger, 129)", "gen:screenshot");
|
ScriptLoader.ExecuteScript(frame, "window.setTimeout($TD_NotificationScreenshot.trigger, document.getElementsByTagName('iframe').length ? 267 : 67)", "gen:screenshot");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -42,11 +42,8 @@ namespace TweetDuck.Core.Notification.Screenshot{
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
screenshot = new FormNotificationScreenshotable(Callback, owner, plugins){
|
screenshot = new FormNotificationScreenshotable(Callback, owner, plugins);
|
||||||
CanMoveWindow = () => false
|
screenshot.LoadNotificationForScreenshot(new TweetNotification(string.Empty, string.Empty, string.Empty, html, 0, string.Empty, string.Empty), width, height);
|
||||||
};
|
|
||||||
|
|
||||||
screenshot.LoadNotificationForScreenshot(new TweetNotification(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);
|
||||||
}
|
}
|
||||||
|
@@ -1,50 +1,15 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using TweetDuck.Core.Bridge;
|
||||||
using TweetDuck.Resources;
|
using TweetDuck.Resources;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Notification{
|
namespace TweetDuck.Core.Notification{
|
||||||
sealed class TweetNotification{
|
sealed class TweetNotification{
|
||||||
private static string FontSizeClass { get; set; }
|
private const string DefaultHeadLayout = @"<html id='tduck' class='os-windows txt-size--14' data-td-font='medium' data-td-theme='dark'><head><meta charset='utf-8'><meta http-equiv='X-UA-Compatible' content='chrome=1'><link rel='stylesheet' href='https://ton.twimg.com/tweetdeck-web/web/css/font.5ef884f9f9.css'><link rel='stylesheet' href='https://ton.twimg.com/tweetdeck-web/web/css/app-dark.5631e0dd42.css'><style type='text/css'>body{background:#222426}</style>";
|
||||||
private static string HeadTag { get; set; }
|
private static readonly string CustomCSS = ScriptLoader.LoadResource("styles/notification.css");
|
||||||
|
|
||||||
private const string DefaultFontSizeClass = "medium";
|
public static TweetNotification Example(string html, int characters){
|
||||||
private const string DefaultHeadTag = @"<meta charset='utf-8'><meta http-equiv='X-UA-Compatible' content='chrome=1'><link rel='stylesheet' href='https://ton.twimg.com/tweetdeck-web/web/css/font.5ef884f9f9.css'><link rel='stylesheet' href='https://ton.twimg.com/tweetdeck-web/web/css/app-dark.5631e0dd42.css'><style type='text/css'>body{background:#222426}</style>";
|
return new TweetNotification(string.Empty, string.Empty, "Home", html, characters, string.Empty, string.Empty, true);
|
||||||
private const string CustomCSS = @"body:before{content:none}body{overflow-y:auto}.scroll-styled-v::-webkit-scrollbar{width:7px}.scroll-styled-v::-webkit-scrollbar-thumb{border-radius:0}.scroll-styled-v::-webkit-scrollbar-track{border-left:0}#td-skip{opacity:0;cursor:pointer;transition:opacity 0.15s ease}.td-hover #td-skip{opacity:0.75}#td-skip:hover{opacity:1}.media-size-medium{height:calc(100vh - 16px)!important;max-height:240px;border-radius:1px!important}.js-quote-detail .media-size-medium{height:calc(100vh - 28px)!important;}.js-media.margin-vm, .js-media-preview-container.margin-vm{margin-bottom:0!important}";
|
|
||||||
|
|
||||||
public static int FontSizeLevel{
|
|
||||||
get{
|
|
||||||
switch(FontSizeClass){
|
|
||||||
case "largest": return 4;
|
|
||||||
case "large": return 3;
|
|
||||||
case "medium": return 2;
|
|
||||||
case "small": return 1;
|
|
||||||
default: return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string ExampleTweetHTML;
|
|
||||||
|
|
||||||
public static TweetNotification ExampleTweet{
|
|
||||||
get{
|
|
||||||
if (ExampleTweetHTML == null){
|
|
||||||
ExampleTweetHTML = ScriptLoader.LoadResource("pages/example.html", true);
|
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
ExampleTweetHTML = ExampleTweetHTML.Replace("</p>", @"</p><div style='margin-top:256px'>Scrollbar test padding...</div>");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
return new TweetNotification("Home", ExampleTweetHTML, 95, string.Empty, string.Empty, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetFontSizeClass(string newFSClass){
|
|
||||||
FontSizeClass = newFSClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetHeadTag(string headContents){
|
|
||||||
HeadTag = headContents;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Position{
|
public enum Position{
|
||||||
@@ -55,7 +20,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 +31,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;
|
||||||
|
|
||||||
@@ -82,8 +53,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
public string GenerateHtml(string bodyClasses = null, bool enableCustomCSS = true){
|
public string GenerateHtml(string bodyClasses = null, bool enableCustomCSS = true){
|
||||||
StringBuilder build = new StringBuilder();
|
StringBuilder build = new StringBuilder();
|
||||||
build.Append("<!DOCTYPE html>");
|
build.Append("<!DOCTYPE html>");
|
||||||
build.Append("<html class='os-windows txt-base-").Append(FontSizeClass ?? DefaultFontSizeClass).Append("'>");
|
build.Append(TweetDeckBridge.NotificationHeadLayout ?? DefaultHeadLayout);
|
||||||
build.Append("<head>").Append(HeadTag ?? DefaultHeadTag);
|
|
||||||
|
|
||||||
if (enableCustomCSS){
|
if (enableCustomCSS){
|
||||||
build.Append("<style type='text/css'>").Append(CustomCSS).Append("</style>");
|
build.Append("<style type='text/css'>").Append(CustomCSS).Append("</style>");
|
||||||
@@ -100,7 +70,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
build.Append(' ').Append(bodyClasses);
|
build.Append(' ').Append(bodyClasses);
|
||||||
}
|
}
|
||||||
|
|
||||||
build.Append('\'').Append(isExample ? " td-example-notification" : "").Append("><div class='column' style='width:100%;height:auto;overflow:initial;'>");
|
build.Append('\'').Append(isExample ? " td-example-notification" : "").Append("><div class='column' style='width:100%!important;height:auto!important;overflow:initial!important;'>");
|
||||||
build.Append(html);
|
build.Append(html);
|
||||||
build.Append("</div></body>");
|
build.Append("</div></body>");
|
||||||
build.Append("</html>");
|
build.Append("</html>");
|
||||||
|
103
Core/Other/Analytics/AnalyticsFile.cs
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
using System;
|
||||||
|
using TweetDuck.Data.Serialization;
|
||||||
|
|
||||||
|
namespace TweetDuck.Core.Other.Analytics{
|
||||||
|
sealed class AnalyticsFile{
|
||||||
|
private static readonly FileSerializer<AnalyticsFile> Serializer = new FileSerializer<AnalyticsFile>();
|
||||||
|
|
||||||
|
static AnalyticsFile(){
|
||||||
|
Serializer.RegisterTypeConverter(typeof(DateTime), new SingleTypeConverter<DateTime>{
|
||||||
|
ConvertToString = value => value.ToBinary().ToString(),
|
||||||
|
ConvertToObject = value => DateTime.FromBinary(long.Parse(value))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Event{
|
||||||
|
OpenOptions, OpenPlugins, OpenAbout, OpenGuide,
|
||||||
|
DesktopNotification, SoundNotification, MuteNotification,
|
||||||
|
BrowserContextMenu, BrowserExtraMouseButton,
|
||||||
|
NotificationContextMenu, NotificationExtraMouseButton, NotificationKeyboardShortcut,
|
||||||
|
TweetScreenshot, TweetDetail, VideoPlay, GCReload
|
||||||
|
}
|
||||||
|
|
||||||
|
// STATE PROPERTIES
|
||||||
|
|
||||||
|
public DateTime LastDataCollection { get; set; } = DateTime.MinValue;
|
||||||
|
public string LastCollectionVersion { get; set; } = null;
|
||||||
|
public string LastCollectionMessage { get; set; } = null;
|
||||||
|
|
||||||
|
// USAGE DATA
|
||||||
|
|
||||||
|
public int CountOpenOptions { get; private set; } = 0;
|
||||||
|
public int CountOpenPlugins { get; private set; } = 0;
|
||||||
|
public int CountOpenAbout { get; private set; } = 0;
|
||||||
|
public int CountOpenGuide { get; private set; } = 0;
|
||||||
|
|
||||||
|
public int CountDesktopNotifications { get; private set; } = 0;
|
||||||
|
public int CountSoundNotifications { get; private set; } = 0;
|
||||||
|
public int CountMuteNotifications { get; private set; } = 0;
|
||||||
|
|
||||||
|
public int CountBrowserContextMenus { get; private set; } = 0;
|
||||||
|
public int CountBrowserExtraMouseButtons { get; private set; } = 0;
|
||||||
|
public int CountNotificationContextMenus { get; private set; } = 0;
|
||||||
|
public int CountNotificationExtraMouseButtons { get; private set; } = 0;
|
||||||
|
public int CountNotificationKeyboardShortcuts { get; private set; } = 0;
|
||||||
|
|
||||||
|
public int CountTweetScreenshots { get; private set; } = 0;
|
||||||
|
public int CountTweetDetails { get; private set; } = 0;
|
||||||
|
public int CountVideoPlays { get; private set; } = 0;
|
||||||
|
public int CountGCReloads { get; private set; } = 0;
|
||||||
|
|
||||||
|
// END OF DATA
|
||||||
|
|
||||||
|
private readonly string file;
|
||||||
|
|
||||||
|
private AnalyticsFile(string file){
|
||||||
|
this.file = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TriggerEvent(Event e){
|
||||||
|
switch(e){
|
||||||
|
case Event.OpenOptions: ++CountOpenOptions; break;
|
||||||
|
case Event.OpenPlugins: ++CountOpenPlugins; break;
|
||||||
|
case Event.OpenAbout: ++CountOpenAbout; break;
|
||||||
|
case Event.OpenGuide: ++CountOpenGuide; break;
|
||||||
|
|
||||||
|
case Event.DesktopNotification: ++CountDesktopNotifications; break;
|
||||||
|
case Event.SoundNotification: ++CountSoundNotifications; break;
|
||||||
|
case Event.MuteNotification: ++CountMuteNotifications; break;
|
||||||
|
|
||||||
|
case Event.BrowserContextMenu: ++CountBrowserContextMenus; break;
|
||||||
|
case Event.BrowserExtraMouseButton: ++CountBrowserExtraMouseButtons; break;
|
||||||
|
case Event.NotificationContextMenu: ++CountNotificationContextMenus; break;
|
||||||
|
case Event.NotificationExtraMouseButton: ++CountNotificationExtraMouseButtons; break;
|
||||||
|
case Event.NotificationKeyboardShortcut: ++CountNotificationKeyboardShortcuts; break;
|
||||||
|
|
||||||
|
case Event.TweetScreenshot: ++CountTweetScreenshots; break;
|
||||||
|
case Event.TweetDetail: ++CountTweetDetails; break;
|
||||||
|
case Event.VideoPlay: ++CountVideoPlays; break;
|
||||||
|
case Event.GCReload: ++CountGCReloads; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Save(){
|
||||||
|
try{
|
||||||
|
Serializer.Write(file, this);
|
||||||
|
}catch(Exception e){
|
||||||
|
Program.Reporter.HandleException("Analytics File Error", "Could not save the analytics file.", true, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AnalyticsFile Load(string file){
|
||||||
|
AnalyticsFile config = new AnalyticsFile(file);
|
||||||
|
|
||||||
|
try{
|
||||||
|
Serializer.ReadIfExists(file, config);
|
||||||
|
}catch(Exception e){
|
||||||
|
Program.Reporter.HandleException("Analytics File Error", "Could not open the analytics file.", true, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
127
Core/Other/Analytics/AnalyticsManager.cs
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Timers;
|
||||||
|
using TweetDuck.Core.Controls;
|
||||||
|
using TweetDuck.Core.Utils;
|
||||||
|
using TweetDuck.Plugins;
|
||||||
|
|
||||||
|
namespace TweetDuck.Core.Other.Analytics{
|
||||||
|
sealed class AnalyticsManager : IDisposable{
|
||||||
|
private static readonly TimeSpan CollectionInterval = TimeSpan.FromDays(7);
|
||||||
|
private static readonly Uri CollectionUrl = new Uri("https://tweetduck.chylex.com/breadcrumb/report");
|
||||||
|
|
||||||
|
public AnalyticsFile File { get; }
|
||||||
|
|
||||||
|
private readonly FormBrowser browser;
|
||||||
|
private readonly PluginManager plugins;
|
||||||
|
private readonly Timer currentTimer, saveTimer;
|
||||||
|
|
||||||
|
public AnalyticsManager(FormBrowser browser, PluginManager plugins, string file){
|
||||||
|
this.browser = browser;
|
||||||
|
this.plugins = plugins;
|
||||||
|
this.File = AnalyticsFile.Load(file);
|
||||||
|
|
||||||
|
this.currentTimer = new Timer{ SynchronizingObject = browser };
|
||||||
|
this.currentTimer.Elapsed += currentTimer_Elapsed;
|
||||||
|
|
||||||
|
this.saveTimer = new Timer{ SynchronizingObject = browser, Interval = 60000 };
|
||||||
|
this.saveTimer.Elapsed += saveTimer_Elapsed;
|
||||||
|
|
||||||
|
if (this.File.LastCollectionVersion != Program.VersionTag){
|
||||||
|
ScheduleReportIn(TimeSpan.FromHours(12), string.Empty);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
RestartTimer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose(){
|
||||||
|
if (saveTimer.Enabled){
|
||||||
|
File.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
currentTimer.Dispose();
|
||||||
|
saveTimer.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TriggerEvent(AnalyticsFile.Event e){
|
||||||
|
File.TriggerEvent(e);
|
||||||
|
saveTimer.Enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveTimer_Elapsed(object sender, ElapsedEventArgs e){
|
||||||
|
saveTimer.Stop();
|
||||||
|
File.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ScheduleReportIn(TimeSpan delay, string message = null){
|
||||||
|
SetLastDataCollectionTime(DateTime.Now.Subtract(CollectionInterval).Add(delay), message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetLastDataCollectionTime(DateTime dt, string message = null){
|
||||||
|
File.LastDataCollection = new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, 0, dt.Kind);
|
||||||
|
File.LastCollectionVersion = Program.VersionTag;
|
||||||
|
File.LastCollectionMessage = message ?? dt.ToString("g", Program.Culture);
|
||||||
|
|
||||||
|
File.Save();
|
||||||
|
RestartTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RestartTimer(){
|
||||||
|
TimeSpan diff = DateTime.Now.Subtract(File.LastDataCollection);
|
||||||
|
int minutesTillNext = (int)(CollectionInterval.TotalMinutes-Math.Floor(diff.TotalMinutes));
|
||||||
|
|
||||||
|
currentTimer.Interval = Math.Max(minutesTillNext, 1)*60000;
|
||||||
|
currentTimer.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void currentTimer_Elapsed(object sender, ElapsedEventArgs e){
|
||||||
|
currentTimer.Stop();
|
||||||
|
|
||||||
|
TimeSpan diff = DateTime.Now.Subtract(File.LastDataCollection);
|
||||||
|
|
||||||
|
if (Math.Floor(diff.TotalMinutes) >= CollectionInterval.TotalMinutes){
|
||||||
|
SendReport();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
RestartTimer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SendReport(){
|
||||||
|
AnalyticsReportGenerator.ExternalInfo info = AnalyticsReportGenerator.ExternalInfo.From(browser);
|
||||||
|
|
||||||
|
Task.Factory.StartNew(() => {
|
||||||
|
AnalyticsReport report = AnalyticsReportGenerator.Create(File, info, plugins);
|
||||||
|
BrowserUtils.CreateWebClient().UploadValues(CollectionUrl, "POST", report.ToNameValueCollection());
|
||||||
|
}).ContinueWith(task => browser.InvokeAsyncSafe(() => {
|
||||||
|
if (task.Status == TaskStatus.RanToCompletion){
|
||||||
|
SetLastDataCollectionTime(DateTime.Now);
|
||||||
|
}
|
||||||
|
else if (task.Exception != null){
|
||||||
|
string message = null;
|
||||||
|
|
||||||
|
if (task.Exception.InnerException is WebException e){
|
||||||
|
switch(e.Status){
|
||||||
|
case WebExceptionStatus.ConnectFailure:
|
||||||
|
message = "Connection Error";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WebExceptionStatus.NameResolutionFailure:
|
||||||
|
message = "DNS Error";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WebExceptionStatus.ProtocolError:
|
||||||
|
HttpWebResponse response = e.Response as HttpWebResponse;
|
||||||
|
message = "HTTP Error "+(response != null ? $"{(int)response.StatusCode} ({response.StatusDescription})" : "(unknown code)");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ScheduleReportIn(TimeSpan.FromHours(4), message ?? "Error: "+(task.Exception.InnerException?.Message ?? task.Exception.Message));
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
57
Core/Other/Analytics/AnalyticsReport.cs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace TweetDuck.Core.Other.Analytics{
|
||||||
|
sealed class AnalyticsReport : IEnumerable{
|
||||||
|
private OrderedDictionary data = new OrderedDictionary(32);
|
||||||
|
private int separators;
|
||||||
|
|
||||||
|
public void Add(int ignored){ // adding separators to pretty print
|
||||||
|
data.Add((++separators).ToString(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(string key, string value){
|
||||||
|
data.Add(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AnalyticsReport FinalizeReport(){
|
||||||
|
if (!data.IsReadOnly){
|
||||||
|
data = data.AsReadOnly();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator GetEnumerator(){
|
||||||
|
return data.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public NameValueCollection ToNameValueCollection(){
|
||||||
|
NameValueCollection collection = new NameValueCollection();
|
||||||
|
|
||||||
|
foreach(DictionaryEntry entry in data){
|
||||||
|
if (entry.Value != null){
|
||||||
|
collection.Add(((string)entry.Key).ToLower().Replace(' ', '_'), (string)entry.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString(){
|
||||||
|
StringBuilder build = new StringBuilder();
|
||||||
|
|
||||||
|
foreach(DictionaryEntry entry in data){
|
||||||
|
if (entry.Value == null){
|
||||||
|
build.AppendLine();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
build.AppendLine(entry.Key+": "+entry.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return build.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
306
Core/Other/Analytics/AnalyticsReportGenerator.cs
Normal file
@@ -0,0 +1,306 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.IO;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using Microsoft.Win32;
|
||||||
|
using TweetDuck.Configuration;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Management;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using TweetDuck.Core.Handling;
|
||||||
|
using TweetDuck.Core.Notification;
|
||||||
|
using TweetDuck.Core.Utils;
|
||||||
|
using TweetDuck.Plugins;
|
||||||
|
|
||||||
|
namespace TweetDuck.Core.Other.Analytics{
|
||||||
|
static class AnalyticsReportGenerator{
|
||||||
|
public static AnalyticsReport Create(AnalyticsFile file, ExternalInfo info, PluginManager plugins){
|
||||||
|
Dictionary<string, string> editLayoutDesign = EditLayoutDesignPluginData;
|
||||||
|
|
||||||
|
return new AnalyticsReport{
|
||||||
|
{ "App Version" , Program.VersionTag },
|
||||||
|
{ "App Type" , Program.IsPortable ? "portable" : "installed" },
|
||||||
|
{ "App Dev Tools" , Bool(ContextMenuBase.HasDevTools) },
|
||||||
|
0,
|
||||||
|
{ "System Name" , SystemName },
|
||||||
|
{ "System Edition" , SystemEdition },
|
||||||
|
{ "System Environment" , Environment.Is64BitOperatingSystem ? "64-bit" : "32-bit" },
|
||||||
|
{ "System Build" , SystemBuild },
|
||||||
|
{ "System Locale" , Program.Culture.Name.ToLower() },
|
||||||
|
0,
|
||||||
|
{ "RAM" , Exact(RamSize) },
|
||||||
|
{ "GPU" , GpuVendor },
|
||||||
|
0,
|
||||||
|
{ "Screen Count" , Exact(Screen.AllScreens.Length) },
|
||||||
|
{ "Screen Resolution" , info.Resolution ?? "(unknown)" },
|
||||||
|
{ "Screen DPI" , info.DPI != null ? Exact(info.DPI.Value) : "(unknown)" },
|
||||||
|
0,
|
||||||
|
{ "Hardware Acceleration" , Bool(SysConfig.HardwareAcceleration) },
|
||||||
|
{ "Browser GC Reload" , Bool(SysConfig.EnableBrowserGCReload) },
|
||||||
|
{ "Browser GC Threshold" , Exact(SysConfig.BrowserMemoryThreshold) },
|
||||||
|
0,
|
||||||
|
{ "Expand Links" , Bool(UserConfig.ExpandLinksOnHover) },
|
||||||
|
{ "Switch Account Selectors" , Bool(UserConfig.SwitchAccountSelectors) },
|
||||||
|
{ "Search In First Column" , Bool(UserConfig.OpenSearchInFirstColumn) },
|
||||||
|
{ "Best Image Quality" , Bool(UserConfig.BestImageQuality) },
|
||||||
|
{ "Spell Check" , Bool(UserConfig.EnableSpellCheck) },
|
||||||
|
{ "Zoom" , Exact(UserConfig.ZoomLevel) },
|
||||||
|
0,
|
||||||
|
{ "Updates" , Bool(UserConfig.EnableUpdateCheck) },
|
||||||
|
{ "Update Dismissed" , Bool(!string.IsNullOrEmpty(UserConfig.DismissedUpdate)) },
|
||||||
|
0,
|
||||||
|
{ "Tray" , TrayMode },
|
||||||
|
{ "Tray Highlight" , Bool(UserConfig.EnableTrayHighlight) },
|
||||||
|
0,
|
||||||
|
{ "Notification Position" , NotificationPosition },
|
||||||
|
{ "Notification Size" , NotificationSize },
|
||||||
|
{ "Notification Timer" , NotificationTimer },
|
||||||
|
{ "Notification Timer Speed" , RoundUp(UserConfig.NotificationDurationValue, 5) },
|
||||||
|
{ "Notification Scroll Speed" , Exact(UserConfig.NotificationScrollSpeed) },
|
||||||
|
{ "Notification Column Title" , Bool(UserConfig.DisplayNotificationColumn) },
|
||||||
|
{ "Notification Media Previews" , Bool(UserConfig.NotificationMediaPreviews) },
|
||||||
|
{ "Notification Link Skip" , Bool(UserConfig.NotificationSkipOnLinkClick) },
|
||||||
|
{ "Notification Non-Intrusive" , Bool(UserConfig.NotificationNonIntrusiveMode) },
|
||||||
|
{ "Notification Idle Pause" , Exact(UserConfig.NotificationIdlePauseSeconds) },
|
||||||
|
{ "Custom Sound Notification" , string.IsNullOrEmpty(UserConfig.NotificationSoundPath) ? "off" : Path.GetExtension(UserConfig.NotificationSoundPath) },
|
||||||
|
{ "Custom Sound Notification Volume" , RoundUp(UserConfig.NotificationSoundVolume, 5) },
|
||||||
|
0,
|
||||||
|
{ "Program Arguments" , List(ProgramArguments) },
|
||||||
|
{ "Custom CEF Arguments" , RoundUp((UserConfig.CustomCefArgs ?? string.Empty).Length, 10) },
|
||||||
|
{ "Custom Browser CSS" , RoundUp((UserConfig.CustomBrowserCSS ?? string.Empty).Length, 50) },
|
||||||
|
{ "Custom Notification CSS" , RoundUp((UserConfig.CustomNotificationCSS ?? string.Empty).Length, 50) },
|
||||||
|
0,
|
||||||
|
{ "Plugins All" , List(plugins.Plugins.Select(plugin => plugin.Identifier)) },
|
||||||
|
{ "Plugins Enabled" , List(plugins.Plugins.Where(plugin => plugins.Config.IsEnabled(plugin)).Select(plugin => plugin.Identifier)) },
|
||||||
|
0,
|
||||||
|
{ "Theme" , Dict(editLayoutDesign, "_theme", "light/def") },
|
||||||
|
{ "Column Width" , Dict(editLayoutDesign, "columnWidth", "310px/def") },
|
||||||
|
{ "Font Size" , Dict(editLayoutDesign, "fontSize", "12px/def") },
|
||||||
|
{ "Large Quote Font" , Dict(editLayoutDesign, "increaseQuoteTextSize", "false/def") },
|
||||||
|
{ "Small Compose Font" , Dict(editLayoutDesign, "smallComposeTextSize", "false/def") },
|
||||||
|
{ "Avatar Radius" , Dict(editLayoutDesign, "avatarRadius", "2/def") },
|
||||||
|
{ "Hide Tweet Actions" , Dict(editLayoutDesign, "hideTweetActions", "true/def") },
|
||||||
|
{ "Move Tweet Actions" , Dict(editLayoutDesign, "moveTweetActionsToRight", "true/def") },
|
||||||
|
{ "Theme Color Tweaks" , Dict(editLayoutDesign, "themeColorTweaks", "true/def") },
|
||||||
|
{ "Revert Icons" , Dict(editLayoutDesign, "revertIcons", "true/def") },
|
||||||
|
{ "Optimize Animations" , Dict(editLayoutDesign, "optimizeAnimations", "true/def") },
|
||||||
|
{ "Reply Account Mode" , ReplyAccountConfigFromPlugin },
|
||||||
|
{ "Template Count" , Exact(TemplateCountFromPlugin) },
|
||||||
|
0,
|
||||||
|
{ "Opened Options" , LogRound(file.CountOpenOptions, 4) },
|
||||||
|
{ "Opened Plugins" , LogRound(file.CountOpenPlugins, 4) },
|
||||||
|
{ "Opened About" , LogRound(file.CountOpenAbout, 4) },
|
||||||
|
{ "Opened Guide" , LogRound(file.CountOpenGuide, 4) },
|
||||||
|
{ "Desktop Notifications" , LogRound(file.CountDesktopNotifications, 5) },
|
||||||
|
{ "Sound Notifications" , LogRound(file.CountSoundNotifications, 5) },
|
||||||
|
{ "Mute Notifications" , LogRound(file.CountMuteNotifications, 2) },
|
||||||
|
{ "Browser Context Menus" , LogRound(file.CountBrowserContextMenus, 2) },
|
||||||
|
{ "Browser Extra Mouse Buttons" , LogRound(file.CountBrowserExtraMouseButtons, 2) },
|
||||||
|
{ "Notification Context Menus" , LogRound(file.CountNotificationContextMenus, 2) },
|
||||||
|
{ "Notification Extra Mouse Buttons" , LogRound(file.CountNotificationExtraMouseButtons, 2) },
|
||||||
|
{ "Notification Keyboard Shortcuts" , LogRound(file.CountNotificationKeyboardShortcuts, 2) },
|
||||||
|
{ "Tweet Screenshots" , LogRound(file.CountTweetScreenshots, 2) },
|
||||||
|
{ "Tweet Details" , LogRound(file.CountTweetDetails, 2) },
|
||||||
|
{ "Video Plays" , LogRound(file.CountVideoPlays, 4) },
|
||||||
|
{ "GC Reloads" , LogRound(file.CountGCReloads, 4) }
|
||||||
|
}.FinalizeReport();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static UserConfig UserConfig => Program.UserConfig;
|
||||||
|
private static SystemConfig SysConfig => Program.SystemConfig;
|
||||||
|
|
||||||
|
private static string Bool(bool value) => value ? "on" : "off";
|
||||||
|
private static string Exact(int value) => value.ToString();
|
||||||
|
private static string RoundUp(int value, int multiple) => (multiple*(int)Math.Ceiling((double)value/multiple)).ToString();
|
||||||
|
private static string LogRound(int value, int logBase) => (value <= 0 ? 0 : (int)Math.Pow(logBase, Math.Floor(Math.Log(value, logBase)))).ToString();
|
||||||
|
private static string Dict(Dictionary<string, string> dict, string key, string def = "(unknown)") => dict.TryGetValue(key, out string value) ? value : def;
|
||||||
|
private static string List(IEnumerable<string> list) => string.Join("|", list.DefaultIfEmpty("(none)"));
|
||||||
|
|
||||||
|
private static string SystemName { get; }
|
||||||
|
private static string SystemEdition { get; }
|
||||||
|
private static string SystemBuild { get; }
|
||||||
|
private static int RamSize { get; }
|
||||||
|
private static string GpuVendor { get; }
|
||||||
|
private static string[] ProgramArguments { get; }
|
||||||
|
|
||||||
|
static AnalyticsReportGenerator(){
|
||||||
|
string osName, osEdition, osBuild;
|
||||||
|
|
||||||
|
try{
|
||||||
|
using(RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", false)){
|
||||||
|
// ReSharper disable once PossibleNullReferenceException
|
||||||
|
osName = key.GetValue("ProductName") as string;
|
||||||
|
osBuild = key.GetValue("CurrentBuild") as string;
|
||||||
|
osEdition = null;
|
||||||
|
|
||||||
|
if (osName != null){
|
||||||
|
Match match = Regex.Match(osName, @"^(.*?\d+(?:\.\d+)?) (.*)$");
|
||||||
|
|
||||||
|
if (match.Success){
|
||||||
|
osName = match.Groups[1].Value;
|
||||||
|
osEdition = match.Groups[2].Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}catch{
|
||||||
|
osName = osEdition = osBuild = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
SystemName = osName ?? "Windows (unknown)";
|
||||||
|
SystemEdition = osEdition ?? "(unknown)";
|
||||||
|
SystemBuild = osBuild ?? "(unknown)";
|
||||||
|
|
||||||
|
try{
|
||||||
|
using(ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT Capacity FROM Win32_PhysicalMemory")){
|
||||||
|
foreach(ManagementBaseObject obj in searcher.Get()){
|
||||||
|
RamSize += (int)((ulong)obj["Capacity"]/(1024L*1024L));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}catch{
|
||||||
|
RamSize = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
string gpu = null;
|
||||||
|
|
||||||
|
try{
|
||||||
|
using(ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT Caption FROM Win32_VideoController")){
|
||||||
|
foreach(ManagementBaseObject obj in searcher.Get()){
|
||||||
|
string vendor = obj["Caption"] as string;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(vendor)){
|
||||||
|
gpu = vendor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}catch{
|
||||||
|
// rip
|
||||||
|
}
|
||||||
|
|
||||||
|
GpuVendor = gpu ?? "(unknown)";
|
||||||
|
|
||||||
|
Dictionary<string, string> args = new Dictionary<string, string>();
|
||||||
|
Arguments.GetCurrentClean().ToDictionary(args);
|
||||||
|
ProgramArguments = args.Keys.Select(key => key.TrimStart('-')).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string TrayMode{
|
||||||
|
get{
|
||||||
|
switch(UserConfig.TrayBehavior){
|
||||||
|
case TrayIcon.Behavior.DisplayOnly: return "icon";
|
||||||
|
case TrayIcon.Behavior.MinimizeToTray: return "minimize";
|
||||||
|
case TrayIcon.Behavior.CloseToTray: return "close";
|
||||||
|
case TrayIcon.Behavior.Combined: return "combined";
|
||||||
|
default: return "off";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NotificationPosition{
|
||||||
|
get{
|
||||||
|
switch(UserConfig.NotificationPosition){
|
||||||
|
case TweetNotification.Position.TopLeft: return "top left";
|
||||||
|
case TweetNotification.Position.TopRight: return "top right";
|
||||||
|
case TweetNotification.Position.BottomLeft: return "bottom left";
|
||||||
|
case TweetNotification.Position.BottomRight: return "bottom right";
|
||||||
|
default: return "custom";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NotificationSize{
|
||||||
|
get{
|
||||||
|
switch(UserConfig.NotificationSize){
|
||||||
|
case TweetNotification.Size.Auto: return "auto";
|
||||||
|
default: return RoundUp(UserConfig.CustomNotificationSize.Width, 20)+"x"+RoundUp(UserConfig.CustomNotificationSize.Height, 20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NotificationTimer{
|
||||||
|
get{
|
||||||
|
if (!UserConfig.DisplayNotificationTimer){
|
||||||
|
return "off";
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return UserConfig.NotificationTimerCountDown ? "count down" : "count up";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Dictionary<string, string> EditLayoutDesignPluginData{
|
||||||
|
get{
|
||||||
|
Dictionary<string, string> dict = new Dictionary<string, string>();
|
||||||
|
|
||||||
|
try{
|
||||||
|
string data = File.ReadAllText(Path.Combine(Program.PluginDataPath, "official", "edit-design", "config.json"));
|
||||||
|
|
||||||
|
foreach(Match match in Regex.Matches(data, "\"(\\w+?)\":(.*?)[,}]")){
|
||||||
|
dict[match.Groups[1].Value] = match.Groups[2].Value.Trim('"');
|
||||||
|
}
|
||||||
|
}catch{
|
||||||
|
// rip
|
||||||
|
}
|
||||||
|
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int TemplateCountFromPlugin{
|
||||||
|
get{
|
||||||
|
try{
|
||||||
|
string data = File.ReadAllText(Path.Combine(Program.PluginDataPath, "official", "templates", "config.json"));
|
||||||
|
return Math.Min(StringUtils.CountOccurrences(data, "{\"name\":"), StringUtils.CountOccurrences(data, ",\"contents\":"));
|
||||||
|
}catch{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ReplyAccountConfigFromPlugin{
|
||||||
|
get{
|
||||||
|
try{
|
||||||
|
string data = File.ReadAllText(Path.Combine(Program.PluginDataPath, "official", "reply-account", "configuration.js")).Replace(" ", "");
|
||||||
|
|
||||||
|
Match matchType = Regex.Match(data, "defaultAccount:\"([#@])(.*?)\"(?:,|$)");
|
||||||
|
Match matchAdvanced = Regex.Match(data, "useAdvancedSelector:(.*?)(?:,|$)", RegexOptions.Multiline);
|
||||||
|
|
||||||
|
if (!matchType.Success){
|
||||||
|
return data.Contains("defaultAccount:\"\"") ? "(legacy)" : "(unknown)";
|
||||||
|
}
|
||||||
|
|
||||||
|
string accType = matchType.Groups[1].Value == "#" ? matchType.Groups[2].Value : "account";
|
||||||
|
return matchAdvanced.Success && !matchAdvanced.Value.Contains("false") ? "advanced/"+accType : accType;
|
||||||
|
}catch{
|
||||||
|
return "(unknown)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ExternalInfo{
|
||||||
|
public static ExternalInfo From(Form form){
|
||||||
|
if (form == null){
|
||||||
|
return new ExternalInfo();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
Screen screen = Screen.FromControl(form);
|
||||||
|
int dpi;
|
||||||
|
|
||||||
|
using(Graphics graphics = form.CreateGraphics()){
|
||||||
|
dpi = (int)graphics.DpiY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ExternalInfo{
|
||||||
|
Resolution = screen.Bounds.Width+"x"+screen.Bounds.Height,
|
||||||
|
DPI = dpi
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Resolution { get; private set; }
|
||||||
|
public int? DPI { get; private set; }
|
||||||
|
|
||||||
|
private ExternalInfo(){}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3
Core/Other/FormAbout.Designer.cs
generated
@@ -131,11 +131,14 @@ namespace TweetDuck.Core.Other {
|
|||||||
this.Controls.Add(this.labelDescription);
|
this.Controls.Add(this.labelDescription);
|
||||||
this.Controls.Add(this.pictureLogo);
|
this.Controls.Add(this.pictureLogo);
|
||||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
||||||
|
this.HelpButton = true;
|
||||||
this.MaximizeBox = false;
|
this.MaximizeBox = false;
|
||||||
this.MinimizeBox = false;
|
this.MinimizeBox = false;
|
||||||
this.Name = "FormAbout";
|
this.Name = "FormAbout";
|
||||||
this.ShowIcon = false;
|
this.ShowIcon = false;
|
||||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||||
|
this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.FormAbout_HelpButtonClicked);
|
||||||
|
this.HelpRequested += new System.Windows.Forms.HelpEventHandler(this.FormAbout_HelpRequested);
|
||||||
((System.ComponentModel.ISupportInitialize)(this.pictureLogo)).EndInit();
|
((System.ComponentModel.ISupportInitialize)(this.pictureLogo)).EndInit();
|
||||||
this.tablePanelLinks.ResumeLayout(false);
|
this.tablePanelLinks.ResumeLayout(false);
|
||||||
this.tablePanelLinks.PerformLayout();
|
this.tablePanelLinks.PerformLayout();
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using System.Windows.Forms;
|
using System.ComponentModel;
|
||||||
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Other{
|
namespace TweetDuck.Core.Other{
|
||||||
@@ -19,7 +20,21 @@ namespace TweetDuck.Core.Other{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void OnLinkClicked(object sender, LinkLabelLinkClickedEventArgs e){
|
private void OnLinkClicked(object sender, LinkLabelLinkClickedEventArgs e){
|
||||||
BrowserUtils.OpenExternalBrowserUnsafe(e.Link.LinkData as string);
|
BrowserUtils.OpenExternalBrowser(e.Link.LinkData as string);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FormAbout_HelpRequested(object sender, HelpEventArgs hlpevent){
|
||||||
|
ShowGuide();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FormAbout_HelpButtonClicked(object sender, CancelEventArgs e){
|
||||||
|
e.Cancel = true;
|
||||||
|
ShowGuide();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowGuide(){
|
||||||
|
FormGuide.Show();
|
||||||
|
Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
44
Core/Other/FormGuide.Designer.cs
generated
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
namespace TweetDuck.Core.Other {
|
||||||
|
partial class FormGuide {
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up any resources being used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose(bool disposing) {
|
||||||
|
if (disposing && (components != null)) {
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Windows Form Designer generated code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent() {
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// FormGuide
|
||||||
|
//
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(34)))), ((int)(((byte)(34)))), ((int)(((byte)(34)))));
|
||||||
|
this.ClientSize = new System.Drawing.Size(424, 282);
|
||||||
|
this.Icon = global::TweetDuck.Properties.Resources.icon;
|
||||||
|
this.MinimumSize = new System.Drawing.Size(440, 320);
|
||||||
|
this.Name = "FormGuide";
|
||||||
|
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
117
Core/Other/FormGuide.cs
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using CefSharp;
|
||||||
|
using CefSharp.WinForms;
|
||||||
|
using TweetDuck.Core.Controls;
|
||||||
|
using TweetDuck.Core.Handling;
|
||||||
|
using TweetDuck.Core.Handling.General;
|
||||||
|
using TweetDuck.Core.Other.Analytics;
|
||||||
|
using TweetDuck.Core.Utils;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace TweetDuck.Core.Other{
|
||||||
|
sealed partial class FormGuide : Form{
|
||||||
|
private const string GuideUrl = "https://tweetduck.chylex.com/guide/v2/";
|
||||||
|
private const string GuidePathRegex = @"^guide(?:/v\d+)?(?:/(#.*))?";
|
||||||
|
|
||||||
|
public static bool CheckGuideUrl(string url, out string hash){
|
||||||
|
if (!url.Contains("//tweetduck.chylex.com/guide")){
|
||||||
|
hash = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
string path = url.Substring(url.IndexOf("/guide")+1);
|
||||||
|
Match match = Regex.Match(path, GuidePathRegex);
|
||||||
|
|
||||||
|
if (match.Success){
|
||||||
|
hash = match.Groups[1].Value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
hash = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Show(string hash = null){
|
||||||
|
string url = GuideUrl+(hash ?? string.Empty);
|
||||||
|
FormGuide guide = FormManager.TryFind<FormGuide>();
|
||||||
|
|
||||||
|
if (guide == null){
|
||||||
|
FormBrowser owner = FormManager.TryFind<FormBrowser>();
|
||||||
|
|
||||||
|
if (owner != null){
|
||||||
|
owner.TriggerAnalyticsEvent(AnalyticsFile.Event.OpenGuide);
|
||||||
|
new FormGuide(url, owner).Show(owner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
guide.Reload(url);
|
||||||
|
guide.Activate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly ChromiumWebBrowser browser;
|
||||||
|
|
||||||
|
private FormGuide(string url, Form owner){
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
Text = Program.BrandName+" Guide";
|
||||||
|
|
||||||
|
if (owner != null){
|
||||||
|
Size = new Size(owner.Size.Width*3/4, owner.Size.Height*3/4);
|
||||||
|
VisibleChanged += (sender, args) => this.MoveToCenter(owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.browser = new ChromiumWebBrowser(url){
|
||||||
|
MenuHandler = new ContextMenuGuide(),
|
||||||
|
JsDialogHandler = new JavaScriptDialogHandler(),
|
||||||
|
LifeSpanHandler = new LifeSpanHandler(),
|
||||||
|
RequestHandler = new RequestHandlerBrowser()
|
||||||
|
};
|
||||||
|
|
||||||
|
browser.LoadingStateChanged += browser_LoadingStateChanged;
|
||||||
|
browser.FrameLoadStart += browser_FrameLoadStart;
|
||||||
|
|
||||||
|
browser.BrowserSettings.BackgroundColor = (uint)BackColor.ToArgb();
|
||||||
|
browser.Dock = DockStyle.None;
|
||||||
|
browser.Location = ControlExtensions.InvisibleLocation;
|
||||||
|
Controls.Add(browser);
|
||||||
|
|
||||||
|
Disposed += (sender, args) => {
|
||||||
|
Program.UserConfig.ZoomLevelChanged -= Config_ZoomLevelChanged;
|
||||||
|
browser.Dispose();
|
||||||
|
};
|
||||||
|
|
||||||
|
Program.UserConfig.ZoomLevelChanged += Config_ZoomLevelChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Reload(string url){
|
||||||
|
browser.LoadingStateChanged += browser_LoadingStateChanged;
|
||||||
|
browser.Dock = DockStyle.None;
|
||||||
|
browser.Location = ControlExtensions.InvisibleLocation;
|
||||||
|
browser.Load("about:blank");
|
||||||
|
browser.Load(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
|
||||||
|
if (!e.IsLoading && browser.Address != "about:blank"){
|
||||||
|
this.InvokeAsyncSafe(() => {
|
||||||
|
browser.Location = Point.Empty;
|
||||||
|
browser.Dock = DockStyle.Fill;
|
||||||
|
});
|
||||||
|
|
||||||
|
browser.LoadingStateChanged -= browser_LoadingStateChanged;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void browser_FrameLoadStart(object sender, FrameLoadStartEventArgs e){
|
||||||
|
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Program.UserConfig.ZoomLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Config_ZoomLevelChanged(object sender, EventArgs e){
|
||||||
|
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Program.UserConfig.ZoomLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -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{
|
||||||
|
@@ -35,8 +35,8 @@ namespace TweetDuck.Core.Other{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void ReloadPluginList(){
|
private void ReloadPluginList(){
|
||||||
flowLayoutPlugins.SuspendLayout();
|
|
||||||
flowLayoutPlugins.Controls.Clear();
|
flowLayoutPlugins.Controls.Clear();
|
||||||
|
flowLayoutPlugins.SuspendLayout();
|
||||||
|
|
||||||
foreach(Plugin plugin in pluginManager.Plugins.OrderBy(GetPluginOrderIndex).ThenBy(plugin => plugin.Name)){
|
foreach(Plugin plugin in pluginManager.Plugins.OrderBy(GetPluginOrderIndex).ThenBy(plugin => plugin.Name)){
|
||||||
flowLayoutPlugins.Controls.Add(new PluginControl(pluginManager, plugin));
|
flowLayoutPlugins.Controls.Add(new PluginControl(pluginManager, plugin));
|
||||||
|
@@ -3,6 +3,9 @@ using System.Collections.Generic;
|
|||||||
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.Handling.General;
|
||||||
|
using TweetDuck.Core.Notification.Example;
|
||||||
|
using TweetDuck.Core.Other.Analytics;
|
||||||
using TweetDuck.Core.Other.Settings;
|
using TweetDuck.Core.Other.Settings;
|
||||||
using TweetDuck.Core.Other.Settings.Dialogs;
|
using TweetDuck.Core.Other.Settings.Dialogs;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
@@ -19,7 +22,9 @@ 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 FormSettings(FormBrowser browser, PluginManager plugins, UpdateHandler updates, Type startTab){
|
public bool ShouldReloadBrowser { get; private set; }
|
||||||
|
|
||||||
|
public FormSettings(FormBrowser browser, PluginManager plugins, UpdateHandler updates, AnalyticsManager analytics, Type startTab){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
Text = Program.BrandName+" Options";
|
Text = Program.BrandName+" Options";
|
||||||
@@ -31,18 +36,21 @@ namespace TweetDuck.Core.Other{
|
|||||||
|
|
||||||
this.buttonHeight = BrowserUtils.Scale(39, this.GetDPIScale()) | 1;
|
this.buttonHeight = BrowserUtils.Scale(39, this.GetDPIScale()) | 1;
|
||||||
|
|
||||||
AddButton("General", () => new TabSettingsGeneral(updates));
|
AddButton("General", () => new TabSettingsGeneral(this.browser, updates));
|
||||||
AddButton("Notifications", () => new TabSettingsNotifications(browser.CreateNotificationForm(false)));
|
AddButton("System Tray", () => new TabSettingsTray());
|
||||||
|
AddButton("Notifications", () => new TabSettingsNotifications(new FormNotificationExample(this.browser, this.plugins)));
|
||||||
AddButton("Sounds", () => new TabSettingsSounds());
|
AddButton("Sounds", () => new TabSettingsSounds());
|
||||||
AddButton("Advanced", () => new TabSettingsAdvanced(browser.ReinjectCustomCSS));
|
AddButton("Feedback", () => new TabSettingsFeedback(analytics, AnalyticsReportGenerator.ExternalInfo.From(this.browser), this.plugins));
|
||||||
|
AddButton("Advanced", () => new TabSettingsAdvanced(this.browser.ReinjectCustomCSS));
|
||||||
|
|
||||||
SelectTab(tabs[startTab ?? typeof(TabSettingsGeneral)]);
|
SelectTab(tabs[startTab ?? typeof(TabSettingsGeneral)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FormSettings_FormClosing(object sender, FormClosingEventArgs e){
|
private void FormSettings_FormClosing(object sender, FormClosingEventArgs e){
|
||||||
|
currentTab.Control.OnClosing();
|
||||||
|
|
||||||
foreach(SettingsTab tab in tabs.Values){
|
foreach(SettingsTab tab in tabs.Values){
|
||||||
if (tab.IsInitialized){
|
if (tab.IsInitialized){
|
||||||
tab.Control.OnClosing();
|
|
||||||
tab.Control.Dispose();
|
tab.Control.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,13 +60,20 @@ 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)){
|
currentTab.Control.OnClosing();
|
||||||
if (dialog.ShowDialog() == DialogResult.OK && dialog.ShouldReloadUI){
|
|
||||||
foreach(SettingsTab tab in tabs.Values){
|
|
||||||
tab.Control = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
SelectTab(currentTab);
|
using(DialogSettingsManage dialog = new DialogSettingsManage(plugins)){
|
||||||
|
FormClosing -= FormSettings_FormClosing;
|
||||||
|
|
||||||
|
if (dialog.ShowDialog() == DialogResult.OK){
|
||||||
|
browser.ResumeNotification();
|
||||||
|
|
||||||
|
BrowserProcessHandler.UpdatePrefs();
|
||||||
|
ShouldReloadBrowser = dialog.ShouldReloadBrowser;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
FormClosing += FormSettings_FormClosing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,6 +118,7 @@ namespace TweetDuck.Core.Other{
|
|||||||
private void SelectTab(SettingsTab tab){
|
private void SelectTab(SettingsTab tab){
|
||||||
if (currentTab != null){
|
if (currentTab != null){
|
||||||
currentTab.Button.BackColor = SystemColors.Control;
|
currentTab.Button.BackColor = SystemColors.Control;
|
||||||
|
currentTab.Control.OnClosing();
|
||||||
}
|
}
|
||||||
|
|
||||||
tab.Button.BackColor = tab.Button.FlatAppearance.MouseDownBackColor;
|
tab.Button.BackColor = tab.Button.FlatAppearance.MouseDownBackColor;
|
||||||
@@ -120,11 +136,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,14 +160,10 @@ 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 => control ?? (control = constructor());
|
||||||
get => control ?? (control = constructor());
|
|
||||||
set => control = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsInitialized => control != null;
|
public bool IsInitialized => control != null;
|
||||||
|
|
||||||
private readonly Func<BaseTabSettings> constructor;
|
private readonly Func<BaseTabSettings> constructor;
|
||||||
|
@@ -1,8 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Timers;
|
using System.Timers;
|
||||||
using System.Windows.Forms;
|
|
||||||
using CefSharp;
|
using CefSharp;
|
||||||
|
using CefSharp.WinForms;
|
||||||
|
using TweetDuck.Core.Controls;
|
||||||
using Timer = System.Timers.Timer;
|
using Timer = System.Timers.Timer;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Other.Management{
|
namespace TweetDuck.Core.Other.Management{
|
||||||
@@ -12,7 +13,7 @@ namespace TweetDuck.Core.Other.Management{
|
|||||||
|
|
||||||
private readonly string script;
|
private readonly string script;
|
||||||
private readonly Timer timer;
|
private readonly Timer timer;
|
||||||
private Form owner;
|
private FormBrowser owner;
|
||||||
private IBrowser browser;
|
private IBrowser browser;
|
||||||
|
|
||||||
private long threshold;
|
private long threshold;
|
||||||
@@ -25,11 +26,11 @@ namespace TweetDuck.Core.Other.Management{
|
|||||||
this.timer.Elapsed += timer_Elapsed;
|
this.timer.Elapsed += timer_Elapsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Start(Form owner, IBrowser browser, int thresholdMB){
|
public void Start(ChromiumWebBrowser control, int thresholdMB){
|
||||||
Stop();
|
Stop();
|
||||||
|
|
||||||
this.owner = owner;
|
this.owner = (FormBrowser)control.Parent; // TODO ugly
|
||||||
this.browser = browser;
|
this.browser = control.GetBrowser();
|
||||||
this.threshold = thresholdMB*1024L*1024L;
|
this.threshold = thresholdMB*1024L*1024L;
|
||||||
this.timer.SynchronizingObject = owner;
|
this.timer.SynchronizingObject = owner;
|
||||||
this.timer.Start();
|
this.timer.Start();
|
||||||
@@ -70,6 +71,7 @@ namespace TweetDuck.Core.Other.Management{
|
|||||||
|
|
||||||
if (response.Success && (response.Result as bool? ?? false)){
|
if (response.Success && (response.Result as bool? ?? false)){
|
||||||
SetNeedsCleanup(false);
|
SetNeedsCleanup(false);
|
||||||
|
owner.InvokeAsyncSafe(() => owner.TriggerAnalyticsEvent(Analytics.AnalyticsFile.Event.GCReload));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -8,8 +8,6 @@ using TweetLib.Communication;
|
|||||||
|
|
||||||
namespace TweetDuck.Core.Other.Management{
|
namespace TweetDuck.Core.Other.Management{
|
||||||
sealed class VideoPlayer : IDisposable{
|
sealed class VideoPlayer : IDisposable{
|
||||||
private readonly string PlayerExe = Path.Combine(Program.ProgramPath, "TweetDuck.Video.exe");
|
|
||||||
|
|
||||||
public bool Running{
|
public bool Running{
|
||||||
get{
|
get{
|
||||||
if (currentProcess == null){
|
if (currentProcess == null){
|
||||||
@@ -25,6 +23,7 @@ namespace TweetDuck.Core.Other.Management{
|
|||||||
|
|
||||||
private readonly Form owner;
|
private readonly Form owner;
|
||||||
private string lastUrl;
|
private string lastUrl;
|
||||||
|
private string lastUsername;
|
||||||
|
|
||||||
private Process currentProcess;
|
private Process currentProcess;
|
||||||
private DuplexPipe.Server currentPipe;
|
private DuplexPipe.Server currentPipe;
|
||||||
@@ -35,20 +34,21 @@ namespace TweetDuck.Core.Other.Management{
|
|||||||
this.owner.FormClosing += owner_FormClosing;
|
this.owner.FormClosing += owner_FormClosing;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Launch(string url){
|
public void Launch(string url, string username){
|
||||||
if (Running){
|
if (Running){
|
||||||
Destroy();
|
Destroy();
|
||||||
isClosing = false;
|
isClosing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
lastUrl = url;
|
lastUrl = url;
|
||||||
|
lastUsername = username;
|
||||||
|
|
||||||
try{
|
try{
|
||||||
currentPipe = DuplexPipe.CreateServer();
|
currentPipe = DuplexPipe.CreateServer();
|
||||||
currentPipe.DataIn += currentPipe_DataIn;
|
currentPipe.DataIn += currentPipe_DataIn;
|
||||||
|
|
||||||
if ((currentProcess = Process.Start(new ProcessStartInfo{
|
if ((currentProcess = Process.Start(new ProcessStartInfo{
|
||||||
FileName = PlayerExe,
|
FileName = Path.Combine(Program.ProgramPath, "TweetDuck.Video.exe"),
|
||||||
Arguments = $"{owner.Handle} {Program.UserConfig.VideoPlayerVolume} \"{url}\" \"{currentPipe.GenerateToken()}\"",
|
Arguments = $"{owner.Handle} {Program.UserConfig.VideoPlayerVolume} \"{url}\" \"{currentPipe.GenerateToken()}\"",
|
||||||
UseShellExecute = false,
|
UseShellExecute = false,
|
||||||
RedirectStandardOutput = true
|
RedirectStandardOutput = true
|
||||||
@@ -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, lastUsername);
|
||||||
|
break;
|
||||||
|
|
||||||
case "rip":
|
case "rip":
|
||||||
currentPipe.Dispose();
|
currentPipe.Dispose();
|
||||||
currentPipe = null;
|
currentPipe = null;
|
||||||
@@ -143,21 +147,7 @@ namespace TweetDuck.Core.Other.Management{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void process_Exited(object sender, EventArgs e){
|
private void process_Exited(object sender, EventArgs e){
|
||||||
switch(currentProcess.ExitCode){
|
int exitCode = currentProcess.ExitCode;
|
||||||
case 3: // CODE_LAUNCH_FAIL
|
|
||||||
if (FormMessage.Error("Video Playback Error", "Error launching video player, this may be caused by missing Windows Media Player. Do you want to open the video in a browser?", FormMessage.Yes, FormMessage.No)){
|
|
||||||
BrowserUtils.OpenExternalBrowser(lastUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 4: // CODE_MEDIA_ERROR
|
|
||||||
if (FormMessage.Error("Video Playback Error", "The video could not be loaded, most likely due to unknown format. Do you want to open the video in a browser?", FormMessage.Yes, FormMessage.No)){
|
|
||||||
BrowserUtils.OpenExternalBrowser(lastUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentProcess.Dispose();
|
currentProcess.Dispose();
|
||||||
currentProcess = null;
|
currentProcess = null;
|
||||||
@@ -165,11 +155,27 @@ namespace TweetDuck.Core.Other.Management{
|
|||||||
currentPipe.Dispose();
|
currentPipe.Dispose();
|
||||||
currentPipe = null;
|
currentPipe = null;
|
||||||
|
|
||||||
|
switch(exitCode){
|
||||||
|
case 3: // CODE_LAUNCH_FAIL
|
||||||
|
if (FormMessage.Error("Video Playback Error", "Error launching video player, this may be caused by missing Windows Media Player. Do you want to open the video in your browser?", FormMessage.Yes, FormMessage.No)){
|
||||||
|
BrowserUtils.OpenExternalBrowser(lastUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4: // CODE_MEDIA_ERROR
|
||||||
|
if (FormMessage.Error("Video Playback Error", "The video could not be loaded, most likely due to unknown format. Do you want to open the video in your browser?", FormMessage.Yes, FormMessage.No)){
|
||||||
|
BrowserUtils.OpenExternalBrowser(lastUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
owner.InvokeAsyncSafe(TriggerProcessExitEventUnsafe);
|
owner.InvokeAsyncSafe(TriggerProcessExitEventUnsafe);
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
90
Core/Other/Settings/Dialogs/DialogSettingsAnalytics.Designer.cs
generated
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
namespace TweetDuck.Core.Other.Settings.Dialogs {
|
||||||
|
partial class DialogSettingsAnalytics {
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up any resources being used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose(bool disposing) {
|
||||||
|
if (disposing && (components != null)) {
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Windows Form Designer generated code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent() {
|
||||||
|
this.textBoxReport = new System.Windows.Forms.TextBox();
|
||||||
|
this.btnClose = new System.Windows.Forms.Button();
|
||||||
|
this.labelInfo = new System.Windows.Forms.Label();
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// textBoxReport
|
||||||
|
//
|
||||||
|
this.textBoxReport.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Left)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.textBoxReport.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
||||||
|
this.textBoxReport.Location = new System.Drawing.Point(12, 41);
|
||||||
|
this.textBoxReport.Multiline = true;
|
||||||
|
this.textBoxReport.Name = "textBoxReport";
|
||||||
|
this.textBoxReport.ReadOnly = true;
|
||||||
|
this.textBoxReport.ScrollBars = System.Windows.Forms.ScrollBars.Both;
|
||||||
|
this.textBoxReport.Size = new System.Drawing.Size(460, 480);
|
||||||
|
this.textBoxReport.TabIndex = 1;
|
||||||
|
//
|
||||||
|
// btnClose
|
||||||
|
//
|
||||||
|
this.btnClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.btnClose.Location = new System.Drawing.Point(416, 527);
|
||||||
|
this.btnClose.Name = "btnClose";
|
||||||
|
this.btnClose.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||||
|
this.btnClose.Size = new System.Drawing.Size(56, 23);
|
||||||
|
this.btnClose.TabIndex = 2;
|
||||||
|
this.btnClose.Text = "Close";
|
||||||
|
this.btnClose.UseVisualStyleBackColor = true;
|
||||||
|
this.btnClose.Click += new System.EventHandler(this.btnClose_Click);
|
||||||
|
//
|
||||||
|
// labelInfo
|
||||||
|
//
|
||||||
|
this.labelInfo.Location = new System.Drawing.Point(12, 9);
|
||||||
|
this.labelInfo.Margin = new System.Windows.Forms.Padding(3, 0, 3, 3);
|
||||||
|
this.labelInfo.Name = "labelInfo";
|
||||||
|
this.labelInfo.Size = new System.Drawing.Size(460, 26);
|
||||||
|
this.labelInfo.TabIndex = 0;
|
||||||
|
this.labelInfo.Text = "When enabled, this data will be sent over a secure network roughly once every wee" +
|
||||||
|
"k.\r\nSome numbers in the report were made imprecise on purpose.";
|
||||||
|
//
|
||||||
|
// DialogSettingsAnalytics
|
||||||
|
//
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.ClientSize = new System.Drawing.Size(484, 562);
|
||||||
|
this.Controls.Add(this.labelInfo);
|
||||||
|
this.Controls.Add(this.btnClose);
|
||||||
|
this.Controls.Add(this.textBoxReport);
|
||||||
|
this.MinimumSize = new System.Drawing.Size(450, 340);
|
||||||
|
this.Name = "DialogSettingsAnalytics";
|
||||||
|
this.ShowIcon = false;
|
||||||
|
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
this.PerformLayout();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private System.Windows.Forms.TextBox textBoxReport;
|
||||||
|
private System.Windows.Forms.Button btnClose;
|
||||||
|
private System.Windows.Forms.Label labelInfo;
|
||||||
|
}
|
||||||
|
}
|
24
Core/Other/Settings/Dialogs/DialogSettingsAnalytics.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using System;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using TweetDuck.Core.Controls;
|
||||||
|
using TweetDuck.Core.Other.Analytics;
|
||||||
|
|
||||||
|
namespace TweetDuck.Core.Other.Settings.Dialogs{
|
||||||
|
sealed partial class DialogSettingsAnalytics : Form{
|
||||||
|
public string CefArgs => textBoxReport.Text;
|
||||||
|
|
||||||
|
public DialogSettingsAnalytics(AnalyticsReport report){
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
Text = Program.BrandName+" Options - Analytics Report";
|
||||||
|
|
||||||
|
textBoxReport.EnableMultilineShortcuts();
|
||||||
|
textBoxReport.Text = report.ToString().TrimEnd();
|
||||||
|
textBoxReport.Select(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btnClose_Click(object sender, EventArgs e){
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -35,7 +35,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void btnOpenWiki_Click(object sender, EventArgs e){
|
private void btnOpenWiki_Click(object sender, EventArgs e){
|
||||||
BrowserUtils.OpenExternalBrowserUnsafe("https://github.com/chylex/TweetDuck/wiki");
|
BrowserUtils.OpenExternalBrowser("https://github.com/chylex/TweetDuck/wiki");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void btnApply_Click(object sender, EventArgs e){
|
private void btnApply_Click(object sender, EventArgs e){
|
||||||
|
@@ -19,7 +19,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void btnHelp_Click(object sender, EventArgs e){
|
private void btnHelp_Click(object sender, EventArgs e){
|
||||||
BrowserUtils.OpenExternalBrowserUnsafe("http://peter.sh/experiments/chromium-command-line-switches/");
|
BrowserUtils.OpenExternalBrowser("http://peter.sh/experiments/chromium-command-line-switches/");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void btnApply_Click(object sender, EventArgs e){
|
private void btnApply_Click(object sender, EventArgs e){
|
||||||
|
@@ -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{
|
||||||
@@ -14,13 +16,13 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
|
|
||||||
set{
|
set{
|
||||||
// this will call events and SetFlag, which also updates the UI
|
// this will call events and SetFlag, which also updates the UI
|
||||||
cbConfig.Checked = value.HasFlag(ExportFileFlags.Config);
|
cbConfig.Checked = value.HasFlag(ExportFileFlags.UserConfig);
|
||||||
cbSession.Checked = value.HasFlag(ExportFileFlags.Session);
|
cbSession.Checked = value.HasFlag(ExportFileFlags.Session);
|
||||||
cbPluginData.Checked = value.HasFlag(ExportFileFlags.PluginData);
|
cbPluginData.Checked = value.HasFlag(ExportFileFlags.PluginData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
@@ -40,7 +42,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void cbConfig_CheckedChanged(object sender, EventArgs e){
|
private void cbConfig_CheckedChanged(object sender, EventArgs e){
|
||||||
SetFlag(ExportFileFlags.Config, cbConfig.Checked);
|
SetFlag(ExportFileFlags.UserConfig, cbConfig.Checked);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cbSession_CheckedChanged(object sender, EventArgs e){
|
private void cbSession_CheckedChanged(object sender, EventArgs e){
|
||||||
@@ -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.UserConfig;
|
||||||
Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import
|
// Import
|
||||||
@@ -109,10 +106,59 @@ 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.UserConfig)){
|
||||||
|
Program.ResetConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Flags.HasFlag(ExportFileFlags.SystemConfig)){
|
||||||
|
try{
|
||||||
|
File.Delete(Program.SystemConfigFilePath);
|
||||||
|
}catch(Exception ex){
|
||||||
|
Program.Reporter.HandleException("System Config Reset Error", "Could not delete system config.", true, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 if (Flags.HasFlag(ExportFileFlags.SystemConfig)){
|
||||||
|
Program.Restart();
|
||||||
|
}
|
||||||
|
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){
|
||||||
|
if (Flags.HasFlag(ExportFileFlags.Session)){
|
||||||
|
Program.Restart(Arguments.ArgImportCookies);
|
||||||
|
}
|
||||||
|
else if (Flags.HasFlag(ExportFileFlags.SystemConfig)){
|
||||||
|
Program.Restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
ShouldReloadBrowser = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
@@ -141,6 +187,8 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
}
|
}
|
||||||
|
|
||||||
Program.UserConfig.Save();
|
Program.UserConfig.Save();
|
||||||
|
Program.SystemConfig.Save();
|
||||||
|
|
||||||
ExportManager manager = new ExportManager(file, plugins);
|
ExportManager manager = new ExportManager(file, plugins);
|
||||||
|
|
||||||
if (!manager.Export(Flags)){
|
if (!manager.Export(Flags)){
|
||||||
@@ -165,6 +213,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();
|
||||||
}
|
}
|
||||||
|
@@ -4,9 +4,10 @@ namespace TweetDuck.Core.Other.Settings.Export{
|
|||||||
[Flags]
|
[Flags]
|
||||||
enum ExportFileFlags{
|
enum ExportFileFlags{
|
||||||
None = 0,
|
None = 0,
|
||||||
Config = 1,
|
UserConfig = 1,
|
||||||
Session = 2,
|
SystemConfig = 2, // TODO implement later
|
||||||
PluginData = 4,
|
Session = 4,
|
||||||
All = Config|Session|PluginData
|
PluginData = 8,
|
||||||
|
All = UserConfig|SystemConfig|Session|PluginData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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;
|
||||||
@@ -26,10 +25,14 @@ namespace TweetDuck.Core.Other.Settings.Export{
|
|||||||
public bool Export(ExportFileFlags flags){
|
public bool Export(ExportFileFlags flags){
|
||||||
try{
|
try{
|
||||||
using(CombinedFileStream stream = new CombinedFileStream(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))){
|
using(CombinedFileStream stream = new CombinedFileStream(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))){
|
||||||
if (flags.HasFlag(ExportFileFlags.Config)){
|
if (flags.HasFlag(ExportFileFlags.UserConfig)){
|
||||||
stream.WriteFile("config", Program.UserConfigFilePath);
|
stream.WriteFile("config", Program.UserConfigFilePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (flags.HasFlag(ExportFileFlags.SystemConfig)){
|
||||||
|
stream.WriteFile("system", Program.SystemConfigFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
if (flags.HasFlag(ExportFileFlags.PluginData)){
|
if (flags.HasFlag(ExportFileFlags.PluginData)){
|
||||||
stream.WriteFile("plugin.config", Program.PluginConfigFilePath);
|
stream.WriteFile("plugin.config", Program.PluginConfigFilePath);
|
||||||
|
|
||||||
@@ -68,7 +71,11 @@ namespace TweetDuck.Core.Other.Settings.Export{
|
|||||||
while((key = stream.SkipFile()) != null){
|
while((key = stream.SkipFile()) != null){
|
||||||
switch(key){
|
switch(key){
|
||||||
case "config":
|
case "config":
|
||||||
flags |= ExportFileFlags.Config;
|
flags |= ExportFileFlags.UserConfig;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "system":
|
||||||
|
flags |= ExportFileFlags.SystemConfig;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "plugin.config":
|
case "plugin.config":
|
||||||
@@ -100,12 +107,20 @@ namespace TweetDuck.Core.Other.Settings.Export{
|
|||||||
while((entry = stream.ReadFile()) != null){
|
while((entry = stream.ReadFile()) != null){
|
||||||
switch(entry.KeyName){
|
switch(entry.KeyName){
|
||||||
case "config":
|
case "config":
|
||||||
if (flags.HasFlag(ExportFileFlags.Config)){
|
if (flags.HasFlag(ExportFileFlags.UserConfig)){
|
||||||
entry.WriteToFile(Program.UserConfigFilePath);
|
entry.WriteToFile(Program.UserConfigFilePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "system":
|
||||||
|
if (flags.HasFlag(ExportFileFlags.SystemConfig)){
|
||||||
|
entry.WriteToFile(Program.SystemConfigFilePath);
|
||||||
|
IsRestarting = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
case "plugin.config":
|
case "plugin.config":
|
||||||
if (flags.HasFlag(ExportFileFlags.PluginData)){
|
if (flags.HasFlag(ExportFileFlags.PluginData)){
|
||||||
entry.WriteToFile(Program.PluginConfigFilePath);
|
entry.WriteToFile(Program.PluginConfigFilePath);
|
||||||
@@ -141,13 +156,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 +177,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 +194,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;
|
||||||
@@ -29,14 +29,10 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
numMemoryThreshold.Enabled = checkBrowserGCReload.Checked;
|
numMemoryThreshold.Enabled = checkBrowserGCReload.Checked;
|
||||||
numMemoryThreshold.SetValueSafe(SysConfig.BrowserMemoryThreshold);
|
numMemoryThreshold.SetValueSafe(SysConfig.BrowserMemoryThreshold);
|
||||||
|
|
||||||
BrowserCache.CalculateCacheSize(bytes => this.InvokeSafe(() => {
|
BrowserCache.CalculateCacheSize(task => {
|
||||||
if (bytes == -1L){
|
string text = task.IsCompleted ? (int)Math.Ceiling(task.Result/(1024.0*1024.0))+" MB" : "(unknown size)";
|
||||||
btnClearCache.Text = "Clear Cache (unknown size)";
|
this.InvokeSafe(() => btnClearCache.Text = $"Clear Cache ({text})");
|
||||||
}
|
});
|
||||||
else{
|
|
||||||
btnClearCache.Text = "Clear Cache ("+(int)Math.Ceiling(bytes/(1024.0*1024.0))+" MB)";
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnReady(){
|
public override void OnReady(){
|
||||||
|
158
Core/Other/Settings/TabSettingsFeedback.Designer.cs
generated
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
namespace TweetDuck.Core.Other.Settings {
|
||||||
|
partial class TabSettingsFeedback {
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up any resources being used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose(bool disposing) {
|
||||||
|
if (disposing && (components != null)) {
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Component Designer generated code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent() {
|
||||||
|
this.components = new System.ComponentModel.Container();
|
||||||
|
this.panelFeedback = new System.Windows.Forms.Panel();
|
||||||
|
this.labelDataCollectionMessage = new System.Windows.Forms.Label();
|
||||||
|
this.btnViewReport = new System.Windows.Forms.Button();
|
||||||
|
this.btnSendFeedback = new System.Windows.Forms.Button();
|
||||||
|
this.labelDataCollectionLink = new System.Windows.Forms.LinkLabel();
|
||||||
|
this.checkDataCollection = new System.Windows.Forms.CheckBox();
|
||||||
|
this.labelDataCollection = new System.Windows.Forms.Label();
|
||||||
|
this.labelFeedback = new System.Windows.Forms.Label();
|
||||||
|
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
||||||
|
this.panelFeedback.SuspendLayout();
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// panelFeedback
|
||||||
|
//
|
||||||
|
this.panelFeedback.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.panelFeedback.Controls.Add(this.labelDataCollectionMessage);
|
||||||
|
this.panelFeedback.Controls.Add(this.btnViewReport);
|
||||||
|
this.panelFeedback.Controls.Add(this.btnSendFeedback);
|
||||||
|
this.panelFeedback.Controls.Add(this.labelDataCollectionLink);
|
||||||
|
this.panelFeedback.Controls.Add(this.checkDataCollection);
|
||||||
|
this.panelFeedback.Controls.Add(this.labelDataCollection);
|
||||||
|
this.panelFeedback.Location = new System.Drawing.Point(9, 31);
|
||||||
|
this.panelFeedback.Name = "panelFeedback";
|
||||||
|
this.panelFeedback.Size = new System.Drawing.Size(322, 188);
|
||||||
|
this.panelFeedback.TabIndex = 1;
|
||||||
|
//
|
||||||
|
// labelDataCollectionMessage
|
||||||
|
//
|
||||||
|
this.labelDataCollectionMessage.Location = new System.Drawing.Point(6, 114);
|
||||||
|
this.labelDataCollectionMessage.Margin = new System.Windows.Forms.Padding(6);
|
||||||
|
this.labelDataCollectionMessage.Name = "labelDataCollectionMessage";
|
||||||
|
this.labelDataCollectionMessage.Size = new System.Drawing.Size(310, 67);
|
||||||
|
this.labelDataCollectionMessage.TabIndex = 5;
|
||||||
|
//
|
||||||
|
// btnViewReport
|
||||||
|
//
|
||||||
|
this.btnViewReport.AutoSize = true;
|
||||||
|
this.btnViewReport.Location = new System.Drawing.Point(6, 82);
|
||||||
|
this.btnViewReport.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
|
||||||
|
this.btnViewReport.Name = "btnViewReport";
|
||||||
|
this.btnViewReport.Size = new System.Drawing.Size(144, 23);
|
||||||
|
this.btnViewReport.TabIndex = 4;
|
||||||
|
this.btnViewReport.Text = "View My Analytics Report";
|
||||||
|
this.btnViewReport.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
|
// btnSendFeedback
|
||||||
|
//
|
||||||
|
this.btnSendFeedback.AutoSize = true;
|
||||||
|
this.btnSendFeedback.Location = new System.Drawing.Point(5, 3);
|
||||||
|
this.btnSendFeedback.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
|
||||||
|
this.btnSendFeedback.Name = "btnSendFeedback";
|
||||||
|
this.btnSendFeedback.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||||
|
this.btnSendFeedback.Size = new System.Drawing.Size(164, 23);
|
||||||
|
this.btnSendFeedback.TabIndex = 0;
|
||||||
|
this.btnSendFeedback.Text = "Send Feedback / Bug Report";
|
||||||
|
this.btnSendFeedback.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
|
// labelDataCollectionLink
|
||||||
|
//
|
||||||
|
this.labelDataCollectionLink.AutoSize = true;
|
||||||
|
this.labelDataCollectionLink.LinkArea = new System.Windows.Forms.LinkArea(1, 10);
|
||||||
|
this.labelDataCollectionLink.LinkBehavior = System.Windows.Forms.LinkBehavior.HoverUnderline;
|
||||||
|
this.labelDataCollectionLink.Location = new System.Drawing.Point(141, 60);
|
||||||
|
this.labelDataCollectionLink.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
|
||||||
|
this.labelDataCollectionLink.Name = "labelDataCollectionLink";
|
||||||
|
this.labelDataCollectionLink.Size = new System.Drawing.Size(66, 17);
|
||||||
|
this.labelDataCollectionLink.TabIndex = 3;
|
||||||
|
this.labelDataCollectionLink.TabStop = true;
|
||||||
|
this.labelDataCollectionLink.Text = "(learn more)";
|
||||||
|
this.labelDataCollectionLink.UseCompatibleTextRendering = true;
|
||||||
|
//
|
||||||
|
// checkDataCollection
|
||||||
|
//
|
||||||
|
this.checkDataCollection.AutoSize = true;
|
||||||
|
this.checkDataCollection.Location = new System.Drawing.Point(6, 59);
|
||||||
|
this.checkDataCollection.Margin = new System.Windows.Forms.Padding(6, 5, 0, 3);
|
||||||
|
this.checkDataCollection.Name = "checkDataCollection";
|
||||||
|
this.checkDataCollection.Size = new System.Drawing.Size(135, 17);
|
||||||
|
this.checkDataCollection.TabIndex = 2;
|
||||||
|
this.checkDataCollection.Text = "Send Anonymous Data";
|
||||||
|
this.checkDataCollection.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
|
// labelDataCollection
|
||||||
|
//
|
||||||
|
this.labelDataCollection.AutoSize = true;
|
||||||
|
this.labelDataCollection.Location = new System.Drawing.Point(3, 41);
|
||||||
|
this.labelDataCollection.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
|
||||||
|
this.labelDataCollection.Name = "labelDataCollection";
|
||||||
|
this.labelDataCollection.Size = new System.Drawing.Size(79, 13);
|
||||||
|
this.labelDataCollection.TabIndex = 1;
|
||||||
|
this.labelDataCollection.Text = "Data Collection";
|
||||||
|
//
|
||||||
|
// labelFeedback
|
||||||
|
//
|
||||||
|
this.labelFeedback.AutoSize = true;
|
||||||
|
this.labelFeedback.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
||||||
|
this.labelFeedback.Location = new System.Drawing.Point(6, 8);
|
||||||
|
this.labelFeedback.Margin = new System.Windows.Forms.Padding(0, 2, 0, 0);
|
||||||
|
this.labelFeedback.Name = "labelFeedback";
|
||||||
|
this.labelFeedback.Size = new System.Drawing.Size(80, 20);
|
||||||
|
this.labelFeedback.TabIndex = 0;
|
||||||
|
this.labelFeedback.Text = "Feedback";
|
||||||
|
//
|
||||||
|
// TabSettingsFeedback
|
||||||
|
//
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.Controls.Add(this.panelFeedback);
|
||||||
|
this.Controls.Add(this.labelFeedback);
|
||||||
|
this.Name = "TabSettingsFeedback";
|
||||||
|
this.Size = new System.Drawing.Size(340, 230);
|
||||||
|
this.panelFeedback.ResumeLayout(false);
|
||||||
|
this.panelFeedback.PerformLayout();
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
this.PerformLayout();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private System.Windows.Forms.Panel panelFeedback;
|
||||||
|
private System.Windows.Forms.CheckBox checkDataCollection;
|
||||||
|
private System.Windows.Forms.Label labelDataCollection;
|
||||||
|
private System.Windows.Forms.Label labelFeedback;
|
||||||
|
private System.Windows.Forms.ToolTip toolTip;
|
||||||
|
private System.Windows.Forms.LinkLabel labelDataCollectionLink;
|
||||||
|
private System.Windows.Forms.Button btnSendFeedback;
|
||||||
|
private System.Windows.Forms.Button btnViewReport;
|
||||||
|
private System.Windows.Forms.Label labelDataCollectionMessage;
|
||||||
|
}
|
||||||
|
}
|
54
Core/Other/Settings/TabSettingsFeedback.cs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
using System;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using TweetDuck.Core.Other.Analytics;
|
||||||
|
using TweetDuck.Core.Other.Settings.Dialogs;
|
||||||
|
using TweetDuck.Core.Utils;
|
||||||
|
using TweetDuck.Plugins;
|
||||||
|
|
||||||
|
namespace TweetDuck.Core.Other.Settings{
|
||||||
|
sealed partial class TabSettingsFeedback : BaseTabSettings{
|
||||||
|
private readonly AnalyticsFile analyticsFile;
|
||||||
|
private readonly AnalyticsReportGenerator.ExternalInfo analyticsInfo;
|
||||||
|
private readonly PluginManager plugins;
|
||||||
|
|
||||||
|
public TabSettingsFeedback(AnalyticsManager analytics, AnalyticsReportGenerator.ExternalInfo analyticsInfo, PluginManager plugins){
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
this.analyticsFile = analytics?.File ?? AnalyticsFile.Load(Program.AnalyticsFilePath);
|
||||||
|
this.analyticsInfo = analyticsInfo;
|
||||||
|
this.plugins = plugins;
|
||||||
|
|
||||||
|
checkDataCollection.Checked = Config.AllowDataCollection;
|
||||||
|
|
||||||
|
if (analytics != null){
|
||||||
|
string collectionTime = analyticsFile.LastCollectionMessage;
|
||||||
|
labelDataCollectionMessage.Text = string.IsNullOrEmpty(collectionTime) ? "No collection yet" : "Last collection: "+collectionTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnReady(){
|
||||||
|
btnSendFeedback.Click += btnSendFeedback_Click;
|
||||||
|
checkDataCollection.CheckedChanged += checkDataCollection_CheckedChanged;
|
||||||
|
labelDataCollectionLink.LinkClicked += labelDataCollectionLink_LinkClicked;
|
||||||
|
btnViewReport.Click += btnViewReport_Click;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btnSendFeedback_Click(object sender, EventArgs e){
|
||||||
|
BrowserUtils.OpenExternalBrowser("https://github.com/chylex/TweetDuck/issues/new");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkDataCollection_CheckedChanged(object sender, EventArgs e){
|
||||||
|
Config.AllowDataCollection = checkDataCollection.Checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void labelDataCollectionLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e){
|
||||||
|
BrowserUtils.OpenExternalBrowser("https://github.com/chylex/TweetDuck/wiki/Send-anonymous-data");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btnViewReport_Click(object sender, EventArgs e){
|
||||||
|
using(DialogSettingsAnalytics dialog = new DialogSettingsAnalytics(AnalyticsReportGenerator.Create(analyticsFile, analyticsInfo, plugins))){
|
||||||
|
dialog.ShowDialog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
149
Core/Other/Settings/TabSettingsGeneral.Designer.cs
generated
@@ -25,29 +25,25 @@
|
|||||||
private void InitializeComponent() {
|
private void InitializeComponent() {
|
||||||
this.components = new System.ComponentModel.Container();
|
this.components = new System.ComponentModel.Container();
|
||||||
this.checkExpandLinks = new System.Windows.Forms.CheckBox();
|
this.checkExpandLinks = new System.Windows.Forms.CheckBox();
|
||||||
this.comboBoxTrayType = new System.Windows.Forms.ComboBox();
|
|
||||||
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
||||||
this.checkTrayHighlight = new System.Windows.Forms.CheckBox();
|
|
||||||
this.checkSpellCheck = new System.Windows.Forms.CheckBox();
|
this.checkSpellCheck = new System.Windows.Forms.CheckBox();
|
||||||
this.checkUpdateNotifications = new System.Windows.Forms.CheckBox();
|
this.checkUpdateNotifications = new System.Windows.Forms.CheckBox();
|
||||||
this.btnCheckUpdates = new System.Windows.Forms.Button();
|
this.btnCheckUpdates = new System.Windows.Forms.Button();
|
||||||
this.labelZoomValue = new System.Windows.Forms.Label();
|
this.labelZoomValue = new System.Windows.Forms.Label();
|
||||||
this.checkSwitchAccountSelectors = new System.Windows.Forms.CheckBox();
|
this.checkSwitchAccountSelectors = new System.Windows.Forms.CheckBox();
|
||||||
this.labelTrayIcon = new System.Windows.Forms.Label();
|
this.checkBestImageQuality = new System.Windows.Forms.CheckBox();
|
||||||
|
this.checkOpenSearchInFirstColumn = new System.Windows.Forms.CheckBox();
|
||||||
this.trackBarZoom = new System.Windows.Forms.TrackBar();
|
this.trackBarZoom = new System.Windows.Forms.TrackBar();
|
||||||
this.labelZoom = new System.Windows.Forms.Label();
|
this.labelZoom = new System.Windows.Forms.Label();
|
||||||
this.zoomUpdateTimer = new System.Windows.Forms.Timer(this.components);
|
this.zoomUpdateTimer = new System.Windows.Forms.Timer(this.components);
|
||||||
this.labelUI = new System.Windows.Forms.Label();
|
this.labelUI = new System.Windows.Forms.Label();
|
||||||
this.panelUI = new System.Windows.Forms.Panel();
|
this.panelUI = new System.Windows.Forms.Panel();
|
||||||
this.labelTray = new System.Windows.Forms.Label();
|
|
||||||
this.panelUpdates = new System.Windows.Forms.Panel();
|
this.panelUpdates = new System.Windows.Forms.Panel();
|
||||||
this.panelTray = new System.Windows.Forms.Panel();
|
|
||||||
this.labelUpdates = new System.Windows.Forms.Label();
|
this.labelUpdates = new System.Windows.Forms.Label();
|
||||||
this.checkBestImageQuality = new System.Windows.Forms.CheckBox();
|
this.checkAnimatedAvatars = new System.Windows.Forms.CheckBox();
|
||||||
((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).BeginInit();
|
((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).BeginInit();
|
||||||
this.panelUI.SuspendLayout();
|
this.panelUI.SuspendLayout();
|
||||||
this.panelUpdates.SuspendLayout();
|
this.panelUpdates.SuspendLayout();
|
||||||
this.panelTray.SuspendLayout();
|
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
//
|
//
|
||||||
// checkExpandLinks
|
// checkExpandLinks
|
||||||
@@ -62,37 +58,14 @@
|
|||||||
this.toolTip.SetToolTip(this.checkExpandLinks, "Expands links inside the tweets. If disabled,\r\nthe full links show up in a tooltip instead.");
|
this.toolTip.SetToolTip(this.checkExpandLinks, "Expands links inside the tweets. If disabled,\r\nthe full links show up in a tooltip instead.");
|
||||||
this.checkExpandLinks.UseVisualStyleBackColor = true;
|
this.checkExpandLinks.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
// comboBoxTrayType
|
|
||||||
//
|
|
||||||
this.comboBoxTrayType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
|
||||||
this.comboBoxTrayType.FormattingEnabled = true;
|
|
||||||
this.comboBoxTrayType.Location = new System.Drawing.Point(5, 5);
|
|
||||||
this.comboBoxTrayType.Margin = new System.Windows.Forms.Padding(5, 5, 3, 3);
|
|
||||||
this.comboBoxTrayType.Name = "comboBoxTrayType";
|
|
||||||
this.comboBoxTrayType.Size = new System.Drawing.Size(144, 21);
|
|
||||||
this.comboBoxTrayType.TabIndex = 0;
|
|
||||||
this.toolTip.SetToolTip(this.comboBoxTrayType, "Changes behavior of the Tray icon.\r\nRight-click the icon for an action menu.");
|
|
||||||
//
|
|
||||||
// checkTrayHighlight
|
|
||||||
//
|
|
||||||
this.checkTrayHighlight.AutoSize = true;
|
|
||||||
this.checkTrayHighlight.Location = new System.Drawing.Point(6, 56);
|
|
||||||
this.checkTrayHighlight.Margin = new System.Windows.Forms.Padding(6, 5, 3, 3);
|
|
||||||
this.checkTrayHighlight.Name = "checkTrayHighlight";
|
|
||||||
this.checkTrayHighlight.Size = new System.Drawing.Size(103, 17);
|
|
||||||
this.checkTrayHighlight.TabIndex = 2;
|
|
||||||
this.checkTrayHighlight.Text = "Enable Highlight";
|
|
||||||
this.toolTip.SetToolTip(this.checkTrayHighlight, "Highlights the tray icon if there are new tweets.\r\nOnly works for columns with popup or audio notifications.\r\nThe icon resets when the main window is restored.");
|
|
||||||
this.checkTrayHighlight.UseVisualStyleBackColor = true;
|
|
||||||
//
|
|
||||||
// checkSpellCheck
|
// checkSpellCheck
|
||||||
//
|
//
|
||||||
this.checkSpellCheck.AutoSize = true;
|
this.checkSpellCheck.AutoSize = true;
|
||||||
this.checkSpellCheck.Location = new System.Drawing.Point(6, 74);
|
this.checkSpellCheck.Location = new System.Drawing.Point(6, 120);
|
||||||
this.checkSpellCheck.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
|
this.checkSpellCheck.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
|
||||||
this.checkSpellCheck.Name = "checkSpellCheck";
|
this.checkSpellCheck.Name = "checkSpellCheck";
|
||||||
this.checkSpellCheck.Size = new System.Drawing.Size(119, 17);
|
this.checkSpellCheck.Size = new System.Drawing.Size(119, 17);
|
||||||
this.checkSpellCheck.TabIndex = 3;
|
this.checkSpellCheck.TabIndex = 5;
|
||||||
this.checkSpellCheck.Text = "Enable Spell Check";
|
this.checkSpellCheck.Text = "Enable Spell Check";
|
||||||
this.toolTip.SetToolTip(this.checkSpellCheck, "Underlines words that are spelled incorrectly.");
|
this.toolTip.SetToolTip(this.checkSpellCheck, "Underlines words that are spelled incorrectly.");
|
||||||
this.checkSpellCheck.UseVisualStyleBackColor = true;
|
this.checkSpellCheck.UseVisualStyleBackColor = true;
|
||||||
@@ -123,11 +96,11 @@
|
|||||||
// 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, 169);
|
||||||
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);
|
||||||
this.labelZoomValue.TabIndex = 6;
|
this.labelZoomValue.TabIndex = 8;
|
||||||
this.labelZoomValue.Text = "100%";
|
this.labelZoomValue.Text = "100%";
|
||||||
this.labelZoomValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
|
this.labelZoomValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
|
||||||
this.toolTip.SetToolTip(this.labelZoomValue, "Changes the zoom level.\r\nAlso affects notifications and screenshots.");
|
this.toolTip.SetToolTip(this.labelZoomValue, "Changes the zoom level.\r\nAlso affects notifications and screenshots.");
|
||||||
@@ -144,39 +117,53 @@
|
|||||||
this.toolTip.SetToolTip(this.checkSwitchAccountSelectors, "When (re)tweeting, click to select a single account or hold Shift to\r\nselect multiple accounts, instead of TweetDeck\'s default behavior.");
|
this.toolTip.SetToolTip(this.checkSwitchAccountSelectors, "When (re)tweeting, click to select a single account or hold Shift to\r\nselect multiple accounts, instead of TweetDeck\'s default behavior.");
|
||||||
this.checkSwitchAccountSelectors.UseVisualStyleBackColor = true;
|
this.checkSwitchAccountSelectors.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
// labelTrayIcon
|
// checkBestImageQuality
|
||||||
//
|
//
|
||||||
this.labelTrayIcon.AutoSize = true;
|
this.checkBestImageQuality.AutoSize = true;
|
||||||
this.labelTrayIcon.Location = new System.Drawing.Point(3, 38);
|
this.checkBestImageQuality.Location = new System.Drawing.Point(6, 74);
|
||||||
this.labelTrayIcon.Margin = new System.Windows.Forms.Padding(3, 9, 3, 0);
|
this.checkBestImageQuality.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
|
||||||
this.labelTrayIcon.Name = "labelTrayIcon";
|
this.checkBestImageQuality.Name = "checkBestImageQuality";
|
||||||
this.labelTrayIcon.Size = new System.Drawing.Size(52, 13);
|
this.checkBestImageQuality.Size = new System.Drawing.Size(114, 17);
|
||||||
this.labelTrayIcon.TabIndex = 1;
|
this.checkBestImageQuality.TabIndex = 3;
|
||||||
this.labelTrayIcon.Text = "Tray Icon";
|
this.checkBestImageQuality.Text = "Best Image Quality";
|
||||||
|
this.toolTip.SetToolTip(this.checkBestImageQuality, "When right-clicking a tweet image, the context menu options\r\nwill use links to the original image size (:orig in the URL).");
|
||||||
|
this.checkBestImageQuality.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
|
// checkOpenSearchInFirstColumn
|
||||||
|
//
|
||||||
|
this.checkOpenSearchInFirstColumn.AutoSize = true;
|
||||||
|
this.checkOpenSearchInFirstColumn.Location = new System.Drawing.Point(6, 51);
|
||||||
|
this.checkOpenSearchInFirstColumn.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
|
||||||
|
this.checkOpenSearchInFirstColumn.Name = "checkOpenSearchInFirstColumn";
|
||||||
|
this.checkOpenSearchInFirstColumn.Size = new System.Drawing.Size(219, 17);
|
||||||
|
this.checkOpenSearchInFirstColumn.TabIndex = 2;
|
||||||
|
this.checkOpenSearchInFirstColumn.Text = "Add Search Columns Before First Column";
|
||||||
|
this.toolTip.SetToolTip(this.checkOpenSearchInFirstColumn, "By default, TweetDeck adds Search columns at the end.\r\nThis option makes them appear before the first column instead.");
|
||||||
|
this.checkOpenSearchInFirstColumn.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
// trackBarZoom
|
// trackBarZoom
|
||||||
//
|
//
|
||||||
this.trackBarZoom.AutoSize = false;
|
this.trackBarZoom.AutoSize = false;
|
||||||
this.trackBarZoom.BackColor = System.Drawing.SystemColors.Control;
|
this.trackBarZoom.BackColor = System.Drawing.SystemColors.Control;
|
||||||
this.trackBarZoom.LargeChange = 25;
|
this.trackBarZoom.LargeChange = 25;
|
||||||
this.trackBarZoom.Location = new System.Drawing.Point(3, 122);
|
this.trackBarZoom.Location = new System.Drawing.Point(3, 168);
|
||||||
this.trackBarZoom.Maximum = 200;
|
this.trackBarZoom.Maximum = 200;
|
||||||
this.trackBarZoom.Minimum = 50;
|
this.trackBarZoom.Minimum = 50;
|
||||||
this.trackBarZoom.Name = "trackBarZoom";
|
this.trackBarZoom.Name = "trackBarZoom";
|
||||||
this.trackBarZoom.Size = new System.Drawing.Size(148, 30);
|
this.trackBarZoom.Size = new System.Drawing.Size(148, 30);
|
||||||
this.trackBarZoom.SmallChange = 5;
|
this.trackBarZoom.SmallChange = 5;
|
||||||
this.trackBarZoom.TabIndex = 5;
|
this.trackBarZoom.TabIndex = 7;
|
||||||
this.trackBarZoom.TickFrequency = 25;
|
this.trackBarZoom.TickFrequency = 25;
|
||||||
this.trackBarZoom.Value = 100;
|
this.trackBarZoom.Value = 100;
|
||||||
//
|
//
|
||||||
// labelZoom
|
// labelZoom
|
||||||
//
|
//
|
||||||
this.labelZoom.AutoSize = true;
|
this.labelZoom.AutoSize = true;
|
||||||
this.labelZoom.Location = new System.Drawing.Point(3, 106);
|
this.labelZoom.Location = new System.Drawing.Point(3, 152);
|
||||||
this.labelZoom.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
|
this.labelZoom.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
|
||||||
this.labelZoom.Name = "labelZoom";
|
this.labelZoom.Name = "labelZoom";
|
||||||
this.labelZoom.Size = new System.Drawing.Size(34, 13);
|
this.labelZoom.Size = new System.Drawing.Size(34, 13);
|
||||||
this.labelZoom.TabIndex = 4;
|
this.labelZoom.TabIndex = 6;
|
||||||
this.labelZoom.Text = "Zoom";
|
this.labelZoom.Text = "Zoom";
|
||||||
//
|
//
|
||||||
// zoomUpdateTimer
|
// zoomUpdateTimer
|
||||||
@@ -199,94 +186,69 @@
|
|||||||
//
|
//
|
||||||
this.panelUI.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
this.panelUI.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.panelUI.Controls.Add(this.checkAnimatedAvatars);
|
||||||
|
this.panelUI.Controls.Add(this.checkOpenSearchInFirstColumn);
|
||||||
this.panelUI.Controls.Add(this.checkBestImageQuality);
|
this.panelUI.Controls.Add(this.checkBestImageQuality);
|
||||||
this.panelUI.Controls.Add(this.checkExpandLinks);
|
this.panelUI.Controls.Add(this.checkExpandLinks);
|
||||||
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, 205);
|
||||||
this.panelUI.TabIndex = 1;
|
this.panelUI.TabIndex = 1;
|
||||||
//
|
//
|
||||||
// labelTray
|
|
||||||
//
|
|
||||||
this.labelTray.AutoSize = true;
|
|
||||||
this.labelTray.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
|
||||||
this.labelTray.Location = new System.Drawing.Point(6, 212);
|
|
||||||
this.labelTray.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
|
|
||||||
this.labelTray.Name = "labelTray";
|
|
||||||
this.labelTray.Size = new System.Drawing.Size(96, 20);
|
|
||||||
this.labelTray.TabIndex = 2;
|
|
||||||
this.labelTray.Text = "System Tray";
|
|
||||||
//
|
|
||||||
// panelUpdates
|
// panelUpdates
|
||||||
//
|
//
|
||||||
this.panelUpdates.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
this.panelUpdates.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.panelUpdates.Controls.Add(this.checkUpdateNotifications);
|
this.panelUpdates.Controls.Add(this.checkUpdateNotifications);
|
||||||
this.panelUpdates.Controls.Add(this.btnCheckUpdates);
|
this.panelUpdates.Controls.Add(this.btnCheckUpdates);
|
||||||
this.panelUpdates.Location = new System.Drawing.Point(9, 358);
|
this.panelUpdates.Location = new System.Drawing.Point(8, 283);
|
||||||
this.panelUpdates.Name = "panelUpdates";
|
this.panelUpdates.Name = "panelUpdates";
|
||||||
this.panelUpdates.Size = new System.Drawing.Size(322, 55);
|
this.panelUpdates.Size = new System.Drawing.Size(322, 55);
|
||||||
this.panelUpdates.TabIndex = 5;
|
this.panelUpdates.TabIndex = 3;
|
||||||
//
|
|
||||||
// panelTray
|
|
||||||
//
|
|
||||||
this.panelTray.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
|
||||||
| System.Windows.Forms.AnchorStyles.Right)));
|
|
||||||
this.panelTray.Controls.Add(this.checkTrayHighlight);
|
|
||||||
this.panelTray.Controls.Add(this.comboBoxTrayType);
|
|
||||||
this.panelTray.Controls.Add(this.labelTrayIcon);
|
|
||||||
this.panelTray.Location = new System.Drawing.Point(9, 235);
|
|
||||||
this.panelTray.Name = "panelTray";
|
|
||||||
this.panelTray.Size = new System.Drawing.Size(322, 76);
|
|
||||||
this.panelTray.TabIndex = 3;
|
|
||||||
//
|
//
|
||||||
// labelUpdates
|
// labelUpdates
|
||||||
//
|
//
|
||||||
this.labelUpdates.AutoSize = true;
|
this.labelUpdates.AutoSize = true;
|
||||||
this.labelUpdates.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
this.labelUpdates.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
||||||
this.labelUpdates.Location = new System.Drawing.Point(6, 335);
|
this.labelUpdates.Location = new System.Drawing.Point(5, 260);
|
||||||
this.labelUpdates.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
|
this.labelUpdates.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
|
||||||
this.labelUpdates.Name = "labelUpdates";
|
this.labelUpdates.Name = "labelUpdates";
|
||||||
this.labelUpdates.Size = new System.Drawing.Size(70, 20);
|
this.labelUpdates.Size = new System.Drawing.Size(70, 20);
|
||||||
this.labelUpdates.TabIndex = 4;
|
this.labelUpdates.TabIndex = 2;
|
||||||
this.labelUpdates.Text = "Updates";
|
this.labelUpdates.Text = "Updates";
|
||||||
//
|
//
|
||||||
// checkBestImageQuality
|
// checkAnimatedAvatars
|
||||||
//
|
//
|
||||||
this.checkBestImageQuality.AutoSize = true;
|
this.checkAnimatedAvatars.AutoSize = true;
|
||||||
this.checkBestImageQuality.Location = new System.Drawing.Point(6, 51);
|
this.checkAnimatedAvatars.Location = new System.Drawing.Point(6, 97);
|
||||||
this.checkBestImageQuality.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
|
this.checkAnimatedAvatars.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
|
||||||
this.checkBestImageQuality.Name = "checkBestImageQuality";
|
this.checkAnimatedAvatars.Name = "checkAnimatedAvatars";
|
||||||
this.checkBestImageQuality.Size = new System.Drawing.Size(114, 17);
|
this.checkAnimatedAvatars.Size = new System.Drawing.Size(145, 17);
|
||||||
this.checkBestImageQuality.TabIndex = 2;
|
this.checkAnimatedAvatars.TabIndex = 4;
|
||||||
this.checkBestImageQuality.Text = "Best Image Quality";
|
this.checkAnimatedAvatars.Text = "Enable Animated Avatars";
|
||||||
this.toolTip.SetToolTip(this.checkBestImageQuality, "When right-clicking a tweet image, the context menu options\r\nwill use links to the original image size (:orig in the URL).");
|
this.toolTip.SetToolTip(this.checkAnimatedAvatars, "Some old Twitter avatars could be uploaded as animated GIFs.");
|
||||||
this.checkBestImageQuality.UseVisualStyleBackColor = true;
|
this.checkAnimatedAvatars.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
// TabSettingsGeneral
|
// TabSettingsGeneral
|
||||||
//
|
//
|
||||||
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.Controls.Add(this.labelUpdates);
|
this.Controls.Add(this.labelUpdates);
|
||||||
this.Controls.Add(this.panelTray);
|
|
||||||
this.Controls.Add(this.panelUpdates);
|
this.Controls.Add(this.panelUpdates);
|
||||||
this.Controls.Add(this.labelTray);
|
|
||||||
this.Controls.Add(this.panelUI);
|
this.Controls.Add(this.panelUI);
|
||||||
this.Controls.Add(this.labelUI);
|
this.Controls.Add(this.labelUI);
|
||||||
this.Name = "TabSettingsGeneral";
|
this.Name = "TabSettingsGeneral";
|
||||||
this.Size = new System.Drawing.Size(340, 422);
|
this.Size = new System.Drawing.Size(340, 348);
|
||||||
((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).EndInit();
|
((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).EndInit();
|
||||||
this.panelUI.ResumeLayout(false);
|
this.panelUI.ResumeLayout(false);
|
||||||
this.panelUI.PerformLayout();
|
this.panelUI.PerformLayout();
|
||||||
this.panelUpdates.ResumeLayout(false);
|
this.panelUpdates.ResumeLayout(false);
|
||||||
this.panelUpdates.PerformLayout();
|
this.panelUpdates.PerformLayout();
|
||||||
this.panelTray.ResumeLayout(false);
|
|
||||||
this.panelTray.PerformLayout();
|
|
||||||
this.ResumeLayout(false);
|
this.ResumeLayout(false);
|
||||||
this.PerformLayout();
|
this.PerformLayout();
|
||||||
|
|
||||||
@@ -295,10 +257,7 @@
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private System.Windows.Forms.CheckBox checkExpandLinks;
|
private System.Windows.Forms.CheckBox checkExpandLinks;
|
||||||
private System.Windows.Forms.ComboBox comboBoxTrayType;
|
|
||||||
private System.Windows.Forms.ToolTip toolTip;
|
private System.Windows.Forms.ToolTip toolTip;
|
||||||
private System.Windows.Forms.Label labelTrayIcon;
|
|
||||||
private System.Windows.Forms.CheckBox checkTrayHighlight;
|
|
||||||
private System.Windows.Forms.CheckBox checkSpellCheck;
|
private System.Windows.Forms.CheckBox checkSpellCheck;
|
||||||
private System.Windows.Forms.CheckBox checkUpdateNotifications;
|
private System.Windows.Forms.CheckBox checkUpdateNotifications;
|
||||||
private System.Windows.Forms.Button btnCheckUpdates;
|
private System.Windows.Forms.Button btnCheckUpdates;
|
||||||
@@ -309,10 +268,10 @@
|
|||||||
private System.Windows.Forms.CheckBox checkSwitchAccountSelectors;
|
private System.Windows.Forms.CheckBox checkSwitchAccountSelectors;
|
||||||
private System.Windows.Forms.Label labelUI;
|
private System.Windows.Forms.Label labelUI;
|
||||||
private System.Windows.Forms.Panel panelUI;
|
private System.Windows.Forms.Panel panelUI;
|
||||||
private System.Windows.Forms.Label labelTray;
|
|
||||||
private System.Windows.Forms.Panel panelUpdates;
|
private System.Windows.Forms.Panel panelUpdates;
|
||||||
private System.Windows.Forms.Panel panelTray;
|
|
||||||
private System.Windows.Forms.Label labelUpdates;
|
private System.Windows.Forms.Label labelUpdates;
|
||||||
private System.Windows.Forms.CheckBox checkBestImageQuality;
|
private System.Windows.Forms.CheckBox checkBestImageQuality;
|
||||||
|
private System.Windows.Forms.CheckBox checkOpenSearchInFirstColumn;
|
||||||
|
private System.Windows.Forms.CheckBox checkAnimatedAvatars;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,36 +1,33 @@
|
|||||||
using System;
|
using System;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
|
using TweetDuck.Core.Handling.General;
|
||||||
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 FormBrowser browser;
|
||||||
private readonly UpdateHandler updates;
|
private readonly UpdateHandler updates;
|
||||||
private int updateCheckEventId = -1;
|
private int updateCheckEventId = -1;
|
||||||
|
|
||||||
public TabSettingsGeneral(UpdateHandler updates){
|
public TabSettingsGeneral(FormBrowser browser, UpdateHandler updates){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
this.browser = browser;
|
||||||
|
|
||||||
this.updates = updates;
|
this.updates = updates;
|
||||||
this.updates.CheckFinished += updates_CheckFinished;
|
this.updates.CheckFinished += updates_CheckFinished;
|
||||||
Disposed += (sender, args) => this.updates.CheckFinished -= updates_CheckFinished;
|
Disposed += (sender, args) => this.updates.CheckFinished -= updates_CheckFinished;
|
||||||
|
|
||||||
comboBoxTrayType.Items.Add("Disabled");
|
|
||||||
comboBoxTrayType.Items.Add("Display Icon Only");
|
|
||||||
comboBoxTrayType.Items.Add("Minimize to Tray");
|
|
||||||
comboBoxTrayType.Items.Add("Close to Tray");
|
|
||||||
comboBoxTrayType.Items.Add("Combined");
|
|
||||||
comboBoxTrayType.SelectedIndex = Math.Min(Math.Max((int)Config.TrayBehavior, 0), comboBoxTrayType.Items.Count-1);
|
|
||||||
|
|
||||||
toolTip.SetToolTip(trackBarZoom, toolTip.GetToolTip(labelZoomValue));
|
toolTip.SetToolTip(trackBarZoom, toolTip.GetToolTip(labelZoomValue));
|
||||||
trackBarZoom.SetValueSafe(Config.ZoomLevel);
|
trackBarZoom.SetValueSafe(Config.ZoomLevel);
|
||||||
labelZoomValue.Text = trackBarZoom.Value+"%";
|
labelZoomValue.Text = trackBarZoom.Value+"%";
|
||||||
|
|
||||||
checkExpandLinks.Checked = Config.ExpandLinksOnHover;
|
checkExpandLinks.Checked = Config.ExpandLinksOnHover;
|
||||||
checkSwitchAccountSelectors.Checked = Config.SwitchAccountSelectors;
|
checkSwitchAccountSelectors.Checked = Config.SwitchAccountSelectors;
|
||||||
|
checkOpenSearchInFirstColumn.Checked = Config.OpenSearchInFirstColumn;
|
||||||
checkBestImageQuality.Checked = Config.BestImageQuality;
|
checkBestImageQuality.Checked = Config.BestImageQuality;
|
||||||
|
checkAnimatedAvatars.Checked = Config.EnableAnimatedImages;
|
||||||
checkSpellCheck.Checked = Config.EnableSpellCheck;
|
checkSpellCheck.Checked = Config.EnableSpellCheck;
|
||||||
checkTrayHighlight.Checked = Config.EnableTrayHighlight;
|
|
||||||
|
|
||||||
checkUpdateNotifications.Checked = Config.EnableUpdateCheck;
|
checkUpdateNotifications.Checked = Config.EnableUpdateCheck;
|
||||||
}
|
}
|
||||||
@@ -38,13 +35,12 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
public override void OnReady(){
|
public override void OnReady(){
|
||||||
checkExpandLinks.CheckedChanged += checkExpandLinks_CheckedChanged;
|
checkExpandLinks.CheckedChanged += checkExpandLinks_CheckedChanged;
|
||||||
checkSwitchAccountSelectors.CheckedChanged += checkSwitchAccountSelectors_CheckedChanged;
|
checkSwitchAccountSelectors.CheckedChanged += checkSwitchAccountSelectors_CheckedChanged;
|
||||||
|
checkOpenSearchInFirstColumn.CheckedChanged += checkOpenSearchInFirstColumn_CheckedChanged;
|
||||||
checkBestImageQuality.CheckedChanged += checkBestImageQuality_CheckedChanged;
|
checkBestImageQuality.CheckedChanged += checkBestImageQuality_CheckedChanged;
|
||||||
|
checkAnimatedAvatars.CheckedChanged += checkAnimatedAvatars_CheckedChanged;
|
||||||
checkSpellCheck.CheckedChanged += checkSpellCheck_CheckedChanged;
|
checkSpellCheck.CheckedChanged += checkSpellCheck_CheckedChanged;
|
||||||
trackBarZoom.ValueChanged += trackBarZoom_ValueChanged;
|
trackBarZoom.ValueChanged += trackBarZoom_ValueChanged;
|
||||||
|
|
||||||
comboBoxTrayType.SelectedIndexChanged += comboBoxTrayType_SelectedIndexChanged;
|
|
||||||
checkTrayHighlight.CheckedChanged += checkTrayHighlight_CheckedChanged;
|
|
||||||
|
|
||||||
checkUpdateNotifications.CheckedChanged += checkUpdateNotifications_CheckedChanged;
|
checkUpdateNotifications.CheckedChanged += checkUpdateNotifications_CheckedChanged;
|
||||||
btnCheckUpdates.Click += btnCheckUpdates_Click;
|
btnCheckUpdates.Click += btnCheckUpdates_Click;
|
||||||
}
|
}
|
||||||
@@ -61,13 +57,22 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
Config.SwitchAccountSelectors = checkSwitchAccountSelectors.Checked;
|
Config.SwitchAccountSelectors = checkSwitchAccountSelectors.Checked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkOpenSearchInFirstColumn_CheckedChanged(object sender, EventArgs e){
|
||||||
|
Config.OpenSearchInFirstColumn = checkOpenSearchInFirstColumn.Checked;
|
||||||
|
}
|
||||||
|
|
||||||
private void checkBestImageQuality_CheckedChanged(object sender, EventArgs e){
|
private void checkBestImageQuality_CheckedChanged(object sender, EventArgs e){
|
||||||
Config.BestImageQuality = checkBestImageQuality.Checked;
|
Config.BestImageQuality = checkBestImageQuality.Checked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkAnimatedAvatars_CheckedChanged(object sender, EventArgs e){
|
||||||
|
Config.EnableAnimatedImages = checkAnimatedAvatars.Checked;
|
||||||
|
BrowserProcessHandler.UpdatePrefs().ContinueWith(task => browser.ReloadColumns());
|
||||||
|
}
|
||||||
|
|
||||||
private void checkSpellCheck_CheckedChanged(object sender, EventArgs e){
|
private void checkSpellCheck_CheckedChanged(object sender, EventArgs e){
|
||||||
Config.EnableSpellCheck = checkSpellCheck.Checked;
|
Config.EnableSpellCheck = checkSpellCheck.Checked;
|
||||||
PromptRestart();
|
BrowserProcessHandler.UpdatePrefs();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void trackBarZoom_ValueChanged(object sender, EventArgs e){
|
private void trackBarZoom_ValueChanged(object sender, EventArgs e){
|
||||||
@@ -78,36 +83,23 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void comboBoxTrayType_SelectedIndexChanged(object sender, EventArgs e){
|
|
||||||
Config.TrayBehavior = (TrayIcon.Behavior)comboBoxTrayType.SelectedIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkTrayHighlight_CheckedChanged(object sender, EventArgs e){
|
|
||||||
Config.EnableTrayHighlight = checkTrayHighlight.Checked;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkUpdateNotifications_CheckedChanged(object sender, EventArgs e){
|
private void checkUpdateNotifications_CheckedChanged(object sender, EventArgs e){
|
||||||
Config.EnableUpdateCheck = checkUpdateNotifications.Checked;
|
Config.EnableUpdateCheck = checkUpdateNotifications.Checked;
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
@@ -2,21 +2,22 @@
|
|||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Notification;
|
using TweetDuck.Core.Notification;
|
||||||
|
using TweetDuck.Core.Notification.Example;
|
||||||
|
|
||||||
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 FormNotificationExample notification;
|
||||||
|
|
||||||
public TabSettingsNotifications(FormNotificationMain notification){
|
public TabSettingsNotifications(FormNotificationExample notification){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
this.notification = notification;
|
this.notification = notification;
|
||||||
|
|
||||||
this.notification.Initialized += (sender, args) => {
|
this.notification.Initialized += (sender, args) => {
|
||||||
this.InvokeAsyncSafe(() => {
|
this.InvokeAsyncSafe(() => {
|
||||||
this.notification.ShowNotificationForSettings(true);
|
this.notification.ShowExampleNotification(true);
|
||||||
this.notification.Move += notification_Move;
|
this.notification.Move += notification_Move;
|
||||||
this.notification.ResizeEnd += notification_ResizeEnd;
|
this.notification.ResizeEnd += notification_ResizeEnd;
|
||||||
});
|
});
|
||||||
@@ -73,9 +74,6 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
trackBarEdgeDistance.SetValueSafe(Config.NotificationEdgeDistance);
|
trackBarEdgeDistance.SetValueSafe(Config.NotificationEdgeDistance);
|
||||||
labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value+" px";
|
labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value+" px";
|
||||||
|
|
||||||
this.notification.CanMoveWindow = () => radioLocCustom.Checked;
|
|
||||||
this.notification.CanResizeWindow = radioSizeCustom.Checked;
|
|
||||||
|
|
||||||
Disposed += (sender, args) => this.notification.Dispose();
|
Disposed += (sender, args) => this.notification.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,10 +108,10 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
|
|
||||||
private void TabSettingsNotifications_ParentChanged(object sender, EventArgs e){
|
private void TabSettingsNotifications_ParentChanged(object sender, EventArgs e){
|
||||||
if (Parent == null){
|
if (Parent == null){
|
||||||
notification.HideNotification(false);
|
notification.HideNotification();
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
notification.ShowNotificationForSettings(true);
|
notification.ShowExampleNotification(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,7 +129,7 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
private void notification_ResizeEnd(object sender, EventArgs e){
|
private void notification_ResizeEnd(object sender, EventArgs e){
|
||||||
if (radioSizeCustom.Checked){
|
if (radioSizeCustom.Checked){
|
||||||
Config.CustomNotificationSize = notification.BrowserSize;
|
Config.CustomNotificationSize = notification.BrowserSize;
|
||||||
notification.ShowNotificationForSettings(false);
|
notification.ShowExampleNotification(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +140,7 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
else if (radioLocBR.Checked)Config.NotificationPosition = TweetNotification.Position.BottomRight;
|
else if (radioLocBR.Checked)Config.NotificationPosition = TweetNotification.Position.BottomRight;
|
||||||
|
|
||||||
comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = true;
|
comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = true;
|
||||||
notification.ShowNotificationForSettings(false);
|
notification.ShowExampleNotification(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void radioLocCustom_Click(object sender, EventArgs e){
|
private void radioLocCustom_Click(object sender, EventArgs e){
|
||||||
@@ -153,7 +151,7 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
Config.NotificationPosition = TweetNotification.Position.Custom;
|
Config.NotificationPosition = TweetNotification.Position.Custom;
|
||||||
|
|
||||||
comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = false;
|
comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = false;
|
||||||
notification.ShowNotificationForSettings(false);
|
notification.ShowExampleNotification(false);
|
||||||
|
|
||||||
if (notification.IsFullyOutsideView() && FormMessage.Question("Notification is outside view", "The notification seems to be outside of view, would you like to reset its position?", FormMessage.Yes, FormMessage.No)){
|
if (notification.IsFullyOutsideView() && FormMessage.Question("Notification is outside view", "The notification seems to be outside of view, would you like to reset its position?", FormMessage.Yes, FormMessage.No)){
|
||||||
Config.NotificationPosition = TweetNotification.Position.TopRight;
|
Config.NotificationPosition = TweetNotification.Position.TopRight;
|
||||||
@@ -167,10 +165,11 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void radioSize_CheckedChanged(object sender, EventArgs e){
|
private void radioSize_CheckedChanged(object sender, EventArgs e){
|
||||||
if (radioSizeAuto.Checked)Config.NotificationSize = TweetNotification.Size.Auto;
|
if (radioSizeAuto.Checked){
|
||||||
|
Config.NotificationSize = TweetNotification.Size.Auto;
|
||||||
|
}
|
||||||
|
|
||||||
notification.ShowNotificationForSettings(false);
|
notification.ShowExampleNotification(false);
|
||||||
notification.CanResizeWindow = false; // must be after ShowNotificationForSettings
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void radioSizeCustom_Click(object sender, EventArgs e){
|
private void radioSizeCustom_Click(object sender, EventArgs e){
|
||||||
@@ -179,9 +178,7 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
}
|
}
|
||||||
|
|
||||||
Config.NotificationSize = TweetNotification.Size.Custom;
|
Config.NotificationSize = TweetNotification.Size.Custom;
|
||||||
|
notification.ShowExampleNotification(false);
|
||||||
notification.CanResizeWindow = true;
|
|
||||||
notification.ShowNotificationForSettings(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void trackBarDuration_ValueChanged(object sender, EventArgs e){
|
private void trackBarDuration_ValueChanged(object sender, EventArgs e){
|
||||||
@@ -206,18 +203,18 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
|
|
||||||
private void checkColumnName_CheckedChanged(object sender, EventArgs e){
|
private void checkColumnName_CheckedChanged(object sender, EventArgs e){
|
||||||
Config.DisplayNotificationColumn = checkColumnName.Checked;
|
Config.DisplayNotificationColumn = checkColumnName.Checked;
|
||||||
notification.ShowNotificationForSettings(false);
|
notification.ShowExampleNotification(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkNotificationTimer_CheckedChanged(object sender, EventArgs e){
|
private void checkNotificationTimer_CheckedChanged(object sender, EventArgs e){
|
||||||
Config.DisplayNotificationTimer = checkNotificationTimer.Checked;
|
Config.DisplayNotificationTimer = checkNotificationTimer.Checked;
|
||||||
checkTimerCountDown.Enabled = checkNotificationTimer.Checked;
|
checkTimerCountDown.Enabled = checkNotificationTimer.Checked;
|
||||||
notification.ShowNotificationForSettings(true);
|
notification.ShowExampleNotification(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkTimerCountDown_CheckedChanged(object sender, EventArgs e){
|
private void checkTimerCountDown_CheckedChanged(object sender, EventArgs e){
|
||||||
Config.NotificationTimerCountDown = checkTimerCountDown.Checked;
|
Config.NotificationTimerCountDown = checkTimerCountDown.Checked;
|
||||||
notification.ShowNotificationForSettings(true);
|
notification.ShowExampleNotification(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkMediaPreviews_CheckedChanged(object sender, EventArgs e){
|
private void checkMediaPreviews_CheckedChanged(object sender, EventArgs e){
|
||||||
@@ -245,17 +242,17 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
|
|
||||||
private void comboBoxDisplay_SelectedValueChanged(object sender, EventArgs e){
|
private void comboBoxDisplay_SelectedValueChanged(object sender, EventArgs e){
|
||||||
Config.NotificationDisplay = comboBoxDisplay.SelectedIndex;
|
Config.NotificationDisplay = comboBoxDisplay.SelectedIndex;
|
||||||
notification.ShowNotificationForSettings(false);
|
notification.ShowExampleNotification(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void trackBarEdgeDistance_ValueChanged(object sender, EventArgs e){
|
private void trackBarEdgeDistance_ValueChanged(object sender, EventArgs e){
|
||||||
labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value+" px";
|
labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value+" px";
|
||||||
Config.NotificationEdgeDistance = trackBarEdgeDistance.Value;
|
Config.NotificationEdgeDistance = trackBarEdgeDistance.Value;
|
||||||
notification.ShowNotificationForSettings(false);
|
notification.ShowExampleNotification(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void durationUpdateTimer_Tick(object sender, EventArgs e){
|
private void durationUpdateTimer_Tick(object sender, EventArgs e){
|
||||||
notification.ShowNotificationForSettings(true);
|
notification.ShowExampleNotification(true);
|
||||||
durationUpdateTimer.Stop();
|
durationUpdateTimer.Stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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+"%";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
116
Core/Other/Settings/TabSettingsTray.Designer.cs
generated
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
namespace TweetDuck.Core.Other.Settings {
|
||||||
|
partial class TabSettingsTray {
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up any resources being used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose(bool disposing) {
|
||||||
|
if (disposing && (components != null)) {
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Component Designer generated code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent() {
|
||||||
|
this.components = new System.ComponentModel.Container();
|
||||||
|
this.panelTray = new System.Windows.Forms.Panel();
|
||||||
|
this.checkTrayHighlight = new System.Windows.Forms.CheckBox();
|
||||||
|
this.comboBoxTrayType = new System.Windows.Forms.ComboBox();
|
||||||
|
this.labelTrayIcon = new System.Windows.Forms.Label();
|
||||||
|
this.labelTray = new System.Windows.Forms.Label();
|
||||||
|
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
||||||
|
this.panelTray.SuspendLayout();
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// panelTray
|
||||||
|
//
|
||||||
|
this.panelTray.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.panelTray.Controls.Add(this.checkTrayHighlight);
|
||||||
|
this.panelTray.Controls.Add(this.comboBoxTrayType);
|
||||||
|
this.panelTray.Controls.Add(this.labelTrayIcon);
|
||||||
|
this.panelTray.Location = new System.Drawing.Point(9, 31);
|
||||||
|
this.panelTray.Name = "panelTray";
|
||||||
|
this.panelTray.Size = new System.Drawing.Size(322, 76);
|
||||||
|
this.panelTray.TabIndex = 1;
|
||||||
|
//
|
||||||
|
// checkTrayHighlight
|
||||||
|
//
|
||||||
|
this.checkTrayHighlight.AutoSize = true;
|
||||||
|
this.checkTrayHighlight.Location = new System.Drawing.Point(6, 56);
|
||||||
|
this.checkTrayHighlight.Margin = new System.Windows.Forms.Padding(6, 5, 3, 3);
|
||||||
|
this.checkTrayHighlight.Name = "checkTrayHighlight";
|
||||||
|
this.checkTrayHighlight.Size = new System.Drawing.Size(103, 17);
|
||||||
|
this.checkTrayHighlight.TabIndex = 2;
|
||||||
|
this.checkTrayHighlight.Text = "Enable Highlight";
|
||||||
|
this.toolTip.SetToolTip(this.checkTrayHighlight, "Highlights the tray icon if there are new tweets.\r\nOnly works for columns with popup or audio notifications.\r\nThe icon resets when the main window is restored.");
|
||||||
|
this.checkTrayHighlight.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
|
// comboBoxTrayType
|
||||||
|
//
|
||||||
|
this.comboBoxTrayType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||||
|
this.comboBoxTrayType.FormattingEnabled = true;
|
||||||
|
this.comboBoxTrayType.Location = new System.Drawing.Point(5, 5);
|
||||||
|
this.comboBoxTrayType.Margin = new System.Windows.Forms.Padding(5, 5, 3, 3);
|
||||||
|
this.comboBoxTrayType.Name = "comboBoxTrayType";
|
||||||
|
this.comboBoxTrayType.Size = new System.Drawing.Size(144, 21);
|
||||||
|
this.comboBoxTrayType.TabIndex = 0;
|
||||||
|
this.toolTip.SetToolTip(this.comboBoxTrayType, "Changes behavior of the Tray icon.\r\nRight-click the icon for an action menu.");
|
||||||
|
//
|
||||||
|
// labelTrayIcon
|
||||||
|
//
|
||||||
|
this.labelTrayIcon.AutoSize = true;
|
||||||
|
this.labelTrayIcon.Location = new System.Drawing.Point(3, 38);
|
||||||
|
this.labelTrayIcon.Margin = new System.Windows.Forms.Padding(3, 9, 3, 0);
|
||||||
|
this.labelTrayIcon.Name = "labelTrayIcon";
|
||||||
|
this.labelTrayIcon.Size = new System.Drawing.Size(52, 13);
|
||||||
|
this.labelTrayIcon.TabIndex = 1;
|
||||||
|
this.labelTrayIcon.Text = "Tray Icon";
|
||||||
|
//
|
||||||
|
// labelTray
|
||||||
|
//
|
||||||
|
this.labelTray.AutoSize = true;
|
||||||
|
this.labelTray.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
||||||
|
this.labelTray.Location = new System.Drawing.Point(6, 8);
|
||||||
|
this.labelTray.Margin = new System.Windows.Forms.Padding(0, 2, 0, 0);
|
||||||
|
this.labelTray.Name = "labelTray";
|
||||||
|
this.labelTray.Size = new System.Drawing.Size(96, 20);
|
||||||
|
this.labelTray.TabIndex = 0;
|
||||||
|
this.labelTray.Text = "System Tray";
|
||||||
|
//
|
||||||
|
// TabSettingsTray
|
||||||
|
//
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.Controls.Add(this.panelTray);
|
||||||
|
this.Controls.Add(this.labelTray);
|
||||||
|
this.Name = "TabSettingsTray";
|
||||||
|
this.Size = new System.Drawing.Size(340, 119);
|
||||||
|
this.panelTray.ResumeLayout(false);
|
||||||
|
this.panelTray.PerformLayout();
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
this.PerformLayout();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private System.Windows.Forms.Panel panelTray;
|
||||||
|
private System.Windows.Forms.CheckBox checkTrayHighlight;
|
||||||
|
private System.Windows.Forms.ComboBox comboBoxTrayType;
|
||||||
|
private System.Windows.Forms.Label labelTrayIcon;
|
||||||
|
private System.Windows.Forms.Label labelTray;
|
||||||
|
private System.Windows.Forms.ToolTip toolTip;
|
||||||
|
}
|
||||||
|
}
|
33
Core/Other/Settings/TabSettingsTray.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace TweetDuck.Core.Other.Settings{
|
||||||
|
sealed partial class TabSettingsTray : BaseTabSettings{
|
||||||
|
public TabSettingsTray(){
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
comboBoxTrayType.Items.Add("Disabled");
|
||||||
|
comboBoxTrayType.Items.Add("Display Icon Only");
|
||||||
|
comboBoxTrayType.Items.Add("Minimize to Tray");
|
||||||
|
comboBoxTrayType.Items.Add("Close to Tray");
|
||||||
|
comboBoxTrayType.Items.Add("Combined");
|
||||||
|
comboBoxTrayType.SelectedIndex = Math.Min(Math.Max((int)Config.TrayBehavior, 0), comboBoxTrayType.Items.Count-1);
|
||||||
|
|
||||||
|
checkTrayHighlight.Enabled = Config.TrayBehavior.ShouldDisplayIcon();
|
||||||
|
checkTrayHighlight.Checked = Config.EnableTrayHighlight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnReady(){
|
||||||
|
comboBoxTrayType.SelectedIndexChanged += comboBoxTrayType_SelectedIndexChanged;
|
||||||
|
checkTrayHighlight.CheckedChanged += checkTrayHighlight_CheckedChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void comboBoxTrayType_SelectedIndexChanged(object sender, EventArgs e){
|
||||||
|
Config.TrayBehavior = (TrayIcon.Behavior)comboBoxTrayType.SelectedIndex;
|
||||||
|
checkTrayHighlight.Enabled = Config.TrayBehavior.ShouldDisplayIcon();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkTrayHighlight_CheckedChanged(object sender, EventArgs e){
|
||||||
|
Config.EnableTrayHighlight = checkTrayHighlight.Checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
namespace TweetDuck.Core {
|
namespace TweetDuck.Core.Other {
|
||||||
partial class TrayIcon {
|
partial class TrayIcon {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Required designer variable.
|
/// Required designer variable.
|
@@ -2,8 +2,8 @@
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
namespace TweetDuck.Core{
|
namespace TweetDuck.Core.Other{
|
||||||
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
|
||||||
}
|
}
|
||||||
@@ -12,7 +12,9 @@ namespace TweetDuck.Core{
|
|||||||
public event EventHandler ClickClose;
|
public event EventHandler ClickClose;
|
||||||
|
|
||||||
public bool Visible{
|
public bool Visible{
|
||||||
get => notifyIcon.Visible;
|
get{
|
||||||
|
return notifyIcon.Visible;
|
||||||
|
}
|
||||||
|
|
||||||
set{
|
set{
|
||||||
if (value){
|
if (value){
|
242
Core/TweetDeckBrowser.cs
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using CefSharp;
|
||||||
|
using CefSharp.WinForms;
|
||||||
|
using TweetDuck.Core.Bridge;
|
||||||
|
using TweetDuck.Core.Controls;
|
||||||
|
using TweetDuck.Core.Handling;
|
||||||
|
using TweetDuck.Core.Handling.General;
|
||||||
|
using TweetDuck.Core.Other.Management;
|
||||||
|
using TweetDuck.Core.Utils;
|
||||||
|
using TweetDuck.Plugins;
|
||||||
|
using TweetDuck.Plugins.Enums;
|
||||||
|
using TweetDuck.Plugins.Events;
|
||||||
|
using TweetDuck.Resources;
|
||||||
|
using TweetDuck.Updates;
|
||||||
|
|
||||||
|
namespace TweetDuck.Core{
|
||||||
|
sealed class TweetDeckBrowser : IDisposable{
|
||||||
|
public bool Ready { get; private set; }
|
||||||
|
|
||||||
|
public bool Enabled{
|
||||||
|
get => browser.Enabled;
|
||||||
|
set => browser.Enabled = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsTweetDeckWebsite{
|
||||||
|
get{
|
||||||
|
using(IFrame frame = browser.GetBrowser().MainFrame){
|
||||||
|
return TwitterUtils.IsTweetDeckWebsite(frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler PageLoaded;
|
||||||
|
|
||||||
|
private readonly ChromiumWebBrowser browser;
|
||||||
|
private readonly PluginManager plugins;
|
||||||
|
private readonly MemoryUsageTracker memoryUsageTracker;
|
||||||
|
|
||||||
|
public TweetDeckBrowser(FormBrowser owner, PluginManager plugins, TweetDeckBridge bridge){
|
||||||
|
this.browser = new ChromiumWebBrowser(TwitterUtils.TweetDeckURL){
|
||||||
|
DialogHandler = new FileDialogHandler(),
|
||||||
|
DragHandler = new DragHandlerBrowser(),
|
||||||
|
MenuHandler = new ContextMenuBrowser(owner),
|
||||||
|
JsDialogHandler = new JavaScriptDialogHandler(),
|
||||||
|
KeyboardHandler = new KeyboardHandlerBrowser(owner),
|
||||||
|
LifeSpanHandler = new LifeSpanHandler(),
|
||||||
|
RequestHandler = new RequestHandlerBrowser()
|
||||||
|
};
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
this.browser.ConsoleMessage += BrowserUtils.HandleConsoleMessage;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
this.browser.LoadingStateChanged += browser_LoadingStateChanged;
|
||||||
|
this.browser.FrameLoadStart += browser_FrameLoadStart;
|
||||||
|
this.browser.FrameLoadEnd += browser_FrameLoadEnd;
|
||||||
|
this.browser.LoadError += browser_LoadError;
|
||||||
|
|
||||||
|
this.browser.RegisterAsyncJsObject("$TD", bridge);
|
||||||
|
this.browser.RegisterAsyncJsObject("$TDP", plugins.Bridge);
|
||||||
|
|
||||||
|
this.browser.BrowserSettings.BackgroundColor = (uint)TwitterUtils.BackgroundColor.ToArgb();
|
||||||
|
this.browser.Dock = DockStyle.None;
|
||||||
|
this.browser.Location = ControlExtensions.InvisibleLocation;
|
||||||
|
|
||||||
|
owner.Controls.Add(browser);
|
||||||
|
|
||||||
|
this.plugins = plugins;
|
||||||
|
this.plugins.PluginChangedState += plugins_PluginChangedState;
|
||||||
|
|
||||||
|
this.memoryUsageTracker = new MemoryUsageTracker("TDGF_tryRunCleanup");
|
||||||
|
|
||||||
|
Program.UserConfig.MuteToggled += UserConfig_MuteToggled;
|
||||||
|
Program.UserConfig.ZoomLevelChanged += UserConfig_ZoomLevelChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup and management
|
||||||
|
|
||||||
|
private void OnBrowserReady(){
|
||||||
|
if (!Ready){
|
||||||
|
browser.Location = Point.Empty;
|
||||||
|
browser.Dock = DockStyle.Fill;
|
||||||
|
Ready = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Focus(){
|
||||||
|
browser.Focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose(){
|
||||||
|
plugins.PluginChangedState -= plugins_PluginChangedState;
|
||||||
|
|
||||||
|
Program.UserConfig.MuteToggled -= UserConfig_MuteToggled;
|
||||||
|
Program.UserConfig.ZoomLevelChanged -= UserConfig_ZoomLevelChanged;
|
||||||
|
|
||||||
|
memoryUsageTracker.Dispose();
|
||||||
|
browser.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
// event handlers
|
||||||
|
|
||||||
|
private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
|
||||||
|
if (!e.IsLoading){
|
||||||
|
foreach(string word in TwitterUtils.DictionaryWords){
|
||||||
|
browser.AddWordToDictionary(word);
|
||||||
|
}
|
||||||
|
|
||||||
|
browser.BeginInvoke(new Action(OnBrowserReady));
|
||||||
|
browser.LoadingStateChanged -= browser_LoadingStateChanged;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void browser_FrameLoadStart(object sender, FrameLoadStartEventArgs e){
|
||||||
|
if (e.Frame.IsMain){
|
||||||
|
memoryUsageTracker.Stop();
|
||||||
|
|
||||||
|
if (Program.UserConfig.ZoomLevel != 100){
|
||||||
|
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Program.UserConfig.ZoomLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TwitterUtils.IsTwitterWebsite(e.Frame)){
|
||||||
|
ScriptLoader.ExecuteFile(e.Frame, "twitter.js");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
|
||||||
|
if (e.Frame.IsMain && TwitterUtils.IsTweetDeckWebsite(e.Frame)){
|
||||||
|
e.Frame.ExecuteJavaScriptAsync(TwitterUtils.BackgroundColorFix);
|
||||||
|
|
||||||
|
UpdateProperties();
|
||||||
|
TweetDeckBridge.RestoreSessionData(e.Frame);
|
||||||
|
ScriptLoader.ExecuteFile(e.Frame, "code.js");
|
||||||
|
InjectBrowserCSS();
|
||||||
|
ReinjectCustomCSS(Program.UserConfig.CustomBrowserCSS);
|
||||||
|
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Browser);
|
||||||
|
|
||||||
|
TweetDeckBridge.ResetStaticProperties();
|
||||||
|
|
||||||
|
if (Program.SystemConfig.EnableBrowserGCReload){
|
||||||
|
memoryUsageTracker.Start(browser, Program.SystemConfig.BrowserMemoryThreshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Program.UserConfig.FirstRun){
|
||||||
|
ScriptLoader.ExecuteFile(e.Frame, "introduction.js");
|
||||||
|
}
|
||||||
|
|
||||||
|
PageLoaded?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void browser_LoadError(object sender, LoadErrorEventArgs e){
|
||||||
|
if (e.ErrorCode == CefErrorCode.Aborted){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!e.FailedUrl.StartsWith("http://td/", StringComparison.Ordinal)){
|
||||||
|
string errorPage = ScriptLoader.LoadResource("pages/error.html", true);
|
||||||
|
|
||||||
|
if (errorPage != null){
|
||||||
|
browser.LoadHtml(errorPage.Replace("{err}", BrowserUtils.GetErrorName(e.ErrorCode)), "http://td/error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void plugins_PluginChangedState(object sender, PluginChangedStateEventArgs e){
|
||||||
|
browser.ExecuteScriptAsync("TDPF_setPluginState", e.Plugin, e.IsEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UserConfig_MuteToggled(object sender, EventArgs e){
|
||||||
|
UpdateProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UserConfig_ZoomLevelChanged(object sender, EventArgs e){
|
||||||
|
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Program.UserConfig.ZoomLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
// external handling
|
||||||
|
|
||||||
|
public UpdateHandler CreateUpdateHandler(UpdaterSettings settings){
|
||||||
|
return new UpdateHandler(browser, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RefreshMemoryTracker(){
|
||||||
|
if (Program.SystemConfig.EnableBrowserGCReload){
|
||||||
|
memoryUsageTracker.Start(browser, Program.SystemConfig.BrowserMemoryThreshold);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
memoryUsageTracker.Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HideVideoOverlay(bool focus){
|
||||||
|
if (focus){
|
||||||
|
browser.GetBrowser().GetHost().SendFocusEvent(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
browser.ExecuteScriptAsync("$('#td-video-player-overlay').remove()");
|
||||||
|
}
|
||||||
|
|
||||||
|
// javascript calls
|
||||||
|
|
||||||
|
public void ReloadToTweetDeck(){
|
||||||
|
browser.ExecuteScriptAsync($"if(window.TDGF_reload)window.TDGF_reload();else window.location.href='{TwitterUtils.TweetDeckURL}'");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateProperties(){
|
||||||
|
browser.ExecuteScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Environment.Browser));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InjectBrowserCSS(){
|
||||||
|
browser.ExecuteScriptAsync("TDGF_injectBrowserCSS", ScriptLoader.LoadResource("styles/browser.css").TrimEnd());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReinjectCustomCSS(string css){
|
||||||
|
browser.ExecuteScriptAsync("TDGF_reinjectCustomCSS", css?.Replace(Environment.NewLine, " ") ?? string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnMouseClickExtra(IntPtr param){
|
||||||
|
browser.ExecuteScriptAsync("TDGF_onMouseClickExtra", (param.ToInt32() >> 16) & 0xFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowTweetDetail(string columnId, string chirpId, string fallbackUrl){
|
||||||
|
browser.ExecuteScriptAsync("TDGF_showTweetDetail", columnId, chirpId, fallbackUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TriggerTweetScreenshot(){
|
||||||
|
browser.ExecuteScriptAsync("TDGF_triggerScreenshot()");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReloadColumns(){
|
||||||
|
browser.ExecuteScriptAsync("TDGF_reloadColumns()");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplyROT13(){
|
||||||
|
browser.ExecuteScriptAsync("TDGF_applyROT13()");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -8,15 +8,10 @@ namespace TweetDuck.Core.Utils{
|
|||||||
static class BrowserCache{
|
static class BrowserCache{
|
||||||
private static bool ClearOnExit { get; set; }
|
private static bool ClearOnExit { get; set; }
|
||||||
|
|
||||||
private static readonly string CacheFolder = Path.Combine(Program.StoragePath, "Cache");
|
public static readonly string CacheFolder = Path.Combine(Program.StoragePath, "Cache");
|
||||||
|
private static IEnumerable<string> CacheFiles => Directory.EnumerateFiles(CacheFolder);
|
||||||
|
|
||||||
private static IEnumerable<string> CacheFiles{
|
public static void CalculateCacheSize(Action<Task<long>> callbackBytes){
|
||||||
get{
|
|
||||||
return Directory.EnumerateFiles(CacheFolder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void CalculateCacheSize(Action<long> callbackBytes){
|
|
||||||
Task<long> task = new Task<long>(() => {
|
Task<long> task = new Task<long>(() => {
|
||||||
return CacheFiles.Select(file => {
|
return CacheFiles.Select(file => {
|
||||||
try{
|
try{
|
||||||
@@ -27,7 +22,7 @@ namespace TweetDuck.Core.Utils{
|
|||||||
}).Sum();
|
}).Sum();
|
||||||
});
|
});
|
||||||
|
|
||||||
task.ContinueWith(originalTask => callbackBytes(originalTask.Exception == null ? originalTask.Result : -1L), TaskContinuationOptions.ExecuteSynchronously);
|
task.ContinueWith(callbackBytes);
|
||||||
task.Start();
|
task.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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{
|
||||||
@@ -30,7 +31,7 @@ namespace TweetDuck.Core.Utils{
|
|||||||
args["disable-gpu-vsync"] = "1";
|
args["disable-gpu-vsync"] = "1";
|
||||||
}
|
}
|
||||||
|
|
||||||
args["disable-extensions"] = "1";
|
args["disable-pdf-extension"] = "1";
|
||||||
args["disable-plugins-discovery"] = "1";
|
args["disable-plugins-discovery"] = "1";
|
||||||
args["enable-system-flash"] = "0";
|
args["enable-system-flash"] = "0";
|
||||||
|
|
||||||
@@ -42,28 +43,53 @@ 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)){
|
||||||
OpenExternalBrowserUnsafe(url);
|
case UrlCheckResult.Fine:
|
||||||
|
if (FormGuide.CheckGuideUrl(url, out string hash)){
|
||||||
|
FormGuide.Show(hash);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
FormMessage.Warning("Blocked URL", "A potentially malicious URL was blocked from opening:\n"+url, FormMessage.OK);
|
WindowsUtils.OpenAssociatedProgram(url);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OpenExternalBrowserUnsafe(string url){
|
break;
|
||||||
using(Process.Start(url)){}
|
|
||||||
|
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)){
|
||||||
|
WindowsUtils.OpenAssociatedProgram(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UrlCheckResult.Invalid:
|
||||||
|
FormMessage.Warning("Blocked URL", "A potentially malicious URL was blocked from opening:\n"+url, FormMessage.OK);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetFileNameFromUrl(string url){
|
public static string GetFileNameFromUrl(string url){
|
||||||
@@ -75,9 +101,14 @@ namespace TweetDuck.Core.Utils{
|
|||||||
return StringUtils.ConvertPascalCaseToScreamingSnakeCase(Enum.GetName(typeof(CefErrorCode), code) ?? string.Empty);
|
return StringUtils.ConvertPascalCaseToScreamingSnakeCase(Enum.GetName(typeof(CefErrorCode), code) ?? string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static WebClient DownloadFileAsync(string url, string target, Action onSuccess, Action<Exception> onFailure){
|
public static WebClient CreateWebClient(){
|
||||||
WebClient client = new WebClient{ Proxy = null };
|
WebClient client = new WebClient{ Proxy = null };
|
||||||
client.Headers[HttpRequestHeader.UserAgent] = HeaderUserAgent;
|
client.Headers[HttpRequestHeader.UserAgent] = HeaderUserAgent;
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WebClient DownloadFileAsync(string url, string target, Action onSuccess, Action<Exception> onFailure){
|
||||||
|
WebClient client = CreateWebClient();
|
||||||
|
|
||||||
client.DownloadFileCompleted += (sender, args) => {
|
client.DownloadFileCompleted += (sender, args) => {
|
||||||
if (args.Cancelled){
|
if (args.Cancelled){
|
||||||
|
@@ -8,6 +8,7 @@ namespace TweetDuck.Core.Utils{
|
|||||||
[SuppressMessage("ReSharper", "FieldCanBeMadeReadOnly.Local")]
|
[SuppressMessage("ReSharper", "FieldCanBeMadeReadOnly.Local")]
|
||||||
[SuppressMessage("ReSharper", "MemberCanBePrivate.Local")]
|
[SuppressMessage("ReSharper", "MemberCanBePrivate.Local")]
|
||||||
static class NativeMethods{
|
static class NativeMethods{
|
||||||
|
public static readonly IntPtr HWND_BROADCAST = new IntPtr(0xFFFF);
|
||||||
public static readonly IntPtr HOOK_HANDLED = new IntPtr(-1);
|
public static readonly IntPtr HOOK_HANDLED = new IntPtr(-1);
|
||||||
|
|
||||||
public const int HWND_TOPMOST = -1;
|
public const int HWND_TOPMOST = -1;
|
||||||
@@ -16,7 +17,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;
|
||||||
@@ -52,6 +52,12 @@ namespace TweetDuck.Core.Utils{
|
|||||||
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
|
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
|
||||||
private static extern bool SetWindowPos(int hWnd, int hWndOrder, int x, int y, int width, int height, uint flags);
|
private static extern bool SetWindowPos(int hWnd, int hWndOrder, int x, int y, int width, int height, uint flags);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
public static extern bool PostMessage(IntPtr hWnd, uint msg, UIntPtr wParam, IntPtr lParam);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
public static extern uint RegisterWindowMessage(string messageName);
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
[DllImport("user32.dll")]
|
||||||
private static extern bool GetLastInputInfo(ref LASTINPUTINFO info);
|
private static extern bool GetLastInputInfo(ref LASTINPUTINFO info);
|
||||||
|
|
||||||
@@ -97,13 +103,18 @@ namespace TweetDuck.Core.Utils{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void BroadcastMessage(uint msg, uint wParam, int lParam){
|
||||||
|
PostMessage(HWND_BROADCAST, msg, new UIntPtr(wParam), new IntPtr(lParam));
|
||||||
|
}
|
||||||
|
|
||||||
public static int GetMouseHookData(IntPtr ptr){
|
public static int GetMouseHookData(IntPtr ptr){
|
||||||
return Marshal.PtrToStructure<MSLLHOOKSTRUCT>(ptr).mouseData >> 16;
|
return Marshal.PtrToStructure<MSLLHOOKSTRUCT>(ptr).mouseData >> 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int GetIdleSeconds(){
|
public static int GetIdleSeconds(){
|
||||||
LASTINPUTINFO info = new LASTINPUTINFO();
|
LASTINPUTINFO info = new LASTINPUTINFO{
|
||||||
info.cbSize = LASTINPUTINFO.Size;
|
cbSize = LASTINPUTINFO.Size
|
||||||
|
};
|
||||||
|
|
||||||
if (!GetLastInputInfo(ref info)){
|
if (!GetLastInputInfo(ref info)){
|
||||||
return 0;
|
return 0;
|
||||||
|
@@ -18,5 +18,16 @@ namespace TweetDuck.Core.Utils{
|
|||||||
public static string ConvertPascalCaseToScreamingSnakeCase(string str){
|
public static string ConvertPascalCaseToScreamingSnakeCase(string str){
|
||||||
return Regex.Replace(str, @"(\p{Ll})(\P{Ll})|(\P{Ll})(\P{Ll}\p{Ll})", "$1$3_$2$4").ToUpper();
|
return Regex.Replace(str, @"(\p{Ll})(\P{Ll})|(\P{Ll})(\P{Ll}\p{Ll})", "$1$3_$2$4").ToUpper();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int CountOccurrences(string source, string substring){
|
||||||
|
int count = 0, index = 0;
|
||||||
|
|
||||||
|
while((index = source.IndexOf(substring, index)) != -1){
|
||||||
|
index += substring.Length;
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -14,13 +14,17 @@ 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 = {
|
||||||
"tweetdeck", "TweetDeck", "tweetduck", "TweetDuck", "TD"
|
"tweetdeck", "TweetDeck", "tweetduck", "TweetDuck", "TD"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static readonly string[] ValidImageExtensions = {
|
||||||
|
".jpg", ".jpeg", ".png", ".gif"
|
||||||
|
};
|
||||||
|
|
||||||
public enum ImageQuality{
|
public enum ImageQuality{
|
||||||
Default, Orig
|
Default, Orig
|
||||||
}
|
}
|
||||||
@@ -33,14 +37,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";
|
||||||
@@ -53,6 +57,10 @@ namespace TweetDuck.Core.Utils{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string GetImageFileName(string url){
|
||||||
|
return BrowserUtils.GetFileNameFromUrl(ExtractMediaBaseLink(url));
|
||||||
|
}
|
||||||
|
|
||||||
public static void DownloadImage(string url, string username, ImageQuality quality){
|
public static void DownloadImage(string url, string username, ImageQuality quality){
|
||||||
DownloadImages(new string[]{ url }, username, quality);
|
DownloadImages(new string[]{ url }, username, quality);
|
||||||
}
|
}
|
||||||
@@ -62,10 +70,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 = GetImageFileName(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 +104,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 username){
|
||||||
|
string filename = BrowserUtils.GetFileNameFromUrl(url);
|
||||||
|
string ext = Path.GetExtension(filename);
|
||||||
|
|
||||||
|
using(SaveFileDialog dialog = new SaveFileDialog{
|
||||||
|
AutoUpgradeEnabled = true,
|
||||||
|
OverwritePrompt = true,
|
||||||
|
Title = "Save video",
|
||||||
|
FileName = string.IsNullOrEmpty(username) ? filename : $"{username} {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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Management;
|
using System.Management;
|
||||||
@@ -49,17 +50,22 @@ namespace TweetDuck.Core.Utils{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Process StartProcess(string file, string arguments, bool runElevated){
|
public static bool OpenAssociatedProgram(string file, string arguments = "", bool runElevated = false){
|
||||||
ProcessStartInfo processInfo = new ProcessStartInfo{
|
try{
|
||||||
|
using(Process.Start(new ProcessStartInfo{
|
||||||
FileName = file,
|
FileName = file,
|
||||||
Arguments = arguments
|
Arguments = arguments,
|
||||||
};
|
Verb = runElevated ? "runas" : string.Empty,
|
||||||
|
ErrorDialog = true
|
||||||
if (runElevated){
|
})){
|
||||||
processInfo.Verb = "runas";
|
return true;
|
||||||
|
}
|
||||||
|
}catch(Win32Exception e) when (e.NativeErrorCode == 0x000004C7){ // operation canceled by the user
|
||||||
|
return false;
|
||||||
|
}catch(Exception e){
|
||||||
|
Program.Reporter.HandleException("Error opening file", e.Message, true, e);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Process.Start(processInfo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool TrySleepUntil(Func<bool> test, int timeoutMillis, int timeStepMillis){
|
public static bool TrySleepUntil(Func<bool> test, int timeoutMillis, int timeStepMillis){
|
||||||
@@ -104,7 +110,7 @@ namespace TweetDuck.Core.Utils{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void ClipboardStripHtmlStyles(){
|
public static void ClipboardStripHtmlStyles(){
|
||||||
if (!Clipboard.ContainsText(TextDataFormat.Html)){
|
if (!Clipboard.ContainsText(TextDataFormat.Html) || !Clipboard.ContainsText(TextDataFormat.UnicodeText)){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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{
|
||||||
|
@@ -4,6 +4,7 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
|
using TweetDuck.Core.Utils;
|
||||||
|
|
||||||
namespace TweetDuck.Data.Serialization{
|
namespace TweetDuck.Data.Serialization{
|
||||||
sealed class FileSerializer<T>{
|
sealed class FileSerializer<T>{
|
||||||
@@ -28,6 +29,8 @@ namespace TweetDuck.Data.Serialization{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void Write(string file, T obj){
|
public void Write(string file, T obj){
|
||||||
|
WindowsUtils.CreateDirectoryForFile(file);
|
||||||
|
|
||||||
using(StreamWriter writer = new StreamWriter(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))){
|
using(StreamWriter writer = new StreamWriter(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))){
|
||||||
foreach(KeyValuePair<string, PropertyInfo> prop in props){
|
foreach(KeyValuePair<string, PropertyInfo> prop in props){
|
||||||
Type type = prop.Value.PropertyType;
|
Type type = prop.Value.PropertyType;
|
||||||
@@ -54,7 +57,11 @@ namespace TweetDuck.Data.Serialization{
|
|||||||
Dictionary<string, string> unknownProperties = new Dictionary<string, string>(4);
|
Dictionary<string, string> unknownProperties = new Dictionary<string, string>(4);
|
||||||
|
|
||||||
using(StreamReader reader = new StreamReader(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))){
|
using(StreamReader reader = new StreamReader(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))){
|
||||||
if (reader.Peek() <= 1){
|
switch(reader.Peek()){
|
||||||
|
case -1:
|
||||||
|
throw new FormatException("File is empty.");
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
throw new FormatException("Input appears to be a binary file.");
|
throw new FormatException("Input appears to be a binary file.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,12 +97,19 @@ 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{
|
public void ReadIfExists(string file, T obj){
|
||||||
|
try{
|
||||||
|
Read(file, obj);
|
||||||
|
}catch(FileNotFoundException){
|
||||||
|
}catch(DirectoryNotFoundException){}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
@@ -62,9 +62,7 @@ namespace TweetDuck.Plugins.Controls{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void btnToggleState_Click(object sender, EventArgs e){
|
private void btnToggleState_Click(object sender, EventArgs e){
|
||||||
bool newState = !pluginManager.Config.IsEnabled(plugin);
|
pluginManager.Config.ToggleEnabled(plugin);
|
||||||
pluginManager.Config.SetEnabled(plugin, newState);
|
|
||||||
|
|
||||||
UpdatePluginState();
|
UpdatePluginState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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,9 +7,11 @@ using TweetDuck.Plugins.Enums;
|
|||||||
|
|
||||||
namespace TweetDuck.Plugins{
|
namespace TweetDuck.Plugins{
|
||||||
sealed class Plugin{
|
sealed class Plugin{
|
||||||
|
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; }
|
||||||
|
|
||||||
public string Name => metadata["NAME"];
|
public string Name => metadata["NAME"];
|
||||||
public string Description => metadata["DESCRIPTION"];
|
public string Description => metadata["DESCRIPTION"];
|
||||||
@@ -20,9 +22,7 @@ namespace TweetDuck.Plugins{
|
|||||||
public string ConfigDefault => metadata["CONFIGDEFAULT"];
|
public string ConfigDefault => metadata["CONFIGDEFAULT"];
|
||||||
public string RequiredVersion => metadata["REQUIRES"];
|
public string RequiredVersion => metadata["REQUIRES"];
|
||||||
|
|
||||||
public bool CanRun{
|
public bool CanRun { get; private set; }
|
||||||
get => canRun ?? (canRun = CheckRequiredVersion(RequiredVersion)).Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool HasConfig{
|
public bool HasConfig{
|
||||||
get => ConfigFile.Length > 0 && GetFullPathIfSafe(PluginFolder.Data, ConfigFile).Length > 0;
|
get => ConfigFile.Length > 0 && GetFullPathIfSafe(PluginFolder.Data, ConfigFile).Length > 0;
|
||||||
@@ -50,24 +50,21 @@ namespace TweetDuck.Plugins{
|
|||||||
{ "WEBSITE", "" },
|
{ "WEBSITE", "" },
|
||||||
{ "CONFIGFILE", "" },
|
{ "CONFIGFILE", "" },
|
||||||
{ "CONFIGDEFAULT", "" },
|
{ "CONFIGDEFAULT", "" },
|
||||||
{ "REQUIRES", "*" }
|
{ "REQUIRES", VersionWildcard }
|
||||||
};
|
};
|
||||||
|
|
||||||
private bool? canRun;
|
private Plugin(string path, string name, PluginGroup group, PluginEnvironment environments){
|
||||||
|
|
||||||
private Plugin(string path, PluginGroup group){
|
|
||||||
string name = Path.GetFileName(path);
|
|
||||||
System.Diagnostics.Debug.Assert(name != null);
|
|
||||||
|
|
||||||
this.pathRoot = path;
|
this.pathRoot = path;
|
||||||
this.pathData = Path.Combine(Program.PluginDataPath, group.GetIdentifierPrefix(), name);
|
this.pathData = Path.Combine(Program.PluginDataPath, group.GetIdentifierPrefix(), name);
|
||||||
|
|
||||||
this.Identifier = group.GetIdentifierPrefix()+name;
|
this.Identifier = group.GetIdentifierPrefix()+name;
|
||||||
this.Group = group;
|
this.Group = group;
|
||||||
this.Environments = PluginEnvironment.None;
|
this.Environments = environments;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMetadataLoaded(){
|
private void OnMetadataLoaded(){
|
||||||
|
CanRun = CheckRequiredVersion(RequiredVersion);
|
||||||
|
|
||||||
string configPath = ConfigPath, defaultConfigPath = DefaultConfigPath;
|
string configPath = ConfigPath, defaultConfigPath = DefaultConfigPath;
|
||||||
|
|
||||||
if (configPath.Length > 0 && defaultConfigPath.Length > 0 && !File.Exists(configPath) && File.Exists(defaultConfigPath)){
|
if (configPath.Length > 0 && defaultConfigPath.Length > 0 && !File.Exists(configPath) && File.Exists(defaultConfigPath)){
|
||||||
@@ -77,7 +74,7 @@ namespace TweetDuck.Plugins{
|
|||||||
Directory.CreateDirectory(dataFolder);
|
Directory.CreateDirectory(dataFolder);
|
||||||
File.Copy(defaultConfigPath, configPath, false);
|
File.Copy(defaultConfigPath, configPath, false);
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
Program.Reporter.HandleException("Plugin Loading Error", "Could not generate a configuration file for '"+Identifier+"' plugin.", true, e);
|
throw new IOException("Could not generate a configuration file for '"+Identifier+"' plugin: "+e.Message, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -135,93 +132,78 @@ namespace TweetDuck.Plugins{
|
|||||||
return obj is Plugin plugin && plugin.Identifier.Equals(Identifier);
|
return obj is Plugin plugin && plugin.Identifier.Equals(Identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Plugin CreateFromFolder(string path, PluginGroup group, out string error){
|
// Static
|
||||||
Plugin plugin = new Plugin(path, group);
|
|
||||||
|
|
||||||
if (!LoadMetadata(path, plugin, out error)){
|
private static readonly Version AppVersion = new Version(Program.VersionTag);
|
||||||
return null;
|
private static readonly string[] EndTag = { "[END]" };
|
||||||
}
|
|
||||||
|
|
||||||
if (!LoadEnvironments(path, plugin, out error)){
|
public static Plugin CreateFromFolder(string path, PluginGroup group){
|
||||||
return null;
|
Plugin plugin = new Plugin(path, Path.GetFileName(path), group, LoadEnvironments(path));
|
||||||
}
|
LoadMetadata(path, plugin);
|
||||||
|
|
||||||
error = string.Empty;
|
|
||||||
return plugin;
|
return plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool LoadEnvironments(string path, Plugin plugin, out string error){
|
private static PluginEnvironment LoadEnvironments(string path){
|
||||||
|
PluginEnvironment environments = PluginEnvironment.None;
|
||||||
|
|
||||||
foreach(string file in Directory.EnumerateFiles(path, "*.js", SearchOption.TopDirectoryOnly).Select(Path.GetFileName)){
|
foreach(string file in Directory.EnumerateFiles(path, "*.js", SearchOption.TopDirectoryOnly).Select(Path.GetFileName)){
|
||||||
plugin.Environments |= PluginEnvironmentExtensions.Values.FirstOrDefault(env => file.Equals(env.GetPluginScriptFile(), StringComparison.Ordinal));
|
environments |= PluginEnvironmentExtensions.Values.FirstOrDefault(env => file.Equals(env.GetPluginScriptFile(), StringComparison.Ordinal));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (plugin.Environments == PluginEnvironment.None){
|
if (environments == PluginEnvironment.None){
|
||||||
error = "Plugin has no script files.";
|
throw new ArgumentException("Plugin has no script files");
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
error = string.Empty;
|
return environments;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly string[] endTag = { "[END]" };
|
private static void LoadMetadata(string path, Plugin plugin){
|
||||||
|
|
||||||
private static bool LoadMetadata(string path, Plugin plugin, out string error){
|
|
||||||
string metaFile = Path.Combine(path, ".meta");
|
string metaFile = Path.Combine(path, ".meta");
|
||||||
|
|
||||||
if (!File.Exists(metaFile)){
|
if (!File.Exists(metaFile)){
|
||||||
error = "Missing .meta file.";
|
throw new ArgumentException("Missing .meta file");
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
string[] lines = File.ReadAllLines(metaFile, Encoding.UTF8);
|
string currentTag = null, currentContents = string.Empty;
|
||||||
string currentTag = null, currentContents = "";
|
|
||||||
|
|
||||||
foreach(string line in lines.Concat(endTag).Select(line => line.TrimEnd()).Where(line => line.Length > 0)){
|
foreach(string line in File.ReadAllLines(metaFile, Encoding.UTF8).Concat(EndTag).Select(line => line.TrimEnd()).Where(line => line.Length > 0)){
|
||||||
if (line[0] == '[' && line[line.Length-1] == ']'){
|
if (line[0] == '[' && line[line.Length-1] == ']'){
|
||||||
if (currentTag != null){
|
if (currentTag != null){
|
||||||
plugin.metadata[currentTag] = currentContents;
|
plugin.metadata[currentTag] = currentContents;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentTag = line.Substring(1, line.Length-2).ToUpper();
|
currentTag = line.Substring(1, line.Length-2).ToUpper();
|
||||||
currentContents = "";
|
currentContents = string.Empty;
|
||||||
|
|
||||||
if (line.Equals(endTag[0])){
|
if (line.Equals(EndTag[0])){
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!plugin.metadata.ContainsKey(currentTag)){
|
if (!plugin.metadata.ContainsKey(currentTag)){
|
||||||
error = "Invalid metadata tag: "+currentTag;
|
throw new FormatException("Invalid metadata tag: "+currentTag);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (currentTag != null){
|
else if (currentTag != null){
|
||||||
currentContents = currentContents.Length == 0 ? line : currentContents+"\r\n"+line;
|
currentContents = currentContents.Length == 0 ? line : currentContents+Environment.NewLine+line;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
error = "Missing metadata tag before value: "+line;
|
throw new FormatException("Missing metadata tag before value: "+line);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (plugin.Name.Length == 0){
|
if (plugin.Name.Length == 0){
|
||||||
error = "Plugin is missing a name in the .meta file.";
|
throw new FormatException("Plugin is missing a name in the .meta file");
|
||||||
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;
|
throw new FormatException("Plugin contains invalid version: "+plugin.RequiredVersion);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
plugin.OnMetadataLoaded();
|
plugin.OnMetadataLoaded();
|
||||||
|
|
||||||
error = string.Empty;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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,42 @@ 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 void ToggleEnabled(Plugin plugin){
|
||||||
|
SetEnabled(plugin, !IsEnabled(plugin));
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsEnabled(Plugin plugin){
|
public bool IsEnabled(Plugin plugin){
|
||||||
return !Disabled.Contains(plugin.Identifier);
|
return !disabled.Contains(plugin.Identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Save(string file){
|
||||||
|
try{
|
||||||
|
using(StreamWriter writer = new StreamWriter(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None), Encoding.UTF8)){
|
||||||
|
writer.WriteLine("#Disabled");
|
||||||
|
|
||||||
|
foreach(string identifier in disabled){
|
||||||
|
writer.WriteLine(identifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}catch(Exception e){
|
||||||
|
Program.Reporter.HandleException("Plugin Configuration Error", "Could not save the plugin configuration file.", true, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Load(string file){
|
public void Load(string file){
|
||||||
@@ -32,33 +52,21 @@ 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();
|
||||||
|
disabled.UnionWith(DefaultDisabled);
|
||||||
Save(file);
|
Save(file);
|
||||||
}catch(DirectoryNotFoundException){
|
}catch(DirectoryNotFoundException){
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
Program.Reporter.HandleException("Plugin Configuration Error", "Could not read the plugin configuration file. If you continue, the list of disabled plugins will be reset to default.", true, e);
|
Program.Reporter.HandleException("Plugin Configuration Error", "Could not read the plugin configuration file. If you continue, the list of disabled plugins will be reset to default.", true, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Save(string file){
|
|
||||||
try{
|
|
||||||
using(StreamWriter writer = new StreamWriter(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None), Encoding.UTF8)){
|
|
||||||
writer.WriteLine("#Disabled");
|
|
||||||
|
|
||||||
foreach(string disabled in Disabled){
|
|
||||||
writer.WriteLine(disabled);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}catch(Exception e){
|
|
||||||
Program.Reporter.HandleException("Plugin Configuration Error", "Could not save the plugin configuration file.", true, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,8 +9,6 @@ using TweetDuck.Resources;
|
|||||||
|
|
||||||
namespace TweetDuck.Plugins{
|
namespace TweetDuck.Plugins{
|
||||||
sealed class PluginManager{
|
sealed class PluginManager{
|
||||||
private const int InvalidToken = 0;
|
|
||||||
|
|
||||||
private static readonly Dictionary<PluginEnvironment, string> PluginSetupScripts = new Dictionary<PluginEnvironment, string>(4){
|
private static readonly Dictionary<PluginEnvironment, string> PluginSetupScripts = new Dictionary<PluginEnvironment, string>(4){
|
||||||
{ PluginEnvironment.None, ScriptLoader.LoadResource("plugins.js") },
|
{ PluginEnvironment.None, ScriptLoader.LoadResource("plugins.js") },
|
||||||
{ PluginEnvironment.Browser, ScriptLoader.LoadResource("plugins.browser.js") },
|
{ PluginEnvironment.Browser, ScriptLoader.LoadResource("plugins.browser.js") },
|
||||||
@@ -35,8 +33,6 @@ namespace TweetDuck.Plugins{
|
|||||||
private readonly Dictionary<int, Plugin> tokens = new Dictionary<int, Plugin>();
|
private readonly Dictionary<int, Plugin> tokens = new Dictionary<int, Plugin>();
|
||||||
private readonly Random rand = new Random();
|
private readonly Random rand = new Random();
|
||||||
|
|
||||||
private List<string> loadErrors;
|
|
||||||
|
|
||||||
public PluginManager(string rootPath, string configPath){
|
public PluginManager(string rootPath, string configPath){
|
||||||
this.rootPath = rootPath;
|
this.rootPath = rootPath;
|
||||||
this.configPath = configPath;
|
this.configPath = configPath;
|
||||||
@@ -45,14 +41,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){
|
||||||
@@ -75,7 +64,18 @@ namespace TweetDuck.Plugins{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return InvalidToken;
|
int token, attempts = 1000;
|
||||||
|
|
||||||
|
do{
|
||||||
|
token = rand.Next();
|
||||||
|
}while(tokens.ContainsKey(token) && --attempts >= 0);
|
||||||
|
|
||||||
|
if (attempts < 0){
|
||||||
|
token = -tokens.Count-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens[token] = plugin;
|
||||||
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Plugin GetPluginFromToken(int token){
|
public Plugin GetPluginFromToken(int token){
|
||||||
@@ -83,31 +83,46 @@ namespace TweetDuck.Plugins{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void Reload(){
|
public void Reload(){
|
||||||
|
Config.Load(configPath);
|
||||||
|
|
||||||
plugins.Clear();
|
plugins.Clear();
|
||||||
tokens.Clear();
|
tokens.Clear();
|
||||||
|
|
||||||
loadErrors = new List<string>(2);
|
List<string> loadErrors = new List<string>(2);
|
||||||
|
|
||||||
foreach(Plugin plugin in LoadPluginsFrom(PathOfficialPlugins, PluginGroup.Official)){
|
IEnumerable<Plugin> LoadPluginsFrom(string path, PluginGroup group){
|
||||||
plugins.Add(plugin);
|
if (!Directory.Exists(path)){
|
||||||
|
yield break;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach(Plugin plugin in LoadPluginsFrom(PathCustomPlugins, PluginGroup.Custom)){
|
foreach(string fullDir in Directory.EnumerateDirectories(path, "*", SearchOption.TopDirectoryOnly)){
|
||||||
plugins.Add(plugin);
|
Plugin plugin;
|
||||||
|
|
||||||
|
try{
|
||||||
|
plugin = Plugin.CreateFromFolder(fullDir, group);
|
||||||
|
}catch(Exception e){
|
||||||
|
loadErrors.Add(group.GetIdentifierPrefix()+Path.GetFileName(fullDir)+": "+e.Message);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
yield return plugin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins.UnionWith(LoadPluginsFrom(PathOfficialPlugins, PluginGroup.Official));
|
||||||
|
plugins.UnionWith(LoadPluginsFrom(PathCustomPlugins, PluginGroup.Custom));
|
||||||
|
|
||||||
Reloaded?.Invoke(this, new PluginErrorEventArgs(loadErrors));
|
Reloaded?.Invoke(this, new PluginErrorEventArgs(loadErrors));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ExecutePlugins(IFrame frame, PluginEnvironment environment){
|
public void ExecutePlugins(IFrame frame, PluginEnvironment environment){
|
||||||
if (HasAnyPlugin(environment)){
|
if (!HasAnyPlugin(environment)){
|
||||||
ScriptLoader.ExecuteScript(frame, PluginSetupScripts[environment], environment.GetScriptIdentifier());
|
return;
|
||||||
ScriptLoader.ExecuteScript(frame, PluginSetupScripts[PluginEnvironment.None], PluginEnvironment.None.GetScriptIdentifier());
|
|
||||||
ExecutePluginScripts(frame, environment);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExecutePluginScripts(IFrame frame, PluginEnvironment environment){
|
ScriptLoader.ExecuteScript(frame, PluginSetupScripts[environment], environment.GetScriptIdentifier());
|
||||||
|
ScriptLoader.ExecuteScript(frame, PluginSetupScripts[PluginEnvironment.None], PluginEnvironment.None.GetScriptIdentifier());
|
||||||
|
|
||||||
bool includeDisabled = environment.IncludesDisabledPlugins();
|
bool includeDisabled = environment.IncludesDisabledPlugins();
|
||||||
|
|
||||||
if (includeDisabled){
|
if (includeDisabled){
|
||||||
@@ -118,7 +133,10 @@ namespace TweetDuck.Plugins{
|
|||||||
|
|
||||||
foreach(Plugin plugin in Plugins){
|
foreach(Plugin plugin in Plugins){
|
||||||
string path = plugin.GetScriptPath(environment);
|
string path = plugin.GetScriptPath(environment);
|
||||||
if (string.IsNullOrEmpty(path) || (!includeDisabled && !Config.IsEnabled(plugin)) || !plugin.CanRun)continue;
|
|
||||||
|
if (string.IsNullOrEmpty(path) || (!includeDisabled && !Config.IsEnabled(plugin)) || !plugin.CanRun){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
string script;
|
string script;
|
||||||
|
|
||||||
@@ -129,49 +147,10 @@ namespace TweetDuck.Plugins{
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int token;
|
ScriptLoader.ExecuteScript(frame, PluginScriptGenerator.GeneratePlugin(plugin.Identifier, script, GetTokenFromPlugin(plugin), environment), "plugin:"+plugin);
|
||||||
|
|
||||||
if (tokens.ContainsValue(plugin)){
|
|
||||||
token = GetTokenFromPlugin(plugin);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
token = GenerateToken();
|
|
||||||
tokens[token] = plugin;
|
|
||||||
}
|
|
||||||
|
|
||||||
ScriptLoader.ExecuteScript(frame, PluginScriptGenerator.GeneratePlugin(plugin.Identifier, script, token, environment), "plugin:"+plugin);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Executed?.Invoke(this, new PluginErrorEventArgs(failedPlugins));
|
Executed?.Invoke(this, new PluginErrorEventArgs(failedPlugins));
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<Plugin> LoadPluginsFrom(string path, PluginGroup group){
|
|
||||||
if (!Directory.Exists(path)){
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach(string fullDir in Directory.EnumerateDirectories(path, "*", SearchOption.TopDirectoryOnly)){
|
|
||||||
Plugin plugin = Plugin.CreateFromFolder(fullDir, group, out string error);
|
|
||||||
|
|
||||||
if (plugin == null){
|
|
||||||
loadErrors.Add(group.GetIdentifierPrefix()+Path.GetFileName(fullDir)+": "+error);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
yield return plugin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int GenerateToken(){
|
|
||||||
for(int attempt = 0; attempt < 1000; attempt++){
|
|
||||||
int token = rand.Next();
|
|
||||||
|
|
||||||
if (!tokens.ContainsKey(token) && token != InvalidToken){
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return -tokens.Count;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
41
Program.cs
@@ -14,17 +14,14 @@ using TweetDuck.Core.Other.Settings.Export;
|
|||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetDuck.Data;
|
using TweetDuck.Data;
|
||||||
using TweetDuck.Updates;
|
using TweetDuck.Updates;
|
||||||
using TweetLib.Communication;
|
|
||||||
|
|
||||||
namespace TweetDuck{
|
namespace TweetDuck{
|
||||||
static class Program{
|
static class Program{
|
||||||
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.11.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;
|
||||||
@@ -39,6 +36,7 @@ namespace TweetDuck{
|
|||||||
public static string UserConfigFilePath => Path.Combine(StoragePath, "TD_UserConfig.cfg");
|
public static string UserConfigFilePath => Path.Combine(StoragePath, "TD_UserConfig.cfg");
|
||||||
public static string SystemConfigFilePath => Path.Combine(StoragePath, "TD_SystemConfig.cfg");
|
public static string SystemConfigFilePath => Path.Combine(StoragePath, "TD_SystemConfig.cfg");
|
||||||
public static string PluginConfigFilePath => Path.Combine(StoragePath, "TD_PluginConfig.cfg");
|
public static string PluginConfigFilePath => Path.Combine(StoragePath, "TD_PluginConfig.cfg");
|
||||||
|
public static string AnalyticsFilePath => Path.Combine(StoragePath, "TD_Analytics.cfg");
|
||||||
|
|
||||||
private static string ErrorLogFilePath => Path.Combine(StoragePath, "TD_Log.txt");
|
private static string ErrorLogFilePath => Path.Combine(StoragePath, "TD_Log.txt");
|
||||||
private static string ConsoleLogFilePath => Path.Combine(StoragePath, "TD_Console.txt");
|
private static string ConsoleLogFilePath => Path.Combine(StoragePath, "TD_Console.txt");
|
||||||
@@ -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;
|
||||||
@@ -73,9 +69,10 @@ namespace TweetDuck{
|
|||||||
private static void Main(){
|
private static void Main(){
|
||||||
Application.EnableVisualStyles();
|
Application.EnableVisualStyles();
|
||||||
Application.SetCompatibleTextRenderingDefault(false);
|
Application.SetCompatibleTextRenderingDefault(false);
|
||||||
|
Cef.EnableHighDPISupport();
|
||||||
|
|
||||||
WindowRestoreMessage = Comms.RegisterMessage("TweetDuckRestore");
|
WindowRestoreMessage = NativeMethods.RegisterWindowMessage("TweetDuckRestore");
|
||||||
SubProcessMessage = Comms.RegisterMessage("TweetDuckSubProcess");
|
SubProcessMessage = NativeMethods.RegisterWindowMessage("TweetDuckSubProcess");
|
||||||
|
|
||||||
if (!WindowsUtils.CheckFolderWritePermission(StoragePath)){
|
if (!WindowsUtils.CheckFolderWritePermission(StoragePath)){
|
||||||
FormMessage.Warning("Permission Error", "TweetDuck does not have write permissions to the storage folder: "+StoragePath, FormMessage.OK);
|
FormMessage.Warning("Permission Error", "TweetDuck does not have write permissions to the storage folder: "+StoragePath, FormMessage.OK);
|
||||||
@@ -118,12 +115,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);
|
||||||
@@ -146,7 +146,6 @@ namespace TweetDuck{
|
|||||||
CommandLineArgs.ReadCefArguments(UserConfig.CustomCefArgs).ToDictionary(settings.CefCommandLineArgs);
|
CommandLineArgs.ReadCefArguments(UserConfig.CustomCefArgs).ToDictionary(settings.CefCommandLineArgs);
|
||||||
BrowserUtils.SetupCefArgs(settings.CefCommandLineArgs);
|
BrowserUtils.SetupCefArgs(settings.CefCommandLineArgs);
|
||||||
|
|
||||||
Cef.EnableHighDPISupport();
|
|
||||||
Cef.Initialize(settings, false, new BrowserProcessHandler());
|
Cef.Initialize(settings, false, new BrowserProcessHandler());
|
||||||
|
|
||||||
Application.ApplicationExit += (sender, args) => ExitCleanup();
|
Application.ApplicationExit += (sender, args) => ExitCleanup();
|
||||||
@@ -167,9 +166,13 @@ namespace TweetDuck{
|
|||||||
string updaterArgs = "/SP- /SILENT /CLOSEAPPLICATIONS /UPDATEPATH=\""+ProgramPath+"\" /RUNARGS=\""+Arguments.GetCurrentForInstallerCmd()+"\""+(IsPortable ? " /PORTABLE=1" : "");
|
string updaterArgs = "/SP- /SILENT /CLOSEAPPLICATIONS /UPDATEPATH=\""+ProgramPath+"\" /RUNARGS=\""+Arguments.GetCurrentForInstallerCmd()+"\""+(IsPortable ? " /PORTABLE=1" : "");
|
||||||
bool runElevated = !IsPortable || !WindowsUtils.CheckFolderWritePermission(ProgramPath);
|
bool runElevated = !IsPortable || !WindowsUtils.CheckFolderWritePermission(ProgramPath);
|
||||||
|
|
||||||
WindowsUtils.StartProcess(mainForm.UpdateInstallerPath, updaterArgs, runElevated);
|
if (WindowsUtils.OpenAssociatedProgram(mainForm.UpdateInstallerPath, updaterArgs, runElevated)){
|
||||||
Application.Exit();
|
Application.Exit();
|
||||||
}
|
}
|
||||||
|
else{
|
||||||
|
RestartWithArgsInternal(Arguments.GetCurrentClean());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetDataStoragePath(){
|
private static string GetDataStoragePath(){
|
||||||
@@ -190,11 +193,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 +202,7 @@ namespace TweetDuck{
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReloadConfig();
|
UserConfig.Reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Restart(params string[] extraArgs){
|
public static void Restart(params string[] extraArgs){
|
||||||
@@ -217,11 +215,14 @@ namespace TweetDuck{
|
|||||||
FormBrowser browserForm = Application.OpenForms.OfType<FormBrowser>().FirstOrDefault();
|
FormBrowser browserForm = Application.OpenForms.OfType<FormBrowser>().FirstOrDefault();
|
||||||
if (browserForm == null)return;
|
if (browserForm == null)return;
|
||||||
|
|
||||||
args.AddFlag(Arguments.ArgRestart);
|
|
||||||
|
|
||||||
browserForm.ForceClose();
|
browserForm.ForceClose();
|
||||||
ExitCleanup();
|
|
||||||
|
|
||||||
|
ExitCleanup();
|
||||||
|
RestartWithArgsInternal(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RestartWithArgsInternal(CommandLineArgs args){
|
||||||
|
args.AddFlag(Arguments.ArgRestart);
|
||||||
Process.Start(Application.ExecutablePath, args.ToString());
|
Process.Start(Application.ExecutablePath, args.ToString());
|
||||||
Application.Exit();
|
Application.Exit();
|
||||||
}
|
}
|
||||||
|
@@ -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")]
|
||||||
|
|
||||||
|
68
README.md
@@ -1,11 +1,73 @@
|
|||||||
|
# Support
|
||||||
|
|
||||||
|
[Follow TweetDuck on Twitter](https://twitter.com/TryTweetDuck) | [Support via PayPal](https://paypal.me/chylex) | [Support via Patreon](https://www.patreon.com/chylex)
|
||||||
|
|
||||||
# Build Instructions
|
# Build Instructions
|
||||||
|
|
||||||
The program was built using Visual Studio 2017. After opening the solution, make sure you have **CefSharp.WinForms** and **Microsoft.VC120.CRT.JetBrains** included - if not, download them using NuGet.
|
### Setup
|
||||||
|
|
||||||
|
The program was built using Visual Studio 2017. Before opening the solution, please make sure you have the following workloads and components installed (optional components that are not listed can be deselected to save space):
|
||||||
|
* **.NET desktop development**
|
||||||
|
* .NET Framework 4 – 4.6 development tools
|
||||||
|
* **Desktop development with C++**
|
||||||
|
* VC++ 2017 v141 toolset
|
||||||
|
|
||||||
|
After opening the solution, download the following NuGet packages by right-clicking on the solution and selecting **Restore NuGet Packages**, or manually running these commands in the **Package Manager Console**:
|
||||||
```
|
```
|
||||||
PM> Install-Package CefSharp.WinForms -Version 57.0.0
|
PM> Install-Package CefSharp.WinForms -Version 57.0.0
|
||||||
PM> Install-Package Microsoft.VC120.CRT.JetBrains
|
PM> Install-Package Microsoft.VC120.CRT.JetBrains
|
||||||
```
|
```
|
||||||
|
|
||||||
When building the `Release` configuration, the folder will only contain files intended for distribution (no debug symbols or other unnecessary files). You may package the files yourself, or use `bld/RUN BUILD.bat` to generate installer files using Inno Setup (make sure the Inno Setup binaries are on your PATH).
|
### Debug
|
||||||
|
|
||||||
Built files are available in **bin/x86**, installer files are generated in **bld/Output**. If you decide to release a custom version publicly, please make it clear that it is not the original TweetDuck.
|
It is recommended to create a separate data folder for debugging, otherwise you will not be able to run TweetDuck while debugging the solution.
|
||||||
|
|
||||||
|
To do that, open **TweetDuck Properties**, click the **Debug** tab, make sure your **Configuration** is set to `Active (Debug)` (or just `Debug`), and insert this into the **Command line arguments** field:
|
||||||
|
```
|
||||||
|
-datafolder TweetDuckDebug
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
|
To make a release build of TweetDuck, open **Batch Build**, tick all `Release` configurations except for the `UnitTest` project (otherwise the build will fail), and click **Rebuild**. Check the status bar to make sure it says **Rebuild All succeeded**; if not, see the [Troubleshooting](#troubleshooting) section.
|
||||||
|
|
||||||
|
After the build succeeds, the `bin/x86/Release` folder will contain files intended for distribution (no debug symbols or other unnecessary files). You may package these files yourself, or see the [Installers](#installers) section for automated installer generation.
|
||||||
|
|
||||||
|
If you decide to release a custom version publicly, please make it clear that it is not an official release of TweetDuck.
|
||||||
|
|
||||||
|
### Troubleshooting
|
||||||
|
|
||||||
|
There are a few quirks in the build process that may catch you off guard:
|
||||||
|
|
||||||
|
- **Plugin files are not updated automatically**
|
||||||
|
- Since official plugins (`Resources/Plugins`) are not included in the project, Visual Studio will not automatically detect changes in the files
|
||||||
|
- To ensure plugins are updated when testing the app, click **Rebuild Solution** before clicking **Start**
|
||||||
|
- **Error: The command (...) exited with code 1**
|
||||||
|
- If the post-build event fails, open the **Output** tab and look for the cause
|
||||||
|
- Determine if there was an IO error while copying files or modifying folders, or whether the final .ps1 script failed (`Encountered an error while running PostBuild.ps1 on line xyz`)
|
||||||
|
- Some files are checked for invalid characters:
|
||||||
|
- `Resources/Plugins/emoji-keyboard/emoji-ordering.txt` line endings must be LF (line feed); any CR (carriage return) in the file will cause a failed build, and you will need to ensure correct line endings in your text editor
|
||||||
|
|
||||||
|
### Installers
|
||||||
|
|
||||||
|
TweetDuck uses **Inno Setup** to automate the creation of installers. First, download and install [InnoSetup QuickStart Pack](http://www.jrsoftware.org/isdl.php) (non-unicode; editor and encryption support not required) and the [Inno Download Plugin](https://code.google.com/archive/p/inno-download-plugin).
|
||||||
|
|
||||||
|
Next, add the Inno Setup installation folder (usually `C:\Program Files (x86)\Inno Setup 5`) into your **PATH** environment variable. You may need to restart File Explorer for the change to take place.
|
||||||
|
|
||||||
|
Now you can generate installers after a build by running `bld/RUN BUILD.bat`. Note that despite the name, this will only package the files, you still need to run the [build](#build) in Visual Studio!
|
||||||
|
|
||||||
|
After the window closes, three installers will be generated inside the `bld/Output` folder:
|
||||||
|
* **TweetDuck.exe**
|
||||||
|
* This is the main installer that creates entries in the Start Menu & Programs and Features, and an optional desktop icon
|
||||||
|
* **TweetDuck.Update.exe**
|
||||||
|
* This is a lightweight update installer that only contains the most important files that usually change across releases
|
||||||
|
* It will automatically download and apply the full installer if the user's current version of CEF does not match (the download link is in `gen_upd.iss` and points to this repository by default)
|
||||||
|
* **TweetDuck.Portable.exe**
|
||||||
|
* This is a portable installer that does not need administrator privileges
|
||||||
|
* It automatically creates a `makeportable` file in the program folder, which forces TweetDuck to run in portable mode
|
||||||
|
|
||||||
|
Note: There is a small chance you will see a resource error when running `RUN BUILD.bat`. If that happens, close the console window (which will terminate all Inno Setup processes and leave corrupted installer files in the output folder), and run it again.
|
||||||
|
|
||||||
|
### Code Notes
|
||||||
|
|
||||||
|
There are many references to the official TweetDuck website and this repository in the code and installers, so if you plan to release your own version, make sure to search for `tweetduck.chylex.com` and `github.com` in the whole repository and replace them appropriately.
|
||||||
|
BIN
Resources/Guide/img/app-menu.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
Resources/Guide/img/column-clear-header.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
Resources/Guide/img/column-clear-preferences.png
Normal file
After Width: | Height: | Size: 7.5 KiB |
BIN
Resources/Guide/img/column-preferences.png
Normal file
After Width: | Height: | Size: 7.3 KiB |
BIN
Resources/Guide/img/icon.ico
Normal file
After Width: | Height: | Size: 100 KiB |
BIN
Resources/Guide/img/new-tweet-emoji.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
Resources/Guide/img/new-tweet-pin.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
Resources/Guide/img/new-tweet-template-advanced.png
Normal file
After Width: | Height: | Size: 5.4 KiB |