Compare commits
317 Commits
Author | SHA1 | Date | |
---|---|---|---|
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 | |||
63b58b1cfe | |||
77e656d8e4 | |||
a673957bd0 | |||
c99a0c9974 | |||
0fb06d0ff2 | |||
c51eebfe22 | |||
a51b34b48f | |||
1b239bada1 | |||
50ab1a6ac3 | |||
f181f1fadc | |||
c686349922 | |||
5f44a1f4ad | |||
a968938832 | |||
8d67f3dfdc | |||
973ae8cb5d | |||
a4747b0d7b | |||
f07640cc84 | |||
c235c55b19 | |||
485ef684be | |||
7caca22e57 | |||
f1d9e32bf5 | |||
23d5fa3107 | |||
4e7d8aba1c | |||
98ba871a71 | |||
3ff23c0264 | |||
e21f89477b | |||
f177f514f5 | |||
af30f3b348 | |||
82df618429 | |||
bb3538e270 | |||
71925e1126 | |||
93c1cbd231 | |||
894b890fe5 | |||
8e9e8f7fad | |||
2a0461a76f | |||
85f923a6fc | |||
b35e4d4d01 | |||
cb24a859f4 | |||
b1ef00746f | |||
aebe82e3a7 | |||
7c87856b4d | |||
d1b1dd539f | |||
55eea88ace | |||
a70f64e1f6 | |||
fa0cb120a7 | |||
e3080d07dc |
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
|
||||
public const string ArgRestart = "-restart";
|
||||
public const string ArgImportCookies = "-importcookies";
|
||||
public const string ArgDeleteCookies = "-deletecookies";
|
||||
public const string ArgUpdated = "-updated";
|
||||
|
||||
// class data and methods
|
||||
@@ -29,6 +30,7 @@ namespace TweetDuck.Configuration{
|
||||
CommandLineArgs args = Current.Clone();
|
||||
args.RemoveFlag(ArgRestart);
|
||||
args.RemoveFlag(ArgImportCookies);
|
||||
args.RemoveFlag(ArgDeleteCookies);
|
||||
args.RemoveFlag(ArgUpdated);
|
||||
return args;
|
||||
}
|
||||
|
@@ -1,29 +1,28 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using TweetDuck.Core.Utils;
|
||||
using TweetLib.Communication;
|
||||
|
||||
namespace TweetDuck.Configuration{
|
||||
sealed class LockManager{
|
||||
private const int RetryDelay = 250;
|
||||
|
||||
public enum Result{
|
||||
Success, HasProcess, Fail
|
||||
}
|
||||
|
||||
public Process LockingProcess { get; private set; }
|
||||
|
||||
private readonly string file;
|
||||
private FileStream lockStream;
|
||||
private Process lockingProcess;
|
||||
|
||||
public LockManager(string file){
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
private void CreateLockFileStream(){
|
||||
lockStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read);
|
||||
WriteIntToStream(lockStream, WindowsUtils.CurrentProcessID);
|
||||
lockStream.Flush(true);
|
||||
}
|
||||
// Lock file
|
||||
|
||||
private bool ReleaseLockFileStream(){
|
||||
if (lockStream != null){
|
||||
@@ -37,8 +36,10 @@ namespace TweetDuck.Configuration{
|
||||
}
|
||||
|
||||
private Result TryCreateLockFile(){
|
||||
if (lockStream != null){
|
||||
throw new InvalidOperationException("Lock file already exists.");
|
||||
void CreateLockFileStream(){
|
||||
lockStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read);
|
||||
lockStream.Write(BitConverter.GetBytes(WindowsUtils.CurrentProcessID), 0, sizeof(int));
|
||||
lockStream.Flush(true);
|
||||
}
|
||||
|
||||
try{
|
||||
@@ -60,6 +61,8 @@ namespace TweetDuck.Configuration{
|
||||
}
|
||||
}
|
||||
|
||||
// Lock management
|
||||
|
||||
public Result Lock(){
|
||||
if (lockStream != null){
|
||||
return Result.Success;
|
||||
@@ -72,7 +75,9 @@ namespace TweetDuck.Configuration{
|
||||
int pid;
|
||||
|
||||
using(FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)){
|
||||
pid = ReadIntFromStream(fileStream);
|
||||
byte[] bytes = new byte[sizeof(int)];
|
||||
fileStream.Read(bytes, 0, bytes.Length);
|
||||
pid = BitConverter.ToInt32(bytes, 0);
|
||||
}
|
||||
|
||||
try{
|
||||
@@ -80,7 +85,7 @@ namespace TweetDuck.Configuration{
|
||||
|
||||
using(Process currentProcess = Process.GetCurrentProcess()){
|
||||
if (foundProcess.MainModule.FileVersionInfo.InternalName == currentProcess.MainModule.FileVersionInfo.InternalName){
|
||||
LockingProcess = foundProcess;
|
||||
lockingProcess = foundProcess;
|
||||
}
|
||||
else{
|
||||
foundProcess.Close();
|
||||
@@ -91,7 +96,7 @@ namespace TweetDuck.Configuration{
|
||||
// Process.MainModule can throw exceptions in some cases
|
||||
}
|
||||
|
||||
return LockingProcess == null ? Result.Fail : Result.HasProcess;
|
||||
return lockingProcess == null ? Result.Fail : Result.HasProcess;
|
||||
}catch{
|
||||
return Result.Fail;
|
||||
}
|
||||
@@ -100,45 +105,72 @@ namespace TweetDuck.Configuration{
|
||||
return initialResult;
|
||||
}
|
||||
|
||||
public bool Unlock(){
|
||||
bool result = true;
|
||||
public Result LockWait(int timeout){
|
||||
for(int elapsed = 0; elapsed < timeout; elapsed += RetryDelay){
|
||||
Result result = Lock();
|
||||
|
||||
if (result == Result.HasProcess){
|
||||
Thread.Sleep(RetryDelay);
|
||||
}
|
||||
else{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return Lock();
|
||||
}
|
||||
|
||||
public bool Unlock(){
|
||||
if (ReleaseLockFileStream()){
|
||||
try{
|
||||
File.Delete(file);
|
||||
}catch(Exception e){
|
||||
Program.Reporter.Log(e.ToString());
|
||||
result = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Locking process
|
||||
|
||||
public bool RestoreLockingProcess(int failTimeout){
|
||||
if (lockingProcess != null){
|
||||
if (lockingProcess.MainWindowHandle == IntPtr.Zero){ // restore if the original process is in tray
|
||||
Comms.BroadcastMessage(Program.WindowRestoreMessage, (uint)lockingProcess.Id, 0);
|
||||
|
||||
if (WindowsUtils.TrySleepUntil(() => CheckLockingProcessExited() || (lockingProcess.MainWindowHandle != IntPtr.Zero && lockingProcess.Responding), failTimeout, RetryDelay)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool CloseLockingProcess(int closeTimeout, int killTimeout){
|
||||
if (LockingProcess != null){
|
||||
if (lockingProcess != null){
|
||||
try{
|
||||
if (LockingProcess.CloseMainWindow()){
|
||||
WindowsUtils.TrySleepUntil(CheckLockingProcessExited, closeTimeout, 250);
|
||||
if (lockingProcess.CloseMainWindow()){
|
||||
WindowsUtils.TrySleepUntil(CheckLockingProcessExited, closeTimeout, RetryDelay);
|
||||
}
|
||||
|
||||
if (!LockingProcess.HasExited){
|
||||
LockingProcess.Kill();
|
||||
WindowsUtils.TrySleepUntil(CheckLockingProcessExited, killTimeout, 250);
|
||||
if (!lockingProcess.HasExited){
|
||||
lockingProcess.Kill();
|
||||
WindowsUtils.TrySleepUntil(CheckLockingProcessExited, killTimeout, RetryDelay);
|
||||
}
|
||||
|
||||
if (LockingProcess.HasExited){
|
||||
LockingProcess.Dispose();
|
||||
LockingProcess = null;
|
||||
if (lockingProcess.HasExited){
|
||||
lockingProcess.Dispose();
|
||||
lockingProcess = null;
|
||||
return true;
|
||||
}
|
||||
}catch(Exception ex){
|
||||
if (ex is InvalidOperationException || ex is Win32Exception){
|
||||
if (LockingProcess != null){
|
||||
LockingProcess.Refresh();
|
||||
|
||||
bool hasExited = LockingProcess.HasExited;
|
||||
LockingProcess.Dispose();
|
||||
if (lockingProcess != null){
|
||||
bool hasExited = CheckLockingProcessExited();
|
||||
lockingProcess.Dispose();
|
||||
return hasExited;
|
||||
}
|
||||
}
|
||||
@@ -150,21 +182,8 @@ namespace TweetDuck.Configuration{
|
||||
}
|
||||
|
||||
private bool CheckLockingProcessExited(){
|
||||
LockingProcess.Refresh();
|
||||
return LockingProcess.HasExited;
|
||||
}
|
||||
|
||||
// Utility functions
|
||||
|
||||
private static void WriteIntToStream(Stream stream, int value){
|
||||
byte[] id = BitConverter.GetBytes(value);
|
||||
stream.Write(id, 0, id.Length);
|
||||
}
|
||||
|
||||
private static int ReadIntFromStream(Stream stream){
|
||||
byte[] bytes = new byte[4];
|
||||
stream.Read(bytes, 0, 4);
|
||||
return BitConverter.ToInt32(bytes, 0);
|
||||
lockingProcess.Refresh();
|
||||
return lockingProcess.HasExited;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,43 +1,41 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using TweetDuck.Core.Utils;
|
||||
using TweetDuck.Data.Serialization;
|
||||
|
||||
namespace TweetDuck.Configuration{
|
||||
sealed class SystemConfig{
|
||||
private static readonly FileSerializer<SystemConfig> Serializer = new FileSerializer<SystemConfig>();
|
||||
|
||||
public static readonly bool IsHardwareAccelerationSupported = File.Exists(Path.Combine(Program.ProgramPath, "libEGL.dll")) &&
|
||||
File.Exists(Path.Combine(Program.ProgramPath, "libGLESv2.dll"));
|
||||
|
||||
// CONFIGURATION DATA
|
||||
|
||||
private bool _hardwareAcceleration = true;
|
||||
|
||||
public bool EnableBrowserGCReload { get; set; } = true;
|
||||
public int BrowserMemoryThreshold { get; set; } = 400;
|
||||
|
||||
// SPECIAL PROPERTIES
|
||||
|
||||
public bool HardwareAcceleration{
|
||||
get => hardwareAcceleration && IsHardwareAccelerationSupported;
|
||||
set => hardwareAcceleration = value;
|
||||
get => _hardwareAcceleration && IsHardwareAccelerationSupported;
|
||||
set => _hardwareAcceleration = value;
|
||||
}
|
||||
|
||||
private readonly string file;
|
||||
// END OF CONFIG
|
||||
|
||||
private bool hardwareAcceleration;
|
||||
private readonly string file;
|
||||
|
||||
private SystemConfig(string file){
|
||||
this.file = file;
|
||||
|
||||
HardwareAcceleration = true;
|
||||
}
|
||||
|
||||
private void WriteToStream(Stream stream){
|
||||
stream.WriteByte((byte)(HardwareAcceleration ? 1 : 0));
|
||||
}
|
||||
|
||||
private void ReadFromStream(Stream stream){
|
||||
HardwareAcceleration = stream.ReadByte() > 0;
|
||||
}
|
||||
|
||||
public bool Save(){
|
||||
try{
|
||||
WindowsUtils.CreateDirectoryForFile(file);
|
||||
|
||||
using(Stream stream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None)){
|
||||
WriteToStream(stream);
|
||||
}
|
||||
|
||||
Serializer.Write(file, this);
|
||||
return true;
|
||||
}catch(Exception e){
|
||||
Program.Reporter.HandleException("Configuration Error", "Could not save the system configuration file.", true, e);
|
||||
@@ -49,9 +47,7 @@ namespace TweetDuck.Configuration{
|
||||
SystemConfig config = new SystemConfig(file);
|
||||
|
||||
try{
|
||||
using(Stream stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read)){
|
||||
config.ReadFromStream(stream);
|
||||
}
|
||||
Serializer.Read(file, config);
|
||||
}catch(FileNotFoundException){
|
||||
}catch(DirectoryNotFoundException){
|
||||
}catch(Exception e){
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using TweetDuck.Core;
|
||||
@@ -9,8 +10,17 @@ using TweetDuck.Data;
|
||||
using TweetDuck.Data.Serialization;
|
||||
|
||||
namespace TweetDuck.Configuration{
|
||||
sealed class UserConfig : ISerializedObject{
|
||||
private static readonly FileSerializer<UserConfig> Serializer = new FileSerializer<UserConfig>();
|
||||
sealed class UserConfig{
|
||||
private static readonly FileSerializer<UserConfig> Serializer = new FileSerializer<UserConfig>{ HandleUnknownProperties = HandleUnknownProperties };
|
||||
|
||||
private static void HandleUnknownProperties(UserConfig obj, Dictionary<string, string> data){
|
||||
data.Remove("EnableBrowserGCReload");
|
||||
data.Remove("BrowserMemoryThreshold");
|
||||
|
||||
if (data.Count == 0){
|
||||
obj.Save();
|
||||
}
|
||||
}
|
||||
|
||||
static UserConfig(){
|
||||
Serializer.RegisterTypeConverter(typeof(WindowState), WindowState.Converter);
|
||||
@@ -34,22 +44,29 @@ namespace TweetDuck.Configuration{
|
||||
|
||||
// CONFIGURATION DATA
|
||||
|
||||
public bool FirstRun { get; set; } = true;
|
||||
public bool AllowDataCollection { get; set; } = false;
|
||||
|
||||
public WindowState BrowserWindow { get; set; } = new WindowState();
|
||||
public WindowState PluginsWindow { get; set; } = new WindowState();
|
||||
|
||||
public bool ExpandLinksOnHover { get; set; } = true;
|
||||
public bool SwitchAccountSelectors { get; set; } = true;
|
||||
public bool EnableSpellCheck { get; set; } = false;
|
||||
public bool ExpandLinksOnHover { get; set; } = true;
|
||||
public bool SwitchAccountSelectors { get; set; } = true;
|
||||
public bool OpenSearchInFirstColumn { get; set; } = true;
|
||||
public bool BestImageQuality { get; set; } = true;
|
||||
public bool EnableSpellCheck { get; set; } = false;
|
||||
public int VideoPlayerVolume { get; set; } = 50;
|
||||
private int _zoomLevel = 100;
|
||||
private bool _muteNotifications;
|
||||
|
||||
private TrayIcon.Behavior _trayBehavior = TrayIcon.Behavior.Disabled;
|
||||
public bool EnableTrayHighlight { get; set; } = true;
|
||||
private TrayIcon.Behavior _trayBehavior = TrayIcon.Behavior.Disabled;
|
||||
public bool EnableTrayHighlight { get; set; } = true;
|
||||
|
||||
public bool EnableUpdateCheck { get; set; } = true;
|
||||
public string DismissedUpdate { get; set; } = null;
|
||||
|
||||
public bool DisplayNotificationColumn { get; set; } = false;
|
||||
public bool NotificationMediaPreviews { get; set; } = true;
|
||||
public bool NotificationSkipOnLinkClick { get; set; } = false;
|
||||
public bool NotificationNonIntrusiveMode { get; set; } = true;
|
||||
public int NotificationIdlePauseSeconds { get; set; } = 0;
|
||||
@@ -67,20 +84,20 @@ namespace TweetDuck.Configuration{
|
||||
public Size CustomNotificationSize { get; set; } = Size.Empty;
|
||||
public int NotificationScrollSpeed { get; set; } = 10;
|
||||
|
||||
public int NotificationSoundVolume { get; set; } = 100;
|
||||
private string _notificationSoundPath;
|
||||
|
||||
public string CustomCefArgs { get; set; } = null;
|
||||
public string CustomBrowserCSS { get; set; } = null;
|
||||
public string CustomNotificationCSS { get; set; } = null;
|
||||
|
||||
public bool EnableBrowserGCReload { get; set; } = false;
|
||||
public int BrowserMemoryThreshold { get; set; } = 350;
|
||||
|
||||
// SPECIAL PROPERTIES
|
||||
|
||||
public bool IsCustomNotificationPositionSet => CustomNotificationPosition != ControlExtensions.InvisibleLocation;
|
||||
public bool IsCustomNotificationSizeSet => CustomNotificationSize != Size.Empty;
|
||||
|
||||
public TwitterUtils.ImageQuality TwitterImageQuality => BestImageQuality ? TwitterUtils.ImageQuality.Orig : TwitterUtils.ImageQuality.Default;
|
||||
|
||||
public string NotificationSoundPath{
|
||||
get => string.IsNullOrEmpty(_notificationSoundPath) ? string.Empty : _notificationSoundPath;
|
||||
set => _notificationSoundPath = value;
|
||||
@@ -92,7 +109,7 @@ namespace TweetDuck.Configuration{
|
||||
set{
|
||||
if (_muteNotifications != value){
|
||||
_muteNotifications = value;
|
||||
MuteToggled?.Invoke(this, new EventArgs());
|
||||
MuteToggled?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,7 +120,7 @@ namespace TweetDuck.Configuration{
|
||||
set{
|
||||
if (_zoomLevel != value){
|
||||
_zoomLevel = value;
|
||||
ZoomLevelChanged?.Invoke(this, new EventArgs());
|
||||
ZoomLevelChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,7 +133,7 @@ namespace TweetDuck.Configuration{
|
||||
set{
|
||||
if (_trayBehavior != value){
|
||||
_trayBehavior = value;
|
||||
TrayBehaviorChanged?.Invoke(this, new EventArgs());
|
||||
TrayBehaviorChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,17 +143,15 @@ namespace TweetDuck.Configuration{
|
||||
public event EventHandler MuteToggled;
|
||||
public event EventHandler ZoomLevelChanged;
|
||||
public event EventHandler TrayBehaviorChanged;
|
||||
|
||||
// END OF CONFIG
|
||||
|
||||
private readonly string file;
|
||||
|
||||
public UserConfig(string file){ // TODO make private after removing UserConfigLegacy
|
||||
private UserConfig(string file){
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
bool ISerializedObject.OnReadUnknownProperty(string property, string value){
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Save(){
|
||||
try{
|
||||
WindowsUtils.CreateDirectoryForFile(file);
|
||||
@@ -154,6 +169,29 @@ namespace TweetDuck.Configuration{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Reload(){
|
||||
try{
|
||||
LoadInternal(false);
|
||||
return true;
|
||||
}catch(FileNotFoundException){
|
||||
try{
|
||||
Serializer.Write(file, new UserConfig(file));
|
||||
LoadInternal(false);
|
||||
return true;
|
||||
}catch(Exception e){
|
||||
Program.Reporter.HandleException("Configuration Error", "Could not regenerate configuration file.", true, e);
|
||||
return false;
|
||||
}
|
||||
}catch(Exception e){
|
||||
Program.Reporter.HandleException("Configuration Error", "Could not reload configuration file.", true, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadInternal(bool backup){
|
||||
Serializer.Read(backup ? GetBackupFile(file) : file, this);
|
||||
}
|
||||
|
||||
public static UserConfig Load(string file){
|
||||
Exception firstException = null;
|
||||
@@ -161,22 +199,23 @@ namespace TweetDuck.Configuration{
|
||||
for(int attempt = 0; attempt < 2; attempt++){
|
||||
try{
|
||||
UserConfig config = new UserConfig(file);
|
||||
Serializer.Read(attempt == 0 ? file : GetBackupFile(file), config);
|
||||
config.LoadInternal(attempt > 0);
|
||||
return config;
|
||||
}catch(FileNotFoundException){
|
||||
}catch(DirectoryNotFoundException){
|
||||
break;
|
||||
}catch(FormatException){
|
||||
UserConfig config = UserConfigLegacy.Load(file);
|
||||
config.Save();
|
||||
return config;
|
||||
}catch(Exception e){
|
||||
if (attempt == 0){
|
||||
firstException = e;
|
||||
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){
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,45 +1,33 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Text;
|
||||
|
||||
namespace TweetDuck.Core.Bridge{
|
||||
static class PropertyBridge{
|
||||
[Flags]
|
||||
public enum Properties{
|
||||
ExpandLinksOnHover = 1,
|
||||
MuteNotifications = 2,
|
||||
HasCustomNotificationSound = 4,
|
||||
SkipOnLinkClick = 8,
|
||||
SwitchAccountSelectors = 16,
|
||||
AllBrowser = ExpandLinksOnHover | SwitchAccountSelectors | MuteNotifications | HasCustomNotificationSound,
|
||||
AllNotification = ExpandLinksOnHover | SkipOnLinkClick
|
||||
public enum Environment{
|
||||
Browser, Notification
|
||||
}
|
||||
|
||||
public static string GenerateScript(Properties properties){
|
||||
StringBuilder build = new StringBuilder();
|
||||
build.Append("(function(c){");
|
||||
|
||||
if (properties.HasFlag(Properties.ExpandLinksOnHover)){
|
||||
build.Append("c.expandLinksOnHover=").Append(Program.UserConfig.ExpandLinksOnHover ? "true;" : "false;");
|
||||
public static string GenerateScript(Environment environment){
|
||||
string Bool(bool value){
|
||||
return value ? "true;" : "false;";
|
||||
}
|
||||
|
||||
if (properties.HasFlag(Properties.SwitchAccountSelectors)){
|
||||
build.Append("c.switchAccountSelectors=").Append(Program.UserConfig.SwitchAccountSelectors ? "true;" : "false;");
|
||||
StringBuilder build = new StringBuilder().Append("(function(x){");
|
||||
|
||||
build.Append("x.expandLinksOnHover=").Append(Bool(Program.UserConfig.ExpandLinksOnHover));
|
||||
|
||||
if (environment == Environment.Browser){
|
||||
build.Append("x.switchAccountSelectors=").Append(Bool(Program.UserConfig.SwitchAccountSelectors));
|
||||
build.Append("x.openSearchInFirstColumn=").Append(Bool(Program.UserConfig.OpenSearchInFirstColumn));
|
||||
build.Append("x.muteNotifications=").Append(Bool(Program.UserConfig.MuteNotifications));
|
||||
build.Append("x.hasCustomNotificationSound=").Append(Bool(Program.UserConfig.NotificationSoundPath.Length > 0));
|
||||
build.Append("x.notificationMediaPreviews=").Append(Bool(Program.UserConfig.NotificationMediaPreviews));
|
||||
}
|
||||
|
||||
if (properties.HasFlag(Properties.MuteNotifications)){
|
||||
build.Append("c.muteNotifications=").Append(Program.UserConfig.MuteNotifications ? "true;" : "false;");
|
||||
if (environment == Environment.Notification){
|
||||
build.Append("x.skipOnLinkClick=").Append(Bool(Program.UserConfig.NotificationSkipOnLinkClick));
|
||||
}
|
||||
|
||||
if (properties.HasFlag(Properties.HasCustomNotificationSound)){
|
||||
build.Append("c.hasCustomNotificationSound=").Append(Program.UserConfig.NotificationSoundPath.Length > 0 ? "true;" : "false;");
|
||||
}
|
||||
|
||||
if (properties.HasFlag(Properties.SkipOnLinkClick)){
|
||||
build.Append("c.skipOnLinkClick=").Append(Program.UserConfig.NotificationSkipOnLinkClick ? "true;" : "false;");
|
||||
}
|
||||
|
||||
build.Append("})(window.$TDX=window.$TDX||{})");
|
||||
return build.ToString();
|
||||
|
||||
return build.Append("})(window.$TDX=window.$TDX||{})").ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,17 +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.Handling;
|
||||
using TweetDuck.Core.Notification;
|
||||
using TweetDuck.Core.Other;
|
||||
using TweetDuck.Core.Utils;
|
||||
using TweetDuck.Resources;
|
||||
|
||||
namespace TweetDuck.Core.Bridge{
|
||||
sealed class TweetDeckBridge{
|
||||
public static string LastRightClickedLink = string.Empty;
|
||||
public static string LastHighlightedTweet = string.Empty;
|
||||
public static string LastHighlightedQuotedTweet = string.Empty;
|
||||
public static string FontSize { get; private set; }
|
||||
public static string NotificationHeadLayout { get; private set; }
|
||||
|
||||
public static string LastHighlightedTweetUrl = string.Empty;
|
||||
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(){
|
||||
LastRightClickedLink = LastHighlightedTweet = LastHighlightedQuotedTweet = string.Empty;
|
||||
FontSize = NotificationHeadLayout = null;
|
||||
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;
|
||||
@@ -22,26 +50,29 @@ namespace TweetDuck.Core.Bridge{
|
||||
this.notification = notification;
|
||||
}
|
||||
|
||||
public void LoadFontSizeClass(string fsClass){
|
||||
public void OnIntroductionClosed(bool showGuide, bool allowDataCollection){
|
||||
form.InvokeAsyncSafe(() => {
|
||||
TweetNotification.SetFontSizeClass(fsClass);
|
||||
form.OnIntroductionClosed(showGuide, allowDataCollection);
|
||||
});
|
||||
}
|
||||
|
||||
public void LoadNotificationHeadContents(string headContents){
|
||||
public void LoadNotificationLayout(string fontSize, string headLayout){
|
||||
form.InvokeAsyncSafe(() => {
|
||||
TweetNotification.SetHeadTag(headContents);
|
||||
FontSize = fontSize;
|
||||
NotificationHeadLayout = headLayout;
|
||||
});
|
||||
}
|
||||
|
||||
public void SetLastRightClickedLink(string link){
|
||||
form.InvokeAsyncSafe(() => LastRightClickedLink = link);
|
||||
public void SetLastRightClickInfo(string type, string link){
|
||||
form.InvokeAsyncSafe(() => ContextMenuBase.SetContextInfo(type, link));
|
||||
}
|
||||
|
||||
public void SetLastHighlightedTweet(string link, string quotedLink){
|
||||
public void SetLastHighlightedTweet(string tweetUrl, string quoteUrl, string authors, string imageList){
|
||||
form.InvokeAsyncSafe(() => {
|
||||
LastHighlightedTweet = link;
|
||||
LastHighlightedQuotedTweet = quotedLink;
|
||||
LastHighlightedTweetUrl = tweetUrl;
|
||||
LastHighlightedQuoteUrl = quoteUrl;
|
||||
LastHighlightedTweetAuthors = authors;
|
||||
LastHighlightedTweetImages = imageList;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -49,10 +80,10 @@ namespace TweetDuck.Core.Bridge{
|
||||
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(() => {
|
||||
form.OnTweetNotification();
|
||||
notification.ShowNotification(new TweetNotification(columnName, tweetHtml, tweetCharacters, tweetUrl, quoteUrl));
|
||||
notification.ShowNotification(new TweetNotification(columnId, chirpId, columnName, tweetHtml, tweetCharacters, tweetUrl, quoteUrl));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -72,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(){
|
||||
notification.InvokeAsyncSafe(notification.FinishCurrentNotification);
|
||||
}
|
||||
@@ -80,16 +117,20 @@ namespace TweetDuck.Core.Bridge{
|
||||
form.InvokeAsyncSafe(() => form.OnTweetScreenshotReady(html, width, height));
|
||||
}
|
||||
|
||||
public void PlayVideo(string url, string username){
|
||||
form.InvokeAsyncSafe(() => form.PlayVideo(url, username));
|
||||
}
|
||||
|
||||
public void FixClipboard(){
|
||||
form.InvokeAsyncSafe(WindowsUtils.ClipboardStripHtmlStyles);
|
||||
}
|
||||
|
||||
public int GetIdleSeconds(){
|
||||
return NativeMethods.GetIdleSeconds();
|
||||
public void OpenBrowser(string url){
|
||||
form.InvokeAsyncSafe(() => BrowserUtils.OpenExternalBrowser(url));
|
||||
}
|
||||
|
||||
public void OpenBrowser(string url){
|
||||
BrowserUtils.OpenExternalBrowser(url);
|
||||
public int GetIdleSeconds(){
|
||||
return NativeMethods.GetIdleSeconds();
|
||||
}
|
||||
|
||||
public void Alert(string type, string contents){
|
||||
|
@@ -1,8 +1,7 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using TweetDuck.Core.Utils;
|
||||
|
||||
namespace TweetDuck.Core.Controls{
|
||||
static class ControlExtensions{
|
||||
@@ -67,12 +66,6 @@ namespace TweetDuck.Core.Controls{
|
||||
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){
|
||||
textBox.KeyDown += (sender, args) => {
|
||||
if (args.Control && args.KeyCode == Keys.A){
|
||||
|
@@ -2,7 +2,7 @@
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace TweetDuck.Core.Controls{
|
||||
class FlatButton : Button{
|
||||
sealed class FlatButton : Button{
|
||||
protected override bool ShowFocusCues => false;
|
||||
|
||||
public FlatButton(){
|
||||
|
@@ -23,7 +23,7 @@ namespace TweetDuck.Core.Controls{
|
||||
|
||||
Rectangle rect = e.ClipRectangle;
|
||||
rect.Width = (int)(rect.Width*((double)Value/Maximum));
|
||||
e.Graphics.FillRectangle(brush,rect);
|
||||
e.Graphics.FillRectangle(brush, rect);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing){
|
||||
|
6
Core/FormBrowser.Designer.cs
generated
@@ -38,11 +38,11 @@
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.BackColor = TweetDuck.Core.Utils.BrowserUtils.BackgroundColor;
|
||||
this.ClientSize = new System.Drawing.Size(324, 386);
|
||||
this.BackColor = TweetDuck.Core.Utils.TwitterUtils.BackgroundColor;
|
||||
this.ClientSize = new System.Drawing.Size(400, 386);
|
||||
this.Icon = Properties.Resources.icon;
|
||||
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.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
|
||||
this.Activated += new System.EventHandler(this.FormBrowser_Activated);
|
||||
|
@@ -2,15 +2,16 @@
|
||||
using CefSharp.WinForms;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using TweetDuck.Configuration;
|
||||
using TweetDuck.Core.Bridge;
|
||||
using TweetDuck.Core.Controls;
|
||||
using TweetDuck.Core.Handling;
|
||||
using TweetDuck.Core.Handling.General;
|
||||
using TweetDuck.Core.Notification;
|
||||
using TweetDuck.Core.Notification.Screenshot;
|
||||
using TweetDuck.Core.Other;
|
||||
using TweetDuck.Core.Other.Management;
|
||||
using TweetDuck.Core.Other.Settings;
|
||||
using TweetDuck.Core.Utils;
|
||||
using TweetDuck.Plugins;
|
||||
@@ -18,13 +19,29 @@ using TweetDuck.Plugins.Enums;
|
||||
using TweetDuck.Plugins.Events;
|
||||
using TweetDuck.Resources;
|
||||
using TweetDuck.Updates;
|
||||
using TweetDuck.Updates.Events;
|
||||
using TweetLib.Audio;
|
||||
|
||||
namespace TweetDuck.Core{
|
||||
sealed partial class FormBrowser : Form{
|
||||
private static UserConfig Config => Program.UserConfig;
|
||||
|
||||
public bool IsWaiting{
|
||||
set{
|
||||
if (value){
|
||||
browser.Enabled = false;
|
||||
Cursor = Cursors.WaitCursor;
|
||||
}
|
||||
else{
|
||||
browser.Enabled = true;
|
||||
Cursor = Cursors.Default;
|
||||
|
||||
if (Focused){ // re-focus browser only if the window or a child is activated
|
||||
browser.Focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string UpdateInstallerPath { get; private set; }
|
||||
|
||||
private readonly ChromiumWebBrowser browser;
|
||||
@@ -40,15 +57,18 @@ namespace TweetDuck.Core{
|
||||
|
||||
private TweetScreenshotManager notificationScreenshotManager;
|
||||
private SoundNotification soundNotification;
|
||||
private VideoPlayer videoPlayer;
|
||||
|
||||
public FormBrowser(PluginManager pluginManager, UpdaterSettings updaterSettings){
|
||||
public FormBrowser(UpdaterSettings updaterSettings){
|
||||
InitializeComponent();
|
||||
|
||||
Text = Program.BrandName;
|
||||
|
||||
this.plugins = pluginManager;
|
||||
this.plugins = new PluginManager(Program.PluginPath, Program.PluginConfigFilePath);
|
||||
this.plugins.Reloaded += plugins_Reloaded;
|
||||
this.plugins.Executed += plugins_Executed;
|
||||
this.plugins.PluginChangedState += plugins_PluginChangedState;
|
||||
this.plugins.Reload();
|
||||
|
||||
this.contextMenu = ContextMenuBrowser.CreateMenu(this);
|
||||
this.memoryUsageTracker = new MemoryUsageTracker("TDGF_tryRunCleanup");
|
||||
@@ -64,8 +84,11 @@ namespace TweetDuck.Core{
|
||||
this.notification.Show();
|
||||
|
||||
this.browser = new ChromiumWebBrowser("https://tweetdeck.twitter.com/"){
|
||||
DialogHandler = new FileDialogHandler(),
|
||||
DragHandler = new DragHandlerBrowser(),
|
||||
MenuHandler = new ContextMenuBrowser(this),
|
||||
JsDialogHandler = new JavaScriptDialogHandler(),
|
||||
KeyboardHandler = new KeyboardHandlerBrowser(this),
|
||||
LifeSpanHandler = new LifeSpanHandler(),
|
||||
RequestHandler = new RequestHandlerBrowser()
|
||||
};
|
||||
@@ -81,7 +104,7 @@ namespace TweetDuck.Core{
|
||||
this.browser.RegisterAsyncJsObject("$TD", new TweetDeckBridge(this, notification));
|
||||
this.browser.RegisterAsyncJsObject("$TDP", plugins.Bridge);
|
||||
|
||||
browser.BrowserSettings.BackgroundColor = (uint)BrowserUtils.BackgroundColor.ToArgb();
|
||||
browser.BrowserSettings.BackgroundColor = (uint)TwitterUtils.BackgroundColor.ToArgb();
|
||||
browser.Dock = DockStyle.None;
|
||||
browser.Location = ControlExtensions.InvisibleLocation;
|
||||
Controls.Add(browser);
|
||||
@@ -96,6 +119,7 @@ namespace TweetDuck.Core{
|
||||
|
||||
notificationScreenshotManager?.Dispose();
|
||||
soundNotification?.Dispose();
|
||||
videoPlayer?.Dispose();
|
||||
};
|
||||
|
||||
this.trayIcon.ClickRestore += trayIcon_ClickRestore;
|
||||
@@ -114,16 +138,6 @@ namespace TweetDuck.Core{
|
||||
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){
|
||||
form.VisibleChanged += (sender, args) => form.MoveToCenter(this);
|
||||
form.Show(this);
|
||||
@@ -158,7 +172,7 @@ namespace TweetDuck.Core{
|
||||
|
||||
private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
|
||||
if (!e.IsLoading){
|
||||
foreach(string word in BrowserUtils.DictionaryWords){
|
||||
foreach(string word in TwitterUtils.DictionaryWords){
|
||||
browser.AddWordToDictionary(word);
|
||||
}
|
||||
|
||||
@@ -175,30 +189,31 @@ namespace TweetDuck.Core{
|
||||
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Config.ZoomLevel);
|
||||
}
|
||||
|
||||
if (BrowserUtils.IsTwitterWebsite(e.Frame)){
|
||||
if (TwitterUtils.IsTwitterWebsite(e.Frame)){
|
||||
ScriptLoader.ExecuteFile(e.Frame, "twitter.js");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
|
||||
if (e.Frame.IsMain && BrowserUtils.IsTweetDeckWebsite(e.Frame)){
|
||||
e.Frame.ExecuteJavaScriptAsync(BrowserUtils.BackgroundColorFix);
|
||||
if (e.Frame.IsMain && TwitterUtils.IsTweetDeckWebsite(e.Frame)){
|
||||
e.Frame.ExecuteJavaScriptAsync(TwitterUtils.BackgroundColorFix);
|
||||
|
||||
UpdateProperties(PropertyBridge.Properties.AllBrowser);
|
||||
UpdateProperties(PropertyBridge.Environment.Browser);
|
||||
TweetDeckBridge.RestoreSessionData(e.Frame);
|
||||
ScriptLoader.ExecuteFile(e.Frame, "code.js");
|
||||
InjectBrowserCSS();
|
||||
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);
|
||||
}
|
||||
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Browser);
|
||||
|
||||
TweetDeckBridge.ResetStaticProperties();
|
||||
|
||||
if (Config.EnableBrowserGCReload){
|
||||
memoryUsageTracker.Start(this, e.Browser, Config.BrowserMemoryThreshold);
|
||||
if (Program.SystemConfig.EnableBrowserGCReload){
|
||||
memoryUsageTracker.Start(this, e.Browser, Program.SystemConfig.BrowserMemoryThreshold);
|
||||
}
|
||||
|
||||
if (Config.FirstRun){
|
||||
ScriptLoader.ExecuteFile(e.Frame, "introduction.js");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -225,6 +240,10 @@ namespace TweetDuck.Core{
|
||||
if (!isLoaded)return;
|
||||
|
||||
trayIcon.HasNotifications = false;
|
||||
|
||||
if (!browser.Enabled){ // when taking a screenshot, the window is unfocused and
|
||||
browser.Enabled = true; // the browser is disabled; if the user clicks back into
|
||||
} // the window, enable the browser again
|
||||
}
|
||||
|
||||
private void FormBrowser_LocationChanged(object sender, EventArgs e){
|
||||
@@ -282,7 +301,7 @@ namespace TweetDuck.Core{
|
||||
}
|
||||
|
||||
private void Config_MuteToggled(object sender, EventArgs e){
|
||||
UpdateProperties(PropertyBridge.Properties.MuteNotifications);
|
||||
UpdateProperties(PropertyBridge.Environment.Browser);
|
||||
}
|
||||
|
||||
private void Config_ZoomLevelChanged(object sender, EventArgs e){
|
||||
@@ -305,20 +324,28 @@ namespace TweetDuck.Core{
|
||||
}
|
||||
|
||||
private void plugins_Reloaded(object sender, PluginErrorEventArgs e){
|
||||
browser.GetBrowser().Reload();
|
||||
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 plugins_PluginChangedState(object sender, PluginChangedStateEventArgs e){
|
||||
browser.ExecuteScriptAsync("window.TDPF_setPluginState", e.Plugin, e.IsEnabled);
|
||||
}
|
||||
|
||||
private void updates_UpdateAccepted(object sender, UpdateAcceptedEventArgs e){
|
||||
private void updates_UpdateAccepted(object sender, UpdateEventArgs e){
|
||||
this.InvokeAsyncSafe(() => {
|
||||
foreach(Form form in Application.OpenForms.Cast<Form>().Reverse()){
|
||||
if (form is FormSettings || form is FormPlugins || form is FormAbout){
|
||||
form.Close();
|
||||
}
|
||||
}
|
||||
FormManager.CloseAllDialogs();
|
||||
|
||||
updates.BeginUpdateDownload(this, e.UpdateInfo, update => {
|
||||
if (update.DownloadStatus == UpdateDownloadStatus.Done){
|
||||
@@ -330,9 +357,9 @@ namespace TweetDuck.Core{
|
||||
});
|
||||
}
|
||||
|
||||
private void updates_UpdateDismissed(object sender, UpdateDismissedEventArgs e){
|
||||
private void updates_UpdateDismissed(object sender, UpdateEventArgs e){
|
||||
this.InvokeAsyncSafe(() => {
|
||||
Config.DismissedUpdate = e.VersionTag;
|
||||
Config.DismissedUpdate = e.UpdateInfo.VersionTag;
|
||||
Config.Save();
|
||||
});
|
||||
}
|
||||
@@ -357,7 +384,7 @@ namespace TweetDuck.Core{
|
||||
if (isLoaded){
|
||||
if (m.Msg == Program.WindowRestoreMessage){
|
||||
if (WindowsUtils.CurrentProcessID == m.WParam.ToInt32()){
|
||||
trayIcon_ClickRestore(trayIcon, new EventArgs());
|
||||
trayIcon_ClickRestore(trayIcon, EventArgs.Empty);
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -374,7 +401,13 @@ namespace TweetDuck.Core{
|
||||
}
|
||||
|
||||
if (isBrowserReady && 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.ExecuteScriptAsync("TDGF_onMouseClickExtra", (m.WParam.ToInt32() >> 16) & 0xFFFF);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -397,19 +430,35 @@ namespace TweetDuck.Core{
|
||||
|
||||
// javascript calls
|
||||
|
||||
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 UpdateProperties(PropertyBridge.Properties properties){
|
||||
browser.ExecuteScriptAsync(PropertyBridge.GenerateScript(properties));
|
||||
public void UpdateProperties(PropertyBridge.Environment environment){
|
||||
browser.ExecuteScriptAsync(PropertyBridge.GenerateScript(environment));
|
||||
}
|
||||
|
||||
public void ReloadToTweetDeck(){
|
||||
browser.ExecuteScriptAsync($"gc&&gc();window.location.href='{BrowserUtils.TweetDeckURL}'");
|
||||
browser.ExecuteScriptAsync($"if(window.TDGF_reload)window.TDGF_reload();else window.location.href='{TwitterUtils.TweetDeckURL}'");
|
||||
}
|
||||
|
||||
// callback handlers
|
||||
|
||||
public void OnIntroductionClosed(bool showGuide, bool allowDataCollection){
|
||||
if (Config.FirstRun){
|
||||
Config.FirstRun = false;
|
||||
Config.AllowDataCollection = allowDataCollection;
|
||||
Config.Save();
|
||||
}
|
||||
|
||||
if (showGuide){
|
||||
ShowChildForm(new FormGuide());
|
||||
}
|
||||
}
|
||||
|
||||
public void OpenContextMenu(){
|
||||
contextMenu.Show(this, PointToClient(Cursor.Position));
|
||||
@@ -420,29 +469,37 @@ namespace TweetDuck.Core{
|
||||
}
|
||||
|
||||
public void OpenSettings(Type startTab){
|
||||
if (!TryBringToFront<FormSettings>()){
|
||||
if (!FormManager.TryBringToFront<FormSettings>()){
|
||||
bool prevEnableUpdateCheck = Config.EnableUpdateCheck;
|
||||
|
||||
FormSettings form = new FormSettings(this, plugins, updates, startTab);
|
||||
|
||||
form.FormClosed += (sender, args) => {
|
||||
if (!prevEnableUpdateCheck && Config.EnableUpdateCheck){
|
||||
updates.DismissUpdate(string.Empty);
|
||||
updates.Check(false);
|
||||
Config.DismissedUpdate = null;
|
||||
Config.Save();
|
||||
|
||||
updates.Check(true);
|
||||
}
|
||||
|
||||
if (!Config.EnableTrayHighlight){
|
||||
trayIcon.HasNotifications = false;
|
||||
}
|
||||
|
||||
if (Config.EnableBrowserGCReload){
|
||||
memoryUsageTracker.Start(this, browser.GetBrowser(), Config.BrowserMemoryThreshold);
|
||||
if (Program.SystemConfig.EnableBrowserGCReload){
|
||||
memoryUsageTracker.Start(this, browser.GetBrowser(), Program.SystemConfig.BrowserMemoryThreshold);
|
||||
}
|
||||
else{
|
||||
memoryUsageTracker.Stop();
|
||||
}
|
||||
|
||||
UpdateProperties(PropertyBridge.Properties.ExpandLinksOnHover | PropertyBridge.Properties.SwitchAccountSelectors | PropertyBridge.Properties.HasCustomNotificationSound);
|
||||
if (form.ShouldReloadBrowser){
|
||||
FormManager.TryFind<FormPlugins>()?.Close();
|
||||
plugins.Reload(); // also reloads the browser
|
||||
}
|
||||
else{
|
||||
UpdateProperties(PropertyBridge.Environment.Browser);
|
||||
}
|
||||
|
||||
notification.RequiresResize = true;
|
||||
form.Dispose();
|
||||
@@ -453,13 +510,13 @@ namespace TweetDuck.Core{
|
||||
}
|
||||
|
||||
public void OpenAbout(){
|
||||
if (!TryBringToFront<FormAbout>()){
|
||||
if (!FormManager.TryBringToFront<FormAbout>()){
|
||||
ShowChildForm(new FormAbout());
|
||||
}
|
||||
}
|
||||
|
||||
public void OpenPlugins(){
|
||||
if (!TryBringToFront<FormPlugins>()){
|
||||
if (!FormManager.TryBringToFront<FormPlugins>()){
|
||||
ShowChildForm(new FormPlugins(plugins));
|
||||
}
|
||||
}
|
||||
@@ -480,9 +537,55 @@ namespace TweetDuck.Core{
|
||||
soundNotification.PlaybackError += soundNotification_PlaybackError;
|
||||
}
|
||||
|
||||
soundNotification.SetVolume(Config.NotificationSoundVolume);
|
||||
soundNotification.Play(Config.NotificationSoundPath);
|
||||
}
|
||||
|
||||
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.GetBrowser().GetHost().SendFocusEvent(true);
|
||||
HideVideoOverlay();
|
||||
};
|
||||
}
|
||||
|
||||
videoPlayer.Launch(url, username);
|
||||
}
|
||||
|
||||
public void HideVideoOverlay(){
|
||||
browser.ExecuteScriptAsync("$('#td-video-player-overlay').remove()");
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
using(IFrame frame = browser.GetBrowser().MainFrame){
|
||||
if (!TwitterUtils.IsTweetDeckWebsite(frame)){
|
||||
FormMessage.Error("View Tweet Detail", "TweetDeck is not currently loaded.", FormMessage.OK);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
notification.FinishCurrentNotification();
|
||||
browser.ExecuteScriptAsync("window.TDGF_showTweetDetail", columnId, chirpId, fallbackUrl);
|
||||
}
|
||||
|
||||
public void OnTweetScreenshotReady(string html, int width, int height){
|
||||
if (notificationScreenshotManager == null){
|
||||
notificationScreenshotManager = new TweetScreenshotManager(this, plugins);
|
||||
|
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,99 +1,161 @@
|
||||
using CefSharp;
|
||||
using System;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Forms;
|
||||
using CefSharp;
|
||||
using TweetDuck.Core.Bridge;
|
||||
using TweetDuck.Core.Controls;
|
||||
using TweetDuck.Core.Other;
|
||||
using TweetDuck.Core.Utils;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using TweetDuck.Core.Other;
|
||||
|
||||
namespace TweetDuck.Core.Handling{
|
||||
abstract class ContextMenuBase : IContextMenuHandler{
|
||||
private static readonly Lazy<Regex> RegexTwitterAccount = new Lazy<Regex>(() => new Regex(@"^https?://twitter\.com/([^/]+)/?$", RegexOptions.Compiled), false);
|
||||
protected static readonly bool HasDevTools = File.Exists(Path.Combine(Program.ProgramPath, "devtools_resources.pak"));
|
||||
|
||||
private const int MenuOpenLinkUrl = 26500;
|
||||
private const int MenuCopyLinkUrl = 26501;
|
||||
private const int MenuCopyUsername = 26502;
|
||||
private const int MenuOpenImage = 26503;
|
||||
private const int MenuSaveImage = 26504;
|
||||
private const int MenuCopyImageUrl = 26505;
|
||||
private const int MenuOpenDevTools = 26599;
|
||||
private static TwitterUtils.ImageQuality ImageQuality => Program.UserConfig.TwitterImageQuality;
|
||||
|
||||
private static KeyValuePair<string, string> ContextInfo;
|
||||
private static bool IsLink => ContextInfo.Key == "link";
|
||||
private static bool IsImage => ContextInfo.Key == "image";
|
||||
private static bool IsVideo => ContextInfo.Key == "video";
|
||||
|
||||
private readonly Form form;
|
||||
|
||||
protected ContextMenuBase(Form form){
|
||||
this.form = form;
|
||||
public static void SetContextInfo(string type, string link){
|
||||
ContextInfo = new KeyValuePair<string, string>(string.IsNullOrEmpty(link) ? null : type, link);
|
||||
}
|
||||
|
||||
private static string GetMediaLink(IContextMenuParams parameters){
|
||||
return IsImage || IsVideo ? ContextInfo.Value : parameters.SourceUrl;
|
||||
}
|
||||
|
||||
private const CefMenuCommand MenuOpenLinkUrl = (CefMenuCommand)26500;
|
||||
private const CefMenuCommand MenuCopyLinkUrl = (CefMenuCommand)26501;
|
||||
private const CefMenuCommand MenuCopyUsername = (CefMenuCommand)26502;
|
||||
private const CefMenuCommand MenuViewImage = (CefMenuCommand)26503;
|
||||
private const CefMenuCommand MenuOpenMediaUrl = (CefMenuCommand)26504;
|
||||
private const CefMenuCommand MenuCopyMediaUrl = (CefMenuCommand)26505;
|
||||
private const CefMenuCommand MenuSaveMedia = (CefMenuCommand)26506;
|
||||
private const CefMenuCommand MenuSaveTweetImages = (CefMenuCommand)26507;
|
||||
private const CefMenuCommand MenuOpenDevTools = (CefMenuCommand)26599;
|
||||
|
||||
private string[] lastHighlightedTweetAuthors;
|
||||
private string[] lastHighlightedTweetImageList;
|
||||
|
||||
public virtual void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
|
||||
if (parameters.TypeFlags.HasFlag(ContextMenuType.Link) && !parameters.UnfilteredLinkUrl.EndsWith("tweetdeck.twitter.com/#", StringComparison.Ordinal)){
|
||||
if (RegexTwitterAccount.Value.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");
|
||||
if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){
|
||||
lastHighlightedTweetAuthors = StringUtils.EmptyArray;
|
||||
lastHighlightedTweetImageList = StringUtils.EmptyArray;
|
||||
ContextInfo = default(KeyValuePair<string, string>);
|
||||
}
|
||||
else{
|
||||
lastHighlightedTweetAuthors = TweetDeckBridge.LastHighlightedTweetAuthorsArray;
|
||||
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((CefMenuCommand)MenuOpenLinkUrl, "Open link in browser");
|
||||
model.AddItem((CefMenuCommand)MenuCopyLinkUrl, "Copy link address");
|
||||
model.AddItem(MenuOpenLinkUrl, TextOpen("link"));
|
||||
model.AddItem(MenuCopyLinkUrl, TextCopy("link"));
|
||||
}
|
||||
|
||||
model.AddSeparator();
|
||||
}
|
||||
|
||||
if (parameters.TypeFlags.HasFlag(ContextMenuType.Media) && parameters.HasImageContents){
|
||||
model.AddItem((CefMenuCommand)MenuOpenImage, "Open image in browser");
|
||||
model.AddItem((CefMenuCommand)MenuSaveImage, "Save image as...");
|
||||
model.AddItem((CefMenuCommand)MenuCopyImageUrl, "Copy image address");
|
||||
if (hasTweetVideo){
|
||||
model.AddItem(MenuOpenMediaUrl, TextOpen("video"));
|
||||
model.AddItem(MenuCopyMediaUrl, TextCopy("video"));
|
||||
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){
|
||||
model.AddItem(MenuSaveTweetImages, TextSave("all images"));
|
||||
}
|
||||
|
||||
model.AddSeparator();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){
|
||||
switch((int)commandId){
|
||||
switch(commandId){
|
||||
case MenuOpenLinkUrl:
|
||||
BrowserUtils.OpenExternalBrowser(parameters.LinkUrl);
|
||||
OpenBrowser(browserControl.AsControl(), IsLink ? ContextInfo.Value : parameters.LinkUrl);
|
||||
break;
|
||||
|
||||
case MenuCopyLinkUrl:
|
||||
SetClipboardText(string.IsNullOrEmpty(TweetDeckBridge.LastRightClickedLink) ? parameters.UnfilteredLinkUrl : TweetDeckBridge.LastRightClickedLink);
|
||||
SetClipboardText(browserControl.AsControl(), IsLink ? ContextInfo.Value : parameters.UnfilteredLinkUrl);
|
||||
break;
|
||||
|
||||
case MenuOpenImage:
|
||||
BrowserUtils.OpenExternalBrowser(parameters.SourceUrl);
|
||||
case MenuCopyUsername:
|
||||
Match match = TwitterUtils.RegexAccount.Match(parameters.UnfilteredLinkUrl);
|
||||
SetClipboardText(browserControl.AsControl(), match.Success ? match.Groups[1].Value : parameters.UnfilteredLinkUrl);
|
||||
break;
|
||||
|
||||
case MenuSaveImage:
|
||||
string fileName = GetImageFileName(parameters.SourceUrl);
|
||||
string extension = Path.GetExtension(fileName);
|
||||
string saveTarget;
|
||||
case MenuOpenMediaUrl:
|
||||
OpenBrowser(browserControl.AsControl(), TwitterUtils.GetMediaLink(GetMediaLink(parameters), ImageQuality));
|
||||
break;
|
||||
|
||||
using(SaveFileDialog dialog = new SaveFileDialog{
|
||||
AutoUpgradeEnabled = true,
|
||||
OverwritePrompt = true,
|
||||
Title = "Save image",
|
||||
FileName = fileName,
|
||||
Filter = "Image ("+(string.IsNullOrEmpty(extension) ? "unknown" : extension)+")|*.*"
|
||||
}){
|
||||
saveTarget = dialog.ShowDialog() == DialogResult.OK ? dialog.FileName : null;
|
||||
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 (saveTarget != null){
|
||||
BrowserUtils.DownloadFileAsync(parameters.SourceUrl, saveTarget, null, ex => {
|
||||
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 MenuCopyImageUrl:
|
||||
SetClipboardText(parameters.SourceUrl);
|
||||
case MenuSaveMedia:
|
||||
if (IsVideo){
|
||||
TwitterUtils.DownloadVideo(GetMediaLink(parameters), lastHighlightedTweetAuthors.LastOrDefault());
|
||||
}
|
||||
else{
|
||||
TwitterUtils.DownloadImage(GetMediaLink(parameters), lastHighlightedTweetAuthors.LastOrDefault(), ImageQuality);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case MenuCopyUsername:
|
||||
Match match = RegexTwitterAccount.Value.Match(parameters.UnfilteredLinkUrl);
|
||||
SetClipboardText(match.Success ? match.Groups[1].Value : parameters.UnfilteredLinkUrl);
|
||||
case MenuSaveTweetImages:
|
||||
TwitterUtils.DownloadImages(lastHighlightedTweetImageList, lastHighlightedTweetAuthors.LastOrDefault(), ImageQuality);
|
||||
break;
|
||||
|
||||
case MenuOpenDevTools:
|
||||
@@ -104,18 +166,24 @@ namespace TweetDuck.Core.Handling{
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame){}
|
||||
public virtual void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame){
|
||||
ContextInfo = default(KeyValuePair<string, string>);
|
||||
}
|
||||
|
||||
public virtual bool RunContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback){
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void SetClipboardText(string text){
|
||||
form.InvokeAsyncSafe(() => WindowsUtils.SetClipboard(text, TextDataFormat.UnicodeText));
|
||||
protected void OpenBrowser(Control control, string url){
|
||||
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){
|
||||
model.AddItem((CefMenuCommand)MenuOpenDevTools, "Open dev tools");
|
||||
model.AddItem(MenuOpenDevTools, "Open dev tools");
|
||||
}
|
||||
|
||||
protected static void RemoveSeparatorIfLast(IMenuModel model){
|
||||
@@ -129,17 +197,5 @@ namespace TweetDuck.Core.Handling{
|
||||
model.AddSeparator();
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetImageFileName(string url){
|
||||
// twimg adds a colon after file extension
|
||||
int dot = url.LastIndexOf('.');
|
||||
|
||||
if (dot != -1){
|
||||
url = StringUtils.ExtractBefore(url, ':', dot);
|
||||
}
|
||||
|
||||
// return file name
|
||||
return BrowserUtils.GetFileNameFromUrl(url) ?? "unknown";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -5,18 +5,18 @@ using TweetDuck.Core.Controls;
|
||||
using TweetDuck.Core.Utils;
|
||||
|
||||
namespace TweetDuck.Core.Handling{
|
||||
class ContextMenuBrowser : ContextMenuBase{
|
||||
private const int MenuGlobal = 26600;
|
||||
private const int MenuMute = 26601;
|
||||
private const int MenuSettings = 26602;
|
||||
private const int MenuPlugins = 26003;
|
||||
private const int MenuAbout = 26604;
|
||||
sealed class ContextMenuBrowser : ContextMenuBase{
|
||||
private const CefMenuCommand MenuGlobal = (CefMenuCommand)26600;
|
||||
private const CefMenuCommand MenuMute = (CefMenuCommand)26601;
|
||||
private const CefMenuCommand MenuSettings = (CefMenuCommand)26602;
|
||||
private const CefMenuCommand MenuPlugins = (CefMenuCommand)26003;
|
||||
private const CefMenuCommand MenuAbout = (CefMenuCommand)26604;
|
||||
|
||||
private const int MenuOpenTweetUrl = 26610;
|
||||
private const int MenuCopyTweetUrl = 26611;
|
||||
private const int MenuOpenQuotedTweetUrl = 26612;
|
||||
private const int MenuCopyQuotedTweetUrl = 26613;
|
||||
private const int MenuScreenshotTweet = 26614;
|
||||
private const CefMenuCommand MenuOpenTweetUrl = (CefMenuCommand)26610;
|
||||
private const CefMenuCommand MenuCopyTweetUrl = (CefMenuCommand)26611;
|
||||
private const CefMenuCommand MenuOpenQuotedTweetUrl = (CefMenuCommand)26612;
|
||||
private const CefMenuCommand MenuCopyQuotedTweetUrl = (CefMenuCommand)26613;
|
||||
private const CefMenuCommand MenuScreenshotTweet = (CefMenuCommand)26614;
|
||||
|
||||
private const string TitleReloadBrowser = "Reload browser";
|
||||
private const string TitleMuteNotifications = "Mute notifications";
|
||||
@@ -26,10 +26,10 @@ namespace TweetDuck.Core.Handling{
|
||||
|
||||
private readonly FormBrowser form;
|
||||
|
||||
private string lastHighlightedTweet;
|
||||
private string lastHighlightedQuotedTweet;
|
||||
private string lastHighlightedTweetUrl;
|
||||
private string lastHighlightedQuoteUrl;
|
||||
|
||||
public ContextMenuBrowser(FormBrowser form) : base(form){
|
||||
public ContextMenuBrowser(FormBrowser form){
|
||||
this.form = form;
|
||||
}
|
||||
|
||||
@@ -46,23 +46,23 @@ namespace TweetDuck.Core.Handling{
|
||||
|
||||
base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);
|
||||
|
||||
lastHighlightedTweet = TweetDeckBridge.LastHighlightedTweet;
|
||||
lastHighlightedQuotedTweet = TweetDeckBridge.LastHighlightedQuotedTweet;
|
||||
lastHighlightedTweetUrl = TweetDeckBridge.LastHighlightedTweetUrl;
|
||||
lastHighlightedQuoteUrl = TweetDeckBridge.LastHighlightedQuoteUrl;
|
||||
|
||||
if (!BrowserUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){
|
||||
lastHighlightedTweet = string.Empty;
|
||||
lastHighlightedQuotedTweet = string.Empty;
|
||||
if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){
|
||||
lastHighlightedTweetUrl = string.Empty;
|
||||
lastHighlightedQuoteUrl = string.Empty;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(lastHighlightedTweet) && (parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){
|
||||
model.AddItem((CefMenuCommand)MenuOpenTweetUrl, "Open tweet in browser");
|
||||
model.AddItem((CefMenuCommand)MenuCopyTweetUrl, "Copy tweet address");
|
||||
model.AddItem((CefMenuCommand)MenuScreenshotTweet, "Screenshot tweet to clipboard");
|
||||
if (!string.IsNullOrEmpty(lastHighlightedTweetUrl) && (parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){
|
||||
model.AddItem(MenuOpenTweetUrl, "Open tweet in browser");
|
||||
model.AddItem(MenuCopyTweetUrl, "Copy tweet address");
|
||||
model.AddItem(MenuScreenshotTweet, "Screenshot tweet to clipboard");
|
||||
|
||||
if (!string.IsNullOrEmpty(lastHighlightedQuotedTweet)){
|
||||
if (!string.IsNullOrEmpty(lastHighlightedQuoteUrl)){
|
||||
model.AddSeparator();
|
||||
model.AddItem((CefMenuCommand)MenuOpenQuotedTweetUrl, "Open quoted tweet in browser");
|
||||
model.AddItem((CefMenuCommand)MenuCopyQuotedTweetUrl, "Copy quoted tweet address");
|
||||
model.AddItem(MenuOpenQuotedTweetUrl, "Open quoted tweet in browser");
|
||||
model.AddItem(MenuCopyQuotedTweetUrl, "Copy quoted tweet address");
|
||||
}
|
||||
|
||||
model.AddSeparator();
|
||||
@@ -71,16 +71,16 @@ namespace TweetDuck.Core.Handling{
|
||||
if ((parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){
|
||||
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.AddCheckItem((CefMenuCommand)MenuMute, TitleMuteNotifications);
|
||||
globalMenu.SetChecked((CefMenuCommand)MenuMute, Program.UserConfig.MuteNotifications);
|
||||
globalMenu.AddCheckItem(MenuMute, TitleMuteNotifications);
|
||||
globalMenu.SetChecked(MenuMute, Program.UserConfig.MuteNotifications);
|
||||
globalMenu.AddSeparator();
|
||||
|
||||
globalMenu.AddItem((CefMenuCommand)MenuSettings, TitleSettings);
|
||||
globalMenu.AddItem((CefMenuCommand)MenuPlugins, TitlePlugins);
|
||||
globalMenu.AddItem((CefMenuCommand)MenuAbout, TitleAboutProgram);
|
||||
globalMenu.AddItem(MenuSettings, TitleSettings);
|
||||
globalMenu.AddItem(MenuPlugins, TitlePlugins);
|
||||
globalMenu.AddItem(MenuAbout, TitleAboutProgram);
|
||||
|
||||
if (HasDevTools){
|
||||
globalMenu.AddSeparator();
|
||||
@@ -96,8 +96,8 @@ namespace TweetDuck.Core.Handling{
|
||||
return true;
|
||||
}
|
||||
|
||||
switch((int)commandId){
|
||||
case (int)CefMenuCommand.Reload:
|
||||
switch(commandId){
|
||||
case CefMenuCommand.Reload:
|
||||
form.InvokeAsyncSafe(form.ReloadToTweetDeck);
|
||||
return true;
|
||||
|
||||
@@ -118,11 +118,11 @@ namespace TweetDuck.Core.Handling{
|
||||
return true;
|
||||
|
||||
case MenuOpenTweetUrl:
|
||||
BrowserUtils.OpenExternalBrowser(lastHighlightedTweet);
|
||||
OpenBrowser(form, lastHighlightedTweetUrl);
|
||||
return true;
|
||||
|
||||
case MenuCopyTweetUrl:
|
||||
SetClipboardText(lastHighlightedTweet);
|
||||
SetClipboardText(form, lastHighlightedTweetUrl);
|
||||
return true;
|
||||
|
||||
case MenuScreenshotTweet:
|
||||
@@ -130,11 +130,11 @@ namespace TweetDuck.Core.Handling{
|
||||
return true;
|
||||
|
||||
case MenuOpenQuotedTweetUrl:
|
||||
BrowserUtils.OpenExternalBrowser(lastHighlightedQuotedTweet);
|
||||
OpenBrowser(form, lastHighlightedQuoteUrl);
|
||||
return true;
|
||||
|
||||
case MenuCopyQuotedTweetUrl:
|
||||
SetClipboardText(lastHighlightedQuotedTweet);
|
||||
SetClipboardText(form, lastHighlightedQuoteUrl);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -3,16 +3,17 @@ using TweetDuck.Core.Controls;
|
||||
using TweetDuck.Core.Notification;
|
||||
|
||||
namespace TweetDuck.Core.Handling{
|
||||
class ContextMenuNotification : ContextMenuBase{
|
||||
private const int MenuSkipTweet = 26600;
|
||||
private const int MenuFreeze = 26601;
|
||||
private const int MenuCopyTweetUrl = 26602;
|
||||
private const int MenuCopyQuotedTweetUrl = 26603;
|
||||
sealed class ContextMenuNotification : ContextMenuBase{
|
||||
private const CefMenuCommand MenuViewDetail = (CefMenuCommand)26600;
|
||||
private const CefMenuCommand MenuSkipTweet = (CefMenuCommand)26601;
|
||||
private const CefMenuCommand MenuFreeze = (CefMenuCommand)26602;
|
||||
private const CefMenuCommand MenuCopyTweetUrl = (CefMenuCommand)26603;
|
||||
private const CefMenuCommand MenuCopyQuotedTweetUrl = (CefMenuCommand)26604;
|
||||
|
||||
private readonly FormNotificationBase form;
|
||||
private readonly bool enableCustomMenu;
|
||||
|
||||
public ContextMenuNotification(FormNotificationBase form, bool enableCustomMenu) : base(form){
|
||||
public ContextMenuNotification(FormNotificationBase form, bool enableCustomMenu){
|
||||
this.form = form;
|
||||
this.enableCustomMenu = enableCustomMenu;
|
||||
}
|
||||
@@ -28,23 +29,26 @@ namespace TweetDuck.Core.Handling{
|
||||
base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);
|
||||
|
||||
if (enableCustomMenu){
|
||||
model.AddItem((CefMenuCommand)MenuSkipTweet, "Skip tweet");
|
||||
model.AddCheckItem((CefMenuCommand)MenuFreeze, "Freeze");
|
||||
model.SetChecked((CefMenuCommand)MenuFreeze, form.FreezeTimer);
|
||||
model.AddSeparator();
|
||||
if (form.CanViewDetail){
|
||||
model.AddItem(MenuViewDetail, "View detail");
|
||||
}
|
||||
|
||||
model.AddItem(MenuSkipTweet, "Skip tweet");
|
||||
model.AddCheckItem(MenuFreeze, "Freeze");
|
||||
model.SetChecked(MenuFreeze, form.FreezeTimer);
|
||||
|
||||
if (!string.IsNullOrEmpty(form.CurrentTweetUrl)){
|
||||
model.AddItem((CefMenuCommand)MenuCopyTweetUrl, "Copy tweet address");
|
||||
model.AddSeparator();
|
||||
model.AddItem(MenuCopyTweetUrl, "Copy tweet address");
|
||||
|
||||
if (!string.IsNullOrEmpty(form.CurrentQuoteUrl)){
|
||||
model.AddItem((CefMenuCommand)MenuCopyQuotedTweetUrl, "Copy quoted tweet address");
|
||||
model.AddItem(MenuCopyQuotedTweetUrl, "Copy quoted tweet address");
|
||||
}
|
||||
|
||||
model.AddSeparator();
|
||||
}
|
||||
}
|
||||
|
||||
if (HasDevTools){
|
||||
AddSeparator(model);
|
||||
AddDebugMenuItems(model);
|
||||
}
|
||||
|
||||
@@ -58,7 +62,7 @@ namespace TweetDuck.Core.Handling{
|
||||
return true;
|
||||
}
|
||||
|
||||
switch((int)commandId){
|
||||
switch(commandId){
|
||||
case MenuSkipTweet:
|
||||
form.InvokeAsyncSafe(form.FinishCurrentNotification);
|
||||
return true;
|
||||
@@ -67,12 +71,16 @@ namespace TweetDuck.Core.Handling{
|
||||
form.InvokeAsyncSafe(() => form.FreezeTimer = !form.FreezeTimer);
|
||||
return true;
|
||||
|
||||
case MenuViewDetail:
|
||||
form.InvokeSafe(form.ShowTweetDetail);
|
||||
return true;
|
||||
|
||||
case MenuCopyTweetUrl:
|
||||
SetClipboardText(form.CurrentTweetUrl);
|
||||
SetClipboardText(form, form.CurrentTweetUrl);
|
||||
return true;
|
||||
|
||||
case MenuCopyQuotedTweetUrl:
|
||||
SetClipboardText(form.CurrentQuoteUrl);
|
||||
SetClipboardText(form, form.CurrentQuoteUrl);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
26
Core/Handling/DragHandlerBrowser.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using System.Collections.Generic;
|
||||
using CefSharp;
|
||||
|
||||
namespace TweetDuck.Core.Handling{
|
||||
sealed class DragHandlerBrowser : IDragHandler{
|
||||
public bool OnDragEnter(IWebBrowser browserControl, IBrowser browser, IDragData dragData, DragOperationsMask mask){
|
||||
void TriggerDragStart(string type, string data = null){
|
||||
browserControl.ExecuteScriptAsync("window.TDGF_onGlobalDragStart", type, data);
|
||||
}
|
||||
|
||||
if (dragData.IsLink){
|
||||
TriggerDragStart("link", dragData.LinkUrl);
|
||||
}
|
||||
else if (dragData.IsFragment){
|
||||
TriggerDragStart("text", dragData.FragmentText.Trim());
|
||||
}
|
||||
else{
|
||||
TriggerDragStart("unknown");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnDraggableRegionsChanged(IWebBrowser browserControl, IBrowser browser, IList<DraggableRegion> regions){}
|
||||
}
|
||||
}
|
@@ -1,8 +1,8 @@
|
||||
using CefSharp;
|
||||
using System;
|
||||
using System;
|
||||
using CefSharp;
|
||||
|
||||
namespace TweetDuck.Core.Handling{
|
||||
class BrowserProcessHandler : IBrowserProcessHandler{
|
||||
namespace TweetDuck.Core.Handling.General{
|
||||
sealed class BrowserProcessHandler : IBrowserProcessHandler{
|
||||
void IBrowserProcessHandler.OnContextInitialized(){
|
||||
using(IRequestContext ctx = Cef.GetGlobalRequestContext()){
|
||||
ctx.SetPreference("browser.enable_spellchecking", Program.UserConfig.EnableSpellCheck, out string _);
|
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,35 +1,58 @@
|
||||
using CefSharp;
|
||||
using CefSharp.WinForms;
|
||||
using System.Drawing;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
using CefSharp;
|
||||
using CefSharp.WinForms;
|
||||
using TweetDuck.Core.Controls;
|
||||
using TweetDuck.Core.Other;
|
||||
using TweetDuck.Core.Utils;
|
||||
|
||||
namespace TweetDuck.Core.Handling.General{
|
||||
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);
|
||||
}
|
||||
|
||||
namespace TweetDuck.Core.Handling {
|
||||
class JavaScriptDialogHandler : IJsDialogHandler{
|
||||
bool IJsDialogHandler.OnJSDialog(IWebBrowser browserControl, IBrowser browser, string originUrl, CefJsDialogType dialogType, string messageText, string defaultPromptText, IJsDialogCallback callback, ref bool suppressMessage){
|
||||
((ChromiumWebBrowser)browserControl).InvokeSafe(() => {
|
||||
FormMessage form;
|
||||
TextBox input = null;
|
||||
|
||||
if (dialogType == CefJsDialogType.Alert){
|
||||
form = new FormMessage("TweetDuck Browser Message", messageText, MessageBoxIcon.None);
|
||||
form = CreateMessageForm("Browser Message", messageText);
|
||||
form.AddButton(FormMessage.OK, ControlType.Accept | ControlType.Focused);
|
||||
}
|
||||
else if (dialogType == CefJsDialogType.Confirm){
|
||||
form = new FormMessage("TweetDuck Browser Confirmation", messageText, MessageBoxIcon.None);
|
||||
form = CreateMessageForm("Browser Confirmation", messageText);
|
||||
form.AddButton(FormMessage.No, DialogResult.No, ControlType.Cancel);
|
||||
form.AddButton(FormMessage.Yes, ControlType.Focused);
|
||||
}
|
||||
else if (dialogType == CefJsDialogType.Prompt){
|
||||
form = new FormMessage("TweetDuck Browser Prompt", messageText, MessageBoxIcon.None);
|
||||
form = CreateMessageForm("Browser Prompt", messageText);
|
||||
form.AddButton(FormMessage.Cancel, DialogResult.Cancel, ControlType.Cancel);
|
||||
form.AddButton(FormMessage.OK, ControlType.Accept | ControlType.Focused);
|
||||
|
||||
float dpiScale = form.GetDPIScale();
|
||||
int inputPad = form.HasIcon ? 43 : 0;
|
||||
|
||||
input = new TextBox{
|
||||
Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom,
|
||||
Location = new Point(27, form.ActionPanelY-46),
|
||||
Size = new Size(form.ClientSize.Width-54, 20)
|
||||
Location = new Point(BrowserUtils.Scale(22+inputPad, dpiScale), form.ActionPanelY-BrowserUtils.Scale(46, dpiScale)),
|
||||
Size = new Size(form.ClientSize.Width-BrowserUtils.Scale(44+inputPad, dpiScale), 20)
|
||||
};
|
||||
|
||||
form.Controls.Add(input);
|
@@ -1,8 +1,9 @@
|
||||
using CefSharp;
|
||||
using TweetDuck.Core.Controls;
|
||||
using TweetDuck.Core.Utils;
|
||||
|
||||
namespace TweetDuck.Core.Handling{
|
||||
class LifeSpanHandler : ILifeSpanHandler{
|
||||
namespace TweetDuck.Core.Handling.General{
|
||||
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){
|
||||
newBrowser = null;
|
||||
|
||||
@@ -11,7 +12,7 @@ namespace TweetDuck.Core.Handling{
|
||||
case WindowOpenDisposition.NewForegroundTab:
|
||||
case WindowOpenDisposition.NewPopup:
|
||||
case WindowOpenDisposition.NewWindow:
|
||||
BrowserUtils.OpenExternalBrowser(targetUrl);
|
||||
browserControl.AsControl().InvokeAsyncSafe(() => BrowserUtils.OpenExternalBrowser(targetUrl));
|
||||
return true;
|
||||
|
||||
default:
|
@@ -1,8 +1,8 @@
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using CefSharp;
|
||||
|
||||
namespace TweetDuck.Core.Handling{
|
||||
abstract class RequestHandler : IRequestHandler{
|
||||
namespace TweetDuck.Core.Handling.General{
|
||||
abstract class RequestHandlerBase : IRequestHandler{
|
||||
// Browser
|
||||
|
||||
public virtual void OnRenderViewReady(IWebBrowser browserControl, IBrowser browser){}
|
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;
|
||||
}
|
||||
}
|
||||
}
|
38
Core/Handling/KeyboardHandlerNotification.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using CefSharp;
|
||||
using System.Windows.Forms;
|
||||
using TweetDuck.Core.Controls;
|
||||
using TweetDuck.Core.Notification;
|
||||
|
||||
namespace TweetDuck.Core.Handling {
|
||||
sealed class KeyboardHandlerNotification : IKeyboardHandler{
|
||||
private readonly FormNotificationBase notification;
|
||||
|
||||
public KeyboardHandlerNotification(FormNotificationBase notification){
|
||||
this.notification = notification;
|
||||
}
|
||||
|
||||
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://")){
|
||||
switch((Keys)windowsKeyCode){
|
||||
case Keys.Enter:
|
||||
notification.InvokeAsyncSafe(notification.FinishCurrentNotification);
|
||||
return true;
|
||||
|
||||
case Keys.Escape:
|
||||
notification.InvokeAsyncSafe(() => notification.HideNotification(true));
|
||||
return true;
|
||||
|
||||
case Keys.Space:
|
||||
notification.InvokeAsyncSafe(() => notification.FreezeTimer = !notification.FreezeTimer);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IKeyboardHandler.OnKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,7 +1,8 @@
|
||||
using CefSharp;
|
||||
using TweetDuck.Core.Handling.General;
|
||||
|
||||
namespace TweetDuck.Core.Handling{
|
||||
class RequestHandlerBrowser : RequestHandler{
|
||||
sealed class RequestHandlerBrowser : RequestHandlerBase{
|
||||
public override void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status){
|
||||
browser.Reload();
|
||||
}
|
||||
|
@@ -4,7 +4,7 @@ using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace TweetDuck.Core.Handling{
|
||||
class ResourceHandlerNotification : IResourceHandler{
|
||||
sealed class ResourceHandlerNotification : IResourceHandler{
|
||||
private readonly NameValueCollection headers = new NameValueCollection(0);
|
||||
private MemoryStream dataIn;
|
||||
|
||||
|
@@ -4,12 +4,27 @@ using System;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
using TweetDuck.Configuration;
|
||||
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;
|
||||
|
||||
namespace TweetDuck.Core.Notification{
|
||||
partial class FormNotificationBase : Form{
|
||||
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 Point PrimaryLocation{
|
||||
get{
|
||||
UserConfig config = Program.UserConfig;
|
||||
@@ -71,25 +86,27 @@ namespace TweetDuck.Core.Notification{
|
||||
|
||||
protected double SizeScale => dpiScale*Program.UserConfig.ZoomMultiplier;
|
||||
|
||||
protected readonly Form owner;
|
||||
protected readonly FormBrowser owner;
|
||||
protected readonly ChromiumWebBrowser browser;
|
||||
|
||||
private readonly ResourceHandlerNotification resourceHandler = new ResourceHandlerNotification();
|
||||
private readonly float dpiScale;
|
||||
|
||||
private string currentColumn;
|
||||
private TweetNotification currentNotification;
|
||||
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 FreezeTimer { get; set; }
|
||||
public bool ContextMenuOpen { get; set; }
|
||||
public string CurrentTweetUrl { get; private set; }
|
||||
public string CurrentQuoteUrl { get; private set; }
|
||||
|
||||
public event EventHandler Initialized;
|
||||
|
||||
public FormNotificationBase(Form owner, bool enableContextMenu){
|
||||
protected FormNotificationBase(FormBrowser owner, bool enableContextMenu){
|
||||
InitializeComponent();
|
||||
|
||||
this.owner = owner;
|
||||
@@ -97,6 +114,7 @@ namespace TweetDuck.Core.Notification{
|
||||
|
||||
this.browser = new ChromiumWebBrowser("about:blank"){
|
||||
MenuHandler = new ContextMenuNotification(this, enableContextMenu),
|
||||
JsDialogHandler = new JavaScriptDialogHandler(),
|
||||
LifeSpanHandler = new LifeSpanHandler()
|
||||
};
|
||||
|
||||
@@ -111,7 +129,7 @@ namespace TweetDuck.Core.Notification{
|
||||
this.dpiScale = this.GetDPIScale();
|
||||
|
||||
DefaultResourceHandlerFactory handlerFactory = (DefaultResourceHandlerFactory)browser.ResourceHandlerFactory;
|
||||
handlerFactory.RegisterHandler(BrowserUtils.TweetDeckURL, this.resourceHandler);
|
||||
handlerFactory.RegisterHandler(TwitterUtils.TweetDeckURL, this.resourceHandler);
|
||||
|
||||
Controls.Add(browser);
|
||||
|
||||
@@ -140,7 +158,7 @@ namespace TweetDuck.Core.Notification{
|
||||
|
||||
private void Browser_IsBrowserInitializedChanged(object sender, IsBrowserInitializedChangedEventArgs e){
|
||||
if (e.IsBrowserInitialized){
|
||||
Initialized?.Invoke(this, new EventArgs());
|
||||
Initialized?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
int identifier = browser.GetBrowser().Identifier;
|
||||
Disposed += (sender2, args2) => BrowserProcesses.Forget(identifier);
|
||||
@@ -155,7 +173,7 @@ namespace TweetDuck.Core.Notification{
|
||||
}
|
||||
|
||||
Location = ControlExtensions.InvisibleLocation;
|
||||
currentColumn = null;
|
||||
currentNotification = null;
|
||||
}
|
||||
|
||||
public virtual void FinishCurrentNotification(){}
|
||||
@@ -178,24 +196,22 @@ namespace TweetDuck.Core.Notification{
|
||||
}
|
||||
|
||||
protected virtual void LoadTweet(TweetNotification tweet){
|
||||
CurrentTweetUrl = tweet.TweetUrl;
|
||||
CurrentQuoteUrl = tweet.QuoteUrl;
|
||||
currentColumn = tweet.Column;
|
||||
|
||||
currentNotification = tweet;
|
||||
resourceHandler.SetHTML(GetTweetHTML(tweet));
|
||||
browser.Load(BrowserUtils.TweetDeckURL);
|
||||
browser.Load(TwitterUtils.TweetDeckURL);
|
||||
}
|
||||
|
||||
protected virtual void SetNotificationSize(int width, int height){
|
||||
browser.ClientSize = ClientSize = new Size(BrowserUtils.Scale(width, SizeScale), BrowserUtils.Scale(height, SizeScale));
|
||||
}
|
||||
|
||||
protected virtual void OnNotificationReady(){
|
||||
MoveToVisibleLocation();
|
||||
protected virtual void UpdateTitle(){
|
||||
string title = currentNotification?.ColumnTitle;
|
||||
Text = string.IsNullOrEmpty(title) || !Program.UserConfig.DisplayNotificationColumn ? Program.BrandName : Program.BrandName+" - "+title;
|
||||
}
|
||||
|
||||
protected virtual void UpdateTitle(){
|
||||
Text = string.IsNullOrEmpty(currentColumn) || !Program.UserConfig.DisplayNotificationColumn ? Program.BrandName : Program.BrandName+" - "+currentColumn;
|
||||
public void ShowTweetDetail(){
|
||||
owner.ShowTweetDetail(currentNotification.ColumnId, currentNotification.ChirpId, currentNotification.TweetUrl);
|
||||
}
|
||||
|
||||
public void MoveToVisibleLocation(){
|
||||
|
@@ -4,6 +4,7 @@ using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
using TweetDuck.Core.Bridge;
|
||||
using TweetDuck.Core.Controls;
|
||||
using TweetDuck.Core.Handling;
|
||||
using TweetDuck.Core.Utils;
|
||||
using TweetDuck.Data;
|
||||
using TweetDuck.Plugins;
|
||||
@@ -16,10 +17,7 @@ namespace TweetDuck.Core.Notification{
|
||||
private const int TimerBarHeight = 4;
|
||||
|
||||
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 PluginJS = ScriptLoader.LoadResource(PluginManager.PluginNotificationScriptFile);
|
||||
|
||||
private readonly PluginManager plugins;
|
||||
|
||||
@@ -35,7 +33,7 @@ namespace TweetDuck.Core.Notification{
|
||||
|
||||
public bool RequiresResize{
|
||||
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 || CanResizeWindow;
|
||||
}
|
||||
|
||||
set{
|
||||
@@ -45,7 +43,7 @@ namespace TweetDuck.Core.Notification{
|
||||
}
|
||||
else{
|
||||
prevDisplayTimer = Program.UserConfig.DisplayNotificationTimer;
|
||||
prevFontSize = TweetNotification.FontSizeLevel;
|
||||
prevFontSize = FontSizeLevel;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,7 +52,7 @@ namespace TweetDuck.Core.Notification{
|
||||
get{
|
||||
switch(Program.UserConfig.NotificationSize){
|
||||
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:
|
||||
return Program.UserConfig.CustomNotificationSize.Width;
|
||||
@@ -66,7 +64,7 @@ namespace TweetDuck.Core.Notification{
|
||||
get{
|
||||
switch(Program.UserConfig.NotificationSize){
|
||||
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:
|
||||
return Program.UserConfig.CustomNotificationSize.Height;
|
||||
@@ -82,7 +80,9 @@ namespace TweetDuck.Core.Notification{
|
||||
InitializeComponent();
|
||||
|
||||
this.plugins = pluginManager;
|
||||
|
||||
|
||||
browser.KeyboardHandler = new KeyboardHandlerNotification(this);
|
||||
|
||||
browser.RegisterAsyncJsObject("$TD", new TweetDeckBridge(owner, this));
|
||||
browser.RegisterAsyncJsObject("$TDP", plugins.Bridge);
|
||||
|
||||
@@ -164,14 +164,9 @@ namespace TweetDuck.Core.Notification{
|
||||
|
||||
private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
|
||||
if (e.Frame.IsMain && NotificationJS != null && browser.Address != "about:blank"){
|
||||
e.Frame.ExecuteJavaScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Properties.AllNotification));
|
||||
e.Frame.ExecuteJavaScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Environment.Notification));
|
||||
ScriptLoader.ExecuteScript(e.Frame, NotificationJS, NotificationScriptIdentifier);
|
||||
|
||||
if (plugins.HasAnyPlugin(PluginEnvironment.Notification)){
|
||||
ScriptLoader.ExecuteScript(e.Frame, PluginJS, PluginScriptIdentifier);
|
||||
ScriptLoader.ExecuteFile(e.Frame, PluginManager.PluginGlobalScriptFile);
|
||||
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Notification, false);
|
||||
}
|
||||
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Notification);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,7 +279,7 @@ namespace TweetDuck.Core.Notification{
|
||||
StartMouseHook();
|
||||
}
|
||||
|
||||
protected override void OnNotificationReady(){
|
||||
protected virtual void OnNotificationReady(){
|
||||
PrepareAndDisplayWindow();
|
||||
timerProgress.Start();
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Windows.Forms;
|
||||
using CefSharp;
|
||||
using TweetDuck.Core.Bridge;
|
||||
using TweetDuck.Core.Other;
|
||||
using TweetDuck.Core.Utils;
|
||||
@@ -13,14 +14,16 @@ namespace TweetDuck.Core.Notification.Screenshot{
|
||||
sealed class FormNotificationScreenshotable : FormNotificationBase{
|
||||
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;
|
||||
|
||||
browser.RegisterAsyncJsObject("$TD_NotificationScreenshot", new CallbackBridge(this, callback));
|
||||
|
||||
browser.FrameLoadEnd += (sender, args) => {
|
||||
if (args.Frame.IsMain && browser.Address != "about:blank"){
|
||||
ScriptLoader.ExecuteScript(args.Frame, "window.setTimeout($TD_NotificationScreenshot.trigger, 67)", "gen:screenshot");
|
||||
browser.LoadingStateChanged += (sender, args) => {
|
||||
if (!args.IsLoading){
|
||||
using(IFrame frame = args.Browser.MainFrame){
|
||||
ScriptLoader.ExecuteScript(frame, "window.setTimeout($TD_NotificationScreenshot.trigger, document.getElementsByTagName('iframe').length ? 267 : 67)", "gen:screenshot");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@@ -8,14 +8,14 @@ using TweetDuck.Plugins;
|
||||
|
||||
namespace TweetDuck.Core.Notification.Screenshot{
|
||||
sealed class TweetScreenshotManager : IDisposable{
|
||||
private readonly Form owner;
|
||||
private readonly FormBrowser owner;
|
||||
private readonly PluginManager plugins;
|
||||
private readonly Timer timeout;
|
||||
private readonly Timer disposer;
|
||||
|
||||
private FormNotificationScreenshotable screenshot;
|
||||
|
||||
public TweetScreenshotManager(Form owner, PluginManager pluginManager){
|
||||
public TweetScreenshotManager(FormBrowser owner, PluginManager pluginManager){
|
||||
this.owner = owner;
|
||||
this.plugins = pluginManager;
|
||||
|
||||
@@ -28,8 +28,7 @@ namespace TweetDuck.Core.Notification.Screenshot{
|
||||
|
||||
private void timeout_Tick(object sender, EventArgs e){
|
||||
timeout.Stop();
|
||||
screenshot.Location = ControlExtensions.InvisibleLocation;
|
||||
disposer.Start();
|
||||
OnFinished();
|
||||
}
|
||||
|
||||
private void disposer_Tick(object sender, EventArgs e){
|
||||
@@ -47,9 +46,13 @@ namespace TweetDuck.Core.Notification.Screenshot{
|
||||
CanMoveWindow = () => false
|
||||
};
|
||||
|
||||
screenshot.LoadNotificationForScreenshot(new TweetNotification(string.Empty, html, 0, string.Empty, string.Empty), width, height);
|
||||
screenshot.LoadNotificationForScreenshot(new TweetNotification(string.Empty, string.Empty, string.Empty, html, 0, string.Empty, string.Empty), width, height);
|
||||
screenshot.Show();
|
||||
timeout.Start();
|
||||
|
||||
#if !(DEBUG && NO_HIDE_SCREENSHOTS)
|
||||
owner.IsWaiting = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
private void Callback(){
|
||||
@@ -61,14 +64,19 @@ namespace TweetDuck.Core.Notification.Screenshot{
|
||||
screenshot.TakeScreenshot();
|
||||
|
||||
#if !(DEBUG && NO_HIDE_SCREENSHOTS)
|
||||
screenshot.Location = ControlExtensions.InvisibleLocation;
|
||||
disposer.Start();
|
||||
OnFinished();
|
||||
#else
|
||||
screenshot.MoveToVisibleLocation();
|
||||
screenshot.FormClosed += (sender, args) => disposer.Start();
|
||||
#endif
|
||||
}
|
||||
|
||||
private void OnFinished(){
|
||||
screenshot.Location = ControlExtensions.InvisibleLocation;
|
||||
owner.IsWaiting = false;
|
||||
disposer.Start();
|
||||
}
|
||||
|
||||
public void Dispose(){
|
||||
timeout.Dispose();
|
||||
disposer.Dispose();
|
||||
|
@@ -17,6 +17,10 @@ namespace TweetDuck.Core.Notification{
|
||||
player.Play(file);
|
||||
}
|
||||
|
||||
public bool SetVolume(int volume){
|
||||
return player.SetVolume(volume);
|
||||
}
|
||||
|
||||
private void Player_PlaybackError(object sender, PlaybackErrorEventArgs e){
|
||||
PlaybackError?.Invoke(this, e);
|
||||
}
|
||||
|
@@ -1,27 +1,12 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using TweetDuck.Core.Bridge;
|
||||
using TweetDuck.Resources;
|
||||
|
||||
namespace TweetDuck.Core.Notification{
|
||||
sealed class TweetNotification{
|
||||
private static string FontSizeClass { get; set; }
|
||||
private static string HeadTag { get; set; }
|
||||
|
||||
private const string DefaultFontSizeClass = "medium";
|
||||
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>";
|
||||
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}";
|
||||
|
||||
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 const string DefaultHeadLayout = @"<html 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 readonly string CustomCSS = ScriptLoader.LoadResource("styles/notification.css");
|
||||
|
||||
private static string ExampleTweetHTML;
|
||||
|
||||
@@ -35,18 +20,10 @@ namespace TweetDuck.Core.Notification{
|
||||
#endif
|
||||
}
|
||||
|
||||
return new TweetNotification("Home", ExampleTweetHTML, 95, string.Empty, string.Empty, true);
|
||||
return new TweetNotification(string.Empty, string.Empty, "Home", ExampleTweetHTML, 95, string.Empty, string.Empty, true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetFontSizeClass(string newFSClass){
|
||||
FontSizeClass = newFSClass;
|
||||
}
|
||||
|
||||
public static void SetHeadTag(string headContents){
|
||||
HeadTag = headContents;
|
||||
}
|
||||
|
||||
public enum Position{
|
||||
TopLeft, TopRight, BottomLeft, BottomRight, Custom
|
||||
}
|
||||
@@ -55,7 +32,10 @@ namespace TweetDuck.Core.Notification{
|
||||
Auto, Custom
|
||||
}
|
||||
|
||||
public string Column { get; }
|
||||
public string ColumnId { get; }
|
||||
public string ChirpId { get; }
|
||||
|
||||
public string ColumnTitle { get; }
|
||||
public string TweetUrl { get; }
|
||||
public string QuoteUrl { get; }
|
||||
|
||||
@@ -63,10 +43,13 @@ namespace TweetDuck.Core.Notification{
|
||||
private readonly int characters;
|
||||
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){
|
||||
this.Column = column;
|
||||
private TweetNotification(string columnId, string chirpId, string title, string html, int characters, string tweetUrl, string quoteUrl, bool isExample){
|
||||
this.ColumnId = columnId;
|
||||
this.ChirpId = chirpId;
|
||||
|
||||
this.ColumnTitle = title;
|
||||
this.TweetUrl = tweetUrl;
|
||||
this.QuoteUrl = quoteUrl;
|
||||
|
||||
@@ -82,8 +65,7 @@ namespace TweetDuck.Core.Notification{
|
||||
public string GenerateHtml(string bodyClasses = null, bool enableCustomCSS = true){
|
||||
StringBuilder build = new StringBuilder();
|
||||
build.Append("<!DOCTYPE html>");
|
||||
build.Append("<html class='os-windows txt-base-").Append(FontSizeClass ?? DefaultFontSizeClass).Append("'>");
|
||||
build.Append("<head>").Append(HeadTag ?? DefaultHeadTag);
|
||||
build.Append(TweetDeckBridge.NotificationHeadLayout ?? DefaultHeadLayout);
|
||||
|
||||
if (enableCustomCSS){
|
||||
build.Append("<style type='text/css'>").Append(CustomCSS).Append("</style>");
|
||||
|
3
Core/Other/FormAbout.Designer.cs
generated
@@ -131,11 +131,14 @@ namespace TweetDuck.Core.Other {
|
||||
this.Controls.Add(this.labelDescription);
|
||||
this.Controls.Add(this.pictureLogo);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
||||
this.HelpButton = true;
|
||||
this.MaximizeBox = false;
|
||||
this.MinimizeBox = false;
|
||||
this.Name = "FormAbout";
|
||||
this.ShowIcon = false;
|
||||
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();
|
||||
this.tablePanelLinks.ResumeLayout(false);
|
||||
this.tablePanelLinks.PerformLayout();
|
||||
|
@@ -1,4 +1,6 @@
|
||||
using System.Windows.Forms;
|
||||
using System.ComponentModel;
|
||||
using System.Windows.Forms;
|
||||
using TweetDuck.Core.Controls;
|
||||
using TweetDuck.Core.Utils;
|
||||
|
||||
namespace TweetDuck.Core.Other{
|
||||
@@ -19,7 +21,21 @@ namespace TweetDuck.Core.Other{
|
||||
}
|
||||
|
||||
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(){
|
||||
new FormGuide().Show();
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
44
Core/Other/FormGuide.Designer.cs
generated
Normal file
@@ -0,0 +1,44 @@
|
||||
namespace TweetDuck.Core.Other {
|
||||
partial class FormGuide {
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing) {
|
||||
if (disposing && (components != null)) {
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent() {
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// FormGuide
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(34)))), ((int)(((byte)(34)))), ((int)(((byte)(34)))));
|
||||
this.ClientSize = new System.Drawing.Size(424, 282);
|
||||
this.Icon = global::TweetDuck.Properties.Resources.icon;
|
||||
this.MinimumSize = new System.Drawing.Size(440, 320);
|
||||
this.Name = "FormGuide";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
71
Core/Other/FormGuide.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
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.Utils;
|
||||
|
||||
namespace TweetDuck.Core.Other{
|
||||
sealed partial class FormGuide : Form{
|
||||
private const string GuideUrl = "https://tweetduck.chylex.com/guide/v1/";
|
||||
|
||||
private readonly ChromiumWebBrowser browser;
|
||||
|
||||
public FormGuide(){
|
||||
InitializeComponent();
|
||||
|
||||
Text = Program.BrandName+" Guide";
|
||||
|
||||
FormBrowser owner = FormManager.TryFind<FormBrowser>();
|
||||
|
||||
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(GuideUrl){
|
||||
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 browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
|
||||
if (!e.IsLoading){
|
||||
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 bool HasIcon => icon != null;
|
||||
public int ActionPanelY => panelActions.Location.Y;
|
||||
|
||||
private int ClientWidth{
|
||||
@@ -87,7 +88,7 @@ namespace TweetDuck.Core.Other{
|
||||
|
||||
this.prevLabelWidth = labelMessage.Width;
|
||||
this.prevLabelHeight = labelMessage.Height;
|
||||
this.minFormWidth = BrowserUtils.Scale(40, dpiScale);
|
||||
this.minFormWidth = BrowserUtils.Scale(42, dpiScale);
|
||||
|
||||
switch(messageIcon){
|
||||
case MessageBoxIcon.Information:
|
||||
@@ -108,7 +109,7 @@ namespace TweetDuck.Core.Other{
|
||||
|
||||
default:
|
||||
icon = null;
|
||||
labelMessage.Location = new Point(labelMessage.Location.X-38, labelMessage.Location.Y);
|
||||
labelMessage.Location = new Point(BrowserUtils.Scale(19, dpiScale), labelMessage.Location.Y); // 19 instead of 9 due to larger height
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -176,7 +177,7 @@ namespace TweetDuck.Core.Other{
|
||||
|
||||
private void RecalculateButtonLocation(){
|
||||
int dist = ButtonDistance;
|
||||
int start = ClientWidth-dist-BrowserUtils.Scale(1, dpiScale);
|
||||
int start = ClientWidth-dist;
|
||||
|
||||
for(int index = 0; index < buttonCount; index++){
|
||||
Control control = panelActions.Controls[index];
|
||||
@@ -201,7 +202,7 @@ namespace TweetDuck.Core.Other{
|
||||
prevLabelHeight -= labelOffset;
|
||||
}
|
||||
|
||||
realFormWidth = ClientWidth-(icon == null ? 50 : 0)+labelMessage.Width-prevLabelWidth;
|
||||
realFormWidth = ClientWidth-(icon == null ? BrowserUtils.Scale(50, dpiScale) : 0)+labelMessage.Width-prevLabelWidth;
|
||||
ClientWidth = Math.Max(realFormWidth, minFormWidth);
|
||||
Height += labelMessage.Height-prevLabelHeight;
|
||||
|
||||
@@ -212,7 +213,7 @@ namespace TweetDuck.Core.Other{
|
||||
|
||||
protected override void OnPaint(PaintEventArgs e){
|
||||
if (icon != null){
|
||||
e.Graphics.DrawIcon(icon, BrowserUtils.Scale(25, dpiScale), BrowserUtils.Scale(26, dpiScale));
|
||||
e.Graphics.DrawIcon(icon, BrowserUtils.Scale(25, dpiScale), 1+BrowserUtils.Scale(25, dpiScale));
|
||||
}
|
||||
|
||||
base.OnPaint(e);
|
||||
|
@@ -19,6 +19,8 @@ namespace TweetDuck.Core.Other{
|
||||
private readonly Dictionary<Type, SettingsTab> tabs = new Dictionary<Type, SettingsTab>(4);
|
||||
private SettingsTab currentTab;
|
||||
|
||||
public bool ShouldReloadBrowser { get; private set; }
|
||||
|
||||
public FormSettings(FormBrowser browser, PluginManager plugins, UpdateHandler updates, Type startTab){
|
||||
InitializeComponent();
|
||||
|
||||
@@ -32,8 +34,10 @@ namespace TweetDuck.Core.Other{
|
||||
this.buttonHeight = BrowserUtils.Scale(39, this.GetDPIScale()) | 1;
|
||||
|
||||
AddButton("General", () => new TabSettingsGeneral(updates));
|
||||
AddButton("System Tray", () => new TabSettingsTray());
|
||||
AddButton("Notifications", () => new TabSettingsNotifications(browser.CreateNotificationForm(false)));
|
||||
AddButton("Sounds", () => new TabSettingsSounds());
|
||||
AddButton("Feedback", () => new TabSettingsFeedback());
|
||||
AddButton("Advanced", () => new TabSettingsAdvanced(browser.ReinjectCustomCSS));
|
||||
|
||||
SelectTab(tabs[startTab ?? typeof(TabSettingsGeneral)]);
|
||||
@@ -52,13 +56,19 @@ namespace TweetDuck.Core.Other{
|
||||
}
|
||||
|
||||
private void btnManageOptions_Click(object sender, EventArgs e){
|
||||
using(DialogSettingsManage dialog = new DialogSettingsManage(plugins)){
|
||||
if (dialog.ShowDialog() == DialogResult.OK && dialog.ShouldReloadUI){
|
||||
foreach(SettingsTab tab in tabs.Values){
|
||||
tab.Control = null;
|
||||
}
|
||||
foreach(SettingsTab tab in tabs.Values){
|
||||
if (tab.IsInitialized){
|
||||
tab.Control.OnClosing();
|
||||
}
|
||||
}
|
||||
|
||||
SelectTab(currentTab);
|
||||
using(DialogSettingsManage dialog = new DialogSettingsManage(plugins)){
|
||||
if (dialog.ShowDialog() == DialogResult.OK){
|
||||
FormClosing -= FormSettings_FormClosing;
|
||||
browser.ResumeNotification();
|
||||
|
||||
ShouldReloadBrowser = dialog.ShouldReloadBrowser;
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,11 +130,16 @@ namespace TweetDuck.Core.Other{
|
||||
tab.Control.OnReady();
|
||||
}
|
||||
|
||||
panelContents.VerticalScroll.Enabled = false; // required to stop animation that would otherwise break everything
|
||||
panelContents.PerformLayout();
|
||||
|
||||
panelContents.SuspendLayout();
|
||||
panelContents.VerticalScroll.Value = 0; // https://gfycat.com/GrotesqueTastyAstarte
|
||||
panelContents.Controls.Clear();
|
||||
panelContents.Controls.Add(tab.Control);
|
||||
panelContents.ResumeLayout(true);
|
||||
|
||||
panelContents.VerticalScroll.Enabled = true;
|
||||
panelContents.Focus();
|
||||
|
||||
currentTab = tab;
|
||||
@@ -139,7 +154,7 @@ namespace TweetDuck.Core.Other{
|
||||
panelContents.Focus();
|
||||
}
|
||||
|
||||
private class SettingsTab{
|
||||
private sealed class SettingsTab{
|
||||
public Button Button { get; }
|
||||
|
||||
public BaseTabSettings Control{
|
||||
|
@@ -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.Diagnostics;
|
||||
using CefSharp;
|
||||
using TweetDuck.Core.Utils;
|
||||
|
||||
namespace TweetDuck.Core.Utils{
|
||||
namespace TweetDuck.Core.Other.Management{
|
||||
static class BrowserProcesses{
|
||||
private static readonly Dictionary<int, int> PIDs = new Dictionary<int, int>();
|
||||
|
@@ -5,7 +5,7 @@ using System.Windows.Forms;
|
||||
using CefSharp;
|
||||
using Timer = System.Timers.Timer;
|
||||
|
||||
namespace TweetDuck.Core.Utils{
|
||||
namespace TweetDuck.Core.Other.Management{
|
||||
sealed class MemoryUsageTracker : IDisposable{
|
||||
private const int IntervalMemoryCheck = 60000*30; // 30 minutes
|
||||
private const int IntervalCleanupAttempt = 60000*5; // 5 minutes
|
183
Core/Other/Management/VideoPlayer.cs
Normal file
@@ -0,0 +1,183 @@
|
||||
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{
|
||||
private readonly string PlayerExe = Path.Combine(Program.ProgramPath, "TweetDuck.Video.exe");
|
||||
|
||||
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 = PlayerExe,
|
||||
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);
|
||||
}
|
||||
|
||||
|
@@ -35,7 +35,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
||||
}
|
||||
|
||||
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){
|
||||
|
@@ -2,6 +2,7 @@
|
||||
using System.Windows.Forms;
|
||||
using TweetDuck.Core.Controls;
|
||||
using TweetDuck.Core.Utils;
|
||||
using TweetDuck.Data;
|
||||
|
||||
namespace TweetDuck.Core.Other.Settings.Dialogs{
|
||||
sealed partial class DialogSettingsCefArgs : Form{
|
||||
@@ -18,7 +19,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
||||
}
|
||||
|
||||
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){
|
||||
@@ -30,7 +31,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
||||
return;
|
||||
}
|
||||
|
||||
int count = CommandLineArgsParser.ReadCefArguments(CefArgs).Count;
|
||||
int count = CommandLineArgs.ReadCefArguments(CefArgs).Count;
|
||||
string prompt = count == 0 && !string.IsNullOrWhiteSpace(prevArgs) ? "All current arguments will be removed. Continue?" : count+(count == 1 ? " argument was" : " arguments were")+" detected. Continue?";
|
||||
|
||||
if (FormMessage.Question("Confirm CEF Arguments", prompt, FormMessage.OK, FormMessage.Cancel)){
|
||||
|
@@ -1,12 +1,14 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
using TweetDuck.Configuration;
|
||||
using TweetDuck.Core.Other.Settings.Export;
|
||||
using TweetDuck.Plugins;
|
||||
|
||||
namespace TweetDuck.Core.Other.Settings.Dialogs{
|
||||
sealed partial class DialogSettingsManage : Form{
|
||||
private enum State{
|
||||
Deciding, Import, Export
|
||||
Deciding, Reset, Import, Export
|
||||
}
|
||||
|
||||
public ExportFileFlags Flags{
|
||||
@@ -20,7 +22,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShouldReloadUI { get; private set; }
|
||||
public bool ShouldReloadBrowser { get; private set; }
|
||||
|
||||
private readonly PluginManager plugins;
|
||||
private State currentState;
|
||||
@@ -58,15 +60,10 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
||||
case State.Deciding:
|
||||
// Reset
|
||||
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)){
|
||||
Program.ResetConfig();
|
||||
currentState = State.Reset;
|
||||
|
||||
ShouldReloadUI = true;
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
}
|
||||
|
||||
return;
|
||||
Text = "Restore Defaults";
|
||||
Flags = ExportFileFlags.Config;
|
||||
}
|
||||
|
||||
// Import
|
||||
@@ -109,10 +106,43 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
||||
panelExport.Visible = true;
|
||||
break;
|
||||
|
||||
case State.Reset:
|
||||
if (FormMessage.Warning("Reset TweetDuck Options", "This will reset the selected items. Are you sure you want to proceed?", FormMessage.Yes, FormMessage.No)){
|
||||
if (Flags.HasFlag(ExportFileFlags.Config)){
|
||||
Program.ResetConfig();
|
||||
}
|
||||
|
||||
if (Flags.HasFlag(ExportFileFlags.PluginData)){
|
||||
try{
|
||||
File.Delete(Program.PluginConfigFilePath);
|
||||
Directory.Delete(Program.PluginDataPath, true);
|
||||
}catch(Exception ex){
|
||||
Program.Reporter.HandleException("Plugin Data Reset Error", "Could not delete plugin data.", true, ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (Flags.HasFlag(ExportFileFlags.Session)){
|
||||
Program.Restart(Arguments.ArgDeleteCookies);
|
||||
}
|
||||
else{
|
||||
ShouldReloadBrowser = true;
|
||||
}
|
||||
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case State.Import:
|
||||
if (importManager.Import(Flags)){
|
||||
if (!importManager.IsRestarting){
|
||||
ShouldReloadUI = true;
|
||||
Program.UserConfig.Reload();
|
||||
|
||||
if (importManager.IsRestarting){
|
||||
Program.Restart(Arguments.ArgImportCookies);
|
||||
}
|
||||
else{
|
||||
ShouldReloadBrowser = true;
|
||||
}
|
||||
}
|
||||
else{
|
||||
@@ -165,6 +195,9 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
||||
if (currentState == State.Import){
|
||||
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.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
||||
this.cbDebugUpdates = new System.Windows.Forms.CheckBox();
|
||||
this.labelLocale = new System.Windows.Forms.Label();
|
||||
this.comboLocale = new System.Windows.Forms.ComboBox();
|
||||
this.labelDataFolder = new System.Windows.Forms.Label();
|
||||
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();
|
||||
//
|
||||
// btnCancel
|
||||
//
|
||||
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.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||
this.btnCancel.Size = new System.Drawing.Size(56, 23);
|
||||
this.btnCancel.TabIndex = 7;
|
||||
this.btnCancel.TabIndex = 9;
|
||||
this.btnCancel.Text = "Cancel";
|
||||
this.btnCancel.UseVisualStyleBackColor = true;
|
||||
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
|
||||
@@ -50,11 +52,11 @@
|
||||
// btnRestart
|
||||
//
|
||||
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.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||
this.btnRestart.Size = new System.Drawing.Size(57, 23);
|
||||
this.btnRestart.TabIndex = 6;
|
||||
this.btnRestart.TabIndex = 8;
|
||||
this.btnRestart.Text = "Restart";
|
||||
this.btnRestart.UseVisualStyleBackColor = true;
|
||||
this.btnRestart.Click += new System.EventHandler(this.btnRestart_Click);
|
||||
@@ -67,7 +69,7 @@
|
||||
this.cbLogging.Size = new System.Drawing.Size(64, 17);
|
||||
this.cbLogging.TabIndex = 0;
|
||||
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;
|
||||
//
|
||||
// cbDebugUpdates
|
||||
@@ -81,6 +83,40 @@
|
||||
this.toolTip.SetToolTip(this.cbDebugUpdates, "Allows updating to pre-releases.");
|
||||
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
|
||||
//
|
||||
this.labelLocale.AutoSize = true;
|
||||
@@ -91,16 +127,6 @@
|
||||
this.labelLocale.TabIndex = 2;
|
||||
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
|
||||
//
|
||||
this.labelDataFolder.AutoSize = true;
|
||||
@@ -111,20 +137,23 @@
|
||||
this.labelDataFolder.TabIndex = 4;
|
||||
this.labelDataFolder.Text = "Data Folder";
|
||||
//
|
||||
// tbDataFolder
|
||||
// labelShortcutTarget
|
||||
//
|
||||
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(201, 20);
|
||||
this.tbDataFolder.TabIndex = 5;
|
||||
this.labelShortcutTarget.AutoSize = true;
|
||||
this.labelShortcutTarget.Location = new System.Drawing.Point(12, 170);
|
||||
this.labelShortcutTarget.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
|
||||
this.labelShortcutTarget.Name = "labelShortcutTarget";
|
||||
this.labelShortcutTarget.Size = new System.Drawing.Size(155, 13);
|
||||
this.labelShortcutTarget.TabIndex = 6;
|
||||
this.labelShortcutTarget.Text = "Shortcut Target (click to select)";
|
||||
//
|
||||
// DialogSettingsRestart
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
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.labelDataFolder);
|
||||
this.Controls.Add(this.comboLocale);
|
||||
@@ -155,5 +184,7 @@
|
||||
private System.Windows.Forms.ComboBox comboLocale;
|
||||
private System.Windows.Forms.Label labelDataFolder;
|
||||
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);
|
||||
cbDebugUpdates.Checked = currentArgs.HasFlag(Arguments.ArgDebugUpdates);
|
||||
comboLocale.SelectedItem = currentArgs.GetValue(Arguments.ArgLocale, DefaultLocale);
|
||||
tbDataFolder.Text = currentArgs.GetValue(Arguments.ArgDataFolder, string.Empty);
|
||||
|
||||
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.TextChanged += control_Change;
|
||||
}
|
||||
|
||||
control_Change(this, EventArgs.Empty);
|
||||
|
||||
Text = Program.BrandName+" Arguments";
|
||||
}
|
||||
|
||||
private void btnRestart_Click(object sender, EventArgs e){
|
||||
private void control_Change(object sender, EventArgs e){
|
||||
Args = new CommandLineArgs();
|
||||
|
||||
if (cbLogging.Checked){
|
||||
@@ -46,10 +60,21 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
||||
Args.SetValue(Arguments.ArgLocale, locale);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tbDataFolder.Text)){
|
||||
if (!string.IsNullOrWhiteSpace(tbDataFolder.Text) && tbDataFolder.Enabled){
|
||||
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;
|
||||
Close();
|
||||
}
|
||||
|
@@ -2,7 +2,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using TweetDuck.Configuration;
|
||||
using TweetDuck.Data;
|
||||
using TweetDuck.Plugins;
|
||||
using TweetDuck.Plugins.Enums;
|
||||
@@ -141,13 +140,6 @@ namespace TweetDuck.Core.Other.Settings.Export{
|
||||
FormMessage.Information("Importing TweetDuck Profile", "Detected missing plugins when importing plugin data:\n"+string.Join("\n", missingPlugins), FormMessage.OK);
|
||||
}
|
||||
|
||||
if (IsRestarting){
|
||||
Program.Restart(Arguments.ArgImportCookies);
|
||||
}
|
||||
else{
|
||||
Program.ReloadConfig();
|
||||
}
|
||||
|
||||
return true;
|
||||
}catch(Exception e){
|
||||
LastException = e;
|
||||
@@ -169,6 +161,16 @@ namespace TweetDuck.Core.Other.Settings.Export{
|
||||
}
|
||||
}
|
||||
|
||||
public static void DeleteCookies(){
|
||||
try{
|
||||
if (File.Exists(CookiesPath)){
|
||||
File.Delete(CookiesPath);
|
||||
}
|
||||
}catch(Exception e){
|
||||
Program.Reporter.HandleException("Session Reset Error", "Could not remove the cookie file to reset the login session.", true, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<PathInfo> EnumerateFilesRelative(string root){
|
||||
return Directory.Exists(root) ? Directory.EnumerateFiles(root, "*.*", SearchOption.AllDirectories).Select(fullPath => new PathInfo{
|
||||
Full = fullPath,
|
||||
@@ -176,7 +178,7 @@ namespace TweetDuck.Core.Other.Settings.Export{
|
||||
}) : Enumerable.Empty<PathInfo>();
|
||||
}
|
||||
|
||||
private class PathInfo{
|
||||
private sealed class PathInfo{
|
||||
public string Full { get; set; }
|
||||
public string Relative { get; set; }
|
||||
}
|
||||
|
22
Core/Other/Settings/TabSettingsAdvanced.Designer.cs
generated
@@ -68,7 +68,7 @@
|
||||
this.checkHardwareAcceleration.Size = new System.Drawing.Size(134, 17);
|
||||
this.checkHardwareAcceleration.TabIndex = 0;
|
||||
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;
|
||||
//
|
||||
// btnEditCefArgs
|
||||
@@ -142,26 +142,14 @@
|
||||
0,
|
||||
0});
|
||||
this.numMemoryThreshold.Location = new System.Drawing.Point(202, 82);
|
||||
this.numMemoryThreshold.Maximum = new decimal(new int[] {
|
||||
3000,
|
||||
0,
|
||||
0,
|
||||
0});
|
||||
this.numMemoryThreshold.Minimum = new decimal(new int[] {
|
||||
200,
|
||||
0,
|
||||
0,
|
||||
0});
|
||||
this.numMemoryThreshold.Maximum = 2000;
|
||||
this.numMemoryThreshold.Minimum = 200;
|
||||
this.numMemoryThreshold.Name = "numMemoryThreshold";
|
||||
this.numMemoryThreshold.Size = new System.Drawing.Size(97, 20);
|
||||
this.numMemoryThreshold.TabIndex = 4;
|
||||
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.numMemoryThreshold.Value = new decimal(new int[] {
|
||||
350,
|
||||
0,
|
||||
0,
|
||||
0});
|
||||
this.numMemoryThreshold.Value = 400;
|
||||
//
|
||||
// checkBrowserGCReload
|
||||
//
|
||||
@@ -172,7 +160,7 @@
|
||||
this.checkBrowserGCReload.Size = new System.Drawing.Size(190, 17);
|
||||
this.checkBrowserGCReload.TabIndex = 3;
|
||||
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;
|
||||
//
|
||||
// labelApp
|
||||
|
@@ -7,7 +7,9 @@ using TweetDuck.Core.Other.Settings.Dialogs;
|
||||
using TweetDuck.Core.Utils;
|
||||
|
||||
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;
|
||||
|
||||
public TabSettingsAdvanced(Action<string> reinjectBrowserCSS){
|
||||
@@ -16,16 +18,16 @@ namespace TweetDuck.Core.Other.Settings{
|
||||
this.reinjectBrowserCSS = reinjectBrowserCSS;
|
||||
|
||||
if (SystemConfig.IsHardwareAccelerationSupported){
|
||||
checkHardwareAcceleration.Checked = Program.SystemConfig.HardwareAcceleration;
|
||||
checkHardwareAcceleration.Checked = SysConfig.HardwareAcceleration;
|
||||
}
|
||||
else{
|
||||
checkHardwareAcceleration.Enabled = false;
|
||||
checkHardwareAcceleration.Checked = false;
|
||||
}
|
||||
|
||||
checkBrowserGCReload.Checked = Config.EnableBrowserGCReload;
|
||||
checkBrowserGCReload.Checked = SysConfig.EnableBrowserGCReload;
|
||||
numMemoryThreshold.Enabled = checkBrowserGCReload.Checked;
|
||||
numMemoryThreshold.SetValueSafe(Config.BrowserMemoryThreshold);
|
||||
numMemoryThreshold.SetValueSafe(SysConfig.BrowserMemoryThreshold);
|
||||
|
||||
BrowserCache.CalculateCacheSize(bytes => this.InvokeSafe(() => {
|
||||
if (bytes == -1L){
|
||||
@@ -53,6 +55,10 @@ namespace TweetDuck.Core.Other.Settings{
|
||||
btnRestartArgs.Click += btnRestartArgs_Click;
|
||||
}
|
||||
|
||||
public override void OnClosing(){
|
||||
SysConfig.Save();
|
||||
}
|
||||
|
||||
private void btnClearCache_Click(object sender, EventArgs e){
|
||||
btnClearCache.Enabled = false;
|
||||
BrowserCache.SetClearOnExit();
|
||||
@@ -60,18 +66,17 @@ namespace TweetDuck.Core.Other.Settings{
|
||||
}
|
||||
|
||||
private void checkHardwareAcceleration_CheckedChanged(object sender, EventArgs e){
|
||||
Program.SystemConfig.HardwareAcceleration = checkHardwareAcceleration.Checked;
|
||||
Program.SystemConfig.Save();
|
||||
PromptRestart();
|
||||
SysConfig.HardwareAcceleration = checkHardwareAcceleration.Checked;
|
||||
PromptRestart(); // calls OnClosing
|
||||
}
|
||||
|
||||
private void checkBrowserGCReload_CheckedChanged(object sender, EventArgs e){
|
||||
Config.EnableBrowserGCReload = checkBrowserGCReload.Checked;
|
||||
SysConfig.EnableBrowserGCReload = checkBrowserGCReload.Checked;
|
||||
numMemoryThreshold.Enabled = checkBrowserGCReload.Checked;
|
||||
}
|
||||
|
||||
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){
|
||||
@@ -82,7 +87,7 @@ namespace TweetDuck.Core.Other.Settings{
|
||||
};
|
||||
|
||||
form.FormClosed += (sender2, args2) => {
|
||||
NativeMethods.SetFormDisabled(ParentForm, false);
|
||||
RestoreParentForm();
|
||||
|
||||
if (form.DialogResult == DialogResult.OK){
|
||||
Config.CustomCefArgs = form.CefArgs;
|
||||
@@ -104,7 +109,7 @@ namespace TweetDuck.Core.Other.Settings{
|
||||
};
|
||||
|
||||
form.FormClosed += (sender2, args2) => {
|
||||
NativeMethods.SetFormDisabled(ParentForm, false);
|
||||
RestoreParentForm();
|
||||
|
||||
if (form.DialogResult == DialogResult.OK){
|
||||
Config.CustomBrowserCSS = form.BrowserCSS;
|
||||
@@ -138,5 +143,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
134
Core/Other/Settings/TabSettingsFeedback.Designer.cs
generated
Normal file
@@ -0,0 +1,134 @@
|
||||
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.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.btnSendFeedback = new System.Windows.Forms.Button();
|
||||
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.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, 80);
|
||||
this.panelFeedback.TabIndex = 1;
|
||||
//
|
||||
// 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;
|
||||
this.labelDataCollectionLink.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.labelDataCollectionLink_LinkClicked);
|
||||
//
|
||||
// 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";
|
||||
//
|
||||
// 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;
|
||||
//
|
||||
// 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, 122);
|
||||
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;
|
||||
}
|
||||
}
|
30
Core/Other/Settings/TabSettingsFeedback.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using TweetDuck.Core.Utils;
|
||||
|
||||
namespace TweetDuck.Core.Other.Settings{
|
||||
sealed partial class TabSettingsFeedback : BaseTabSettings{
|
||||
public TabSettingsFeedback(){
|
||||
InitializeComponent();
|
||||
|
||||
checkDataCollection.Checked = Config.AllowDataCollection;
|
||||
}
|
||||
|
||||
public override void OnReady(){
|
||||
btnSendFeedback.Click += btnSendFeedback_Click;
|
||||
checkDataCollection.CheckedChanged += checkDataCollection_CheckedChanged;
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
127
Core/Other/Settings/TabSettingsGeneral.Designer.cs
generated
@@ -25,28 +25,24 @@
|
||||
private void InitializeComponent() {
|
||||
this.components = new System.ComponentModel.Container();
|
||||
this.checkExpandLinks = new System.Windows.Forms.CheckBox();
|
||||
this.comboBoxTrayType = new System.Windows.Forms.ComboBox();
|
||||
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
||||
this.checkTrayHighlight = new System.Windows.Forms.CheckBox();
|
||||
this.checkSpellCheck = new System.Windows.Forms.CheckBox();
|
||||
this.checkUpdateNotifications = new System.Windows.Forms.CheckBox();
|
||||
this.btnCheckUpdates = new System.Windows.Forms.Button();
|
||||
this.labelZoomValue = new System.Windows.Forms.Label();
|
||||
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.labelZoom = new System.Windows.Forms.Label();
|
||||
this.zoomUpdateTimer = new System.Windows.Forms.Timer(this.components);
|
||||
this.labelUI = new System.Windows.Forms.Label();
|
||||
this.panelUI = new System.Windows.Forms.Panel();
|
||||
this.labelTray = new System.Windows.Forms.Label();
|
||||
this.panelUpdates = new System.Windows.Forms.Panel();
|
||||
this.panelTray = new System.Windows.Forms.Panel();
|
||||
this.labelUpdates = new System.Windows.Forms.Label();
|
||||
((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).BeginInit();
|
||||
this.panelUI.SuspendLayout();
|
||||
this.panelUpdates.SuspendLayout();
|
||||
this.panelTray.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// checkExpandLinks
|
||||
@@ -61,37 +57,14 @@
|
||||
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;
|
||||
//
|
||||
// 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
|
||||
//
|
||||
this.checkSpellCheck.AutoSize = true;
|
||||
this.checkSpellCheck.Location = new System.Drawing.Point(6, 51);
|
||||
this.checkSpellCheck.Location = new System.Drawing.Point(6, 97);
|
||||
this.checkSpellCheck.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
|
||||
this.checkSpellCheck.Name = "checkSpellCheck";
|
||||
this.checkSpellCheck.Size = new System.Drawing.Size(119, 17);
|
||||
this.checkSpellCheck.TabIndex = 2;
|
||||
this.checkSpellCheck.TabIndex = 4;
|
||||
this.checkSpellCheck.Text = "Enable Spell Check";
|
||||
this.toolTip.SetToolTip(this.checkSpellCheck, "Underlines words that are spelled incorrectly.");
|
||||
this.checkSpellCheck.UseVisualStyleBackColor = true;
|
||||
@@ -122,11 +95,11 @@
|
||||
// labelZoomValue
|
||||
//
|
||||
this.labelZoomValue.BackColor = System.Drawing.Color.Transparent;
|
||||
this.labelZoomValue.Location = new System.Drawing.Point(141, 100);
|
||||
this.labelZoomValue.Location = new System.Drawing.Point(147, 146);
|
||||
this.labelZoomValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
|
||||
this.labelZoomValue.Name = "labelZoomValue";
|
||||
this.labelZoomValue.Size = new System.Drawing.Size(38, 13);
|
||||
this.labelZoomValue.TabIndex = 5;
|
||||
this.labelZoomValue.TabIndex = 7;
|
||||
this.labelZoomValue.Text = "100%";
|
||||
this.labelZoomValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
|
||||
this.toolTip.SetToolTip(this.labelZoomValue, "Changes the zoom level.\r\nAlso affects notifications and screenshots.");
|
||||
@@ -143,39 +116,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.checkSwitchAccountSelectors.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// labelTrayIcon
|
||||
// checkBestImageQuality
|
||||
//
|
||||
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";
|
||||
this.checkBestImageQuality.AutoSize = true;
|
||||
this.checkBestImageQuality.Location = new System.Drawing.Point(6, 74);
|
||||
this.checkBestImageQuality.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
|
||||
this.checkBestImageQuality.Name = "checkBestImageQuality";
|
||||
this.checkBestImageQuality.Size = new System.Drawing.Size(114, 17);
|
||||
this.checkBestImageQuality.TabIndex = 3;
|
||||
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
|
||||
//
|
||||
this.trackBarZoom.AutoSize = false;
|
||||
this.trackBarZoom.BackColor = System.Drawing.SystemColors.Control;
|
||||
this.trackBarZoom.LargeChange = 25;
|
||||
this.trackBarZoom.Location = new System.Drawing.Point(3, 99);
|
||||
this.trackBarZoom.Location = new System.Drawing.Point(3, 145);
|
||||
this.trackBarZoom.Maximum = 200;
|
||||
this.trackBarZoom.Minimum = 50;
|
||||
this.trackBarZoom.Name = "trackBarZoom";
|
||||
this.trackBarZoom.Size = new System.Drawing.Size(148, 30);
|
||||
this.trackBarZoom.SmallChange = 5;
|
||||
this.trackBarZoom.TabIndex = 4;
|
||||
this.trackBarZoom.TabIndex = 6;
|
||||
this.trackBarZoom.TickFrequency = 25;
|
||||
this.trackBarZoom.Value = 100;
|
||||
//
|
||||
// labelZoom
|
||||
//
|
||||
this.labelZoom.AutoSize = true;
|
||||
this.labelZoom.Location = new System.Drawing.Point(3, 83);
|
||||
this.labelZoom.Location = new System.Drawing.Point(3, 129);
|
||||
this.labelZoom.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
|
||||
this.labelZoom.Name = "labelZoom";
|
||||
this.labelZoom.Size = new System.Drawing.Size(34, 13);
|
||||
this.labelZoom.TabIndex = 3;
|
||||
this.labelZoom.TabIndex = 5;
|
||||
this.labelZoom.Text = "Zoom";
|
||||
//
|
||||
// zoomUpdateTimer
|
||||
@@ -198,60 +185,39 @@
|
||||
//
|
||||
this.panelUI.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.panelUI.Controls.Add(this.checkOpenSearchInFirstColumn);
|
||||
this.panelUI.Controls.Add(this.checkBestImageQuality);
|
||||
this.panelUI.Controls.Add(this.checkExpandLinks);
|
||||
this.panelUI.Controls.Add(this.checkSwitchAccountSelectors);
|
||||
this.panelUI.Controls.Add(this.checkSpellCheck);
|
||||
this.panelUI.Controls.Add(this.labelZoom);
|
||||
this.panelUI.Controls.Add(this.labelZoomValue);
|
||||
this.panelUI.Controls.Add(this.trackBarZoom);
|
||||
this.panelUI.Controls.Add(this.labelZoomValue);
|
||||
this.panelUI.Location = new System.Drawing.Point(9, 31);
|
||||
this.panelUI.Name = "panelUI";
|
||||
this.panelUI.Size = new System.Drawing.Size(322, 134);
|
||||
this.panelUI.Size = new System.Drawing.Size(322, 179);
|
||||
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(5, 189);
|
||||
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
|
||||
//
|
||||
this.panelUpdates.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.panelUpdates.Controls.Add(this.checkUpdateNotifications);
|
||||
this.panelUpdates.Controls.Add(this.btnCheckUpdates);
|
||||
this.panelUpdates.Location = new System.Drawing.Point(9, 335);
|
||||
this.panelUpdates.Location = new System.Drawing.Point(9, 257);
|
||||
this.panelUpdates.Name = "panelUpdates";
|
||||
this.panelUpdates.Size = new System.Drawing.Size(322, 55);
|
||||
this.panelUpdates.TabIndex = 5;
|
||||
//
|
||||
// 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, 212);
|
||||
this.panelTray.Name = "panelTray";
|
||||
this.panelTray.Size = new System.Drawing.Size(322, 76);
|
||||
this.panelTray.TabIndex = 3;
|
||||
this.panelUpdates.TabIndex = 3;
|
||||
//
|
||||
// labelUpdates
|
||||
//
|
||||
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.Location = new System.Drawing.Point(6, 312);
|
||||
this.labelUpdates.Location = new System.Drawing.Point(6, 234);
|
||||
this.labelUpdates.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
|
||||
this.labelUpdates.Name = "labelUpdates";
|
||||
this.labelUpdates.Size = new System.Drawing.Size(70, 20);
|
||||
this.labelUpdates.TabIndex = 4;
|
||||
this.labelUpdates.TabIndex = 2;
|
||||
this.labelUpdates.Text = "Updates";
|
||||
//
|
||||
// TabSettingsGeneral
|
||||
@@ -259,20 +225,16 @@
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.Controls.Add(this.labelUpdates);
|
||||
this.Controls.Add(this.panelTray);
|
||||
this.Controls.Add(this.panelUpdates);
|
||||
this.Controls.Add(this.labelTray);
|
||||
this.Controls.Add(this.panelUI);
|
||||
this.Controls.Add(this.labelUI);
|
||||
this.Name = "TabSettingsGeneral";
|
||||
this.Size = new System.Drawing.Size(340, 400);
|
||||
this.Size = new System.Drawing.Size(340, 322);
|
||||
((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).EndInit();
|
||||
this.panelUI.ResumeLayout(false);
|
||||
this.panelUI.PerformLayout();
|
||||
this.panelUpdates.ResumeLayout(false);
|
||||
this.panelUpdates.PerformLayout();
|
||||
this.panelTray.ResumeLayout(false);
|
||||
this.panelTray.PerformLayout();
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
@@ -281,10 +243,7 @@
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.CheckBox checkExpandLinks;
|
||||
private System.Windows.Forms.ComboBox comboBoxTrayType;
|
||||
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 checkUpdateNotifications;
|
||||
private System.Windows.Forms.Button btnCheckUpdates;
|
||||
@@ -295,9 +254,9 @@
|
||||
private System.Windows.Forms.CheckBox checkSwitchAccountSelectors;
|
||||
private System.Windows.Forms.Label labelUI;
|
||||
private System.Windows.Forms.Panel panelUI;
|
||||
private System.Windows.Forms.Label labelTray;
|
||||
private System.Windows.Forms.Panel panelUpdates;
|
||||
private System.Windows.Forms.Panel panelTray;
|
||||
private System.Windows.Forms.Label labelUpdates;
|
||||
private System.Windows.Forms.CheckBox checkBestImageQuality;
|
||||
private System.Windows.Forms.CheckBox checkOpenSearchInFirstColumn;
|
||||
}
|
||||
}
|
||||
|
@@ -1,10 +1,9 @@
|
||||
using System;
|
||||
using TweetDuck.Core.Controls;
|
||||
using TweetDuck.Updates;
|
||||
using TweetDuck.Updates.Events;
|
||||
|
||||
namespace TweetDuck.Core.Other.Settings{
|
||||
partial class TabSettingsGeneral : BaseTabSettings{
|
||||
sealed partial class TabSettingsGeneral : BaseTabSettings{
|
||||
private readonly UpdateHandler updates;
|
||||
private int updateCheckEventId = -1;
|
||||
|
||||
@@ -15,21 +14,15 @@ namespace TweetDuck.Core.Other.Settings{
|
||||
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));
|
||||
trackBarZoom.SetValueSafe(Config.ZoomLevel);
|
||||
labelZoomValue.Text = trackBarZoom.Value+"%";
|
||||
|
||||
checkExpandLinks.Checked = Config.ExpandLinksOnHover;
|
||||
checkSwitchAccountSelectors.Checked = Config.SwitchAccountSelectors;
|
||||
checkOpenSearchInFirstColumn.Checked = Config.OpenSearchInFirstColumn;
|
||||
checkBestImageQuality.Checked = Config.BestImageQuality;
|
||||
checkSpellCheck.Checked = Config.EnableSpellCheck;
|
||||
checkTrayHighlight.Checked = Config.EnableTrayHighlight;
|
||||
|
||||
checkUpdateNotifications.Checked = Config.EnableUpdateCheck;
|
||||
}
|
||||
@@ -37,12 +30,11 @@ namespace TweetDuck.Core.Other.Settings{
|
||||
public override void OnReady(){
|
||||
checkExpandLinks.CheckedChanged += checkExpandLinks_CheckedChanged;
|
||||
checkSwitchAccountSelectors.CheckedChanged += checkSwitchAccountSelectors_CheckedChanged;
|
||||
checkOpenSearchInFirstColumn.CheckedChanged += checkOpenSearchInFirstColumn_CheckedChanged;
|
||||
checkBestImageQuality.CheckedChanged += checkBestImageQuality_CheckedChanged;
|
||||
checkSpellCheck.CheckedChanged += checkSpellCheck_CheckedChanged;
|
||||
trackBarZoom.ValueChanged += trackBarZoom_ValueChanged;
|
||||
|
||||
comboBoxTrayType.SelectedIndexChanged += comboBoxTrayType_SelectedIndexChanged;
|
||||
checkTrayHighlight.CheckedChanged += checkTrayHighlight_CheckedChanged;
|
||||
|
||||
checkUpdateNotifications.CheckedChanged += checkUpdateNotifications_CheckedChanged;
|
||||
btnCheckUpdates.Click += btnCheckUpdates_Click;
|
||||
}
|
||||
@@ -59,6 +51,14 @@ namespace TweetDuck.Core.Other.Settings{
|
||||
Config.SwitchAccountSelectors = checkSwitchAccountSelectors.Checked;
|
||||
}
|
||||
|
||||
private void checkOpenSearchInFirstColumn_CheckedChanged(object sender, EventArgs e){
|
||||
Config.OpenSearchInFirstColumn = checkOpenSearchInFirstColumn.Checked;
|
||||
}
|
||||
|
||||
private void checkBestImageQuality_CheckedChanged(object sender, EventArgs e){
|
||||
Config.BestImageQuality = checkBestImageQuality.Checked;
|
||||
}
|
||||
|
||||
private void checkSpellCheck_CheckedChanged(object sender, EventArgs e){
|
||||
Config.EnableSpellCheck = checkSpellCheck.Checked;
|
||||
PromptRestart();
|
||||
@@ -72,36 +72,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){
|
||||
Config.EnableUpdateCheck = checkUpdateNotifications.Checked;
|
||||
}
|
||||
|
||||
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;
|
||||
updates.DismissUpdate(string.Empty);
|
||||
}
|
||||
btnCheckUpdates.Enabled = false;
|
||||
updateCheckEventId = updates.Check(true);
|
||||
}
|
||||
|
||||
private void updates_CheckFinished(object sender, UpdateCheckEventArgs e){
|
||||
private void updates_CheckFinished(object sender, UpdateEventArgs e){
|
||||
this.InvokeAsyncSafe(() => {
|
||||
if (e.EventId == updateCheckEventId){
|
||||
btnCheckUpdates.Enabled = true;
|
||||
|
||||
if (!e.UpdateAvailable){
|
||||
if (!e.IsUpdateAvailable){
|
||||
FormMessage.Information("No Updates Available", "Your version of TweetDuck is up to date.", FormMessage.OK);
|
||||
}
|
||||
}
|
||||
|
@@ -63,6 +63,7 @@
|
||||
this.labelSize = new System.Windows.Forms.Label();
|
||||
this.panelMiscellaneous = new System.Windows.Forms.Panel();
|
||||
this.durationUpdateTimer = new System.Windows.Forms.Timer(this.components);
|
||||
this.checkMediaPreviews = new System.Windows.Forms.CheckBox();
|
||||
((System.ComponentModel.ISupportInitialize)(this.trackBarEdgeDistance)).BeginInit();
|
||||
this.tableLayoutDurationButtons.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.trackBarDuration)).BeginInit();
|
||||
@@ -78,7 +79,7 @@
|
||||
this.labelEdgeDistanceValue.Location = new System.Drawing.Point(147, 129);
|
||||
this.labelEdgeDistanceValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
|
||||
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.Text = "0 px";
|
||||
this.labelEdgeDistanceValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
|
||||
@@ -252,7 +253,7 @@
|
||||
this.labelDurationValue.Location = new System.Drawing.Point(147, 77);
|
||||
this.labelDurationValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
|
||||
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.Text = "0 ms/c";
|
||||
this.labelDurationValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
|
||||
@@ -273,11 +274,11 @@
|
||||
// checkSkipOnLinkClick
|
||||
//
|
||||
this.checkSkipOnLinkClick.AutoSize = true;
|
||||
this.checkSkipOnLinkClick.Location = new System.Drawing.Point(6, 28);
|
||||
this.checkSkipOnLinkClick.Location = new System.Drawing.Point(6, 51);
|
||||
this.checkSkipOnLinkClick.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
|
||||
this.checkSkipOnLinkClick.Name = "checkSkipOnLinkClick";
|
||||
this.checkSkipOnLinkClick.Size = new System.Drawing.Size(113, 17);
|
||||
this.checkSkipOnLinkClick.TabIndex = 1;
|
||||
this.checkSkipOnLinkClick.TabIndex = 2;
|
||||
this.checkSkipOnLinkClick.Text = "Skip On Link Click";
|
||||
this.toolTip.SetToolTip(this.checkSkipOnLinkClick, "Skips current notification when a link\r\ninside the notification is clicked.");
|
||||
this.checkSkipOnLinkClick.UseVisualStyleBackColor = true;
|
||||
@@ -297,32 +298,32 @@
|
||||
// labelIdlePause
|
||||
//
|
||||
this.labelIdlePause.AutoSize = true;
|
||||
this.labelIdlePause.Location = new System.Drawing.Point(3, 83);
|
||||
this.labelIdlePause.Location = new System.Drawing.Point(3, 106);
|
||||
this.labelIdlePause.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
|
||||
this.labelIdlePause.Name = "labelIdlePause";
|
||||
this.labelIdlePause.Size = new System.Drawing.Size(89, 13);
|
||||
this.labelIdlePause.TabIndex = 3;
|
||||
this.labelIdlePause.TabIndex = 4;
|
||||
this.labelIdlePause.Text = "Pause When Idle";
|
||||
//
|
||||
// comboBoxIdlePause
|
||||
//
|
||||
this.comboBoxIdlePause.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||
this.comboBoxIdlePause.FormattingEnabled = true;
|
||||
this.comboBoxIdlePause.Location = new System.Drawing.Point(5, 99);
|
||||
this.comboBoxIdlePause.Location = new System.Drawing.Point(5, 122);
|
||||
this.comboBoxIdlePause.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
|
||||
this.comboBoxIdlePause.Name = "comboBoxIdlePause";
|
||||
this.comboBoxIdlePause.Size = new System.Drawing.Size(144, 21);
|
||||
this.comboBoxIdlePause.TabIndex = 4;
|
||||
this.comboBoxIdlePause.TabIndex = 5;
|
||||
this.toolTip.SetToolTip(this.comboBoxIdlePause, "Pauses new notifications after going idle for a set amount of time.");
|
||||
//
|
||||
// checkNonIntrusive
|
||||
//
|
||||
this.checkNonIntrusive.AutoSize = true;
|
||||
this.checkNonIntrusive.Location = new System.Drawing.Point(6, 51);
|
||||
this.checkNonIntrusive.Location = new System.Drawing.Point(6, 74);
|
||||
this.checkNonIntrusive.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
|
||||
this.checkNonIntrusive.Name = "checkNonIntrusive";
|
||||
this.checkNonIntrusive.Size = new System.Drawing.Size(128, 17);
|
||||
this.checkNonIntrusive.TabIndex = 2;
|
||||
this.checkNonIntrusive.TabIndex = 3;
|
||||
this.checkNonIntrusive.Text = "Non-Intrusive Popups";
|
||||
this.toolTip.SetToolTip(this.checkNonIntrusive, "When not idle and the cursor is within the notification window area,\r\nit will be delayed until the cursor moves away to prevent accidental clicks.");
|
||||
this.checkNonIntrusive.UseVisualStyleBackColor = true;
|
||||
@@ -389,6 +390,7 @@
|
||||
//
|
||||
this.panelGeneral.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.panelGeneral.Controls.Add(this.checkMediaPreviews);
|
||||
this.panelGeneral.Controls.Add(this.checkColumnName);
|
||||
this.panelGeneral.Controls.Add(this.checkSkipOnLinkClick);
|
||||
this.panelGeneral.Controls.Add(this.checkNonIntrusive);
|
||||
@@ -396,7 +398,7 @@
|
||||
this.panelGeneral.Controls.Add(this.comboBoxIdlePause);
|
||||
this.panelGeneral.Location = new System.Drawing.Point(9, 31);
|
||||
this.panelGeneral.Name = "panelGeneral";
|
||||
this.panelGeneral.Size = new System.Drawing.Size(322, 126);
|
||||
this.panelGeneral.Size = new System.Drawing.Size(322, 149);
|
||||
this.panelGeneral.TabIndex = 1;
|
||||
//
|
||||
// labelScrollSpeedValue
|
||||
@@ -404,7 +406,7 @@
|
||||
this.labelScrollSpeedValue.Location = new System.Drawing.Point(147, 53);
|
||||
this.labelScrollSpeedValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
|
||||
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.Text = "100%";
|
||||
this.labelScrollSpeedValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
|
||||
@@ -437,7 +439,7 @@
|
||||
//
|
||||
this.labelLocation.AutoSize = true;
|
||||
this.labelLocation.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
||||
this.labelLocation.Location = new System.Drawing.Point(6, 372);
|
||||
this.labelLocation.Location = new System.Drawing.Point(6, 395);
|
||||
this.labelLocation.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
|
||||
this.labelLocation.Name = "labelLocation";
|
||||
this.labelLocation.Size = new System.Drawing.Size(70, 20);
|
||||
@@ -448,7 +450,6 @@
|
||||
//
|
||||
this.panelLocation.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.panelLocation.Controls.Add(this.labelEdgeDistanceValue);
|
||||
this.panelLocation.Controls.Add(this.radioLocTL);
|
||||
this.panelLocation.Controls.Add(this.labelDisplay);
|
||||
this.panelLocation.Controls.Add(this.trackBarEdgeDistance);
|
||||
@@ -458,7 +459,8 @@
|
||||
this.panelLocation.Controls.Add(this.radioLocBL);
|
||||
this.panelLocation.Controls.Add(this.radioLocCustom);
|
||||
this.panelLocation.Controls.Add(this.radioLocBR);
|
||||
this.panelLocation.Location = new System.Drawing.Point(9, 395);
|
||||
this.panelLocation.Controls.Add(this.labelEdgeDistanceValue);
|
||||
this.panelLocation.Location = new System.Drawing.Point(9, 418);
|
||||
this.panelLocation.Name = "panelLocation";
|
||||
this.panelLocation.Size = new System.Drawing.Size(322, 165);
|
||||
this.panelLocation.TabIndex = 5;
|
||||
@@ -473,7 +475,7 @@
|
||||
this.panelTimer.Controls.Add(this.checkTimerCountDown);
|
||||
this.panelTimer.Controls.Add(this.labelDurationValue);
|
||||
this.panelTimer.Controls.Add(this.trackBarDuration);
|
||||
this.panelTimer.Location = new System.Drawing.Point(9, 204);
|
||||
this.panelTimer.Location = new System.Drawing.Point(9, 227);
|
||||
this.panelTimer.Name = "panelTimer";
|
||||
this.panelTimer.Size = new System.Drawing.Size(322, 144);
|
||||
this.panelTimer.TabIndex = 3;
|
||||
@@ -492,7 +494,7 @@
|
||||
//
|
||||
this.labelTimer.AutoSize = true;
|
||||
this.labelTimer.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
||||
this.labelTimer.Location = new System.Drawing.Point(6, 181);
|
||||
this.labelTimer.Location = new System.Drawing.Point(6, 204);
|
||||
this.labelTimer.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
|
||||
this.labelTimer.Name = "labelTimer";
|
||||
this.labelTimer.Size = new System.Drawing.Size(48, 20);
|
||||
@@ -503,7 +505,7 @@
|
||||
//
|
||||
this.labelSize.AutoSize = true;
|
||||
this.labelSize.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
||||
this.labelSize.Location = new System.Drawing.Point(6, 584);
|
||||
this.labelSize.Location = new System.Drawing.Point(6, 607);
|
||||
this.labelSize.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
|
||||
this.labelSize.Name = "labelSize";
|
||||
this.labelSize.Size = new System.Drawing.Size(40, 20);
|
||||
@@ -519,7 +521,7 @@
|
||||
this.panelMiscellaneous.Controls.Add(this.labelScrollSpeedValue);
|
||||
this.panelMiscellaneous.Controls.Add(this.trackBarScrollSpeed);
|
||||
this.panelMiscellaneous.Controls.Add(this.labelScrollSpeed);
|
||||
this.panelMiscellaneous.Location = new System.Drawing.Point(9, 607);
|
||||
this.panelMiscellaneous.Location = new System.Drawing.Point(9, 630);
|
||||
this.panelMiscellaneous.Name = "panelMiscellaneous";
|
||||
this.panelMiscellaneous.Size = new System.Drawing.Size(322, 92);
|
||||
this.panelMiscellaneous.TabIndex = 7;
|
||||
@@ -529,6 +531,18 @@
|
||||
this.durationUpdateTimer.Interval = 200;
|
||||
this.durationUpdateTimer.Tick += new System.EventHandler(this.durationUpdateTimer_Tick);
|
||||
//
|
||||
// checkMediaPreviews
|
||||
//
|
||||
this.checkMediaPreviews.AutoSize = true;
|
||||
this.checkMediaPreviews.Location = new System.Drawing.Point(6, 28);
|
||||
this.checkMediaPreviews.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
|
||||
this.checkMediaPreviews.Name = "checkMediaPreviews";
|
||||
this.checkMediaPreviews.Size = new System.Drawing.Size(131, 17);
|
||||
this.checkMediaPreviews.TabIndex = 1;
|
||||
this.checkMediaPreviews.Text = "Show Media Previews";
|
||||
this.toolTip.SetToolTip(this.checkMediaPreviews, "Shows image and video thumbnails in the notification window.");
|
||||
this.checkMediaPreviews.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// TabSettingsNotifications
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
@@ -542,7 +556,7 @@
|
||||
this.Controls.Add(this.labelGeneral);
|
||||
this.Controls.Add(this.panelTimer);
|
||||
this.Name = "TabSettingsNotifications";
|
||||
this.Size = new System.Drawing.Size(340, 708);
|
||||
this.Size = new System.Drawing.Size(340, 731);
|
||||
this.ParentChanged += new System.EventHandler(this.TabSettingsNotifications_ParentChanged);
|
||||
((System.ComponentModel.ISupportInitialize)(this.trackBarEdgeDistance)).EndInit();
|
||||
this.tableLayoutDurationButtons.ResumeLayout(false);
|
||||
@@ -601,5 +615,6 @@
|
||||
private System.Windows.Forms.Timer durationUpdateTimer;
|
||||
private System.Windows.Forms.RadioButton radioSizeCustom;
|
||||
private System.Windows.Forms.RadioButton radioSizeAuto;
|
||||
private System.Windows.Forms.CheckBox checkMediaPreviews;
|
||||
}
|
||||
}
|
||||
|
@@ -4,7 +4,7 @@ using TweetDuck.Core.Controls;
|
||||
using TweetDuck.Core.Notification;
|
||||
|
||||
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 readonly FormNotificationMain notification;
|
||||
@@ -63,6 +63,7 @@ namespace TweetDuck.Core.Other.Settings{
|
||||
checkNotificationTimer.Checked = Config.DisplayNotificationTimer;
|
||||
checkTimerCountDown.Enabled = checkNotificationTimer.Checked;
|
||||
checkTimerCountDown.Checked = Config.NotificationTimerCountDown;
|
||||
checkMediaPreviews.Checked = Config.NotificationMediaPreviews;
|
||||
checkSkipOnLinkClick.Checked = Config.NotificationSkipOnLinkClick;
|
||||
checkNonIntrusive.Checked = Config.NotificationNonIntrusiveMode;
|
||||
|
||||
@@ -96,6 +97,7 @@ namespace TweetDuck.Core.Other.Settings{
|
||||
checkColumnName.CheckedChanged += checkColumnName_CheckedChanged;
|
||||
checkNotificationTimer.CheckedChanged += checkNotificationTimer_CheckedChanged;
|
||||
checkTimerCountDown.CheckedChanged += checkTimerCountDown_CheckedChanged;
|
||||
checkMediaPreviews.CheckedChanged += checkMediaPreviews_CheckedChanged;
|
||||
checkSkipOnLinkClick.CheckedChanged += checkSkipOnLinkClick_CheckedChanged;
|
||||
checkNonIntrusive.CheckedChanged += checkNonIntrusive_CheckedChanged;
|
||||
|
||||
@@ -218,6 +220,10 @@ namespace TweetDuck.Core.Other.Settings{
|
||||
notification.ShowNotificationForSettings(true);
|
||||
}
|
||||
|
||||
private void checkMediaPreviews_CheckedChanged(object sender, EventArgs e){
|
||||
Config.NotificationMediaPreviews = checkMediaPreviews.Checked;
|
||||
}
|
||||
|
||||
private void checkSkipOnLinkClick_CheckedChanged(object sender, EventArgs e){
|
||||
Config.NotificationSkipOnLinkClick = checkSkipOnLinkClick.Checked;
|
||||
}
|
||||
|
72
Core/Other/Settings/TabSettingsSounds.Designer.cs
generated
@@ -25,15 +25,40 @@
|
||||
private void InitializeComponent() {
|
||||
this.components = new System.ComponentModel.Container();
|
||||
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.btnResetSound = 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.panelSoundNotification = new System.Windows.Forms.Panel();
|
||||
this.labelVolume = new System.Windows.Forms.Label();
|
||||
this.trackBarVolume = new System.Windows.Forms.TrackBar();
|
||||
this.panelSoundNotification.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.trackBarVolume)).BeginInit();
|
||||
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
|
||||
//
|
||||
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.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
|
||||
//
|
||||
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)
|
||||
| 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.tbCustomSound);
|
||||
this.panelSoundNotification.Controls.Add(this.btnResetSound);
|
||||
this.panelSoundNotification.Controls.Add(this.btnBrowseSound);
|
||||
this.panelSoundNotification.Controls.Add(this.labelVolumeValue);
|
||||
this.panelSoundNotification.Location = new System.Drawing.Point(9, 31);
|
||||
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;
|
||||
//
|
||||
// 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
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
@@ -110,9 +152,10 @@
|
||||
this.Controls.Add(this.panelSoundNotification);
|
||||
this.Controls.Add(this.labelSoundNotification);
|
||||
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.PerformLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.trackBarVolume)).EndInit();
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
@@ -127,5 +170,8 @@
|
||||
private System.Windows.Forms.Button btnPlaySound;
|
||||
private System.Windows.Forms.Label labelSoundNotification;
|
||||
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,21 +2,29 @@
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
using TweetDuck.Core.Controls;
|
||||
using TweetDuck.Core.Notification;
|
||||
using TweetLib.Audio;
|
||||
|
||||
namespace TweetDuck.Core.Other.Settings{
|
||||
partial class TabSettingsSounds : BaseTabSettings{
|
||||
sealed partial class TabSettingsSounds : BaseTabSettings{
|
||||
private readonly SoundNotification soundNotification;
|
||||
private readonly bool supportsChangingVolume;
|
||||
|
||||
public TabSettingsSounds(){
|
||||
InitializeComponent();
|
||||
|
||||
soundNotification = new SoundNotification();
|
||||
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_TextChanged(tbCustomSound, new EventArgs());
|
||||
tbCustomSound_TextChanged(tbCustomSound, EventArgs.Empty);
|
||||
|
||||
Disposed += (sender, args) => soundNotification.Dispose();
|
||||
}
|
||||
@@ -34,9 +42,10 @@ namespace TweetDuck.Core.Other.Settings{
|
||||
|
||||
private void tbCustomSound_TextChanged(object sender, EventArgs e){
|
||||
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;
|
||||
btnResetSound.Enabled = !isEmpty;
|
||||
trackBarVolume.Enabled = supportsChangingVolume && !isEmpty;
|
||||
}
|
||||
|
||||
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){
|
||||
tbCustomSound.Text = string.Empty;
|
||||
}
|
||||
|
||||
private void trackBarVolume_ValueChanged(object sender, EventArgs e){
|
||||
Config.NotificationSoundVolume = trackBarVolume.Value;
|
||||
soundNotification.SetVolume(Config.NotificationSoundVolume);
|
||||
labelVolumeValue.Text = Config.NotificationSoundVolume+"%";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
116
Core/Other/Settings/TabSettingsTray.Designer.cs
generated
Normal file
@@ -0,0 +1,116 @@
|
||||
namespace TweetDuck.Core.Other.Settings {
|
||||
partial class TabSettingsTray {
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing) {
|
||||
if (disposing && (components != null)) {
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Component Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent() {
|
||||
this.components = new System.ComponentModel.Container();
|
||||
this.panelTray = new System.Windows.Forms.Panel();
|
||||
this.checkTrayHighlight = new System.Windows.Forms.CheckBox();
|
||||
this.comboBoxTrayType = new System.Windows.Forms.ComboBox();
|
||||
this.labelTrayIcon = new System.Windows.Forms.Label();
|
||||
this.labelTray = new System.Windows.Forms.Label();
|
||||
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
||||
this.panelTray.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// panelTray
|
||||
//
|
||||
this.panelTray.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.panelTray.Controls.Add(this.checkTrayHighlight);
|
||||
this.panelTray.Controls.Add(this.comboBoxTrayType);
|
||||
this.panelTray.Controls.Add(this.labelTrayIcon);
|
||||
this.panelTray.Location = new System.Drawing.Point(9, 31);
|
||||
this.panelTray.Name = "panelTray";
|
||||
this.panelTray.Size = new System.Drawing.Size(322, 76);
|
||||
this.panelTray.TabIndex = 1;
|
||||
//
|
||||
// checkTrayHighlight
|
||||
//
|
||||
this.checkTrayHighlight.AutoSize = true;
|
||||
this.checkTrayHighlight.Location = new System.Drawing.Point(6, 56);
|
||||
this.checkTrayHighlight.Margin = new System.Windows.Forms.Padding(6, 5, 3, 3);
|
||||
this.checkTrayHighlight.Name = "checkTrayHighlight";
|
||||
this.checkTrayHighlight.Size = new System.Drawing.Size(103, 17);
|
||||
this.checkTrayHighlight.TabIndex = 2;
|
||||
this.checkTrayHighlight.Text = "Enable Highlight";
|
||||
this.toolTip.SetToolTip(this.checkTrayHighlight, "Highlights the tray icon if there are new tweets.\r\nOnly works for columns with popup or audio notifications.\r\nThe icon resets when the main window is restored.");
|
||||
this.checkTrayHighlight.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// comboBoxTrayType
|
||||
//
|
||||
this.comboBoxTrayType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||
this.comboBoxTrayType.FormattingEnabled = true;
|
||||
this.comboBoxTrayType.Location = new System.Drawing.Point(5, 5);
|
||||
this.comboBoxTrayType.Margin = new System.Windows.Forms.Padding(5, 5, 3, 3);
|
||||
this.comboBoxTrayType.Name = "comboBoxTrayType";
|
||||
this.comboBoxTrayType.Size = new System.Drawing.Size(144, 21);
|
||||
this.comboBoxTrayType.TabIndex = 0;
|
||||
this.toolTip.SetToolTip(this.comboBoxTrayType, "Changes behavior of the Tray icon.\r\nRight-click the icon for an action menu.");
|
||||
//
|
||||
// labelTrayIcon
|
||||
//
|
||||
this.labelTrayIcon.AutoSize = true;
|
||||
this.labelTrayIcon.Location = new System.Drawing.Point(3, 38);
|
||||
this.labelTrayIcon.Margin = new System.Windows.Forms.Padding(3, 9, 3, 0);
|
||||
this.labelTrayIcon.Name = "labelTrayIcon";
|
||||
this.labelTrayIcon.Size = new System.Drawing.Size(52, 13);
|
||||
this.labelTrayIcon.TabIndex = 1;
|
||||
this.labelTrayIcon.Text = "Tray Icon";
|
||||
//
|
||||
// labelTray
|
||||
//
|
||||
this.labelTray.AutoSize = true;
|
||||
this.labelTray.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
||||
this.labelTray.Location = new System.Drawing.Point(6, 8);
|
||||
this.labelTray.Margin = new System.Windows.Forms.Padding(0, 2, 0, 0);
|
||||
this.labelTray.Name = "labelTray";
|
||||
this.labelTray.Size = new System.Drawing.Size(96, 20);
|
||||
this.labelTray.TabIndex = 0;
|
||||
this.labelTray.Text = "System Tray";
|
||||
//
|
||||
// TabSettingsTray
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.Controls.Add(this.panelTray);
|
||||
this.Controls.Add(this.labelTray);
|
||||
this.Name = "TabSettingsTray";
|
||||
this.Size = new System.Drawing.Size(340, 119);
|
||||
this.panelTray.ResumeLayout(false);
|
||||
this.panelTray.PerformLayout();
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.Panel panelTray;
|
||||
private System.Windows.Forms.CheckBox checkTrayHighlight;
|
||||
private System.Windows.Forms.ComboBox comboBoxTrayType;
|
||||
private System.Windows.Forms.Label labelTrayIcon;
|
||||
private System.Windows.Forms.Label labelTray;
|
||||
private System.Windows.Forms.ToolTip toolTip;
|
||||
}
|
||||
}
|
33
Core/Other/Settings/TabSettingsTray.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
|
||||
namespace TweetDuck.Core.Other.Settings{
|
||||
sealed partial class TabSettingsTray : BaseTabSettings{
|
||||
public TabSettingsTray(){
|
||||
InitializeComponent();
|
||||
|
||||
comboBoxTrayType.Items.Add("Disabled");
|
||||
comboBoxTrayType.Items.Add("Display Icon Only");
|
||||
comboBoxTrayType.Items.Add("Minimize to Tray");
|
||||
comboBoxTrayType.Items.Add("Close to Tray");
|
||||
comboBoxTrayType.Items.Add("Combined");
|
||||
comboBoxTrayType.SelectedIndex = Math.Min(Math.Max((int)Config.TrayBehavior, 0), comboBoxTrayType.Items.Count-1);
|
||||
|
||||
checkTrayHighlight.Enabled = Config.TrayBehavior.ShouldDisplayIcon();
|
||||
checkTrayHighlight.Checked = Config.EnableTrayHighlight;
|
||||
}
|
||||
|
||||
public override void OnReady(){
|
||||
comboBoxTrayType.SelectedIndexChanged += comboBoxTrayType_SelectedIndexChanged;
|
||||
checkTrayHighlight.CheckedChanged += checkTrayHighlight_CheckedChanged;
|
||||
}
|
||||
|
||||
private void comboBoxTrayType_SelectedIndexChanged(object sender, EventArgs e){
|
||||
Config.TrayBehavior = (TrayIcon.Behavior)comboBoxTrayType.SelectedIndex;
|
||||
checkTrayHighlight.Enabled = Config.TrayBehavior.ShouldDisplayIcon();
|
||||
}
|
||||
|
||||
private void checkTrayHighlight_CheckedChanged(object sender, EventArgs e){
|
||||
Config.EnableTrayHighlight = checkTrayHighlight.Checked;
|
||||
}
|
||||
}
|
||||
}
|
@@ -3,7 +3,7 @@ using System.ComponentModel;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace TweetDuck.Core{
|
||||
partial class TrayIcon : Component{
|
||||
sealed partial class TrayIcon : Component{
|
||||
public enum Behavior{ // keep order
|
||||
Disabled, DisplayOnly, MinimizeToTray, CloseToTray, Combined
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@ namespace TweetDuck.Core.Utils{
|
||||
static class BrowserCache{
|
||||
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{
|
||||
get{
|
||||
|
@@ -2,16 +2,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Windows.Forms;
|
||||
using CefSharp.WinForms;
|
||||
using TweetDuck.Core.Other;
|
||||
|
||||
namespace TweetDuck.Core.Utils{
|
||||
static class BrowserUtils{
|
||||
public const string TweetDeckURL = "https://tweetdeck.twitter.com";
|
||||
|
||||
public static string HeaderAcceptLanguage{
|
||||
get{
|
||||
string culture = Program.Culture.Name;
|
||||
@@ -27,13 +25,6 @@ namespace TweetDuck.Core.Utils{
|
||||
|
||||
public static string HeaderUserAgent => Program.BrandName+" "+Application.ProductVersion;
|
||||
|
||||
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 static readonly string[] DictionaryWords = {
|
||||
"tweetdeck", "TweetDeck", "tweetduck", "TweetDuck", "TD"
|
||||
};
|
||||
|
||||
public static void SetupCefArgs(IDictionary<string, string> args){
|
||||
if (!Program.SystemConfig.HardwareAcceleration){
|
||||
args["disable-gpu"] = "1";
|
||||
@@ -52,28 +43,47 @@ namespace TweetDuck.Core.Utils{
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsValidUrl(string url){
|
||||
public static ChromiumWebBrowser AsControl(this IWebBrowser browserControl){
|
||||
return (ChromiumWebBrowser)browserControl;
|
||||
}
|
||||
|
||||
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;
|
||||
return scheme == Uri.UriSchemeHttp || scheme == Uri.UriSchemeHttps || scheme == Uri.UriSchemeFtp || scheme == Uri.UriSchemeMailto;
|
||||
|
||||
if (scheme == Uri.UriSchemeHttps || scheme == Uri.UriSchemeHttp || scheme == Uri.UriSchemeFtp || scheme == Uri.UriSchemeMailto){
|
||||
return uri.Host == TwitterTrackingUrl ? UrlCheckResult.Tracking : UrlCheckResult.Fine;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return UrlCheckResult.Invalid;
|
||||
}
|
||||
|
||||
public static void OpenExternalBrowser(string url){
|
||||
if (string.IsNullOrWhiteSpace(url))return;
|
||||
|
||||
if (IsValidUrl(url)){
|
||||
OpenExternalBrowserUnsafe(url);
|
||||
}
|
||||
else{
|
||||
FormMessage.Warning("Blocked URL", "A potentially malicious URL was blocked from opening:\n"+url, FormMessage.OK);
|
||||
}
|
||||
}
|
||||
switch(CheckUrl(url)){
|
||||
case UrlCheckResult.Fine:
|
||||
WindowsUtils.OpenAssociatedProgram(url);
|
||||
break;
|
||||
|
||||
public static void OpenExternalBrowserUnsafe(string url){
|
||||
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){
|
||||
@@ -117,14 +127,6 @@ namespace TweetDuck.Core.Utils{
|
||||
browser.GetHost().SetZoomLevel(Math.Log(percentage/100.0, 1.2));
|
||||
}
|
||||
|
||||
public static bool IsTweetDeckWebsite(IFrame frame){
|
||||
return frame.Url.Contains("//tweetdeck.twitter.com/");
|
||||
}
|
||||
|
||||
public static bool IsTwitterWebsite(IFrame frame){
|
||||
return frame.Url.Contains("//twitter.com/");
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
public static void HandleConsoleMessage(object sender, ConsoleMessageEventArgs e){
|
||||
Debug.WriteLine("[Console] {0} ({1}:{2})", e.Message, e.Source, e.Line);
|
||||
|
@@ -1,39 +0,0 @@
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using TweetDuck.Data;
|
||||
|
||||
namespace TweetDuck.Core.Utils{
|
||||
static class CommandLineArgsParser{
|
||||
private static readonly Lazy<Regex> SplitRegex = new Lazy<Regex>(() => new Regex(@"([^=\s]+(?:=(?:\S*""[^""]*?""\S*|\S*))?)", RegexOptions.Compiled), false);
|
||||
|
||||
public static CommandLineArgs ReadCefArguments(string argumentString){
|
||||
CommandLineArgs args = new CommandLineArgs();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(argumentString)){
|
||||
return args;
|
||||
}
|
||||
|
||||
foreach(Match match in SplitRegex.Value.Matches(argumentString)){
|
||||
string matchValue = match.Value;
|
||||
|
||||
int indexEquals = matchValue.IndexOf('=');
|
||||
string key, value;
|
||||
|
||||
if (indexEquals == -1){
|
||||
key = matchValue.TrimStart('-');
|
||||
value = "1";
|
||||
}
|
||||
else{
|
||||
key = matchValue.Substring(0, indexEquals).TrimStart('-');
|
||||
value = matchValue.Substring(indexEquals+1).Trim('"');
|
||||
}
|
||||
|
||||
if (key.Length != 0){
|
||||
args.SetValue(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
}
|
||||
}
|
@@ -8,7 +8,6 @@ namespace TweetDuck.Core.Utils{
|
||||
[SuppressMessage("ReSharper", "FieldCanBeMadeReadOnly.Local")]
|
||||
[SuppressMessage("ReSharper", "MemberCanBePrivate.Local")]
|
||||
static class NativeMethods{
|
||||
public static readonly IntPtr HWND_BROADCAST = new IntPtr(0xFFFF);
|
||||
public static readonly IntPtr HOOK_HANDLED = new IntPtr(-1);
|
||||
|
||||
public const int HWND_TOPMOST = -1;
|
||||
@@ -17,7 +16,6 @@ namespace TweetDuck.Core.Utils{
|
||||
public const int GWL_STYLE = -16;
|
||||
|
||||
public const int SB_HORZ = 0;
|
||||
public const int BCM_SETSHIELD = 0x160C;
|
||||
|
||||
public const int WM_MOUSE_LL = 14;
|
||||
public const int WM_MOUSEWHEEL = 0x020A;
|
||||
@@ -65,16 +63,7 @@ namespace TweetDuck.Core.Utils{
|
||||
[DllImport("gdi32.dll")]
|
||||
[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);
|
||||
|
||||
[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")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool ShowScrollBar(IntPtr hWnd, int wBar, bool bShow);
|
||||
|
@@ -4,6 +4,8 @@ using System.Text.RegularExpressions;
|
||||
|
||||
namespace TweetDuck.Core.Utils{
|
||||
static class StringUtils{
|
||||
public static readonly string[] EmptyArray = new string[0];
|
||||
|
||||
public static string ExtractBefore(string str, char search, int startIndex = 0){
|
||||
int index = str.IndexOf(search, startIndex);
|
||||
return index == -1 ? str : str.Substring(0, index);
|
||||
|
133
Core/Utils/TwitterUtils.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
using System;
|
||||
using CefSharp;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Forms;
|
||||
using TweetDuck.Core.Other;
|
||||
|
||||
namespace TweetDuck.Core.Utils{
|
||||
static class TwitterUtils{
|
||||
public const string TweetDeckURL = "https://tweetdeck.twitter.com";
|
||||
|
||||
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}'";
|
||||
|
||||
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 readonly string[] DictionaryWords = {
|
||||
"tweetdeck", "TweetDeck", "tweetduck", "TweetDuck", "TD"
|
||||
};
|
||||
|
||||
public static readonly string[] ValidImageExtensions = {
|
||||
".jpg", ".jpeg", ".png", ".gif"
|
||||
};
|
||||
|
||||
public enum ImageQuality{
|
||||
Default, Orig
|
||||
}
|
||||
|
||||
public static bool IsTweetDeckWebsite(IFrame frame){
|
||||
return frame.Url.Contains("//tweetdeck.twitter.com/");
|
||||
}
|
||||
|
||||
public static bool IsTwitterWebsite(IFrame frame){
|
||||
return frame.Url.Contains("//twitter.com/");
|
||||
}
|
||||
|
||||
private static string ExtractMediaBaseLink(string url){
|
||||
int dot = url.LastIndexOf('/');
|
||||
return dot == -1 ? url : StringUtils.ExtractBefore(url, ':', dot);
|
||||
}
|
||||
|
||||
public static string GetMediaLink(string url, ImageQuality quality){
|
||||
if (quality == ImageQuality.Orig){
|
||||
string result = ExtractMediaBaseLink(url);
|
||||
|
||||
if (result != url || url.Contains("//pbs.twimg.com/media/")){
|
||||
result += ":orig";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
else{
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetImageFileName(string url){
|
||||
return BrowserUtils.GetFileNameFromUrl(ExtractMediaBaseLink(url));
|
||||
}
|
||||
|
||||
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){
|
||||
return;
|
||||
}
|
||||
|
||||
string firstImageLink = GetMediaLink(urls[0], quality);
|
||||
int qualityIndex = firstImageLink.IndexOf(':', firstImageLink.LastIndexOf('/'));
|
||||
|
||||
string file = GetImageFileName(firstImageLink);
|
||||
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{
|
||||
AutoUpgradeEnabled = true,
|
||||
OverwritePrompt = urls.Length == 1,
|
||||
Title = "Save image",
|
||||
FileName = $"{string.Join(" ", fileNameParts.Where(part => part.Length > 0))}{ext}",
|
||||
Filter = (urls.Length == 1 ? "Image" : "Images")+(string.IsNullOrEmpty(ext) ? " (unknown)|*.*" : $" (*{ext})|*{ext}")
|
||||
}){
|
||||
if (dialog.ShowDialog() == DialogResult.OK){
|
||||
void OnFailure(Exception ex){
|
||||
FormMessage.Error("Image Download", "An error occurred while downloading the image: "+ex.Message, FormMessage.OK);
|
||||
}
|
||||
|
||||
if (urls.Length == 1){
|
||||
BrowserUtils.DownloadFileAsync(firstImageLink, dialog.FileName, null, OnFailure);
|
||||
}
|
||||
else{
|
||||
string pathBase = Path.ChangeExtension(dialog.FileName, null);
|
||||
string pathExt = Path.GetExtension(dialog.FileName);
|
||||
|
||||
for(int index = 0; index < urls.Length; index++){
|
||||
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.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Management;
|
||||
@@ -49,17 +50,22 @@ namespace TweetDuck.Core.Utils{
|
||||
}
|
||||
}
|
||||
|
||||
public static Process StartProcess(string file, string arguments, bool runElevated){
|
||||
ProcessStartInfo processInfo = new ProcessStartInfo{
|
||||
FileName = file,
|
||||
Arguments = arguments
|
||||
};
|
||||
|
||||
if (runElevated){
|
||||
processInfo.Verb = "runas";
|
||||
public static bool OpenAssociatedProgram(string file, string arguments = "", bool runElevated = false){
|
||||
try{
|
||||
using(Process.Start(new ProcessStartInfo{
|
||||
FileName = file,
|
||||
Arguments = arguments,
|
||||
Verb = runElevated ? "runas" : string.Empty,
|
||||
ErrorDialog = true
|
||||
})){
|
||||
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){
|
||||
@@ -104,7 +110,7 @@ namespace TweetDuck.Core.Utils{
|
||||
}
|
||||
|
||||
public static void ClipboardStripHtmlStyles(){
|
||||
if (!Clipboard.ContainsText(TextDataFormat.Html)){
|
||||
if (!Clipboard.ContainsText(TextDataFormat.Html) || !Clipboard.ContainsText(TextDataFormat.UnicodeText)){
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -115,7 +121,7 @@ namespace TweetDuck.Core.Utils{
|
||||
|
||||
int removed = originalHtml.Length-updatedHtml.Length;
|
||||
updatedHtml = RegexOffsetClipboardHtml.Value.Replace(updatedHtml, match => (int.Parse(match.Value)-removed).ToString().PadLeft(match.Value.Length, '0'));
|
||||
|
||||
|
||||
DataObject obj = new DataObject();
|
||||
obj.SetText(originalText, TextDataFormat.UnicodeText);
|
||||
obj.SetText(updatedHtml, TextDataFormat.Html);
|
||||
|
@@ -91,7 +91,7 @@ namespace TweetDuck.Data{
|
||||
stream.Dispose();
|
||||
}
|
||||
|
||||
public class Entry{
|
||||
public sealed class Entry{
|
||||
public string Identifier { get; }
|
||||
|
||||
public string KeyName{
|
||||
@@ -103,7 +103,7 @@ namespace TweetDuck.Data{
|
||||
public string[] KeyValue{
|
||||
get{
|
||||
int index = Identifier.IndexOf(KeySeparator);
|
||||
return index == -1 ? new string[0] : Identifier.Substring(index+1).Split(KeySeparator);
|
||||
return index == -1 ? StringUtils.EmptyArray : Identifier.Substring(index+1).Split(KeySeparator);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace TweetDuck.Data{
|
||||
sealed class CommandLineArgs{
|
||||
@@ -32,6 +33,36 @@ namespace TweetDuck.Data{
|
||||
}
|
||||
}
|
||||
|
||||
public static CommandLineArgs ReadCefArguments(string argumentString){
|
||||
CommandLineArgs args = new CommandLineArgs();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(argumentString)){
|
||||
return args;
|
||||
}
|
||||
|
||||
foreach(Match match in Regex.Matches(argumentString, @"([^=\s]+(?:=(?:\S*""[^""]*?""\S*|\S*))?)")){
|
||||
string matchValue = match.Value;
|
||||
|
||||
int indexEquals = matchValue.IndexOf('=');
|
||||
string key, value;
|
||||
|
||||
if (indexEquals == -1){
|
||||
key = matchValue.TrimStart('-');
|
||||
value = "1";
|
||||
}
|
||||
else{
|
||||
key = matchValue.Substring(0, indexEquals).TrimStart('-');
|
||||
value = matchValue.Substring(indexEquals+1).Trim('"');
|
||||
}
|
||||
|
||||
if (key.Length != 0){
|
||||
args.SetValue(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
private readonly HashSet<string> flags = new HashSet<string>();
|
||||
private readonly Dictionary<string, string> values = new Dictionary<string, string>();
|
||||
|
||||
|
@@ -6,12 +6,15 @@ using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace TweetDuck.Data.Serialization{
|
||||
sealed class FileSerializer<T> where T : ISerializedObject{
|
||||
sealed class FileSerializer<T>{
|
||||
private const string NewLineReal = "\r\n";
|
||||
private const string NewLineCustom = "\r~\n";
|
||||
|
||||
private static readonly ITypeConverter BasicSerializerObj = new BasicTypeConverter();
|
||||
|
||||
public delegate void HandleUnknownPropertiesHandler(T obj, Dictionary<string, string> data);
|
||||
public HandleUnknownPropertiesHandler HandleUnknownProperties { get; set; }
|
||||
|
||||
private readonly Dictionary<string, PropertyInfo> props;
|
||||
private readonly Dictionary<Type, ITypeConverter> converters;
|
||||
|
||||
@@ -48,8 +51,10 @@ namespace TweetDuck.Data.Serialization{
|
||||
}
|
||||
|
||||
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))){
|
||||
if (reader.Peek() == 0){
|
||||
if (reader.Peek() <= 1){
|
||||
throw new FormatException("Input appears to be a binary file.");
|
||||
}
|
||||
|
||||
@@ -75,14 +80,22 @@ namespace TweetDuck.Data.Serialization{
|
||||
throw new SerializationException($"Invalid file format, cannot convert value: {value} (property: {property})");
|
||||
}
|
||||
}
|
||||
else if (!obj.OnReadUnknownProperty(property, value)){
|
||||
throw new SerializationException($"Invalid file format, unknown property: {property}+");
|
||||
else{
|
||||
unknownProperties[property] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (unknownProperties.Count > 0){
|
||||
HandleUnknownProperties?.Invoke(obj, unknownProperties);
|
||||
|
||||
if (unknownProperties.Count > 0){
|
||||
throw new SerializationException($"Invalid file format, unknown properties: {string.Join(", ", unknownProperties.Keys)}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class BasicTypeConverter : ITypeConverter{
|
||||
private sealed class BasicTypeConverter : ITypeConverter{
|
||||
bool ITypeConverter.TryWriteType(Type type, object value, out string converted){
|
||||
switch(Type.GetTypeCode(type)){
|
||||
case TypeCode.Boolean:
|
||||
|
@@ -1,5 +0,0 @@
|
||||
namespace TweetDuck.Data.Serialization{
|
||||
interface ISerializedObject{
|
||||
bool OnReadUnknownProperty(string property, string value);
|
||||
}
|
||||
}
|
@@ -2,11 +2,8 @@
|
||||
|
||||
namespace TweetDuck.Data.Serialization{
|
||||
sealed class SingleTypeConverter<T> : ITypeConverter{
|
||||
public delegate string FuncConvertToString<U>(U value);
|
||||
public delegate U FuncConvertToObject<U>(string value);
|
||||
|
||||
public FuncConvertToString<T> ConvertToString { get; set; }
|
||||
public FuncConvertToObject<T> ConvertToObject { get; set; }
|
||||
public Func<T, string> ConvertToString { get; set; }
|
||||
public Func<string, T> ConvertToObject { get; set; }
|
||||
|
||||
bool ITypeConverter.TryWriteType(Type type, object value, out string converted){
|
||||
try{
|
||||
|
@@ -1,12 +1,10 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
using TweetDuck.Core.Controls;
|
||||
using TweetDuck.Core.Utils;
|
||||
using TweetDuck.Data.Serialization;
|
||||
|
||||
namespace TweetDuck.Data{
|
||||
[Serializable] // TODO remove attribute with UserConfigLegacy
|
||||
sealed class WindowState{
|
||||
private Rectangle rect;
|
||||
private bool isMaximized;
|
||||
|
@@ -8,7 +8,7 @@ using TweetDuck.Core.Utils;
|
||||
using TweetDuck.Plugins.Enums;
|
||||
|
||||
namespace TweetDuck.Plugins.Controls{
|
||||
partial class PluginControl : UserControl{
|
||||
sealed partial class PluginControl : UserControl{
|
||||
private readonly PluginManager pluginManager;
|
||||
private readonly Plugin plugin;
|
||||
|
||||
|
@@ -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){
|
||||
case PluginEnvironment.Browser: return "browser.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){
|
||||
case PluginEnvironment.Browser: return "$,$TD,$TDP,TD";
|
||||
case PluginEnvironment.Notification: return "$TD,$TDP";
|
||||
|
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
|
||||
namespace TweetDuck.Plugins.Events{
|
||||
class PluginChangedStateEventArgs : EventArgs{
|
||||
sealed class PluginChangedStateEventArgs : EventArgs{
|
||||
public Plugin Plugin { get; }
|
||||
public bool IsEnabled { get; }
|
||||
|
||||
|
@@ -2,10 +2,10 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TweetDuck.Plugins.Events{
|
||||
class PluginErrorEventArgs : EventArgs{
|
||||
sealed class PluginErrorEventArgs : EventArgs{
|
||||
public bool HasErrors => Errors.Count > 0;
|
||||
|
||||
public IList<string> Errors;
|
||||
public IList<string> Errors { get; }
|
||||
|
||||
public PluginErrorEventArgs(IList<string> errors){
|
||||
this.Errors = errors;
|
||||
|
@@ -7,6 +7,9 @@ using TweetDuck.Plugins.Enums;
|
||||
|
||||
namespace TweetDuck.Plugins{
|
||||
sealed class Plugin{
|
||||
private static readonly Version AppVersion = new Version(Program.VersionTag);
|
||||
private const string VersionWildcard = "*";
|
||||
|
||||
public string Identifier { get; }
|
||||
public PluginGroup Group { get; }
|
||||
public PluginEnvironment Environments { get; private set; }
|
||||
@@ -42,7 +45,7 @@ namespace TweetDuck.Plugins{
|
||||
|
||||
private readonly string pathRoot;
|
||||
private readonly string pathData;
|
||||
private readonly Dictionary<string, string> metadata = new Dictionary<string, string>(4){
|
||||
private readonly Dictionary<string, string> metadata = new Dictionary<string, string>(8){
|
||||
{ "NAME", "" },
|
||||
{ "DESCRIPTION", "" },
|
||||
{ "AUTHOR", "(anonymous)" },
|
||||
@@ -50,7 +53,7 @@ namespace TweetDuck.Plugins{
|
||||
{ "WEBSITE", "" },
|
||||
{ "CONFIGFILE", "" },
|
||||
{ "CONFIGDEFAULT", "" },
|
||||
{ "REQUIRES", "*" }
|
||||
{ "REQUIRES", VersionWildcard }
|
||||
};
|
||||
|
||||
private bool? canRun;
|
||||
@@ -84,7 +87,7 @@ namespace TweetDuck.Plugins{
|
||||
|
||||
public string GetScriptPath(PluginEnvironment environment){
|
||||
if (Environments.HasFlag(environment)){
|
||||
string file = environment.GetScriptFile();
|
||||
string file = environment.GetPluginScriptFile();
|
||||
return file != null ? Path.Combine(pathRoot, file) : string.Empty;
|
||||
}
|
||||
else{
|
||||
@@ -152,7 +155,7 @@ namespace TweetDuck.Plugins{
|
||||
|
||||
private static bool LoadEnvironments(string path, Plugin plugin, out string error){
|
||||
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));
|
||||
plugin.Environments |= PluginEnvironmentExtensions.Values.FirstOrDefault(env => file.Equals(env.GetPluginScriptFile(), StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
if (plugin.Environments == PluginEnvironment.None){
|
||||
@@ -209,7 +212,7 @@ namespace TweetDuck.Plugins{
|
||||
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;
|
||||
return false;
|
||||
}
|
||||
@@ -221,7 +224,7 @@ namespace TweetDuck.Plugins{
|
||||
}
|
||||
|
||||
private static bool CheckRequiredVersion(string requires){
|
||||
return requires.Equals("*", StringComparison.Ordinal) || Program.Version >= new Version(requires);
|
||||
return requires == VersionWildcard || AppVersion >= new Version(requires);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -8,22 +8,24 @@ namespace TweetDuck.Plugins{
|
||||
sealed class PluginConfig{
|
||||
public event EventHandler<PluginChangedStateEventArgs> InternalPluginChangedState; // should only be accessed from PluginManager
|
||||
|
||||
public IEnumerable<string> DisabledPlugins => Disabled;
|
||||
public bool AnyDisabled => Disabled.Count > 0;
|
||||
public IEnumerable<string> DisabledPlugins => disabled;
|
||||
public bool AnyDisabled => disabled.Count > 0;
|
||||
|
||||
private readonly HashSet<string> Disabled = new HashSet<string>{
|
||||
private static readonly string[] DefaultDisabled = {
|
||||
"official/clear-columns",
|
||||
"official/reply-account"
|
||||
};
|
||||
|
||||
private readonly HashSet<string> disabled = new HashSet<string>();
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsEnabled(Plugin plugin){
|
||||
return !Disabled.Contains(plugin.Identifier);
|
||||
return !disabled.Contains(plugin.Identifier);
|
||||
}
|
||||
|
||||
public void Load(string file){
|
||||
@@ -32,14 +34,21 @@ namespace TweetDuck.Plugins{
|
||||
string line = reader.ReadLine();
|
||||
|
||||
if (line == "#Disabled"){
|
||||
Disabled.Clear();
|
||||
disabled.Clear();
|
||||
|
||||
while((line = reader.ReadLine()) != null){
|
||||
Disabled.Add(line);
|
||||
disabled.Add(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}catch(FileNotFoundException){
|
||||
disabled.Clear();
|
||||
|
||||
foreach(string identifier in DefaultDisabled){
|
||||
disabled.Add(identifier);
|
||||
}
|
||||
|
||||
Save(file);
|
||||
}catch(DirectoryNotFoundException){
|
||||
}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);
|
||||
@@ -51,8 +60,8 @@ namespace TweetDuck.Plugins{
|
||||
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);
|
||||
foreach(string identifier in disabled){
|
||||
writer.WriteLine(identifier);
|
||||
}
|
||||
}
|
||||
}catch(Exception e){
|
||||
|
@@ -9,12 +9,14 @@ using TweetDuck.Resources;
|
||||
|
||||
namespace TweetDuck.Plugins{
|
||||
sealed class PluginManager{
|
||||
public const string PluginBrowserScriptFile = "plugins.browser.js";
|
||||
public const string PluginNotificationScriptFile = "plugins.notification.js";
|
||||
public const string PluginGlobalScriptFile = "plugins.js";
|
||||
|
||||
private const int InvalidToken = 0;
|
||||
|
||||
private static readonly Dictionary<PluginEnvironment, string> PluginSetupScripts = new Dictionary<PluginEnvironment, string>(4){
|
||||
{ PluginEnvironment.None, ScriptLoader.LoadResource("plugins.js") },
|
||||
{ PluginEnvironment.Browser, ScriptLoader.LoadResource("plugins.browser.js") },
|
||||
{ PluginEnvironment.Notification, ScriptLoader.LoadResource("plugins.notification.js") }
|
||||
};
|
||||
|
||||
public string PathOfficialPlugins => Path.Combine(rootPath, "official");
|
||||
public string PathCustomPlugins => Path.Combine(rootPath, "user");
|
||||
|
||||
@@ -43,14 +45,7 @@ namespace TweetDuck.Plugins{
|
||||
this.Bridge = new PluginBridge(this);
|
||||
|
||||
Config.Load(configPath);
|
||||
|
||||
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){
|
||||
@@ -81,6 +76,8 @@ namespace TweetDuck.Plugins{
|
||||
}
|
||||
|
||||
public void Reload(){
|
||||
Config.Load(configPath);
|
||||
|
||||
plugins.Clear();
|
||||
tokens.Clear();
|
||||
|
||||
@@ -97,7 +94,17 @@ namespace TweetDuck.Plugins{
|
||||
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)){
|
||||
ScriptLoader.ExecuteScript(frame, PluginSetupScripts[environment], environment.GetScriptIdentifier());
|
||||
ScriptLoader.ExecuteScript(frame, PluginSetupScripts[PluginEnvironment.None], PluginEnvironment.None.GetScriptIdentifier());
|
||||
ExecutePluginScripts(frame, environment);
|
||||
}
|
||||
}
|
||||
|
||||
private void ExecutePluginScripts(IFrame frame, PluginEnvironment environment){
|
||||
bool includeDisabled = environment.IncludesDisabledPlugins();
|
||||
|
||||
if (includeDisabled){
|
||||
ScriptLoader.ExecuteScript(frame, PluginScriptGenerator.GenerateConfig(Config), "gen:pluginconfig");
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@ namespace TweetDuck.Plugins{
|
||||
|
||||
public static string GeneratePlugin(string pluginIdentifier, string pluginContents, int pluginToken, PluginEnvironment environment){
|
||||
return PluginGen
|
||||
.Replace("%params", environment.GetScriptVariables())
|
||||
.Replace("%params", environment.GetPluginScriptVariables())
|
||||
.Replace("%id", pluginIdentifier)
|
||||
.Replace("%token", pluginToken.ToString())
|
||||
.Replace("%contents", pluginContents);
|
||||
|
124
Program.cs
@@ -1,4 +1,4 @@
|
||||
using CefSharp;
|
||||
using CefSharp;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
@@ -8,24 +8,21 @@ using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
using TweetDuck.Configuration;
|
||||
using TweetDuck.Core;
|
||||
using TweetDuck.Core.Handling;
|
||||
using TweetDuck.Core.Handling.General;
|
||||
using TweetDuck.Core.Other;
|
||||
using TweetDuck.Core.Other.Settings.Export;
|
||||
using TweetDuck.Core.Utils;
|
||||
using TweetDuck.Data;
|
||||
using TweetDuck.Plugins;
|
||||
using TweetDuck.Plugins.Events;
|
||||
using TweetDuck.Updates;
|
||||
using TweetLib.Communication;
|
||||
|
||||
namespace TweetDuck{
|
||||
static class Program{
|
||||
public const string BrandName = "TweetDuck";
|
||||
public const string Website = "https://tweetduck.chylex.com";
|
||||
|
||||
public const string VersionTag = "1.8.3";
|
||||
public const string VersionFull = "1.8.3.0";
|
||||
public const string VersionTag = "1.10.2";
|
||||
|
||||
public static readonly Version Version = new Version(VersionTag);
|
||||
public static readonly bool IsPortable = File.Exists("makeportable");
|
||||
|
||||
public static readonly string ProgramPath = AppDomain.CurrentDomain.BaseDirectory;
|
||||
@@ -34,13 +31,13 @@ namespace TweetDuck{
|
||||
public static readonly string ScriptPath = Path.Combine(ProgramPath, "scripts");
|
||||
public static readonly string PluginPath = Path.Combine(ProgramPath, "plugins");
|
||||
|
||||
public static readonly string UserConfigFilePath = Path.Combine(StoragePath, "TD_UserConfig.cfg");
|
||||
public static readonly string SystemConfigFilePath = Path.Combine(StoragePath, "TD_SystemConfig.cfg");
|
||||
public static readonly string PluginConfigFilePath = Path.Combine(StoragePath, "TD_PluginConfig.cfg");
|
||||
|
||||
public static readonly string PluginDataPath = Path.Combine(StoragePath, "TD_Plugins");
|
||||
private static readonly string InstallerPath = Path.Combine(StoragePath, "TD_Updates");
|
||||
|
||||
public static string UserConfigFilePath => Path.Combine(StoragePath, "TD_UserConfig.cfg");
|
||||
public static string SystemConfigFilePath => Path.Combine(StoragePath, "TD_SystemConfig.cfg");
|
||||
public static string PluginConfigFilePath => Path.Combine(StoragePath, "TD_PluginConfig.cfg");
|
||||
|
||||
private static string ErrorLogFilePath => Path.Combine(StoragePath, "TD_Log.txt");
|
||||
private static string ConsoleLogFilePath => Path.Combine(StoragePath, "TD_Console.txt");
|
||||
|
||||
@@ -49,19 +46,21 @@ namespace TweetDuck{
|
||||
|
||||
private static readonly LockManager LockManager = new LockManager(Path.Combine(StoragePath, ".lock"));
|
||||
private static bool HasCleanedUp;
|
||||
|
||||
|
||||
public static UserConfig UserConfig { get; private set; }
|
||||
public static SystemConfig SystemConfig { get; private set; }
|
||||
public static Reporter Reporter { get; }
|
||||
public static CultureInfo Culture { get; }
|
||||
|
||||
public static event EventHandler UserConfigReplaced;
|
||||
|
||||
static Program(){
|
||||
Culture = CultureInfo.CurrentCulture;
|
||||
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
|
||||
CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture;
|
||||
|
||||
#if DEBUG
|
||||
CultureInfo.DefaultThreadCurrentUICulture = Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-us"); // force english exceptions
|
||||
#endif
|
||||
|
||||
Reporter = new Reporter(ErrorLogFilePath);
|
||||
Reporter.SetupUnhandledExceptionHandler("TweetDuck Has Failed :(");
|
||||
}
|
||||
@@ -71,8 +70,8 @@ namespace TweetDuck{
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
|
||||
WindowRestoreMessage = NativeMethods.RegisterWindowMessage("TweetDuckRestore");
|
||||
SubProcessMessage = NativeMethods.RegisterWindowMessage("TweetDuckSubProcess");
|
||||
WindowRestoreMessage = Comms.RegisterMessage("TweetDuckRestore");
|
||||
SubProcessMessage = Comms.RegisterMessage("TweetDuckSubProcess");
|
||||
|
||||
if (!WindowsUtils.CheckFolderWritePermission(StoragePath)){
|
||||
FormMessage.Warning("Permission Error", "TweetDuck does not have write permissions to the storage folder: "+StoragePath, FormMessage.OK);
|
||||
@@ -80,64 +79,50 @@ namespace TweetDuck{
|
||||
}
|
||||
|
||||
if (Arguments.HasFlag(Arguments.ArgRestart)){
|
||||
for(int attempt = 0; attempt < 21; attempt++){
|
||||
LockManager.Result lockResult = LockManager.Lock();
|
||||
LockManager.Result lockResult = LockManager.LockWait(10000);
|
||||
|
||||
if (lockResult == LockManager.Result.Success){
|
||||
break;
|
||||
}
|
||||
else if (lockResult == LockManager.Result.Fail){
|
||||
while(lockResult != LockManager.Result.Success){
|
||||
if (lockResult == LockManager.Result.Fail){
|
||||
FormMessage.Error("TweetDuck Has Failed :(", "An unknown error occurred accessing the data folder. Please, make sure TweetDuck is not already running. If the problem persists, try restarting your system.", FormMessage.OK);
|
||||
return;
|
||||
}
|
||||
else if (attempt == 20){
|
||||
if (FormMessage.Warning("TweetDuck Cannot Restart", "TweetDuck is taking too long to close.", FormMessage.Retry, FormMessage.Exit)){
|
||||
attempt /= 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
else if (!FormMessage.Warning("TweetDuck Cannot Restart", "TweetDuck is taking too long to close.", FormMessage.Retry, FormMessage.Exit)){
|
||||
return;
|
||||
}
|
||||
else Thread.Sleep(500);
|
||||
|
||||
lockResult = LockManager.LockWait(5000);
|
||||
}
|
||||
}
|
||||
else{
|
||||
LockManager.Result lockResult = LockManager.Lock();
|
||||
|
||||
|
||||
if (lockResult == LockManager.Result.HasProcess){
|
||||
if (LockManager.LockingProcess.MainWindowHandle == IntPtr.Zero){ // restore if the original process is in tray
|
||||
NativeMethods.PostMessage(NativeMethods.HWND_BROADCAST, WindowRestoreMessage, new UIntPtr((uint)LockManager.LockingProcess.Id), IntPtr.Zero);
|
||||
|
||||
if (WindowsUtils.TrySleepUntil(() => {
|
||||
LockManager.LockingProcess.Refresh();
|
||||
return LockManager.LockingProcess.HasExited || (LockManager.LockingProcess.MainWindowHandle != IntPtr.Zero && LockManager.LockingProcess.Responding);
|
||||
}, 2000, 250)){
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (FormMessage.Error("TweetDuck is Already Running", "Another instance of TweetDuck is already running.\nDo you want to close it?", FormMessage.Yes, FormMessage.No)){
|
||||
if (!LockManager.RestoreLockingProcess(2000) && FormMessage.Error("TweetDuck is Already Running", "Another instance of TweetDuck is already running.\nDo you want to close it?", FormMessage.Yes, FormMessage.No)){
|
||||
if (!LockManager.CloseLockingProcess(10000, 5000)){
|
||||
FormMessage.Error("TweetDuck Has Failed :(", "Could not close the other process.", FormMessage.OK);
|
||||
return;
|
||||
}
|
||||
|
||||
LockManager.Lock();
|
||||
lockResult = LockManager.Lock();
|
||||
}
|
||||
else return;
|
||||
}
|
||||
else if (lockResult != LockManager.Result.Success){
|
||||
|
||||
if (lockResult != LockManager.Result.Success){
|
||||
FormMessage.Error("TweetDuck Has Failed :(", "An unknown error occurred accessing the data folder. Please, make sure TweetDuck is not already running. If the problem persists, try restarting your system.", FormMessage.OK);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ReloadConfig();
|
||||
|
||||
UserConfig = UserConfig.Load(UserConfigFilePath);
|
||||
SystemConfig = SystemConfig.Load(SystemConfigFilePath);
|
||||
|
||||
if (Arguments.HasFlag(Arguments.ArgImportCookies)){
|
||||
ExportManager.ImportCookies();
|
||||
}
|
||||
else if (Arguments.HasFlag(Arguments.ArgDeleteCookies)){
|
||||
ExportManager.DeleteCookies();
|
||||
}
|
||||
|
||||
if (Arguments.HasFlag(Arguments.ArgUpdated)){
|
||||
WindowsUtils.TryDeleteFolderWhenAble(InstallerPath, 8000);
|
||||
@@ -157,7 +142,7 @@ namespace TweetDuck{
|
||||
#endif
|
||||
};
|
||||
|
||||
CommandLineArgsParser.ReadCefArguments(UserConfig.CustomCefArgs).ToDictionary(settings.CefCommandLineArgs);
|
||||
CommandLineArgs.ReadCefArguments(UserConfig.CustomCefArgs).ToDictionary(settings.CefCommandLineArgs);
|
||||
BrowserUtils.SetupCefArgs(settings.CefCommandLineArgs);
|
||||
|
||||
Cef.EnableHighDPISupport();
|
||||
@@ -165,18 +150,13 @@ namespace TweetDuck{
|
||||
|
||||
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{
|
||||
AllowPreReleases = Arguments.HasFlag(Arguments.ArgDebugUpdates),
|
||||
DismissedUpdate = UserConfig.DismissedUpdate,
|
||||
InstallerDownloadFolder = InstallerPath
|
||||
};
|
||||
|
||||
FormBrowser mainForm = new FormBrowser(plugins, updaterSettings);
|
||||
FormBrowser mainForm = new FormBrowser(updaterSettings);
|
||||
Application.Run(mainForm);
|
||||
|
||||
if (mainForm.UpdateInstallerPath != null){
|
||||
@@ -186,20 +166,12 @@ namespace TweetDuck{
|
||||
string updaterArgs = "/SP- /SILENT /CLOSEAPPLICATIONS /UPDATEPATH=\""+ProgramPath+"\" /RUNARGS=\""+Arguments.GetCurrentForInstallerCmd()+"\""+(IsPortable ? " /PORTABLE=1" : "");
|
||||
bool runElevated = !IsPortable || !WindowsUtils.CheckFolderWritePermission(ProgramPath);
|
||||
|
||||
WindowsUtils.StartProcess(mainForm.UpdateInstallerPath, updaterArgs, runElevated);
|
||||
Application.Exit();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
if (WindowsUtils.OpenAssociatedProgram(mainForm.UpdateInstallerPath, updaterArgs, runElevated)){
|
||||
Application.Exit();
|
||||
}
|
||||
else{
|
||||
RestartWithArgsInternal(Arguments.GetCurrentClean());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,11 +193,6 @@ namespace TweetDuck{
|
||||
}
|
||||
}
|
||||
|
||||
public static void ReloadConfig(){
|
||||
UserConfig = UserConfig.Load(UserConfigFilePath);
|
||||
UserConfigReplaced?.Invoke(UserConfig, new EventArgs());
|
||||
}
|
||||
|
||||
public static void ResetConfig(){
|
||||
try{
|
||||
File.Delete(UserConfigFilePath);
|
||||
@@ -234,8 +201,8 @@ namespace TweetDuck{
|
||||
Reporter.HandleException("Configuration Reset Error", "Could not delete configuration files to reset the options.", true, e);
|
||||
return;
|
||||
}
|
||||
|
||||
ReloadConfig();
|
||||
|
||||
UserConfig.Reload();
|
||||
}
|
||||
|
||||
public static void Restart(params string[] extraArgs){
|
||||
@@ -248,11 +215,14 @@ namespace TweetDuck{
|
||||
FormBrowser browserForm = Application.OpenForms.OfType<FormBrowser>().FirstOrDefault();
|
||||
if (browserForm == null)return;
|
||||
|
||||
args.AddFlag(Arguments.ArgRestart);
|
||||
|
||||
browserForm.ForceClose();
|
||||
ExitCleanup();
|
||||
|
||||
ExitCleanup();
|
||||
RestartWithArgsInternal(args);
|
||||
}
|
||||
|
||||
private static void RestartWithArgsInternal(CommandLineArgs args){
|
||||
args.AddFlag(Arguments.ArgRestart);
|
||||
Process.Start(Application.ExecutablePath, args.ToString());
|
||||
Application.Exit();
|
||||
}
|
||||
|
@@ -34,8 +34,8 @@ using TweetDuck;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion(Program.VersionFull)]
|
||||
[assembly: AssemblyFileVersion(Program.VersionFull)]
|
||||
[assembly: AssemblyVersion(Program.VersionTag)]
|
||||
[assembly: AssemblyFileVersion(Program.VersionTag)]
|
||||
|
||||
[assembly: NeutralResourcesLanguage("en")]
|
||||
|
||||
|
68
README.md
@@ -1,11 +1,73 @@
|
||||
# Support
|
||||
|
||||
[Follow TweetDuck on Twitter](https://twitter.com/TryTweetDuck) | [Support via PayPal](https://paypal.me/chylex) | [Support via Patreon](https://www.patreon.com/chylex)
|
||||
|
||||
# Build Instructions
|
||||
|
||||
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 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
After Width: | Height: | Size: 1.9 KiB |
BIN
Resources/Guide/img/column-clear-header.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
Resources/Guide/img/column-clear-preferences.png
Normal file
After Width: | Height: | Size: 7.5 KiB |
BIN
Resources/Guide/img/column-preferences.png
Normal file
After Width: | Height: | Size: 7.3 KiB |
BIN
Resources/Guide/img/icon.ico
Normal file
After Width: | Height: | Size: 100 KiB |
BIN
Resources/Guide/img/new-tweet-emoji.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
Resources/Guide/img/new-tweet-pin.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
Resources/Guide/img/new-tweet-template-advanced.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
Resources/Guide/img/new-tweet-template-basic.png
Normal file
After Width: | Height: | Size: 6.1 KiB |
BIN
Resources/Guide/img/options-manage-export.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
Resources/Guide/img/options-manage-reset.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
Resources/Guide/img/options-manage.png
Normal file
After Width: | Height: | Size: 2.4 KiB |