mirror of
https://github.com/chylex/TweetDuck.git
synced 2025-09-14 10:32:10 +02:00
Compare commits
335 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 | |||
68dca6e3d9 | |||
017f883e0b | |||
77b5c95f75 | |||
9d052c8339 | |||
d67623a657 | |||
c740b3dd46 | |||
2ef5f7f96f | |||
404568d795 | |||
b5a6337a0c | |||
82170c3fbd | |||
e6d6275fcc | |||
97c865a127 | |||
1ff21f0ee0 | |||
2a3dca4467 | |||
d4ecfcceec | |||
ec5d503e4d | |||
346391ca2d | |||
9074cdf340 | |||
2fcf3604a8 | |||
34e5185fa1 | |||
e09e0e69ca | |||
963c98e588 | |||
92acb823a4 | |||
b967b1288f | |||
1db271ce90 | |||
58c64025e3 | |||
643a7a87aa | |||
5e9ed5d713 | |||
78e492c764 | |||
59c2a3642b | |||
40ca923745 | |||
03af6cecaa | |||
3992e447f4 | |||
14a9edeb73 | |||
92f1e9f7ec | |||
19c294c53e | |||
fe88ea5c05 | |||
c9d551213a | |||
1e86a33ceb | |||
551dd229f5 | |||
5ecf3c4147 | |||
91bb2f4df0 | |||
ae3a0ae83d | |||
63ce7523de | |||
9e3b92bfc1 | |||
bc1767fb84 | |||
f917096cc7 | |||
308926a2ae | |||
76f2b1a454 | |||
d899e4b38b | |||
e1422e35cc | |||
2c00c6bb81 | |||
7e56ba6408 | |||
8ceb70e67d | |||
37d5efef1d | |||
924065c26e | |||
58cc7ea10d | |||
f93e275ddf | |||
06d2a5f715 | |||
3a7455eafe | |||
8b676fe6ce | |||
54d12686af | |||
f231256402 | |||
410ead66f8 | |||
c833a810af | |||
50f1336b1d | |||
60ed0b8cde | |||
cc55a81c1b | |||
f832e04e9e | |||
fc760b9a0c | |||
9addff0521 | |||
dcaa3aab19 | |||
628785c68c | |||
a5aa396fda | |||
f53a9f05e3 | |||
7749b14156 | |||
c15f339718 | |||
775f590bfa | |||
76408ea56f | |||
a391d8ee83 | |||
48c38f6e1d | |||
37c5fba162 | |||
23e99b1d44 | |||
8432240a47 | |||
a4bab743d6 | |||
60766789ab | |||
ca014f881c | |||
886eabe26c | |||
65b7167b5f | |||
abbdde851e | |||
54ac54aba6 | |||
184340f400 | |||
93dd6813e8 | |||
b689b08711 | |||
1479a097d6 | |||
b2be530f6b | |||
e4967ea46d | |||
3f28f18fb4 | |||
1b90e0f65e | |||
756ed649e6 | |||
fbc423e2a7 | |||
f04cdb6a13 |
5
.gitattributes
vendored
Normal file
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;
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -135,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);
|
||||||
NativeMethods.PostMessage(NativeMethods.HWND_BROADCAST, Program.WindowRestoreMessage, new UIntPtr((uint)lockingProcess.Id), IntPtr.Zero);
|
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
@@ -165,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>();
|
||||||
OnReadUnknownProperty = (obj, property, value) => false
|
|
||||||
};
|
|
||||||
|
|
||||||
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"));
|
||||||
@@ -16,6 +13,9 @@ namespace TweetDuck.Configuration{
|
|||||||
|
|
||||||
private bool _hardwareAcceleration = true;
|
private bool _hardwareAcceleration = true;
|
||||||
|
|
||||||
|
public bool EnableBrowserGCReload { get; set; } = true;
|
||||||
|
public int BrowserMemoryThreshold { get; set; } = 400;
|
||||||
|
|
||||||
// SPECIAL PROPERTIES
|
// SPECIAL PROPERTIES
|
||||||
|
|
||||||
public bool HardwareAcceleration{
|
public bool HardwareAcceleration{
|
||||||
@@ -31,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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,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,18 +1,16 @@
|
|||||||
using System;
|
using System;
|
||||||
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>{
|
private static readonly FileSerializer<UserConfig> Serializer = new FileSerializer<UserConfig>();
|
||||||
OnReadUnknownProperty = (obj, property, value) => false
|
|
||||||
};
|
|
||||||
|
|
||||||
static UserConfig(){
|
static UserConfig(){
|
||||||
Serializer.RegisterTypeConverter(typeof(WindowState), WindowState.Converter);
|
Serializer.RegisterTypeConverter(typeof(WindowState), WindowState.Converter);
|
||||||
@@ -36,13 +34,20 @@ 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;
|
||||||
private int _zoomLevel = 100;
|
private int _zoomLevel = 100;
|
||||||
private bool _muteNotifications;
|
private bool _muteNotifications;
|
||||||
|
|
||||||
@@ -69,17 +74,15 @@ 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;
|
||||||
public string CustomBrowserCSS { get; set; } = null;
|
public string CustomBrowserCSS { get; set; } = null;
|
||||||
public string CustomNotificationCSS { get; set; } = null;
|
public string CustomNotificationCSS { get; set; } = null;
|
||||||
|
|
||||||
public bool EnableBrowserGCReload { get; set; } = false;
|
|
||||||
public int BrowserMemoryThreshold { get; set; } = 350;
|
|
||||||
|
|
||||||
// SPECIAL PROPERTIES
|
// SPECIAL PROPERTIES
|
||||||
|
|
||||||
public bool IsCustomNotificationPositionSet => CustomNotificationPosition != ControlExtensions.InvisibleLocation;
|
public bool IsCustomNotificationPositionSet => CustomNotificationPosition != ControlExtensions.InvisibleLocation;
|
||||||
@@ -98,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,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);
|
||||||
@@ -152,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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -8,25 +8,26 @@ namespace TweetDuck.Core.Bridge{
|
|||||||
|
|
||||||
public static string GenerateScript(Environment environment){
|
public static string GenerateScript(Environment environment){
|
||||||
string Bool(bool value){
|
string Bool(bool value){
|
||||||
return value ? "true," : "false,";
|
return value ? "true;" : "false;";
|
||||||
}
|
}
|
||||||
|
|
||||||
StringBuilder build = new StringBuilder().Append("window.$TDX={");
|
StringBuilder build = new StringBuilder().Append("(function(x){");
|
||||||
|
|
||||||
build.Append("expandLinksOnHover:").Append(Bool(Program.UserConfig.ExpandLinksOnHover));
|
build.Append("x.expandLinksOnHover=").Append(Bool(Program.UserConfig.ExpandLinksOnHover));
|
||||||
|
|
||||||
if (environment == Environment.Browser){
|
if (environment == Environment.Browser){
|
||||||
build.Append("switchAccountSelectors:").Append(Bool(Program.UserConfig.SwitchAccountSelectors));
|
build.Append("x.switchAccountSelectors=").Append(Bool(Program.UserConfig.SwitchAccountSelectors));
|
||||||
build.Append("muteNotifications:").Append(Bool(Program.UserConfig.MuteNotifications));
|
build.Append("x.openSearchInFirstColumn=").Append(Bool(Program.UserConfig.OpenSearchInFirstColumn));
|
||||||
build.Append("hasCustomNotificationSound:").Append(Bool(Program.UserConfig.NotificationSoundPath.Length > 0));
|
build.Append("x.muteNotifications=").Append(Bool(Program.UserConfig.MuteNotifications));
|
||||||
build.Append("notificationMediaPreviews:").Append(Bool(Program.UserConfig.NotificationMediaPreviews));
|
build.Append("x.hasCustomNotificationSound=").Append(Bool(Program.UserConfig.NotificationSoundPath.Length > 0));
|
||||||
|
build.Append("x.notificationMediaPreviews=").Append(Bool(Program.UserConfig.NotificationMediaPreviews));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (environment == Environment.Notification){
|
if (environment == Environment.Notification){
|
||||||
build.Append("skipOnLinkClick:").Append(Bool(Program.UserConfig.NotificationSkipOnLinkClick));
|
build.Append("x.skipOnLinkClick=").Append(Bool(Program.UserConfig.NotificationSkipOnLinkClick));
|
||||||
}
|
}
|
||||||
|
|
||||||
return build.Append("}").ToString();
|
return build.Append("})(window.$TDX=window.$TDX||{})").ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,20 +1,45 @@
|
|||||||
using System.Windows.Forms;
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
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;
|
||||||
|
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[] LastHighlightedTweetImages = StringUtils.EmptyArray;
|
public static string LastHighlightedQuoteUrl = string.Empty;
|
||||||
|
private static string LastHighlightedTweetAuthors = string.Empty;
|
||||||
|
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 = string.Empty;
|
FontSize = NotificationHeadLayout = null;
|
||||||
LastHighlightedTweetImages = StringUtils.EmptyArray;
|
LastHighlightedTweetUrl = LastHighlightedQuoteUrl = LastHighlightedTweetAuthors = LastHighlightedTweetImages = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RestoreSessionData(IFrame frame){
|
||||||
|
if (SessionData.Count > 0){
|
||||||
|
StringBuilder build = new StringBuilder().Append("window.TD_SESSION={");
|
||||||
|
|
||||||
|
foreach(KeyValuePair<string, string> kvp in SessionData){
|
||||||
|
build.Append(kvp.Key).Append(":'").Append(kvp.Value.Replace("'", "\\'")).Append("',");
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptLoader.ExecuteScript(frame, build.Append("}").ToString(), "gen:session");
|
||||||
|
SessionData.Clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly FormBrowser form;
|
private readonly FormBrowser form;
|
||||||
@@ -25,31 +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 imageList){
|
|
||||||
form.InvokeAsyncSafe(() => {
|
form.InvokeAsyncSafe(() => {
|
||||||
LastHighlightedTweet = link;
|
LastHighlightedTweetUrl = tweetUrl;
|
||||||
LastHighlightedQuotedTweet = quotedLink;
|
LastHighlightedQuoteUrl = quoteUrl;
|
||||||
LastHighlightedTweetImages = imageList.Split(';');
|
LastHighlightedTweetAuthors = authors;
|
||||||
|
LastHighlightedTweetImages = imageList;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,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));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,6 +103,12 @@ namespace TweetDuck.Core.Bridge{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetSessionData(string key, string value){
|
||||||
|
form.InvokeSafe(() => { // do not use InvokeAsyncSafe, return only after invocation
|
||||||
|
SessionData.Add(key, value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public void LoadNextNotification(){
|
public void LoadNextNotification(){
|
||||||
notification.InvokeAsyncSafe(notification.FinishCurrentNotification);
|
notification.InvokeAsyncSafe(notification.FinishCurrentNotification);
|
||||||
}
|
}
|
||||||
@@ -88,16 +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, string username){
|
||||||
|
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){
|
||||||
@@ -115,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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,7 @@
|
|||||||
using System;
|
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;
|
|
||||||
|
|
||||||
namespace TweetDuck.Core.Controls{
|
namespace TweetDuck.Core.Controls{
|
||||||
static class ControlExtensions{
|
static class ControlExtensions{
|
||||||
@@ -67,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;
|
|
||||||
NativeMethods.SendMessage(button.Handle, NativeMethods.BCM_SETSHIELD, new UIntPtr(0), new IntPtr(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){
|
||||||
@@ -23,7 +24,7 @@ namespace TweetDuck.Core.Controls{
|
|||||||
|
|
||||||
Rectangle rect = e.ClipRectangle;
|
Rectangle rect = e.ClipRectangle;
|
||||||
rect.Width = (int)(rect.Width*((double)Value/Maximum));
|
rect.Width = (int)(rect.Width*((double)Value/Maximum));
|
||||||
e.Graphics.FillRectangle(brush,rect);
|
e.Graphics.FillRectangle(brush, rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool disposing){
|
protected override void Dispose(bool disposing){
|
||||||
|
8
Core/FormBrowser.Designer.cs
generated
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.Linq;
|
|
||||||
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.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,103 +40,68 @@ 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 AnalyticsManager analytics;
|
||||||
|
|
||||||
public FormBrowser(PluginManager pluginManager, UpdaterSettings updaterSettings){
|
public FormBrowser(UpdaterSettings updaterSettings){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
Text = Program.BrandName;
|
Text = Program.BrandName;
|
||||||
|
|
||||||
this.plugins = pluginManager;
|
this.plugins = new PluginManager(Program.PluginPath, Program.PluginConfigFilePath);
|
||||||
this.plugins.Reloaded += plugins_Reloaded;
|
this.plugins.Reloaded += plugins_Reloaded;
|
||||||
this.plugins.PluginChangedState += plugins_PluginChangedState;
|
this.plugins.Executed += plugins_Executed;
|
||||||
|
this.plugins.Reload();
|
||||||
this.contextMenu = ContextMenuBrowser.CreateMenu(this);
|
|
||||||
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 = new FormNotificationTweet(this, plugins);
|
||||||
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(),
|
|
||||||
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();
|
||||||
|
|
||||||
notificationScreenshotManager?.Dispose();
|
notificationScreenshotManager?.Dispose();
|
||||||
soundNotification?.Dispose();
|
soundNotification?.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;
|
||||||
|
|
||||||
RestoreWindow();
|
RestoreWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryBringToFront<T>() where T : Form{
|
|
||||||
T form = Application.OpenForms.OfType<T>().FirstOrDefault();
|
|
||||||
|
|
||||||
if (form != null){
|
|
||||||
form.BringToFront();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ShowChildForm(Form form){
|
private void ShowChildForm(Form form){
|
||||||
form.VisibleChanged += (sender, args) => form.MoveToCenter(this);
|
form.VisibleChanged += (sender, args) => form.MoveToCenter(this);
|
||||||
form.Show(this);
|
form.Show(this);
|
||||||
@@ -158,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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,68 +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);
|
|
||||||
ScriptLoader.ExecuteFile(e.Frame, "code.js");
|
|
||||||
ReinjectCustomCSS(Config.CustomBrowserCSS);
|
|
||||||
|
|
||||||
if (plugins.HasAnyPlugin(PluginEnvironment.Browser)){
|
|
||||||
ScriptLoader.ExecuteFile(e.Frame, PluginManager.PluginBrowserScriptFile);
|
|
||||||
ScriptLoader.ExecuteFile(e.Frame, PluginManager.PluginGlobalScriptFile);
|
|
||||||
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Browser, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
TweetDeckBridge.ResetStaticProperties();
|
|
||||||
|
|
||||||
if (Config.EnableBrowserGCReload){
|
|
||||||
memoryUsageTracker.Start(this, e.Browser, Config.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
|
||||||
@@ -304,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){
|
||||||
@@ -326,20 +217,61 @@ namespace TweetDuck.Core{
|
|||||||
ForceClose();
|
ForceClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void plugins_Reloaded(object sender, PluginErrorEventArgs e){
|
private void browser_PageLoaded(object sender, EventArgs e){
|
||||||
browser.GetBrowser().Reload();
|
if (Config.ShowDataCollectionNotification){
|
||||||
}
|
|
||||||
|
|
||||||
private void plugins_PluginChangedState(object sender, PluginChangedStateEventArgs e){
|
|
||||||
browser.ExecuteScriptAsync("window.TDPF_setPluginState", e.Plugin, e.IsEnabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updates_UpdateAccepted(object sender, UpdateAcceptedEventArgs e){
|
|
||||||
this.InvokeAsyncSafe(() => {
|
this.InvokeAsyncSafe(() => {
|
||||||
foreach(Form form in Application.OpenForms.Cast<Form>().Reverse()){
|
if (!Config.FirstRun && Config.AllowDataCollection){
|
||||||
if (form is FormSettings || form is FormPlugins || form is FormAbout){
|
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();
|
form.Close();
|
||||||
|
OpenSettings(typeof(TabSettingsFeedback));
|
||||||
|
};
|
||||||
|
|
||||||
|
form.AddActionControl(btnReviewSettings);
|
||||||
|
ShowChildForm(form);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Config.ShowDataCollectionNotification = false;
|
||||||
|
Config.Save();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void plugins_Reloaded(object sender, PluginErrorEventArgs e){
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLoaded){
|
||||||
|
ReloadToTweetDeck();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void plugins_Executed(object sender, PluginErrorEventArgs e){
|
||||||
|
if (e.HasErrors){
|
||||||
|
FormMessage.Error("Error Executing Plugins", "Failed to execute the following plugins:\n\n"+string.Join("\n\n", e.Errors), FormMessage.OK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updates_UpdateAccepted(object sender, UpdateEventArgs e){
|
||||||
|
this.InvokeAsyncSafe(() => {
|
||||||
|
FormManager.CloseAllDialogs();
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(Config.DismissedUpdate)){
|
||||||
|
Config.DismissedUpdate = null;
|
||||||
|
Config.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
updates.BeginUpdateDownload(this, e.UpdateInfo, update => {
|
updates.BeginUpdateDownload(this, e.UpdateInfo, update => {
|
||||||
@@ -352,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();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -379,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;
|
||||||
@@ -395,19 +327,22 @@ 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){
|
||||||
browser.ExecuteScriptAsync("TDGF_onMouseClickExtra", (m.WParam.ToInt32() >> 16) & 0xFFFF);
|
if (videoPlayer != null && videoPlayer.Running){
|
||||||
|
videoPlayer.Close();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
browser.OnMouseClickExtra(m.WParam);
|
||||||
|
TriggerAnalyticsEvent(AnalyticsFile.Event.BrowserExtraMouseButton);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
||||||
@@ -417,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($"gc&&gc();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));
|
||||||
}
|
}
|
||||||
@@ -442,46 +404,62 @@ namespace TweetDuck.Core{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void OpenSettings(Type startTab){
|
public void OpenSettings(Type startTab){
|
||||||
if (!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 (Config.EnableBrowserGCReload){
|
browser.RefreshMemoryTracker();
|
||||||
memoryUsageTracker.Start(this, browser.GetBrowser(), Config.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 (!TryBringToFront<FormAbout>()){
|
if (!FormManager.TryBringToFront<FormAbout>()){
|
||||||
|
TriggerAnalyticsEvent(AnalyticsFile.Event.OpenAbout);
|
||||||
ShowChildForm(new FormAbout());
|
ShowChildForm(new FormAbout());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OpenPlugins(){
|
public void OpenPlugins(){
|
||||||
if (!TryBringToFront<FormPlugins>()){
|
if (!FormManager.TryBringToFront<FormPlugins>()){
|
||||||
|
TriggerAnalyticsEvent(AnalyticsFile.Event.OpenPlugins);
|
||||||
ShowChildForm(new FormPlugins(plugins));
|
ShowChildForm(new FormPlugins(plugins));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -502,7 +480,50 @@ 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, string username){
|
||||||
|
if (string.IsNullOrEmpty(url)){
|
||||||
|
videoPlayer?.Close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoPlayer == null){
|
||||||
|
videoPlayer = new VideoPlayer(this);
|
||||||
|
|
||||||
|
videoPlayer.ProcessExited += (sender, args) => {
|
||||||
|
browser.HideVideoOverlay(true);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
videoPlayer.Launch(url, username);
|
||||||
|
TriggerAnalyticsEvent(AnalyticsFile.Event.VideoPlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ProcessBrowserKey(Keys key){
|
||||||
|
if (videoPlayer != null && videoPlayer.Running){
|
||||||
|
videoPlayer.SendKeyEvent(key);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowTweetDetail(string columnId, string chirpId, string fallbackUrl){
|
||||||
|
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){
|
||||||
@@ -511,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){
|
||||||
@@ -523,9 +545,5 @@ namespace TweetDuck.Core{
|
|||||||
toolTip.Show(text, this, position);
|
toolTip.Show(text, this, position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void TriggerTweetScreenshot(){
|
|
||||||
browser.ExecuteScriptAsync("TDGF_triggerScreenshot()");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
29
Core/FormManager.cs
Normal file
29
Core/FormManager.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using TweetDuck.Core.Other;
|
||||||
|
|
||||||
|
namespace TweetDuck.Core{
|
||||||
|
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{
|
||||||
|
T form = TryFind<T>();
|
||||||
|
|
||||||
|
if (form != null){
|
||||||
|
form.BringToFront();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CloseAllDialogs(){
|
||||||
|
foreach(Form form in Application.OpenForms.Cast<Form>().Reverse()){
|
||||||
|
if (form is FormSettings || form is FormPlugins || form is FormAbout || form is FormGuide){
|
||||||
|
form.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -6,67 +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[] 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);
|
|
||||||
lastHighlightedTweetImageList = TweetDeckBridge.LastHighlightedTweetImages;
|
|
||||||
|
|
||||||
if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){
|
if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){
|
||||||
|
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();
|
||||||
@@ -74,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), ImageQuality);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MenuSaveAllImages:
|
|
||||||
TwitterUtils.DownloadImages(lastHighlightedTweetImageList, 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:
|
||||||
@@ -113,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
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
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
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:
|
||||||
|
20
Core/Handling/KeyboardHandlerBrowser.cs
Normal file
20
Core/Handling/KeyboardHandlerBrowser.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Windows.Forms;
|
||||||
|
using CefSharp;
|
||||||
|
|
||||||
|
namespace TweetDuck.Core.Handling{
|
||||||
|
sealed class KeyboardHandlerBrowser : IKeyboardHandler{
|
||||||
|
private readonly FormBrowser form;
|
||||||
|
|
||||||
|
public KeyboardHandlerBrowser(FormBrowser form){
|
||||||
|
this.form = form;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IKeyboardHandler.OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut){
|
||||||
|
return type == KeyType.RawKeyDown && form.ProcessBrowserKey((Keys)windowsKeyCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IKeyboardHandler.OnKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -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
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,14 +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.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;
|
||||||
@@ -52,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;
|
||||||
@@ -104,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);
|
||||||
@@ -127,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);
|
||||||
@@ -151,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(){}
|
||||||
@@ -175,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);
|
||||||
}
|
}
|
||||||
@@ -192,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(){
|
||||||
@@ -219,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,17 +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 PluginScriptIdentifier = ScriptLoader.GetRootIdentifier(PluginManager.PluginNotificationScriptFile);
|
|
||||||
|
|
||||||
private static readonly string NotificationJS = ScriptLoader.LoadResource(NotificationScriptFile);
|
private static readonly string NotificationJS = ScriptLoader.LoadResource(NotificationScriptFile);
|
||||||
private static readonly string PluginJS = ScriptLoader.LoadResource(PluginManager.PluginNotificationScriptFile);
|
|
||||||
|
|
||||||
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;
|
||||||
@@ -34,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{
|
||||||
@@ -46,7 +44,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
prevDisplayTimer = Program.UserConfig.DisplayNotificationTimer;
|
prevDisplayTimer = Program.UserConfig.DisplayNotificationTimer;
|
||||||
prevFontSize = TweetNotification.FontSizeLevel;
|
prevFontSize = FontSizeLevel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -55,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;
|
||||||
@@ -67,7 +65,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
get{
|
get{
|
||||||
switch(Program.UserConfig.NotificationSize){
|
switch(Program.UserConfig.NotificationSize){
|
||||||
default:
|
default:
|
||||||
return BrowserUtils.Scale(118, 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;
|
||||||
@@ -75,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);
|
||||||
|
|
||||||
@@ -116,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)){
|
||||||
@@ -131,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){
|
||||||
@@ -151,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -169,12 +167,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
if (e.Frame.IsMain && NotificationJS != null && browser.Address != "about:blank"){
|
if (e.Frame.IsMain && NotificationJS != null && browser.Address != "about:blank"){
|
||||||
e.Frame.ExecuteJavaScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Environment.Notification));
|
e.Frame.ExecuteJavaScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Environment.Notification));
|
||||||
ScriptLoader.ExecuteScript(e.Frame, NotificationJS, NotificationScriptIdentifier);
|
ScriptLoader.ExecuteScript(e.Frame, NotificationJS, NotificationScriptIdentifier);
|
||||||
|
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Notification);
|
||||||
if (plugins.HasAnyPlugin(PluginEnvironment.Notification)){
|
|
||||||
ScriptLoader.ExecuteScript(e.Frame, PluginJS, PluginScriptIdentifier);
|
|
||||||
ScriptLoader.ExecuteFile(e.Frame, PluginManager.PluginGlobalScriptFile);
|
|
||||||
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Notification, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,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();
|
||||||
@@ -202,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;
|
||||||
|
|
||||||
@@ -259,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{
|
||||||
@@ -277,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);
|
||||||
@@ -287,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
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
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
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
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
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
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
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,120 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<root>
|
|
||||||
<!--
|
|
||||||
Microsoft ResX Schema
|
|
||||||
|
|
||||||
Version 2.0
|
|
||||||
|
|
||||||
The primary goals of this format is to allow a simple XML format
|
|
||||||
that is mostly human readable. The generation and parsing of the
|
|
||||||
various data types are done through the TypeConverter classes
|
|
||||||
associated with the data types.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
... ado.net/XML headers & schema ...
|
|
||||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
|
||||||
<resheader name="version">2.0</resheader>
|
|
||||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
|
||||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
|
||||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
|
||||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
|
||||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
|
||||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
|
||||||
</data>
|
|
||||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
|
||||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
|
||||||
<comment>This is a comment</comment>
|
|
||||||
</data>
|
|
||||||
|
|
||||||
There are any number of "resheader" rows that contain simple
|
|
||||||
name/value pairs.
|
|
||||||
|
|
||||||
Each data row contains a name, and value. The row also contains a
|
|
||||||
type or mimetype. Type corresponds to a .NET class that support
|
|
||||||
text/value conversion through the TypeConverter architecture.
|
|
||||||
Classes that don't support this are serialized and stored with the
|
|
||||||
mimetype set.
|
|
||||||
|
|
||||||
The mimetype is used for serialized objects, and tells the
|
|
||||||
ResXResourceReader how to depersist the object. This is currently not
|
|
||||||
extensible. For a given mimetype the value must be set accordingly:
|
|
||||||
|
|
||||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
|
||||||
that the ResXResourceWriter will generate, however the reader can
|
|
||||||
read any of the formats listed below.
|
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.binary.base64
|
|
||||||
value : The object must be serialized with
|
|
||||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
|
||||||
: and then encoded with base64 encoding.
|
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.soap.base64
|
|
||||||
value : The object must be serialized with
|
|
||||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
|
||||||
: and then encoded with base64 encoding.
|
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
|
||||||
value : The object must be serialized into a byte array
|
|
||||||
: using a System.ComponentModel.TypeConverter
|
|
||||||
: and then encoded with base64 encoding.
|
|
||||||
-->
|
|
||||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
|
||||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
|
||||||
<xsd:element name="root" msdata:IsDataSet="true">
|
|
||||||
<xsd:complexType>
|
|
||||||
<xsd:choice maxOccurs="unbounded">
|
|
||||||
<xsd:element name="metadata">
|
|
||||||
<xsd:complexType>
|
|
||||||
<xsd:sequence>
|
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
|
||||||
</xsd:sequence>
|
|
||||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
|
||||||
<xsd:attribute name="type" type="xsd:string" />
|
|
||||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
|
||||||
<xsd:attribute ref="xml:space" />
|
|
||||||
</xsd:complexType>
|
|
||||||
</xsd:element>
|
|
||||||
<xsd:element name="assembly">
|
|
||||||
<xsd:complexType>
|
|
||||||
<xsd:attribute name="alias" type="xsd:string" />
|
|
||||||
<xsd:attribute name="name" type="xsd:string" />
|
|
||||||
</xsd:complexType>
|
|
||||||
</xsd:element>
|
|
||||||
<xsd:element name="data">
|
|
||||||
<xsd:complexType>
|
|
||||||
<xsd:sequence>
|
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
|
||||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
|
||||||
</xsd:sequence>
|
|
||||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
|
||||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
|
||||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
|
||||||
<xsd:attribute ref="xml:space" />
|
|
||||||
</xsd:complexType>
|
|
||||||
</xsd:element>
|
|
||||||
<xsd:element name="resheader">
|
|
||||||
<xsd:complexType>
|
|
||||||
<xsd:sequence>
|
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
|
||||||
</xsd:sequence>
|
|
||||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
|
||||||
</xsd:complexType>
|
|
||||||
</xsd:element>
|
|
||||||
</xsd:choice>
|
|
||||||
</xsd:complexType>
|
|
||||||
</xsd:element>
|
|
||||||
</xsd:schema>
|
|
||||||
<resheader name="resmimetype">
|
|
||||||
<value>text/microsoft-resx</value>
|
|
||||||
</resheader>
|
|
||||||
<resheader name="version">
|
|
||||||
<value>2.0</value>
|
|
||||||
</resheader>
|
|
||||||
<resheader name="reader">
|
|
||||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
|
||||||
</resheader>
|
|
||||||
<resheader name="writer">
|
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
|
||||||
</resheader>
|
|
||||||
</root>
|
|
@@ -1,8 +1,9 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using CefSharp;
|
using CefSharp;
|
||||||
|
using TweetDuck.Core.Utils;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Utils{
|
namespace TweetDuck.Core.Other.Management{
|
||||||
static class BrowserProcesses{
|
static class BrowserProcesses{
|
||||||
private static readonly Dictionary<int, int> PIDs = new Dictionary<int, int>();
|
private static readonly Dictionary<int, int> PIDs = new Dictionary<int, int>();
|
||||||
|
|
@@ -1,18 +1,19 @@
|
|||||||
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.Utils{
|
namespace TweetDuck.Core.Other.Management{
|
||||||
sealed class MemoryUsageTracker : IDisposable{
|
sealed class MemoryUsageTracker : IDisposable{
|
||||||
private const int IntervalMemoryCheck = 60000*30; // 30 minutes
|
private const int IntervalMemoryCheck = 60000*30; // 30 minutes
|
||||||
private const int IntervalCleanupAttempt = 60000*5; // 5 minutes
|
private const int IntervalCleanupAttempt = 60000*5; // 5 minutes
|
||||||
|
|
||||||
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.Utils{
|
|||||||
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.Utils{
|
|||||||
|
|
||||||
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));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
181
Core/Other/Management/VideoPlayer.cs
Normal file
181
Core/Other/Management/VideoPlayer.cs
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using TweetDuck.Core.Controls;
|
||||||
|
using TweetDuck.Core.Utils;
|
||||||
|
using TweetLib.Communication;
|
||||||
|
|
||||||
|
namespace TweetDuck.Core.Other.Management{
|
||||||
|
sealed class VideoPlayer : IDisposable{
|
||||||
|
public bool Running{
|
||||||
|
get{
|
||||||
|
if (currentProcess == null){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentProcess.Refresh();
|
||||||
|
return !currentProcess.HasExited;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler ProcessExited;
|
||||||
|
|
||||||
|
private readonly Form owner;
|
||||||
|
private string lastUrl;
|
||||||
|
private string lastUsername;
|
||||||
|
|
||||||
|
private Process currentProcess;
|
||||||
|
private DuplexPipe.Server currentPipe;
|
||||||
|
private bool isClosing;
|
||||||
|
|
||||||
|
public VideoPlayer(Form owner){
|
||||||
|
this.owner = owner;
|
||||||
|
this.owner.FormClosing += owner_FormClosing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Launch(string url, string username){
|
||||||
|
if (Running){
|
||||||
|
Destroy();
|
||||||
|
isClosing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastUrl = url;
|
||||||
|
lastUsername = username;
|
||||||
|
|
||||||
|
try{
|
||||||
|
currentPipe = DuplexPipe.CreateServer();
|
||||||
|
currentPipe.DataIn += currentPipe_DataIn;
|
||||||
|
|
||||||
|
if ((currentProcess = Process.Start(new ProcessStartInfo{
|
||||||
|
FileName = Path.Combine(Program.ProgramPath, "TweetDuck.Video.exe"),
|
||||||
|
Arguments = $"{owner.Handle} {Program.UserConfig.VideoPlayerVolume} \"{url}\" \"{currentPipe.GenerateToken()}\"",
|
||||||
|
UseShellExecute = false,
|
||||||
|
RedirectStandardOutput = true
|
||||||
|
})) != null){
|
||||||
|
currentProcess.EnableRaisingEvents = true;
|
||||||
|
currentProcess.Exited += process_Exited;
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
currentProcess.BeginOutputReadLine();
|
||||||
|
currentProcess.OutputDataReceived += (sender, args) => Debug.WriteLine("VideoPlayer: "+args.Data);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
currentPipe.DisposeToken();
|
||||||
|
}catch(Exception e){
|
||||||
|
Program.Reporter.HandleException("Video Playback Error", "Error launching video player.", true, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SendKeyEvent(Keys key){
|
||||||
|
currentPipe?.Write("key", ((int)key).ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void currentPipe_DataIn(object sender, DuplexPipe.PipeReadEventArgs e){
|
||||||
|
owner.InvokeSafe(() => {
|
||||||
|
switch(e.Key){
|
||||||
|
case "vol":
|
||||||
|
if (int.TryParse(e.Data, out int volume) && volume != Program.UserConfig.VideoPlayerVolume){
|
||||||
|
Program.UserConfig.VideoPlayerVolume = volume;
|
||||||
|
Program.UserConfig.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "download":
|
||||||
|
TwitterUtils.DownloadVideo(lastUrl, lastUsername);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "rip":
|
||||||
|
currentPipe.Dispose();
|
||||||
|
currentPipe = null;
|
||||||
|
|
||||||
|
currentProcess.Dispose();
|
||||||
|
currentProcess = null;
|
||||||
|
|
||||||
|
isClosing = false;
|
||||||
|
TriggerProcessExitEventUnsafe();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Close(){
|
||||||
|
if (currentProcess != null){
|
||||||
|
if (isClosing){
|
||||||
|
Destroy();
|
||||||
|
isClosing = false;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
isClosing = true;
|
||||||
|
currentProcess.Exited -= process_Exited;
|
||||||
|
currentPipe.Write("die");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose(){
|
||||||
|
ProcessExited = null;
|
||||||
|
|
||||||
|
isClosing = true;
|
||||||
|
Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Destroy(){
|
||||||
|
if (currentProcess != null){
|
||||||
|
try{
|
||||||
|
currentProcess.Kill();
|
||||||
|
}catch{
|
||||||
|
// kill me instead then
|
||||||
|
}
|
||||||
|
|
||||||
|
currentProcess.Dispose();
|
||||||
|
currentProcess = null;
|
||||||
|
|
||||||
|
currentPipe.Dispose();
|
||||||
|
currentPipe = null;
|
||||||
|
|
||||||
|
TriggerProcessExitEventUnsafe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void owner_FormClosing(object sender, FormClosingEventArgs e){
|
||||||
|
if (currentProcess != null){
|
||||||
|
currentProcess.Exited -= process_Exited;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void process_Exited(object sender, EventArgs e){
|
||||||
|
int exitCode = currentProcess.ExitCode;
|
||||||
|
|
||||||
|
currentProcess.Dispose();
|
||||||
|
currentProcess = null;
|
||||||
|
|
||||||
|
currentPipe.Dispose();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TriggerProcessExitEventUnsafe(){
|
||||||
|
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
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
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; }
|
||||||
}
|
}
|
||||||
|
22
Core/Other/Settings/TabSettingsAdvanced.Designer.cs
generated
22
Core/Other/Settings/TabSettingsAdvanced.Designer.cs
generated
@@ -68,7 +68,7 @@
|
|||||||
this.checkHardwareAcceleration.Size = new System.Drawing.Size(134, 17);
|
this.checkHardwareAcceleration.Size = new System.Drawing.Size(134, 17);
|
||||||
this.checkHardwareAcceleration.TabIndex = 0;
|
this.checkHardwareAcceleration.TabIndex = 0;
|
||||||
this.checkHardwareAcceleration.Text = "Hardware Acceleration";
|
this.checkHardwareAcceleration.Text = "Hardware Acceleration";
|
||||||
this.toolTip.SetToolTip(this.checkHardwareAcceleration, "Uses your graphics card to improve performance.\r\nDisable if you experience issues with rendering.");
|
this.toolTip.SetToolTip(this.checkHardwareAcceleration, "Uses graphics card to improve performance. Disable if you experience\r\nvisual glitches. This option will not be exported in a profile.");
|
||||||
this.checkHardwareAcceleration.UseVisualStyleBackColor = true;
|
this.checkHardwareAcceleration.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
// btnEditCefArgs
|
// btnEditCefArgs
|
||||||
@@ -142,26 +142,14 @@
|
|||||||
0,
|
0,
|
||||||
0});
|
0});
|
||||||
this.numMemoryThreshold.Location = new System.Drawing.Point(202, 82);
|
this.numMemoryThreshold.Location = new System.Drawing.Point(202, 82);
|
||||||
this.numMemoryThreshold.Maximum = new decimal(new int[] {
|
this.numMemoryThreshold.Maximum = 2000;
|
||||||
3000,
|
this.numMemoryThreshold.Minimum = 200;
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0});
|
|
||||||
this.numMemoryThreshold.Minimum = new decimal(new int[] {
|
|
||||||
200,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0});
|
|
||||||
this.numMemoryThreshold.Name = "numMemoryThreshold";
|
this.numMemoryThreshold.Name = "numMemoryThreshold";
|
||||||
this.numMemoryThreshold.Size = new System.Drawing.Size(97, 20);
|
this.numMemoryThreshold.Size = new System.Drawing.Size(97, 20);
|
||||||
this.numMemoryThreshold.TabIndex = 4;
|
this.numMemoryThreshold.TabIndex = 4;
|
||||||
this.numMemoryThreshold.TextSuffix = " MB";
|
this.numMemoryThreshold.TextSuffix = " MB";
|
||||||
this.toolTip.SetToolTip(this.numMemoryThreshold, "Minimum amount of memory usage by the browser process to trigger the cleanup.\r\nThis is not a limit, the usage is allowed to exceed this value.");
|
this.toolTip.SetToolTip(this.numMemoryThreshold, "Minimum amount of memory usage by the browser process to trigger the cleanup.\r\nThis is not a limit, the usage is allowed to exceed this value.");
|
||||||
this.numMemoryThreshold.Value = new decimal(new int[] {
|
this.numMemoryThreshold.Value = 400;
|
||||||
350,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0});
|
|
||||||
//
|
//
|
||||||
// checkBrowserGCReload
|
// checkBrowserGCReload
|
||||||
//
|
//
|
||||||
@@ -172,7 +160,7 @@
|
|||||||
this.checkBrowserGCReload.Size = new System.Drawing.Size(190, 17);
|
this.checkBrowserGCReload.Size = new System.Drawing.Size(190, 17);
|
||||||
this.checkBrowserGCReload.TabIndex = 3;
|
this.checkBrowserGCReload.TabIndex = 3;
|
||||||
this.checkBrowserGCReload.Text = "Enable Browser Memory Threshold";
|
this.checkBrowserGCReload.Text = "Enable Browser Memory Threshold";
|
||||||
this.toolTip.SetToolTip(this.checkBrowserGCReload, "Automatically reloads TweetDeck to save memory. This option only works\r\nif the browser is in a \'default state\', i.e. all modals and drawers are closed,\r\nand all columns are scrolled to top. Some notifications may be lost.");
|
this.toolTip.SetToolTip(this.checkBrowserGCReload, "Automatically reloads TweetDeck to save memory. This option only works if\r\nthe browser is in a \'default state\', i.e. all modals and drawers are closed, and\r\nall columns are scrolled to top. This option will not be exported in a profile.");
|
||||||
this.checkBrowserGCReload.UseVisualStyleBackColor = true;
|
this.checkBrowserGCReload.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
// labelApp
|
// labelApp
|
||||||
|
@@ -7,7 +7,9 @@ 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 readonly Action<string> reinjectBrowserCSS;
|
private readonly Action<string> reinjectBrowserCSS;
|
||||||
|
|
||||||
public TabSettingsAdvanced(Action<string> reinjectBrowserCSS){
|
public TabSettingsAdvanced(Action<string> reinjectBrowserCSS){
|
||||||
@@ -16,25 +18,21 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
this.reinjectBrowserCSS = reinjectBrowserCSS;
|
this.reinjectBrowserCSS = reinjectBrowserCSS;
|
||||||
|
|
||||||
if (SystemConfig.IsHardwareAccelerationSupported){
|
if (SystemConfig.IsHardwareAccelerationSupported){
|
||||||
checkHardwareAcceleration.Checked = Program.SystemConfig.HardwareAcceleration;
|
checkHardwareAcceleration.Checked = SysConfig.HardwareAcceleration;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
checkHardwareAcceleration.Enabled = false;
|
checkHardwareAcceleration.Enabled = false;
|
||||||
checkHardwareAcceleration.Checked = false;
|
checkHardwareAcceleration.Checked = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
checkBrowserGCReload.Checked = Config.EnableBrowserGCReload;
|
checkBrowserGCReload.Checked = SysConfig.EnableBrowserGCReload;
|
||||||
numMemoryThreshold.Enabled = checkBrowserGCReload.Checked;
|
numMemoryThreshold.Enabled = checkBrowserGCReload.Checked;
|
||||||
numMemoryThreshold.SetValueSafe(Config.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(){
|
||||||
@@ -53,6 +51,10 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
btnRestartArgs.Click += btnRestartArgs_Click;
|
btnRestartArgs.Click += btnRestartArgs_Click;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void OnClosing(){
|
||||||
|
SysConfig.Save();
|
||||||
|
}
|
||||||
|
|
||||||
private void btnClearCache_Click(object sender, EventArgs e){
|
private void btnClearCache_Click(object sender, EventArgs e){
|
||||||
btnClearCache.Enabled = false;
|
btnClearCache.Enabled = false;
|
||||||
BrowserCache.SetClearOnExit();
|
BrowserCache.SetClearOnExit();
|
||||||
@@ -60,18 +62,17 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void checkHardwareAcceleration_CheckedChanged(object sender, EventArgs e){
|
private void checkHardwareAcceleration_CheckedChanged(object sender, EventArgs e){
|
||||||
Program.SystemConfig.HardwareAcceleration = checkHardwareAcceleration.Checked;
|
SysConfig.HardwareAcceleration = checkHardwareAcceleration.Checked;
|
||||||
Program.SystemConfig.Save();
|
PromptRestart(); // calls OnClosing
|
||||||
PromptRestart();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkBrowserGCReload_CheckedChanged(object sender, EventArgs e){
|
private void checkBrowserGCReload_CheckedChanged(object sender, EventArgs e){
|
||||||
Config.EnableBrowserGCReload = checkBrowserGCReload.Checked;
|
SysConfig.EnableBrowserGCReload = checkBrowserGCReload.Checked;
|
||||||
numMemoryThreshold.Enabled = checkBrowserGCReload.Checked;
|
numMemoryThreshold.Enabled = checkBrowserGCReload.Checked;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void numMemoryThreshold_ValueChanged(object sender, EventArgs e){
|
private void numMemoryThreshold_ValueChanged(object sender, EventArgs e){
|
||||||
Config.BrowserMemoryThreshold = (int)numMemoryThreshold.Value;
|
SysConfig.BrowserMemoryThreshold = (int)numMemoryThreshold.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void btnEditCefArgs_Click(object sender, EventArgs e){
|
private void btnEditCefArgs_Click(object sender, EventArgs e){
|
||||||
@@ -82,7 +83,7 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
};
|
};
|
||||||
|
|
||||||
form.FormClosed += (sender2, args2) => {
|
form.FormClosed += (sender2, args2) => {
|
||||||
NativeMethods.SetFormDisabled(ParentForm, false);
|
RestoreParentForm();
|
||||||
|
|
||||||
if (form.DialogResult == DialogResult.OK){
|
if (form.DialogResult == DialogResult.OK){
|
||||||
Config.CustomCefArgs = form.CefArgs;
|
Config.CustomCefArgs = form.CefArgs;
|
||||||
@@ -104,7 +105,7 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
};
|
};
|
||||||
|
|
||||||
form.FormClosed += (sender2, args2) => {
|
form.FormClosed += (sender2, args2) => {
|
||||||
NativeMethods.SetFormDisabled(ParentForm, false);
|
RestoreParentForm();
|
||||||
|
|
||||||
if (form.DialogResult == DialogResult.OK){
|
if (form.DialogResult == DialogResult.OK){
|
||||||
Config.CustomBrowserCSS = form.BrowserCSS;
|
Config.CustomBrowserCSS = form.BrowserCSS;
|
||||||
@@ -138,5 +139,11 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void RestoreParentForm(){
|
||||||
|
if (ParentForm != null){ // when the parent is closed first, ParentForm is null in FormClosed event
|
||||||
|
NativeMethods.SetFormDisabled(ParentForm, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
158
Core/Other/Settings/TabSettingsFeedback.Designer.cs
generated
Normal file
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
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
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
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();
|
||||||
}
|
}
|
||||||
@@ -34,9 +42,10 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
|
|
||||||
private void tbCustomSound_TextChanged(object sender, EventArgs e){
|
private void tbCustomSound_TextChanged(object sender, EventArgs e){
|
||||||
bool isEmpty = string.IsNullOrEmpty(tbCustomSound.Text);
|
bool isEmpty = string.IsNullOrEmpty(tbCustomSound.Text);
|
||||||
tbCustomSound.ForeColor = isEmpty || File.Exists(tbCustomSound.Text) ? SystemColors.WindowText : Color.Maroon;
|
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
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
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
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){
|
||||||
|
@@ -17,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;
|
||||||
@@ -53,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);
|
||||||
|
|
||||||
@@ -66,15 +71,6 @@ namespace TweetDuck.Core.Utils{
|
|||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop);
|
private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop);
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
|
||||||
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, UIntPtr wParam, IntPtr lParam);
|
|
||||||
|
|
||||||
[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")]
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
public static extern bool ShowScrollBar(IntPtr hWnd, int wBar, bool bShow);
|
public static extern bool ShowScrollBar(IntPtr hWnd, int wBar, bool bShow);
|
||||||
@@ -107,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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
using CefSharp;
|
using CefSharp;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Core.Other;
|
using TweetDuck.Core.Other;
|
||||||
@@ -13,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
|
||||||
}
|
}
|
||||||
@@ -32,16 +37,16 @@ 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){
|
if (result != url || url.Contains("//pbs.twimg.com/media/")){
|
||||||
result += ":orig";
|
result += ":orig";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,23 +57,38 @@ namespace TweetDuck.Core.Utils{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void DownloadImage(string url, ImageQuality quality){
|
public static string GetImageFileName(string url){
|
||||||
DownloadImages(new string[]{ url }, quality);
|
return BrowserUtils.GetFileNameFromUrl(ExtractMediaBaseLink(url));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void DownloadImages(string[] urls, ImageQuality quality){
|
public static void DownloadImage(string url, string username, ImageQuality quality){
|
||||||
|
DownloadImages(new string[]{ url }, username, quality);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DownloadImages(string[] urls, string username, ImageQuality quality){
|
||||||
if (urls.Length == 0){
|
if (urls.Length == 0){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
string file = BrowserUtils.GetFileNameFromUrl(ExtractImageBaseLink(urls[0]));
|
string firstImageLink = GetMediaLink(urls[0], quality);
|
||||||
|
int qualityIndex = firstImageLink.IndexOf(':', firstImageLink.LastIndexOf('/'));
|
||||||
|
|
||||||
|
string file = GetImageFileName(firstImageLink);
|
||||||
string ext = Path.GetExtension(file); // includes dot
|
string ext = Path.GetExtension(file); // includes dot
|
||||||
|
|
||||||
|
string[] fileNameParts = qualityIndex == -1 ? new string[]{
|
||||||
|
Path.ChangeExtension(file, null)
|
||||||
|
} : new string[]{
|
||||||
|
username,
|
||||||
|
Path.ChangeExtension(file, null),
|
||||||
|
firstImageLink.Substring(qualityIndex+1)
|
||||||
|
};
|
||||||
|
|
||||||
using(SaveFileDialog dialog = new SaveFileDialog{
|
using(SaveFileDialog dialog = new SaveFileDialog{
|
||||||
AutoUpgradeEnabled = true,
|
AutoUpgradeEnabled = true,
|
||||||
OverwritePrompt = urls.Length == 1,
|
OverwritePrompt = urls.Length == 1,
|
||||||
Title = "Save image",
|
Title = "Save image",
|
||||||
FileName = file,
|
FileName = $"{string.Join(" ", fileNameParts.Where(part => part.Length > 0))}{ext}",
|
||||||
Filter = (urls.Length == 1 ? "Image" : "Images")+(string.IsNullOrEmpty(ext) ? " (unknown)|*.*" : $" (*{ext})|*{ext}")
|
Filter = (urls.Length == 1 ? "Image" : "Images")+(string.IsNullOrEmpty(ext) ? " (unknown)|*.*" : $" (*{ext})|*{ext}")
|
||||||
}){
|
}){
|
||||||
if (dialog.ShowDialog() == DialogResult.OK){
|
if (dialog.ShowDialog() == DialogResult.OK){
|
||||||
@@ -77,18 +97,37 @@ namespace TweetDuck.Core.Utils{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (urls.Length == 1){
|
if (urls.Length == 1){
|
||||||
BrowserUtils.DownloadFileAsync(GetImageLink(urls[0], quality), dialog.FileName, null, OnFailure);
|
BrowserUtils.DownloadFileAsync(firstImageLink, dialog.FileName, null, OnFailure);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
string pathBase = Path.ChangeExtension(dialog.FileName, null);
|
string pathBase = Path.ChangeExtension(dialog.FileName, null);
|
||||||
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>{
|
||||||
@@ -12,8 +13,8 @@ namespace TweetDuck.Data.Serialization{
|
|||||||
|
|
||||||
private static readonly ITypeConverter BasicSerializerObj = new BasicTypeConverter();
|
private static readonly ITypeConverter BasicSerializerObj = new BasicTypeConverter();
|
||||||
|
|
||||||
public delegate bool OnReadUnknownPropertyHandler(T obj, string property, string value);
|
public delegate void HandleUnknownPropertiesHandler(T obj, Dictionary<string, string> data);
|
||||||
public OnReadUnknownPropertyHandler OnReadUnknownProperty { get; set; }
|
public HandleUnknownPropertiesHandler HandleUnknownProperties { get; set; }
|
||||||
|
|
||||||
private readonly Dictionary<string, PropertyInfo> props;
|
private readonly Dictionary<string, PropertyInfo> props;
|
||||||
private readonly Dictionary<Type, ITypeConverter> converters;
|
private readonly Dictionary<Type, ITypeConverter> converters;
|
||||||
@@ -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;
|
||||||
@@ -51,8 +54,14 @@ namespace TweetDuck.Data.Serialization{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void Read(string file, T obj){
|
public void Read(string file, T obj){
|
||||||
|
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.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,14 +87,29 @@ namespace TweetDuck.Data.Serialization{
|
|||||||
throw new SerializationException($"Invalid file format, cannot convert value: {value} (property: {property})");
|
throw new SerializationException($"Invalid file format, cannot convert value: {value} (property: {property})");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!(OnReadUnknownProperty?.Invoke(obj, property, value) ?? false)){
|
else{
|
||||||
throw new SerializationException($"Invalid file format, unknown property: {property}+");
|
unknownProperties[property] = value;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class BasicTypeConverter : ITypeConverter{
|
if (unknownProperties.Count > 0){
|
||||||
|
HandleUnknownProperties?.Invoke(obj, unknownProperties);
|
||||||
|
|
||||||
|
if (unknownProperties.Count > 0){
|
||||||
|
throw new SerializationException($"Invalid file format, unknown properties: {string.Join(", ", unknownProperties.Keys)}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -17,7 +17,20 @@ namespace TweetDuck.Plugins.Enums{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetScriptFile(this PluginEnvironment environment){
|
public static bool IncludesDisabledPlugins(this PluginEnvironment environment){
|
||||||
|
return environment == PluginEnvironment.Browser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetScriptIdentifier(this PluginEnvironment environment){
|
||||||
|
switch(environment){
|
||||||
|
case PluginEnvironment.None: return "root:plugins";
|
||||||
|
case PluginEnvironment.Browser: return "root:plugins.browser";
|
||||||
|
case PluginEnvironment.Notification: return "root:plugins.notification";
|
||||||
|
default: return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetPluginScriptFile(this PluginEnvironment environment){
|
||||||
switch(environment){
|
switch(environment){
|
||||||
case PluginEnvironment.Browser: return "browser.js";
|
case PluginEnvironment.Browser: return "browser.js";
|
||||||
case PluginEnvironment.Notification: return "notification.js";
|
case PluginEnvironment.Notification: return "notification.js";
|
||||||
@@ -25,7 +38,7 @@ namespace TweetDuck.Plugins.Enums{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetScriptVariables(this PluginEnvironment environment){
|
public static string GetPluginScriptVariables(this PluginEnvironment environment){
|
||||||
switch(environment){
|
switch(environment){
|
||||||
case PluginEnvironment.Browser: return "$,$TD,$TDP,TD";
|
case PluginEnvironment.Browser: return "$,$TD,$TDP,TD";
|
||||||
case PluginEnvironment.Notification: return "$TD,$TDP";
|
case PluginEnvironment.Notification: return "$TD,$TDP";
|
||||||
|
@@ -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,14 +74,14 @@ 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetScriptPath(PluginEnvironment environment){
|
public string GetScriptPath(PluginEnvironment environment){
|
||||||
if (Environments.HasFlag(environment)){
|
if (Environments.HasFlag(environment)){
|
||||||
string file = environment.GetScriptFile();
|
string file = environment.GetPluginScriptFile();
|
||||||
return file != null ? Path.Combine(pathRoot, file) : string.Empty;
|
return file != null ? Path.Combine(pathRoot, file) : string.Empty;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
@@ -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.GetScriptFile(), 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,11 +9,11 @@ using TweetDuck.Resources;
|
|||||||
|
|
||||||
namespace TweetDuck.Plugins{
|
namespace TweetDuck.Plugins{
|
||||||
sealed class PluginManager{
|
sealed class PluginManager{
|
||||||
public const string PluginBrowserScriptFile = "plugins.browser.js";
|
private static readonly Dictionary<PluginEnvironment, string> PluginSetupScripts = new Dictionary<PluginEnvironment, string>(4){
|
||||||
public const string PluginNotificationScriptFile = "plugins.notification.js";
|
{ PluginEnvironment.None, ScriptLoader.LoadResource("plugins.js") },
|
||||||
public const string PluginGlobalScriptFile = "plugins.js";
|
{ PluginEnvironment.Browser, ScriptLoader.LoadResource("plugins.browser.js") },
|
||||||
|
{ PluginEnvironment.Notification, ScriptLoader.LoadResource("plugins.notification.js") }
|
||||||
private const int InvalidToken = 0;
|
};
|
||||||
|
|
||||||
public string PathOfficialPlugins => Path.Combine(rootPath, "official");
|
public string PathOfficialPlugins => Path.Combine(rootPath, "official");
|
||||||
public string PathCustomPlugins => Path.Combine(rootPath, "user");
|
public string PathCustomPlugins => Path.Combine(rootPath, "user");
|
||||||
@@ -33,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;
|
||||||
@@ -43,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){
|
||||||
@@ -73,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){
|
||||||
@@ -81,23 +83,48 @@ 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, bool includeDisabled){
|
public void ExecutePlugins(IFrame frame, PluginEnvironment environment){
|
||||||
|
if (!HasAnyPlugin(environment)){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptLoader.ExecuteScript(frame, PluginSetupScripts[environment], environment.GetScriptIdentifier());
|
||||||
|
ScriptLoader.ExecuteScript(frame, PluginSetupScripts[PluginEnvironment.None], PluginEnvironment.None.GetScriptIdentifier());
|
||||||
|
|
||||||
|
bool includeDisabled = environment.IncludesDisabledPlugins();
|
||||||
|
|
||||||
if (includeDisabled){
|
if (includeDisabled){
|
||||||
ScriptLoader.ExecuteScript(frame, PluginScriptGenerator.GenerateConfig(Config), "gen:pluginconfig");
|
ScriptLoader.ExecuteScript(frame, PluginScriptGenerator.GenerateConfig(Config), "gen:pluginconfig");
|
||||||
}
|
}
|
||||||
@@ -106,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;
|
||||||
|
|
||||||
@@ -117,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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -8,7 +8,7 @@ namespace TweetDuck.Plugins{
|
|||||||
|
|
||||||
public static string GeneratePlugin(string pluginIdentifier, string pluginContents, int pluginToken, PluginEnvironment environment){
|
public static string GeneratePlugin(string pluginIdentifier, string pluginContents, int pluginToken, PluginEnvironment environment){
|
||||||
return PluginGen
|
return PluginGen
|
||||||
.Replace("%params", environment.GetScriptVariables())
|
.Replace("%params", environment.GetPluginScriptVariables())
|
||||||
.Replace("%id", pluginIdentifier)
|
.Replace("%id", pluginIdentifier)
|
||||||
.Replace("%token", pluginToken.ToString())
|
.Replace("%token", pluginToken.ToString())
|
||||||
.Replace("%contents", pluginContents);
|
.Replace("%contents", pluginContents);
|
||||||
|
59
Program.cs
59
Program.cs
@@ -1,4 +1,4 @@
|
|||||||
using CefSharp;
|
using CefSharp;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
@@ -13,8 +13,6 @@ using TweetDuck.Core.Other;
|
|||||||
using TweetDuck.Core.Other.Settings.Export;
|
using TweetDuck.Core.Other.Settings.Export;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetDuck.Data;
|
using TweetDuck.Data;
|
||||||
using TweetDuck.Plugins;
|
|
||||||
using TweetDuck.Plugins.Events;
|
|
||||||
using TweetDuck.Updates;
|
using TweetDuck.Updates;
|
||||||
|
|
||||||
namespace TweetDuck{
|
namespace TweetDuck{
|
||||||
@@ -22,10 +20,8 @@ namespace TweetDuck{
|
|||||||
public const string BrandName = "TweetDuck";
|
public const string BrandName = "TweetDuck";
|
||||||
public const string Website = "https://tweetduck.chylex.com";
|
public const string Website = "https://tweetduck.chylex.com";
|
||||||
|
|
||||||
public const string VersionTag = "1.8.4";
|
public const string VersionTag = "1.11.1";
|
||||||
public const string VersionFull = "1.8.4";
|
|
||||||
|
|
||||||
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;
|
||||||
@@ -40,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");
|
||||||
@@ -55,13 +52,15 @@ 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;
|
||||||
CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture;
|
CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture;
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
CultureInfo.DefaultThreadCurrentUICulture = Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-us"); // force english exceptions
|
||||||
|
#endif
|
||||||
|
|
||||||
Reporter = new Reporter(ErrorLogFilePath);
|
Reporter = new Reporter(ErrorLogFilePath);
|
||||||
Reporter.SetupUnhandledExceptionHandler("TweetDuck Has Failed :(");
|
Reporter.SetupUnhandledExceptionHandler("TweetDuck Has Failed :(");
|
||||||
}
|
}
|
||||||
@@ -70,6 +69,7 @@ namespace TweetDuck{
|
|||||||
private static void Main(){
|
private static void Main(){
|
||||||
Application.EnableVisualStyles();
|
Application.EnableVisualStyles();
|
||||||
Application.SetCompatibleTextRenderingDefault(false);
|
Application.SetCompatibleTextRenderingDefault(false);
|
||||||
|
Cef.EnableHighDPISupport();
|
||||||
|
|
||||||
WindowRestoreMessage = NativeMethods.RegisterWindowMessage("TweetDuckRestore");
|
WindowRestoreMessage = NativeMethods.RegisterWindowMessage("TweetDuckRestore");
|
||||||
SubProcessMessage = NativeMethods.RegisterWindowMessage("TweetDuckSubProcess");
|
SubProcessMessage = NativeMethods.RegisterWindowMessage("TweetDuckSubProcess");
|
||||||
@@ -115,12 +115,15 @@ namespace TweetDuck{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ReloadConfig();
|
UserConfig = UserConfig.Load(UserConfigFilePath);
|
||||||
SystemConfig = SystemConfig.Load(SystemConfigFilePath);
|
SystemConfig = SystemConfig.Load(SystemConfigFilePath);
|
||||||
|
|
||||||
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);
|
||||||
@@ -143,23 +146,17 @@ 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();
|
||||||
|
|
||||||
PluginManager plugins = new PluginManager(PluginPath, PluginConfigFilePath);
|
|
||||||
plugins.Reloaded += plugins_Reloaded;
|
|
||||||
plugins.Executed += plugins_Executed;
|
|
||||||
plugins.Reload();
|
|
||||||
|
|
||||||
UpdaterSettings updaterSettings = new UpdaterSettings{
|
UpdaterSettings updaterSettings = new UpdaterSettings{
|
||||||
AllowPreReleases = Arguments.HasFlag(Arguments.ArgDebugUpdates),
|
AllowPreReleases = Arguments.HasFlag(Arguments.ArgDebugUpdates),
|
||||||
DismissedUpdate = UserConfig.DismissedUpdate,
|
DismissedUpdate = UserConfig.DismissedUpdate,
|
||||||
InstallerDownloadFolder = InstallerPath
|
InstallerDownloadFolder = InstallerPath
|
||||||
};
|
};
|
||||||
|
|
||||||
FormBrowser mainForm = new FormBrowser(plugins, updaterSettings);
|
FormBrowser mainForm = new FormBrowser(updaterSettings);
|
||||||
Application.Run(mainForm);
|
Application.Run(mainForm);
|
||||||
|
|
||||||
if (mainForm.UpdateInstallerPath != null){
|
if (mainForm.UpdateInstallerPath != null){
|
||||||
@@ -169,20 +166,12 @@ 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 void plugins_Reloaded(object sender, PluginErrorEventArgs e){
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void plugins_Executed(object sender, PluginErrorEventArgs e){
|
|
||||||
if (e.HasErrors){
|
|
||||||
FormMessage.Error("Error Executing Plugins", "Failed to execute the following plugins:\n\n"+string.Join("\n\n", e.Errors), FormMessage.OK);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,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);
|
||||||
@@ -218,7 +202,7 @@ namespace TweetDuck{
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReloadConfig();
|
UserConfig.Reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Restart(params string[] extraArgs){
|
public static void Restart(params string[] extraArgs){
|
||||||
@@ -231,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
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
|
||||||
```
|
```
|
||||||
|
|
||||||
After building, run either `_postbuild.bat` if you want to package the files yourself, or `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 then 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
BIN
Resources/Guide/img/app-menu.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
BIN
Resources/Guide/img/column-clear-header.png
Normal file
BIN
Resources/Guide/img/column-clear-header.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.7 KiB |
BIN
Resources/Guide/img/column-clear-preferences.png
Normal file
BIN
Resources/Guide/img/column-clear-preferences.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.5 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user