mirror of
https://github.com/chylex/TweetDuck.git
synced 2026-02-19 11:46:35 +01:00
Compare commits
282 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 | |||
| 34726c533e | |||
| 4a0d72d2cc | |||
| fe3fc5c9f7 | |||
| 441228e2b0 | |||
| 7538aee4f2 | |||
| acf809268e | |||
| 4ebc0c10b6 | |||
| a453888ca2 | |||
| 530b44762b | |||
| f85587fb0b | |||
| edb8799b1a | |||
| e47aeb37f0 | |||
| 776e9968dc | |||
| 1898bf4731 | |||
| 78df020737 | |||
| b93f9a4b9a | |||
| 748b230ef5 | |||
| deb8dde9e1 | |||
| dbb2f10754 | |||
| 0ded03ab92 | |||
| 2198e84f3b | |||
| 14d44528b0 | |||
| eb8159ca0f | |||
| 9811f40a53 | |||
| 8de7e13aa3 | |||
| c63e6a1e49 | |||
| 5a21d2cb10 | |||
| 424c0e596c | |||
| d431b63c27 | |||
| 38c2781cd3 | |||
| 796fb348a3 | |||
| 71b306d5fd | |||
| 4c610ea32d | |||
| 4bff006743 | |||
| 1645079bc0 | |||
| 9afb58e4a7 | |||
| 2820fc8acf | |||
| 4d77a498f6 | |||
| d77de3bb12 | |||
| 29e7ad6ce6 | |||
| 1712b5120e | |||
| 06c0153cf5 | |||
| 44f7ecda6d | |||
| fb94bf1b80 | |||
| 4818652582 | |||
| c69b9784fc | |||
| 0ac244a3ea | |||
| 19a445fdab | |||
| c90a18a2c0 | |||
| 502310c413 | |||
| 6f9424d4ec | |||
| bb379fe667 | |||
| 0fd86bf214 | |||
| 29b75d4391 | |||
| a7124e5449 | |||
| a714f3480a | |||
| c10e0df898 | |||
| fba734fd5a | |||
| 27e2372097 | |||
| 7f5b99495c | |||
| 1efe2a56af | |||
| 850873aec8 | |||
| d9e6afbf36 | |||
| 7f3bd2715c | |||
| c81cb393e9 | |||
| 4800faa783 | |||
| 1087b5e1d1 |
@@ -1,5 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Data;
|
||||||
|
|
||||||
namespace TweetDuck.Configuration{
|
namespace TweetDuck.Configuration{
|
||||||
static class Arguments{
|
static class Arguments{
|
||||||
@@ -12,6 +12,7 @@ namespace TweetDuck.Configuration{
|
|||||||
// internal args
|
// internal args
|
||||||
public const string ArgRestart = "-restart";
|
public const string ArgRestart = "-restart";
|
||||||
public const string ArgImportCookies = "-importcookies";
|
public const string ArgImportCookies = "-importcookies";
|
||||||
|
public const string ArgDeleteCookies = "-deletecookies";
|
||||||
public const string ArgUpdated = "-updated";
|
public const string ArgUpdated = "-updated";
|
||||||
|
|
||||||
// class data and methods
|
// class data and methods
|
||||||
@@ -29,6 +30,7 @@ namespace TweetDuck.Configuration{
|
|||||||
CommandLineArgs args = Current.Clone();
|
CommandLineArgs args = Current.Clone();
|
||||||
args.RemoveFlag(ArgRestart);
|
args.RemoveFlag(ArgRestart);
|
||||||
args.RemoveFlag(ArgImportCookies);
|
args.RemoveFlag(ArgImportCookies);
|
||||||
|
args.RemoveFlag(ArgDeleteCookies);
|
||||||
args.RemoveFlag(ArgUpdated);
|
args.RemoveFlag(ArgUpdated);
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,28 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
using TweetLib.Communication;
|
||||||
|
|
||||||
namespace TweetDuck.Configuration{
|
namespace TweetDuck.Configuration{
|
||||||
sealed class LockManager{
|
sealed class LockManager{
|
||||||
|
private const int RetryDelay = 250;
|
||||||
|
|
||||||
public enum Result{
|
public enum Result{
|
||||||
Success, HasProcess, Fail
|
Success, HasProcess, Fail
|
||||||
}
|
}
|
||||||
|
|
||||||
public Process LockingProcess { get; private set; }
|
|
||||||
|
|
||||||
private readonly string file;
|
private readonly string file;
|
||||||
private FileStream lockStream;
|
private FileStream lockStream;
|
||||||
|
private Process lockingProcess;
|
||||||
|
|
||||||
public LockManager(string file){
|
public LockManager(string file){
|
||||||
this.file = file;
|
this.file = file;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateLockFileStream(){
|
// Lock file
|
||||||
lockStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read);
|
|
||||||
WriteIntToStream(lockStream, GetCurrentProcessId());
|
|
||||||
lockStream.Flush(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool ReleaseLockFileStream(){
|
private bool ReleaseLockFileStream(){
|
||||||
if (lockStream != null){
|
if (lockStream != null){
|
||||||
@@ -37,8 +36,10 @@ namespace TweetDuck.Configuration{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Result TryCreateLockFile(){
|
private Result TryCreateLockFile(){
|
||||||
if (lockStream != null){
|
void CreateLockFileStream(){
|
||||||
throw new InvalidOperationException("Lock file already exists.");
|
lockStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read);
|
||||||
|
lockStream.Write(BitConverter.GetBytes(WindowsUtils.CurrentProcessID), 0, sizeof(int));
|
||||||
|
lockStream.Flush(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
try{
|
try{
|
||||||
@@ -60,6 +61,8 @@ namespace TweetDuck.Configuration{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Lock management
|
||||||
|
|
||||||
public Result Lock(){
|
public Result Lock(){
|
||||||
if (lockStream != null){
|
if (lockStream != null){
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
@@ -72,7 +75,9 @@ namespace TweetDuck.Configuration{
|
|||||||
int pid;
|
int pid;
|
||||||
|
|
||||||
using(FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)){
|
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{
|
try{
|
||||||
@@ -80,7 +85,7 @@ namespace TweetDuck.Configuration{
|
|||||||
|
|
||||||
using(Process currentProcess = Process.GetCurrentProcess()){
|
using(Process currentProcess = Process.GetCurrentProcess()){
|
||||||
if (foundProcess.MainModule.FileVersionInfo.InternalName == currentProcess.MainModule.FileVersionInfo.InternalName){
|
if (foundProcess.MainModule.FileVersionInfo.InternalName == currentProcess.MainModule.FileVersionInfo.InternalName){
|
||||||
LockingProcess = foundProcess;
|
lockingProcess = foundProcess;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
foundProcess.Close();
|
foundProcess.Close();
|
||||||
@@ -91,7 +96,7 @@ namespace TweetDuck.Configuration{
|
|||||||
// Process.MainModule can throw exceptions in some cases
|
// Process.MainModule can throw exceptions in some cases
|
||||||
}
|
}
|
||||||
|
|
||||||
return LockingProcess == null ? Result.Fail : Result.HasProcess;
|
return lockingProcess == null ? Result.Fail : Result.HasProcess;
|
||||||
}catch{
|
}catch{
|
||||||
return Result.Fail;
|
return Result.Fail;
|
||||||
}
|
}
|
||||||
@@ -100,45 +105,72 @@ namespace TweetDuck.Configuration{
|
|||||||
return initialResult;
|
return initialResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Unlock(){
|
public Result LockWait(int timeout){
|
||||||
bool result = true;
|
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()){
|
if (ReleaseLockFileStream()){
|
||||||
try{
|
try{
|
||||||
File.Delete(file);
|
File.Delete(file);
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
Program.Reporter.Log(e.ToString());
|
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){
|
public bool CloseLockingProcess(int closeTimeout, int killTimeout){
|
||||||
if (LockingProcess != null){
|
if (lockingProcess != null){
|
||||||
try{
|
try{
|
||||||
if (LockingProcess.CloseMainWindow()){
|
if (lockingProcess.CloseMainWindow()){
|
||||||
WindowsUtils.TrySleepUntil(CheckLockingProcessExited, closeTimeout, 250);
|
WindowsUtils.TrySleepUntil(CheckLockingProcessExited, closeTimeout, RetryDelay);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!LockingProcess.HasExited){
|
if (!lockingProcess.HasExited){
|
||||||
LockingProcess.Kill();
|
lockingProcess.Kill();
|
||||||
WindowsUtils.TrySleepUntil(CheckLockingProcessExited, killTimeout, 250);
|
WindowsUtils.TrySleepUntil(CheckLockingProcessExited, killTimeout, RetryDelay);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LockingProcess.HasExited){
|
if (lockingProcess.HasExited){
|
||||||
LockingProcess.Dispose();
|
lockingProcess.Dispose();
|
||||||
LockingProcess = null;
|
lockingProcess = null;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}catch(Exception ex){
|
}catch(Exception ex){
|
||||||
if (ex is InvalidOperationException || ex is Win32Exception){
|
if (ex is InvalidOperationException || ex is Win32Exception){
|
||||||
if (LockingProcess != null){
|
if (lockingProcess != null){
|
||||||
LockingProcess.Refresh();
|
bool hasExited = CheckLockingProcessExited();
|
||||||
|
lockingProcess.Dispose();
|
||||||
bool hasExited = LockingProcess.HasExited;
|
|
||||||
LockingProcess.Dispose();
|
|
||||||
return hasExited;
|
return hasExited;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -150,27 +182,8 @@ namespace TweetDuck.Configuration{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private bool CheckLockingProcessExited(){
|
private bool CheckLockingProcessExited(){
|
||||||
LockingProcess.Refresh();
|
lockingProcess.Refresh();
|
||||||
return LockingProcess.HasExited;
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int GetCurrentProcessId(){
|
|
||||||
using(Process process = Process.GetCurrentProcess()){
|
|
||||||
return process.Id;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,45 +1,41 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using TweetDuck.Core.Utils;
|
||||||
|
using TweetDuck.Data.Serialization;
|
||||||
|
|
||||||
namespace TweetDuck.Configuration{
|
namespace TweetDuck.Configuration{
|
||||||
sealed class SystemConfig{
|
sealed class SystemConfig{
|
||||||
|
private static readonly FileSerializer<SystemConfig> Serializer = new FileSerializer<SystemConfig>();
|
||||||
|
|
||||||
public static readonly bool IsHardwareAccelerationSupported = File.Exists(Path.Combine(Program.ProgramPath, "libEGL.dll")) &&
|
public static readonly bool IsHardwareAccelerationSupported = File.Exists(Path.Combine(Program.ProgramPath, "libEGL.dll")) &&
|
||||||
File.Exists(Path.Combine(Program.ProgramPath, "libGLESv2.dll"));
|
File.Exists(Path.Combine(Program.ProgramPath, "libGLESv2.dll"));
|
||||||
|
|
||||||
|
// CONFIGURATION DATA
|
||||||
|
|
||||||
|
private bool _hardwareAcceleration = true;
|
||||||
|
|
||||||
|
public bool EnableBrowserGCReload { get; set; } = true;
|
||||||
|
public int BrowserMemoryThreshold { get; set; } = 400;
|
||||||
|
|
||||||
|
// SPECIAL PROPERTIES
|
||||||
|
|
||||||
public bool HardwareAcceleration{
|
public bool HardwareAcceleration{
|
||||||
get => hardwareAcceleration && IsHardwareAccelerationSupported;
|
get => _hardwareAcceleration && IsHardwareAccelerationSupported;
|
||||||
set => hardwareAcceleration = value;
|
set => _hardwareAcceleration = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// END OF CONFIG
|
||||||
|
|
||||||
private readonly string file;
|
private readonly string file;
|
||||||
|
|
||||||
private bool hardwareAcceleration;
|
|
||||||
|
|
||||||
private SystemConfig(string file){
|
private SystemConfig(string file){
|
||||||
this.file = 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(){
|
public bool Save(){
|
||||||
try{
|
try{
|
||||||
string directory = Path.GetDirectoryName(file);
|
WindowsUtils.CreateDirectoryForFile(file);
|
||||||
if (directory == null)return false;
|
Serializer.Write(file, this);
|
||||||
|
|
||||||
Directory.CreateDirectory(directory);
|
|
||||||
|
|
||||||
using(Stream stream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None)){
|
|
||||||
WriteToStream(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
Program.Reporter.HandleException("Configuration Error", "Could not save the system configuration file.", true, e);
|
Program.Reporter.HandleException("Configuration Error", "Could not save the system configuration file.", true, e);
|
||||||
@@ -51,9 +47,7 @@ namespace TweetDuck.Configuration{
|
|||||||
SystemConfig config = new SystemConfig(file);
|
SystemConfig config = new SystemConfig(file);
|
||||||
|
|
||||||
try{
|
try{
|
||||||
using(Stream stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read)){
|
Serializer.Read(file, config);
|
||||||
config.ReadFromStream(stream);
|
|
||||||
}
|
|
||||||
}catch(FileNotFoundException){
|
}catch(FileNotFoundException){
|
||||||
}catch(DirectoryNotFoundException){
|
}catch(DirectoryNotFoundException){
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
|
|||||||
@@ -1,206 +1,156 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.Serialization;
|
|
||||||
using System.Runtime.Serialization.Formatters.Binary;
|
|
||||||
using TweetDuck.Core;
|
using TweetDuck.Core;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Notification;
|
using TweetDuck.Core.Notification;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
using TweetDuck.Data;
|
||||||
|
using TweetDuck.Data.Serialization;
|
||||||
|
|
||||||
namespace TweetDuck.Configuration{
|
namespace TweetDuck.Configuration{
|
||||||
[Serializable]
|
|
||||||
sealed class UserConfig{
|
sealed class UserConfig{
|
||||||
private static readonly IFormatter Formatter = new BinaryFormatter{ Binder = new LegacyBinder() };
|
private static readonly FileSerializer<UserConfig> Serializer = new FileSerializer<UserConfig>{ HandleUnknownProperties = HandleUnknownProperties };
|
||||||
|
|
||||||
private class LegacyBinder : SerializationBinder{
|
private static void HandleUnknownProperties(UserConfig obj, Dictionary<string, string> data){
|
||||||
public override Type BindToType(string assemblyName, string typeName){
|
data.Remove("EnableBrowserGCReload");
|
||||||
return Type.GetType(string.Format("{0}, {1}", typeName.Replace("TweetDck", "TweetDuck"), assemblyName.Replace("TweetDck", "TweetDuck")));
|
data.Remove("BrowserMemoryThreshold");
|
||||||
|
|
||||||
|
if (data.Count == 0){
|
||||||
|
obj.Save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private const int CurrentFileVersion = 10;
|
static UserConfig(){
|
||||||
|
Serializer.RegisterTypeConverter(typeof(WindowState), WindowState.Converter);
|
||||||
|
|
||||||
// START OF CONFIGURATION
|
Serializer.RegisterTypeConverter(typeof(Point), new SingleTypeConverter<Point>{
|
||||||
|
ConvertToString = value => $"{value.X} {value.Y}",
|
||||||
|
ConvertToObject = value => {
|
||||||
|
int[] elements = StringUtils.ParseInts(value, ' ');
|
||||||
|
return new Point(elements[0], elements[1]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
public WindowState BrowserWindow { get; set; }
|
Serializer.RegisterTypeConverter(typeof(Size), new SingleTypeConverter<Size>{
|
||||||
public bool DisplayNotificationColumn { get; set; }
|
ConvertToString = value => $"{value.Width} {value.Height}",
|
||||||
public bool DisplayNotificationTimer { get; set; }
|
ConvertToObject = value => {
|
||||||
public bool NotificationTimerCountDown { get; set; }
|
int[] elements = StringUtils.ParseInts(value, ' ');
|
||||||
public bool NotificationSkipOnLinkClick { get; set; }
|
return new Size(elements[0], elements[1]);
|
||||||
public bool NotificationNonIntrusiveMode { get; set; }
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public TweetNotification.Position NotificationPosition { get; set; }
|
// CONFIGURATION DATA
|
||||||
public Point CustomNotificationPosition { get; set; }
|
|
||||||
public int NotificationEdgeDistance { get; set; }
|
|
||||||
public int NotificationDisplay { get; set; }
|
|
||||||
public int NotificationIdlePauseSeconds { get; set; }
|
|
||||||
public int NotificationDurationValue { get; set; }
|
|
||||||
public int NotificationScrollSpeed { get; set; }
|
|
||||||
|
|
||||||
public bool EnableSpellCheck { get; set; }
|
public WindowState BrowserWindow { get; set; } = new WindowState();
|
||||||
public bool ExpandLinksOnHover { get; set; }
|
public WindowState PluginsWindow { get; set; } = new WindowState();
|
||||||
public bool SwitchAccountSelectors { get; set; }
|
|
||||||
public bool EnableTrayHighlight { get; set; }
|
|
||||||
|
|
||||||
public bool EnableUpdateCheck { get; set; }
|
public bool ExpandLinksOnHover { get; set; } = true;
|
||||||
public string DismissedUpdate { get; set; }
|
public bool SwitchAccountSelectors { 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;
|
||||||
|
|
||||||
public WindowState PluginsWindow { get; set; }
|
private TrayIcon.Behavior _trayBehavior = TrayIcon.Behavior.Disabled;
|
||||||
|
public bool EnableTrayHighlight { get; set; } = true;
|
||||||
|
|
||||||
public string CustomCefArgs { get; set; }
|
public bool EnableUpdateCheck { get; set; } = true;
|
||||||
public string CustomBrowserCSS { get; set; }
|
public string DismissedUpdate { get; set; } = null;
|
||||||
public string CustomNotificationCSS { get; set; }
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
public bool DisplayNotificationTimer { get; set; } = true;
|
||||||
|
public bool NotificationTimerCountDown { get; set; } = false;
|
||||||
|
public int NotificationDurationValue { get; set; } = 25;
|
||||||
|
|
||||||
|
public TweetNotification.Position NotificationPosition { get; set; } = TweetNotification.Position.TopRight;
|
||||||
|
public Point CustomNotificationPosition { get; set; } = ControlExtensions.InvisibleLocation;
|
||||||
|
public int NotificationDisplay { get; set; } = 0;
|
||||||
|
public int NotificationEdgeDistance { get; set; } = 8;
|
||||||
|
|
||||||
|
public TweetNotification.Size NotificationSize { get; set; } = TweetNotification.Size.Auto;
|
||||||
|
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;
|
||||||
|
|
||||||
|
// SPECIAL PROPERTIES
|
||||||
|
|
||||||
public bool IsCustomNotificationPositionSet => CustomNotificationPosition != ControlExtensions.InvisibleLocation;
|
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{
|
public string NotificationSoundPath{
|
||||||
get => string.IsNullOrEmpty(notificationSoundPath) ? string.Empty : notificationSoundPath;
|
get => string.IsNullOrEmpty(_notificationSoundPath) ? string.Empty : _notificationSoundPath;
|
||||||
set => notificationSoundPath = value;
|
set => _notificationSoundPath = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool MuteNotifications{
|
public bool MuteNotifications{
|
||||||
get => muteNotifications;
|
get => _muteNotifications;
|
||||||
|
|
||||||
set{
|
set{
|
||||||
if (muteNotifications != value){
|
if (_muteNotifications != value){
|
||||||
muteNotifications = value;
|
_muteNotifications = value;
|
||||||
MuteToggled?.Invoke(this, new EventArgs());
|
MuteToggled?.Invoke(this, new EventArgs());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int ZoomLevel{
|
public int ZoomLevel{
|
||||||
get => zoomLevel;
|
get => _zoomLevel;
|
||||||
|
|
||||||
set{
|
set{
|
||||||
if (zoomLevel != value){
|
if (_zoomLevel != value){
|
||||||
zoomLevel = value;
|
_zoomLevel = value;
|
||||||
ZoomLevelChanged?.Invoke(this, new EventArgs());
|
ZoomLevelChanged?.Invoke(this, new EventArgs());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public double ZoomMultiplier => zoomLevel/100.0;
|
public double ZoomMultiplier => _zoomLevel/100.0;
|
||||||
|
|
||||||
public TrayIcon.Behavior TrayBehavior{
|
public TrayIcon.Behavior TrayBehavior{
|
||||||
get => trayBehavior;
|
get => _trayBehavior;
|
||||||
|
|
||||||
set{
|
set{
|
||||||
if (trayBehavior != value){
|
if (_trayBehavior != value){
|
||||||
trayBehavior = value;
|
_trayBehavior = value;
|
||||||
TrayBehaviorChanged?.Invoke(this, new EventArgs());
|
TrayBehaviorChanged?.Invoke(this, new EventArgs());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// END OF CONFIGURATION
|
// EVENTS
|
||||||
|
|
||||||
[field:NonSerialized]
|
|
||||||
public event EventHandler MuteToggled;
|
public event EventHandler MuteToggled;
|
||||||
|
|
||||||
[field:NonSerialized]
|
|
||||||
public event EventHandler ZoomLevelChanged;
|
public event EventHandler ZoomLevelChanged;
|
||||||
|
|
||||||
[field:NonSerialized]
|
|
||||||
public event EventHandler TrayBehaviorChanged;
|
public event EventHandler TrayBehaviorChanged;
|
||||||
|
|
||||||
[NonSerialized]
|
// END OF CONFIG
|
||||||
private string file;
|
|
||||||
|
|
||||||
private int fileVersion;
|
private readonly string file;
|
||||||
private bool muteNotifications;
|
|
||||||
private int zoomLevel;
|
|
||||||
private string notificationSoundPath;
|
|
||||||
private TrayIcon.Behavior trayBehavior;
|
|
||||||
|
|
||||||
private UserConfig(string file){
|
private UserConfig(string file){
|
||||||
this.file = file;
|
this.file = file;
|
||||||
|
|
||||||
BrowserWindow = new WindowState();
|
|
||||||
ZoomLevel = 100;
|
|
||||||
DisplayNotificationTimer = true;
|
|
||||||
NotificationNonIntrusiveMode = true;
|
|
||||||
NotificationPosition = TweetNotification.Position.TopRight;
|
|
||||||
CustomNotificationPosition = ControlExtensions.InvisibleLocation;
|
|
||||||
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 == 0){
|
|
||||||
DisplayNotificationTimer = true;
|
|
||||||
EnableUpdateCheck = true;
|
|
||||||
++fileVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fileVersion == 1){
|
|
||||||
ExpandLinksOnHover = true;
|
|
||||||
++fileVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fileVersion == 2){
|
|
||||||
BrowserWindow = new WindowState();
|
|
||||||
PluginsWindow = new WindowState();
|
|
||||||
++fileVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fileVersion == 3){
|
|
||||||
EnableTrayHighlight = true;
|
|
||||||
NotificationDurationValue = 25;
|
|
||||||
++fileVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fileVersion == 4){
|
|
||||||
++fileVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fileVersion == 5){
|
|
||||||
++fileVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fileVersion == 6){
|
|
||||||
NotificationNonIntrusiveMode = true;
|
|
||||||
++fileVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fileVersion == 7){
|
|
||||||
ZoomLevel = 100;
|
|
||||||
++fileVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fileVersion == 8){
|
|
||||||
SwitchAccountSelectors = true;
|
|
||||||
++fileVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fileVersion == 9){
|
|
||||||
NotificationScrollSpeed = 100;
|
|
||||||
++fileVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update the version
|
|
||||||
fileVersion = CurrentFileVersion;
|
|
||||||
Save();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Save(){
|
public bool Save(){
|
||||||
try{
|
try{
|
||||||
string directory = Path.GetDirectoryName(file);
|
WindowsUtils.CreateDirectoryForFile(file);
|
||||||
if (directory == null)return false;
|
|
||||||
|
|
||||||
Directory.CreateDirectory(directory);
|
|
||||||
|
|
||||||
if (File.Exists(file)){
|
if (File.Exists(file)){
|
||||||
string backupFile = GetBackupFile(file);
|
string backupFile = GetBackupFile(file);
|
||||||
@@ -208,10 +158,7 @@ namespace TweetDuck.Configuration{
|
|||||||
File.Move(file, backupFile);
|
File.Move(file, backupFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
using(Stream stream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None)){
|
Serializer.Write(file, this);
|
||||||
Formatter.Serialize(stream, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
Program.Reporter.HandleException("Configuration Error", "Could not save the configuration file.", true, e);
|
Program.Reporter.HandleException("Configuration Error", "Could not save the configuration file.", true, e);
|
||||||
@@ -219,20 +166,37 @@ namespace TweetDuck.Configuration{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool Reload(){
|
||||||
|
try{
|
||||||
|
LoadInternal(false);
|
||||||
|
return true;
|
||||||
|
}catch(FileNotFoundException){
|
||||||
|
try{
|
||||||
|
Serializer.Write(file, new UserConfig(file));
|
||||||
|
LoadInternal(false);
|
||||||
|
return true;
|
||||||
|
}catch(Exception e){
|
||||||
|
Program.Reporter.HandleException("Configuration Error", "Could not regenerate configuration file.", true, e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}catch(Exception e){
|
||||||
|
Program.Reporter.HandleException("Configuration Error", "Could not reload configuration file.", true, e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadInternal(bool backup){
|
||||||
|
Serializer.Read(backup ? GetBackupFile(file) : file, this);
|
||||||
|
}
|
||||||
|
|
||||||
public static UserConfig Load(string file){
|
public static UserConfig Load(string file){
|
||||||
UserConfig config = null;
|
|
||||||
Exception firstException = null;
|
Exception firstException = null;
|
||||||
|
|
||||||
for(int attempt = 0; attempt < 2; attempt++){
|
for(int attempt = 0; attempt < 2; attempt++){
|
||||||
try{
|
try{
|
||||||
using(Stream stream = new FileStream(attempt == 0 ? file : GetBackupFile(file), FileMode.Open, FileAccess.Read, FileShare.Read)){
|
UserConfig config = new UserConfig(file);
|
||||||
if ((config = Formatter.Deserialize(stream) as UserConfig) != null){
|
config.LoadInternal(attempt > 0);
|
||||||
config.file = file;
|
return config;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
config?.UpgradeFile();
|
|
||||||
break;
|
|
||||||
}catch(FileNotFoundException){
|
}catch(FileNotFoundException){
|
||||||
}catch(DirectoryNotFoundException){
|
}catch(DirectoryNotFoundException){
|
||||||
break;
|
break;
|
||||||
@@ -241,17 +205,22 @@ namespace TweetDuck.Configuration{
|
|||||||
firstException = e;
|
firstException = e;
|
||||||
Program.Reporter.Log(e.ToString());
|
Program.Reporter.Log(e.ToString());
|
||||||
}
|
}
|
||||||
|
else if (firstException is FormatException){
|
||||||
|
Program.Reporter.HandleException("Configuration Error", "The configuration file is outdated or corrupted. If you continue, your program options will be reset.", true, e);
|
||||||
|
return new UserConfig(file);
|
||||||
|
}
|
||||||
else if (firstException != null){
|
else if (firstException != null){
|
||||||
Program.Reporter.HandleException("Configuration Error", "Could not open the backup configuration file. If you continue, your program options will be reset.", true, e);
|
Program.Reporter.HandleException("Configuration Error", "Could not open the backup configuration file. If you continue, your program options will be reset.", true, e);
|
||||||
|
return new UserConfig(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (firstException != null && config == null){
|
if (firstException != null){
|
||||||
Program.Reporter.HandleException("Configuration Error", "Could not open the configuration file.", true, firstException);
|
Program.Reporter.HandleException("Configuration Error", "Could not open the configuration file.", true, firstException);
|
||||||
}
|
}
|
||||||
|
|
||||||
return config ?? new UserConfig(file);
|
return new UserConfig(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetBackupFile(string file){
|
public static string GetBackupFile(string file){
|
||||||
|
|||||||
@@ -1,45 +1,32 @@
|
|||||||
using System;
|
using System.Text;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace TweetDuck.Core.Bridge{
|
namespace TweetDuck.Core.Bridge{
|
||||||
static class PropertyBridge{
|
static class PropertyBridge{
|
||||||
[Flags]
|
public enum Environment{
|
||||||
public enum Properties{
|
Browser, Notification
|
||||||
ExpandLinksOnHover = 1,
|
|
||||||
MuteNotifications = 2,
|
|
||||||
HasCustomNotificationSound = 4,
|
|
||||||
SkipOnLinkClick = 8,
|
|
||||||
SwitchAccountSelectors = 16,
|
|
||||||
AllBrowser = ExpandLinksOnHover | SwitchAccountSelectors | MuteNotifications | HasCustomNotificationSound,
|
|
||||||
AllNotification = ExpandLinksOnHover | SkipOnLinkClick
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GenerateScript(Properties properties){
|
public static string GenerateScript(Environment environment){
|
||||||
StringBuilder build = new StringBuilder();
|
string Bool(bool value){
|
||||||
build.Append("(function(c){");
|
return value ? "true;" : "false;";
|
||||||
|
|
||||||
if (properties.HasFlag(Properties.ExpandLinksOnHover)){
|
|
||||||
build.Append("c.expandLinksOnHover=").Append(Program.UserConfig.ExpandLinksOnHover ? "true;" : "false;");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (properties.HasFlag(Properties.SwitchAccountSelectors)){
|
StringBuilder build = new StringBuilder().Append("(function(x){");
|
||||||
build.Append("c.switchAccountSelectors=").Append(Program.UserConfig.SwitchAccountSelectors ? "true;" : "false;");
|
|
||||||
|
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.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)){
|
if (environment == Environment.Notification){
|
||||||
build.Append("c.muteNotifications=").Append(Program.UserConfig.MuteNotifications ? "true;" : "false;");
|
build.Append("x.skipOnLinkClick=").Append(Bool(Program.UserConfig.NotificationSkipOnLinkClick));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (properties.HasFlag(Properties.HasCustomNotificationSound)){
|
return build.Append("})(window.$TDX=window.$TDX||{})").ToString();
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,38 @@
|
|||||||
using System.Windows.Forms;
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using CefSharp;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
|
using TweetDuck.Core.Handling;
|
||||||
using TweetDuck.Core.Notification;
|
using TweetDuck.Core.Notification;
|
||||||
|
using TweetDuck.Core.Other;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
using TweetDuck.Resources;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Bridge{
|
namespace TweetDuck.Core.Bridge{
|
||||||
sealed class TweetDeckBridge{
|
sealed class TweetDeckBridge{
|
||||||
public static string LastRightClickedLink = string.Empty;
|
|
||||||
public static string LastHighlightedTweet = string.Empty;
|
public static string LastHighlightedTweet = string.Empty;
|
||||||
public static string LastHighlightedQuotedTweet = string.Empty;
|
public static string LastHighlightedQuotedTweet = string.Empty;
|
||||||
|
public static string LastHighlightedTweetAuthor = string.Empty;
|
||||||
|
public static string[] LastHighlightedTweetImages = StringUtils.EmptyArray;
|
||||||
|
public static Dictionary<string, string> SessionData = new Dictionary<string, string>(2);
|
||||||
|
|
||||||
public static void ResetStaticProperties(){
|
public static void ResetStaticProperties(){
|
||||||
LastRightClickedLink = LastHighlightedTweet = LastHighlightedQuotedTweet = string.Empty;
|
LastHighlightedTweet = LastHighlightedQuotedTweet = LastHighlightedTweetAuthor = string.Empty;
|
||||||
|
LastHighlightedTweetImages = StringUtils.EmptyArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RestoreSessionData(IFrame frame){
|
||||||
|
if (SessionData.Count > 0){
|
||||||
|
StringBuilder build = new StringBuilder().Append("window.TD_SESSION={");
|
||||||
|
|
||||||
|
foreach(KeyValuePair<string, string> kvp in SessionData){
|
||||||
|
build.Append(kvp.Key).Append(":'").Append(kvp.Value.Replace("'", "\\'")).Append("',");
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptLoader.ExecuteScript(frame, build.Append("}").ToString(), "gen:session");
|
||||||
|
SessionData.Clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly FormBrowser form;
|
private readonly FormBrowser form;
|
||||||
@@ -33,14 +55,16 @@ namespace TweetDuck.Core.Bridge{
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetLastRightClickedLink(string link){
|
public void SetLastRightClickInfo(string type, string link){
|
||||||
form.InvokeAsyncSafe(() => LastRightClickedLink = link);
|
form.InvokeAsyncSafe(() => ContextMenuBase.SetContextInfo(type, link));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetLastHighlightedTweet(string link, string quotedLink){
|
public void SetLastHighlightedTweet(string link, string quotedLink, string author, string imageList){
|
||||||
form.InvokeAsyncSafe(() => {
|
form.InvokeAsyncSafe(() => {
|
||||||
LastHighlightedTweet = link;
|
LastHighlightedTweet = link;
|
||||||
LastHighlightedQuotedTweet = quotedLink;
|
LastHighlightedQuotedTweet = quotedLink;
|
||||||
|
LastHighlightedTweetAuthor = author;
|
||||||
|
LastHighlightedTweetImages = imageList.Split(';');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,10 +72,10 @@ namespace TweetDuck.Core.Bridge{
|
|||||||
form.InvokeAsyncSafe(form.OpenContextMenu);
|
form.InvokeAsyncSafe(form.OpenContextMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnTweetPopup(string columnName, string tweetHtml, int tweetCharacters, string tweetUrl, string quoteUrl){
|
public void OnTweetPopup(string columnKey, string chirpId, string columnName, string tweetHtml, int tweetCharacters, string tweetUrl, string quoteUrl){
|
||||||
notification.InvokeAsyncSafe(() => {
|
notification.InvokeAsyncSafe(() => {
|
||||||
form.OnTweetNotification();
|
form.OnTweetNotification();
|
||||||
notification.ShowNotification(new TweetNotification(columnName, tweetHtml, tweetCharacters, tweetUrl, quoteUrl));
|
notification.ShowNotification(new TweetNotification(columnKey, chirpId, columnName, tweetHtml, tweetCharacters, tweetUrl, quoteUrl));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,6 +95,12 @@ namespace TweetDuck.Core.Bridge{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetSessionData(string key, string value){
|
||||||
|
form.InvokeSafe(() => { // do not use InvokeAsyncSafe, return only after invocation
|
||||||
|
SessionData.Add(key, value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public void LoadNextNotification(){
|
public void LoadNextNotification(){
|
||||||
notification.InvokeAsyncSafe(notification.FinishCurrentNotification);
|
notification.InvokeAsyncSafe(notification.FinishCurrentNotification);
|
||||||
}
|
}
|
||||||
@@ -79,6 +109,10 @@ namespace TweetDuck.Core.Bridge{
|
|||||||
form.InvokeAsyncSafe(() => form.OnTweetScreenshotReady(html, width, height));
|
form.InvokeAsyncSafe(() => form.OnTweetScreenshotReady(html, width, height));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void PlayVideo(string url){
|
||||||
|
form.InvokeAsyncSafe(() => form.PlayVideo(url));
|
||||||
|
}
|
||||||
|
|
||||||
public void FixClipboard(){
|
public void FixClipboard(){
|
||||||
form.InvokeAsyncSafe(WindowsUtils.ClipboardStripHtmlStyles);
|
form.InvokeAsyncSafe(WindowsUtils.ClipboardStripHtmlStyles);
|
||||||
}
|
}
|
||||||
@@ -101,7 +135,7 @@ namespace TweetDuck.Core.Bridge{
|
|||||||
default: icon = MessageBoxIcon.None; break;
|
default: icon = MessageBoxIcon.None; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageBox.Show(contents, Program.BrandName+" Browser Message", MessageBoxButtons.OK, icon);
|
FormMessage.Show("TweetDuck Browser Message", contents, icon, FormMessage.OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CrashDebug(string message){
|
public void CrashDebug(string message){
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Core.Utils;
|
|
||||||
|
|
||||||
namespace TweetDuck.Core.Controls{
|
namespace TweetDuck.Core.Controls{
|
||||||
static class ControlExtensions{
|
static class ControlExtensions{
|
||||||
@@ -21,6 +20,12 @@ namespace TweetDuck.Core.Controls{
|
|||||||
control.BeginInvoke(func);
|
control.BeginInvoke(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static float GetDPIScale(this Control control){
|
||||||
|
using(Graphics graphics = control.CreateGraphics()){
|
||||||
|
return graphics.DpiY/96F;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static bool IsFullyOutsideView(this Form form){
|
public static bool IsFullyOutsideView(this Form form){
|
||||||
return !Screen.AllScreens.Any(screen => screen.WorkingArea.IntersectsWith(form.Bounds));
|
return !Screen.AllScreens.Any(screen => screen.WorkingArea.IntersectsWith(form.Bounds));
|
||||||
}
|
}
|
||||||
@@ -41,6 +46,12 @@ namespace TweetDuck.Core.Controls{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void SetValueSafe(this NumericUpDown numUpDown, int value){
|
||||||
|
if (value >= numUpDown.Minimum && value <= numUpDown.Maximum){
|
||||||
|
numUpDown.Value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void SetValueSafe(this TrackBar trackBar, int value){
|
public static void SetValueSafe(this TrackBar trackBar, int value){
|
||||||
if (value >= trackBar.Minimum && value <= trackBar.Maximum){
|
if (value >= trackBar.Minimum && value <= trackBar.Maximum){
|
||||||
trackBar.Value = value;
|
trackBar.Value = value;
|
||||||
@@ -55,12 +66,6 @@ namespace TweetDuck.Core.Controls{
|
|||||||
else return true;
|
else return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SetElevated(this Button button){
|
|
||||||
button.Text = " "+button.Text;
|
|
||||||
button.FlatStyle = FlatStyle.System;
|
|
||||||
NativeMethods.SendMessage(button.Handle, NativeMethods.BCM_SETSHIELD, 0, new IntPtr(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void EnableMultilineShortcuts(this TextBox textBox){
|
public static void EnableMultilineShortcuts(this TextBox textBox){
|
||||||
textBox.KeyDown += (sender, args) => {
|
textBox.KeyDown += (sender, args) => {
|
||||||
if (args.Control && args.KeyCode == Keys.A){
|
if (args.Control && args.KeyCode == Keys.A){
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Controls{
|
namespace TweetDuck.Core.Controls{
|
||||||
class FlatButton : Button{
|
sealed class FlatButton : Button{
|
||||||
protected override bool ShowFocusCues => false;
|
protected override bool ShowFocusCues => false;
|
||||||
|
|
||||||
public FlatButton(){
|
public FlatButton(){
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ namespace TweetDuck.Core.Controls{
|
|||||||
|
|
||||||
Rectangle rect = e.ClipRectangle;
|
Rectangle rect = e.ClipRectangle;
|
||||||
rect.Width = (int)(rect.Width*((double)Value/Maximum));
|
rect.Width = (int)(rect.Width*((double)Value/Maximum));
|
||||||
e.Graphics.FillRectangle(brush,rect);
|
e.Graphics.FillRectangle(brush, rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool disposing){
|
protected override void Dispose(bool disposing){
|
||||||
|
|||||||
18
Core/Controls/NumericUpDownEx.cs
Normal file
18
Core/Controls/NumericUpDownEx.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace TweetDuck.Core.Controls{
|
||||||
|
sealed class NumericUpDownEx : NumericUpDown{
|
||||||
|
public string TextSuffix { get; set ; }
|
||||||
|
|
||||||
|
protected override void UpdateEditText(){
|
||||||
|
base.UpdateEditText();
|
||||||
|
|
||||||
|
if (LicenseManager.UsageMode != LicenseUsageMode.Designtime){
|
||||||
|
ChangingText = true;
|
||||||
|
Text += TextSuffix;
|
||||||
|
ChangingText = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
Core/FormBrowser.Designer.cs
generated
14
Core/FormBrowser.Designer.cs
generated
@@ -26,22 +26,29 @@
|
|||||||
this.components = new System.ComponentModel.Container();
|
this.components = new System.ComponentModel.Container();
|
||||||
this.trayIcon = new TweetDuck.Core.TrayIcon(this.components);
|
this.trayIcon = new TweetDuck.Core.TrayIcon(this.components);
|
||||||
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
||||||
|
this.timerResize = new System.Windows.Forms.Timer(this.components);
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
//
|
//
|
||||||
|
// timerResize
|
||||||
|
//
|
||||||
|
this.timerResize.Interval = 500;
|
||||||
|
this.timerResize.Tick += new System.EventHandler(this.timerResize_Tick);
|
||||||
|
//
|
||||||
// FormBrowser
|
// FormBrowser
|
||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
this.BackColor = TweetDuck.Core.Utils.BrowserUtils.BackgroundColor;
|
this.BackColor = TweetDuck.Core.Utils.TwitterUtils.BackgroundColor;
|
||||||
this.ClientSize = new System.Drawing.Size(324, 386);
|
this.ClientSize = new System.Drawing.Size(400, 386);
|
||||||
this.Icon = Properties.Resources.icon;
|
this.Icon = Properties.Resources.icon;
|
||||||
this.Location = TweetDuck.Core.Controls.ControlExtensions.InvisibleLocation;
|
this.Location = TweetDuck.Core.Controls.ControlExtensions.InvisibleLocation;
|
||||||
this.MinimumSize = new System.Drawing.Size(340, 424);
|
this.MinimumSize = new System.Drawing.Size(416, 424);
|
||||||
this.Name = "FormBrowser";
|
this.Name = "FormBrowser";
|
||||||
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
|
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
|
||||||
this.Activated += new System.EventHandler(this.FormBrowser_Activated);
|
this.Activated += new System.EventHandler(this.FormBrowser_Activated);
|
||||||
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FormBrowser_FormClosing);
|
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FormBrowser_FormClosing);
|
||||||
this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.FormBrowser_FormClosed);
|
this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.FormBrowser_FormClosed);
|
||||||
|
this.LocationChanged += new System.EventHandler(this.FormBrowser_LocationChanged);
|
||||||
this.ResizeEnd += new System.EventHandler(this.FormBrowser_ResizeEnd);
|
this.ResizeEnd += new System.EventHandler(this.FormBrowser_ResizeEnd);
|
||||||
this.Resize += new System.EventHandler(this.FormBrowser_Resize);
|
this.Resize += new System.EventHandler(this.FormBrowser_Resize);
|
||||||
this.ResumeLayout(false);
|
this.ResumeLayout(false);
|
||||||
@@ -52,6 +59,7 @@
|
|||||||
|
|
||||||
private TrayIcon trayIcon;
|
private TrayIcon trayIcon;
|
||||||
private System.Windows.Forms.ToolTip toolTip;
|
private System.Windows.Forms.ToolTip toolTip;
|
||||||
|
private System.Windows.Forms.Timer timerResize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
using CefSharp;
|
using CefSharp;
|
||||||
using CefSharp.WinForms;
|
using CefSharp.WinForms;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Linq;
|
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Configuration;
|
using TweetDuck.Configuration;
|
||||||
using TweetDuck.Core.Bridge;
|
using TweetDuck.Core.Bridge;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Handling;
|
using TweetDuck.Core.Handling;
|
||||||
|
using TweetDuck.Core.Handling.General;
|
||||||
using TweetDuck.Core.Notification;
|
using TweetDuck.Core.Notification;
|
||||||
using TweetDuck.Core.Notification.Screenshot;
|
using TweetDuck.Core.Notification.Screenshot;
|
||||||
using TweetDuck.Core.Other;
|
using TweetDuck.Core.Other;
|
||||||
|
using TweetDuck.Core.Other.Management;
|
||||||
using TweetDuck.Core.Other.Settings;
|
using TweetDuck.Core.Other.Settings;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetDuck.Plugins;
|
using TweetDuck.Plugins;
|
||||||
@@ -19,13 +19,29 @@ using TweetDuck.Plugins.Enums;
|
|||||||
using TweetDuck.Plugins.Events;
|
using TweetDuck.Plugins.Events;
|
||||||
using TweetDuck.Resources;
|
using TweetDuck.Resources;
|
||||||
using TweetDuck.Updates;
|
using TweetDuck.Updates;
|
||||||
using TweetDuck.Updates.Events;
|
using TweetLib.Audio;
|
||||||
using TweetLib.Audio.Utils;
|
|
||||||
|
|
||||||
namespace TweetDuck.Core{
|
namespace TweetDuck.Core{
|
||||||
sealed partial class FormBrowser : Form{
|
sealed partial class FormBrowser : Form{
|
||||||
private static UserConfig Config => Program.UserConfig;
|
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; }
|
public string UpdateInstallerPath { get; private set; }
|
||||||
|
|
||||||
private readonly ChromiumWebBrowser browser;
|
private readonly ChromiumWebBrowser browser;
|
||||||
@@ -33,6 +49,7 @@ namespace TweetDuck.Core{
|
|||||||
private readonly UpdateHandler updates;
|
private readonly UpdateHandler updates;
|
||||||
private readonly FormNotificationTweet notification;
|
private readonly FormNotificationTweet notification;
|
||||||
private readonly ContextMenu contextMenu;
|
private readonly ContextMenu contextMenu;
|
||||||
|
private readonly MemoryUsageTracker memoryUsageTracker;
|
||||||
|
|
||||||
private bool isLoaded;
|
private bool isLoaded;
|
||||||
private bool isBrowserReady;
|
private bool isBrowserReady;
|
||||||
@@ -40,17 +57,21 @@ namespace TweetDuck.Core{
|
|||||||
|
|
||||||
private TweetScreenshotManager notificationScreenshotManager;
|
private TweetScreenshotManager notificationScreenshotManager;
|
||||||
private SoundNotification soundNotification;
|
private SoundNotification soundNotification;
|
||||||
|
private VideoPlayer videoPlayer;
|
||||||
|
|
||||||
public FormBrowser(PluginManager pluginManager, UpdaterSettings updaterSettings){
|
public FormBrowser(UpdaterSettings updaterSettings){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
Text = Program.BrandName;
|
Text = Program.BrandName;
|
||||||
|
|
||||||
this.plugins = pluginManager;
|
this.plugins = new PluginManager(Program.PluginPath, Program.PluginConfigFilePath);
|
||||||
this.plugins.Reloaded += plugins_Reloaded;
|
this.plugins.Reloaded += plugins_Reloaded;
|
||||||
|
this.plugins.Executed += plugins_Executed;
|
||||||
this.plugins.PluginChangedState += plugins_PluginChangedState;
|
this.plugins.PluginChangedState += plugins_PluginChangedState;
|
||||||
|
this.plugins.Reload();
|
||||||
|
|
||||||
this.contextMenu = ContextMenuBrowser.CreateMenu(this);
|
this.contextMenu = ContextMenuBrowser.CreateMenu(this);
|
||||||
|
this.memoryUsageTracker = new MemoryUsageTracker("TDGF_tryRunCleanup");
|
||||||
|
|
||||||
this.notification = new FormNotificationTweet(this, plugins){
|
this.notification = new FormNotificationTweet(this, plugins){
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
@@ -65,6 +86,7 @@ namespace TweetDuck.Core{
|
|||||||
this.browser = new ChromiumWebBrowser("https://tweetdeck.twitter.com/"){
|
this.browser = new ChromiumWebBrowser("https://tweetdeck.twitter.com/"){
|
||||||
MenuHandler = new ContextMenuBrowser(this),
|
MenuHandler = new ContextMenuBrowser(this),
|
||||||
JsDialogHandler = new JavaScriptDialogHandler(),
|
JsDialogHandler = new JavaScriptDialogHandler(),
|
||||||
|
KeyboardHandler = new KeyboardHandlerBrowser(this),
|
||||||
LifeSpanHandler = new LifeSpanHandler(),
|
LifeSpanHandler = new LifeSpanHandler(),
|
||||||
RequestHandler = new RequestHandlerBrowser()
|
RequestHandler = new RequestHandlerBrowser()
|
||||||
};
|
};
|
||||||
@@ -80,7 +102,7 @@ namespace TweetDuck.Core{
|
|||||||
this.browser.RegisterAsyncJsObject("$TD", new TweetDeckBridge(this, notification));
|
this.browser.RegisterAsyncJsObject("$TD", new TweetDeckBridge(this, notification));
|
||||||
this.browser.RegisterAsyncJsObject("$TDP", plugins.Bridge);
|
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.Dock = DockStyle.None;
|
||||||
browser.Location = ControlExtensions.InvisibleLocation;
|
browser.Location = ControlExtensions.InvisibleLocation;
|
||||||
Controls.Add(browser);
|
Controls.Add(browser);
|
||||||
@@ -88,11 +110,14 @@ namespace TweetDuck.Core{
|
|||||||
Controls.Add(new MenuStrip{ Visible = false }); // fixes Alt freezing the program in Win 10 Anniversary Update
|
Controls.Add(new MenuStrip{ Visible = false }); // fixes Alt freezing the program in Win 10 Anniversary Update
|
||||||
|
|
||||||
Disposed += (sender, args) => {
|
Disposed += (sender, args) => {
|
||||||
|
memoryUsageTracker.Dispose();
|
||||||
|
|
||||||
browser.Dispose();
|
browser.Dispose();
|
||||||
contextMenu.Dispose();
|
contextMenu.Dispose();
|
||||||
|
|
||||||
notificationScreenshotManager?.Dispose();
|
notificationScreenshotManager?.Dispose();
|
||||||
soundNotification?.Dispose();
|
soundNotification?.Dispose();
|
||||||
|
videoPlayer?.Dispose();
|
||||||
};
|
};
|
||||||
|
|
||||||
this.trayIcon.ClickRestore += trayIcon_ClickRestore;
|
this.trayIcon.ClickRestore += trayIcon_ClickRestore;
|
||||||
@@ -104,23 +129,13 @@ namespace TweetDuck.Core{
|
|||||||
Config.MuteToggled += Config_MuteToggled;
|
Config.MuteToggled += Config_MuteToggled;
|
||||||
Config.ZoomLevelChanged += Config_ZoomLevelChanged;
|
Config.ZoomLevelChanged += Config_ZoomLevelChanged;
|
||||||
|
|
||||||
this.updates = new UpdateHandler(browser, this, updaterSettings);
|
this.updates = new UpdateHandler(browser, updaterSettings);
|
||||||
this.updates.UpdateAccepted += updates_UpdateAccepted;
|
this.updates.UpdateAccepted += updates_UpdateAccepted;
|
||||||
this.updates.UpdateDismissed += updates_UpdateDismissed;
|
this.updates.UpdateDismissed += updates_UpdateDismissed;
|
||||||
|
|
||||||
RestoreWindow();
|
RestoreWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryBringToFront<T>() where T : Form{
|
|
||||||
T form = Application.OpenForms.OfType<T>().FirstOrDefault();
|
|
||||||
|
|
||||||
if (form != null){
|
|
||||||
form.BringToFront();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ShowChildForm(Form form){
|
private void ShowChildForm(Form form){
|
||||||
form.VisibleChanged += (sender, args) => form.MoveToCenter(this);
|
form.VisibleChanged += (sender, args) => form.MoveToCenter(this);
|
||||||
form.Show(this);
|
form.Show(this);
|
||||||
@@ -155,7 +170,7 @@ namespace TweetDuck.Core{
|
|||||||
|
|
||||||
private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
|
private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
|
||||||
if (!e.IsLoading){
|
if (!e.IsLoading){
|
||||||
foreach(string word in BrowserUtils.DictionaryWords){
|
foreach(string word in TwitterUtils.DictionaryWords){
|
||||||
browser.AddWordToDictionary(word);
|
browser.AddWordToDictionary(word);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,31 +181,33 @@ namespace TweetDuck.Core{
|
|||||||
|
|
||||||
private void browser_FrameLoadStart(object sender, FrameLoadStartEventArgs e){
|
private void browser_FrameLoadStart(object sender, FrameLoadStartEventArgs e){
|
||||||
if (e.Frame.IsMain){
|
if (e.Frame.IsMain){
|
||||||
|
memoryUsageTracker.Stop();
|
||||||
|
|
||||||
if (Config.ZoomLevel != 100){
|
if (Config.ZoomLevel != 100){
|
||||||
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Config.ZoomLevel);
|
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Config.ZoomLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BrowserUtils.IsTwitterWebsite(e.Frame)){
|
if (TwitterUtils.IsTwitterWebsite(e.Frame)){
|
||||||
ScriptLoader.ExecuteFile(e.Frame, "twitter.js");
|
ScriptLoader.ExecuteFile(e.Frame, "twitter.js");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
|
private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
|
||||||
if (e.Frame.IsMain && BrowserUtils.IsTweetDeckWebsite(e.Frame)){
|
if (e.Frame.IsMain && TwitterUtils.IsTweetDeckWebsite(e.Frame)){
|
||||||
e.Frame.ExecuteJavaScriptAsync(BrowserUtils.BackgroundColorFix);
|
e.Frame.ExecuteJavaScriptAsync(TwitterUtils.BackgroundColorFix);
|
||||||
|
|
||||||
UpdateProperties(PropertyBridge.Properties.AllBrowser);
|
UpdateProperties(PropertyBridge.Environment.Browser);
|
||||||
|
TweetDeckBridge.RestoreSessionData(e.Frame);
|
||||||
ScriptLoader.ExecuteFile(e.Frame, "code.js");
|
ScriptLoader.ExecuteFile(e.Frame, "code.js");
|
||||||
ReinjectCustomCSS(Config.CustomBrowserCSS);
|
ReinjectCustomCSS(Config.CustomBrowserCSS);
|
||||||
|
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Browser);
|
||||||
if (plugins.HasAnyPlugin(PluginEnvironment.Browser)){
|
|
||||||
ScriptLoader.ExecuteFile(e.Frame, PluginManager.PluginBrowserScriptFile);
|
|
||||||
ScriptLoader.ExecuteFile(e.Frame, PluginManager.PluginGlobalScriptFile);
|
|
||||||
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Browser, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
TweetDeckBridge.ResetStaticProperties();
|
TweetDeckBridge.ResetStaticProperties();
|
||||||
|
|
||||||
|
if (Program.SystemConfig.EnableBrowserGCReload){
|
||||||
|
memoryUsageTracker.Start(this, e.Browser, Program.SystemConfig.BrowserMemoryThreshold);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,7 +216,7 @@ namespace TweetDuck.Core{
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!e.FailedUrl.StartsWith("http://td/")){
|
if (!e.FailedUrl.StartsWith("http://td/", StringComparison.Ordinal)){
|
||||||
string errorPage = ScriptLoader.LoadResource("pages/error.html", true);
|
string errorPage = ScriptLoader.LoadResource("pages/error.html", true);
|
||||||
|
|
||||||
if (errorPage != null){
|
if (errorPage != null){
|
||||||
@@ -208,10 +225,25 @@ namespace TweetDuck.Core{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void timerResize_Tick(object sender, EventArgs e){
|
||||||
|
FormBrowser_ResizeEnd(this, e); // also stops timer
|
||||||
|
}
|
||||||
|
|
||||||
private void FormBrowser_Activated(object sender, EventArgs e){
|
private void FormBrowser_Activated(object sender, EventArgs e){
|
||||||
if (!isLoaded)return;
|
if (!isLoaded)return;
|
||||||
|
|
||||||
trayIcon.HasNotifications = false;
|
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){
|
||||||
|
if (!isLoaded)return;
|
||||||
|
|
||||||
|
timerResize.Stop();
|
||||||
|
timerResize.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FormBrowser_Resize(object sender, EventArgs e){
|
private void FormBrowser_Resize(object sender, EventArgs e){
|
||||||
@@ -229,11 +261,17 @@ namespace TweetDuck.Core{
|
|||||||
FormBrowser_ResizeEnd(sender, e);
|
FormBrowser_ResizeEnd(sender, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else{
|
||||||
|
timerResize.Stop();
|
||||||
|
timerResize.Start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FormBrowser_ResizeEnd(object sender, EventArgs e){ // also triggers when the window moves
|
private void FormBrowser_ResizeEnd(object sender, EventArgs e){ // also triggers when the window moves
|
||||||
if (!isLoaded)return;
|
if (!isLoaded)return;
|
||||||
|
|
||||||
|
timerResize.Stop();
|
||||||
|
|
||||||
if (Location != ControlExtensions.InvisibleLocation){
|
if (Location != ControlExtensions.InvisibleLocation){
|
||||||
Config.BrowserWindow.Save(this);
|
Config.BrowserWindow.Save(this);
|
||||||
Config.Save();
|
Config.Save();
|
||||||
@@ -256,7 +294,7 @@ namespace TweetDuck.Core{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void Config_MuteToggled(object sender, EventArgs e){
|
private void Config_MuteToggled(object sender, EventArgs e){
|
||||||
UpdateProperties(PropertyBridge.Properties.MuteNotifications);
|
UpdateProperties(PropertyBridge.Environment.Browser);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Config_ZoomLevelChanged(object sender, EventArgs e){
|
private void Config_ZoomLevelChanged(object sender, EventArgs e){
|
||||||
@@ -279,39 +317,51 @@ namespace TweetDuck.Core{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void plugins_Reloaded(object sender, PluginErrorEventArgs e){
|
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){
|
private void plugins_PluginChangedState(object sender, PluginChangedStateEventArgs e){
|
||||||
browser.ExecuteScriptAsync("window.TDPF_setPluginState", e.Plugin, e.IsEnabled);
|
browser.ExecuteScriptAsync("window.TDPF_setPluginState", e.Plugin, e.IsEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updates_UpdateAccepted(object sender, UpdateAcceptedEventArgs e){
|
private void updates_UpdateAccepted(object sender, UpdateEventArgs e){
|
||||||
foreach(Form form in Application.OpenForms.Cast<Form>().Reverse()){
|
this.InvokeAsyncSafe(() => {
|
||||||
if (form is FormSettings || form is FormPlugins || form is FormAbout){
|
FormManager.CloseAllDialogs();
|
||||||
form.Close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updates.BeginUpdateDownload(this, e.UpdateInfo, update => {
|
updates.BeginUpdateDownload(this, e.UpdateInfo, update => {
|
||||||
if (update.DownloadStatus == UpdateDownloadStatus.Done){
|
if (update.DownloadStatus == UpdateDownloadStatus.Done){
|
||||||
UpdateInstallerPath = update.InstallerPath;
|
UpdateInstallerPath = update.InstallerPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
ForceClose();
|
ForceClose();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updates_UpdateDismissed(object sender, UpdateDismissedEventArgs e){
|
private void updates_UpdateDismissed(object sender, UpdateEventArgs e){
|
||||||
Config.DismissedUpdate = e.VersionTag;
|
this.InvokeAsyncSafe(() => {
|
||||||
Config.Save();
|
Config.DismissedUpdate = e.UpdateInfo.VersionTag;
|
||||||
|
Config.Save();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void soundNotification_PlaybackError(object sender, PlaybackErrorEventArgs e){
|
private void soundNotification_PlaybackError(object sender, PlaybackErrorEventArgs e){
|
||||||
e.Ignore = true;
|
e.Ignore = true;
|
||||||
|
|
||||||
using(FormMessage form = new FormMessage("Notification Sound Error", "Could not play custom notification sound."+Environment.NewLine+e.Message, MessageBoxIcon.Error)){
|
using(FormMessage form = new FormMessage("Notification Sound Error", "Could not play custom notification sound.\n"+e.Message, MessageBoxIcon.Error)){
|
||||||
form.CancelButton = form.AddButton("Ignore");
|
form.AddButton(FormMessage.Ignore, ControlType.Cancel | ControlType.Focused);
|
||||||
|
|
||||||
Button btnOpenSettings = form.AddButton("View Options");
|
Button btnOpenSettings = form.AddButton("View Options");
|
||||||
btnOpenSettings.Width += 16;
|
btnOpenSettings.Width += 16;
|
||||||
@@ -324,18 +374,33 @@ namespace TweetDuck.Core{
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override void WndProc(ref Message m){
|
protected override void WndProc(ref Message m){
|
||||||
if (isLoaded && m.Msg == Program.WindowRestoreMessage){
|
if (isLoaded){
|
||||||
using(Process process = Process.GetCurrentProcess()){
|
if (m.Msg == Program.WindowRestoreMessage){
|
||||||
if (process.Id == m.WParam.ToInt32()){
|
if (WindowsUtils.CurrentProcessID == m.WParam.ToInt32()){
|
||||||
trayIcon_ClickRestore(trayIcon, new EventArgs());
|
trayIcon_ClickRestore(trayIcon, new EventArgs());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
else if (m.Msg == Program.SubProcessMessage){
|
||||||
|
int processId = m.WParam.ToInt32();
|
||||||
|
|
||||||
|
if (WindowsUtils.IsChildProcess(processId)){ // child process is checked in two places for safety
|
||||||
|
BrowserProcesses.Link(m.LParam.ToInt32(), processId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isBrowserReady && m.Msg == NativeMethods.WM_PARENTNOTIFY && (m.WParam.ToInt32() & 0xFFFF) == NativeMethods.WM_XBUTTONDOWN){
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,12 +427,12 @@ namespace TweetDuck.Core{
|
|||||||
browser.ExecuteScriptAsync("TDGF_reinjectCustomCSS", css?.Replace(Environment.NewLine, " ") ?? string.Empty);
|
browser.ExecuteScriptAsync("TDGF_reinjectCustomCSS", css?.Replace(Environment.NewLine, " ") ?? string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateProperties(PropertyBridge.Properties properties){
|
public void UpdateProperties(PropertyBridge.Environment environment){
|
||||||
browser.ExecuteScriptAsync(PropertyBridge.GenerateScript(properties));
|
browser.ExecuteScriptAsync(PropertyBridge.GenerateScript(environment));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ReloadToTweetDeck(){
|
public void ReloadToTweetDeck(){
|
||||||
browser.ExecuteScriptAsync("window.location.href = 'https://tweetdeck.twitter.com'");
|
browser.ExecuteScriptAsync($"if(window.TDGF_reload)window.TDGF_reload();else window.location.href='{TwitterUtils.TweetDeckURL}'");
|
||||||
}
|
}
|
||||||
|
|
||||||
// callback handlers
|
// callback handlers
|
||||||
@@ -381,22 +446,39 @@ namespace TweetDuck.Core{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void OpenSettings(Type startTab){
|
public void OpenSettings(Type startTab){
|
||||||
if (!TryBringToFront<FormSettings>()){
|
if (!FormManager.TryBringToFront<FormSettings>()){
|
||||||
bool prevEnableUpdateCheck = Config.EnableUpdateCheck;
|
bool prevEnableUpdateCheck = Config.EnableUpdateCheck;
|
||||||
|
|
||||||
FormSettings form = new FormSettings(this, plugins, updates, startTab);
|
FormSettings form = new FormSettings(this, plugins, updates, startTab);
|
||||||
|
|
||||||
form.FormClosed += (sender, args) => {
|
form.FormClosed += (sender, args) => {
|
||||||
if (!prevEnableUpdateCheck && Config.EnableUpdateCheck){
|
if (!prevEnableUpdateCheck && Config.EnableUpdateCheck){
|
||||||
updates.DismissUpdate(string.Empty);
|
Config.DismissedUpdate = null;
|
||||||
updates.Check(false);
|
Config.Save();
|
||||||
|
|
||||||
|
updates.Check(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Config.EnableTrayHighlight){
|
if (!Config.EnableTrayHighlight){
|
||||||
trayIcon.HasNotifications = false;
|
trayIcon.HasNotifications = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateProperties(PropertyBridge.Properties.ExpandLinksOnHover | PropertyBridge.Properties.SwitchAccountSelectors | PropertyBridge.Properties.HasCustomNotificationSound);
|
if (Program.SystemConfig.EnableBrowserGCReload){
|
||||||
|
memoryUsageTracker.Start(this, browser.GetBrowser(), Program.SystemConfig.BrowserMemoryThreshold);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
memoryUsageTracker.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (form.ShouldReloadBrowser){
|
||||||
|
FormManager.TryFind<FormPlugins>()?.Close();
|
||||||
|
plugins.Reload(); // also reloads the browser
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
UpdateProperties(PropertyBridge.Environment.Browser);
|
||||||
|
}
|
||||||
|
|
||||||
|
notification.RequiresResize = true;
|
||||||
form.Dispose();
|
form.Dispose();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -405,13 +487,13 @@ namespace TweetDuck.Core{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void OpenAbout(){
|
public void OpenAbout(){
|
||||||
if (!TryBringToFront<FormAbout>()){
|
if (!FormManager.TryBringToFront<FormAbout>()){
|
||||||
ShowChildForm(new FormAbout());
|
ShowChildForm(new FormAbout());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OpenPlugins(){
|
public void OpenPlugins(){
|
||||||
if (!TryBringToFront<FormPlugins>()){
|
if (!FormManager.TryBringToFront<FormPlugins>()){
|
||||||
ShowChildForm(new FormPlugins(plugins));
|
ShowChildForm(new FormPlugins(plugins));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -432,12 +514,58 @@ namespace TweetDuck.Core{
|
|||||||
soundNotification.PlaybackError += soundNotification_PlaybackError;
|
soundNotification.PlaybackError += soundNotification_PlaybackError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
soundNotification.SetVolume(Config.NotificationSoundVolume);
|
||||||
soundNotification.Play(Config.NotificationSoundPath);
|
soundNotification.Play(Config.NotificationSoundPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void PlayVideo(string url){
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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){
|
public void OnTweetScreenshotReady(string html, int width, int height){
|
||||||
if (notificationScreenshotManager == null){
|
if (notificationScreenshotManager == null){
|
||||||
notificationScreenshotManager = new TweetScreenshotManager(this);
|
notificationScreenshotManager = new TweetScreenshotManager(this, plugins);
|
||||||
}
|
}
|
||||||
|
|
||||||
notificationScreenshotManager.Trigger(html, width, height);
|
notificationScreenshotManager.Trigger(html, width, height);
|
||||||
|
|||||||
@@ -117,11 +117,13 @@
|
|||||||
<resheader name="writer">
|
<resheader name="writer">
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
<metadata name="trayIcon.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
<data name="trayIcon.TrayLocation" type="System.Drawing.Point, System.Drawing">
|
|
||||||
<value>17, 17</value>
|
<value>17, 17</value>
|
||||||
</data>
|
</metadata>
|
||||||
<data name="toolTip.TrayLocation" type="System.Drawing.Point, System.Drawing">
|
<metadata name="toolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
<value>112, 17</value>
|
<value>112, 17</value>
|
||||||
</data>
|
</metadata>
|
||||||
|
<metadata name="timerResize.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
|
<value>202, 17</value>
|
||||||
|
</metadata>
|
||||||
</root>
|
</root>
|
||||||
29
Core/FormManager.cs
Normal file
29
Core/FormManager.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using TweetDuck.Core.Other;
|
||||||
|
|
||||||
|
namespace TweetDuck.Core{
|
||||||
|
static class FormManager{
|
||||||
|
public static T TryFind<T>() where T : Form{
|
||||||
|
return Application.OpenForms.OfType<T>().FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TryBringToFront<T>() where T : Form{
|
||||||
|
T form = TryFind<T>();
|
||||||
|
|
||||||
|
if (form != null){
|
||||||
|
form.BringToFront();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CloseAllDialogs(){
|
||||||
|
foreach(Form form in Application.OpenForms.Cast<Form>().Reverse()){
|
||||||
|
if (form is FormSettings || form is FormPlugins || form is FormAbout){
|
||||||
|
form.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,100 +1,138 @@
|
|||||||
using CefSharp;
|
using System;
|
||||||
using System;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using CefSharp;
|
||||||
using TweetDuck.Core.Bridge;
|
using TweetDuck.Core.Bridge;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Handling{
|
namespace TweetDuck.Core.Handling{
|
||||||
abstract class ContextMenuBase : IContextMenuHandler{
|
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"));
|
protected static readonly bool HasDevTools = File.Exists(Path.Combine(Program.ProgramPath, "devtools_resources.pak"));
|
||||||
|
|
||||||
private const int MenuOpenLinkUrl = 26500;
|
private static TwitterUtils.ImageQuality ImageQuality => Program.UserConfig.TwitterImageQuality;
|
||||||
private const int MenuCopyLinkUrl = 26501;
|
|
||||||
private const int MenuCopyUsername = 26502;
|
private static KeyValuePair<string, string> ContextInfo;
|
||||||
private const int MenuOpenImage = 26503;
|
private static bool IsLink => ContextInfo.Key == "link";
|
||||||
private const int MenuSaveImage = 26504;
|
private static bool IsImage => ContextInfo.Key == "image";
|
||||||
private const int MenuCopyImageUrl = 26505;
|
private static bool IsVideo => ContextInfo.Key == "video";
|
||||||
private const int MenuOpenDevTools = 26599;
|
|
||||||
|
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 MenuOpenMediaUrl = (CefMenuCommand)26503;
|
||||||
|
private const CefMenuCommand MenuCopyMediaUrl = (CefMenuCommand)26504;
|
||||||
|
private const CefMenuCommand MenuSaveMedia = (CefMenuCommand)26505;
|
||||||
|
private const CefMenuCommand MenuSaveTweetImages = (CefMenuCommand)26506;
|
||||||
|
private const CefMenuCommand MenuOpenDevTools = (CefMenuCommand)26599;
|
||||||
|
|
||||||
private readonly Form form;
|
private readonly Form form;
|
||||||
|
|
||||||
|
private string lastHighlightedTweetAuthor;
|
||||||
|
private string[] lastHighlightedTweetImageList;
|
||||||
|
|
||||||
protected ContextMenuBase(Form form){
|
protected ContextMenuBase(Form form){
|
||||||
this.form = form;
|
this.form = form;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
|
public virtual void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
|
||||||
if (parameters.TypeFlags.HasFlag(ContextMenuType.Link) && !parameters.UnfilteredLinkUrl.EndsWith("tweetdeck.twitter.com/#", StringComparison.Ordinal)){
|
if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){
|
||||||
if (RegexTwitterAccount.Value.IsMatch(parameters.UnfilteredLinkUrl)){
|
lastHighlightedTweetAuthor = string.Empty;
|
||||||
model.AddItem((CefMenuCommand)MenuOpenLinkUrl, "Open account in browser");
|
lastHighlightedTweetImageList = StringUtils.EmptyArray;
|
||||||
model.AddItem((CefMenuCommand)MenuCopyLinkUrl, "Copy account address");
|
ContextInfo = default(KeyValuePair<string, string>);
|
||||||
model.AddItem((CefMenuCommand)MenuCopyUsername, "Copy account username");
|
}
|
||||||
|
else{
|
||||||
|
lastHighlightedTweetAuthor = TweetDeckBridge.LastHighlightedTweetAuthor;
|
||||||
|
lastHighlightedTweetImageList = TweetDeckBridge.LastHighlightedTweetImages;
|
||||||
|
}
|
||||||
|
|
||||||
|
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{
|
else{
|
||||||
model.AddItem((CefMenuCommand)MenuOpenLinkUrl, "Open link in browser");
|
model.AddItem(MenuOpenLinkUrl, TextOpen("link"));
|
||||||
model.AddItem((CefMenuCommand)MenuCopyLinkUrl, "Copy link address");
|
model.AddItem(MenuCopyLinkUrl, TextCopy("link"));
|
||||||
}
|
}
|
||||||
|
|
||||||
model.AddSeparator();
|
model.AddSeparator();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parameters.TypeFlags.HasFlag(ContextMenuType.Media) && parameters.HasImageContents){
|
if (hasTweetVideo){
|
||||||
model.AddItem((CefMenuCommand)MenuOpenImage, "Open image in browser");
|
model.AddItem(MenuOpenMediaUrl, TextOpen("video"));
|
||||||
model.AddItem((CefMenuCommand)MenuSaveImage, "Save image as...");
|
model.AddItem(MenuCopyMediaUrl, TextCopy("video"));
|
||||||
model.AddItem((CefMenuCommand)MenuCopyImageUrl, "Copy image address");
|
model.AddItem(MenuSaveMedia, TextSave("video"));
|
||||||
|
model.AddSeparator();
|
||||||
|
}
|
||||||
|
else if ((parameters.TypeFlags.HasFlag(ContextMenuType.Media) && parameters.HasImageContents) || hasTweetImage){
|
||||||
|
model.AddItem(MenuOpenMediaUrl, TextOpen("image"));
|
||||||
|
model.AddItem(MenuCopyMediaUrl, TextCopy("image"));
|
||||||
|
model.AddItem(MenuSaveMedia, TextSave("image"));
|
||||||
|
|
||||||
|
if (lastHighlightedTweetImageList.Length > 1){
|
||||||
|
model.AddItem(MenuSaveTweetImages, TextSave("all images"));
|
||||||
|
}
|
||||||
|
|
||||||
model.AddSeparator();
|
model.AddSeparator();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){
|
public virtual bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){
|
||||||
switch((int)commandId){
|
switch(commandId){
|
||||||
case MenuOpenLinkUrl:
|
case MenuOpenLinkUrl:
|
||||||
BrowserUtils.OpenExternalBrowser(parameters.LinkUrl);
|
BrowserUtils.OpenExternalBrowser(parameters.LinkUrl);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MenuCopyLinkUrl:
|
case MenuCopyLinkUrl:
|
||||||
SetClipboardText(string.IsNullOrEmpty(TweetDeckBridge.LastRightClickedLink) ? parameters.UnfilteredLinkUrl : TweetDeckBridge.LastRightClickedLink);
|
SetClipboardText(IsLink ? ContextInfo.Value : parameters.UnfilteredLinkUrl);
|
||||||
break;
|
|
||||||
|
|
||||||
case MenuOpenImage:
|
|
||||||
BrowserUtils.OpenExternalBrowser(parameters.SourceUrl);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MenuSaveImage:
|
|
||||||
string fileName = GetImageFileName(parameters.SourceUrl);
|
|
||||||
string extension = Path.GetExtension(fileName);
|
|
||||||
string saveTarget;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (saveTarget != null){
|
|
||||||
BrowserUtils.DownloadFileAsync(parameters.SourceUrl, saveTarget, null, ex => {
|
|
||||||
MessageBox.Show("An error occurred while downloading the image: "+ex.Message, Program.BrandName+" Has Failed :(", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MenuCopyImageUrl:
|
|
||||||
SetClipboardText(parameters.SourceUrl);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MenuCopyUsername:
|
case MenuCopyUsername:
|
||||||
Match match = RegexTwitterAccount.Value.Match(parameters.UnfilteredLinkUrl);
|
Match match = TwitterUtils.RegexAccount.Match(parameters.UnfilteredLinkUrl);
|
||||||
SetClipboardText(match.Success ? match.Groups[1].Value : parameters.UnfilteredLinkUrl);
|
SetClipboardText(match.Success ? match.Groups[1].Value : parameters.UnfilteredLinkUrl);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case MenuOpenMediaUrl:
|
||||||
|
BrowserUtils.OpenExternalBrowser(TwitterUtils.GetMediaLink(GetMediaLink(parameters), ImageQuality));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MenuCopyMediaUrl:
|
||||||
|
SetClipboardText(TwitterUtils.GetMediaLink(GetMediaLink(parameters), ImageQuality));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MenuSaveMedia:
|
||||||
|
if (IsVideo){
|
||||||
|
TwitterUtils.DownloadVideo(GetMediaLink(parameters));
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
TwitterUtils.DownloadImage(GetMediaLink(parameters), lastHighlightedTweetAuthor, ImageQuality);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MenuSaveTweetImages:
|
||||||
|
TwitterUtils.DownloadImages(lastHighlightedTweetImageList, lastHighlightedTweetAuthor, ImageQuality);
|
||||||
|
break;
|
||||||
|
|
||||||
case MenuOpenDevTools:
|
case MenuOpenDevTools:
|
||||||
browserControl.ShowDevTools();
|
browserControl.ShowDevTools();
|
||||||
break;
|
break;
|
||||||
@@ -103,7 +141,9 @@ namespace TweetDuck.Core.Handling{
|
|||||||
return false;
|
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){
|
public virtual bool RunContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback){
|
||||||
return false;
|
return false;
|
||||||
@@ -113,8 +153,8 @@ namespace TweetDuck.Core.Handling{
|
|||||||
form.InvokeAsyncSafe(() => WindowsUtils.SetClipboard(text, TextDataFormat.UnicodeText));
|
form.InvokeAsyncSafe(() => WindowsUtils.SetClipboard(text, TextDataFormat.UnicodeText));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void AddDebugMenuItems(IMenuModel model){
|
protected static void AddDebugMenuItems(IMenuModel model){
|
||||||
model.AddItem((CefMenuCommand)MenuOpenDevTools, "Open dev tools");
|
model.AddItem(MenuOpenDevTools, "Open dev tools");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static void RemoveSeparatorIfLast(IMenuModel model){
|
protected static void RemoveSeparatorIfLast(IMenuModel model){
|
||||||
@@ -128,21 +168,5 @@ namespace TweetDuck.Core.Handling{
|
|||||||
model.AddSeparator();
|
model.AddSeparator();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetImageFileName(string url){
|
|
||||||
// twimg adds a colon after file extension
|
|
||||||
int dot = url.LastIndexOf('.');
|
|
||||||
|
|
||||||
if (dot != -1){
|
|
||||||
int colon = url.IndexOf(':', dot);
|
|
||||||
|
|
||||||
if (colon != -1){
|
|
||||||
url = url.Substring(0, colon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// return file name
|
|
||||||
return BrowserUtils.GetFileNameFromUrl(url) ?? "unknown";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,18 +5,18 @@ using TweetDuck.Core.Controls;
|
|||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Handling{
|
namespace TweetDuck.Core.Handling{
|
||||||
class ContextMenuBrowser : ContextMenuBase{
|
sealed class ContextMenuBrowser : ContextMenuBase{
|
||||||
private const int MenuGlobal = 26600;
|
private const CefMenuCommand MenuGlobal = (CefMenuCommand)26600;
|
||||||
private const int MenuMute = 26601;
|
private const CefMenuCommand MenuMute = (CefMenuCommand)26601;
|
||||||
private const int MenuSettings = 26602;
|
private const CefMenuCommand MenuSettings = (CefMenuCommand)26602;
|
||||||
private const int MenuPlugins = 26003;
|
private const CefMenuCommand MenuPlugins = (CefMenuCommand)26003;
|
||||||
private const int MenuAbout = 26604;
|
private const CefMenuCommand MenuAbout = (CefMenuCommand)26604;
|
||||||
|
|
||||||
private const int MenuOpenTweetUrl = 26610;
|
private const CefMenuCommand MenuOpenTweetUrl = (CefMenuCommand)26610;
|
||||||
private const int MenuCopyTweetUrl = 26611;
|
private const CefMenuCommand MenuCopyTweetUrl = (CefMenuCommand)26611;
|
||||||
private const int MenuOpenQuotedTweetUrl = 26612;
|
private const CefMenuCommand MenuOpenQuotedTweetUrl = (CefMenuCommand)26612;
|
||||||
private const int MenuCopyQuotedTweetUrl = 26613;
|
private const CefMenuCommand MenuCopyQuotedTweetUrl = (CefMenuCommand)26613;
|
||||||
private const int MenuScreenshotTweet = 26614;
|
private const CefMenuCommand MenuScreenshotTweet = (CefMenuCommand)26614;
|
||||||
|
|
||||||
private const string TitleReloadBrowser = "Reload browser";
|
private const string TitleReloadBrowser = "Reload browser";
|
||||||
private const string TitleMuteNotifications = "Mute notifications";
|
private const string TitleMuteNotifications = "Mute notifications";
|
||||||
@@ -49,20 +49,20 @@ namespace TweetDuck.Core.Handling{
|
|||||||
lastHighlightedTweet = TweetDeckBridge.LastHighlightedTweet;
|
lastHighlightedTweet = TweetDeckBridge.LastHighlightedTweet;
|
||||||
lastHighlightedQuotedTweet = TweetDeckBridge.LastHighlightedQuotedTweet;
|
lastHighlightedQuotedTweet = TweetDeckBridge.LastHighlightedQuotedTweet;
|
||||||
|
|
||||||
if (!BrowserUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){
|
if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){
|
||||||
lastHighlightedTweet = string.Empty;
|
lastHighlightedTweet = string.Empty;
|
||||||
lastHighlightedQuotedTweet = string.Empty;
|
lastHighlightedQuotedTweet = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(lastHighlightedTweet) && (parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){
|
if (!string.IsNullOrEmpty(lastHighlightedTweet) && (parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){
|
||||||
model.AddItem((CefMenuCommand)MenuOpenTweetUrl, "Open tweet in browser");
|
model.AddItem(MenuOpenTweetUrl, "Open tweet in browser");
|
||||||
model.AddItem((CefMenuCommand)MenuCopyTweetUrl, "Copy tweet address");
|
model.AddItem(MenuCopyTweetUrl, "Copy tweet address");
|
||||||
model.AddItem((CefMenuCommand)MenuScreenshotTweet, "Screenshot tweet to clipboard");
|
model.AddItem(MenuScreenshotTweet, "Screenshot tweet to clipboard");
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(lastHighlightedQuotedTweet)){
|
if (!string.IsNullOrEmpty(lastHighlightedQuotedTweet)){
|
||||||
model.AddSeparator();
|
model.AddSeparator();
|
||||||
model.AddItem((CefMenuCommand)MenuOpenQuotedTweetUrl, "Open quoted tweet in browser");
|
model.AddItem(MenuOpenQuotedTweetUrl, "Open quoted tweet in browser");
|
||||||
model.AddItem((CefMenuCommand)MenuCopyQuotedTweetUrl, "Copy quoted tweet address");
|
model.AddItem(MenuCopyQuotedTweetUrl, "Copy quoted tweet address");
|
||||||
}
|
}
|
||||||
|
|
||||||
model.AddSeparator();
|
model.AddSeparator();
|
||||||
@@ -71,16 +71,16 @@ namespace TweetDuck.Core.Handling{
|
|||||||
if ((parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){
|
if ((parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){
|
||||||
AddSeparator(model);
|
AddSeparator(model);
|
||||||
|
|
||||||
IMenuModel globalMenu = model.Count == 0 ? model : model.AddSubMenu((CefMenuCommand)MenuGlobal, Program.BrandName);
|
IMenuModel globalMenu = model.Count == 0 ? model : model.AddSubMenu(MenuGlobal, Program.BrandName);
|
||||||
|
|
||||||
globalMenu.AddItem(CefMenuCommand.Reload, TitleReloadBrowser);
|
globalMenu.AddItem(CefMenuCommand.Reload, TitleReloadBrowser);
|
||||||
globalMenu.AddCheckItem((CefMenuCommand)MenuMute, TitleMuteNotifications);
|
globalMenu.AddCheckItem(MenuMute, TitleMuteNotifications);
|
||||||
globalMenu.SetChecked((CefMenuCommand)MenuMute, Program.UserConfig.MuteNotifications);
|
globalMenu.SetChecked(MenuMute, Program.UserConfig.MuteNotifications);
|
||||||
globalMenu.AddSeparator();
|
globalMenu.AddSeparator();
|
||||||
|
|
||||||
globalMenu.AddItem((CefMenuCommand)MenuSettings, TitleSettings);
|
globalMenu.AddItem(MenuSettings, TitleSettings);
|
||||||
globalMenu.AddItem((CefMenuCommand)MenuPlugins, TitlePlugins);
|
globalMenu.AddItem(MenuPlugins, TitlePlugins);
|
||||||
globalMenu.AddItem((CefMenuCommand)MenuAbout, TitleAboutProgram);
|
globalMenu.AddItem(MenuAbout, TitleAboutProgram);
|
||||||
|
|
||||||
if (HasDevTools){
|
if (HasDevTools){
|
||||||
globalMenu.AddSeparator();
|
globalMenu.AddSeparator();
|
||||||
@@ -96,8 +96,8 @@ namespace TweetDuck.Core.Handling{
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch((int)commandId){
|
switch(commandId){
|
||||||
case (int)CefMenuCommand.Reload:
|
case CefMenuCommand.Reload:
|
||||||
form.InvokeAsyncSafe(form.ReloadToTweetDeck);
|
form.InvokeAsyncSafe(form.ReloadToTweetDeck);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,12 @@ using TweetDuck.Core.Controls;
|
|||||||
using TweetDuck.Core.Notification;
|
using TweetDuck.Core.Notification;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Handling{
|
namespace TweetDuck.Core.Handling{
|
||||||
class ContextMenuNotification : ContextMenuBase{
|
sealed class ContextMenuNotification : ContextMenuBase{
|
||||||
private const int MenuSkipTweet = 26600;
|
private const CefMenuCommand MenuViewDetail = (CefMenuCommand)26600;
|
||||||
private const int MenuFreeze = 26601;
|
private const CefMenuCommand MenuSkipTweet = (CefMenuCommand)26601;
|
||||||
private const int MenuCopyTweetUrl = 26602;
|
private const CefMenuCommand MenuFreeze = (CefMenuCommand)26602;
|
||||||
private const int MenuCopyQuotedTweetUrl = 26603;
|
private const CefMenuCommand MenuCopyTweetUrl = (CefMenuCommand)26603;
|
||||||
|
private const CefMenuCommand MenuCopyQuotedTweetUrl = (CefMenuCommand)26604;
|
||||||
|
|
||||||
private readonly FormNotificationBase form;
|
private readonly FormNotificationBase form;
|
||||||
private readonly bool enableCustomMenu;
|
private readonly bool enableCustomMenu;
|
||||||
@@ -28,23 +29,26 @@ namespace TweetDuck.Core.Handling{
|
|||||||
base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);
|
base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);
|
||||||
|
|
||||||
if (enableCustomMenu){
|
if (enableCustomMenu){
|
||||||
model.AddItem((CefMenuCommand)MenuSkipTweet, "Skip tweet");
|
if (!string.IsNullOrEmpty(form.CurrentChirpId)){
|
||||||
model.AddCheckItem((CefMenuCommand)MenuFreeze, "Freeze");
|
model.AddItem(MenuViewDetail, "View detail");
|
||||||
model.SetChecked((CefMenuCommand)MenuFreeze, form.FreezeTimer);
|
}
|
||||||
model.AddSeparator();
|
|
||||||
|
model.AddItem(MenuSkipTweet, "Skip tweet");
|
||||||
|
model.AddCheckItem(MenuFreeze, "Freeze");
|
||||||
|
model.SetChecked(MenuFreeze, form.FreezeTimer);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(form.CurrentTweetUrl)){
|
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)){
|
if (!string.IsNullOrEmpty(form.CurrentQuoteUrl)){
|
||||||
model.AddItem((CefMenuCommand)MenuCopyQuotedTweetUrl, "Copy quoted tweet address");
|
model.AddItem(MenuCopyQuotedTweetUrl, "Copy quoted tweet address");
|
||||||
}
|
}
|
||||||
|
|
||||||
model.AddSeparator();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HasDevTools){
|
if (HasDevTools){
|
||||||
|
model.AddSeparator();
|
||||||
AddDebugMenuItems(model);
|
AddDebugMenuItems(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +62,7 @@ namespace TweetDuck.Core.Handling{
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch((int)commandId){
|
switch(commandId){
|
||||||
case MenuSkipTweet:
|
case MenuSkipTweet:
|
||||||
form.InvokeAsyncSafe(form.FinishCurrentNotification);
|
form.InvokeAsyncSafe(form.FinishCurrentNotification);
|
||||||
return true;
|
return true;
|
||||||
@@ -67,6 +71,10 @@ namespace TweetDuck.Core.Handling{
|
|||||||
form.InvokeAsyncSafe(() => form.FreezeTimer = !form.FreezeTimer);
|
form.InvokeAsyncSafe(() => form.FreezeTimer = !form.FreezeTimer);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case MenuViewDetail:
|
||||||
|
form.InvokeSafe(form.ShowTweetDetail);
|
||||||
|
return true;
|
||||||
|
|
||||||
case MenuCopyTweetUrl:
|
case MenuCopyTweetUrl:
|
||||||
SetClipboardText(form.CurrentTweetUrl);
|
SetClipboardText(form.CurrentTweetUrl);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
using CefSharp;
|
using System;
|
||||||
using System;
|
using CefSharp;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Handling{
|
namespace TweetDuck.Core.Handling.General{
|
||||||
class BrowserProcessHandler : IBrowserProcessHandler{
|
sealed class BrowserProcessHandler : IBrowserProcessHandler{
|
||||||
void IBrowserProcessHandler.OnContextInitialized(){
|
void IBrowserProcessHandler.OnContextInitialized(){
|
||||||
using(IRequestContext ctx = Cef.GetGlobalRequestContext()){
|
using(IRequestContext ctx = Cef.GetGlobalRequestContext()){
|
||||||
ctx.SetPreference("browser.enable_spellchecking", Program.UserConfig.EnableSpellCheck, out string _);
|
ctx.SetPreference("browser.enable_spellchecking", Program.UserConfig.EnableSpellCheck, out string _);
|
||||||
90
Core/Handling/General/JavaScriptDialogHandler.cs
Normal file
90
Core/Handling/General/JavaScriptDialogHandler.cs
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = CreateMessageForm("Browser Message", messageText);
|
||||||
|
form.AddButton(FormMessage.OK, ControlType.Accept | ControlType.Focused);
|
||||||
|
}
|
||||||
|
else if (dialogType == CefJsDialogType.Confirm){
|
||||||
|
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 = 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(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);
|
||||||
|
form.ActiveControl = input;
|
||||||
|
form.Height += input.Size.Height+input.Margin.Vertical;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
callback.Continue(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool success = form.ShowDialog() == DialogResult.OK;
|
||||||
|
|
||||||
|
if (input == null){
|
||||||
|
callback.Continue(success);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
callback.Continue(success, input.Text);
|
||||||
|
input.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
form.Dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IJsDialogHandler.OnJSBeforeUnload(IWebBrowser browserControl, IBrowser browser, string message, bool isReload, IJsDialogCallback callback){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IJsDialogHandler.OnResetDialogState(IWebBrowser browserControl, IBrowser browser){}
|
||||||
|
void IJsDialogHandler.OnDialogClosed(IWebBrowser browserControl, IBrowser browser){}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
using CefSharp;
|
using CefSharp;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Handling{
|
namespace TweetDuck.Core.Handling.General{
|
||||||
class LifeSpanHandler : ILifeSpanHandler{
|
sealed class LifeSpanHandler : ILifeSpanHandler{
|
||||||
public bool OnBeforePopup(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser){
|
public bool OnBeforePopup(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser){
|
||||||
newBrowser = null;
|
newBrowser = null;
|
||||||
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using CefSharp;
|
using CefSharp;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Handling{
|
namespace TweetDuck.Core.Handling.General{
|
||||||
abstract class RequestHandler : IRequestHandler{
|
abstract class RequestHandlerBase : IRequestHandler{
|
||||||
// Browser
|
// Browser
|
||||||
|
|
||||||
public virtual void OnRenderViewReady(IWebBrowser browserControl, IBrowser browser){}
|
public virtual void OnRenderViewReady(IWebBrowser browserControl, IBrowser browser){}
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
using CefSharp;
|
|
||||||
using CefSharp.WinForms;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.Windows.Forms;
|
|
||||||
using TweetDuck.Core.Controls;
|
|
||||||
using TweetDuck.Core.Other;
|
|
||||||
|
|
||||||
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 = new FormMessage(Program.BrandName, messageText, MessageBoxIcon.None);
|
|
||||||
TextBox input = null;
|
|
||||||
|
|
||||||
if (dialogType == CefJsDialogType.Alert){
|
|
||||||
form.AcceptButton = form.AddButton("OK");
|
|
||||||
}
|
|
||||||
else if (dialogType == CefJsDialogType.Confirm){
|
|
||||||
form.CancelButton = form.AddButton("No", DialogResult.No);
|
|
||||||
form.AcceptButton = form.AddButton("Yes");
|
|
||||||
}
|
|
||||||
else if (dialogType == CefJsDialogType.Prompt){
|
|
||||||
form.CancelButton = form.AddButton("Cancel", DialogResult.Cancel);
|
|
||||||
form.AcceptButton = form.AddButton("OK");
|
|
||||||
|
|
||||||
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)
|
|
||||||
};
|
|
||||||
|
|
||||||
form.Controls.Add(input);
|
|
||||||
form.ActiveControl = input;
|
|
||||||
form.Height += input.Size.Height+input.Margin.Vertical;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool success = form.ShowDialog() == DialogResult.OK;
|
|
||||||
|
|
||||||
if (input == null){
|
|
||||||
callback.Continue(success);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
callback.Continue(success, input.Text);
|
|
||||||
input.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
form.Dispose();
|
|
||||||
});
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IJsDialogHandler.OnJSBeforeUnload(IWebBrowser browserControl, IBrowser browser, string message, bool isReload, IJsDialogCallback callback){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void IJsDialogHandler.OnResetDialogState(IWebBrowser browserControl, IBrowser browser){}
|
|
||||||
void IJsDialogHandler.OnDialogClosed(IWebBrowser browserControl, IBrowser browser){}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
20
Core/Handling/KeyboardHandlerBrowser.cs
Normal file
20
Core/Handling/KeyboardHandlerBrowser.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Windows.Forms;
|
||||||
|
using CefSharp;
|
||||||
|
|
||||||
|
namespace TweetDuck.Core.Handling{
|
||||||
|
sealed class KeyboardHandlerBrowser : IKeyboardHandler{
|
||||||
|
private readonly FormBrowser form;
|
||||||
|
|
||||||
|
public KeyboardHandlerBrowser(FormBrowser form){
|
||||||
|
this.form = form;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IKeyboardHandler.OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut){
|
||||||
|
return type == KeyType.RawKeyDown && form.ProcessBrowserKey((Keys)windowsKeyCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IKeyboardHandler.OnKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
38
Core/Handling/KeyboardHandlerNotification.cs
Normal file
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 CefSharp;
|
||||||
|
using TweetDuck.Core.Handling.General;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Handling{
|
namespace TweetDuck.Core.Handling{
|
||||||
class RequestHandlerBrowser : RequestHandler{
|
sealed class RequestHandlerBrowser : RequestHandlerBase{
|
||||||
public override void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status){
|
public override void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status){
|
||||||
browser.Reload();
|
browser.Reload();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using System.IO;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Handling{
|
namespace TweetDuck.Core.Handling{
|
||||||
class ResourceHandlerNotification : IResourceHandler{
|
sealed class ResourceHandlerNotification : IResourceHandler{
|
||||||
private readonly NameValueCollection headers = new NameValueCollection(0);
|
private readonly NameValueCollection headers = new NameValueCollection(0);
|
||||||
private MemoryStream dataIn;
|
private MemoryStream dataIn;
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ using System.Windows.Forms;
|
|||||||
using TweetDuck.Configuration;
|
using TweetDuck.Configuration;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Handling;
|
using TweetDuck.Core.Handling;
|
||||||
|
using TweetDuck.Core.Handling.General;
|
||||||
|
using TweetDuck.Core.Other.Management;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Notification{
|
namespace TweetDuck.Core.Notification{
|
||||||
@@ -57,34 +59,40 @@ namespace TweetDuck.Core.Notification{
|
|||||||
|
|
||||||
set{
|
set{
|
||||||
Visible = (base.Location = value) != ControlExtensions.InvisibleLocation;
|
Visible = (base.Location = value) != ControlExtensions.InvisibleLocation;
|
||||||
|
FormBorderStyle = GetBorderStyle(CanResizeWindow);
|
||||||
if (WindowsUtils.ShouldAvoidToolWindow){
|
|
||||||
FormBorderStyle = Visible ? FormBorderStyle.FixedSingle : FormBorderStyle.FixedToolWindow; // workaround for alt+tab
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Func<bool> CanMoveWindow = () => true;
|
public bool CanResizeWindow{
|
||||||
|
get => FormBorderStyle == FormBorderStyle.Sizable || FormBorderStyle == FormBorderStyle.SizableToolWindow;
|
||||||
|
set => FormBorderStyle = GetBorderStyle(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Func<bool> CanMoveWindow { get; set; } = () => true;
|
||||||
protected override bool ShowWithoutActivation => true;
|
protected override bool ShowWithoutActivation => true;
|
||||||
|
|
||||||
protected readonly Form owner;
|
protected double SizeScale => dpiScale*Program.UserConfig.ZoomMultiplier;
|
||||||
|
|
||||||
|
protected readonly FormBrowser owner;
|
||||||
protected readonly ChromiumWebBrowser browser;
|
protected readonly ChromiumWebBrowser browser;
|
||||||
|
|
||||||
private readonly ResourceHandlerNotification resourceHandler = new ResourceHandlerNotification();
|
private readonly ResourceHandlerNotification resourceHandler = new ResourceHandlerNotification();
|
||||||
|
private readonly float dpiScale;
|
||||||
|
|
||||||
private string currentColumn;
|
private TweetNotification currentNotification;
|
||||||
private int pauseCounter;
|
private int pauseCounter;
|
||||||
|
|
||||||
|
public string CurrentChirpId => currentNotification?.ChirpId;
|
||||||
|
public string CurrentTweetUrl => currentNotification?.TweetUrl;
|
||||||
|
public string CurrentQuoteUrl => currentNotification?.QuoteUrl;
|
||||||
public bool IsPaused => pauseCounter > 0;
|
public bool IsPaused => pauseCounter > 0;
|
||||||
|
|
||||||
public bool FreezeTimer { get; set; }
|
public bool FreezeTimer { get; set; }
|
||||||
public bool ContextMenuOpen { get; set; }
|
public bool ContextMenuOpen { get; set; }
|
||||||
public string CurrentTweetUrl { get; private set; }
|
|
||||||
public string CurrentQuoteUrl { get; private set; }
|
|
||||||
|
|
||||||
public event EventHandler Initialized;
|
public event EventHandler Initialized;
|
||||||
|
|
||||||
public FormNotificationBase(Form owner, bool enableContextMenu){
|
protected FormNotificationBase(FormBrowser owner, bool enableContextMenu){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
@@ -92,6 +100,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
|
|
||||||
this.browser = new ChromiumWebBrowser("about:blank"){
|
this.browser = new ChromiumWebBrowser("about:blank"){
|
||||||
MenuHandler = new ContextMenuNotification(this, enableContextMenu),
|
MenuHandler = new ContextMenuNotification(this, enableContextMenu),
|
||||||
|
JsDialogHandler = new JavaScriptDialogHandler(),
|
||||||
LifeSpanHandler = new LifeSpanHandler()
|
LifeSpanHandler = new LifeSpanHandler()
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -103,8 +112,10 @@ namespace TweetDuck.Core.Notification{
|
|||||||
this.browser.ConsoleMessage += BrowserUtils.HandleConsoleMessage;
|
this.browser.ConsoleMessage += BrowserUtils.HandleConsoleMessage;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
this.dpiScale = this.GetDPIScale();
|
||||||
|
|
||||||
DefaultResourceHandlerFactory handlerFactory = (DefaultResourceHandlerFactory)browser.ResourceHandlerFactory;
|
DefaultResourceHandlerFactory handlerFactory = (DefaultResourceHandlerFactory)browser.ResourceHandlerFactory;
|
||||||
handlerFactory.RegisterHandler("https://tweetdeck.twitter.com", this.resourceHandler);
|
handlerFactory.RegisterHandler(TwitterUtils.TweetDeckURL, this.resourceHandler);
|
||||||
|
|
||||||
Controls.Add(browser);
|
Controls.Add(browser);
|
||||||
|
|
||||||
@@ -134,6 +145,9 @@ namespace TweetDuck.Core.Notification{
|
|||||||
private void Browser_IsBrowserInitializedChanged(object sender, IsBrowserInitializedChangedEventArgs e){
|
private void Browser_IsBrowserInitializedChanged(object sender, IsBrowserInitializedChangedEventArgs e){
|
||||||
if (e.IsBrowserInitialized){
|
if (e.IsBrowserInitialized){
|
||||||
Initialized?.Invoke(this, new EventArgs());
|
Initialized?.Invoke(this, new EventArgs());
|
||||||
|
|
||||||
|
int identifier = browser.GetBrowser().Identifier;
|
||||||
|
Disposed += (sender2, args2) => BrowserProcesses.Forget(identifier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,7 +159,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
}
|
}
|
||||||
|
|
||||||
Location = ControlExtensions.InvisibleLocation;
|
Location = ControlExtensions.InvisibleLocation;
|
||||||
currentColumn = null;
|
currentNotification = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void FinishCurrentNotification(){}
|
public virtual void FinishCurrentNotification(){}
|
||||||
@@ -168,24 +182,22 @@ namespace TweetDuck.Core.Notification{
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void LoadTweet(TweetNotification tweet){
|
protected virtual void LoadTweet(TweetNotification tweet){
|
||||||
CurrentTweetUrl = tweet.TweetUrl;
|
currentNotification = tweet;
|
||||||
CurrentQuoteUrl = tweet.QuoteUrl;
|
|
||||||
currentColumn = tweet.Column;
|
|
||||||
|
|
||||||
resourceHandler.SetHTML(GetTweetHTML(tweet));
|
resourceHandler.SetHTML(GetTweetHTML(tweet));
|
||||||
browser.Load("https://tweetdeck.twitter.com");
|
browser.Load(TwitterUtils.TweetDeckURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void SetNotificationSize(int width, int height){
|
protected virtual void SetNotificationSize(int width, int height){
|
||||||
browser.ClientSize = ClientSize = new Size((int)Math.Round(width*Program.UserConfig.ZoomMultiplier), (int)Math.Round(height*Program.UserConfig.ZoomMultiplier));
|
browser.ClientSize = ClientSize = new Size(BrowserUtils.Scale(width, SizeScale), BrowserUtils.Scale(height, SizeScale));
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnNotificationReady(){
|
|
||||||
MoveToVisibleLocation();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void UpdateTitle(){
|
protected virtual void UpdateTitle(){
|
||||||
Text = string.IsNullOrEmpty(currentColumn) || !Program.UserConfig.DisplayNotificationColumn ? Program.BrandName : Program.BrandName+" - "+currentColumn;
|
string title = currentNotification?.ColumnTitle;
|
||||||
|
Text = string.IsNullOrEmpty(title) || !Program.UserConfig.DisplayNotificationColumn ? Program.BrandName : Program.BrandName+" - "+title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowTweetDetail(){
|
||||||
|
owner.ShowTweetDetail(currentNotification.ColumnId, currentNotification.ChirpId, currentNotification.TweetUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void MoveToVisibleLocation(){
|
public void MoveToVisibleLocation(){
|
||||||
@@ -207,5 +219,14 @@ namespace TweetDuck.Core.Notification{
|
|||||||
toolTip.Show(text, this, position);
|
toolTip.Show(text, this, position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private FormBorderStyle GetBorderStyle(bool sizable){
|
||||||
|
if (WindowsUtils.ShouldAvoidToolWindow && Visible){ // Visible = workaround for alt+tab
|
||||||
|
return sizable ? FormBorderStyle.Sizable : FormBorderStyle.FixedSingle;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return sizable ? FormBorderStyle.SizableToolWindow : FormBorderStyle.FixedToolWindow;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@
|
|||||||
this.progressBarTimer.Margin = new System.Windows.Forms.Padding(0);
|
this.progressBarTimer.Margin = new System.Windows.Forms.Padding(0);
|
||||||
this.progressBarTimer.Maximum = 1000;
|
this.progressBarTimer.Maximum = 1000;
|
||||||
this.progressBarTimer.Name = "progressBarTimer";
|
this.progressBarTimer.Name = "progressBarTimer";
|
||||||
this.progressBarTimer.Size = new System.Drawing.Size(284, 4);
|
this.progressBarTimer.Size = new System.Drawing.Size(284, TimerBarHeight);
|
||||||
this.progressBarTimer.TabIndex = 1;
|
this.progressBarTimer.TabIndex = 1;
|
||||||
//
|
//
|
||||||
// FormNotification
|
// FormNotification
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ using System.Drawing;
|
|||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Core.Bridge;
|
using TweetDuck.Core.Bridge;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
|
using TweetDuck.Core.Handling;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
using TweetDuck.Data;
|
||||||
using TweetDuck.Plugins;
|
using TweetDuck.Plugins;
|
||||||
using TweetDuck.Plugins.Enums;
|
using TweetDuck.Plugins.Enums;
|
||||||
using TweetDuck.Resources;
|
using TweetDuck.Resources;
|
||||||
@@ -12,32 +14,10 @@ using TweetDuck.Resources;
|
|||||||
namespace TweetDuck.Core.Notification{
|
namespace TweetDuck.Core.Notification{
|
||||||
partial class FormNotificationMain : FormNotificationBase{
|
partial class FormNotificationMain : FormNotificationBase{
|
||||||
private const string NotificationScriptFile = "notification.js";
|
private const string NotificationScriptFile = "notification.js";
|
||||||
|
private const int TimerBarHeight = 4;
|
||||||
|
|
||||||
private static readonly string NotificationScriptIdentifier = ScriptLoader.GetRootIdentifier(NotificationScriptFile);
|
private static readonly string NotificationScriptIdentifier = ScriptLoader.GetRootIdentifier(NotificationScriptFile);
|
||||||
private static readonly string PluginScriptIdentifier = ScriptLoader.GetRootIdentifier(PluginManager.PluginNotificationScriptFile);
|
private static readonly string NotificationJS = ScriptLoader.LoadResource(NotificationScriptFile);
|
||||||
|
|
||||||
private static readonly string NotificationJS, PluginJS;
|
|
||||||
|
|
||||||
static FormNotificationMain(){
|
|
||||||
NotificationJS = ScriptLoader.LoadResource(NotificationScriptFile);
|
|
||||||
PluginJS = ScriptLoader.LoadResource(PluginManager.PluginNotificationScriptFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int BaseClientWidth{
|
|
||||||
get{
|
|
||||||
int level = TweetNotification.FontSizeLevel;
|
|
||||||
int width = level == 0 ? 284 : (int)Math.Round(284.0*(1.0+0.05*level));
|
|
||||||
return (int)Math.Round(width*Program.UserConfig.ZoomMultiplier);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int BaseClientHeight{
|
|
||||||
get{
|
|
||||||
int level = TweetNotification.FontSizeLevel;
|
|
||||||
int height = level == 0 ? 118 : (int)Math.Round(118.0*(1.0+0.075*level));
|
|
||||||
return (int)Math.Round(height*Program.UserConfig.ZoomMultiplier);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly PluginManager plugins;
|
private readonly PluginManager plugins;
|
||||||
|
|
||||||
@@ -51,9 +31,9 @@ namespace TweetDuck.Core.Notification{
|
|||||||
private bool? prevDisplayTimer;
|
private bool? prevDisplayTimer;
|
||||||
private int? prevFontSize;
|
private int? prevFontSize;
|
||||||
|
|
||||||
private bool RequiresResize{
|
public bool RequiresResize{
|
||||||
get{
|
get{
|
||||||
return !prevDisplayTimer.HasValue || !prevFontSize.HasValue || prevDisplayTimer != Program.UserConfig.DisplayNotificationTimer || prevFontSize != TweetNotification.FontSizeLevel;
|
return !prevDisplayTimer.HasValue || !prevFontSize.HasValue || prevDisplayTimer != Program.UserConfig.DisplayNotificationTimer || prevFontSize != TweetNotification.FontSizeLevel || CanResizeWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
set{
|
set{
|
||||||
@@ -68,11 +48,41 @@ namespace TweetDuck.Core.Notification{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int BaseClientWidth{
|
||||||
|
get{
|
||||||
|
switch(Program.UserConfig.NotificationSize){
|
||||||
|
default:
|
||||||
|
return BrowserUtils.Scale(284, SizeScale*(1.0+0.05*TweetNotification.FontSizeLevel));
|
||||||
|
|
||||||
|
case TweetNotification.Size.Custom:
|
||||||
|
return Program.UserConfig.CustomNotificationSize.Width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int BaseClientHeight{
|
||||||
|
get{
|
||||||
|
switch(Program.UserConfig.NotificationSize){
|
||||||
|
default:
|
||||||
|
return BrowserUtils.Scale(122, SizeScale*(1.0+0.075*TweetNotification.FontSizeLevel));
|
||||||
|
|
||||||
|
case TweetNotification.Size.Custom:
|
||||||
|
return Program.UserConfig.CustomNotificationSize.Height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Size BrowserSize{
|
||||||
|
get => Program.UserConfig.DisplayNotificationTimer ? new Size(ClientSize.Width, ClientSize.Height-TimerBarHeight) : ClientSize;
|
||||||
|
}
|
||||||
|
|
||||||
public FormNotificationMain(FormBrowser owner, PluginManager pluginManager, bool enableContextMenu) : base(owner, enableContextMenu){
|
public FormNotificationMain(FormBrowser owner, PluginManager pluginManager, bool enableContextMenu) : base(owner, enableContextMenu){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
this.plugins = pluginManager;
|
this.plugins = pluginManager;
|
||||||
|
|
||||||
|
browser.KeyboardHandler = new KeyboardHandlerNotification(this);
|
||||||
|
|
||||||
browser.RegisterAsyncJsObject("$TD", new TweetDeckBridge(owner, this));
|
browser.RegisterAsyncJsObject("$TD", new TweetDeckBridge(owner, this));
|
||||||
browser.RegisterAsyncJsObject("$TDP", plugins.Bridge);
|
browser.RegisterAsyncJsObject("$TDP", plugins.Bridge);
|
||||||
|
|
||||||
@@ -104,9 +114,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
int eventType = wParam.ToInt32();
|
int eventType = wParam.ToInt32();
|
||||||
|
|
||||||
if (eventType == NativeMethods.WM_MOUSEWHEEL && browser.Bounds.Contains(PointToClient(Cursor.Position))){
|
if (eventType == NativeMethods.WM_MOUSEWHEEL && browser.Bounds.Contains(PointToClient(Cursor.Position))){
|
||||||
int distance = (int)Math.Round(NativeMethods.GetMouseHookData(lParam)*(Program.UserConfig.NotificationScrollSpeed/100.0));
|
browser.SendMouseWheelEvent(0, 0, 0, BrowserUtils.Scale(NativeMethods.GetMouseHookData(lParam), Program.UserConfig.NotificationScrollSpeed/100.0), CefEventFlags.None);
|
||||||
|
|
||||||
browser.SendMouseWheelEvent(0, 0, 0, distance, CefEventFlags.None);
|
|
||||||
return NativeMethods.HOOK_HANDLED;
|
return NativeMethods.HOOK_HANDLED;
|
||||||
}
|
}
|
||||||
else if (eventType == NativeMethods.WM_XBUTTONDOWN && DesktopBounds.Contains(Cursor.Position)){
|
else if (eventType == NativeMethods.WM_XBUTTONDOWN && DesktopBounds.Contains(Cursor.Position)){
|
||||||
@@ -156,14 +164,9 @@ namespace TweetDuck.Core.Notification{
|
|||||||
|
|
||||||
private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
|
private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
|
||||||
if (e.Frame.IsMain && NotificationJS != null && browser.Address != "about:blank"){
|
if (e.Frame.IsMain && NotificationJS != null && browser.Address != "about:blank"){
|
||||||
e.Frame.ExecuteJavaScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Properties.AllNotification));
|
e.Frame.ExecuteJavaScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Environment.Notification));
|
||||||
ScriptLoader.ExecuteScript(e.Frame, NotificationJS, NotificationScriptIdentifier);
|
ScriptLoader.ExecuteScript(e.Frame, NotificationJS, NotificationScriptIdentifier);
|
||||||
|
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Notification);
|
||||||
if (plugins.HasAnyPlugin(PluginEnvironment.Notification)){
|
|
||||||
ScriptLoader.ExecuteScript(e.Frame, PluginJS, PluginScriptIdentifier);
|
|
||||||
ScriptLoader.ExecuteFile(e.Frame, PluginManager.PluginGlobalScriptFile);
|
|
||||||
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Notification, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,7 +180,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
|
|
||||||
timeLeft -= timerProgress.Interval;
|
timeLeft -= timerProgress.Interval;
|
||||||
|
|
||||||
int value = (int)Math.Round(1025.0*(totalTime-timeLeft)/totalTime);
|
int value = BrowserUtils.Scale(1025, (totalTime-timeLeft)/(double)totalTime);
|
||||||
progressBarTimer.SetValueInstant(Math.Min(1000, Math.Max(0, Program.UserConfig.NotificationTimerCountDown ? 1000-value : value)));
|
progressBarTimer.SetValueInstant(Math.Min(1000, Math.Max(0, Program.UserConfig.NotificationTimerCountDown ? 1000-value : value)));
|
||||||
|
|
||||||
if (timeLeft <= 0){
|
if (timeLeft <= 0){
|
||||||
@@ -255,7 +258,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
|
|
||||||
protected override void SetNotificationSize(int width, int height){
|
protected override void SetNotificationSize(int width, int height){
|
||||||
if (Program.UserConfig.DisplayNotificationTimer){
|
if (Program.UserConfig.DisplayNotificationTimer){
|
||||||
ClientSize = new Size(width, height+4);
|
ClientSize = new Size(width, height+TimerBarHeight);
|
||||||
progressBarTimer.Visible = true;
|
progressBarTimer.Visible = true;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
@@ -276,7 +279,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
StartMouseHook();
|
StartMouseHook();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnNotificationReady(){
|
protected virtual void OnNotificationReady(){
|
||||||
PrepareAndDisplayWindow();
|
PrepareAndDisplayWindow();
|
||||||
timerProgress.Start();
|
timerProgress.Start();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,24 +2,40 @@
|
|||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Drawing.Imaging;
|
using System.Drawing.Imaging;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using CefSharp;
|
||||||
using TweetDuck.Core.Bridge;
|
using TweetDuck.Core.Bridge;
|
||||||
|
using TweetDuck.Core.Other;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
using TweetDuck.Data;
|
||||||
|
using TweetDuck.Plugins;
|
||||||
using TweetDuck.Resources;
|
using TweetDuck.Resources;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Notification.Screenshot{
|
namespace TweetDuck.Core.Notification.Screenshot{
|
||||||
sealed class FormNotificationScreenshotable : FormNotificationBase{
|
sealed class FormNotificationScreenshotable : FormNotificationBase{
|
||||||
public FormNotificationScreenshotable(Action callback, Form owner) : base(owner, false){
|
private readonly PluginManager plugins;
|
||||||
|
|
||||||
|
public FormNotificationScreenshotable(Action callback, FormBrowser owner, PluginManager pluginManager) : base(owner, false){
|
||||||
|
this.plugins = pluginManager;
|
||||||
|
|
||||||
browser.RegisterAsyncJsObject("$TD_NotificationScreenshot", new CallbackBridge(this, callback));
|
browser.RegisterAsyncJsObject("$TD_NotificationScreenshot", new CallbackBridge(this, callback));
|
||||||
|
|
||||||
browser.FrameLoadEnd += (sender, args) => {
|
browser.LoadingStateChanged += (sender, args) => {
|
||||||
if (args.Frame.IsMain && browser.Address != "about:blank"){
|
if (!args.IsLoading){
|
||||||
ScriptLoader.ExecuteScript(args.Frame, "window.setTimeout($TD_NotificationScreenshot.trigger, 67)", "gen:screenshot");
|
using(IFrame frame = args.Browser.MainFrame){
|
||||||
|
ScriptLoader.ExecuteScript(frame, "window.setTimeout($TD_NotificationScreenshot.trigger, 129)", "gen:screenshot");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override string GetTweetHTML(TweetNotification tweet){
|
protected override string GetTweetHTML(TweetNotification tweet){
|
||||||
return tweet.GenerateHtml(enableCustomCSS: false);
|
string html = tweet.GenerateHtml("td-screenshot", false);
|
||||||
|
|
||||||
|
foreach(InjectedHTML injection in plugins.Bridge.NotificationInjections){
|
||||||
|
html = injection.Inject(html);
|
||||||
|
}
|
||||||
|
|
||||||
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadNotificationForScreenshot(TweetNotification tweet, int width, int height){
|
public void LoadNotificationForScreenshot(TweetNotification tweet, int width, int height){
|
||||||
@@ -31,7 +47,7 @@ namespace TweetDuck.Core.Notification.Screenshot{
|
|||||||
IntPtr context = NativeMethods.GetDC(this.Handle);
|
IntPtr context = NativeMethods.GetDC(this.Handle);
|
||||||
|
|
||||||
if (context == IntPtr.Zero){
|
if (context == IntPtr.Zero){
|
||||||
MessageBox.Show("Could not retrieve a graphics context handle for the notification window to take the screenshot.", "Screenshot Failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
FormMessage.Error("Screenshot Failed", "Could not retrieve a graphics context handle for the notification window to take the screenshot.", FormMessage.OK);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
using(Bitmap bmp = new Bitmap(ClientSize.Width, ClientSize.Height, PixelFormat.Format32bppRgb)){
|
using(Bitmap bmp = new Bitmap(ClientSize.Width, ClientSize.Height, PixelFormat.Format32bppRgb)){
|
||||||
|
|||||||
@@ -4,17 +4,20 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
|
using TweetDuck.Plugins;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Notification.Screenshot{
|
namespace TweetDuck.Core.Notification.Screenshot{
|
||||||
sealed class TweetScreenshotManager : IDisposable{
|
sealed class TweetScreenshotManager : IDisposable{
|
||||||
private readonly Form owner;
|
private readonly FormBrowser owner;
|
||||||
|
private readonly PluginManager plugins;
|
||||||
private readonly Timer timeout;
|
private readonly Timer timeout;
|
||||||
private readonly Timer disposer;
|
private readonly Timer disposer;
|
||||||
|
|
||||||
private FormNotificationScreenshotable screenshot;
|
private FormNotificationScreenshotable screenshot;
|
||||||
|
|
||||||
public TweetScreenshotManager(Form owner){
|
public TweetScreenshotManager(FormBrowser owner, PluginManager pluginManager){
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
|
this.plugins = pluginManager;
|
||||||
|
|
||||||
this.timeout = new Timer{ Interval = 8000 };
|
this.timeout = new Timer{ Interval = 8000 };
|
||||||
this.timeout.Tick += timeout_Tick;
|
this.timeout.Tick += timeout_Tick;
|
||||||
@@ -25,8 +28,7 @@ namespace TweetDuck.Core.Notification.Screenshot{
|
|||||||
|
|
||||||
private void timeout_Tick(object sender, EventArgs e){
|
private void timeout_Tick(object sender, EventArgs e){
|
||||||
timeout.Stop();
|
timeout.Stop();
|
||||||
screenshot.Location = ControlExtensions.InvisibleLocation;
|
OnFinished();
|
||||||
disposer.Start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void disposer_Tick(object sender, EventArgs e){
|
private void disposer_Tick(object sender, EventArgs e){
|
||||||
@@ -40,13 +42,17 @@ namespace TweetDuck.Core.Notification.Screenshot{
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
screenshot = new FormNotificationScreenshotable(Callback, owner){
|
screenshot = new FormNotificationScreenshotable(Callback, owner, plugins){
|
||||||
CanMoveWindow = () => false
|
CanMoveWindow = () => false
|
||||||
};
|
};
|
||||||
|
|
||||||
screenshot.LoadNotificationForScreenshot(new TweetNotification(string.Empty, html, 0, string.Empty, string.Empty), width, height);
|
screenshot.LoadNotificationForScreenshot(new TweetNotification(string.Empty, string.Empty, string.Empty, html, 0, string.Empty, string.Empty), width, height);
|
||||||
screenshot.Show();
|
screenshot.Show();
|
||||||
timeout.Start();
|
timeout.Start();
|
||||||
|
|
||||||
|
#if !(DEBUG && NO_HIDE_SCREENSHOTS)
|
||||||
|
owner.IsWaiting = true;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Callback(){
|
private void Callback(){
|
||||||
@@ -58,14 +64,19 @@ namespace TweetDuck.Core.Notification.Screenshot{
|
|||||||
screenshot.TakeScreenshot();
|
screenshot.TakeScreenshot();
|
||||||
|
|
||||||
#if !(DEBUG && NO_HIDE_SCREENSHOTS)
|
#if !(DEBUG && NO_HIDE_SCREENSHOTS)
|
||||||
screenshot.Location = ControlExtensions.InvisibleLocation;
|
OnFinished();
|
||||||
disposer.Start();
|
|
||||||
#else
|
#else
|
||||||
screenshot.MoveToVisibleLocation();
|
screenshot.MoveToVisibleLocation();
|
||||||
screenshot.FormClosed += (sender, args) => disposer.Start();
|
screenshot.FormClosed += (sender, args) => disposer.Start();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnFinished(){
|
||||||
|
screenshot.Location = ControlExtensions.InvisibleLocation;
|
||||||
|
owner.IsWaiting = false;
|
||||||
|
disposer.Start();
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose(){
|
public void Dispose(){
|
||||||
timeout.Dispose();
|
timeout.Dispose();
|
||||||
disposer.Dispose();
|
disposer.Dispose();
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using TweetLib.Audio;
|
using TweetLib.Audio;
|
||||||
using TweetLib.Audio.Utils;
|
|
||||||
|
|
||||||
namespace TweetDuck.Core.Notification{
|
namespace TweetDuck.Core.Notification{
|
||||||
sealed class SoundNotification : IDisposable{
|
sealed class SoundNotification : IDisposable{
|
||||||
@@ -18,6 +17,10 @@ namespace TweetDuck.Core.Notification{
|
|||||||
player.Play(file);
|
player.Play(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool SetVolume(int volume){
|
||||||
|
return player.SetVolume(volume);
|
||||||
|
}
|
||||||
|
|
||||||
private void Player_PlaybackError(object sender, PlaybackErrorEventArgs e){
|
private void Player_PlaybackError(object sender, PlaybackErrorEventArgs e){
|
||||||
PlaybackError?.Invoke(this, e);
|
PlaybackError?.Invoke(this, e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
|
|
||||||
private const string DefaultFontSizeClass = "medium";
|
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 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}";
|
private const string CustomCSS = @"body:before{content:none}body{overflow-y:auto}.scroll-styled-v::-webkit-scrollbar{width:7px}.scroll-styled-v::-webkit-scrollbar-thumb{border-radius:0}.scroll-styled-v::-webkit-scrollbar-track{border-left:0}#td-skip{opacity:0;cursor:pointer;transition:opacity 0.15s ease}.td-hover #td-skip{opacity:0.75}#td-skip:hover{opacity:1}.media-size-medium{height:calc(100vh - 16px)!important;max-height:240px;border-radius:1px!important}.js-quote-detail .media-size-medium{height:calc(100vh - 28px)!important;}.js-media.margin-vm, .js-media-preview-container.margin-vm{margin-bottom:0!important}";
|
||||||
|
|
||||||
public static int FontSizeLevel{
|
public static int FontSizeLevel{
|
||||||
get{
|
get{
|
||||||
@@ -35,7 +35,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
return new TweetNotification("Home", ExampleTweetHTML, 95, string.Empty, string.Empty, true);
|
return new TweetNotification(string.Empty, string.Empty, "Home", ExampleTweetHTML, 95, string.Empty, string.Empty, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,7 +51,14 @@ namespace TweetDuck.Core.Notification{
|
|||||||
TopLeft, TopRight, BottomLeft, BottomRight, Custom
|
TopLeft, TopRight, BottomLeft, BottomRight, Custom
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Column { get; }
|
public enum Size{
|
||||||
|
Auto, Custom
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ColumnId { get; }
|
||||||
|
public string ChirpId { get; }
|
||||||
|
|
||||||
|
public string ColumnTitle { get; }
|
||||||
public string TweetUrl { get; }
|
public string TweetUrl { get; }
|
||||||
public string QuoteUrl { get; }
|
public string QuoteUrl { get; }
|
||||||
|
|
||||||
@@ -59,10 +66,13 @@ namespace TweetDuck.Core.Notification{
|
|||||||
private readonly int characters;
|
private readonly int characters;
|
||||||
private readonly bool isExample;
|
private readonly bool isExample;
|
||||||
|
|
||||||
public TweetNotification(string column, string html, int characters, string tweetUrl, string quoteUrl) : this(column, html, characters, tweetUrl, quoteUrl, false){}
|
public TweetNotification(string columnId, string chirpId, string title, string html, int characters, string tweetUrl, string quoteUrl) : this(columnId, chirpId, title, html, characters, tweetUrl, quoteUrl, false){}
|
||||||
|
|
||||||
private TweetNotification(string column, string html, int characters, string tweetUrl, string quoteUrl, bool isExample){
|
private TweetNotification(string columnId, string chirpId, string title, string html, int characters, string tweetUrl, string quoteUrl, bool isExample){
|
||||||
this.Column = column;
|
this.ColumnId = columnId;
|
||||||
|
this.ChirpId = chirpId;
|
||||||
|
|
||||||
|
this.ColumnTitle = title;
|
||||||
this.TweetUrl = tweetUrl;
|
this.TweetUrl = tweetUrl;
|
||||||
this.QuoteUrl = quoteUrl;
|
this.QuoteUrl = quoteUrl;
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ namespace TweetDuck.Core.Other{
|
|||||||
|
|
||||||
Text = "About "+Program.BrandName+" "+Program.VersionTag;
|
Text = "About "+Program.BrandName+" "+Program.VersionTag;
|
||||||
|
|
||||||
labelDescription.Text = Program.BrandName+" was created by chylex as a replacement to the discontinued official TweetDeck client for Windows.\n\nThe program is available for free under the open source MIT license.";
|
labelDescription.Text = "TweetDuck was created by chylex as a replacement to the discontinued official TweetDeck client for Windows.\n\nThe program is available for free under the open source MIT license.";
|
||||||
|
|
||||||
labelWebsite.Links.Add(new LinkLabel.Link(0, labelWebsite.Text.Length, Program.Website));
|
labelWebsite.Links.Add(new LinkLabel.Link(0, labelWebsite.Text.Length, Program.Website));
|
||||||
labelTips.Links.Add(new LinkLabel.Link(0, labelTips.Text.Length, TipsLink));
|
labelTips.Links.Add(new LinkLabel.Link(0, labelTips.Text.Length, TipsLink));
|
||||||
|
|||||||
@@ -1,11 +1,66 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using TweetDuck.Core.Controls;
|
||||||
|
using TweetDuck.Core.Utils;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Other{
|
namespace TweetDuck.Core.Other{
|
||||||
|
[Flags]
|
||||||
|
enum ControlType{
|
||||||
|
None = 0,
|
||||||
|
Accept = 1, // triggered by pressing enter when a non-button is focused
|
||||||
|
Cancel = 2, // triggered by closing the dialog without pressing a button
|
||||||
|
Focused = 4 // active control after the dialog is showed
|
||||||
|
}
|
||||||
|
|
||||||
sealed partial class FormMessage : Form{
|
sealed partial class FormMessage : Form{
|
||||||
|
public const string OK = "OK";
|
||||||
|
public const string Yes = "Yes";
|
||||||
|
public const string No = "No";
|
||||||
|
public const string Cancel = "Cancel";
|
||||||
|
public const string Retry = "Retry";
|
||||||
|
public const string Ignore = "Ignore";
|
||||||
|
public const string Exit = "Exit";
|
||||||
|
|
||||||
|
public static bool Information(string caption, string text, string buttonAccept, string buttonCancel = null){
|
||||||
|
return Show(caption, text, MessageBoxIcon.Information, buttonAccept, buttonCancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool Warning(string caption, string text, string buttonAccept, string buttonCancel = null){
|
||||||
|
return Show(caption, text, MessageBoxIcon.Warning, buttonAccept, buttonCancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool Error(string caption, string text, string buttonAccept, string buttonCancel = null){
|
||||||
|
return Show(caption, text, MessageBoxIcon.Error, buttonAccept, buttonCancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool Question(string caption, string text, string buttonAccept, string buttonCancel = null){
|
||||||
|
return Show(caption, text, MessageBoxIcon.Question, buttonAccept, buttonCancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool Show(string caption, string text, MessageBoxIcon icon, string button){
|
||||||
|
return Show(caption, text, icon, button, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool Show(string caption, string text, MessageBoxIcon icon, string buttonAccept, string buttonCancel){
|
||||||
|
using(FormMessage message = new FormMessage(caption, text, icon)){
|
||||||
|
if (buttonCancel == null){
|
||||||
|
message.AddButton(buttonAccept, DialogResult.OK, ControlType.Cancel | ControlType.Focused);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
message.AddButton(buttonCancel, DialogResult.Cancel, ControlType.Cancel);
|
||||||
|
message.AddButton(buttonAccept, DialogResult.OK, ControlType.Accept | ControlType.Focused);
|
||||||
|
}
|
||||||
|
|
||||||
|
return message.ShowDialog() == DialogResult.OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instance
|
||||||
|
|
||||||
public Button ClickedButton { get; private set; }
|
public Button ClickedButton { get; private set; }
|
||||||
|
|
||||||
|
public bool HasIcon => icon != null;
|
||||||
public int ActionPanelY => panelActions.Location.Y;
|
public int ActionPanelY => panelActions.Location.Y;
|
||||||
|
|
||||||
private int ClientWidth{
|
private int ClientWidth{
|
||||||
@@ -13,8 +68,13 @@ namespace TweetDuck.Core.Other{
|
|||||||
set => ClientSize = new Size(value, ClientSize.Height);
|
set => ClientSize = new Size(value, ClientSize.Height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int ButtonDistance{
|
||||||
|
get => BrowserUtils.Scale(96, dpiScale);
|
||||||
|
}
|
||||||
|
|
||||||
private readonly Icon icon;
|
private readonly Icon icon;
|
||||||
private readonly bool isReady;
|
private readonly bool isReady;
|
||||||
|
private readonly float dpiScale;
|
||||||
|
|
||||||
private int realFormWidth, minFormWidth;
|
private int realFormWidth, minFormWidth;
|
||||||
private int buttonCount;
|
private int buttonCount;
|
||||||
@@ -24,9 +84,11 @@ namespace TweetDuck.Core.Other{
|
|||||||
public FormMessage(string caption, string text, MessageBoxIcon messageIcon){
|
public FormMessage(string caption, string text, MessageBoxIcon messageIcon){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
this.dpiScale = this.GetDPIScale();
|
||||||
|
|
||||||
this.prevLabelWidth = labelMessage.Width;
|
this.prevLabelWidth = labelMessage.Width;
|
||||||
this.prevLabelHeight = labelMessage.Height;
|
this.prevLabelHeight = labelMessage.Height;
|
||||||
this.minFormWidth = 40;
|
this.minFormWidth = BrowserUtils.Scale(42, dpiScale);
|
||||||
|
|
||||||
switch(messageIcon){
|
switch(messageIcon){
|
||||||
case MessageBoxIcon.Information:
|
case MessageBoxIcon.Information:
|
||||||
@@ -47,26 +109,30 @@ namespace TweetDuck.Core.Other{
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
icon = null;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isReady = true;
|
this.isReady = true;
|
||||||
|
|
||||||
this.Text = caption;
|
this.Text = caption;
|
||||||
this.labelMessage.Text = text;
|
this.labelMessage.Text = text.Replace("\r", "").Replace("\n", Environment.NewLine);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FormMessage_SizeChanged(object sender, EventArgs e){
|
private void FormMessage_SizeChanged(object sender, EventArgs e){
|
||||||
RecalculateButtonLocation();
|
RecalculateButtonLocation();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Button AddButton(string title, DialogResult result = DialogResult.OK){
|
public Button AddButton(string title, ControlType type){
|
||||||
|
return AddButton(title, DialogResult.OK, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Button AddButton(string title, DialogResult result = DialogResult.OK, ControlType type = ControlType.None){
|
||||||
Button button = new Button{
|
Button button = new Button{
|
||||||
Anchor = AnchorStyles.Bottom,
|
Anchor = AnchorStyles.Bottom,
|
||||||
Font = SystemFonts.MessageBoxFont,
|
Font = SystemFonts.MessageBoxFont,
|
||||||
Location = new Point(0, 12),
|
Location = new Point(0, 12),
|
||||||
Size = new Size(88, 26),
|
Size = new Size(BrowserUtils.Scale(88, dpiScale), BrowserUtils.Scale(26, dpiScale)),
|
||||||
TabIndex = buttonCount,
|
TabIndex = buttonCount,
|
||||||
Text = title,
|
Text = title,
|
||||||
UseVisualStyleBackColor = true
|
UseVisualStyleBackColor = true
|
||||||
@@ -81,24 +147,41 @@ namespace TweetDuck.Core.Other{
|
|||||||
panelActions.Controls.Add(button);
|
panelActions.Controls.Add(button);
|
||||||
++buttonCount;
|
++buttonCount;
|
||||||
|
|
||||||
minFormWidth += 96;
|
minFormWidth += ButtonDistance;
|
||||||
ClientWidth = Math.Max(realFormWidth, minFormWidth);
|
ClientWidth = Math.Max(realFormWidth, minFormWidth);
|
||||||
RecalculateButtonLocation();
|
RecalculateButtonLocation();
|
||||||
|
|
||||||
|
if (type.HasFlag(ControlType.Accept)){
|
||||||
|
AcceptButton = button;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.HasFlag(ControlType.Cancel)){
|
||||||
|
CancelButton = button;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.HasFlag(ControlType.Focused)){
|
||||||
|
ActiveControl = button;
|
||||||
|
}
|
||||||
|
|
||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddActionControl(Control control){
|
public void AddActionControl(Control control){
|
||||||
panelActions.Controls.Add(control);
|
panelActions.Controls.Add(control);
|
||||||
|
|
||||||
|
control.Size = new Size(BrowserUtils.Scale(control.Width, dpiScale), BrowserUtils.Scale(control.Height, dpiScale));
|
||||||
|
|
||||||
minFormWidth += control.Width+control.Margin.Horizontal;
|
minFormWidth += control.Width+control.Margin.Horizontal;
|
||||||
ClientWidth = Math.Max(realFormWidth, minFormWidth);
|
ClientWidth = Math.Max(realFormWidth, minFormWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RecalculateButtonLocation(){
|
private void RecalculateButtonLocation(){
|
||||||
|
int dist = ButtonDistance;
|
||||||
|
int start = ClientWidth-dist;
|
||||||
|
|
||||||
for(int index = 0; index < buttonCount; index++){
|
for(int index = 0; index < buttonCount; index++){
|
||||||
Control control = panelActions.Controls[index];
|
Control control = panelActions.Controls[index];
|
||||||
control.Location = new Point(ClientWidth-97-index*96, control.Location.Y);
|
control.Location = new Point(start-index*dist, control.Location.Y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,17 +191,18 @@ namespace TweetDuck.Core.Other{
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool isMultiline = labelMessage.Height > labelMessage.MinimumSize.Height;
|
bool isMultiline = labelMessage.Height > labelMessage.MinimumSize.Height;
|
||||||
|
int labelOffset = BrowserUtils.Scale(8, dpiScale);
|
||||||
|
|
||||||
if (isMultiline && !wasLabelMultiline){
|
if (isMultiline && !wasLabelMultiline){
|
||||||
labelMessage.Location = new Point(labelMessage.Location.X, labelMessage.Location.Y-8);
|
labelMessage.Location = new Point(labelMessage.Location.X, labelMessage.Location.Y-labelOffset);
|
||||||
prevLabelHeight += 8;
|
prevLabelHeight += labelOffset;
|
||||||
}
|
}
|
||||||
else if (!isMultiline && wasLabelMultiline){
|
else if (!isMultiline && wasLabelMultiline){
|
||||||
labelMessage.Location = new Point(labelMessage.Location.X, labelMessage.Location.Y+8);
|
labelMessage.Location = new Point(labelMessage.Location.X, labelMessage.Location.Y+labelOffset);
|
||||||
prevLabelHeight -= 8;
|
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);
|
ClientWidth = Math.Max(realFormWidth, minFormWidth);
|
||||||
Height += labelMessage.Height-prevLabelHeight;
|
Height += labelMessage.Height-prevLabelHeight;
|
||||||
|
|
||||||
@@ -129,7 +213,7 @@ namespace TweetDuck.Core.Other{
|
|||||||
|
|
||||||
protected override void OnPaint(PaintEventArgs e){
|
protected override void OnPaint(PaintEventArgs e){
|
||||||
if (icon != null){
|
if (icon != null){
|
||||||
e.Graphics.DrawIcon(icon, 25, 26);
|
e.Graphics.DrawIcon(icon, BrowserUtils.Scale(25, dpiScale), 1+BrowserUtils.Scale(25, dpiScale));
|
||||||
}
|
}
|
||||||
|
|
||||||
base.OnPaint(e);
|
base.OnPaint(e);
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ namespace TweetDuck.Core.Other{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void btnReload_Click(object sender, EventArgs e){
|
private void btnReload_Click(object sender, EventArgs e){
|
||||||
if (MessageBox.Show("This will also reload the browser window. Do you want to proceed?", "Reloading Plugins", MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == DialogResult.Yes){
|
if (FormMessage.Warning("Reloading Plugins", "This will also reload the browser window. Do you want to proceed?", FormMessage.Yes, FormMessage.No)){
|
||||||
pluginManager.Reload();
|
pluginManager.Reload();
|
||||||
ReloadPluginList();
|
ReloadPluginList();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
|
||||||
@@ -5,6 +5,7 @@ using System.Windows.Forms;
|
|||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Other.Settings;
|
using TweetDuck.Core.Other.Settings;
|
||||||
using TweetDuck.Core.Other.Settings.Dialogs;
|
using TweetDuck.Core.Other.Settings.Dialogs;
|
||||||
|
using TweetDuck.Core.Utils;
|
||||||
using TweetDuck.Plugins;
|
using TweetDuck.Plugins;
|
||||||
using TweetDuck.Updates;
|
using TweetDuck.Updates;
|
||||||
|
|
||||||
@@ -13,9 +14,13 @@ namespace TweetDuck.Core.Other{
|
|||||||
private readonly FormBrowser browser;
|
private readonly FormBrowser browser;
|
||||||
private readonly PluginManager plugins;
|
private readonly PluginManager plugins;
|
||||||
|
|
||||||
|
private readonly int buttonHeight;
|
||||||
|
|
||||||
private readonly Dictionary<Type, SettingsTab> tabs = new Dictionary<Type, SettingsTab>(4);
|
private readonly Dictionary<Type, SettingsTab> tabs = new Dictionary<Type, SettingsTab>(4);
|
||||||
private SettingsTab currentTab;
|
private SettingsTab currentTab;
|
||||||
|
|
||||||
|
public bool ShouldReloadBrowser { get; private set; }
|
||||||
|
|
||||||
public FormSettings(FormBrowser browser, PluginManager plugins, UpdateHandler updates, Type startTab){
|
public FormSettings(FormBrowser browser, PluginManager plugins, UpdateHandler updates, Type startTab){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
@@ -26,6 +31,8 @@ namespace TweetDuck.Core.Other{
|
|||||||
|
|
||||||
this.plugins = plugins;
|
this.plugins = plugins;
|
||||||
|
|
||||||
|
this.buttonHeight = BrowserUtils.Scale(39, this.GetDPIScale()) | 1;
|
||||||
|
|
||||||
AddButton("General", () => new TabSettingsGeneral(updates));
|
AddButton("General", () => new TabSettingsGeneral(updates));
|
||||||
AddButton("Notifications", () => new TabSettingsNotifications(browser.CreateNotificationForm(false)));
|
AddButton("Notifications", () => new TabSettingsNotifications(browser.CreateNotificationForm(false)));
|
||||||
AddButton("Sounds", () => new TabSettingsSounds());
|
AddButton("Sounds", () => new TabSettingsSounds());
|
||||||
@@ -47,13 +54,19 @@ namespace TweetDuck.Core.Other{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void btnManageOptions_Click(object sender, EventArgs e){
|
private void btnManageOptions_Click(object sender, EventArgs e){
|
||||||
using(DialogSettingsManage dialog = new DialogSettingsManage(plugins)){
|
foreach(SettingsTab tab in tabs.Values){
|
||||||
if (dialog.ShowDialog() == DialogResult.OK && dialog.ShouldReloadUI){
|
if (tab.IsInitialized){
|
||||||
foreach(SettingsTab tab in tabs.Values){
|
tab.Control.OnClosing();
|
||||||
tab.Control = null;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectTab(currentTab);
|
using(DialogSettingsManage dialog = new DialogSettingsManage(plugins)){
|
||||||
|
if (dialog.ShowDialog() == DialogResult.OK){
|
||||||
|
FormClosing -= FormSettings_FormClosing;
|
||||||
|
browser.ResumeNotification();
|
||||||
|
|
||||||
|
ShouldReloadBrowser = dialog.ShouldReloadBrowser;
|
||||||
|
Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,14 +76,12 @@ namespace TweetDuck.Core.Other{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void AddButton<T>(string title, Func<T> constructor) where T : BaseTabSettings{
|
private void AddButton<T>(string title, Func<T> constructor) where T : BaseTabSettings{
|
||||||
const int btnHeight = 39;
|
|
||||||
|
|
||||||
FlatButton btn = new FlatButton{
|
FlatButton btn = new FlatButton{
|
||||||
BackColor = SystemColors.Control,
|
BackColor = SystemColors.Control,
|
||||||
FlatStyle = FlatStyle.Flat,
|
FlatStyle = FlatStyle.Flat,
|
||||||
Location = new Point(0, (btnHeight+1)*(panelButtons.Controls.Count/2)),
|
Location = new Point(0, (buttonHeight+1)*(panelButtons.Controls.Count/2)),
|
||||||
Margin = new Padding(0),
|
Margin = new Padding(0),
|
||||||
Size = new Size(panelButtons.Width, btnHeight),
|
Size = new Size(panelButtons.Width, buttonHeight),
|
||||||
Text = title,
|
Text = title,
|
||||||
UseVisualStyleBackColor = true
|
UseVisualStyleBackColor = true
|
||||||
};
|
};
|
||||||
@@ -83,7 +94,7 @@ namespace TweetDuck.Core.Other{
|
|||||||
|
|
||||||
panelButtons.Controls.Add(new Panel{
|
panelButtons.Controls.Add(new Panel{
|
||||||
BackColor = Color.DimGray,
|
BackColor = Color.DimGray,
|
||||||
Location = new Point(0, panelButtons.Controls[panelButtons.Controls.Count-1].Location.Y+btnHeight),
|
Location = new Point(0, panelButtons.Controls[panelButtons.Controls.Count-1].Location.Y+buttonHeight),
|
||||||
Margin = new Padding(0),
|
Margin = new Padding(0),
|
||||||
Size = new Size(panelButtons.Width, 1)
|
Size = new Size(panelButtons.Width, 1)
|
||||||
});
|
});
|
||||||
@@ -106,7 +117,12 @@ namespace TweetDuck.Core.Other{
|
|||||||
|
|
||||||
if (!tab.IsInitialized){
|
if (!tab.IsInitialized){
|
||||||
foreach(Control control in tab.Control.InteractiveControls){
|
foreach(Control control in tab.Control.InteractiveControls){
|
||||||
control.MouseLeave += control_MouseLeave;
|
if (control is ComboBox){
|
||||||
|
control.MouseLeave += control_MouseLeave;
|
||||||
|
}
|
||||||
|
else if (control is TrackBar){
|
||||||
|
control.MouseWheel += control_MouseWheel;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tab.Control.OnReady();
|
tab.Control.OnReady();
|
||||||
@@ -126,7 +142,12 @@ namespace TweetDuck.Core.Other{
|
|||||||
panelContents.Focus();
|
panelContents.Focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SettingsTab{
|
private void control_MouseWheel(object sender, MouseEventArgs e){
|
||||||
|
((HandledMouseEventArgs)e).Handled = true;
|
||||||
|
panelContents.Focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class SettingsTab{
|
||||||
public Button Button { get; }
|
public Button Button { get; }
|
||||||
|
|
||||||
public BaseTabSettings Control{
|
public BaseTabSettings Control{
|
||||||
|
|||||||
27
Core/Other/Management/BrowserProcesses.cs
Normal file
27
Core/Other/Management/BrowserProcesses.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using CefSharp;
|
||||||
|
using TweetDuck.Core.Utils;
|
||||||
|
|
||||||
|
namespace TweetDuck.Core.Other.Management{
|
||||||
|
static class BrowserProcesses{
|
||||||
|
private static readonly Dictionary<int, int> PIDs = new Dictionary<int, int>();
|
||||||
|
|
||||||
|
public static void Link(int identifier, int pid){
|
||||||
|
PIDs[identifier] = pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Forget(int identifier){
|
||||||
|
PIDs.Remove(identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Process FindProcess(IBrowser browser){
|
||||||
|
if (PIDs.TryGetValue(browser.Identifier, out int pid) && WindowsUtils.IsChildProcess(pid)){ // child process is checked in two places for safety
|
||||||
|
return Process.GetProcessById(pid);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
91
Core/Other/Management/MemoryUsageTracker.cs
Normal file
91
Core/Other/Management/MemoryUsageTracker.cs
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Timers;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using CefSharp;
|
||||||
|
using Timer = System.Timers.Timer;
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
private readonly string script;
|
||||||
|
private readonly Timer timer;
|
||||||
|
private Form owner;
|
||||||
|
private IBrowser browser;
|
||||||
|
|
||||||
|
private long threshold;
|
||||||
|
private bool needsCleanup;
|
||||||
|
|
||||||
|
public MemoryUsageTracker(string cleanupFunctionName){
|
||||||
|
this.script = $"window.{cleanupFunctionName} && window.{cleanupFunctionName}()";
|
||||||
|
|
||||||
|
this.timer = new Timer{ Interval = IntervalMemoryCheck };
|
||||||
|
this.timer.Elapsed += timer_Elapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Start(Form owner, IBrowser browser, int thresholdMB){
|
||||||
|
Stop();
|
||||||
|
|
||||||
|
this.owner = owner;
|
||||||
|
this.browser = browser;
|
||||||
|
this.threshold = thresholdMB*1024L*1024L;
|
||||||
|
this.timer.SynchronizingObject = owner;
|
||||||
|
this.timer.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Stop(){
|
||||||
|
timer.Stop();
|
||||||
|
timer.SynchronizingObject = null;
|
||||||
|
owner = null;
|
||||||
|
browser = null;
|
||||||
|
SetNeedsCleanup(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose(){
|
||||||
|
timer.SynchronizingObject = null;
|
||||||
|
timer.Dispose();
|
||||||
|
owner = null;
|
||||||
|
browser = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetNeedsCleanup(bool value){
|
||||||
|
if (needsCleanup != value){
|
||||||
|
needsCleanup = value;
|
||||||
|
timer.Interval = value ? IntervalCleanupAttempt : IntervalMemoryCheck; // restarts timer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void timer_Elapsed(object sender, ElapsedEventArgs e){
|
||||||
|
if (owner == null || browser == null){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needsCleanup){
|
||||||
|
if (!owner.ContainsFocus){
|
||||||
|
using(IFrame frame = browser.MainFrame){
|
||||||
|
frame.EvaluateScriptAsync(script).ContinueWith(task => {
|
||||||
|
JavascriptResponse response = task.Result;
|
||||||
|
|
||||||
|
if (response.Success && (response.Result as bool? ?? false)){
|
||||||
|
SetNeedsCleanup(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
try{
|
||||||
|
using(Process process = BrowserProcesses.FindProcess(browser)){
|
||||||
|
if (process?.PrivateMemorySize64 > threshold){
|
||||||
|
SetNeedsCleanup(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}catch{
|
||||||
|
// ignore I guess?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
179
Core/Other/Management/VideoPlayer.cs
Normal file
179
Core/Other/Management/VideoPlayer.cs
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
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 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){
|
||||||
|
if (Running){
|
||||||
|
Destroy();
|
||||||
|
isClosing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastUrl = url;
|
||||||
|
|
||||||
|
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);
|
||||||
|
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){
|
||||||
|
switch(currentProcess.ExitCode){
|
||||||
|
case 3: // CODE_LAUNCH_FAIL
|
||||||
|
if (FormMessage.Error("Video Playback Error", "Error launching video player, this may be caused by missing Windows Media Player. Do you want to open the video in a browser?", FormMessage.Yes, FormMessage.No)){
|
||||||
|
BrowserUtils.OpenExternalBrowser(lastUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4: // CODE_MEDIA_ERROR
|
||||||
|
if (FormMessage.Error("Video Playback Error", "The video could not be loaded, most likely due to unknown format. Do you want to open the video in a browser?", FormMessage.Yes, FormMessage.No)){
|
||||||
|
BrowserUtils.OpenExternalBrowser(lastUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentProcess.Dispose();
|
||||||
|
currentProcess = null;
|
||||||
|
|
||||||
|
currentPipe.Dispose();
|
||||||
|
currentPipe = null;
|
||||||
|
|
||||||
|
owner.InvokeAsyncSafe(TriggerProcessExitEventUnsafe);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TriggerProcessExitEventUnsafe(){
|
||||||
|
ProcessExited?.Invoke(this, new EventArgs());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,7 +17,7 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public BaseTabSettings(){
|
protected BaseTabSettings(){
|
||||||
Padding = new Padding(6);
|
Padding = new Padding(6);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
public virtual void OnClosing(){}
|
public virtual void OnClosing(){}
|
||||||
|
|
||||||
protected static void PromptRestart(){
|
protected static void PromptRestart(){
|
||||||
if (MessageBox.Show("The application must restart for the option to take place. Do you want to restart now?", Program.BrandName+" Options", MessageBoxButtons.YesNo, MessageBoxIcon.Information) == DialogResult.Yes){
|
if (FormMessage.Information("TweetDuck Options", "The application must restart for the option to take place. Do you want to restart now?", FormMessage.Yes, FormMessage.No)){
|
||||||
Program.Restart();
|
Program.Restart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,16 +42,14 @@
|
|||||||
//
|
//
|
||||||
// textBoxBrowserCSS
|
// textBoxBrowserCSS
|
||||||
//
|
//
|
||||||
this.textBoxBrowserCSS.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
this.textBoxBrowserCSS.Dock = System.Windows.Forms.DockStyle.Bottom;
|
||||||
| System.Windows.Forms.AnchorStyles.Left)
|
|
||||||
| System.Windows.Forms.AnchorStyles.Right)));
|
|
||||||
this.textBoxBrowserCSS.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
this.textBoxBrowserCSS.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
||||||
this.textBoxBrowserCSS.Location = new System.Drawing.Point(0, 16);
|
this.textBoxBrowserCSS.Location = new System.Drawing.Point(0, 16);
|
||||||
this.textBoxBrowserCSS.Margin = new System.Windows.Forms.Padding(0, 3, 0, 0);
|
this.textBoxBrowserCSS.Margin = new System.Windows.Forms.Padding(0, 3, 0, 0);
|
||||||
this.textBoxBrowserCSS.Multiline = true;
|
this.textBoxBrowserCSS.Multiline = true;
|
||||||
this.textBoxBrowserCSS.Name = "textBoxBrowserCSS";
|
this.textBoxBrowserCSS.Name = "textBoxBrowserCSS";
|
||||||
this.textBoxBrowserCSS.ScrollBars = System.Windows.Forms.ScrollBars.Both;
|
this.textBoxBrowserCSS.ScrollBars = System.Windows.Forms.ScrollBars.Both;
|
||||||
this.textBoxBrowserCSS.Size = new System.Drawing.Size(373, 253);
|
this.textBoxBrowserCSS.Size = new System.Drawing.Size(378, 253);
|
||||||
this.textBoxBrowserCSS.TabIndex = 1;
|
this.textBoxBrowserCSS.TabIndex = 1;
|
||||||
this.textBoxBrowserCSS.WordWrap = false;
|
this.textBoxBrowserCSS.WordWrap = false;
|
||||||
this.textBoxBrowserCSS.KeyUp += new System.Windows.Forms.KeyEventHandler(this.textBoxBrowserCSS_KeyUp);
|
this.textBoxBrowserCSS.KeyUp += new System.Windows.Forms.KeyEventHandler(this.textBoxBrowserCSS_KeyUp);
|
||||||
@@ -100,7 +98,7 @@
|
|||||||
this.splitContainer.Panel2.Controls.Add(this.textBoxNotificationCSS);
|
this.splitContainer.Panel2.Controls.Add(this.textBoxNotificationCSS);
|
||||||
this.splitContainer.Panel2MinSize = 64;
|
this.splitContainer.Panel2MinSize = 64;
|
||||||
this.splitContainer.Size = new System.Drawing.Size(760, 269);
|
this.splitContainer.Size = new System.Drawing.Size(760, 269);
|
||||||
this.splitContainer.SplitterDistance = 373;
|
this.splitContainer.SplitterDistance = 378;
|
||||||
this.splitContainer.SplitterWidth = 5;
|
this.splitContainer.SplitterWidth = 5;
|
||||||
this.splitContainer.TabIndex = 0;
|
this.splitContainer.TabIndex = 0;
|
||||||
//
|
//
|
||||||
@@ -126,16 +124,14 @@
|
|||||||
//
|
//
|
||||||
// textBoxNotificationCSS
|
// textBoxNotificationCSS
|
||||||
//
|
//
|
||||||
this.textBoxNotificationCSS.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
this.textBoxNotificationCSS.Dock = System.Windows.Forms.DockStyle.Bottom;
|
||||||
| System.Windows.Forms.AnchorStyles.Left)
|
|
||||||
| System.Windows.Forms.AnchorStyles.Right)));
|
|
||||||
this.textBoxNotificationCSS.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
this.textBoxNotificationCSS.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
||||||
this.textBoxNotificationCSS.Location = new System.Drawing.Point(0, 16);
|
this.textBoxNotificationCSS.Location = new System.Drawing.Point(0, 16);
|
||||||
this.textBoxNotificationCSS.Margin = new System.Windows.Forms.Padding(0, 3, 0, 0);
|
this.textBoxNotificationCSS.Margin = new System.Windows.Forms.Padding(0, 3, 0, 0);
|
||||||
this.textBoxNotificationCSS.Multiline = true;
|
this.textBoxNotificationCSS.Multiline = true;
|
||||||
this.textBoxNotificationCSS.Name = "textBoxNotificationCSS";
|
this.textBoxNotificationCSS.Name = "textBoxNotificationCSS";
|
||||||
this.textBoxNotificationCSS.ScrollBars = System.Windows.Forms.ScrollBars.Both;
|
this.textBoxNotificationCSS.ScrollBars = System.Windows.Forms.ScrollBars.Both;
|
||||||
this.textBoxNotificationCSS.Size = new System.Drawing.Size(372, 253);
|
this.textBoxNotificationCSS.Size = new System.Drawing.Size(377, 253);
|
||||||
this.textBoxNotificationCSS.TabIndex = 1;
|
this.textBoxNotificationCSS.TabIndex = 1;
|
||||||
this.textBoxNotificationCSS.WordWrap = false;
|
this.textBoxNotificationCSS.WordWrap = false;
|
||||||
//
|
//
|
||||||
@@ -152,7 +148,6 @@
|
|||||||
// btnOpenWiki
|
// btnOpenWiki
|
||||||
//
|
//
|
||||||
this.btnOpenWiki.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
this.btnOpenWiki.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||||
this.btnOpenWiki.AutoSize = true;
|
|
||||||
this.btnOpenWiki.Location = new System.Drawing.Point(12, 287);
|
this.btnOpenWiki.Location = new System.Drawing.Point(12, 287);
|
||||||
this.btnOpenWiki.Name = "btnOpenWiki";
|
this.btnOpenWiki.Name = "btnOpenWiki";
|
||||||
this.btnOpenWiki.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
this.btnOpenWiki.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||||
|
|||||||
@@ -69,7 +69,6 @@
|
|||||||
// btnHelp
|
// btnHelp
|
||||||
//
|
//
|
||||||
this.btnHelp.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
this.btnHelp.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||||
this.btnHelp.AutoSize = true;
|
|
||||||
this.btnHelp.Location = new System.Drawing.Point(12, 227);
|
this.btnHelp.Location = new System.Drawing.Point(12, 227);
|
||||||
this.btnHelp.Name = "btnHelp";
|
this.btnHelp.Name = "btnHelp";
|
||||||
this.btnHelp.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
this.btnHelp.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
using TweetDuck.Data;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Other.Settings.Dialogs{
|
namespace TweetDuck.Core.Other.Settings.Dialogs{
|
||||||
sealed partial class DialogSettingsCefArgs : Form{
|
sealed partial class DialogSettingsCefArgs : Form{
|
||||||
@@ -30,10 +31,10 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
return;
|
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?";
|
string prompt = count == 0 && !string.IsNullOrWhiteSpace(prevArgs) ? "All current arguments will be removed. Continue?" : count+(count == 1 ? " argument was" : " arguments were")+" detected. Continue?";
|
||||||
|
|
||||||
if (MessageBox.Show(prompt, "Confirm CEF Arguments", MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.OK){
|
if (FormMessage.Question("Confirm CEF Arguments", prompt, FormMessage.OK, FormMessage.Cancel)){
|
||||||
DialogResult = DialogResult.OK;
|
DialogResult = DialogResult.OK;
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,6 @@
|
|||||||
// btnCancel
|
// btnCancel
|
||||||
//
|
//
|
||||||
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
this.btnCancel.AutoSize = true;
|
|
||||||
this.btnCancel.Location = new System.Drawing.Point(176, 97);
|
this.btnCancel.Location = new System.Drawing.Point(176, 97);
|
||||||
this.btnCancel.Name = "btnCancel";
|
this.btnCancel.Name = "btnCancel";
|
||||||
this.btnCancel.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
this.btnCancel.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||||
@@ -56,7 +55,6 @@
|
|||||||
//
|
//
|
||||||
this.btnContinue.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
this.btnContinue.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
this.btnContinue.AutoSize = true;
|
this.btnContinue.AutoSize = true;
|
||||||
this.btnContinue.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
|
|
||||||
this.btnContinue.Enabled = false;
|
this.btnContinue.Enabled = false;
|
||||||
this.btnContinue.Location = new System.Drawing.Point(125, 97);
|
this.btnContinue.Location = new System.Drawing.Point(125, 97);
|
||||||
this.btnContinue.Name = "btnContinue";
|
this.btnContinue.Name = "btnContinue";
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using TweetDuck.Configuration;
|
||||||
using TweetDuck.Core.Other.Settings.Export;
|
using TweetDuck.Core.Other.Settings.Export;
|
||||||
using TweetDuck.Plugins;
|
using TweetDuck.Plugins;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Other.Settings.Dialogs{
|
namespace TweetDuck.Core.Other.Settings.Dialogs{
|
||||||
sealed partial class DialogSettingsManage : Form{
|
sealed partial class DialogSettingsManage : Form{
|
||||||
private enum State{
|
private enum State{
|
||||||
Deciding, Import, Export
|
Deciding, Reset, Import, Export
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExportFileFlags Flags{
|
public ExportFileFlags Flags{
|
||||||
@@ -20,7 +22,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ShouldReloadUI { get; private set; }
|
public bool ShouldReloadBrowser { get; private set; }
|
||||||
|
|
||||||
private readonly PluginManager plugins;
|
private readonly PluginManager plugins;
|
||||||
private State currentState;
|
private State currentState;
|
||||||
@@ -58,15 +60,10 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
case State.Deciding:
|
case State.Deciding:
|
||||||
// Reset
|
// Reset
|
||||||
if (radioReset.Checked){
|
if (radioReset.Checked){
|
||||||
if (MessageBox.Show("This will reset all of your program options. Plugins will not be affected. Do you want to proceed?", "Reset "+Program.BrandName+" Options", MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == DialogResult.Yes){
|
currentState = State.Reset;
|
||||||
Program.ResetConfig();
|
|
||||||
|
|
||||||
ShouldReloadUI = true;
|
Text = "Restore Defaults";
|
||||||
DialogResult = DialogResult.OK;
|
Flags = ExportFileFlags.Config;
|
||||||
Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import
|
// Import
|
||||||
@@ -74,8 +71,8 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
using(OpenFileDialog dialog = new OpenFileDialog{
|
using(OpenFileDialog dialog = new OpenFileDialog{
|
||||||
AutoUpgradeEnabled = true,
|
AutoUpgradeEnabled = true,
|
||||||
DereferenceLinks = true,
|
DereferenceLinks = true,
|
||||||
Title = "Import "+Program.BrandName+" Profile",
|
Title = "Import TweetDuck Profile",
|
||||||
Filter = Program.BrandName+" Profile (*.tdsettings)|*.tdsettings"
|
Filter = "TweetDuck Profile (*.tdsettings)|*.tdsettings"
|
||||||
}){
|
}){
|
||||||
if (dialog.ShowDialog() != DialogResult.OK){
|
if (dialog.ShowDialog() != DialogResult.OK){
|
||||||
return;
|
return;
|
||||||
@@ -109,14 +106,47 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
panelExport.Visible = true;
|
panelExport.Visible = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case State.Reset:
|
||||||
|
if (FormMessage.Warning("Reset TweetDuck Options", "This will reset the selected items. Are you sure you want to proceed?", FormMessage.Yes, FormMessage.No)){
|
||||||
|
if (Flags.HasFlag(ExportFileFlags.Config)){
|
||||||
|
Program.ResetConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Flags.HasFlag(ExportFileFlags.PluginData)){
|
||||||
|
try{
|
||||||
|
File.Delete(Program.PluginConfigFilePath);
|
||||||
|
Directory.Delete(Program.PluginDataPath, true);
|
||||||
|
}catch(Exception ex){
|
||||||
|
Program.Reporter.HandleException("Plugin Data Reset Error", "Could not delete plugin data.", true, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Flags.HasFlag(ExportFileFlags.Session)){
|
||||||
|
Program.Restart(Arguments.ArgDeleteCookies);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
ShouldReloadBrowser = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DialogResult = DialogResult.OK;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
case State.Import:
|
case State.Import:
|
||||||
if (importManager.Import(Flags)){
|
if (importManager.Import(Flags)){
|
||||||
if (!importManager.IsRestarting){
|
Program.UserConfig.Reload();
|
||||||
ShouldReloadUI = true;
|
|
||||||
|
if (importManager.IsRestarting){
|
||||||
|
Program.Restart(Arguments.ArgImportCookies);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
ShouldReloadBrowser = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
Program.Reporter.HandleException("Profile Import Error", "An exception happened while importing "+Program.BrandName+" profile.", true, importManager.LastException);
|
Program.Reporter.HandleException("Profile Import Error", "An exception happened while importing TweetDuck profile.", true, importManager.LastException);
|
||||||
}
|
}
|
||||||
|
|
||||||
DialogResult = DialogResult.OK;
|
DialogResult = DialogResult.OK;
|
||||||
@@ -129,9 +159,9 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
AutoUpgradeEnabled = true,
|
AutoUpgradeEnabled = true,
|
||||||
OverwritePrompt = true,
|
OverwritePrompt = true,
|
||||||
DefaultExt = "tdsettings",
|
DefaultExt = "tdsettings",
|
||||||
FileName = Program.BrandName+".tdsettings",
|
FileName = "TweetDuck.tdsettings",
|
||||||
Title = "Export "+Program.BrandName+" Profile",
|
Title = "Export TweetDuck Profile",
|
||||||
Filter = Program.BrandName+" Profile (*.tdsettings)|*.tdsettings"
|
Filter = "TweetDuck Profile (*.tdsettings)|*.tdsettings"
|
||||||
}){
|
}){
|
||||||
if (dialog.ShowDialog() != DialogResult.OK){
|
if (dialog.ShowDialog() != DialogResult.OK){
|
||||||
return;
|
return;
|
||||||
@@ -144,7 +174,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
ExportManager manager = new ExportManager(file, plugins);
|
ExportManager manager = new ExportManager(file, plugins);
|
||||||
|
|
||||||
if (!manager.Export(Flags)){
|
if (!manager.Export(Flags)){
|
||||||
Program.Reporter.HandleException("Profile Export Error", "An exception happened while exporting "+Program.BrandName+" profile.", true, manager.LastException);
|
Program.Reporter.HandleException("Profile Export Error", "An exception happened while exporting TweetDuck profile.", true, manager.LastException);
|
||||||
}
|
}
|
||||||
|
|
||||||
DialogResult = DialogResult.OK;
|
DialogResult = DialogResult.OK;
|
||||||
@@ -165,6 +195,9 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
if (currentState == State.Import){
|
if (currentState == State.Import){
|
||||||
btnContinue.Text = selectedFlags.HasFlag(ExportFileFlags.Session) ? "Import && Restart" : "Import Profile";
|
btnContinue.Text = selectedFlags.HasFlag(ExportFileFlags.Session) ? "Import && Restart" : "Import Profile";
|
||||||
}
|
}
|
||||||
|
else if (currentState == State.Reset){
|
||||||
|
btnContinue.Text = selectedFlags.HasFlag(ExportFileFlags.Session) ? "Restore && Restart" : "Restore Defaults";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,21 +29,22 @@
|
|||||||
this.cbLogging = new System.Windows.Forms.CheckBox();
|
this.cbLogging = new System.Windows.Forms.CheckBox();
|
||||||
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
||||||
this.cbDebugUpdates = new System.Windows.Forms.CheckBox();
|
this.cbDebugUpdates = new System.Windows.Forms.CheckBox();
|
||||||
this.labelLocale = new System.Windows.Forms.Label();
|
|
||||||
this.comboLocale = new System.Windows.Forms.ComboBox();
|
this.comboLocale = new System.Windows.Forms.ComboBox();
|
||||||
this.labelDataFolder = new System.Windows.Forms.Label();
|
|
||||||
this.tbDataFolder = new System.Windows.Forms.TextBox();
|
this.tbDataFolder = new System.Windows.Forms.TextBox();
|
||||||
|
this.tbShortcutTarget = new System.Windows.Forms.TextBox();
|
||||||
|
this.labelLocale = new System.Windows.Forms.Label();
|
||||||
|
this.labelDataFolder = new System.Windows.Forms.Label();
|
||||||
|
this.labelShortcutTarget = new System.Windows.Forms.Label();
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
//
|
//
|
||||||
// btnCancel
|
// btnCancel
|
||||||
//
|
//
|
||||||
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
this.btnCancel.AutoSize = true;
|
this.btnCancel.Location = new System.Drawing.Point(216, 217);
|
||||||
this.btnCancel.Location = new System.Drawing.Point(160, 171);
|
|
||||||
this.btnCancel.Name = "btnCancel";
|
this.btnCancel.Name = "btnCancel";
|
||||||
this.btnCancel.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
this.btnCancel.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||||
this.btnCancel.Size = new System.Drawing.Size(56, 23);
|
this.btnCancel.Size = new System.Drawing.Size(56, 23);
|
||||||
this.btnCancel.TabIndex = 7;
|
this.btnCancel.TabIndex = 9;
|
||||||
this.btnCancel.Text = "Cancel";
|
this.btnCancel.Text = "Cancel";
|
||||||
this.btnCancel.UseVisualStyleBackColor = true;
|
this.btnCancel.UseVisualStyleBackColor = true;
|
||||||
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
|
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
|
||||||
@@ -51,12 +52,11 @@
|
|||||||
// btnRestart
|
// btnRestart
|
||||||
//
|
//
|
||||||
this.btnRestart.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
this.btnRestart.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
this.btnRestart.AutoSize = true;
|
this.btnRestart.Location = new System.Drawing.Point(153, 217);
|
||||||
this.btnRestart.Location = new System.Drawing.Point(97, 171);
|
|
||||||
this.btnRestart.Name = "btnRestart";
|
this.btnRestart.Name = "btnRestart";
|
||||||
this.btnRestart.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
this.btnRestart.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||||
this.btnRestart.Size = new System.Drawing.Size(57, 23);
|
this.btnRestart.Size = new System.Drawing.Size(57, 23);
|
||||||
this.btnRestart.TabIndex = 6;
|
this.btnRestart.TabIndex = 8;
|
||||||
this.btnRestart.Text = "Restart";
|
this.btnRestart.Text = "Restart";
|
||||||
this.btnRestart.UseVisualStyleBackColor = true;
|
this.btnRestart.UseVisualStyleBackColor = true;
|
||||||
this.btnRestart.Click += new System.EventHandler(this.btnRestart_Click);
|
this.btnRestart.Click += new System.EventHandler(this.btnRestart_Click);
|
||||||
@@ -69,7 +69,7 @@
|
|||||||
this.cbLogging.Size = new System.Drawing.Size(64, 17);
|
this.cbLogging.Size = new System.Drawing.Size(64, 17);
|
||||||
this.cbLogging.TabIndex = 0;
|
this.cbLogging.TabIndex = 0;
|
||||||
this.cbLogging.Text = "Logging";
|
this.cbLogging.Text = "Logging";
|
||||||
this.toolTip.SetToolTip(this.cbLogging, "Logging JavaScript output into a\r\ndebug.txt file in the data folder.");
|
this.toolTip.SetToolTip(this.cbLogging, "Logging JavaScript output into TD_Console.txt file in the data folder.");
|
||||||
this.cbLogging.UseVisualStyleBackColor = true;
|
this.cbLogging.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
// cbDebugUpdates
|
// cbDebugUpdates
|
||||||
@@ -83,6 +83,40 @@
|
|||||||
this.toolTip.SetToolTip(this.cbDebugUpdates, "Allows updating to pre-releases.");
|
this.toolTip.SetToolTip(this.cbDebugUpdates, "Allows updating to pre-releases.");
|
||||||
this.cbDebugUpdates.UseVisualStyleBackColor = true;
|
this.cbDebugUpdates.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
|
// comboLocale
|
||||||
|
//
|
||||||
|
this.comboLocale.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.comboLocale.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||||
|
this.comboLocale.FormattingEnabled = true;
|
||||||
|
this.comboLocale.Location = new System.Drawing.Point(15, 83);
|
||||||
|
this.comboLocale.Name = "comboLocale";
|
||||||
|
this.comboLocale.Size = new System.Drawing.Size(257, 21);
|
||||||
|
this.comboLocale.TabIndex = 3;
|
||||||
|
this.toolTip.SetToolTip(this.comboLocale, "Language used for spell checking.");
|
||||||
|
//
|
||||||
|
// tbDataFolder
|
||||||
|
//
|
||||||
|
this.tbDataFolder.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.tbDataFolder.Location = new System.Drawing.Point(15, 135);
|
||||||
|
this.tbDataFolder.Name = "tbDataFolder";
|
||||||
|
this.tbDataFolder.Size = new System.Drawing.Size(257, 20);
|
||||||
|
this.tbDataFolder.TabIndex = 5;
|
||||||
|
this.toolTip.SetToolTip(this.tbDataFolder, "Path to the data folder. Must be either an absolute path,\r\nor a simple folder name that will be created in LocalAppData.");
|
||||||
|
//
|
||||||
|
// tbShortcutTarget
|
||||||
|
//
|
||||||
|
this.tbShortcutTarget.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.tbShortcutTarget.Cursor = System.Windows.Forms.Cursors.Hand;
|
||||||
|
this.tbShortcutTarget.Location = new System.Drawing.Point(15, 186);
|
||||||
|
this.tbShortcutTarget.Name = "tbShortcutTarget";
|
||||||
|
this.tbShortcutTarget.ReadOnly = true;
|
||||||
|
this.tbShortcutTarget.Size = new System.Drawing.Size(257, 20);
|
||||||
|
this.tbShortcutTarget.TabIndex = 7;
|
||||||
|
this.tbShortcutTarget.Click += new System.EventHandler(this.tbShortcutTarget_Click);
|
||||||
|
//
|
||||||
// labelLocale
|
// labelLocale
|
||||||
//
|
//
|
||||||
this.labelLocale.AutoSize = true;
|
this.labelLocale.AutoSize = true;
|
||||||
@@ -93,16 +127,6 @@
|
|||||||
this.labelLocale.TabIndex = 2;
|
this.labelLocale.TabIndex = 2;
|
||||||
this.labelLocale.Text = "Locale";
|
this.labelLocale.Text = "Locale";
|
||||||
//
|
//
|
||||||
// comboLocale
|
|
||||||
//
|
|
||||||
this.comboLocale.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
|
||||||
| System.Windows.Forms.AnchorStyles.Right)));
|
|
||||||
this.comboLocale.FormattingEnabled = true;
|
|
||||||
this.comboLocale.Location = new System.Drawing.Point(15, 83);
|
|
||||||
this.comboLocale.Name = "comboLocale";
|
|
||||||
this.comboLocale.Size = new System.Drawing.Size(201, 21);
|
|
||||||
this.comboLocale.TabIndex = 3;
|
|
||||||
//
|
|
||||||
// labelDataFolder
|
// labelDataFolder
|
||||||
//
|
//
|
||||||
this.labelDataFolder.AutoSize = true;
|
this.labelDataFolder.AutoSize = true;
|
||||||
@@ -113,20 +137,23 @@
|
|||||||
this.labelDataFolder.TabIndex = 4;
|
this.labelDataFolder.TabIndex = 4;
|
||||||
this.labelDataFolder.Text = "Data Folder";
|
this.labelDataFolder.Text = "Data Folder";
|
||||||
//
|
//
|
||||||
// tbDataFolder
|
// labelShortcutTarget
|
||||||
//
|
//
|
||||||
this.tbDataFolder.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
this.labelShortcutTarget.AutoSize = true;
|
||||||
| System.Windows.Forms.AnchorStyles.Right)));
|
this.labelShortcutTarget.Location = new System.Drawing.Point(12, 170);
|
||||||
this.tbDataFolder.Location = new System.Drawing.Point(15, 135);
|
this.labelShortcutTarget.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
|
||||||
this.tbDataFolder.Name = "tbDataFolder";
|
this.labelShortcutTarget.Name = "labelShortcutTarget";
|
||||||
this.tbDataFolder.Size = new System.Drawing.Size(201, 20);
|
this.labelShortcutTarget.Size = new System.Drawing.Size(155, 13);
|
||||||
this.tbDataFolder.TabIndex = 5;
|
this.labelShortcutTarget.TabIndex = 6;
|
||||||
|
this.labelShortcutTarget.Text = "Shortcut Target (click to select)";
|
||||||
//
|
//
|
||||||
// DialogSettingsRestart
|
// DialogSettingsRestart
|
||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
this.ClientSize = new System.Drawing.Size(228, 206);
|
this.ClientSize = new System.Drawing.Size(284, 252);
|
||||||
|
this.Controls.Add(this.tbShortcutTarget);
|
||||||
|
this.Controls.Add(this.labelShortcutTarget);
|
||||||
this.Controls.Add(this.tbDataFolder);
|
this.Controls.Add(this.tbDataFolder);
|
||||||
this.Controls.Add(this.labelDataFolder);
|
this.Controls.Add(this.labelDataFolder);
|
||||||
this.Controls.Add(this.comboLocale);
|
this.Controls.Add(this.comboLocale);
|
||||||
@@ -157,5 +184,7 @@
|
|||||||
private System.Windows.Forms.ComboBox comboLocale;
|
private System.Windows.Forms.ComboBox comboLocale;
|
||||||
private System.Windows.Forms.Label labelDataFolder;
|
private System.Windows.Forms.Label labelDataFolder;
|
||||||
private System.Windows.Forms.TextBox tbDataFolder;
|
private System.Windows.Forms.TextBox tbDataFolder;
|
||||||
|
private System.Windows.Forms.TextBox tbShortcutTarget;
|
||||||
|
private System.Windows.Forms.Label labelShortcutTarget;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Configuration;
|
using TweetDuck.Configuration;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Data;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Other.Settings.Dialogs{
|
namespace TweetDuck.Core.Other.Settings.Dialogs{
|
||||||
sealed partial class DialogSettingsRestart : Form{
|
sealed partial class DialogSettingsRestart : Form{
|
||||||
@@ -24,12 +24,26 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
cbLogging.Checked = currentArgs.HasFlag(Arguments.ArgLogging);
|
cbLogging.Checked = currentArgs.HasFlag(Arguments.ArgLogging);
|
||||||
cbDebugUpdates.Checked = currentArgs.HasFlag(Arguments.ArgDebugUpdates);
|
cbDebugUpdates.Checked = currentArgs.HasFlag(Arguments.ArgDebugUpdates);
|
||||||
comboLocale.SelectedItem = currentArgs.GetValue(Arguments.ArgLocale, DefaultLocale);
|
comboLocale.SelectedItem = currentArgs.GetValue(Arguments.ArgLocale, DefaultLocale);
|
||||||
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, new EventArgs());
|
||||||
|
|
||||||
Text = Program.BrandName+" Arguments";
|
Text = Program.BrandName+" Arguments";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void btnRestart_Click(object sender, EventArgs e){
|
private void control_Change(object sender, EventArgs e){
|
||||||
Args = new CommandLineArgs();
|
Args = new CommandLineArgs();
|
||||||
|
|
||||||
if (cbLogging.Checked){
|
if (cbLogging.Checked){
|
||||||
@@ -46,10 +60,21 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
Args.SetValue(Arguments.ArgLocale, locale);
|
Args.SetValue(Arguments.ArgLocale, locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(tbDataFolder.Text)){
|
if (!string.IsNullOrWhiteSpace(tbDataFolder.Text) && tbDataFolder.Enabled){
|
||||||
Args.SetValue(Arguments.ArgDataFolder, tbDataFolder.Text);
|
Args.SetValue(Arguments.ArgDataFolder, tbDataFolder.Text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tbShortcutTarget.Text = $@"""{Application.ExecutablePath}""{(Args.Count > 0 ? " " : "")}{Args}";
|
||||||
|
tbShortcutTarget.Select(tbShortcutTarget.Text.Length, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tbShortcutTarget_Click(object sender, EventArgs e){
|
||||||
|
if (tbShortcutTarget.SelectionLength == 0){
|
||||||
|
tbShortcutTarget.SelectAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btnRestart_Click(object sender, EventArgs e){
|
||||||
DialogResult = DialogResult.OK;
|
DialogResult = DialogResult.OK;
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Windows.Forms;
|
using TweetDuck.Data;
|
||||||
using TweetDuck.Configuration;
|
|
||||||
using TweetDuck.Plugins;
|
using TweetDuck.Plugins;
|
||||||
using TweetDuck.Plugins.Enums;
|
using TweetDuck.Plugins.Enums;
|
||||||
|
|
||||||
@@ -38,7 +37,7 @@ namespace TweetDuck.Core.Other.Settings.Export{
|
|||||||
try{
|
try{
|
||||||
stream.WriteFile(new string[]{ "plugin.data", plugin.Identifier, path.Relative }, path.Full);
|
stream.WriteFile(new string[]{ "plugin.data", plugin.Identifier, path.Relative }, path.Full);
|
||||||
}catch(ArgumentOutOfRangeException e){
|
}catch(ArgumentOutOfRangeException e){
|
||||||
MessageBox.Show("Could not include a plugin file in the export. "+e.Message, "Export Profile", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
FormMessage.Warning("Export Profile", "Could not include a plugin file in the export. "+e.Message, FormMessage.OK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,14 +137,7 @@ namespace TweetDuck.Core.Other.Settings.Export{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (missingPlugins.Count > 0){
|
if (missingPlugins.Count > 0){
|
||||||
MessageBox.Show("Detected missing plugins when importing plugin data:"+Environment.NewLine+string.Join(Environment.NewLine, missingPlugins), "Importing "+Program.BrandName+" Profile", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
FormMessage.Information("Importing TweetDuck Profile", "Detected missing plugins when importing plugin data:\n"+string.Join("\n", missingPlugins), FormMessage.OK);
|
||||||
}
|
|
||||||
|
|
||||||
if (IsRestarting){
|
|
||||||
Program.Restart(new string[]{ Arguments.ArgImportCookies });
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
Program.ReloadConfig();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -169,6 +161,16 @@ namespace TweetDuck.Core.Other.Settings.Export{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void DeleteCookies(){
|
||||||
|
try{
|
||||||
|
if (File.Exists(CookiesPath)){
|
||||||
|
File.Delete(CookiesPath);
|
||||||
|
}
|
||||||
|
}catch(Exception e){
|
||||||
|
Program.Reporter.HandleException("Session Reset Error", "Could not remove the cookie file to reset the login session.", true, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static IEnumerable<PathInfo> EnumerateFilesRelative(string root){
|
private static IEnumerable<PathInfo> EnumerateFilesRelative(string root){
|
||||||
return Directory.Exists(root) ? Directory.EnumerateFiles(root, "*.*", SearchOption.AllDirectories).Select(fullPath => new PathInfo{
|
return Directory.Exists(root) ? Directory.EnumerateFiles(root, "*.*", SearchOption.AllDirectories).Select(fullPath => new PathInfo{
|
||||||
Full = fullPath,
|
Full = fullPath,
|
||||||
@@ -176,7 +178,7 @@ namespace TweetDuck.Core.Other.Settings.Export{
|
|||||||
}) : Enumerable.Empty<PathInfo>();
|
}) : Enumerable.Empty<PathInfo>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class PathInfo{
|
private sealed class PathInfo{
|
||||||
public string Full { get; set; }
|
public string Full { get; set; }
|
||||||
public string Relative { get; set; }
|
public string Relative { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
67
Core/Other/Settings/TabSettingsAdvanced.Designer.cs
generated
67
Core/Other/Settings/TabSettingsAdvanced.Designer.cs
generated
@@ -33,12 +33,16 @@
|
|||||||
this.btnRestart = new System.Windows.Forms.Button();
|
this.btnRestart = new System.Windows.Forms.Button();
|
||||||
this.btnOpenAppFolder = new System.Windows.Forms.Button();
|
this.btnOpenAppFolder = new System.Windows.Forms.Button();
|
||||||
this.btnOpenDataFolder = new System.Windows.Forms.Button();
|
this.btnOpenDataFolder = new System.Windows.Forms.Button();
|
||||||
|
this.numMemoryThreshold = new TweetDuck.Core.Controls.NumericUpDownEx();
|
||||||
|
this.checkBrowserGCReload = new System.Windows.Forms.CheckBox();
|
||||||
this.labelApp = new System.Windows.Forms.Label();
|
this.labelApp = new System.Windows.Forms.Label();
|
||||||
this.panelApp = new System.Windows.Forms.Panel();
|
this.panelApp = new System.Windows.Forms.Panel();
|
||||||
this.labelPerformance = new System.Windows.Forms.Label();
|
this.labelPerformance = new System.Windows.Forms.Label();
|
||||||
this.panelPerformance = new System.Windows.Forms.Panel();
|
this.panelPerformance = new System.Windows.Forms.Panel();
|
||||||
|
this.labelMemoryUsage = new System.Windows.Forms.Label();
|
||||||
this.panelConfiguration = new System.Windows.Forms.Panel();
|
this.panelConfiguration = new System.Windows.Forms.Panel();
|
||||||
this.labelConfiguration = new System.Windows.Forms.Label();
|
this.labelConfiguration = new System.Windows.Forms.Label();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.numMemoryThreshold)).BeginInit();
|
||||||
this.panelApp.SuspendLayout();
|
this.panelApp.SuspendLayout();
|
||||||
this.panelPerformance.SuspendLayout();
|
this.panelPerformance.SuspendLayout();
|
||||||
this.panelConfiguration.SuspendLayout();
|
this.panelConfiguration.SuspendLayout();
|
||||||
@@ -52,8 +56,7 @@
|
|||||||
this.btnClearCache.Size = new System.Drawing.Size(144, 23);
|
this.btnClearCache.Size = new System.Drawing.Size(144, 23);
|
||||||
this.btnClearCache.TabIndex = 1;
|
this.btnClearCache.TabIndex = 1;
|
||||||
this.btnClearCache.Text = "Clear Cache (calculating)";
|
this.btnClearCache.Text = "Clear Cache (calculating)";
|
||||||
this.toolTip.SetToolTip(this.btnClearCache, "Clearing cache will free up space taken by downloaded images and other resources." +
|
this.toolTip.SetToolTip(this.btnClearCache, "Clearing cache will free up space taken by downloaded images and other resources.");
|
||||||
"");
|
|
||||||
this.btnClearCache.UseVisualStyleBackColor = true;
|
this.btnClearCache.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
// checkHardwareAcceleration
|
// checkHardwareAcceleration
|
||||||
@@ -65,8 +68,7 @@
|
|||||||
this.checkHardwareAcceleration.Size = new System.Drawing.Size(134, 17);
|
this.checkHardwareAcceleration.Size = new System.Drawing.Size(134, 17);
|
||||||
this.checkHardwareAcceleration.TabIndex = 0;
|
this.checkHardwareAcceleration.TabIndex = 0;
|
||||||
this.checkHardwareAcceleration.Text = "Hardware Acceleration";
|
this.checkHardwareAcceleration.Text = "Hardware Acceleration";
|
||||||
this.toolTip.SetToolTip(this.checkHardwareAcceleration, "Uses your graphics card to improve performance.\r\nDisable if you experience issues" +
|
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.");
|
||||||
" with rendering.");
|
|
||||||
this.checkHardwareAcceleration.UseVisualStyleBackColor = true;
|
this.checkHardwareAcceleration.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
// btnEditCefArgs
|
// btnEditCefArgs
|
||||||
@@ -107,8 +109,7 @@
|
|||||||
this.btnRestart.Size = new System.Drawing.Size(144, 23);
|
this.btnRestart.Size = new System.Drawing.Size(144, 23);
|
||||||
this.btnRestart.TabIndex = 2;
|
this.btnRestart.TabIndex = 2;
|
||||||
this.btnRestart.Text = "Restart the Program";
|
this.btnRestart.Text = "Restart the Program";
|
||||||
this.toolTip.SetToolTip(this.btnRestart, "Restarts the program using the same command\r\nline arguments that were used at lau" +
|
this.toolTip.SetToolTip(this.btnRestart, "Restarts the program using the same command\r\nline arguments that were used at launch.");
|
||||||
"nch.");
|
|
||||||
this.btnRestart.UseVisualStyleBackColor = true;
|
this.btnRestart.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
// btnOpenAppFolder
|
// btnOpenAppFolder
|
||||||
@@ -133,6 +134,35 @@
|
|||||||
this.toolTip.SetToolTip(this.btnOpenDataFolder, "Opens the folder where your profile data is located.");
|
this.toolTip.SetToolTip(this.btnOpenDataFolder, "Opens the folder where your profile data is located.");
|
||||||
this.btnOpenDataFolder.UseVisualStyleBackColor = true;
|
this.btnOpenDataFolder.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
|
// numMemoryThreshold
|
||||||
|
//
|
||||||
|
this.numMemoryThreshold.Increment = new decimal(new int[] {
|
||||||
|
50,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0});
|
||||||
|
this.numMemoryThreshold.Location = new System.Drawing.Point(202, 82);
|
||||||
|
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 = 400;
|
||||||
|
//
|
||||||
|
// checkBrowserGCReload
|
||||||
|
//
|
||||||
|
this.checkBrowserGCReload.AutoSize = true;
|
||||||
|
this.checkBrowserGCReload.Location = new System.Drawing.Point(6, 84);
|
||||||
|
this.checkBrowserGCReload.Margin = new System.Windows.Forms.Padding(6, 5, 3, 3);
|
||||||
|
this.checkBrowserGCReload.Name = "checkBrowserGCReload";
|
||||||
|
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 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
|
// labelApp
|
||||||
//
|
//
|
||||||
this.labelApp.AutoSize = true;
|
this.labelApp.AutoSize = true;
|
||||||
@@ -172,20 +202,33 @@
|
|||||||
//
|
//
|
||||||
this.panelPerformance.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
this.panelPerformance.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||||
| System.Windows.Forms.AnchorStyles.Right)));
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.panelPerformance.Controls.Add(this.checkBrowserGCReload);
|
||||||
|
this.panelPerformance.Controls.Add(this.numMemoryThreshold);
|
||||||
|
this.panelPerformance.Controls.Add(this.labelMemoryUsage);
|
||||||
this.panelPerformance.Controls.Add(this.checkHardwareAcceleration);
|
this.panelPerformance.Controls.Add(this.checkHardwareAcceleration);
|
||||||
this.panelPerformance.Controls.Add(this.btnClearCache);
|
this.panelPerformance.Controls.Add(this.btnClearCache);
|
||||||
this.panelPerformance.Location = new System.Drawing.Point(9, 137);
|
this.panelPerformance.Location = new System.Drawing.Point(9, 137);
|
||||||
this.panelPerformance.Name = "panelPerformance";
|
this.panelPerformance.Name = "panelPerformance";
|
||||||
this.panelPerformance.Size = new System.Drawing.Size(322, 54);
|
this.panelPerformance.Size = new System.Drawing.Size(322, 105);
|
||||||
this.panelPerformance.TabIndex = 3;
|
this.panelPerformance.TabIndex = 3;
|
||||||
//
|
//
|
||||||
|
// labelMemoryUsage
|
||||||
|
//
|
||||||
|
this.labelMemoryUsage.AutoSize = true;
|
||||||
|
this.labelMemoryUsage.Location = new System.Drawing.Point(3, 66);
|
||||||
|
this.labelMemoryUsage.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
|
||||||
|
this.labelMemoryUsage.Name = "labelMemoryUsage";
|
||||||
|
this.labelMemoryUsage.Size = new System.Drawing.Size(78, 13);
|
||||||
|
this.labelMemoryUsage.TabIndex = 2;
|
||||||
|
this.labelMemoryUsage.Text = "Memory Usage";
|
||||||
|
//
|
||||||
// panelConfiguration
|
// panelConfiguration
|
||||||
//
|
//
|
||||||
this.panelConfiguration.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
this.panelConfiguration.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||||
| System.Windows.Forms.AnchorStyles.Right)));
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
this.panelConfiguration.Controls.Add(this.btnEditCSS);
|
this.panelConfiguration.Controls.Add(this.btnEditCSS);
|
||||||
this.panelConfiguration.Controls.Add(this.btnEditCefArgs);
|
this.panelConfiguration.Controls.Add(this.btnEditCefArgs);
|
||||||
this.panelConfiguration.Location = new System.Drawing.Point(9, 238);
|
this.panelConfiguration.Location = new System.Drawing.Point(9, 289);
|
||||||
this.panelConfiguration.Name = "panelConfiguration";
|
this.panelConfiguration.Name = "panelConfiguration";
|
||||||
this.panelConfiguration.Size = new System.Drawing.Size(322, 29);
|
this.panelConfiguration.Size = new System.Drawing.Size(322, 29);
|
||||||
this.panelConfiguration.TabIndex = 5;
|
this.panelConfiguration.TabIndex = 5;
|
||||||
@@ -194,7 +237,7 @@
|
|||||||
//
|
//
|
||||||
this.labelConfiguration.AutoSize = true;
|
this.labelConfiguration.AutoSize = true;
|
||||||
this.labelConfiguration.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
this.labelConfiguration.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
||||||
this.labelConfiguration.Location = new System.Drawing.Point(6, 215);
|
this.labelConfiguration.Location = new System.Drawing.Point(6, 266);
|
||||||
this.labelConfiguration.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
|
this.labelConfiguration.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
|
||||||
this.labelConfiguration.Name = "labelConfiguration";
|
this.labelConfiguration.Name = "labelConfiguration";
|
||||||
this.labelConfiguration.Size = new System.Drawing.Size(104, 20);
|
this.labelConfiguration.Size = new System.Drawing.Size(104, 20);
|
||||||
@@ -212,7 +255,8 @@
|
|||||||
this.Controls.Add(this.panelApp);
|
this.Controls.Add(this.panelApp);
|
||||||
this.Controls.Add(this.labelApp);
|
this.Controls.Add(this.labelApp);
|
||||||
this.Name = "TabSettingsAdvanced";
|
this.Name = "TabSettingsAdvanced";
|
||||||
this.Size = new System.Drawing.Size(340, 277);
|
this.Size = new System.Drawing.Size(340, 328);
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.numMemoryThreshold)).EndInit();
|
||||||
this.panelApp.ResumeLayout(false);
|
this.panelApp.ResumeLayout(false);
|
||||||
this.panelPerformance.ResumeLayout(false);
|
this.panelPerformance.ResumeLayout(false);
|
||||||
this.panelPerformance.PerformLayout();
|
this.panelPerformance.PerformLayout();
|
||||||
@@ -239,5 +283,8 @@
|
|||||||
private System.Windows.Forms.Panel panelPerformance;
|
private System.Windows.Forms.Panel panelPerformance;
|
||||||
private System.Windows.Forms.Panel panelConfiguration;
|
private System.Windows.Forms.Panel panelConfiguration;
|
||||||
private System.Windows.Forms.Label labelConfiguration;
|
private System.Windows.Forms.Label labelConfiguration;
|
||||||
|
private System.Windows.Forms.Label labelMemoryUsage;
|
||||||
|
private Controls.NumericUpDownEx numMemoryThreshold;
|
||||||
|
private System.Windows.Forms.CheckBox checkBrowserGCReload;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ using TweetDuck.Core.Other.Settings.Dialogs;
|
|||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Other.Settings{
|
namespace TweetDuck.Core.Other.Settings{
|
||||||
partial class TabSettingsAdvanced : BaseTabSettings{
|
sealed partial class TabSettingsAdvanced : BaseTabSettings{
|
||||||
|
private static SystemConfig SysConfig => Program.SystemConfig;
|
||||||
|
|
||||||
private readonly Action<string> reinjectBrowserCSS;
|
private readonly Action<string> reinjectBrowserCSS;
|
||||||
|
|
||||||
public TabSettingsAdvanced(Action<string> reinjectBrowserCSS){
|
public TabSettingsAdvanced(Action<string> reinjectBrowserCSS){
|
||||||
@@ -16,13 +18,17 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
this.reinjectBrowserCSS = reinjectBrowserCSS;
|
this.reinjectBrowserCSS = reinjectBrowserCSS;
|
||||||
|
|
||||||
if (SystemConfig.IsHardwareAccelerationSupported){
|
if (SystemConfig.IsHardwareAccelerationSupported){
|
||||||
checkHardwareAcceleration.Checked = Program.SystemConfig.HardwareAcceleration;
|
checkHardwareAcceleration.Checked = SysConfig.HardwareAcceleration;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
checkHardwareAcceleration.Enabled = false;
|
checkHardwareAcceleration.Enabled = false;
|
||||||
checkHardwareAcceleration.Checked = false;
|
checkHardwareAcceleration.Checked = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkBrowserGCReload.Checked = SysConfig.EnableBrowserGCReload;
|
||||||
|
numMemoryThreshold.Enabled = checkBrowserGCReload.Checked;
|
||||||
|
numMemoryThreshold.SetValueSafe(SysConfig.BrowserMemoryThreshold);
|
||||||
|
|
||||||
BrowserCache.CalculateCacheSize(bytes => this.InvokeSafe(() => {
|
BrowserCache.CalculateCacheSize(bytes => this.InvokeSafe(() => {
|
||||||
if (bytes == -1L){
|
if (bytes == -1L){
|
||||||
btnClearCache.Text = "Clear Cache (unknown size)";
|
btnClearCache.Text = "Clear Cache (unknown size)";
|
||||||
@@ -37,6 +43,9 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
btnClearCache.Click += btnClearCache_Click;
|
btnClearCache.Click += btnClearCache_Click;
|
||||||
checkHardwareAcceleration.CheckedChanged += checkHardwareAcceleration_CheckedChanged;
|
checkHardwareAcceleration.CheckedChanged += checkHardwareAcceleration_CheckedChanged;
|
||||||
|
|
||||||
|
checkBrowserGCReload.CheckedChanged += checkBrowserGCReload_CheckedChanged;
|
||||||
|
numMemoryThreshold.ValueChanged += numMemoryThreshold_ValueChanged;
|
||||||
|
|
||||||
btnEditCefArgs.Click += btnEditCefArgs_Click;
|
btnEditCefArgs.Click += btnEditCefArgs_Click;
|
||||||
btnEditCSS.Click += btnEditCSS_Click;
|
btnEditCSS.Click += btnEditCSS_Click;
|
||||||
|
|
||||||
@@ -46,17 +55,28 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
btnRestartArgs.Click += btnRestartArgs_Click;
|
btnRestartArgs.Click += btnRestartArgs_Click;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void OnClosing(){
|
||||||
|
SysConfig.Save();
|
||||||
|
}
|
||||||
|
|
||||||
private void btnClearCache_Click(object sender, EventArgs e){
|
private void btnClearCache_Click(object sender, EventArgs e){
|
||||||
btnClearCache.Enabled = false;
|
btnClearCache.Enabled = false;
|
||||||
BrowserCache.SetClearOnExit();
|
BrowserCache.SetClearOnExit();
|
||||||
|
FormMessage.Information("Clear Cache", "Cache will be automatically cleared when TweetDuck exits.", FormMessage.OK);
|
||||||
MessageBox.Show("Cache will be automatically cleared when "+Program.BrandName+" exits.", "Clear Cache", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkHardwareAcceleration_CheckedChanged(object sender, EventArgs e){
|
private void checkHardwareAcceleration_CheckedChanged(object sender, EventArgs e){
|
||||||
Program.SystemConfig.HardwareAcceleration = checkHardwareAcceleration.Checked;
|
SysConfig.HardwareAcceleration = checkHardwareAcceleration.Checked;
|
||||||
Program.SystemConfig.Save();
|
PromptRestart(); // calls OnClosing
|
||||||
PromptRestart();
|
}
|
||||||
|
|
||||||
|
private void checkBrowserGCReload_CheckedChanged(object sender, EventArgs e){
|
||||||
|
SysConfig.EnableBrowserGCReload = checkBrowserGCReload.Checked;
|
||||||
|
numMemoryThreshold.Enabled = checkBrowserGCReload.Checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void numMemoryThreshold_ValueChanged(object sender, EventArgs e){
|
||||||
|
SysConfig.BrowserMemoryThreshold = (int)numMemoryThreshold.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void btnEditCefArgs_Click(object sender, EventArgs e){
|
private void btnEditCefArgs_Click(object sender, EventArgs e){
|
||||||
@@ -67,7 +87,7 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
};
|
};
|
||||||
|
|
||||||
form.FormClosed += (sender2, args2) => {
|
form.FormClosed += (sender2, args2) => {
|
||||||
NativeMethods.SetFormDisabled(ParentForm, false);
|
RestoreParentForm();
|
||||||
|
|
||||||
if (form.DialogResult == DialogResult.OK){
|
if (form.DialogResult == DialogResult.OK){
|
||||||
Config.CustomCefArgs = form.CefArgs;
|
Config.CustomCefArgs = form.CefArgs;
|
||||||
@@ -89,7 +109,7 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
};
|
};
|
||||||
|
|
||||||
form.FormClosed += (sender2, args2) => {
|
form.FormClosed += (sender2, args2) => {
|
||||||
NativeMethods.SetFormDisabled(ParentForm, false);
|
RestoreParentForm();
|
||||||
|
|
||||||
if (form.DialogResult == DialogResult.OK){
|
if (form.DialogResult == DialogResult.OK){
|
||||||
Config.CustomBrowserCSS = form.BrowserCSS;
|
Config.CustomBrowserCSS = form.BrowserCSS;
|
||||||
@@ -123,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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
57
Core/Other/Settings/TabSettingsGeneral.Designer.cs
generated
57
Core/Other/Settings/TabSettingsGeneral.Designer.cs
generated
@@ -43,6 +43,7 @@
|
|||||||
this.panelUpdates = new System.Windows.Forms.Panel();
|
this.panelUpdates = new System.Windows.Forms.Panel();
|
||||||
this.panelTray = new System.Windows.Forms.Panel();
|
this.panelTray = new System.Windows.Forms.Panel();
|
||||||
this.labelUpdates = new System.Windows.Forms.Label();
|
this.labelUpdates = new System.Windows.Forms.Label();
|
||||||
|
this.checkBestImageQuality = new System.Windows.Forms.CheckBox();
|
||||||
((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).BeginInit();
|
((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).BeginInit();
|
||||||
this.panelUI.SuspendLayout();
|
this.panelUI.SuspendLayout();
|
||||||
this.panelUpdates.SuspendLayout();
|
this.panelUpdates.SuspendLayout();
|
||||||
@@ -58,8 +59,7 @@
|
|||||||
this.checkExpandLinks.Size = new System.Drawing.Size(166, 17);
|
this.checkExpandLinks.Size = new System.Drawing.Size(166, 17);
|
||||||
this.checkExpandLinks.TabIndex = 0;
|
this.checkExpandLinks.TabIndex = 0;
|
||||||
this.checkExpandLinks.Text = "Expand Links When Hovered";
|
this.checkExpandLinks.Text = "Expand Links When Hovered";
|
||||||
this.toolTip.SetToolTip(this.checkExpandLinks, "Expands links inside the tweets. If disabled,\r\nthe full links show up in a toolti" +
|
this.toolTip.SetToolTip(this.checkExpandLinks, "Expands links inside the tweets. If disabled,\r\nthe full links show up in a tooltip instead.");
|
||||||
"p instead.");
|
|
||||||
this.checkExpandLinks.UseVisualStyleBackColor = true;
|
this.checkExpandLinks.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
// comboBoxTrayType
|
// comboBoxTrayType
|
||||||
@@ -82,18 +82,17 @@
|
|||||||
this.checkTrayHighlight.Size = new System.Drawing.Size(103, 17);
|
this.checkTrayHighlight.Size = new System.Drawing.Size(103, 17);
|
||||||
this.checkTrayHighlight.TabIndex = 2;
|
this.checkTrayHighlight.TabIndex = 2;
|
||||||
this.checkTrayHighlight.Text = "Enable Highlight";
|
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 po" +
|
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.");
|
||||||
"pup or audio notifications.\r\nThe icon resets when the main window is restored.");
|
|
||||||
this.checkTrayHighlight.UseVisualStyleBackColor = true;
|
this.checkTrayHighlight.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
// checkSpellCheck
|
// checkSpellCheck
|
||||||
//
|
//
|
||||||
this.checkSpellCheck.AutoSize = true;
|
this.checkSpellCheck.AutoSize = true;
|
||||||
this.checkSpellCheck.Location = new System.Drawing.Point(6, 51);
|
this.checkSpellCheck.Location = new System.Drawing.Point(6, 74);
|
||||||
this.checkSpellCheck.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
|
this.checkSpellCheck.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
|
||||||
this.checkSpellCheck.Name = "checkSpellCheck";
|
this.checkSpellCheck.Name = "checkSpellCheck";
|
||||||
this.checkSpellCheck.Size = new System.Drawing.Size(119, 17);
|
this.checkSpellCheck.Size = new System.Drawing.Size(119, 17);
|
||||||
this.checkSpellCheck.TabIndex = 2;
|
this.checkSpellCheck.TabIndex = 3;
|
||||||
this.checkSpellCheck.Text = "Enable Spell Check";
|
this.checkSpellCheck.Text = "Enable Spell Check";
|
||||||
this.toolTip.SetToolTip(this.checkSpellCheck, "Underlines words that are spelled incorrectly.");
|
this.toolTip.SetToolTip(this.checkSpellCheck, "Underlines words that are spelled incorrectly.");
|
||||||
this.checkSpellCheck.UseVisualStyleBackColor = true;
|
this.checkSpellCheck.UseVisualStyleBackColor = true;
|
||||||
@@ -107,8 +106,7 @@
|
|||||||
this.checkUpdateNotifications.Size = new System.Drawing.Size(165, 17);
|
this.checkUpdateNotifications.Size = new System.Drawing.Size(165, 17);
|
||||||
this.checkUpdateNotifications.TabIndex = 0;
|
this.checkUpdateNotifications.TabIndex = 0;
|
||||||
this.checkUpdateNotifications.Text = "Check Updates Automatically";
|
this.checkUpdateNotifications.Text = "Check Updates Automatically";
|
||||||
this.toolTip.SetToolTip(this.checkUpdateNotifications, "Checks for updates every hour.\r\nIf an update is dismissed, it will not appear aga" +
|
this.toolTip.SetToolTip(this.checkUpdateNotifications, "Checks for updates every hour.\r\nIf an update is dismissed, it will not appear again.");
|
||||||
"in.");
|
|
||||||
this.checkUpdateNotifications.UseVisualStyleBackColor = true;
|
this.checkUpdateNotifications.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
// btnCheckUpdates
|
// btnCheckUpdates
|
||||||
@@ -125,11 +123,11 @@
|
|||||||
// labelZoomValue
|
// labelZoomValue
|
||||||
//
|
//
|
||||||
this.labelZoomValue.BackColor = System.Drawing.Color.Transparent;
|
this.labelZoomValue.BackColor = System.Drawing.Color.Transparent;
|
||||||
this.labelZoomValue.Location = new System.Drawing.Point(141, 100);
|
this.labelZoomValue.Location = new System.Drawing.Point(147, 123);
|
||||||
this.labelZoomValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
|
this.labelZoomValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
|
||||||
this.labelZoomValue.Name = "labelZoomValue";
|
this.labelZoomValue.Name = "labelZoomValue";
|
||||||
this.labelZoomValue.Size = new System.Drawing.Size(38, 13);
|
this.labelZoomValue.Size = new System.Drawing.Size(38, 13);
|
||||||
this.labelZoomValue.TabIndex = 5;
|
this.labelZoomValue.TabIndex = 6;
|
||||||
this.labelZoomValue.Text = "100%";
|
this.labelZoomValue.Text = "100%";
|
||||||
this.labelZoomValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
|
this.labelZoomValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
|
||||||
this.toolTip.SetToolTip(this.labelZoomValue, "Changes the zoom level.\r\nAlso affects notifications and screenshots.");
|
this.toolTip.SetToolTip(this.labelZoomValue, "Changes the zoom level.\r\nAlso affects notifications and screenshots.");
|
||||||
@@ -143,8 +141,7 @@
|
|||||||
this.checkSwitchAccountSelectors.Size = new System.Drawing.Size(172, 17);
|
this.checkSwitchAccountSelectors.Size = new System.Drawing.Size(172, 17);
|
||||||
this.checkSwitchAccountSelectors.TabIndex = 1;
|
this.checkSwitchAccountSelectors.TabIndex = 1;
|
||||||
this.checkSwitchAccountSelectors.Text = "Shift Selects Multiple Accounts";
|
this.checkSwitchAccountSelectors.Text = "Shift Selects Multiple Accounts";
|
||||||
this.toolTip.SetToolTip(this.checkSwitchAccountSelectors, "When (re)tweeting, click to select a single account or hold Shift to\r\nselect mult" +
|
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.");
|
||||||
"iple accounts, instead of TweetDeck\'s default behavior.");
|
|
||||||
this.checkSwitchAccountSelectors.UseVisualStyleBackColor = true;
|
this.checkSwitchAccountSelectors.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
// labelTrayIcon
|
// labelTrayIcon
|
||||||
@@ -162,24 +159,24 @@
|
|||||||
this.trackBarZoom.AutoSize = false;
|
this.trackBarZoom.AutoSize = false;
|
||||||
this.trackBarZoom.BackColor = System.Drawing.SystemColors.Control;
|
this.trackBarZoom.BackColor = System.Drawing.SystemColors.Control;
|
||||||
this.trackBarZoom.LargeChange = 25;
|
this.trackBarZoom.LargeChange = 25;
|
||||||
this.trackBarZoom.Location = new System.Drawing.Point(3, 99);
|
this.trackBarZoom.Location = new System.Drawing.Point(3, 122);
|
||||||
this.trackBarZoom.Maximum = 200;
|
this.trackBarZoom.Maximum = 200;
|
||||||
this.trackBarZoom.Minimum = 50;
|
this.trackBarZoom.Minimum = 50;
|
||||||
this.trackBarZoom.Name = "trackBarZoom";
|
this.trackBarZoom.Name = "trackBarZoom";
|
||||||
this.trackBarZoom.Size = new System.Drawing.Size(148, 30);
|
this.trackBarZoom.Size = new System.Drawing.Size(148, 30);
|
||||||
this.trackBarZoom.SmallChange = 5;
|
this.trackBarZoom.SmallChange = 5;
|
||||||
this.trackBarZoom.TabIndex = 4;
|
this.trackBarZoom.TabIndex = 5;
|
||||||
this.trackBarZoom.TickFrequency = 25;
|
this.trackBarZoom.TickFrequency = 25;
|
||||||
this.trackBarZoom.Value = 100;
|
this.trackBarZoom.Value = 100;
|
||||||
//
|
//
|
||||||
// labelZoom
|
// labelZoom
|
||||||
//
|
//
|
||||||
this.labelZoom.AutoSize = true;
|
this.labelZoom.AutoSize = true;
|
||||||
this.labelZoom.Location = new System.Drawing.Point(3, 83);
|
this.labelZoom.Location = new System.Drawing.Point(3, 106);
|
||||||
this.labelZoom.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
|
this.labelZoom.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
|
||||||
this.labelZoom.Name = "labelZoom";
|
this.labelZoom.Name = "labelZoom";
|
||||||
this.labelZoom.Size = new System.Drawing.Size(34, 13);
|
this.labelZoom.Size = new System.Drawing.Size(34, 13);
|
||||||
this.labelZoom.TabIndex = 3;
|
this.labelZoom.TabIndex = 4;
|
||||||
this.labelZoom.Text = "Zoom";
|
this.labelZoom.Text = "Zoom";
|
||||||
//
|
//
|
||||||
// zoomUpdateTimer
|
// zoomUpdateTimer
|
||||||
@@ -202,22 +199,23 @@
|
|||||||
//
|
//
|
||||||
this.panelUI.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
this.panelUI.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||||
| System.Windows.Forms.AnchorStyles.Right)));
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.panelUI.Controls.Add(this.checkBestImageQuality);
|
||||||
this.panelUI.Controls.Add(this.checkExpandLinks);
|
this.panelUI.Controls.Add(this.checkExpandLinks);
|
||||||
this.panelUI.Controls.Add(this.checkSwitchAccountSelectors);
|
this.panelUI.Controls.Add(this.checkSwitchAccountSelectors);
|
||||||
this.panelUI.Controls.Add(this.checkSpellCheck);
|
this.panelUI.Controls.Add(this.checkSpellCheck);
|
||||||
this.panelUI.Controls.Add(this.labelZoom);
|
this.panelUI.Controls.Add(this.labelZoom);
|
||||||
this.panelUI.Controls.Add(this.labelZoomValue);
|
|
||||||
this.panelUI.Controls.Add(this.trackBarZoom);
|
this.panelUI.Controls.Add(this.trackBarZoom);
|
||||||
|
this.panelUI.Controls.Add(this.labelZoomValue);
|
||||||
this.panelUI.Location = new System.Drawing.Point(9, 31);
|
this.panelUI.Location = new System.Drawing.Point(9, 31);
|
||||||
this.panelUI.Name = "panelUI";
|
this.panelUI.Name = "panelUI";
|
||||||
this.panelUI.Size = new System.Drawing.Size(322, 134);
|
this.panelUI.Size = new System.Drawing.Size(322, 157);
|
||||||
this.panelUI.TabIndex = 1;
|
this.panelUI.TabIndex = 1;
|
||||||
//
|
//
|
||||||
// labelTray
|
// labelTray
|
||||||
//
|
//
|
||||||
this.labelTray.AutoSize = true;
|
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.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.Location = new System.Drawing.Point(6, 212);
|
||||||
this.labelTray.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
|
this.labelTray.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
|
||||||
this.labelTray.Name = "labelTray";
|
this.labelTray.Name = "labelTray";
|
||||||
this.labelTray.Size = new System.Drawing.Size(96, 20);
|
this.labelTray.Size = new System.Drawing.Size(96, 20);
|
||||||
@@ -230,7 +228,7 @@
|
|||||||
| System.Windows.Forms.AnchorStyles.Right)));
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
this.panelUpdates.Controls.Add(this.checkUpdateNotifications);
|
this.panelUpdates.Controls.Add(this.checkUpdateNotifications);
|
||||||
this.panelUpdates.Controls.Add(this.btnCheckUpdates);
|
this.panelUpdates.Controls.Add(this.btnCheckUpdates);
|
||||||
this.panelUpdates.Location = new System.Drawing.Point(9, 335);
|
this.panelUpdates.Location = new System.Drawing.Point(9, 358);
|
||||||
this.panelUpdates.Name = "panelUpdates";
|
this.panelUpdates.Name = "panelUpdates";
|
||||||
this.panelUpdates.Size = new System.Drawing.Size(322, 55);
|
this.panelUpdates.Size = new System.Drawing.Size(322, 55);
|
||||||
this.panelUpdates.TabIndex = 5;
|
this.panelUpdates.TabIndex = 5;
|
||||||
@@ -242,7 +240,7 @@
|
|||||||
this.panelTray.Controls.Add(this.checkTrayHighlight);
|
this.panelTray.Controls.Add(this.checkTrayHighlight);
|
||||||
this.panelTray.Controls.Add(this.comboBoxTrayType);
|
this.panelTray.Controls.Add(this.comboBoxTrayType);
|
||||||
this.panelTray.Controls.Add(this.labelTrayIcon);
|
this.panelTray.Controls.Add(this.labelTrayIcon);
|
||||||
this.panelTray.Location = new System.Drawing.Point(9, 212);
|
this.panelTray.Location = new System.Drawing.Point(9, 235);
|
||||||
this.panelTray.Name = "panelTray";
|
this.panelTray.Name = "panelTray";
|
||||||
this.panelTray.Size = new System.Drawing.Size(322, 76);
|
this.panelTray.Size = new System.Drawing.Size(322, 76);
|
||||||
this.panelTray.TabIndex = 3;
|
this.panelTray.TabIndex = 3;
|
||||||
@@ -251,13 +249,25 @@
|
|||||||
//
|
//
|
||||||
this.labelUpdates.AutoSize = true;
|
this.labelUpdates.AutoSize = true;
|
||||||
this.labelUpdates.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
this.labelUpdates.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
||||||
this.labelUpdates.Location = new System.Drawing.Point(6, 312);
|
this.labelUpdates.Location = new System.Drawing.Point(6, 335);
|
||||||
this.labelUpdates.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
|
this.labelUpdates.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
|
||||||
this.labelUpdates.Name = "labelUpdates";
|
this.labelUpdates.Name = "labelUpdates";
|
||||||
this.labelUpdates.Size = new System.Drawing.Size(70, 20);
|
this.labelUpdates.Size = new System.Drawing.Size(70, 20);
|
||||||
this.labelUpdates.TabIndex = 4;
|
this.labelUpdates.TabIndex = 4;
|
||||||
this.labelUpdates.Text = "Updates";
|
this.labelUpdates.Text = "Updates";
|
||||||
//
|
//
|
||||||
|
// checkBestImageQuality
|
||||||
|
//
|
||||||
|
this.checkBestImageQuality.AutoSize = true;
|
||||||
|
this.checkBestImageQuality.Location = new System.Drawing.Point(6, 51);
|
||||||
|
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 = 2;
|
||||||
|
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;
|
||||||
|
//
|
||||||
// TabSettingsGeneral
|
// TabSettingsGeneral
|
||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
@@ -269,7 +279,7 @@
|
|||||||
this.Controls.Add(this.panelUI);
|
this.Controls.Add(this.panelUI);
|
||||||
this.Controls.Add(this.labelUI);
|
this.Controls.Add(this.labelUI);
|
||||||
this.Name = "TabSettingsGeneral";
|
this.Name = "TabSettingsGeneral";
|
||||||
this.Size = new System.Drawing.Size(340, 400);
|
this.Size = new System.Drawing.Size(340, 422);
|
||||||
((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).EndInit();
|
((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).EndInit();
|
||||||
this.panelUI.ResumeLayout(false);
|
this.panelUI.ResumeLayout(false);
|
||||||
this.panelUI.PerformLayout();
|
this.panelUI.PerformLayout();
|
||||||
@@ -303,5 +313,6 @@
|
|||||||
private System.Windows.Forms.Panel panelUpdates;
|
private System.Windows.Forms.Panel panelUpdates;
|
||||||
private System.Windows.Forms.Panel panelTray;
|
private System.Windows.Forms.Panel panelTray;
|
||||||
private System.Windows.Forms.Label labelUpdates;
|
private System.Windows.Forms.Label labelUpdates;
|
||||||
|
private System.Windows.Forms.CheckBox checkBestImageQuality;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Windows.Forms;
|
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Updates;
|
using TweetDuck.Updates;
|
||||||
using TweetDuck.Updates.Events;
|
|
||||||
|
|
||||||
namespace TweetDuck.Core.Other.Settings{
|
namespace TweetDuck.Core.Other.Settings{
|
||||||
partial class TabSettingsGeneral : BaseTabSettings{
|
sealed partial class TabSettingsGeneral : BaseTabSettings{
|
||||||
private readonly UpdateHandler updates;
|
private readonly UpdateHandler updates;
|
||||||
private int updateCheckEventId = -1;
|
private int updateCheckEventId = -1;
|
||||||
|
|
||||||
@@ -29,7 +27,10 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
|
|
||||||
checkExpandLinks.Checked = Config.ExpandLinksOnHover;
|
checkExpandLinks.Checked = Config.ExpandLinksOnHover;
|
||||||
checkSwitchAccountSelectors.Checked = Config.SwitchAccountSelectors;
|
checkSwitchAccountSelectors.Checked = Config.SwitchAccountSelectors;
|
||||||
|
checkBestImageQuality.Checked = Config.BestImageQuality;
|
||||||
checkSpellCheck.Checked = Config.EnableSpellCheck;
|
checkSpellCheck.Checked = Config.EnableSpellCheck;
|
||||||
|
|
||||||
|
checkTrayHighlight.Enabled = Config.TrayBehavior.ShouldDisplayIcon();
|
||||||
checkTrayHighlight.Checked = Config.EnableTrayHighlight;
|
checkTrayHighlight.Checked = Config.EnableTrayHighlight;
|
||||||
|
|
||||||
checkUpdateNotifications.Checked = Config.EnableUpdateCheck;
|
checkUpdateNotifications.Checked = Config.EnableUpdateCheck;
|
||||||
@@ -38,6 +39,7 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
public override void OnReady(){
|
public override void OnReady(){
|
||||||
checkExpandLinks.CheckedChanged += checkExpandLinks_CheckedChanged;
|
checkExpandLinks.CheckedChanged += checkExpandLinks_CheckedChanged;
|
||||||
checkSwitchAccountSelectors.CheckedChanged += checkSwitchAccountSelectors_CheckedChanged;
|
checkSwitchAccountSelectors.CheckedChanged += checkSwitchAccountSelectors_CheckedChanged;
|
||||||
|
checkBestImageQuality.CheckedChanged += checkBestImageQuality_CheckedChanged;
|
||||||
checkSpellCheck.CheckedChanged += checkSpellCheck_CheckedChanged;
|
checkSpellCheck.CheckedChanged += checkSpellCheck_CheckedChanged;
|
||||||
trackBarZoom.ValueChanged += trackBarZoom_ValueChanged;
|
trackBarZoom.ValueChanged += trackBarZoom_ValueChanged;
|
||||||
|
|
||||||
@@ -60,6 +62,10 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
Config.SwitchAccountSelectors = checkSwitchAccountSelectors.Checked;
|
Config.SwitchAccountSelectors = checkSwitchAccountSelectors.Checked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkBestImageQuality_CheckedChanged(object sender, EventArgs e){
|
||||||
|
Config.BestImageQuality = checkBestImageQuality.Checked;
|
||||||
|
}
|
||||||
|
|
||||||
private void checkSpellCheck_CheckedChanged(object sender, EventArgs e){
|
private void checkSpellCheck_CheckedChanged(object sender, EventArgs e){
|
||||||
Config.EnableSpellCheck = checkSpellCheck.Checked;
|
Config.EnableSpellCheck = checkSpellCheck.Checked;
|
||||||
PromptRestart();
|
PromptRestart();
|
||||||
@@ -75,6 +81,7 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
|
|
||||||
private void comboBoxTrayType_SelectedIndexChanged(object sender, EventArgs e){
|
private void comboBoxTrayType_SelectedIndexChanged(object sender, EventArgs e){
|
||||||
Config.TrayBehavior = (TrayIcon.Behavior)comboBoxTrayType.SelectedIndex;
|
Config.TrayBehavior = (TrayIcon.Behavior)comboBoxTrayType.SelectedIndex;
|
||||||
|
checkTrayHighlight.Enabled = Config.TrayBehavior.ShouldDisplayIcon();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkTrayHighlight_CheckedChanged(object sender, EventArgs e){
|
private void checkTrayHighlight_CheckedChanged(object sender, EventArgs e){
|
||||||
@@ -86,25 +93,22 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void btnCheckUpdates_Click(object sender, EventArgs e){
|
private void btnCheckUpdates_Click(object sender, EventArgs e){
|
||||||
updateCheckEventId = updates.Check(true);
|
Config.DismissedUpdate = null;
|
||||||
|
|
||||||
if (updateCheckEventId == -1){
|
btnCheckUpdates.Enabled = false;
|
||||||
MessageBox.Show("Sorry, your system is no longer supported.", "Unsupported System", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
updateCheckEventId = updates.Check(true);
|
||||||
}
|
|
||||||
else{
|
|
||||||
btnCheckUpdates.Enabled = false;
|
|
||||||
updates.DismissUpdate(string.Empty);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updates_CheckFinished(object sender, UpdateCheckEventArgs e){
|
private void updates_CheckFinished(object sender, UpdateEventArgs e){
|
||||||
if (e.EventId == updateCheckEventId){
|
this.InvokeAsyncSafe(() => {
|
||||||
btnCheckUpdates.Enabled = true;
|
if (e.EventId == updateCheckEventId){
|
||||||
|
btnCheckUpdates.Enabled = true;
|
||||||
|
|
||||||
if (!e.UpdateAvailable){
|
if (!e.IsUpdateAvailable){
|
||||||
MessageBox.Show("Your version of "+Program.BrandName+" is up to date.", "No Updates Available", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
FormMessage.Information("No Updates Available", "Your version of TweetDuck is up to date.", FormMessage.OK);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void zoomUpdateTimer_Tick(object sender, EventArgs e){
|
private void zoomUpdateTimer_Tick(object sender, EventArgs e){
|
||||||
|
|||||||
150
Core/Other/Settings/TabSettingsNotifications.Designer.cs
generated
150
Core/Other/Settings/TabSettingsNotifications.Designer.cs
generated
@@ -48,6 +48,8 @@
|
|||||||
this.checkTimerCountDown = new System.Windows.Forms.CheckBox();
|
this.checkTimerCountDown = new System.Windows.Forms.CheckBox();
|
||||||
this.checkNotificationTimer = new System.Windows.Forms.CheckBox();
|
this.checkNotificationTimer = new System.Windows.Forms.CheckBox();
|
||||||
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
||||||
|
this.radioSizeAuto = new System.Windows.Forms.RadioButton();
|
||||||
|
this.radioSizeCustom = new System.Windows.Forms.RadioButton();
|
||||||
this.labelGeneral = new System.Windows.Forms.Label();
|
this.labelGeneral = new System.Windows.Forms.Label();
|
||||||
this.panelGeneral = new System.Windows.Forms.Panel();
|
this.panelGeneral = new System.Windows.Forms.Panel();
|
||||||
this.labelScrollSpeedValue = new System.Windows.Forms.Label();
|
this.labelScrollSpeedValue = new System.Windows.Forms.Label();
|
||||||
@@ -58,8 +60,10 @@
|
|||||||
this.panelTimer = new System.Windows.Forms.Panel();
|
this.panelTimer = new System.Windows.Forms.Panel();
|
||||||
this.labelDuration = new System.Windows.Forms.Label();
|
this.labelDuration = new System.Windows.Forms.Label();
|
||||||
this.labelTimer = new System.Windows.Forms.Label();
|
this.labelTimer = new System.Windows.Forms.Label();
|
||||||
this.labelMiscellaneous = new System.Windows.Forms.Label();
|
this.labelSize = new System.Windows.Forms.Label();
|
||||||
this.panelMiscellaneous = new System.Windows.Forms.Panel();
|
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();
|
((System.ComponentModel.ISupportInitialize)(this.trackBarEdgeDistance)).BeginInit();
|
||||||
this.tableLayoutDurationButtons.SuspendLayout();
|
this.tableLayoutDurationButtons.SuspendLayout();
|
||||||
((System.ComponentModel.ISupportInitialize)(this.trackBarDuration)).BeginInit();
|
((System.ComponentModel.ISupportInitialize)(this.trackBarDuration)).BeginInit();
|
||||||
@@ -75,7 +79,7 @@
|
|||||||
this.labelEdgeDistanceValue.Location = new System.Drawing.Point(147, 129);
|
this.labelEdgeDistanceValue.Location = new System.Drawing.Point(147, 129);
|
||||||
this.labelEdgeDistanceValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
|
this.labelEdgeDistanceValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
|
||||||
this.labelEdgeDistanceValue.Name = "labelEdgeDistanceValue";
|
this.labelEdgeDistanceValue.Name = "labelEdgeDistanceValue";
|
||||||
this.labelEdgeDistanceValue.Size = new System.Drawing.Size(34, 13);
|
this.labelEdgeDistanceValue.Size = new System.Drawing.Size(40, 13);
|
||||||
this.labelEdgeDistanceValue.TabIndex = 9;
|
this.labelEdgeDistanceValue.TabIndex = 9;
|
||||||
this.labelEdgeDistanceValue.Text = "0 px";
|
this.labelEdgeDistanceValue.Text = "0 px";
|
||||||
this.labelEdgeDistanceValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
|
this.labelEdgeDistanceValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
|
||||||
@@ -121,7 +125,7 @@
|
|||||||
this.radioLocCustom.TabIndex = 4;
|
this.radioLocCustom.TabIndex = 4;
|
||||||
this.radioLocCustom.TabStop = true;
|
this.radioLocCustom.TabStop = true;
|
||||||
this.radioLocCustom.Text = "Custom";
|
this.radioLocCustom.Text = "Custom";
|
||||||
this.toolTip.SetToolTip(this.radioLocCustom, "Drag the notification window to the desired location.");
|
this.toolTip.SetToolTip(this.radioLocCustom, "Drag the example notification window to the desired location.");
|
||||||
this.radioLocCustom.UseVisualStyleBackColor = true;
|
this.radioLocCustom.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
// radioLocBR
|
// radioLocBR
|
||||||
@@ -249,7 +253,7 @@
|
|||||||
this.labelDurationValue.Location = new System.Drawing.Point(147, 77);
|
this.labelDurationValue.Location = new System.Drawing.Point(147, 77);
|
||||||
this.labelDurationValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
|
this.labelDurationValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
|
||||||
this.labelDurationValue.Name = "labelDurationValue";
|
this.labelDurationValue.Name = "labelDurationValue";
|
||||||
this.labelDurationValue.Size = new System.Drawing.Size(48, 13);
|
this.labelDurationValue.Size = new System.Drawing.Size(52, 13);
|
||||||
this.labelDurationValue.TabIndex = 4;
|
this.labelDurationValue.TabIndex = 4;
|
||||||
this.labelDurationValue.Text = "0 ms/c";
|
this.labelDurationValue.Text = "0 ms/c";
|
||||||
this.labelDurationValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
|
this.labelDurationValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
|
||||||
@@ -270,11 +274,11 @@
|
|||||||
// checkSkipOnLinkClick
|
// checkSkipOnLinkClick
|
||||||
//
|
//
|
||||||
this.checkSkipOnLinkClick.AutoSize = true;
|
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.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
|
||||||
this.checkSkipOnLinkClick.Name = "checkSkipOnLinkClick";
|
this.checkSkipOnLinkClick.Name = "checkSkipOnLinkClick";
|
||||||
this.checkSkipOnLinkClick.Size = new System.Drawing.Size(113, 17);
|
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.checkSkipOnLinkClick.Text = "Skip On Link Click";
|
||||||
this.toolTip.SetToolTip(this.checkSkipOnLinkClick, "Skips current notification when a link\r\ninside the notification is clicked.");
|
this.toolTip.SetToolTip(this.checkSkipOnLinkClick, "Skips current notification when a link\r\ninside the notification is clicked.");
|
||||||
this.checkSkipOnLinkClick.UseVisualStyleBackColor = true;
|
this.checkSkipOnLinkClick.UseVisualStyleBackColor = true;
|
||||||
@@ -288,42 +292,40 @@
|
|||||||
this.checkColumnName.Size = new System.Drawing.Size(129, 17);
|
this.checkColumnName.Size = new System.Drawing.Size(129, 17);
|
||||||
this.checkColumnName.TabIndex = 0;
|
this.checkColumnName.TabIndex = 0;
|
||||||
this.checkColumnName.Text = "Display Column Name";
|
this.checkColumnName.Text = "Display Column Name";
|
||||||
this.toolTip.SetToolTip(this.checkColumnName, "Shows column name each notification originated\r\nfrom in the notification window t" +
|
this.toolTip.SetToolTip(this.checkColumnName, "Shows column name each notification originated\r\nfrom in the notification window title.");
|
||||||
"itle.");
|
|
||||||
this.checkColumnName.UseVisualStyleBackColor = true;
|
this.checkColumnName.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
// labelIdlePause
|
// labelIdlePause
|
||||||
//
|
//
|
||||||
this.labelIdlePause.AutoSize = true;
|
this.labelIdlePause.AutoSize = true;
|
||||||
this.labelIdlePause.Location = new System.Drawing.Point(3, 60);
|
this.labelIdlePause.Location = new System.Drawing.Point(3, 106);
|
||||||
this.labelIdlePause.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
|
this.labelIdlePause.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
|
||||||
this.labelIdlePause.Name = "labelIdlePause";
|
this.labelIdlePause.Name = "labelIdlePause";
|
||||||
this.labelIdlePause.Size = new System.Drawing.Size(89, 13);
|
this.labelIdlePause.Size = new System.Drawing.Size(89, 13);
|
||||||
this.labelIdlePause.TabIndex = 2;
|
this.labelIdlePause.TabIndex = 4;
|
||||||
this.labelIdlePause.Text = "Pause When Idle";
|
this.labelIdlePause.Text = "Pause When Idle";
|
||||||
//
|
//
|
||||||
// comboBoxIdlePause
|
// comboBoxIdlePause
|
||||||
//
|
//
|
||||||
this.comboBoxIdlePause.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
this.comboBoxIdlePause.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||||
this.comboBoxIdlePause.FormattingEnabled = true;
|
this.comboBoxIdlePause.FormattingEnabled = true;
|
||||||
this.comboBoxIdlePause.Location = new System.Drawing.Point(5, 76);
|
this.comboBoxIdlePause.Location = new System.Drawing.Point(5, 122);
|
||||||
this.comboBoxIdlePause.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
|
this.comboBoxIdlePause.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
|
||||||
this.comboBoxIdlePause.Name = "comboBoxIdlePause";
|
this.comboBoxIdlePause.Name = "comboBoxIdlePause";
|
||||||
this.comboBoxIdlePause.Size = new System.Drawing.Size(144, 21);
|
this.comboBoxIdlePause.Size = new System.Drawing.Size(144, 21);
|
||||||
this.comboBoxIdlePause.TabIndex = 3;
|
this.comboBoxIdlePause.TabIndex = 5;
|
||||||
this.toolTip.SetToolTip(this.comboBoxIdlePause, "Pauses new notifications after going idle for a set amount of time.");
|
this.toolTip.SetToolTip(this.comboBoxIdlePause, "Pauses new notifications after going idle for a set amount of time.");
|
||||||
//
|
//
|
||||||
// checkNonIntrusive
|
// checkNonIntrusive
|
||||||
//
|
//
|
||||||
this.checkNonIntrusive.AutoSize = true;
|
this.checkNonIntrusive.AutoSize = true;
|
||||||
this.checkNonIntrusive.Location = new System.Drawing.Point(6, 5);
|
this.checkNonIntrusive.Location = new System.Drawing.Point(6, 74);
|
||||||
this.checkNonIntrusive.Margin = new System.Windows.Forms.Padding(6, 5, 3, 3);
|
this.checkNonIntrusive.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
|
||||||
this.checkNonIntrusive.Name = "checkNonIntrusive";
|
this.checkNonIntrusive.Name = "checkNonIntrusive";
|
||||||
this.checkNonIntrusive.Size = new System.Drawing.Size(128, 17);
|
this.checkNonIntrusive.Size = new System.Drawing.Size(128, 17);
|
||||||
this.checkNonIntrusive.TabIndex = 0;
|
this.checkNonIntrusive.TabIndex = 3;
|
||||||
this.checkNonIntrusive.Text = "Non-Intrusive Popups";
|
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 " +
|
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.");
|
||||||
"delayed until the cursor moves away to prevent accidental clicks.");
|
|
||||||
this.checkNonIntrusive.UseVisualStyleBackColor = true;
|
this.checkNonIntrusive.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
// checkTimerCountDown
|
// checkTimerCountDown
|
||||||
@@ -349,6 +351,30 @@
|
|||||||
this.checkNotificationTimer.Text = "Display Notification Timer";
|
this.checkNotificationTimer.Text = "Display Notification Timer";
|
||||||
this.checkNotificationTimer.UseVisualStyleBackColor = true;
|
this.checkNotificationTimer.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
|
// radioSizeAuto
|
||||||
|
//
|
||||||
|
this.radioSizeAuto.Location = new System.Drawing.Point(6, 4);
|
||||||
|
this.radioSizeAuto.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3);
|
||||||
|
this.radioSizeAuto.Name = "radioSizeAuto";
|
||||||
|
this.radioSizeAuto.Size = new System.Drawing.Size(92, 17);
|
||||||
|
this.radioSizeAuto.TabIndex = 0;
|
||||||
|
this.radioSizeAuto.TabStop = true;
|
||||||
|
this.radioSizeAuto.Text = "Auto";
|
||||||
|
this.toolTip.SetToolTip(this.radioSizeAuto, "Notification size is based on the font size and browser zoom level.");
|
||||||
|
this.radioSizeAuto.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
|
// radioSizeCustom
|
||||||
|
//
|
||||||
|
this.radioSizeCustom.Location = new System.Drawing.Point(106, 4);
|
||||||
|
this.radioSizeCustom.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3);
|
||||||
|
this.radioSizeCustom.Name = "radioSizeCustom";
|
||||||
|
this.radioSizeCustom.Size = new System.Drawing.Size(92, 17);
|
||||||
|
this.radioSizeCustom.TabIndex = 1;
|
||||||
|
this.radioSizeCustom.TabStop = true;
|
||||||
|
this.radioSizeCustom.Text = "Custom";
|
||||||
|
this.toolTip.SetToolTip(this.radioSizeCustom, "Resize the example notification window to the desired size.");
|
||||||
|
this.radioSizeCustom.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
// labelGeneral
|
// labelGeneral
|
||||||
//
|
//
|
||||||
this.labelGeneral.AutoSize = true;
|
this.labelGeneral.AutoSize = true;
|
||||||
@@ -364,22 +390,24 @@
|
|||||||
//
|
//
|
||||||
this.panelGeneral.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
this.panelGeneral.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||||
| System.Windows.Forms.AnchorStyles.Right)));
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.panelGeneral.Controls.Add(this.checkMediaPreviews);
|
||||||
this.panelGeneral.Controls.Add(this.checkColumnName);
|
this.panelGeneral.Controls.Add(this.checkColumnName);
|
||||||
this.panelGeneral.Controls.Add(this.checkSkipOnLinkClick);
|
this.panelGeneral.Controls.Add(this.checkSkipOnLinkClick);
|
||||||
|
this.panelGeneral.Controls.Add(this.checkNonIntrusive);
|
||||||
this.panelGeneral.Controls.Add(this.labelIdlePause);
|
this.panelGeneral.Controls.Add(this.labelIdlePause);
|
||||||
this.panelGeneral.Controls.Add(this.comboBoxIdlePause);
|
this.panelGeneral.Controls.Add(this.comboBoxIdlePause);
|
||||||
this.panelGeneral.Location = new System.Drawing.Point(9, 31);
|
this.panelGeneral.Location = new System.Drawing.Point(9, 31);
|
||||||
this.panelGeneral.Name = "panelGeneral";
|
this.panelGeneral.Name = "panelGeneral";
|
||||||
this.panelGeneral.Size = new System.Drawing.Size(322, 103);
|
this.panelGeneral.Size = new System.Drawing.Size(322, 149);
|
||||||
this.panelGeneral.TabIndex = 1;
|
this.panelGeneral.TabIndex = 1;
|
||||||
//
|
//
|
||||||
// labelScrollSpeedValue
|
// labelScrollSpeedValue
|
||||||
//
|
//
|
||||||
this.labelScrollSpeedValue.Location = new System.Drawing.Point(147, 54);
|
this.labelScrollSpeedValue.Location = new System.Drawing.Point(147, 53);
|
||||||
this.labelScrollSpeedValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
|
this.labelScrollSpeedValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
|
||||||
this.labelScrollSpeedValue.Name = "labelScrollSpeedValue";
|
this.labelScrollSpeedValue.Name = "labelScrollSpeedValue";
|
||||||
this.labelScrollSpeedValue.Size = new System.Drawing.Size(34, 13);
|
this.labelScrollSpeedValue.Size = new System.Drawing.Size(38, 13);
|
||||||
this.labelScrollSpeedValue.TabIndex = 3;
|
this.labelScrollSpeedValue.TabIndex = 4;
|
||||||
this.labelScrollSpeedValue.Text = "100%";
|
this.labelScrollSpeedValue.Text = "100%";
|
||||||
this.labelScrollSpeedValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
|
this.labelScrollSpeedValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
|
||||||
//
|
//
|
||||||
@@ -387,42 +415,41 @@
|
|||||||
//
|
//
|
||||||
this.trackBarScrollSpeed.AutoSize = false;
|
this.trackBarScrollSpeed.AutoSize = false;
|
||||||
this.trackBarScrollSpeed.LargeChange = 25;
|
this.trackBarScrollSpeed.LargeChange = 25;
|
||||||
this.trackBarScrollSpeed.Location = new System.Drawing.Point(5, 53);
|
this.trackBarScrollSpeed.Location = new System.Drawing.Point(5, 52);
|
||||||
this.trackBarScrollSpeed.Maximum = 200;
|
this.trackBarScrollSpeed.Maximum = 200;
|
||||||
this.trackBarScrollSpeed.Minimum = 25;
|
this.trackBarScrollSpeed.Minimum = 25;
|
||||||
this.trackBarScrollSpeed.Name = "trackBarScrollSpeed";
|
this.trackBarScrollSpeed.Name = "trackBarScrollSpeed";
|
||||||
this.trackBarScrollSpeed.Size = new System.Drawing.Size(148, 30);
|
this.trackBarScrollSpeed.Size = new System.Drawing.Size(148, 30);
|
||||||
this.trackBarScrollSpeed.SmallChange = 25;
|
this.trackBarScrollSpeed.SmallChange = 5;
|
||||||
this.trackBarScrollSpeed.TabIndex = 2;
|
this.trackBarScrollSpeed.TabIndex = 3;
|
||||||
this.trackBarScrollSpeed.TickFrequency = 25;
|
this.trackBarScrollSpeed.TickFrequency = 25;
|
||||||
this.trackBarScrollSpeed.Value = 100;
|
this.trackBarScrollSpeed.Value = 100;
|
||||||
//
|
//
|
||||||
// labelScrollSpeed
|
// labelScrollSpeed
|
||||||
//
|
//
|
||||||
this.labelScrollSpeed.AutoSize = true;
|
this.labelScrollSpeed.AutoSize = true;
|
||||||
this.labelScrollSpeed.Location = new System.Drawing.Point(3, 37);
|
this.labelScrollSpeed.Location = new System.Drawing.Point(3, 36);
|
||||||
this.labelScrollSpeed.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
|
this.labelScrollSpeed.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
|
||||||
this.labelScrollSpeed.Name = "labelScrollSpeed";
|
this.labelScrollSpeed.Name = "labelScrollSpeed";
|
||||||
this.labelScrollSpeed.Size = new System.Drawing.Size(67, 13);
|
this.labelScrollSpeed.Size = new System.Drawing.Size(67, 13);
|
||||||
this.labelScrollSpeed.TabIndex = 1;
|
this.labelScrollSpeed.TabIndex = 2;
|
||||||
this.labelScrollSpeed.Text = "Scroll Speed";
|
this.labelScrollSpeed.Text = "Scroll Speed";
|
||||||
//
|
//
|
||||||
// labelLocation
|
// labelLocation
|
||||||
//
|
//
|
||||||
this.labelLocation.AutoSize = true;
|
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.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, 158);
|
this.labelLocation.Location = new System.Drawing.Point(6, 395);
|
||||||
this.labelLocation.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
|
this.labelLocation.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
|
||||||
this.labelLocation.Name = "labelLocation";
|
this.labelLocation.Name = "labelLocation";
|
||||||
this.labelLocation.Size = new System.Drawing.Size(70, 20);
|
this.labelLocation.Size = new System.Drawing.Size(70, 20);
|
||||||
this.labelLocation.TabIndex = 2;
|
this.labelLocation.TabIndex = 4;
|
||||||
this.labelLocation.Text = "Location";
|
this.labelLocation.Text = "Location";
|
||||||
//
|
//
|
||||||
// panelLocation
|
// panelLocation
|
||||||
//
|
//
|
||||||
this.panelLocation.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
this.panelLocation.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||||
| System.Windows.Forms.AnchorStyles.Right)));
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
this.panelLocation.Controls.Add(this.labelEdgeDistanceValue);
|
|
||||||
this.panelLocation.Controls.Add(this.radioLocTL);
|
this.panelLocation.Controls.Add(this.radioLocTL);
|
||||||
this.panelLocation.Controls.Add(this.labelDisplay);
|
this.panelLocation.Controls.Add(this.labelDisplay);
|
||||||
this.panelLocation.Controls.Add(this.trackBarEdgeDistance);
|
this.panelLocation.Controls.Add(this.trackBarEdgeDistance);
|
||||||
@@ -432,10 +459,11 @@
|
|||||||
this.panelLocation.Controls.Add(this.radioLocBL);
|
this.panelLocation.Controls.Add(this.radioLocBL);
|
||||||
this.panelLocation.Controls.Add(this.radioLocCustom);
|
this.panelLocation.Controls.Add(this.radioLocCustom);
|
||||||
this.panelLocation.Controls.Add(this.radioLocBR);
|
this.panelLocation.Controls.Add(this.radioLocBR);
|
||||||
this.panelLocation.Location = new System.Drawing.Point(9, 181);
|
this.panelLocation.Controls.Add(this.labelEdgeDistanceValue);
|
||||||
|
this.panelLocation.Location = new System.Drawing.Point(9, 418);
|
||||||
this.panelLocation.Name = "panelLocation";
|
this.panelLocation.Name = "panelLocation";
|
||||||
this.panelLocation.Size = new System.Drawing.Size(322, 165);
|
this.panelLocation.Size = new System.Drawing.Size(322, 165);
|
||||||
this.panelLocation.TabIndex = 3;
|
this.panelLocation.TabIndex = 5;
|
||||||
//
|
//
|
||||||
// panelTimer
|
// panelTimer
|
||||||
//
|
//
|
||||||
@@ -447,10 +475,10 @@
|
|||||||
this.panelTimer.Controls.Add(this.checkTimerCountDown);
|
this.panelTimer.Controls.Add(this.checkTimerCountDown);
|
||||||
this.panelTimer.Controls.Add(this.labelDurationValue);
|
this.panelTimer.Controls.Add(this.labelDurationValue);
|
||||||
this.panelTimer.Controls.Add(this.trackBarDuration);
|
this.panelTimer.Controls.Add(this.trackBarDuration);
|
||||||
this.panelTimer.Location = new System.Drawing.Point(9, 393);
|
this.panelTimer.Location = new System.Drawing.Point(9, 227);
|
||||||
this.panelTimer.Name = "panelTimer";
|
this.panelTimer.Name = "panelTimer";
|
||||||
this.panelTimer.Size = new System.Drawing.Size(322, 144);
|
this.panelTimer.Size = new System.Drawing.Size(322, 144);
|
||||||
this.panelTimer.TabIndex = 5;
|
this.panelTimer.TabIndex = 3;
|
||||||
//
|
//
|
||||||
// labelDuration
|
// labelDuration
|
||||||
//
|
//
|
||||||
@@ -466,43 +494,61 @@
|
|||||||
//
|
//
|
||||||
this.labelTimer.AutoSize = true;
|
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.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, 370);
|
this.labelTimer.Location = new System.Drawing.Point(6, 204);
|
||||||
this.labelTimer.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
|
this.labelTimer.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
|
||||||
this.labelTimer.Name = "labelTimer";
|
this.labelTimer.Name = "labelTimer";
|
||||||
this.labelTimer.Size = new System.Drawing.Size(48, 20);
|
this.labelTimer.Size = new System.Drawing.Size(48, 20);
|
||||||
this.labelTimer.TabIndex = 4;
|
this.labelTimer.TabIndex = 2;
|
||||||
this.labelTimer.Text = "Timer";
|
this.labelTimer.Text = "Timer";
|
||||||
//
|
//
|
||||||
// labelMiscellaneous
|
// labelSize
|
||||||
//
|
//
|
||||||
this.labelMiscellaneous.AutoSize = true;
|
this.labelSize.AutoSize = true;
|
||||||
this.labelMiscellaneous.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
this.labelSize.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
||||||
this.labelMiscellaneous.Location = new System.Drawing.Point(6, 561);
|
this.labelSize.Location = new System.Drawing.Point(6, 607);
|
||||||
this.labelMiscellaneous.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
|
this.labelSize.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
|
||||||
this.labelMiscellaneous.Name = "labelMiscellaneous";
|
this.labelSize.Name = "labelSize";
|
||||||
this.labelMiscellaneous.Size = new System.Drawing.Size(109, 20);
|
this.labelSize.Size = new System.Drawing.Size(40, 20);
|
||||||
this.labelMiscellaneous.TabIndex = 6;
|
this.labelSize.TabIndex = 6;
|
||||||
this.labelMiscellaneous.Text = "Miscellaneous";
|
this.labelSize.Text = "Size";
|
||||||
//
|
//
|
||||||
// panelMiscellaneous
|
// panelMiscellaneous
|
||||||
//
|
//
|
||||||
this.panelMiscellaneous.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
this.panelMiscellaneous.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||||
| System.Windows.Forms.AnchorStyles.Right)));
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.panelMiscellaneous.Controls.Add(this.radioSizeCustom);
|
||||||
|
this.panelMiscellaneous.Controls.Add(this.radioSizeAuto);
|
||||||
this.panelMiscellaneous.Controls.Add(this.labelScrollSpeedValue);
|
this.panelMiscellaneous.Controls.Add(this.labelScrollSpeedValue);
|
||||||
this.panelMiscellaneous.Controls.Add(this.trackBarScrollSpeed);
|
this.panelMiscellaneous.Controls.Add(this.trackBarScrollSpeed);
|
||||||
this.panelMiscellaneous.Controls.Add(this.checkNonIntrusive);
|
|
||||||
this.panelMiscellaneous.Controls.Add(this.labelScrollSpeed);
|
this.panelMiscellaneous.Controls.Add(this.labelScrollSpeed);
|
||||||
this.panelMiscellaneous.Location = new System.Drawing.Point(9, 584);
|
this.panelMiscellaneous.Location = new System.Drawing.Point(9, 630);
|
||||||
this.panelMiscellaneous.Name = "panelMiscellaneous";
|
this.panelMiscellaneous.Name = "panelMiscellaneous";
|
||||||
this.panelMiscellaneous.Size = new System.Drawing.Size(322, 90);
|
this.panelMiscellaneous.Size = new System.Drawing.Size(322, 92);
|
||||||
this.panelMiscellaneous.TabIndex = 7;
|
this.panelMiscellaneous.TabIndex = 7;
|
||||||
//
|
//
|
||||||
|
// durationUpdateTimer
|
||||||
|
//
|
||||||
|
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
|
// TabSettingsNotifications
|
||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
this.Controls.Add(this.panelMiscellaneous);
|
this.Controls.Add(this.panelMiscellaneous);
|
||||||
this.Controls.Add(this.labelMiscellaneous);
|
this.Controls.Add(this.labelSize);
|
||||||
this.Controls.Add(this.labelTimer);
|
this.Controls.Add(this.labelTimer);
|
||||||
this.Controls.Add(this.panelLocation);
|
this.Controls.Add(this.panelLocation);
|
||||||
this.Controls.Add(this.labelLocation);
|
this.Controls.Add(this.labelLocation);
|
||||||
@@ -510,7 +556,7 @@
|
|||||||
this.Controls.Add(this.labelGeneral);
|
this.Controls.Add(this.labelGeneral);
|
||||||
this.Controls.Add(this.panelTimer);
|
this.Controls.Add(this.panelTimer);
|
||||||
this.Name = "TabSettingsNotifications";
|
this.Name = "TabSettingsNotifications";
|
||||||
this.Size = new System.Drawing.Size(340, 684);
|
this.Size = new System.Drawing.Size(340, 731);
|
||||||
this.ParentChanged += new System.EventHandler(this.TabSettingsNotifications_ParentChanged);
|
this.ParentChanged += new System.EventHandler(this.TabSettingsNotifications_ParentChanged);
|
||||||
((System.ComponentModel.ISupportInitialize)(this.trackBarEdgeDistance)).EndInit();
|
((System.ComponentModel.ISupportInitialize)(this.trackBarEdgeDistance)).EndInit();
|
||||||
this.tableLayoutDurationButtons.ResumeLayout(false);
|
this.tableLayoutDurationButtons.ResumeLayout(false);
|
||||||
@@ -563,8 +609,12 @@
|
|||||||
private System.Windows.Forms.Label labelScrollSpeedValue;
|
private System.Windows.Forms.Label labelScrollSpeedValue;
|
||||||
private System.Windows.Forms.TrackBar trackBarScrollSpeed;
|
private System.Windows.Forms.TrackBar trackBarScrollSpeed;
|
||||||
private System.Windows.Forms.Label labelScrollSpeed;
|
private System.Windows.Forms.Label labelScrollSpeed;
|
||||||
private System.Windows.Forms.Label labelMiscellaneous;
|
private System.Windows.Forms.Label labelSize;
|
||||||
private System.Windows.Forms.Panel panelMiscellaneous;
|
private System.Windows.Forms.Panel panelMiscellaneous;
|
||||||
private System.Windows.Forms.Label labelDuration;
|
private System.Windows.Forms.Label labelDuration;
|
||||||
|
private System.Windows.Forms.Timer durationUpdateTimer;
|
||||||
|
private System.Windows.Forms.RadioButton radioSizeCustom;
|
||||||
|
private System.Windows.Forms.RadioButton radioSizeAuto;
|
||||||
|
private System.Windows.Forms.CheckBox checkMediaPreviews;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Notification;
|
using TweetDuck.Core.Notification;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Other.Settings{
|
namespace TweetDuck.Core.Other.Settings{
|
||||||
partial class TabSettingsNotifications : BaseTabSettings{
|
sealed partial class TabSettingsNotifications : BaseTabSettings{
|
||||||
private static readonly int[] IdlePauseSeconds = { 0, 30, 60, 120, 300 };
|
private static readonly int[] IdlePauseSeconds = { 0, 30, 60, 120, 300 };
|
||||||
|
|
||||||
private readonly FormNotificationMain notification;
|
private readonly FormNotificationMain notification;
|
||||||
@@ -14,16 +13,13 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
this.notification = notification;
|
this.notification = notification;
|
||||||
this.notification.CanMoveWindow = () => radioLocCustom.Checked;
|
|
||||||
|
|
||||||
this.notification.Move += (sender, args) => {
|
|
||||||
if (radioLocCustom.Checked && this.notification.Location != ControlExtensions.InvisibleLocation){
|
|
||||||
Config.CustomNotificationPosition = this.notification.Location;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.notification.Initialized += (sender, args) => {
|
this.notification.Initialized += (sender, args) => {
|
||||||
this.InvokeAsyncSafe(() => this.notification.ShowNotificationForSettings(true));
|
this.InvokeAsyncSafe(() => {
|
||||||
|
this.notification.ShowNotificationForSettings(true);
|
||||||
|
this.notification.Move += notification_Move;
|
||||||
|
this.notification.ResizeEnd += notification_ResizeEnd;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
this.notification.Activated += notification_Activated;
|
this.notification.Activated += notification_Activated;
|
||||||
@@ -39,6 +35,11 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
|
|
||||||
comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = !radioLocCustom.Checked;
|
comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = !radioLocCustom.Checked;
|
||||||
|
|
||||||
|
switch(Config.NotificationSize){
|
||||||
|
case TweetNotification.Size.Auto: radioSizeAuto.Checked = true; break;
|
||||||
|
case TweetNotification.Size.Custom: radioSizeCustom.Checked = true; break;
|
||||||
|
}
|
||||||
|
|
||||||
toolTip.SetToolTip(trackBarDuration, toolTip.GetToolTip(labelDurationValue));
|
toolTip.SetToolTip(trackBarDuration, toolTip.GetToolTip(labelDurationValue));
|
||||||
trackBarDuration.SetValueSafe(Config.NotificationDurationValue);
|
trackBarDuration.SetValueSafe(Config.NotificationDurationValue);
|
||||||
labelDurationValue.Text = Config.NotificationDurationValue+" ms/c";
|
labelDurationValue.Text = Config.NotificationDurationValue+" ms/c";
|
||||||
@@ -50,7 +51,7 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
comboBoxIdlePause.Items.Add("5 minutes");
|
comboBoxIdlePause.Items.Add("5 minutes");
|
||||||
comboBoxIdlePause.SelectedIndex = Math.Max(0, Array.FindIndex(IdlePauseSeconds, val => val == Config.NotificationIdlePauseSeconds));
|
comboBoxIdlePause.SelectedIndex = Math.Max(0, Array.FindIndex(IdlePauseSeconds, val => val == Config.NotificationIdlePauseSeconds));
|
||||||
|
|
||||||
comboBoxDisplay.Items.Add("(Same As "+Program.BrandName+")");
|
comboBoxDisplay.Items.Add("(Same as TweetDuck)");
|
||||||
|
|
||||||
foreach(Screen screen in Screen.AllScreens){
|
foreach(Screen screen in Screen.AllScreens){
|
||||||
comboBoxDisplay.Items.Add(screen.DeviceName.TrimStart('\\', '.')+" ("+screen.Bounds.Width+"x"+screen.Bounds.Height+")");
|
comboBoxDisplay.Items.Add(screen.DeviceName.TrimStart('\\', '.')+" ("+screen.Bounds.Width+"x"+screen.Bounds.Height+")");
|
||||||
@@ -62,14 +63,18 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
checkNotificationTimer.Checked = Config.DisplayNotificationTimer;
|
checkNotificationTimer.Checked = Config.DisplayNotificationTimer;
|
||||||
checkTimerCountDown.Enabled = checkNotificationTimer.Checked;
|
checkTimerCountDown.Enabled = checkNotificationTimer.Checked;
|
||||||
checkTimerCountDown.Checked = Config.NotificationTimerCountDown;
|
checkTimerCountDown.Checked = Config.NotificationTimerCountDown;
|
||||||
|
checkMediaPreviews.Checked = Config.NotificationMediaPreviews;
|
||||||
checkSkipOnLinkClick.Checked = Config.NotificationSkipOnLinkClick;
|
checkSkipOnLinkClick.Checked = Config.NotificationSkipOnLinkClick;
|
||||||
checkNonIntrusive.Checked = Config.NotificationNonIntrusiveMode;
|
checkNonIntrusive.Checked = Config.NotificationNonIntrusiveMode;
|
||||||
|
|
||||||
trackBarScrollSpeed.SetValueSafe(Config.NotificationScrollSpeed);
|
trackBarScrollSpeed.SetValueSafe(Config.NotificationScrollSpeed);
|
||||||
labelScrollSpeedValue.Text = trackBarScrollSpeed.Value.ToString(CultureInfo.InvariantCulture)+"%";
|
labelScrollSpeedValue.Text = trackBarScrollSpeed.Value+"%";
|
||||||
|
|
||||||
trackBarEdgeDistance.SetValueSafe(Config.NotificationEdgeDistance);
|
trackBarEdgeDistance.SetValueSafe(Config.NotificationEdgeDistance);
|
||||||
labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value.ToString(CultureInfo.InvariantCulture)+" px";
|
labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value+" px";
|
||||||
|
|
||||||
|
this.notification.CanMoveWindow = () => radioLocCustom.Checked;
|
||||||
|
this.notification.CanResizeWindow = radioSizeCustom.Checked;
|
||||||
|
|
||||||
Disposed += (sender, args) => this.notification.Dispose();
|
Disposed += (sender, args) => this.notification.Dispose();
|
||||||
}
|
}
|
||||||
@@ -81,6 +86,9 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
radioLocBR.CheckedChanged += radioLoc_CheckedChanged;
|
radioLocBR.CheckedChanged += radioLoc_CheckedChanged;
|
||||||
radioLocCustom.Click += radioLocCustom_Click;
|
radioLocCustom.Click += radioLocCustom_Click;
|
||||||
|
|
||||||
|
radioSizeAuto.CheckedChanged += radioSize_CheckedChanged;
|
||||||
|
radioSizeCustom.Click += radioSizeCustom_Click;
|
||||||
|
|
||||||
trackBarDuration.ValueChanged += trackBarDuration_ValueChanged;
|
trackBarDuration.ValueChanged += trackBarDuration_ValueChanged;
|
||||||
btnDurationShort.Click += btnDurationShort_Click;
|
btnDurationShort.Click += btnDurationShort_Click;
|
||||||
btnDurationMedium.Click += btnDurationMedium_Click;
|
btnDurationMedium.Click += btnDurationMedium_Click;
|
||||||
@@ -89,6 +97,7 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
checkColumnName.CheckedChanged += checkColumnName_CheckedChanged;
|
checkColumnName.CheckedChanged += checkColumnName_CheckedChanged;
|
||||||
checkNotificationTimer.CheckedChanged += checkNotificationTimer_CheckedChanged;
|
checkNotificationTimer.CheckedChanged += checkNotificationTimer_CheckedChanged;
|
||||||
checkTimerCountDown.CheckedChanged += checkTimerCountDown_CheckedChanged;
|
checkTimerCountDown.CheckedChanged += checkTimerCountDown_CheckedChanged;
|
||||||
|
checkMediaPreviews.CheckedChanged += checkMediaPreviews_CheckedChanged;
|
||||||
checkSkipOnLinkClick.CheckedChanged += checkSkipOnLinkClick_CheckedChanged;
|
checkSkipOnLinkClick.CheckedChanged += checkSkipOnLinkClick_CheckedChanged;
|
||||||
checkNonIntrusive.CheckedChanged += checkNonIntrusive_CheckedChanged;
|
checkNonIntrusive.CheckedChanged += checkNonIntrusive_CheckedChanged;
|
||||||
|
|
||||||
@@ -113,6 +122,19 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
notification.Activated -= notification_Activated;
|
notification.Activated -= notification_Activated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void notification_Move(object sender, EventArgs e){
|
||||||
|
if (radioLocCustom.Checked && notification.Location != ControlExtensions.InvisibleLocation){
|
||||||
|
Config.CustomNotificationPosition = notification.Location;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notification_ResizeEnd(object sender, EventArgs e){
|
||||||
|
if (radioSizeCustom.Checked){
|
||||||
|
Config.CustomNotificationSize = notification.BrowserSize;
|
||||||
|
notification.ShowNotificationForSettings(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void radioLoc_CheckedChanged(object sender, EventArgs e){
|
private void radioLoc_CheckedChanged(object sender, EventArgs e){
|
||||||
if (radioLocTL.Checked)Config.NotificationPosition = TweetNotification.Position.TopLeft;
|
if (radioLocTL.Checked)Config.NotificationPosition = TweetNotification.Position.TopLeft;
|
||||||
else if (radioLocTR.Checked)Config.NotificationPosition = TweetNotification.Position.TopRight;
|
else if (radioLocTR.Checked)Config.NotificationPosition = TweetNotification.Position.TopRight;
|
||||||
@@ -133,7 +155,7 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = false;
|
comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = false;
|
||||||
notification.ShowNotificationForSettings(false);
|
notification.ShowNotificationForSettings(false);
|
||||||
|
|
||||||
if (notification.IsFullyOutsideView() && MessageBox.Show("The notification seems to be outside of view, would you like to reset its position?", "Notification is outside view", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.Yes){
|
if (notification.IsFullyOutsideView() && FormMessage.Question("Notification is outside view", "The notification seems to be outside of view, would you like to reset its position?", FormMessage.Yes, FormMessage.No)){
|
||||||
Config.NotificationPosition = TweetNotification.Position.TopRight;
|
Config.NotificationPosition = TweetNotification.Position.TopRight;
|
||||||
notification.MoveToVisibleLocation();
|
notification.MoveToVisibleLocation();
|
||||||
|
|
||||||
@@ -144,11 +166,30 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void radioSize_CheckedChanged(object sender, EventArgs e){
|
||||||
|
if (radioSizeAuto.Checked)Config.NotificationSize = TweetNotification.Size.Auto;
|
||||||
|
|
||||||
|
notification.ShowNotificationForSettings(false);
|
||||||
|
notification.CanResizeWindow = false; // must be after ShowNotificationForSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
private void radioSizeCustom_Click(object sender, EventArgs e){
|
||||||
|
if (!Config.IsCustomNotificationSizeSet){
|
||||||
|
Config.CustomNotificationSize = notification.BrowserSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
Config.NotificationSize = TweetNotification.Size.Custom;
|
||||||
|
|
||||||
|
notification.CanResizeWindow = true;
|
||||||
|
notification.ShowNotificationForSettings(false);
|
||||||
|
}
|
||||||
|
|
||||||
private void trackBarDuration_ValueChanged(object sender, EventArgs e){
|
private void trackBarDuration_ValueChanged(object sender, EventArgs e){
|
||||||
|
durationUpdateTimer.Stop();
|
||||||
|
durationUpdateTimer.Start();
|
||||||
|
|
||||||
Config.NotificationDurationValue = trackBarDuration.Value;
|
Config.NotificationDurationValue = trackBarDuration.Value;
|
||||||
labelDurationValue.Text = Config.NotificationDurationValue+" ms/c";
|
labelDurationValue.Text = Config.NotificationDurationValue+" ms/c";
|
||||||
|
|
||||||
notification.ShowNotificationForSettings(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void btnDurationShort_Click(object sender, EventArgs e){
|
private void btnDurationShort_Click(object sender, EventArgs e){
|
||||||
@@ -179,6 +220,10 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
notification.ShowNotificationForSettings(true);
|
notification.ShowNotificationForSettings(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkMediaPreviews_CheckedChanged(object sender, EventArgs e){
|
||||||
|
Config.NotificationMediaPreviews = checkMediaPreviews.Checked;
|
||||||
|
}
|
||||||
|
|
||||||
private void checkSkipOnLinkClick_CheckedChanged(object sender, EventArgs e){
|
private void checkSkipOnLinkClick_CheckedChanged(object sender, EventArgs e){
|
||||||
Config.NotificationSkipOnLinkClick = checkSkipOnLinkClick.Checked;
|
Config.NotificationSkipOnLinkClick = checkSkipOnLinkClick.Checked;
|
||||||
}
|
}
|
||||||
@@ -193,7 +238,7 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
|
|
||||||
private void trackBarScrollSpeed_ValueChanged(object sender, EventArgs e){
|
private void trackBarScrollSpeed_ValueChanged(object sender, EventArgs e){
|
||||||
if (trackBarScrollSpeed.AlignValueToTick()){
|
if (trackBarScrollSpeed.AlignValueToTick()){
|
||||||
labelScrollSpeedValue.Text = trackBarScrollSpeed.Value.ToString(CultureInfo.InvariantCulture)+"%";
|
labelScrollSpeedValue.Text = trackBarScrollSpeed.Value+"%";
|
||||||
Config.NotificationScrollSpeed = trackBarScrollSpeed.Value;
|
Config.NotificationScrollSpeed = trackBarScrollSpeed.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -204,9 +249,14 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void trackBarEdgeDistance_ValueChanged(object sender, EventArgs e){
|
private void trackBarEdgeDistance_ValueChanged(object sender, EventArgs e){
|
||||||
labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value.ToString(CultureInfo.InvariantCulture)+" px";
|
labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value+" px";
|
||||||
Config.NotificationEdgeDistance = trackBarEdgeDistance.Value;
|
Config.NotificationEdgeDistance = trackBarEdgeDistance.Value;
|
||||||
notification.ShowNotificationForSettings(false);
|
notification.ShowNotificationForSettings(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void durationUpdateTimer_Tick(object sender, EventArgs e){
|
||||||
|
notification.ShowNotificationForSettings(true);
|
||||||
|
durationUpdateTimer.Stop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
71
Core/Other/Settings/TabSettingsSounds.Designer.cs
generated
71
Core/Other/Settings/TabSettingsSounds.Designer.cs
generated
@@ -25,15 +25,40 @@
|
|||||||
private void InitializeComponent() {
|
private void InitializeComponent() {
|
||||||
this.components = new System.ComponentModel.Container();
|
this.components = new System.ComponentModel.Container();
|
||||||
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
||||||
|
this.tbCustomSound = new System.Windows.Forms.TextBox();
|
||||||
|
this.labelVolumeValue = new System.Windows.Forms.Label();
|
||||||
this.btnPlaySound = new System.Windows.Forms.Button();
|
this.btnPlaySound = new System.Windows.Forms.Button();
|
||||||
this.btnResetSound = new System.Windows.Forms.Button();
|
this.btnResetSound = new System.Windows.Forms.Button();
|
||||||
this.btnBrowseSound = new System.Windows.Forms.Button();
|
this.btnBrowseSound = new System.Windows.Forms.Button();
|
||||||
this.tbCustomSound = new System.Windows.Forms.TextBox();
|
|
||||||
this.labelSoundNotification = new System.Windows.Forms.Label();
|
this.labelSoundNotification = new System.Windows.Forms.Label();
|
||||||
this.panelSoundNotification = new System.Windows.Forms.Panel();
|
this.panelSoundNotification = new System.Windows.Forms.Panel();
|
||||||
|
this.labelVolume = new System.Windows.Forms.Label();
|
||||||
|
this.trackBarVolume = new System.Windows.Forms.TrackBar();
|
||||||
this.panelSoundNotification.SuspendLayout();
|
this.panelSoundNotification.SuspendLayout();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.trackBarVolume)).BeginInit();
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
//
|
//
|
||||||
|
// tbCustomSound
|
||||||
|
//
|
||||||
|
this.tbCustomSound.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.tbCustomSound.Location = new System.Drawing.Point(3, 3);
|
||||||
|
this.tbCustomSound.Name = "tbCustomSound";
|
||||||
|
this.tbCustomSound.Size = new System.Drawing.Size(316, 20);
|
||||||
|
this.tbCustomSound.TabIndex = 0;
|
||||||
|
this.toolTip.SetToolTip(this.tbCustomSound, "When empty, the default TweetDeck sound notification is used.");
|
||||||
|
//
|
||||||
|
// labelVolumeValue
|
||||||
|
//
|
||||||
|
this.labelVolumeValue.BackColor = System.Drawing.Color.Transparent;
|
||||||
|
this.labelVolumeValue.Location = new System.Drawing.Point(147, 84);
|
||||||
|
this.labelVolumeValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
|
||||||
|
this.labelVolumeValue.Name = "labelVolumeValue";
|
||||||
|
this.labelVolumeValue.Size = new System.Drawing.Size(38, 13);
|
||||||
|
this.labelVolumeValue.TabIndex = 6;
|
||||||
|
this.labelVolumeValue.Text = "100%";
|
||||||
|
this.labelVolumeValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
|
||||||
|
//
|
||||||
// btnPlaySound
|
// btnPlaySound
|
||||||
//
|
//
|
||||||
this.btnPlaySound.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
this.btnPlaySound.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
@@ -69,15 +94,6 @@
|
|||||||
this.btnBrowseSound.Text = "Browse...";
|
this.btnBrowseSound.Text = "Browse...";
|
||||||
this.btnBrowseSound.UseVisualStyleBackColor = true;
|
this.btnBrowseSound.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
// tbCustomSound
|
|
||||||
//
|
|
||||||
this.tbCustomSound.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
|
||||||
| System.Windows.Forms.AnchorStyles.Right)));
|
|
||||||
this.tbCustomSound.Location = new System.Drawing.Point(3, 3);
|
|
||||||
this.tbCustomSound.Name = "tbCustomSound";
|
|
||||||
this.tbCustomSound.Size = new System.Drawing.Size(316, 20);
|
|
||||||
this.tbCustomSound.TabIndex = 0;
|
|
||||||
//
|
|
||||||
// labelSoundNotification
|
// labelSoundNotification
|
||||||
//
|
//
|
||||||
this.labelSoundNotification.AutoSize = true;
|
this.labelSoundNotification.AutoSize = true;
|
||||||
@@ -93,15 +109,42 @@
|
|||||||
//
|
//
|
||||||
this.panelSoundNotification.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
this.panelSoundNotification.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||||
| System.Windows.Forms.AnchorStyles.Right)));
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.panelSoundNotification.Controls.Add(this.labelVolume);
|
||||||
|
this.panelSoundNotification.Controls.Add(this.trackBarVolume);
|
||||||
this.panelSoundNotification.Controls.Add(this.btnPlaySound);
|
this.panelSoundNotification.Controls.Add(this.btnPlaySound);
|
||||||
this.panelSoundNotification.Controls.Add(this.tbCustomSound);
|
this.panelSoundNotification.Controls.Add(this.tbCustomSound);
|
||||||
this.panelSoundNotification.Controls.Add(this.btnResetSound);
|
this.panelSoundNotification.Controls.Add(this.btnResetSound);
|
||||||
this.panelSoundNotification.Controls.Add(this.btnBrowseSound);
|
this.panelSoundNotification.Controls.Add(this.btnBrowseSound);
|
||||||
|
this.panelSoundNotification.Controls.Add(this.labelVolumeValue);
|
||||||
this.panelSoundNotification.Location = new System.Drawing.Point(9, 31);
|
this.panelSoundNotification.Location = new System.Drawing.Point(9, 31);
|
||||||
this.panelSoundNotification.Name = "panelSoundNotification";
|
this.panelSoundNotification.Name = "panelSoundNotification";
|
||||||
this.panelSoundNotification.Size = new System.Drawing.Size(322, 56);
|
this.panelSoundNotification.Size = new System.Drawing.Size(322, 119);
|
||||||
this.panelSoundNotification.TabIndex = 2;
|
this.panelSoundNotification.TabIndex = 2;
|
||||||
//
|
//
|
||||||
|
// labelVolume
|
||||||
|
//
|
||||||
|
this.labelVolume.AutoSize = true;
|
||||||
|
this.labelVolume.Location = new System.Drawing.Point(3, 67);
|
||||||
|
this.labelVolume.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
|
||||||
|
this.labelVolume.Name = "labelVolume";
|
||||||
|
this.labelVolume.Size = new System.Drawing.Size(42, 13);
|
||||||
|
this.labelVolume.TabIndex = 4;
|
||||||
|
this.labelVolume.Text = "Volume";
|
||||||
|
//
|
||||||
|
// trackBarVolume
|
||||||
|
//
|
||||||
|
this.trackBarVolume.AutoSize = false;
|
||||||
|
this.trackBarVolume.BackColor = System.Drawing.SystemColors.Control;
|
||||||
|
this.trackBarVolume.Location = new System.Drawing.Point(3, 83);
|
||||||
|
this.trackBarVolume.Maximum = 100;
|
||||||
|
this.trackBarVolume.Name = "trackBarVolume";
|
||||||
|
this.trackBarVolume.Size = new System.Drawing.Size(148, 30);
|
||||||
|
this.trackBarVolume.SmallChange = 1;
|
||||||
|
this.trackBarVolume.TabIndex = 5;
|
||||||
|
this.trackBarVolume.TickFrequency = 10;
|
||||||
|
this.trackBarVolume.Value = 100;
|
||||||
|
this.trackBarVolume.ValueChanged += new System.EventHandler(this.trackBarVolume_ValueChanged);
|
||||||
|
//
|
||||||
// TabSettingsSounds
|
// TabSettingsSounds
|
||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
@@ -109,9 +152,10 @@
|
|||||||
this.Controls.Add(this.panelSoundNotification);
|
this.Controls.Add(this.panelSoundNotification);
|
||||||
this.Controls.Add(this.labelSoundNotification);
|
this.Controls.Add(this.labelSoundNotification);
|
||||||
this.Name = "TabSettingsSounds";
|
this.Name = "TabSettingsSounds";
|
||||||
this.Size = new System.Drawing.Size(340, 97);
|
this.Size = new System.Drawing.Size(340, 160);
|
||||||
this.panelSoundNotification.ResumeLayout(false);
|
this.panelSoundNotification.ResumeLayout(false);
|
||||||
this.panelSoundNotification.PerformLayout();
|
this.panelSoundNotification.PerformLayout();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.trackBarVolume)).EndInit();
|
||||||
this.ResumeLayout(false);
|
this.ResumeLayout(false);
|
||||||
this.PerformLayout();
|
this.PerformLayout();
|
||||||
|
|
||||||
@@ -126,5 +170,8 @@
|
|||||||
private System.Windows.Forms.Button btnPlaySound;
|
private System.Windows.Forms.Button btnPlaySound;
|
||||||
private System.Windows.Forms.Label labelSoundNotification;
|
private System.Windows.Forms.Label labelSoundNotification;
|
||||||
private System.Windows.Forms.Panel panelSoundNotification;
|
private System.Windows.Forms.Panel panelSoundNotification;
|
||||||
|
private System.Windows.Forms.Label labelVolume;
|
||||||
|
private System.Windows.Forms.Label labelVolumeValue;
|
||||||
|
private System.Windows.Forms.TrackBar trackBarVolume;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,14 @@
|
|||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Notification;
|
using TweetDuck.Core.Notification;
|
||||||
using TweetLib.Audio.Utils;
|
using TweetLib.Audio;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Other.Settings{
|
namespace TweetDuck.Core.Other.Settings{
|
||||||
partial class TabSettingsSounds : BaseTabSettings{
|
sealed partial class TabSettingsSounds : BaseTabSettings{
|
||||||
private readonly SoundNotification soundNotification;
|
private readonly SoundNotification soundNotification;
|
||||||
|
private readonly bool supportsChangingVolume;
|
||||||
|
|
||||||
public TabSettingsSounds(){
|
public TabSettingsSounds(){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@@ -15,6 +17,12 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
soundNotification = new SoundNotification();
|
soundNotification = new SoundNotification();
|
||||||
soundNotification.PlaybackError += sound_PlaybackError;
|
soundNotification.PlaybackError += sound_PlaybackError;
|
||||||
|
|
||||||
|
supportsChangingVolume = soundNotification.SetVolume(Config.NotificationSoundVolume);
|
||||||
|
|
||||||
|
trackBarVolume.Enabled = supportsChangingVolume && !string.IsNullOrEmpty(Config.NotificationSoundPath);
|
||||||
|
trackBarVolume.SetValueSafe(Config.NotificationSoundVolume);
|
||||||
|
labelVolumeValue.Text = trackBarVolume.Value+"%";
|
||||||
|
|
||||||
tbCustomSound.Text = Config.NotificationSoundPath;
|
tbCustomSound.Text = Config.NotificationSoundPath;
|
||||||
tbCustomSound_TextChanged(tbCustomSound, new EventArgs());
|
tbCustomSound_TextChanged(tbCustomSound, new EventArgs());
|
||||||
|
|
||||||
@@ -34,9 +42,10 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
|
|
||||||
private void tbCustomSound_TextChanged(object sender, EventArgs e){
|
private void tbCustomSound_TextChanged(object sender, EventArgs e){
|
||||||
bool isEmpty = string.IsNullOrEmpty(tbCustomSound.Text);
|
bool isEmpty = string.IsNullOrEmpty(tbCustomSound.Text);
|
||||||
tbCustomSound.ForeColor = isEmpty || File.Exists(tbCustomSound.Text) ? SystemColors.WindowText : Color.Maroon;
|
tbCustomSound.ForeColor = isEmpty || File.Exists(tbCustomSound.Text) ? SystemColors.WindowText : Color.Red;
|
||||||
btnPlaySound.Enabled = !isEmpty;
|
btnPlaySound.Enabled = !isEmpty;
|
||||||
btnResetSound.Enabled = !isEmpty;
|
btnResetSound.Enabled = !isEmpty;
|
||||||
|
trackBarVolume.Enabled = supportsChangingVolume && !isEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void btnPlaySound_Click(object sender, EventArgs e){
|
private void btnPlaySound_Click(object sender, EventArgs e){
|
||||||
@@ -44,7 +53,7 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void sound_PlaybackError(object sender, PlaybackErrorEventArgs e){
|
private void sound_PlaybackError(object sender, PlaybackErrorEventArgs e){
|
||||||
MessageBox.Show("Could not play custom notification sound."+Environment.NewLine+e.Message, "Notification Sound Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
FormMessage.Error("Notification Sound Error", "Could not play custom notification sound.\n"+e.Message, FormMessage.OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void btnBrowseSound_Click(object sender, EventArgs e){
|
private void btnBrowseSound_Click(object sender, EventArgs e){
|
||||||
@@ -63,5 +72,11 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
private void btnResetSound_Click(object sender, EventArgs e){
|
private void btnResetSound_Click(object sender, EventArgs e){
|
||||||
tbCustomSound.Text = string.Empty;
|
tbCustomSound.Text = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void trackBarVolume_ValueChanged(object sender, EventArgs e){
|
||||||
|
Config.NotificationSoundVolume = trackBarVolume.Value;
|
||||||
|
soundNotification.SetVolume(Config.NotificationSoundVolume);
|
||||||
|
labelVolumeValue.Text = Config.NotificationSoundVolume+"%";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using System.ComponentModel;
|
|||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
namespace TweetDuck.Core{
|
namespace TweetDuck.Core{
|
||||||
partial class TrayIcon : Component{
|
sealed partial class TrayIcon : Component{
|
||||||
public enum Behavior{ // keep order
|
public enum Behavior{ // keep order
|
||||||
Disabled, DisplayOnly, MinimizeToTray, CloseToTray, Combined
|
Disabled, DisplayOnly, MinimizeToTray, CloseToTray, Combined
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,36 +1,46 @@
|
|||||||
using CefSharp;
|
using CefSharp;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Drawing;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using TweetDuck.Core.Other;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Utils{
|
namespace TweetDuck.Core.Utils{
|
||||||
static class BrowserUtils{
|
static class BrowserUtils{
|
||||||
public static string HeaderAcceptLanguage{
|
public static string HeaderAcceptLanguage{
|
||||||
get{
|
get{
|
||||||
string culture = CultureInfo.CurrentCulture.Name;
|
string culture = Program.Culture.Name;
|
||||||
|
|
||||||
if (culture == "en"){
|
if (culture == "en"){
|
||||||
return "en-us,en";
|
return "en-us,en";
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
return culture.ToLowerInvariant()+",en;q=0.9";
|
return culture.ToLower()+",en;q=0.9";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string HeaderUserAgent => Program.BrandName+" "+Application.ProductVersion;
|
public static string HeaderUserAgent => Program.BrandName+" "+Application.ProductVersion;
|
||||||
|
|
||||||
public static readonly Color BackgroundColor = Color.FromArgb(28, 99, 153);
|
public static void SetupCefArgs(IDictionary<string, string> args){
|
||||||
public const string BackgroundColorFix = "let e=document.createElement('style');document.head.appendChild(e);e.innerHTML='body::before{background:#1c6399!important}'";
|
if (!Program.SystemConfig.HardwareAcceleration){
|
||||||
|
args["disable-gpu"] = "1";
|
||||||
|
args["disable-gpu-vsync"] = "1";
|
||||||
|
}
|
||||||
|
|
||||||
public static readonly string[] DictionaryWords = {
|
args["disable-extensions"] = "1";
|
||||||
"tweetdeck", "TweetDeck", "tweetduck", "TweetDuck", "TD"
|
args["disable-plugins-discovery"] = "1";
|
||||||
};
|
args["enable-system-flash"] = "0";
|
||||||
|
|
||||||
|
if (args.TryGetValue("js-flags", out string jsFlags)){
|
||||||
|
args["js-flags"] = "--expose-gc "+jsFlags;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
args["js-flags"] = "--expose-gc";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static bool IsValidUrl(string url){
|
public static bool IsValidUrl(string url){
|
||||||
if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri)){
|
if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri)){
|
||||||
@@ -48,7 +58,7 @@ namespace TweetDuck.Core.Utils{
|
|||||||
OpenExternalBrowserUnsafe(url);
|
OpenExternalBrowserUnsafe(url);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
MessageBox.Show("A potentially malicious URL was blocked from opening:"+Environment.NewLine+url, "Blocked URL", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
FormMessage.Warning("Blocked URL", "A potentially malicious URL was blocked from opening:\n"+url, FormMessage.OK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,12 +71,8 @@ namespace TweetDuck.Core.Utils{
|
|||||||
return string.IsNullOrEmpty(file) ? null : file;
|
return string.IsNullOrEmpty(file) ? null : file;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string ConvertPascalCaseToScreamingSnakeCase(string str){
|
|
||||||
return Regex.Replace(str, @"(\p{Ll})(\P{Ll})|(\P{Ll})(\P{Ll}\p{Ll})", "$1$3_$2$4").ToUpperInvariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetErrorName(CefErrorCode code){
|
public static string GetErrorName(CefErrorCode code){
|
||||||
return ConvertPascalCaseToScreamingSnakeCase(Enum.GetName(typeof(CefErrorCode), code) ?? string.Empty);
|
return StringUtils.ConvertPascalCaseToScreamingSnakeCase(Enum.GetName(typeof(CefErrorCode), code) ?? string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static WebClient DownloadFileAsync(string url, string target, Action onSuccess, Action<Exception> onFailure){
|
public static WebClient DownloadFileAsync(string url, string target, Action onSuccess, Action<Exception> onFailure){
|
||||||
@@ -93,18 +99,14 @@ namespace TweetDuck.Core.Utils{
|
|||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int Scale(int baseValue, double scaleFactor){
|
||||||
|
return (int)Math.Round(baseValue*scaleFactor);
|
||||||
|
}
|
||||||
|
|
||||||
public static void SetZoomLevel(IBrowser browser, int percentage){
|
public static void SetZoomLevel(IBrowser browser, int percentage){
|
||||||
browser.GetHost().SetZoomLevel(Math.Log(percentage/100.0, 1.2));
|
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
|
#if DEBUG
|
||||||
public static void HandleConsoleMessage(object sender, ConsoleMessageEventArgs e){
|
public static void HandleConsoleMessage(object sender, ConsoleMessageEventArgs e){
|
||||||
Debug.WriteLine("[Console] {0} ({1}:{2})", e.Message, e.Source, e.Line);
|
Debug.WriteLine("[Console] {0} ({1}:{2})", e.Message, e.Source, e.Line);
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
|
|
||||||
namespace TweetDuck.Core.Utils{
|
|
||||||
static class CommandLineArgsParser{
|
|
||||||
private static readonly Lazy<Regex> SplitRegex = new Lazy<Regex>(() => new Regex(@"([^=\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", "FieldCanBeMadeReadOnly.Local")]
|
||||||
[SuppressMessage("ReSharper", "MemberCanBePrivate.Local")]
|
[SuppressMessage("ReSharper", "MemberCanBePrivate.Local")]
|
||||||
static class NativeMethods{
|
static class NativeMethods{
|
||||||
public static readonly IntPtr HWND_BROADCAST = new IntPtr(0xFFFF);
|
|
||||||
public static readonly IntPtr HOOK_HANDLED = new IntPtr(-1);
|
public static readonly IntPtr HOOK_HANDLED = new IntPtr(-1);
|
||||||
|
|
||||||
public const int HWND_TOPMOST = -1;
|
public const int HWND_TOPMOST = -1;
|
||||||
@@ -17,7 +16,6 @@ namespace TweetDuck.Core.Utils{
|
|||||||
public const int GWL_STYLE = -16;
|
public const int GWL_STYLE = -16;
|
||||||
|
|
||||||
public const int SB_HORZ = 0;
|
public const int SB_HORZ = 0;
|
||||||
public const int BCM_SETSHIELD = 0x160C;
|
|
||||||
|
|
||||||
public const int WM_MOUSE_LL = 14;
|
public const int WM_MOUSE_LL = 14;
|
||||||
public const int WM_MOUSEWHEEL = 0x020A;
|
public const int WM_MOUSEWHEEL = 0x020A;
|
||||||
@@ -66,12 +64,6 @@ namespace TweetDuck.Core.Utils{
|
|||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop);
|
private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop);
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
|
||||||
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam, IntPtr lParam);
|
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
|
||||||
public static extern uint RegisterWindowMessage(string messageName);
|
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
[DllImport("user32.dll")]
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
public static extern bool ShowScrollBar(IntPtr hWnd, int wBar, bool bShow);
|
public static extern bool ShowScrollBar(IntPtr hWnd, int wBar, bool bShow);
|
||||||
|
|||||||
22
Core/Utils/StringUtils.cs
Normal file
22
Core/Utils/StringUtils.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int[] ParseInts(string str, char separator){
|
||||||
|
return str.Split(new char[]{ separator }, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ConvertPascalCaseToScreamingSnakeCase(string str){
|
||||||
|
return Regex.Replace(str, @"(\p{Ll})(\P{Ll})|(\P{Ll})(\P{Ll}\p{Ll})", "$1$3_$2$4").ToUpper();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
125
Core/Utils/TwitterUtils.cs
Normal file
125
Core/Utils/TwitterUtils.cs
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
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/([^/]+)/?$", RegexOptions.Compiled), false);
|
||||||
|
public static Regex RegexAccount => RegexAccountLazy.Value;
|
||||||
|
|
||||||
|
public static readonly string[] DictionaryWords = {
|
||||||
|
"tweetdeck", "TweetDeck", "tweetduck", "TweetDuck", "TD"
|
||||||
|
};
|
||||||
|
|
||||||
|
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 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 = BrowserUtils.GetFileNameFromUrl(ExtractMediaBaseLink(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 filename = BrowserUtils.GetFileNameFromUrl(url);
|
||||||
|
string ext = Path.GetExtension(filename);
|
||||||
|
|
||||||
|
using(SaveFileDialog dialog = new SaveFileDialog{
|
||||||
|
AutoUpgradeEnabled = true,
|
||||||
|
OverwritePrompt = true,
|
||||||
|
Title = "Save video",
|
||||||
|
FileName = filename,
|
||||||
|
Filter = "Video"+(string.IsNullOrEmpty(ext) ? " (unknown)|*.*" : $" (*{ext})|*{ext}")
|
||||||
|
}){
|
||||||
|
if (dialog.ShowDialog() == DialogResult.OK){
|
||||||
|
BrowserUtils.DownloadFileAsync(url, dialog.FileName, null, ex => {
|
||||||
|
FormMessage.Error("Image Download", "An error occurred while downloading the image: "+ex.Message, FormMessage.OK);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Management;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -11,13 +12,29 @@ namespace TweetDuck.Core.Utils{
|
|||||||
private static readonly Lazy<Regex> RegexStripHtmlStyles = new Lazy<Regex>(() => new Regex(@"\s?(?:style|class)="".*?"""), false);
|
private static readonly Lazy<Regex> RegexStripHtmlStyles = new Lazy<Regex>(() => new Regex(@"\s?(?:style|class)="".*?"""), false);
|
||||||
private static readonly Lazy<Regex> RegexOffsetClipboardHtml = new Lazy<Regex>(() => new Regex(@"(?<=EndHTML:|EndFragment:)(\d+)"), false);
|
private static readonly Lazy<Regex> RegexOffsetClipboardHtml = new Lazy<Regex>(() => new Regex(@"(?<=EndHTML:|EndFragment:)(\d+)"), false);
|
||||||
|
|
||||||
|
public static int CurrentProcessID { get; }
|
||||||
public static bool ShouldAvoidToolWindow { get; }
|
public static bool ShouldAvoidToolWindow { get; }
|
||||||
|
|
||||||
static WindowsUtils(){
|
static WindowsUtils(){
|
||||||
|
using(Process me = Process.GetCurrentProcess()){
|
||||||
|
CurrentProcessID = me.Id;
|
||||||
|
}
|
||||||
|
|
||||||
Version ver = Environment.OSVersion.Version;
|
Version ver = Environment.OSVersion.Version;
|
||||||
ShouldAvoidToolWindow = ver.Major == 6 && ver.Minor == 2; // windows 8/10
|
ShouldAvoidToolWindow = ver.Major == 6 && ver.Minor == 2; // windows 8/10
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void CreateDirectoryForFile(string file){
|
||||||
|
string dir = Path.GetDirectoryName(file);
|
||||||
|
|
||||||
|
if (dir == null){
|
||||||
|
throw new ArgumentException("Invalid file path: "+file);
|
||||||
|
}
|
||||||
|
else if (dir.Length > 0){
|
||||||
|
Directory.CreateDirectory(dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static bool CheckFolderWritePermission(string path){
|
public static bool CheckFolderWritePermission(string path){
|
||||||
string testFile = Path.Combine(path, ".test");
|
string testFile = Path.Combine(path, ".test");
|
||||||
|
|
||||||
@@ -72,6 +89,20 @@ namespace TweetDuck.Core.Utils{
|
|||||||
}).Start();
|
}).Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsChildProcess(int pid){
|
||||||
|
try{
|
||||||
|
using(ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT ParentProcessId FROM Win32_Process WHERE ProcessId = "+pid)){
|
||||||
|
foreach(ManagementBaseObject obj in searcher.Get()){
|
||||||
|
return (uint)obj["ParentProcessId"] == CurrentProcessID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}catch{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void ClipboardStripHtmlStyles(){
|
public static void ClipboardStripHtmlStyles(){
|
||||||
if (!Clipboard.ContainsText(TextDataFormat.Html)){
|
if (!Clipboard.ContainsText(TextDataFormat.Html)){
|
||||||
return;
|
return;
|
||||||
@@ -105,7 +136,7 @@ namespace TweetDuck.Core.Utils{
|
|||||||
try{
|
try{
|
||||||
Clipboard.SetDataObject(obj);
|
Clipboard.SetDataObject(obj);
|
||||||
}catch(ExternalException e){
|
}catch(ExternalException e){
|
||||||
Program.Reporter.HandleException("Clipboard Error", Program.BrandName+" could not access the clipboard as it is currently used by another process.", true, e);
|
Program.Reporter.HandleException("Clipboard Error", "TweetDuck could not access the clipboard as it is currently used by another process.", true, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using TweetDuck.Core.Utils;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Other.Settings.Export{
|
namespace TweetDuck.Data{
|
||||||
class CombinedFileStream : IDisposable{
|
sealed class CombinedFileStream : IDisposable{
|
||||||
public const char KeySeparator = '|';
|
public const char KeySeparator = '|';
|
||||||
|
|
||||||
private readonly Stream stream;
|
private readonly Stream stream;
|
||||||
@@ -79,8 +80,7 @@ namespace TweetDuck.Core.Other.Settings.Export{
|
|||||||
stream.Position += BitConverter.ToInt32(contentLength, 0);
|
stream.Position += BitConverter.ToInt32(contentLength, 0);
|
||||||
|
|
||||||
string keyName = Encoding.UTF8.GetString(name);
|
string keyName = Encoding.UTF8.GetString(name);
|
||||||
int separatorIndex = keyName.IndexOf(KeySeparator);
|
return StringUtils.ExtractBefore(keyName, KeySeparator);
|
||||||
return separatorIndex == -1 ? keyName : keyName.Substring(0, separatorIndex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Flush(){
|
public void Flush(){
|
||||||
@@ -91,20 +91,19 @@ namespace TweetDuck.Core.Other.Settings.Export{
|
|||||||
stream.Dispose();
|
stream.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Entry{
|
public sealed class Entry{
|
||||||
public string Identifier { get; }
|
public string Identifier { get; }
|
||||||
|
|
||||||
public string KeyName{
|
public string KeyName{
|
||||||
get{
|
get{
|
||||||
int index = Identifier.IndexOf(KeySeparator);
|
return StringUtils.ExtractBefore(Identifier, KeySeparator);
|
||||||
return index == -1 ? Identifier : Identifier.Substring(0, index);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string[] KeyValue{
|
public string[] KeyValue{
|
||||||
get{
|
get{
|
||||||
int index = Identifier.IndexOf(KeySeparator);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,11 +120,7 @@ namespace TweetDuck.Core.Other.Settings.Export{
|
|||||||
|
|
||||||
public void WriteToFile(string path, bool createDirectory){
|
public void WriteToFile(string path, bool createDirectory){
|
||||||
if (createDirectory){
|
if (createDirectory){
|
||||||
string dir = Path.GetDirectoryName(path);
|
WindowsUtils.CreateDirectoryForFile(path);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(dir)){
|
|
||||||
Directory.CreateDirectory(dir);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
File.WriteAllBytes(path, contents);
|
File.WriteAllBytes(path, contents);
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Utils{
|
namespace TweetDuck.Data{
|
||||||
class CommandLineArgs{
|
sealed class CommandLineArgs{
|
||||||
public static CommandLineArgs FromStringArray(char entryChar, string[] array){
|
public static CommandLineArgs FromStringArray(char entryChar, string[] array){
|
||||||
CommandLineArgs args = new CommandLineArgs();
|
CommandLineArgs args = new CommandLineArgs();
|
||||||
ReadStringArray(entryChar, array, args);
|
ReadStringArray(entryChar, array, args);
|
||||||
@@ -32,37 +33,67 @@ namespace TweetDuck.Core.Utils{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 HashSet<string> flags = new HashSet<string>();
|
||||||
private readonly Dictionary<string, string> values = new Dictionary<string, string>();
|
private readonly Dictionary<string, string> values = new Dictionary<string, string>();
|
||||||
|
|
||||||
public int Count => flags.Count+values.Count;
|
public int Count => flags.Count+values.Count;
|
||||||
|
|
||||||
public void AddFlag(string flag){
|
public void AddFlag(string flag){
|
||||||
flags.Add(flag.ToLowerInvariant());
|
flags.Add(flag.ToLower());
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HasFlag(string flag){
|
public bool HasFlag(string flag){
|
||||||
return flags.Contains(flag.ToLowerInvariant());
|
return flags.Contains(flag.ToLower());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveFlag(string flag){
|
public void RemoveFlag(string flag){
|
||||||
flags.Remove(flag.ToLowerInvariant());
|
flags.Remove(flag.ToLower());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetValue(string key, string value){
|
public void SetValue(string key, string value){
|
||||||
values[key.ToLowerInvariant()] = value;
|
values[key.ToLower()] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HasValue(string key){
|
public bool HasValue(string key){
|
||||||
return values.ContainsKey(key.ToLowerInvariant());
|
return values.ContainsKey(key.ToLower());
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetValue(string key, string defaultValue){
|
public string GetValue(string key, string defaultValue){
|
||||||
return values.TryGetValue(key.ToLowerInvariant(), out string val) ? val : defaultValue;
|
return values.TryGetValue(key.ToLower(), out string val) ? val : defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveValue(string key){
|
public void RemoveValue(string key){
|
||||||
values.Remove(key.ToLowerInvariant());
|
values.Remove(key.ToLower());
|
||||||
}
|
}
|
||||||
|
|
||||||
public CommandLineArgs Clone(){
|
public CommandLineArgs Clone(){
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Utils{
|
namespace TweetDuck.Data{
|
||||||
class InjectedHTML{
|
sealed class InjectedHTML{
|
||||||
public enum Position{
|
public enum Position{
|
||||||
Before, After
|
Before, After
|
||||||
}
|
}
|
||||||
146
Data/Serialization/FileSerializer.cs
Normal file
146
Data/Serialization/FileSerializer.cs
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
|
namespace TweetDuck.Data.Serialization{
|
||||||
|
sealed class FileSerializer<T>{
|
||||||
|
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;
|
||||||
|
|
||||||
|
public FileSerializer(){
|
||||||
|
this.props = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(prop => prop.CanWrite).ToDictionary(prop => prop.Name);
|
||||||
|
this.converters = new Dictionary<Type, ITypeConverter>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegisterTypeConverter(Type type, ITypeConverter converter){
|
||||||
|
converters[type] = converter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Write(string file, T obj){
|
||||||
|
using(StreamWriter writer = new StreamWriter(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))){
|
||||||
|
foreach(KeyValuePair<string, PropertyInfo> prop in props){
|
||||||
|
Type type = prop.Value.PropertyType;
|
||||||
|
object value = prop.Value.GetValue(obj);
|
||||||
|
|
||||||
|
if (!converters.TryGetValue(type, out ITypeConverter serializer)) {
|
||||||
|
serializer = BasicSerializerObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serializer.TryWriteType(type, value, out string converted)){
|
||||||
|
if (converted != null){
|
||||||
|
writer.Write($"{prop.Key} {converted.Replace(Environment.NewLine, NewLineCustom)}");
|
||||||
|
writer.Write(NewLineReal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
throw new SerializationException($"Invalid serialization type, conversion failed for: {type}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Read(string file, T obj){
|
||||||
|
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() <= 1){
|
||||||
|
throw new FormatException("Input appears to be a binary file.");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach(string line in reader.ReadToEnd().Split(new string[]{ NewLineReal }, StringSplitOptions.RemoveEmptyEntries)){
|
||||||
|
int space = line.IndexOf(' ');
|
||||||
|
|
||||||
|
if (space == -1){
|
||||||
|
throw new SerializationException($"Invalid file format, missing separator: {line}");
|
||||||
|
}
|
||||||
|
|
||||||
|
string property = line.Substring(0, space);
|
||||||
|
string value = line.Substring(space+1).Replace(NewLineCustom, Environment.NewLine);
|
||||||
|
|
||||||
|
if (props.TryGetValue(property, out PropertyInfo info)){
|
||||||
|
if (!converters.TryGetValue(info.PropertyType, out ITypeConverter serializer)) {
|
||||||
|
serializer = BasicSerializerObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serializer.TryReadType(info.PropertyType, value, out object converted)){
|
||||||
|
info.SetValue(obj, converted);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
throw new SerializationException($"Invalid file format, cannot convert value: {value} (property: {property})");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
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 sealed class BasicTypeConverter : ITypeConverter{
|
||||||
|
bool ITypeConverter.TryWriteType(Type type, object value, out string converted){
|
||||||
|
switch(Type.GetTypeCode(type)){
|
||||||
|
case TypeCode.Boolean:
|
||||||
|
converted = value.ToString();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case TypeCode.Int32:
|
||||||
|
converted = ((int)value).ToString(); // cast required for enums
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case TypeCode.String:
|
||||||
|
converted = value?.ToString();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
converted = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ITypeConverter.TryReadType(Type type, string value, out object converted){
|
||||||
|
switch(Type.GetTypeCode(type)){
|
||||||
|
case TypeCode.Boolean:
|
||||||
|
if (bool.TryParse(value, out bool b)){
|
||||||
|
converted = b;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else goto default;
|
||||||
|
|
||||||
|
case TypeCode.Int32:
|
||||||
|
if (int.TryParse(value, out int i)){
|
||||||
|
converted = i;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else goto default;
|
||||||
|
|
||||||
|
case TypeCode.String:
|
||||||
|
converted = value;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
converted = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
8
Data/Serialization/ITypeConverter.cs
Normal file
8
Data/Serialization/ITypeConverter.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace TweetDuck.Data.Serialization{
|
||||||
|
interface ITypeConverter{
|
||||||
|
bool TryWriteType(Type type, object value, out string converted);
|
||||||
|
bool TryReadType(Type type, string value, out object converted);
|
||||||
|
}
|
||||||
|
}
|
||||||
28
Data/Serialization/SingleTypeConverter.cs
Normal file
28
Data/Serialization/SingleTypeConverter.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace TweetDuck.Data.Serialization{
|
||||||
|
sealed class SingleTypeConverter<T> : ITypeConverter{
|
||||||
|
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{
|
||||||
|
converted = ConvertToString((T)value);
|
||||||
|
return true;
|
||||||
|
}catch{
|
||||||
|
converted = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ITypeConverter.TryReadType(Type type, string value, out object converted){
|
||||||
|
try{
|
||||||
|
converted = ConvertToObject(value);
|
||||||
|
return true;
|
||||||
|
}catch{
|
||||||
|
converted = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Utils{
|
namespace TweetDuck.Data{
|
||||||
class TwoKeyDictionary<K1, K2, V>{
|
sealed class TwoKeyDictionary<K1, K2, V>{
|
||||||
private readonly Dictionary<K1, Dictionary<K2, V>> dict;
|
private readonly Dictionary<K1, Dictionary<K2, V>> dict;
|
||||||
private readonly int innerCapacity;
|
private readonly int innerCapacity;
|
||||||
|
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
using System;
|
using System.Drawing;
|
||||||
using System.Drawing;
|
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
|
using TweetDuck.Core.Utils;
|
||||||
|
using TweetDuck.Data.Serialization;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Utils{
|
namespace TweetDuck.Data{
|
||||||
[Serializable]
|
sealed class WindowState{
|
||||||
class WindowState{
|
|
||||||
private Rectangle rect;
|
private Rectangle rect;
|
||||||
private bool isMaximized;
|
private bool isMaximized;
|
||||||
|
|
||||||
@@ -26,5 +26,17 @@ namespace TweetDuck.Core.Utils{
|
|||||||
Save(form);
|
Save(form);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static readonly SingleTypeConverter<WindowState> Converter = new SingleTypeConverter<WindowState>{
|
||||||
|
ConvertToString = value => $"{(value.isMaximized ? 'M' : '_')}{value.rect.X} {value.rect.Y} {value.rect.Width} {value.rect.Height}",
|
||||||
|
ConvertToObject = value => {
|
||||||
|
int[] elements = StringUtils.ParseInts(value.Substring(1), ' ');
|
||||||
|
|
||||||
|
return new WindowState{
|
||||||
|
rect = new Rectangle(elements[0], elements[1], elements[2], elements[3]),
|
||||||
|
isMaximized = value[0] == 'M'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
1
Plugins/Controls/PluginControl.Designer.cs
generated
1
Plugins/Controls/PluginControl.Designer.cs
generated
@@ -152,7 +152,6 @@
|
|||||||
| System.Windows.Forms.AnchorStyles.Left)));
|
| System.Windows.Forms.AnchorStyles.Left)));
|
||||||
this.labelType.BackColor = System.Drawing.Color.DarkGray;
|
this.labelType.BackColor = System.Drawing.Color.DarkGray;
|
||||||
this.labelType.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
this.labelType.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
||||||
this.labelType.LineHeight = 9;
|
|
||||||
this.labelType.Location = new System.Drawing.Point(0, 0);
|
this.labelType.Location = new System.Drawing.Point(0, 0);
|
||||||
this.labelType.Name = "labelType";
|
this.labelType.Name = "labelType";
|
||||||
this.labelType.Size = new System.Drawing.Size(18, 109);
|
this.labelType.Size = new System.Drawing.Size(18, 109);
|
||||||
|
|||||||
@@ -3,14 +3,17 @@ using System.Diagnostics;
|
|||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetDuck.Plugins.Enums;
|
using TweetDuck.Plugins.Enums;
|
||||||
|
|
||||||
namespace TweetDuck.Plugins.Controls{
|
namespace TweetDuck.Plugins.Controls{
|
||||||
partial class PluginControl : UserControl{
|
sealed partial class PluginControl : UserControl{
|
||||||
private readonly PluginManager pluginManager;
|
private readonly PluginManager pluginManager;
|
||||||
private readonly Plugin plugin;
|
private readonly Plugin plugin;
|
||||||
|
|
||||||
|
private readonly float dpiScale;
|
||||||
|
|
||||||
public PluginControl(){
|
public PluginControl(){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
@@ -19,12 +22,16 @@ namespace TweetDuck.Plugins.Controls{
|
|||||||
this.pluginManager = pluginManager;
|
this.pluginManager = pluginManager;
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
|
|
||||||
|
this.dpiScale = this.GetDPIScale();
|
||||||
|
|
||||||
this.labelName.Text = plugin.Name;
|
this.labelName.Text = plugin.Name;
|
||||||
this.labelDescription.Text = plugin.CanRun ? plugin.Description : "This plugin requires "+Program.BrandName+" "+plugin.RequiredVersion+" or newer.";
|
this.labelDescription.Text = plugin.CanRun ? plugin.Description : "This plugin requires TweetDuck "+plugin.RequiredVersion+" or newer.";
|
||||||
this.labelVersion.Text = plugin.Version;
|
this.labelVersion.Text = plugin.Version;
|
||||||
this.labelAuthor.Text = plugin.Author;
|
this.labelAuthor.Text = plugin.Author;
|
||||||
this.labelWebsite.Text = plugin.Website;
|
this.labelWebsite.Text = plugin.Website;
|
||||||
|
|
||||||
|
this.labelType.LineHeight = BrowserUtils.Scale(9, dpiScale);
|
||||||
|
|
||||||
UpdatePluginState();
|
UpdatePluginState();
|
||||||
|
|
||||||
if (labelDescription.Text.Length == 0){
|
if (labelDescription.Text.Length == 0){
|
||||||
@@ -40,7 +47,7 @@ namespace TweetDuck.Plugins.Controls{
|
|||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
labelDescription.MaximumSize = new Size(panelDescription.Width-SystemInformation.VerticalScrollBarWidth, 0);
|
labelDescription.MaximumSize = new Size(panelDescription.Width-SystemInformation.VerticalScrollBarWidth, 0);
|
||||||
Height = Math.Min(MinimumSize.Height+9+labelDescription.Height, MaximumSize.Height);
|
Height = Math.Min(MinimumSize.Height+BrowserUtils.Scale(9, dpiScale)+labelDescription.Height, MaximumSize.Height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,20 @@ namespace TweetDuck.Plugins.Enums{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetScriptFile(this PluginEnvironment environment){
|
public static bool IncludesDisabledPlugins(this PluginEnvironment environment){
|
||||||
|
return environment == PluginEnvironment.Browser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetScriptIdentifier(this PluginEnvironment environment){
|
||||||
|
switch(environment){
|
||||||
|
case PluginEnvironment.None: return "root:plugins";
|
||||||
|
case PluginEnvironment.Browser: return "root:plugins.browser";
|
||||||
|
case PluginEnvironment.Notification: return "root:plugins.notification";
|
||||||
|
default: return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetPluginScriptFile(this PluginEnvironment environment){
|
||||||
switch(environment){
|
switch(environment){
|
||||||
case PluginEnvironment.Browser: return "browser.js";
|
case PluginEnvironment.Browser: return "browser.js";
|
||||||
case PluginEnvironment.Notification: return "notification.js";
|
case PluginEnvironment.Notification: return "notification.js";
|
||||||
@@ -25,7 +38,7 @@ namespace TweetDuck.Plugins.Enums{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetScriptVariables(this PluginEnvironment environment){
|
public static string GetPluginScriptVariables(this PluginEnvironment environment){
|
||||||
switch(environment){
|
switch(environment){
|
||||||
case PluginEnvironment.Browser: return "$,$TD,$TDP,TD";
|
case PluginEnvironment.Browser: return "$,$TD,$TDP,TD";
|
||||||
case PluginEnvironment.Notification: return "$TD,$TDP";
|
case PluginEnvironment.Notification: return "$TD,$TDP";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace TweetDuck.Plugins.Events{
|
namespace TweetDuck.Plugins.Events{
|
||||||
class PluginChangedStateEventArgs : EventArgs{
|
sealed class PluginChangedStateEventArgs : EventArgs{
|
||||||
public Plugin Plugin { get; }
|
public Plugin Plugin { get; }
|
||||||
public bool IsEnabled { get; }
|
public bool IsEnabled { get; }
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace TweetDuck.Plugins.Events{
|
namespace TweetDuck.Plugins.Events{
|
||||||
class PluginErrorEventArgs : EventArgs{
|
sealed class PluginErrorEventArgs : EventArgs{
|
||||||
public bool HasErrors => Errors.Count > 0;
|
public bool HasErrors => Errors.Count > 0;
|
||||||
|
|
||||||
public IList<string> Errors;
|
public IList<string> Errors { get; }
|
||||||
|
|
||||||
public PluginErrorEventArgs(IList<string> errors){
|
public PluginErrorEventArgs(IList<string> errors){
|
||||||
this.Errors = errors;
|
this.Errors = errors;
|
||||||
|
|||||||
@@ -6,7 +6,10 @@ using System.Text;
|
|||||||
using TweetDuck.Plugins.Enums;
|
using TweetDuck.Plugins.Enums;
|
||||||
|
|
||||||
namespace TweetDuck.Plugins{
|
namespace TweetDuck.Plugins{
|
||||||
class Plugin{
|
sealed class Plugin{
|
||||||
|
private static readonly Version AppVersion = new Version(Program.VersionTag);
|
||||||
|
private const string VersionWildcard = "*";
|
||||||
|
|
||||||
public string Identifier { get; }
|
public string Identifier { get; }
|
||||||
public PluginGroup Group { get; }
|
public PluginGroup Group { get; }
|
||||||
public PluginEnvironment Environments { get; private set; }
|
public PluginEnvironment Environments { get; private set; }
|
||||||
@@ -42,7 +45,7 @@ namespace TweetDuck.Plugins{
|
|||||||
|
|
||||||
private readonly string pathRoot;
|
private readonly string pathRoot;
|
||||||
private readonly string pathData;
|
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", "" },
|
{ "NAME", "" },
|
||||||
{ "DESCRIPTION", "" },
|
{ "DESCRIPTION", "" },
|
||||||
{ "AUTHOR", "(anonymous)" },
|
{ "AUTHOR", "(anonymous)" },
|
||||||
@@ -50,7 +53,7 @@ namespace TweetDuck.Plugins{
|
|||||||
{ "WEBSITE", "" },
|
{ "WEBSITE", "" },
|
||||||
{ "CONFIGFILE", "" },
|
{ "CONFIGFILE", "" },
|
||||||
{ "CONFIGDEFAULT", "" },
|
{ "CONFIGDEFAULT", "" },
|
||||||
{ "REQUIRES", "*" }
|
{ "REQUIRES", VersionWildcard }
|
||||||
};
|
};
|
||||||
|
|
||||||
private bool? canRun;
|
private bool? canRun;
|
||||||
@@ -84,7 +87,7 @@ namespace TweetDuck.Plugins{
|
|||||||
|
|
||||||
public string GetScriptPath(PluginEnvironment environment){
|
public string GetScriptPath(PluginEnvironment environment){
|
||||||
if (Environments.HasFlag(environment)){
|
if (Environments.HasFlag(environment)){
|
||||||
string file = environment.GetScriptFile();
|
string file = environment.GetPluginScriptFile();
|
||||||
return file != null ? Path.Combine(pathRoot, file) : string.Empty;
|
return file != null ? Path.Combine(pathRoot, file) : string.Empty;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
@@ -132,8 +135,7 @@ namespace TweetDuck.Plugins{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override bool Equals(object obj){
|
public override bool Equals(object obj){
|
||||||
Plugin plugin = obj as Plugin;
|
return obj is Plugin plugin && plugin.Identifier.Equals(Identifier);
|
||||||
return plugin != null && plugin.Identifier.Equals(Identifier);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Plugin CreateFromFolder(string path, PluginGroup group, out string error){
|
public static Plugin CreateFromFolder(string path, PluginGroup group, out string error){
|
||||||
@@ -153,7 +155,7 @@ namespace TweetDuck.Plugins{
|
|||||||
|
|
||||||
private static bool LoadEnvironments(string path, Plugin plugin, out string error){
|
private static bool LoadEnvironments(string path, Plugin plugin, out string error){
|
||||||
foreach(string file in Directory.EnumerateFiles(path, "*.js", SearchOption.TopDirectoryOnly).Select(Path.GetFileName)){
|
foreach(string file in Directory.EnumerateFiles(path, "*.js", SearchOption.TopDirectoryOnly).Select(Path.GetFileName)){
|
||||||
plugin.Environments |= PluginEnvironmentExtensions.Values.FirstOrDefault(env => file.Equals(env.GetScriptFile(), StringComparison.Ordinal));
|
plugin.Environments |= PluginEnvironmentExtensions.Values.FirstOrDefault(env => file.Equals(env.GetPluginScriptFile(), StringComparison.Ordinal));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (plugin.Environments == PluginEnvironment.None){
|
if (plugin.Environments == PluginEnvironment.None){
|
||||||
@@ -184,7 +186,7 @@ namespace TweetDuck.Plugins{
|
|||||||
plugin.metadata[currentTag] = currentContents;
|
plugin.metadata[currentTag] = currentContents;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentTag = line.Substring(1, line.Length-2).ToUpperInvariant();
|
currentTag = line.Substring(1, line.Length-2).ToUpper();
|
||||||
currentContents = "";
|
currentContents = "";
|
||||||
|
|
||||||
if (line.Equals(endTag[0])){
|
if (line.Equals(endTag[0])){
|
||||||
@@ -210,7 +212,7 @@ namespace TweetDuck.Plugins{
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (plugin.RequiredVersion.Length == 0 || !(plugin.RequiredVersion.Equals("*") || System.Version.TryParse(plugin.RequiredVersion, out Version _))){
|
if (plugin.RequiredVersion.Length == 0 || !(plugin.RequiredVersion == VersionWildcard || System.Version.TryParse(plugin.RequiredVersion, out Version _))){
|
||||||
error = "Plugin contains invalid version: "+plugin.RequiredVersion;
|
error = "Plugin contains invalid version: "+plugin.RequiredVersion;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -222,7 +224,7 @@ namespace TweetDuck.Plugins{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static bool CheckRequiredVersion(string requires){
|
private static bool CheckRequiredVersion(string requires){
|
||||||
return requires.Equals("*", StringComparison.Ordinal) || Program.Version >= new Version(requires);
|
return requires == VersionWildcard || AppVersion >= new Version(requires);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,12 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
using TweetDuck.Data;
|
||||||
using TweetDuck.Plugins.Enums;
|
using TweetDuck.Plugins.Enums;
|
||||||
using TweetDuck.Plugins.Events;
|
using TweetDuck.Plugins.Events;
|
||||||
|
|
||||||
namespace TweetDuck.Plugins{
|
namespace TweetDuck.Plugins{
|
||||||
class PluginBridge{
|
sealed class PluginBridge{
|
||||||
private static string SanitizeCacheKey(string key){
|
private static string SanitizeCacheKey(string key){
|
||||||
return key.Replace('\\', '/').Trim();
|
return key.Replace('\\', '/').Trim();
|
||||||
}
|
}
|
||||||
@@ -47,9 +48,9 @@ namespace TweetDuck.Plugins{
|
|||||||
|
|
||||||
if (fullPath.Length == 0){
|
if (fullPath.Length == 0){
|
||||||
switch(folder){
|
switch(folder){
|
||||||
case PluginFolder.Data: throw new Exception("File path has to be relative to the plugin data folder.");
|
case PluginFolder.Data: throw new ArgumentException("File path has to be relative to the plugin data folder.");
|
||||||
case PluginFolder.Root: throw new Exception("File path has to be relative to the plugin root folder.");
|
case PluginFolder.Root: throw new ArgumentException("File path has to be relative to the plugin root folder.");
|
||||||
default: throw new Exception("Invalid folder type "+folder+", this is a "+Program.BrandName+" error.");
|
default: throw new ArgumentException("Invalid folder type "+folder+", this is a TweetDuck error.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
@@ -67,9 +68,9 @@ namespace TweetDuck.Plugins{
|
|||||||
try{
|
try{
|
||||||
return fileCache[token, cacheKey] = File.ReadAllText(fullPath, Encoding.UTF8);
|
return fileCache[token, cacheKey] = File.ReadAllText(fullPath, Encoding.UTF8);
|
||||||
}catch(FileNotFoundException){
|
}catch(FileNotFoundException){
|
||||||
throw new Exception("File not found.");
|
throw new FileNotFoundException("File not found.");
|
||||||
}catch(DirectoryNotFoundException){
|
}catch(DirectoryNotFoundException){
|
||||||
throw new Exception("Directory not found.");
|
throw new DirectoryNotFoundException("Directory not found.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,9 +79,7 @@ namespace TweetDuck.Plugins{
|
|||||||
public void WriteFile(int token, string path, string contents){
|
public void WriteFile(int token, string path, string contents){
|
||||||
string fullPath = GetFullPathOrThrow(token, PluginFolder.Data, path);
|
string fullPath = GetFullPathOrThrow(token, PluginFolder.Data, path);
|
||||||
|
|
||||||
// ReSharper disable once AssignNullToNotNullAttribute
|
WindowsUtils.CreateDirectoryForFile(fullPath);
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(fullPath));
|
|
||||||
|
|
||||||
File.WriteAllText(fullPath, contents, Encoding.UTF8);
|
File.WriteAllText(fullPath, contents, Encoding.UTF8);
|
||||||
fileCache[token, SanitizeCacheKey(path)] = contents;
|
fileCache[token, SanitizeCacheKey(path)] = contents;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,39 +8,47 @@ namespace TweetDuck.Plugins{
|
|||||||
sealed class PluginConfig{
|
sealed class PluginConfig{
|
||||||
public event EventHandler<PluginChangedStateEventArgs> InternalPluginChangedState; // should only be accessed from PluginManager
|
public event EventHandler<PluginChangedStateEventArgs> InternalPluginChangedState; // should only be accessed from PluginManager
|
||||||
|
|
||||||
public IEnumerable<string> DisabledPlugins => Disabled;
|
public IEnumerable<string> DisabledPlugins => disabled;
|
||||||
public bool AnyDisabled => Disabled.Count > 0;
|
public bool AnyDisabled => disabled.Count > 0;
|
||||||
|
|
||||||
private readonly HashSet<string> Disabled = new HashSet<string>{
|
private static readonly string[] DefaultDisabled = {
|
||||||
"official/clear-columns",
|
"official/clear-columns",
|
||||||
"official/reply-account"
|
"official/reply-account"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private readonly HashSet<string> disabled = new HashSet<string>();
|
||||||
|
|
||||||
public void SetEnabled(Plugin plugin, bool enabled){
|
public void SetEnabled(Plugin plugin, bool enabled){
|
||||||
if ((enabled && Disabled.Remove(plugin.Identifier)) || (!enabled && Disabled.Add(plugin.Identifier))){
|
if ((enabled && disabled.Remove(plugin.Identifier)) || (!enabled && disabled.Add(plugin.Identifier))){
|
||||||
InternalPluginChangedState?.Invoke(this, new PluginChangedStateEventArgs(plugin, enabled));
|
InternalPluginChangedState?.Invoke(this, new PluginChangedStateEventArgs(plugin, enabled));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsEnabled(Plugin plugin){
|
public bool IsEnabled(Plugin plugin){
|
||||||
return !Disabled.Contains(plugin.Identifier);
|
return !disabled.Contains(plugin.Identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Load(string file){
|
public void Load(string file){
|
||||||
try{
|
try{
|
||||||
using(FileStream stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
using(StreamReader reader = new StreamReader(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read), Encoding.UTF8)){
|
||||||
using(StreamReader reader = new StreamReader(stream, Encoding.UTF8)){
|
|
||||||
string line = reader.ReadLine();
|
string line = reader.ReadLine();
|
||||||
|
|
||||||
if (line == "#Disabled"){
|
if (line == "#Disabled"){
|
||||||
Disabled.Clear();
|
disabled.Clear();
|
||||||
|
|
||||||
while((line = reader.ReadLine()) != null){
|
while((line = reader.ReadLine()) != null){
|
||||||
Disabled.Add(line);
|
disabled.Add(line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}catch(FileNotFoundException){
|
}catch(FileNotFoundException){
|
||||||
|
disabled.Clear();
|
||||||
|
|
||||||
|
foreach(string identifier in DefaultDisabled){
|
||||||
|
disabled.Add(identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
Save(file);
|
||||||
}catch(DirectoryNotFoundException){
|
}catch(DirectoryNotFoundException){
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
Program.Reporter.HandleException("Plugin Configuration Error", "Could not read the plugin configuration file. If you continue, the list of disabled plugins will be reset to default.", true, e);
|
Program.Reporter.HandleException("Plugin Configuration Error", "Could not read the plugin configuration file. If you continue, the list of disabled plugins will be reset to default.", true, e);
|
||||||
@@ -49,12 +57,11 @@ namespace TweetDuck.Plugins{
|
|||||||
|
|
||||||
public void Save(string file){
|
public void Save(string file){
|
||||||
try{
|
try{
|
||||||
using(FileStream stream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))
|
using(StreamWriter writer = new StreamWriter(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None), Encoding.UTF8)){
|
||||||
using(StreamWriter writer = new StreamWriter(stream, Encoding.UTF8)){
|
|
||||||
writer.WriteLine("#Disabled");
|
writer.WriteLine("#Disabled");
|
||||||
|
|
||||||
foreach(string disabled in Disabled){
|
foreach(string identifier in disabled){
|
||||||
writer.WriteLine(disabled);
|
writer.WriteLine(identifier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
|
|||||||
@@ -9,12 +9,14 @@ using TweetDuck.Resources;
|
|||||||
|
|
||||||
namespace TweetDuck.Plugins{
|
namespace TweetDuck.Plugins{
|
||||||
sealed class PluginManager{
|
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 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 PathOfficialPlugins => Path.Combine(rootPath, "official");
|
||||||
public string PathCustomPlugins => Path.Combine(rootPath, "user");
|
public string PathCustomPlugins => Path.Combine(rootPath, "user");
|
||||||
|
|
||||||
@@ -43,14 +45,7 @@ namespace TweetDuck.Plugins{
|
|||||||
this.Bridge = new PluginBridge(this);
|
this.Bridge = new PluginBridge(this);
|
||||||
|
|
||||||
Config.Load(configPath);
|
Config.Load(configPath);
|
||||||
|
|
||||||
Config.InternalPluginChangedState += Config_InternalPluginChangedState;
|
Config.InternalPluginChangedState += Config_InternalPluginChangedState;
|
||||||
Program.UserConfigReplaced += Program_UserConfigReplaced;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Program_UserConfigReplaced(object sender, EventArgs e){
|
|
||||||
Config.Load(configPath);
|
|
||||||
Reload();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Config_InternalPluginChangedState(object sender, PluginChangedStateEventArgs e){
|
private void Config_InternalPluginChangedState(object sender, PluginChangedStateEventArgs e){
|
||||||
@@ -81,6 +76,8 @@ namespace TweetDuck.Plugins{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void Reload(){
|
public void Reload(){
|
||||||
|
Config.Load(configPath);
|
||||||
|
|
||||||
plugins.Clear();
|
plugins.Clear();
|
||||||
tokens.Clear();
|
tokens.Clear();
|
||||||
|
|
||||||
@@ -97,7 +94,17 @@ namespace TweetDuck.Plugins{
|
|||||||
Reloaded?.Invoke(this, new PluginErrorEventArgs(loadErrors));
|
Reloaded?.Invoke(this, new PluginErrorEventArgs(loadErrors));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ExecutePlugins(IFrame frame, PluginEnvironment environment, bool includeDisabled){
|
public void ExecutePlugins(IFrame frame, PluginEnvironment environment){
|
||||||
|
if (HasAnyPlugin(environment)){
|
||||||
|
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){
|
if (includeDisabled){
|
||||||
ScriptLoader.ExecuteScript(frame, PluginScriptGenerator.GenerateConfig(Config), "gen:pluginconfig");
|
ScriptLoader.ExecuteScript(frame, PluginScriptGenerator.GenerateConfig(Config), "gen:pluginconfig");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System.Globalization;
|
using TweetDuck.Plugins.Enums;
|
||||||
using TweetDuck.Plugins.Enums;
|
|
||||||
|
|
||||||
namespace TweetDuck.Plugins{
|
namespace TweetDuck.Plugins{
|
||||||
static class PluginScriptGenerator{
|
static class PluginScriptGenerator{
|
||||||
@@ -9,9 +8,9 @@ namespace TweetDuck.Plugins{
|
|||||||
|
|
||||||
public static string GeneratePlugin(string pluginIdentifier, string pluginContents, int pluginToken, PluginEnvironment environment){
|
public static string GeneratePlugin(string pluginIdentifier, string pluginContents, int pluginToken, PluginEnvironment environment){
|
||||||
return PluginGen
|
return PluginGen
|
||||||
.Replace("%params", environment.GetScriptVariables())
|
.Replace("%params", environment.GetPluginScriptVariables())
|
||||||
.Replace("%id", pluginIdentifier)
|
.Replace("%id", pluginIdentifier)
|
||||||
.Replace("%token", pluginToken.ToString(CultureInfo.InvariantCulture))
|
.Replace("%token", pluginToken.ToString())
|
||||||
.Replace("%contents", pluginContents);
|
.Replace("%contents", pluginContents);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,7 +18,7 @@ namespace TweetDuck.Plugins{
|
|||||||
|
|
||||||
/* PluginGen
|
/* PluginGen
|
||||||
|
|
||||||
(function(%params, $i, $d){
|
(function(%params, $d){
|
||||||
let tmp = {
|
let tmp = {
|
||||||
id: '%id',
|
id: '%id',
|
||||||
obj: new class extends PluginBase{%contents}
|
obj: new class extends PluginBase{%contents}
|
||||||
|
|||||||
151
Program.cs
151
Program.cs
@@ -1,29 +1,28 @@
|
|||||||
using CefSharp;
|
using CefSharp;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Configuration;
|
using TweetDuck.Configuration;
|
||||||
using TweetDuck.Core;
|
using TweetDuck.Core;
|
||||||
using TweetDuck.Core.Handling;
|
using TweetDuck.Core.Handling.General;
|
||||||
using TweetDuck.Core.Other;
|
using TweetDuck.Core.Other;
|
||||||
using TweetDuck.Core.Other.Settings.Export;
|
using TweetDuck.Core.Other.Settings.Export;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetDuck.Plugins;
|
using TweetDuck.Data;
|
||||||
using TweetDuck.Plugins.Events;
|
|
||||||
using TweetDuck.Updates;
|
using TweetDuck.Updates;
|
||||||
|
using TweetLib.Communication;
|
||||||
|
|
||||||
namespace TweetDuck{
|
namespace TweetDuck{
|
||||||
static class Program{
|
static class Program{
|
||||||
public const string BrandName = "TweetDuck";
|
public const string BrandName = "TweetDuck";
|
||||||
public const string Website = "https://tweetduck.chylex.com";
|
public const string Website = "https://tweetduck.chylex.com";
|
||||||
|
|
||||||
public const string VersionTag = "1.8";
|
public const string VersionTag = "1.9";
|
||||||
public const string VersionFull = "1.8.0.0";
|
|
||||||
|
|
||||||
public static readonly Version Version = new Version(VersionTag);
|
|
||||||
public static readonly bool IsPortable = File.Exists("makeportable");
|
public static readonly bool IsPortable = File.Exists("makeportable");
|
||||||
|
|
||||||
public static readonly string ProgramPath = AppDomain.CurrentDomain.BaseDirectory;
|
public static readonly string ProgramPath = AppDomain.CurrentDomain.BaseDirectory;
|
||||||
@@ -32,106 +31,98 @@ namespace TweetDuck{
|
|||||||
public static readonly string ScriptPath = Path.Combine(ProgramPath, "scripts");
|
public static readonly string ScriptPath = Path.Combine(ProgramPath, "scripts");
|
||||||
public static readonly string PluginPath = Path.Combine(ProgramPath, "plugins");
|
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");
|
public static readonly string PluginDataPath = Path.Combine(StoragePath, "TD_Plugins");
|
||||||
private static readonly string InstallerPath = Path.Combine(StoragePath, "TD_Updates");
|
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 ErrorLogFilePath => Path.Combine(StoragePath, "TD_Log.txt");
|
||||||
private static string ConsoleLogFilePath => Path.Combine(StoragePath, "TD_Console.txt");
|
private static string ConsoleLogFilePath => Path.Combine(StoragePath, "TD_Console.txt");
|
||||||
|
|
||||||
public static uint WindowRestoreMessage;
|
public static uint WindowRestoreMessage;
|
||||||
|
public static uint SubProcessMessage;
|
||||||
|
|
||||||
private static readonly LockManager LockManager = new LockManager(Path.Combine(StoragePath, ".lock"));
|
private static readonly LockManager LockManager = new LockManager(Path.Combine(StoragePath, ".lock"));
|
||||||
private static bool HasCleanedUp;
|
private static bool HasCleanedUp;
|
||||||
|
|
||||||
public static UserConfig UserConfig { get; private set; }
|
public static UserConfig UserConfig { get; private set; }
|
||||||
public static SystemConfig SystemConfig { get; private set; }
|
public static SystemConfig SystemConfig { get; private set; }
|
||||||
public static Reporter Reporter { get; private set; }
|
public static Reporter Reporter { get; }
|
||||||
|
public static CultureInfo Culture { get; }
|
||||||
|
|
||||||
public static event EventHandler UserConfigReplaced;
|
static Program(){
|
||||||
|
Culture = CultureInfo.CurrentCulture;
|
||||||
|
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
|
||||||
|
CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture;
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
CultureInfo.DefaultThreadCurrentUICulture = Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-us"); // force english exceptions
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Reporter = new Reporter(ErrorLogFilePath);
|
||||||
|
Reporter.SetupUnhandledExceptionHandler("TweetDuck Has Failed :(");
|
||||||
|
}
|
||||||
|
|
||||||
[STAThread]
|
[STAThread]
|
||||||
private static void Main(){
|
private static void Main(){
|
||||||
Application.EnableVisualStyles();
|
Application.EnableVisualStyles();
|
||||||
Application.SetCompatibleTextRenderingDefault(false);
|
Application.SetCompatibleTextRenderingDefault(false);
|
||||||
|
|
||||||
WindowRestoreMessage = NativeMethods.RegisterWindowMessage("TweetDuckRestore");
|
WindowRestoreMessage = Comms.RegisterMessage("TweetDuckRestore");
|
||||||
|
SubProcessMessage = Comms.RegisterMessage("TweetDuckSubProcess");
|
||||||
|
|
||||||
if (!WindowsUtils.CheckFolderWritePermission(StoragePath)){
|
if (!WindowsUtils.CheckFolderWritePermission(StoragePath)){
|
||||||
MessageBox.Show(BrandName+" does not have write permissions to the storage folder: "+StoragePath, "Permission Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
FormMessage.Warning("Permission Error", "TweetDuck does not have write permissions to the storage folder: "+StoragePath, FormMessage.OK);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Reporter = new Reporter(ErrorLogFilePath);
|
|
||||||
Reporter.SetupUnhandledExceptionHandler(BrandName+" Has Failed :(");
|
|
||||||
|
|
||||||
if (Arguments.HasFlag(Arguments.ArgRestart)){
|
if (Arguments.HasFlag(Arguments.ArgRestart)){
|
||||||
for(int attempt = 0; attempt < 21; attempt++){
|
LockManager.Result lockResult = LockManager.LockWait(10000);
|
||||||
LockManager.Result lockResult = LockManager.Lock();
|
|
||||||
|
|
||||||
if (lockResult == LockManager.Result.Success){
|
while(lockResult != LockManager.Result.Success){
|
||||||
break;
|
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);
|
||||||
else if (lockResult == LockManager.Result.Fail){
|
|
||||||
MessageBox.Show("An unknown error occurred accessing the data folder. Please, make sure "+BrandName+" is not already running. If the problem persists, try restarting your system.", BrandName+" Has Failed :(", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (attempt == 20){
|
else if (!FormMessage.Warning("TweetDuck Cannot Restart", "TweetDuck is taking too long to close.", FormMessage.Retry, FormMessage.Exit)){
|
||||||
using(FormMessage form = new FormMessage(BrandName+" Cannot Restart", BrandName+" is taking too long to close.", MessageBoxIcon.Warning)){
|
return;
|
||||||
form.CancelButton = form.AddButton("Exit");
|
|
||||||
form.ActiveControl = form.AddButton("Retry", DialogResult.Retry);
|
|
||||||
|
|
||||||
if (form.ShowDialog() == DialogResult.Retry){
|
|
||||||
attempt /= 2;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else Thread.Sleep(500);
|
|
||||||
|
lockResult = LockManager.LockWait(5000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
LockManager.Result lockResult = LockManager.Lock();
|
LockManager.Result lockResult = LockManager.Lock();
|
||||||
|
|
||||||
if (lockResult == LockManager.Result.HasProcess){
|
if (lockResult == LockManager.Result.HasProcess){
|
||||||
if (LockManager.LockingProcess.MainWindowHandle == IntPtr.Zero){ // restore if the original process is in tray
|
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)){
|
||||||
NativeMethods.SendMessage(NativeMethods.HWND_BROADCAST, WindowRestoreMessage, 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; // should trigger on first attempt if succeeded, but wait just in case
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (MessageBox.Show("Another instance of "+BrandName+" is already running.\r\nDo you want to close it?", BrandName+" is Already Running", MessageBoxButtons.YesNo, MessageBoxIcon.Error, MessageBoxDefaultButton.Button2) == DialogResult.Yes){
|
|
||||||
if (!LockManager.CloseLockingProcess(10000, 5000)){
|
if (!LockManager.CloseLockingProcess(10000, 5000)){
|
||||||
MessageBox.Show("Could not close the other process.", BrandName+" Has Failed :(", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
FormMessage.Error("TweetDuck Has Failed :(", "Could not close the other process.", FormMessage.OK);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LockManager.Lock();
|
lockResult = LockManager.Lock();
|
||||||
}
|
}
|
||||||
else return;
|
else return;
|
||||||
}
|
}
|
||||||
else if (lockResult != LockManager.Result.Success){
|
|
||||||
MessageBox.Show("An unknown error occurred accessing the data folder. Please, make sure "+BrandName+" is not already running. If the problem persists, try restarting your system.", BrandName+" Has Failed :(", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ReloadConfig();
|
UserConfig = UserConfig.Load(UserConfigFilePath);
|
||||||
SystemConfig = SystemConfig.Load(SystemConfigFilePath);
|
SystemConfig = SystemConfig.Load(SystemConfigFilePath);
|
||||||
|
|
||||||
if (Arguments.HasFlag(Arguments.ArgImportCookies)){
|
if (Arguments.HasFlag(Arguments.ArgImportCookies)){
|
||||||
ExportManager.ImportCookies();
|
ExportManager.ImportCookies();
|
||||||
}
|
}
|
||||||
|
else if (Arguments.HasFlag(Arguments.ArgDeleteCookies)){
|
||||||
|
ExportManager.DeleteCookies();
|
||||||
|
}
|
||||||
|
|
||||||
if (Arguments.HasFlag(Arguments.ArgUpdated)){
|
if (Arguments.HasFlag(Arguments.ArgUpdated)){
|
||||||
WindowsUtils.TryDeleteFolderWhenAble(InstallerPath, 8000);
|
WindowsUtils.TryDeleteFolderWhenAble(InstallerPath, 8000);
|
||||||
@@ -151,32 +142,21 @@ namespace TweetDuck{
|
|||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
CommandLineArgsParser.ReadCefArguments(UserConfig.CustomCefArgs).ToDictionary(settings.CefCommandLineArgs);
|
CommandLineArgs.ReadCefArguments(UserConfig.CustomCefArgs).ToDictionary(settings.CefCommandLineArgs);
|
||||||
|
BrowserUtils.SetupCefArgs(settings.CefCommandLineArgs);
|
||||||
if (!SystemConfig.HardwareAcceleration){
|
|
||||||
settings.CefCommandLineArgs["disable-gpu"] = "1";
|
|
||||||
settings.CefCommandLineArgs["disable-gpu-vsync"] = "1";
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.CefCommandLineArgs["disable-extensions"] = "1";
|
|
||||||
settings.CefCommandLineArgs["disable-plugins-discovery"] = "1";
|
|
||||||
settings.CefCommandLineArgs["enable-system-flash"] = "0";
|
|
||||||
|
|
||||||
|
Cef.EnableHighDPISupport();
|
||||||
Cef.Initialize(settings, false, new BrowserProcessHandler());
|
Cef.Initialize(settings, false, new BrowserProcessHandler());
|
||||||
|
|
||||||
Application.ApplicationExit += (sender, args) => ExitCleanup();
|
Application.ApplicationExit += (sender, args) => ExitCleanup();
|
||||||
|
|
||||||
PluginManager plugins = new PluginManager(PluginPath, PluginConfigFilePath);
|
UpdaterSettings updaterSettings = new UpdaterSettings{
|
||||||
plugins.Reloaded += plugins_Reloaded;
|
|
||||||
plugins.Executed += plugins_Executed;
|
|
||||||
plugins.Reload();
|
|
||||||
|
|
||||||
FormBrowser mainForm = new FormBrowser(plugins, new UpdaterSettings{
|
|
||||||
AllowPreReleases = Arguments.HasFlag(Arguments.ArgDebugUpdates),
|
AllowPreReleases = Arguments.HasFlag(Arguments.ArgDebugUpdates),
|
||||||
DismissedUpdate = UserConfig.DismissedUpdate,
|
DismissedUpdate = UserConfig.DismissedUpdate,
|
||||||
InstallerDownloadFolder = InstallerPath
|
InstallerDownloadFolder = InstallerPath
|
||||||
});
|
};
|
||||||
|
|
||||||
|
FormBrowser mainForm = new FormBrowser(updaterSettings);
|
||||||
Application.Run(mainForm);
|
Application.Run(mainForm);
|
||||||
|
|
||||||
if (mainForm.UpdateInstallerPath != null){
|
if (mainForm.UpdateInstallerPath != null){
|
||||||
@@ -191,20 +171,6 @@ namespace TweetDuck{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void plugins_Reloaded(object sender, PluginErrorEventArgs e){
|
|
||||||
if (e.HasErrors){
|
|
||||||
string doubleNL = Environment.NewLine+Environment.NewLine;
|
|
||||||
MessageBox.Show("The following plugins will not be available until the issues are resolved:"+doubleNL+string.Join(doubleNL, e.Errors), "Error Loading Plugins", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void plugins_Executed(object sender, PluginErrorEventArgs e){
|
|
||||||
if (e.HasErrors){
|
|
||||||
string doubleNL = Environment.NewLine+Environment.NewLine;
|
|
||||||
MessageBox.Show("Failed to execute the following plugins:"+doubleNL+string.Join(doubleNL, e.Errors), "Error Executing Plugins", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetDataStoragePath(){
|
private static string GetDataStoragePath(){
|
||||||
string custom = Arguments.GetValue(Arguments.ArgDataFolder, null);
|
string custom = Arguments.GetValue(Arguments.ArgDataFolder, null);
|
||||||
|
|
||||||
@@ -223,11 +189,6 @@ namespace TweetDuck{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ReloadConfig(){
|
|
||||||
UserConfig = UserConfig.Load(UserConfigFilePath);
|
|
||||||
UserConfigReplaced?.Invoke(UserConfig, new EventArgs());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ResetConfig(){
|
public static void ResetConfig(){
|
||||||
try{
|
try{
|
||||||
File.Delete(UserConfigFilePath);
|
File.Delete(UserConfigFilePath);
|
||||||
@@ -237,14 +198,10 @@ namespace TweetDuck{
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReloadConfig();
|
UserConfig.Reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Restart(){
|
public static void Restart(params string[] extraArgs){
|
||||||
Restart(new string[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Restart(string[] extraArgs){
|
|
||||||
CommandLineArgs args = Arguments.GetCurrentClean();
|
CommandLineArgs args = Arguments.GetCurrentClean();
|
||||||
CommandLineArgs.ReadStringArray('-', extraArgs, args);
|
CommandLineArgs.ReadStringArray('-', extraArgs, args);
|
||||||
RestartWithArgs(args);
|
RestartWithArgs(args);
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ using TweetDuck;
|
|||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion(Program.VersionFull)]
|
[assembly: AssemblyVersion(Program.VersionTag)]
|
||||||
[assembly: AssemblyFileVersion(Program.VersionFull)]
|
[assembly: AssemblyFileVersion(Program.VersionTag)]
|
||||||
|
|
||||||
[assembly: NeutralResourcesLanguage("en")]
|
[assembly: NeutralResourcesLanguage("en")]
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,6 @@ PM> Install-Package CefSharp.WinForms -Version 57.0.0
|
|||||||
PM> Install-Package Microsoft.VC120.CRT.JetBrains
|
PM> Install-Package Microsoft.VC120.CRT.JetBrains
|
||||||
```
|
```
|
||||||
|
|
||||||
After building, run either `_postbuild.bat` if you want to package the files yourself, or `bld/RUN BUILD.bat` to generate installer files using Inno Setup (make sure the Inno Setup binaries are on your PATH).
|
When building the `Release` configuration, the folder will only contain files intended for distribution (no debug symbols or other unnecessary files). You may package the files yourself, or use `bld/RUN BUILD.bat` to generate installer files using Inno Setup (make sure the Inno Setup binaries are on your PATH).
|
||||||
|
|
||||||
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.
|
Built files are available in **bin/x86**, installer files are generated in **bld/Output**. If you decide to release a custom version publicly, please make it clear that it is not the original TweetDuck.
|
||||||
|
|||||||
24
Reporter.cs
24
Reporter.cs
@@ -1,14 +1,13 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Globalization;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Core.Other;
|
using TweetDuck.Core.Other;
|
||||||
|
|
||||||
namespace TweetDuck{
|
namespace TweetDuck{
|
||||||
class Reporter{
|
sealed class Reporter{
|
||||||
private readonly string logFile;
|
private readonly string logFile;
|
||||||
|
|
||||||
public Reporter(string logFile){
|
public Reporter(string logFile){
|
||||||
@@ -17,9 +16,7 @@ namespace TweetDuck{
|
|||||||
|
|
||||||
public void SetupUnhandledExceptionHandler(string caption){
|
public void SetupUnhandledExceptionHandler(string caption){
|
||||||
AppDomain.CurrentDomain.UnhandledException += (sender, args) => {
|
AppDomain.CurrentDomain.UnhandledException += (sender, args) => {
|
||||||
Exception ex = args.ExceptionObject as Exception;
|
if (args.ExceptionObject is Exception ex) {
|
||||||
|
|
||||||
if (ex != null){
|
|
||||||
HandleException(caption, "An unhandled exception has occurred.", false, ex);
|
HandleException(caption, "An unhandled exception has occurred.", false, ex);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -32,7 +29,7 @@ namespace TweetDuck{
|
|||||||
build.Append("Please, report all issues to: https://github.com/chylex/TweetDuck/issues\r\n\r\n");
|
build.Append("Please, report all issues to: https://github.com/chylex/TweetDuck/issues\r\n\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
build.Append("[").Append(DateTime.Now.ToString("G", CultureInfo.CurrentCulture)).Append("]\r\n");
|
build.Append("[").Append(DateTime.Now.ToString("G", Program.Culture)).Append("]\r\n");
|
||||||
build.Append(data).Append("\r\n\r\n");
|
build.Append(data).Append("\r\n\r\n");
|
||||||
|
|
||||||
try{
|
try{
|
||||||
@@ -46,22 +43,21 @@ namespace TweetDuck{
|
|||||||
public void HandleException(string caption, string message, bool canIgnore, Exception e){
|
public void HandleException(string caption, string message, bool canIgnore, Exception e){
|
||||||
bool loggedSuccessfully = Log(e.ToString());
|
bool loggedSuccessfully = Log(e.ToString());
|
||||||
|
|
||||||
FormMessage form = new FormMessage(caption, message+"\r\nError: "+e.Message, canIgnore ? MessageBoxIcon.Warning : MessageBoxIcon.Error);
|
FormMessage form = new FormMessage(caption, message+"\nError: "+e.Message, canIgnore ? MessageBoxIcon.Warning : MessageBoxIcon.Error);
|
||||||
|
|
||||||
Button btnExit = form.AddButton("Exit");
|
Button btnExit = form.AddButton(FormMessage.Exit);
|
||||||
Button btnIgnore = form.AddButton("Ignore", DialogResult.Ignore);
|
Button btnIgnore = form.AddButton(FormMessage.Ignore, DialogResult.Ignore, ControlType.Cancel);
|
||||||
|
|
||||||
btnIgnore.Enabled = canIgnore;
|
btnIgnore.Enabled = canIgnore;
|
||||||
form.ActiveControl = canIgnore ? btnIgnore : btnExit;
|
form.ActiveControl = canIgnore ? btnIgnore : btnExit;
|
||||||
form.CancelButton = btnIgnore;
|
|
||||||
|
|
||||||
Button btnOpenLog = new Button{
|
Button btnOpenLog = new Button{
|
||||||
Anchor = AnchorStyles.Bottom | AnchorStyles.Left,
|
Anchor = AnchorStyles.Bottom | AnchorStyles.Left,
|
||||||
Enabled = loggedSuccessfully,
|
Enabled = loggedSuccessfully,
|
||||||
Font = SystemFonts.MessageBoxFont,
|
Font = SystemFonts.MessageBoxFont,
|
||||||
Location = new Point(6, 12),
|
Location = new Point(9, 12),
|
||||||
Margin = new Padding(0, 0, 48, 0),
|
Margin = new Padding(0, 0, 48, 0),
|
||||||
Size = new Size(88, 26),
|
Size = new Size(106, 26),
|
||||||
Text = "Show Error Log",
|
Text = "Show Error Log",
|
||||||
UseVisualStyleBackColor = true
|
UseVisualStyleBackColor = true
|
||||||
};
|
};
|
||||||
@@ -87,9 +83,7 @@ namespace TweetDuck{
|
|||||||
Application.EnableVisualStyles();
|
Application.EnableVisualStyles();
|
||||||
Application.SetCompatibleTextRenderingDefault(false);
|
Application.SetCompatibleTextRenderingDefault(false);
|
||||||
|
|
||||||
FormMessage form = new FormMessage(caption, message, MessageBoxIcon.Error);
|
FormMessage.Error(caption, message, "Exit");
|
||||||
form.ActiveControl = form.AddButton("Exit");
|
|
||||||
form.ShowDialog();
|
|
||||||
|
|
||||||
try{
|
try{
|
||||||
Process.GetCurrentProcess().Kill();
|
Process.GetCurrentProcess().Kill();
|
||||||
|
|||||||
@@ -53,9 +53,7 @@ enabled(){
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.eventKeyDown = function(e){
|
this.eventKeyDown = function(e){
|
||||||
if (!(document.activeElement === null || document.activeElement === document.body)){
|
return if !(document.activeElement === null || document.activeElement === document.body);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateShiftState(e.shiftKey);
|
updateShiftState(e.shiftKey);
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ Edit layout & design
|
|||||||
chylex
|
chylex
|
||||||
|
|
||||||
[version]
|
[version]
|
||||||
1.1.1
|
1.1.2
|
||||||
|
|
||||||
[website]
|
[website]
|
||||||
https://tweetduck.chylex.com
|
https://tweetduck.chylex.com
|
||||||
|
|||||||
@@ -1,9 +1,3 @@
|
|||||||
constructor(){
|
|
||||||
super({
|
|
||||||
requiresPageReload: true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
enabled(){
|
enabled(){
|
||||||
// elements & data
|
// elements & data
|
||||||
this.css = null;
|
this.css = null;
|
||||||
@@ -16,13 +10,11 @@ enabled(){
|
|||||||
fontSize: "12px",
|
fontSize: "12px",
|
||||||
hideTweetActions: true,
|
hideTweetActions: true,
|
||||||
moveTweetActionsToRight: true,
|
moveTweetActionsToRight: true,
|
||||||
revertReplies: false,
|
|
||||||
themeColorTweaks: true,
|
themeColorTweaks: true,
|
||||||
roundedScrollBars: false,
|
|
||||||
revertIcons: true,
|
revertIcons: true,
|
||||||
smallComposeTextSize: false,
|
smallComposeTextSize: false,
|
||||||
optimizeAnimations: true,
|
optimizeAnimations: true,
|
||||||
avatarRadius: 10
|
avatarRadius: 2
|
||||||
};
|
};
|
||||||
|
|
||||||
this.firstTimeLoad = null;
|
this.firstTimeLoad = null;
|
||||||
@@ -60,7 +52,6 @@ enabled(){
|
|||||||
this.firstTimeLoad = obj === null;
|
this.firstTimeLoad = obj === null;
|
||||||
|
|
||||||
this.onStageReady();
|
this.onStageReady();
|
||||||
this.injectDeciderReplyHook(obj && obj.revertReplies);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.$$wasLoadedBefore){
|
if (this.$$wasLoadedBefore){
|
||||||
@@ -111,16 +102,14 @@ enabled(){
|
|||||||
|
|
||||||
// settings click event
|
// settings click event
|
||||||
this.onSettingsMenuClickedEvent = () => {
|
this.onSettingsMenuClickedEvent = () => {
|
||||||
if (this.htmlModal === null || this.config === null){
|
return if this.htmlModal === null || this.config === null;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
let menu = $(".js-dropdown-content").children("ul").first();
|
let menu = $(".js-dropdown-content").children("ul").first();
|
||||||
if (menu.length === 0)return;
|
return if menu.length === 0;
|
||||||
|
|
||||||
let itemTD = menu.children("[data-std]").first();
|
let itemTD = menu.children("[data-std]").first();
|
||||||
if (itemTD.length === 0)return;
|
return if itemTD.length === 0;
|
||||||
|
|
||||||
if (!itemTD.prev().hasClass("drp-h-divider")){
|
if (!itemTD.prev().hasClass("drp-h-divider")){
|
||||||
itemTD.before('<li class="drp-h-divider"></li>');
|
itemTD.before('<li class="drp-h-divider"></li>');
|
||||||
@@ -129,9 +118,7 @@ enabled(){
|
|||||||
let itemEditDesign = $('<li class="is-selectable"><a href="#" data-action>Edit layout & design</a></li>');
|
let itemEditDesign = $('<li class="is-selectable"><a href="#" data-action>Edit layout & design</a></li>');
|
||||||
itemTD.after(itemEditDesign);
|
itemTD.after(itemEditDesign);
|
||||||
|
|
||||||
itemEditDesign.on("click", "a", function(){
|
itemEditDesign.on("click", "a", this.openEditDesignDialog);
|
||||||
new customDesignModal();
|
|
||||||
});
|
|
||||||
|
|
||||||
itemEditDesign.hover(function(){
|
itemEditDesign.hover(function(){
|
||||||
$(this).addClass("is-selected");
|
$(this).addClass("is-selected");
|
||||||
@@ -249,18 +236,7 @@ enabled(){
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// decider injections
|
this.openEditDesignDialog = () => new customDesignModal();
|
||||||
this.injectDeciderReplyHook = enable => {
|
|
||||||
let prevFunc = TD.decider.updateFromBackend;
|
|
||||||
|
|
||||||
TD.decider.updateFromBackend = function(data){
|
|
||||||
data["simplified_replies"] = !enable;
|
|
||||||
return prevFunc.apply(this, arguments);
|
|
||||||
};
|
|
||||||
|
|
||||||
TD.decider.updateForGuestId();
|
|
||||||
this.$requiresReload = enable;
|
|
||||||
};
|
|
||||||
|
|
||||||
// animation optimization
|
// animation optimization
|
||||||
this.optimizations = null;
|
this.optimizations = null;
|
||||||
@@ -353,7 +329,7 @@ enabled(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.css.insert("#general_settings .cf { display: none !important }");
|
this.css.insert("#general_settings .cf { display: none !important }");
|
||||||
this.css.insert("#general_settings .divider-bar::after { display: inline-block; padding-top: 10px; line-height: 17px; content: 'Use the new | Edit layout & design | option in the Settings to modify TweetDeck theme, column width, font size, and other features.' }");
|
this.css.insert("#settings-modal .js-setting-list li:nth-child(3) { border-bottom: 1px solid #ccd6dd }");
|
||||||
|
|
||||||
this.css.insert(".txt-base-smallest:not(.icon), .txt-base-largest:not(.icon) { font-size: "+this.config.fontSize+" !important }");
|
this.css.insert(".txt-base-smallest:not(.icon), .txt-base-largest:not(.icon) { font-size: "+this.config.fontSize+" !important }");
|
||||||
this.css.insert(".avatar { border-radius: "+this.config.avatarRadius+"% !important }");
|
this.css.insert(".avatar { border-radius: "+this.config.avatarRadius+"% !important }");
|
||||||
@@ -389,22 +365,6 @@ enabled(){
|
|||||||
this.css.insert(".compose-text { font-size: 12px !important; height: 120px !important }");
|
this.css.insert(".compose-text { font-size: 12px !important; height: 120px !important }");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.config.roundedScrollBars){
|
|
||||||
this.css.insert(".scroll-styled-v:not(.antiscroll-inner)::-webkit-scrollbar { width: 8px }");
|
|
||||||
this.css.insert(".scroll-styled-h:not(.antiscroll-inner)::-webkit-scrollbar { height: 8px }");
|
|
||||||
this.css.insert(".scroll-styled-v::-webkit-scrollbar-thumb { border-radius: 0 }");
|
|
||||||
this.css.insert(".scroll-styled-h::-webkit-scrollbar-thumb { border-radius: 0 }");
|
|
||||||
this.css.insert(".antiscroll-scrollbar { border-radius: 0 }");
|
|
||||||
this.css.insert(".antiscroll-scrollbar-vertical { margin-top: 0 }");
|
|
||||||
this.css.insert(".antiscroll-scrollbar-horizontal { margin-left: 0 }");
|
|
||||||
this.css.insert(".app-columns-container::-webkit-scrollbar { height: 9px !important }");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.config.revertReplies){
|
|
||||||
this.css.insert(".activity-header + .tweet .tweet-context { margin-left: -35px }");
|
|
||||||
this.css.insert(".activity-header + .tweet .tweet-context .obj-left { margin-right: 5px }");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.config.revertIcons){
|
if (this.config.revertIcons){
|
||||||
this.icons = document.createElement("style");
|
this.icons = document.createElement("style");
|
||||||
this.icons.innerHTML = `
|
this.icons.innerHTML = `
|
||||||
@@ -497,7 +457,9 @@ enabled(){
|
|||||||
.icon-list-filled:before{content:"\\f014";font-family:tweetdeckold}
|
.icon-list-filled:before{content:"\\f014";font-family:tweetdeckold}
|
||||||
.icon-user-filled:before{content:"\\f035";font-family:tweetdeckold}
|
.icon-user-filled:before{content:"\\f035";font-family:tweetdeckold}
|
||||||
|
|
||||||
|
.drawer .btn .icon, .app-header .btn .icon { line-height: 1em !important }
|
||||||
.column-header .column-type-icon { bottom: 26px !important }
|
.column-header .column-type-icon { bottom: 26px !important }
|
||||||
|
.is-options-open .column-type-icon { bottom: 25px !important }
|
||||||
.tweet-footer { margin-top: 6px !important }`;
|
.tweet-footer { margin-top: 6px !important }`;
|
||||||
|
|
||||||
document.head.appendChild(this.icons);
|
document.head.appendChild(this.icons);
|
||||||
@@ -540,11 +502,6 @@ enabled(){
|
|||||||
.txt-base-smallest:not(.icon), .txt-base-largest:not(.icon) { font-size: ${this.config.fontSize} !important }
|
.txt-base-smallest:not(.icon), .txt-base-largest:not(.icon) { font-size: ${this.config.fontSize} !important }
|
||||||
.avatar { border-radius: ${this.config.avatarRadius}% !important }
|
.avatar { border-radius: ${this.config.avatarRadius}% !important }
|
||||||
|
|
||||||
${this.config.revertReplies ? `
|
|
||||||
.activity-header + .tweet .tweet-context { margin-left: -35px }
|
|
||||||
.activity-header + .tweet .tweet-context .obj-left { margin-right: 5px }
|
|
||||||
` : ``}
|
|
||||||
|
|
||||||
${this.config.revertIcons ? `
|
${this.config.revertIcons ? `
|
||||||
@font-face { font-family: 'tweetdeckold'; src: url(\"https://ton.twimg.com/tweetdeck-web/web/assets/fonts/tweetdeck-regular-webfont.5f4ea87976.woff\") format(\"woff\"); font-weight: normal; font-style: normal }
|
@font-face { font-family: 'tweetdeckold'; src: url(\"https://ton.twimg.com/tweetdeck-web/web/assets/fonts/tweetdeck-regular-webfont.5f4ea87976.woff\") format(\"woff\"); font-weight: normal; font-style: normal }
|
||||||
.icon-reply:before{content:"\\f006";font-family:tweetdeckold}
|
.icon-reply:before{content:"\\f006";font-family:tweetdeckold}
|
||||||
@@ -575,6 +532,32 @@ ready(){
|
|||||||
// modal
|
// modal
|
||||||
$("[data-action='settings-menu']").on("click", this.onSettingsMenuClickedEvent);
|
$("[data-action='settings-menu']").on("click", this.onSettingsMenuClickedEvent);
|
||||||
$(".js-app").append('<div id="td-design-plugin-modal" class="js-modal settings-modal ovl scroll-v scroll-styled-v"></div>');
|
$(".js-app").append('<div id="td-design-plugin-modal" class="js-modal settings-modal ovl scroll-v scroll-styled-v"></div>');
|
||||||
|
|
||||||
|
// global settings override
|
||||||
|
var me = this;
|
||||||
|
|
||||||
|
this.prevFuncSettingsGetInfo = TD.components.GlobalSettings.prototype.getInfo;
|
||||||
|
this.prevFuncSettingsSwitchTab = TD.components.GlobalSettings.prototype.switchTab;
|
||||||
|
|
||||||
|
TD.components.GlobalSettings.prototype.getInfo = function(){
|
||||||
|
let data = me.prevFuncSettingsGetInfo.apply(this, arguments);
|
||||||
|
|
||||||
|
data.tabs.push({
|
||||||
|
title: "Layout & Design",
|
||||||
|
action: "tdp-edit-design"
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
TD.components.GlobalSettings.prototype.switchTab = function(tab){
|
||||||
|
if (tab === "tdp-edit-design"){
|
||||||
|
me.openEditDesignDialog();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return me.prevFuncSettingsSwitchTab.apply(this, arguments);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
disabled(){
|
disabled(){
|
||||||
@@ -598,6 +581,9 @@ disabled(){
|
|||||||
$(window).off("focus", this.onWindowFocusEvent);
|
$(window).off("focus", this.onWindowFocusEvent);
|
||||||
$(window).off("blur", this.onWindowBlurEvent);
|
$(window).off("blur", this.onWindowBlurEvent);
|
||||||
|
|
||||||
|
TD.components.GlobalSettings.prototype.getInfo = this.prevFuncSettingsGetInfo;
|
||||||
|
TD.components.GlobalSettings.prototype.switchTab = this.prevFuncSettingsSwitchTab;
|
||||||
|
|
||||||
$("[data-action='settings-menu']").off("click", this.onSettingsMenuClickedEvent);
|
$("[data-action='settings-menu']").off("click", this.onSettingsMenuClickedEvent);
|
||||||
$("#td-design-plugin-modal").remove();
|
$("#td-design-plugin-modal").remove();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,10 +88,6 @@
|
|||||||
<input data-td-key="moveTweetActionsToRight" class="js-theme-checkbox touch-larger-label" type="checkbox">
|
<input data-td-key="moveTweetActionsToRight" class="js-theme-checkbox touch-larger-label" type="checkbox">
|
||||||
Tweet actions on the right side
|
Tweet actions on the right side
|
||||||
</label>
|
</label>
|
||||||
<label class="checkbox">
|
|
||||||
<input data-td-key="revertReplies" data-td-reload class="js-theme-checkbox touch-larger-label" type="checkbox">
|
|
||||||
Revert reply style (reloads page)
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<!-- DESIGN -->
|
<!-- DESIGN -->
|
||||||
|
|
||||||
@@ -102,10 +98,6 @@
|
|||||||
<input data-td-key="themeColorTweaks" class="js-theme-checkbox touch-larger-label" type="checkbox">
|
<input data-td-key="themeColorTweaks" class="js-theme-checkbox touch-larger-label" type="checkbox">
|
||||||
Theme color tweaks
|
Theme color tweaks
|
||||||
</label>
|
</label>
|
||||||
<label class="checkbox">
|
|
||||||
<input data-td-key="roundedScrollBars" class="js-theme-checkbox touch-larger-label" type="checkbox">
|
|
||||||
Rounded scroll bars
|
|
||||||
</label>
|
|
||||||
<label class="checkbox">
|
<label class="checkbox">
|
||||||
<input data-td-key="revertIcons" class="js-theme-checkbox touch-larger-label" type="checkbox">
|
<input data-td-key="revertIcons" class="js-theme-checkbox touch-larger-label" type="checkbox">
|
||||||
Revert icon design
|
Revert icon design
|
||||||
@@ -130,7 +122,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="td-avatar-shape-item-outer" data-td-key="avatarRadius" data-td-value="10">
|
<div class="td-avatar-shape-item-outer" data-td-key="avatarRadius" data-td-value="10">
|
||||||
<div class="td-avatar-shape" style="border-radius:10%"></div>
|
<div class="td-avatar-shape" style="border-radius:10%"></div>
|
||||||
<label>Default</label>
|
<label>Legacy</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="td-avatar-shape-item-outer" data-td-key="avatarRadius" data-td-value="30">
|
<div class="td-avatar-shape-item-outer" data-td-key="avatarRadius" data-td-value="30">
|
||||||
<div class="td-avatar-shape" style="border-radius:30%"></div>
|
<div class="td-avatar-shape" style="border-radius:30%"></div>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ Emoji keyboard
|
|||||||
chylex
|
chylex
|
||||||
|
|
||||||
[version]
|
[version]
|
||||||
1.1
|
1.3.1
|
||||||
|
|
||||||
[website]
|
[website]
|
||||||
https://tweetduck.chylex.com
|
https://tweetduck.chylex.com
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
enabled(){
|
enabled(){
|
||||||
|
this.ENABLE_CUSTOM_KEYBOARD = false;
|
||||||
|
|
||||||
this.selectedSkinTone = "";
|
this.selectedSkinTone = "";
|
||||||
this.currentKeywords = [];
|
this.currentKeywords = [];
|
||||||
|
|
||||||
@@ -29,17 +31,22 @@ enabled(){
|
|||||||
// styles
|
// styles
|
||||||
|
|
||||||
this.css = window.TDPF_createCustomStyle(this);
|
this.css = window.TDPF_createCustomStyle(this);
|
||||||
this.css.insert(".emoji-keyboard { position: absolute; width: 15.35em; background-color: white; border-radius: 2px 2px 3px 3px; font-size: 24px; z-index: 9999 }");
|
this.css.insert(".emoji-keyboard { position: absolute; width: 15.35em; background-color: white; border-radius: 1px; font-size: 24px; z-index: 9999 }");
|
||||||
|
|
||||||
this.css.insert(".emoji-keyboard-list { height: 10.14em; padding: 0.1em; box-sizing: border-box; overflow-y: auto }");
|
this.css.insert(".emoji-keyboard-list { height: 10.14em; padding: 0.1em; box-sizing: border-box; overflow-y: auto }");
|
||||||
this.css.insert(".emoji-keyboard-list .separator { height: 26px }");
|
this.css.insert(".emoji-keyboard-list .separator { height: 26px }");
|
||||||
this.css.insert(".emoji-keyboard-list img { padding: 0.1em !important; width: 1em; height: 1em; vertical-align: -0.1em; cursor: pointer }");
|
this.css.insert(".emoji-keyboard-list img { padding: 0.1em !important; width: 1em; height: 1em; vertical-align: -0.1em; cursor: pointer }");
|
||||||
this.css.insert(".emoji-keyboard-search { height: auto; padding: 4px 10px 8px; background-color: #292f33; border-radius: 2px 2px 0 0 }");
|
|
||||||
|
this.css.insert(".emoji-keyboard-search { height: auto; padding: 4px 10px 8px; background-color: #292f33; border-radius: 1px 1px 0 0 }");
|
||||||
this.css.insert(".emoji-keyboard-search input { width: 100%; border-radius: 1px; }");
|
this.css.insert(".emoji-keyboard-search input { width: 100%; border-radius: 1px; }");
|
||||||
this.css.insert(".emoji-keyboard-skintones { height: 1.3em; text-align: center; background-color: #292f33; border-radius: 0 0 2px 2px }");
|
|
||||||
|
this.css.insert(".emoji-keyboard-skintones { height: 1.3em; text-align: center; background-color: #292f33; border-radius: 0 0 1px 1px }");
|
||||||
this.css.insert(".emoji-keyboard-skintones div { width: 0.8em; height: 0.8em; margin: 0.25em 0.1em; border-radius: 50%; display: inline-block; box-sizing: border-box; cursor: pointer }");
|
this.css.insert(".emoji-keyboard-skintones div { width: 0.8em; height: 0.8em; margin: 0.25em 0.1em; border-radius: 50%; display: inline-block; box-sizing: border-box; cursor: pointer }");
|
||||||
this.css.insert(".emoji-keyboard-skintones .sel { border: 2px solid rgba(0, 0, 0, 0.35); box-shadow: 0 0 2px 0 rgba(255, 255, 255, 0.65), 0 0 1px 0 rgba(255, 255, 255, 0.4) inset }");
|
this.css.insert(".emoji-keyboard-skintones .sel { border: 2px solid rgba(0, 0, 0, 0.35); box-shadow: 0 0 2px 0 rgba(255, 255, 255, 0.65), 0 0 1px 0 rgba(255, 255, 255, 0.4) inset }");
|
||||||
this.css.insert(".emoji-keyboard-skintones :hover { border: 2px solid rgba(0, 0, 0, 0.25); box-shadow: 0 0 1px 0 rgba(255, 255, 255, 0.65), 0 0 1px 0 rgba(255, 255, 255, 0.25) inset }");
|
this.css.insert(".emoji-keyboard-skintones :hover { border: 2px solid rgba(0, 0, 0, 0.25); box-shadow: 0 0 1px 0 rgba(255, 255, 255, 0.65), 0 0 1px 0 rgba(255, 255, 255, 0.25) inset }");
|
||||||
|
|
||||||
|
this.css.insert(".js-compose-text { font-family: \"Twitter Color Emoji\", Helvetica, Arial, Verdana, sans-serif; }");
|
||||||
|
|
||||||
// layout
|
// layout
|
||||||
|
|
||||||
var buttonHTML = '<button class="needsclick btn btn-on-blue txt-left padding-v--9 emoji-keyboard-popup-btn"><i class="icon icon-heart"></i></button>';
|
var buttonHTML = '<button class="needsclick btn btn-on-blue txt-left padding-v--9 emoji-keyboard-popup-btn"><i class="icon icon-heart"></i></button>';
|
||||||
@@ -47,10 +54,10 @@ enabled(){
|
|||||||
this.prevComposeMustache = TD.mustaches["compose/docked_compose.mustache"];
|
this.prevComposeMustache = TD.mustaches["compose/docked_compose.mustache"];
|
||||||
TD.mustaches["compose/docked_compose.mustache"] = TD.mustaches["compose/docked_compose.mustache"].replace('<div class="cf margin-t--12 margin-b--30">', '<div class="cf margin-t--12 margin-b--30">'+buttonHTML);
|
TD.mustaches["compose/docked_compose.mustache"] = TD.mustaches["compose/docked_compose.mustache"].replace('<div class="cf margin-t--12 margin-b--30">', '<div class="cf margin-t--12 margin-b--30">'+buttonHTML);
|
||||||
|
|
||||||
var dockedComposePanel = $(".js-docked-compose");
|
var maybeDockedComposePanel = $(".js-docked-compose");
|
||||||
|
|
||||||
if (dockedComposePanel.length){
|
if (maybeDockedComposePanel.length){
|
||||||
dockedComposePanel.find(".cf.margin-t--12.margin-b--30").first().append(buttonHTML);
|
maybeDockedComposePanel.find(".cf.margin-t--12.margin-b--30").first().append(buttonHTML);
|
||||||
}
|
}
|
||||||
|
|
||||||
// keyboard generation
|
// keyboard generation
|
||||||
@@ -58,7 +65,9 @@ enabled(){
|
|||||||
this.currentKeyboard = null;
|
this.currentKeyboard = null;
|
||||||
this.currentSpanner = null;
|
this.currentSpanner = null;
|
||||||
|
|
||||||
var hideKeyboard = () => {
|
var wasSearchFocused = false;
|
||||||
|
|
||||||
|
var hideKeyboard = (refocus) => {
|
||||||
$(this.currentKeyboard).remove();
|
$(this.currentKeyboard).remove();
|
||||||
this.currentKeyboard = null;
|
this.currentKeyboard = null;
|
||||||
|
|
||||||
@@ -70,12 +79,15 @@ enabled(){
|
|||||||
this.composePanelScroller.trigger("scroll");
|
this.composePanelScroller.trigger("scroll");
|
||||||
|
|
||||||
$(".emoji-keyboard-popup-btn").removeClass("is-selected");
|
$(".emoji-keyboard-popup-btn").removeClass("is-selected");
|
||||||
$(".js-compose-text").first().focus();
|
|
||||||
|
if (refocus){
|
||||||
|
$(".js-compose-text", ".js-docked-compose").focus();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var generateEmojiHTML = skinTone => {
|
var generateEmojiHTML = skinTone => {
|
||||||
let index = 0;
|
let index = 0;
|
||||||
let html = [ "<p style='font-size:13px;color:#444;margin:4px;text-align:center'>Please, note that most emoji will not show up properly in the text box above, but they will display in the tweet.</p>" ];
|
let html = [ "<p style='font-size:13px;color:#444;margin:4px;text-align:center'>Please, note that some emoji may not show up correctly in the text box above, but they will display in the tweet.</p>" ];
|
||||||
|
|
||||||
for(let array of [ this.emojiData1, this.emojiData2[skinTone], this.emojiData3 ]){
|
for(let array of [ this.emojiData1, this.emojiData2[skinTone], this.emojiData3 ]){
|
||||||
for(let emoji of array){
|
for(let emoji of array){
|
||||||
@@ -124,7 +136,7 @@ enabled(){
|
|||||||
updateFilters();
|
updateFilters();
|
||||||
};
|
};
|
||||||
|
|
||||||
this.generateKeyboard = (input, left, top) => {
|
this.generateKeyboard = (left, top) => {
|
||||||
var outer = document.createElement("div");
|
var outer = document.createElement("div");
|
||||||
outer.classList.add("emoji-keyboard");
|
outer.classList.add("emoji-keyboard");
|
||||||
outer.style.left = left+"px";
|
outer.style.left = left+"px";
|
||||||
@@ -134,18 +146,10 @@ enabled(){
|
|||||||
keyboard.classList.add("emoji-keyboard-list");
|
keyboard.classList.add("emoji-keyboard-list");
|
||||||
|
|
||||||
keyboard.addEventListener("click", function(e){
|
keyboard.addEventListener("click", function(e){
|
||||||
if (e.target.tagName === "IMG"){
|
let ele = e.target;
|
||||||
var val = input.val();
|
|
||||||
var inserted = e.target.getAttribute("alt");
|
|
||||||
var posStart = input[0].selectionStart;
|
|
||||||
var posEnd = input[0].selectionEnd;
|
|
||||||
|
|
||||||
input.val(val.slice(0, posStart)+inserted+val.slice(posStart));
|
if (ele.tagName === "IMG"){
|
||||||
input.trigger("change");
|
insertEmoji(ele.getAttribute("src"), ele.getAttribute("alt"));
|
||||||
input.focus();
|
|
||||||
|
|
||||||
input[0].selectionStart = posStart+inserted.length;
|
|
||||||
input[0].selectionEnd = posEnd+inserted.length;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@@ -179,16 +183,36 @@ enabled(){
|
|||||||
var searchInput = search.children[0];
|
var searchInput = search.children[0];
|
||||||
searchInput.focus();
|
searchInput.focus();
|
||||||
|
|
||||||
|
wasSearchFocused = false;
|
||||||
|
|
||||||
searchInput.addEventListener("input", function(e){
|
searchInput.addEventListener("input", function(e){
|
||||||
me.currentKeywords = e.target.value.split(" ").filter(kw => kw.length > 0).map(kw => kw.toLowerCase());
|
me.currentKeywords = e.target.value.split(" ").filter(kw => kw.length > 0).map(kw => kw.toLowerCase());
|
||||||
updateFilters();
|
updateFilters();
|
||||||
|
|
||||||
|
wasSearchFocused = $(this).val().length > 0;
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
});
|
});
|
||||||
|
|
||||||
searchInput.addEventListener("focus", function(){
|
searchInput.addEventListener("keydown", function(e){
|
||||||
|
if (e.keyCode === 13 && $(this).val().length){ // enter
|
||||||
|
let ele = $(".emoji-keyboard-list").children("img").filter(":visible").first();
|
||||||
|
|
||||||
|
if (ele.length > 0){
|
||||||
|
insertEmoji(ele[0].getAttribute("src"), ele[0].getAttribute("alt"));
|
||||||
|
}
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
searchInput.addEventListener("click", function(){
|
||||||
$(this).select();
|
$(this).select();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
searchInput.addEventListener("focus", function(){
|
||||||
|
wasSearchFocused = true;
|
||||||
|
});
|
||||||
|
|
||||||
this.currentKeyboard = outer;
|
this.currentKeyboard = outer;
|
||||||
selectSkinTone(this.selectedSkinTone);
|
selectSkinTone(this.selectedSkinTone);
|
||||||
|
|
||||||
@@ -204,15 +228,36 @@ enabled(){
|
|||||||
return button.offset().top+button.outerHeight()+me.composePanelScroller.scrollTop()+8;
|
return button.offset().top+button.outerHeight()+me.composePanelScroller.scrollTop()+8;
|
||||||
};
|
};
|
||||||
|
|
||||||
// event handlers
|
var insertEmoji = (src, alt) => {
|
||||||
|
let input = $(".js-compose-text", ".js-docked-compose");
|
||||||
|
|
||||||
|
let val = input.val();
|
||||||
|
let posStart = input[0].selectionStart;
|
||||||
|
let posEnd = input[0].selectionEnd;
|
||||||
|
|
||||||
|
input.val(val.slice(0, posStart)+alt+val.slice(posEnd));
|
||||||
|
input.trigger("change");
|
||||||
|
|
||||||
|
input[0].selectionStart = posStart+alt.length;
|
||||||
|
input[0].selectionEnd = posStart+alt.length;
|
||||||
|
|
||||||
|
if (wasSearchFocused){
|
||||||
|
$(".emoji-keyboard-search").children("input").focus();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
input.focus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// general event handlers
|
||||||
|
|
||||||
this.emojiKeyboardButtonClickEvent = function(e){
|
this.emojiKeyboardButtonClickEvent = function(e){
|
||||||
if (me.currentKeyboard){
|
if (me.currentKeyboard){
|
||||||
hideKeyboard();
|
|
||||||
$(this).blur();
|
$(this).blur();
|
||||||
|
hideKeyboard(true);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
me.generateKeyboard($(".js-compose-text").first(), $(this).offset().left, getKeyboardTop());
|
me.generateKeyboard($(this).offset().left, getKeyboardTop());
|
||||||
$(this).addClass("is-selected");
|
$(this).addClass("is-selected");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,15 +270,23 @@ enabled(){
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.composeInputFocusEvent = function(e){
|
||||||
|
wasSearchFocused = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.composerSendingEvent = function(e){
|
||||||
|
hideKeyboard();
|
||||||
|
};
|
||||||
|
|
||||||
this.documentClickEvent = function(e){
|
this.documentClickEvent = function(e){
|
||||||
if (me.currentKeyboard && !e.target.classList.contains("js-compose-text")){
|
if (me.currentKeyboard && $(e.target).closest(".compose-text-container").length === 0){
|
||||||
hideKeyboard();
|
hideKeyboard();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.documentKeyEvent = function(e){
|
this.documentKeyEvent = function(e){
|
||||||
if (me.currentKeyboard && e.keyCode === 27){ // escape
|
if (me.currentKeyboard && e.keyCode === 27){ // escape
|
||||||
hideKeyboard();
|
hideKeyboard(true);
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -242,17 +295,21 @@ enabled(){
|
|||||||
if (me.currentKeyboard){
|
if (me.currentKeyboard){
|
||||||
me.currentKeyboard.style.top = getKeyboardTop()+"px";
|
me.currentKeyboard.style.top = getKeyboardTop()+"px";
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
ready(){
|
ready(){
|
||||||
|
this.composeDrawer = $("[data-drawer='compose']");
|
||||||
|
|
||||||
this.composePanelScroller = $(".js-compose-scroller", ".js-docked-compose").first().children().first();
|
this.composePanelScroller = $(".js-compose-scroller", ".js-docked-compose").first().children().first();
|
||||||
this.composePanelScroller.on("scroll", this.composerScrollEvent);
|
this.composePanelScroller.on("scroll", this.composerScrollEvent);
|
||||||
|
|
||||||
$(".emoji-keyboard-popup-btn").on("click", this.emojiKeyboardButtonClickEvent);
|
$(".emoji-keyboard-popup-btn").on("click", this.emojiKeyboardButtonClickEvent);
|
||||||
|
$(".js-compose-text", ".js-docked-compose").on("focus", this.composeInputFocusEvent);
|
||||||
$(document).on("click", this.documentClickEvent);
|
$(document).on("click", this.documentClickEvent);
|
||||||
$(document).on("keydown", this.documentKeyEvent);
|
$(document).on("keydown", this.documentKeyEvent);
|
||||||
$(document).on("uiComposeImageAdded", this.uploadFilesEvent);
|
$(document).on("uiComposeImageAdded", this.uploadFilesEvent);
|
||||||
|
this.composeDrawer.on("uiComposeTweetSending", this.composerSendingEvent);
|
||||||
|
|
||||||
// HTML generation
|
// HTML generation
|
||||||
|
|
||||||
@@ -273,12 +330,14 @@ ready(){
|
|||||||
|
|
||||||
// declaration inserters
|
// declaration inserters
|
||||||
|
|
||||||
|
let mapUnicode = pt => convUnicode(parseInt(pt, 16));
|
||||||
|
|
||||||
let addDeclaration1 = decl => {
|
let addDeclaration1 = decl => {
|
||||||
this.emojiData1.push(decl.split(" ").map(pt => convUnicode(parseInt(pt, 16))).join(""));
|
this.emojiData1.push(decl.split(" ").map(mapUnicode).join(""));
|
||||||
};
|
};
|
||||||
|
|
||||||
let addDeclaration2 = (tone, decl) => {
|
let addDeclaration2 = (tone, decl) => {
|
||||||
let gen = decl.split(" ").map(pt => convUnicode(parseInt(pt, 16))).join("");
|
let gen = decl.split(" ").map(mapUnicode).join("");
|
||||||
|
|
||||||
if (tone === null){
|
if (tone === null){
|
||||||
for(let skinTone of this.skinToneList){
|
for(let skinTone of this.skinToneList){
|
||||||
@@ -291,7 +350,7 @@ ready(){
|
|||||||
};
|
};
|
||||||
|
|
||||||
let addDeclaration3 = decl => {
|
let addDeclaration3 = decl => {
|
||||||
this.emojiData3.push(decl.split(" ").map(pt => convUnicode(parseInt(pt, 16))).join(""));
|
this.emojiData3.push(decl.split(" ").map(mapUnicode).join(""));
|
||||||
};
|
};
|
||||||
|
|
||||||
// line reading
|
// line reading
|
||||||
@@ -306,6 +365,9 @@ ready(){
|
|||||||
case 2: this.emojiData3.push("___"); break;
|
case 2: this.emojiData3.push("___"); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (line[0] === '#'){
|
||||||
if (line[1] === '1'){
|
if (line[1] === '1'){
|
||||||
skinToneState = 1;
|
skinToneState = 1;
|
||||||
}
|
}
|
||||||
@@ -367,8 +429,11 @@ disabled(){
|
|||||||
$(".emoji-keyboard-popup-btn").off("click", this.emojiKeyboardButtonClickEvent);
|
$(".emoji-keyboard-popup-btn").off("click", this.emojiKeyboardButtonClickEvent);
|
||||||
$(".emoji-keyboard-popup-btn").remove();
|
$(".emoji-keyboard-popup-btn").remove();
|
||||||
|
|
||||||
|
$(".js-compose-text", ".js-docked-compose").off("focus", this.composeInputFocusEvent);
|
||||||
$(document).off("click", this.documentClickEvent);
|
$(document).off("click", this.documentClickEvent);
|
||||||
$(document).off("keydown", this.documentKeyEvent);
|
$(document).off("keydown", this.documentKeyEvent);
|
||||||
$(document).off("uiComposeImageAdded", this.uploadFilesEvent);
|
$(document).off("uiComposeImageAdded", this.uploadFilesEvent);
|
||||||
|
this.composeDrawer.off("uiComposeTweetSending", this.composerSendingEvent);
|
||||||
|
|
||||||
TD.mustaches["compose/docked_compose.mustache"] = this.prevComposeMustache;
|
TD.mustaches["compose/docked_compose.mustache"] = this.prevComposeMustache;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
Emoji list: http://unicode.org/emoji/charts/emoji-ordering.html
|
Emoji list: http://unicode.org/emoji/charts/emoji-ordering.html
|
||||||
Emoji order: http://unicode.org/emoji/charts/emoji-ordering.txt
|
Emoji order: http://unicode.org/emoji/charts/emoji-ordering.txt
|
||||||
|
|
||||||
|
The instructions contain search & replace regexes. Make sure your editor supports LF characters (\n).
|
||||||
|
For Brackets, use Find -> Replace in Files with any regex that includes LF.
|
||||||
|
|
||||||
|
|
||||||
------------------------
|
------------------------
|
||||||
Remove unnecessary info:
|
Remove unnecessary info:
|
||||||
@@ -8,7 +11,7 @@ Remove unnecessary info:
|
|||||||
Search: \s;.+?#.+?\s
|
Search: \s;.+?#.+?\s
|
||||||
Replace: ;
|
Replace: ;
|
||||||
|
|
||||||
Search: U+
|
Search: U\+
|
||||||
Replace:
|
Replace:
|
||||||
|
|
||||||
|
|
||||||
@@ -27,7 +30,18 @@ Replace skin tone variations:
|
|||||||
1F9D2;child
|
1F9D2;child
|
||||||
1F9D2 $;child
|
1F9D2 $;child
|
||||||
|
|
||||||
TODO: Update this section with exact regexes
|
|
||||||
|
Search: 1F3FB
|
||||||
|
Replace: $
|
||||||
|
|
||||||
|
Search: ^.*1F3F[C-F].*$
|
||||||
|
Replace:
|
||||||
|
|
||||||
|
Search: tone\n\n\n\n
|
||||||
|
Replace:
|
||||||
|
|
||||||
|
Search: : light skin.*$
|
||||||
|
Replace:
|
||||||
|
|
||||||
|
|
||||||
----------------
|
----------------
|
||||||
@@ -37,19 +51,9 @@ Move some emoji:
|
|||||||
> 1F91D;handshake
|
> 1F91D;handshake
|
||||||
1F463;footprints
|
1F463;footprints
|
||||||
|
|
||||||
1F939 $ 200D 2640 FE0F;woman juggling
|
|
||||||
> 1F6CC;person in bed
|
|
||||||
> 1F6CC $;person in bed
|
|
||||||
> 1F6C0;person taking bath
|
|
||||||
> 1F6C0 $;person taking bath
|
|
||||||
1F46B;man and woman holding hands
|
|
||||||
|
|
||||||
|
-------------------------
|
||||||
------------------
|
Remove unsupported emoji:
|
||||||
Remove some emoji:
|
|
||||||
|
|
||||||
1F469 $ 200D 1F692;woman firefighter
|
|
||||||
> remove all non-gendered duplicates below here
|
|
||||||
|
|
||||||
3030;wavy dash
|
3030;wavy dash
|
||||||
> remove copyright
|
> remove copyright
|
||||||
@@ -59,12 +63,81 @@ Remove some emoji:
|
|||||||
|
|
||||||
1F441;eye
|
1F441;eye
|
||||||
> remove eye in speech bubble
|
> remove eye in speech bubble
|
||||||
1F445;tongue
|
1F9E0;brain
|
||||||
|
|
||||||
|
(more may be present after an emoji update)
|
||||||
|
|
||||||
|
|
||||||
-------------------------
|
-------------------------------
|
||||||
Add preprocessor symbols:
|
Remove non-gendered duplicates:
|
||||||
|
|
||||||
@ = group separator
|
Search: ^(1F(46E|575|482|477|473|471|9D9|9DA|9DB|9DC|9DD|9DE|9DF|64D|64E|645|646|481|64B|647|926|937|486|487|6B6|3C3|46F|9D6|9D7|9D8|3CC|3C4|6A3|3CA|3CB|6B4|6B5|938|93C|93D|93E|939)|26F9)(\s\$;|;).*?\n
|
||||||
@1 = enable skin tones below
|
Replace:
|
||||||
@2 = disable skin tones below
|
|
||||||
|
(new ones may be added with an emoji update)
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------
|
||||||
|
Add skin tone preprocessor symbols:
|
||||||
|
|
||||||
|
#1 = enable skin tones below
|
||||||
|
find and replace '$;light skin ' with #1
|
||||||
|
|
||||||
|
#2 = disable skin tones below
|
||||||
|
find the last entry containing '$' and insert #2 below
|
||||||
|
|
||||||
|
|
||||||
|
----------------------
|
||||||
|
Add separator symbols:
|
||||||
|
|
||||||
|
1F64A;speak-no-evil monkey
|
||||||
|
> @
|
||||||
|
1F476;baby
|
||||||
|
|
||||||
|
1F469 200D 1F467 200D 1F467;family: woman, girl, girl
|
||||||
|
> @
|
||||||
|
1F933;selfie
|
||||||
|
|
||||||
|
|
||||||
|
1F48E;gem stone
|
||||||
|
> @
|
||||||
|
1F435;monkey face
|
||||||
|
|
||||||
|
|
||||||
|
1F982;scorpion
|
||||||
|
> @
|
||||||
|
1F490;bouquet
|
||||||
|
|
||||||
|
|
||||||
|
1F52A;kitchen knife
|
||||||
|
> @
|
||||||
|
1F3FA;amphora
|
||||||
|
|
||||||
|
|
||||||
|
1F6C1;bathtub
|
||||||
|
> @
|
||||||
|
231B;hourglass done
|
||||||
|
|
||||||
|
|
||||||
|
1F6D2;shopping cart
|
||||||
|
> @
|
||||||
|
1F3E7;ATM sign
|
||||||
|
|
||||||
|
|
||||||
|
1F3F3 FE0F 200D 1F308;rainbow flag
|
||||||
|
> @
|
||||||
|
1F1E6 1F1E8;Ascension Island
|
||||||
|
|
||||||
|
|
||||||
|
--------------
|
||||||
|
Final cleanup:
|
||||||
|
|
||||||
|
Search: & |,|“|”|o’
|
||||||
|
Replace:
|
||||||
|
|
||||||
|
---
|
||||||
|
Make sure all emoji are formatted correctly and there are no empty lines (no line at the end of file).
|
||||||
|
|
||||||
|
Test emoji search by searching for the first and last emoji. If broken, a single emoji could be formatted wrong, or an emoji is not supported by Twitter (such as the 'eye in speech bubble' which causes two images to display and messes up the offsets).
|
||||||
|
|
||||||
|
If there is an error in 'td:plugin:official/emoji-keyboard' when typing into the search bar, add a breakpoint to the erroring line and check if 'me.emojiNames.length' equals 'emoji.length'.
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
1F600;grinning face
|
1F600;grinning face
|
||||||
1F601;grinning face with smiling eyes
|
1F601;beaming face with smiling eyes
|
||||||
1F602;face with tears of joy
|
1F602;face with tears of joy
|
||||||
1F923;rolling on the floor laughing
|
1F923;rolling on the floor laughing
|
||||||
1F603;smiling face with open mouth
|
1F603;grinning face with big eyes
|
||||||
1F604;smiling face with open mouth & smiling eyes
|
1F604;grinning face with smiling eyes
|
||||||
1F605;smiling face with open mouth & cold sweat
|
1F605;grinning face with sweat
|
||||||
1F606;smiling face with open mouth & closed eyes
|
1F606;grinning squinting face
|
||||||
1F609;winking face
|
1F609;winking face
|
||||||
1F60A;smiling face with smiling eyes
|
1F60A;smiling face with smiling eyes
|
||||||
1F60B;face savouring delicious food
|
1F60B;face savoring food
|
||||||
1F60E;smiling face with sunglasses
|
1F60E;smiling face with sunglasses
|
||||||
1F60D;smiling face with heart-eyes
|
1F60D;smiling face with heart-eyes
|
||||||
1F618;face blowing a kiss
|
1F618;face blowing a kiss
|
||||||
@@ -18,14 +18,16 @@
|
|||||||
263A;smiling face
|
263A;smiling face
|
||||||
1F642;slightly smiling face
|
1F642;slightly smiling face
|
||||||
1F917;hugging face
|
1F917;hugging face
|
||||||
|
1F929;star-struck
|
||||||
1F914;thinking face
|
1F914;thinking face
|
||||||
|
1F928;face with raised eyebrow
|
||||||
1F610;neutral face
|
1F610;neutral face
|
||||||
1F611;expressionless face
|
1F611;expressionless face
|
||||||
1F636;face without mouth
|
1F636;face without mouth
|
||||||
1F644;face with rolling eyes
|
1F644;face with rolling eyes
|
||||||
1F60F;smirking face
|
1F60F;smirking face
|
||||||
1F623;persevering face
|
1F623;persevering face
|
||||||
1F625;disappointed but relieved face
|
1F625;sad but relieved face
|
||||||
1F62E;face with open mouth
|
1F62E;face with open mouth
|
||||||
1F910;zipper-mouth face
|
1F910;zipper-mouth face
|
||||||
1F62F;hushed face
|
1F62F;hushed face
|
||||||
@@ -33,13 +35,12 @@
|
|||||||
1F62B;tired face
|
1F62B;tired face
|
||||||
1F634;sleeping face
|
1F634;sleeping face
|
||||||
1F60C;relieved face
|
1F60C;relieved face
|
||||||
1F913;nerd face
|
1F61B;face with tongue
|
||||||
1F61B;face with stuck-out tongue
|
1F61C;winking face with tongue
|
||||||
1F61C;face with stuck-out tongue & winking eye
|
1F61D;squinting face with tongue
|
||||||
1F61D;face with stuck-out tongue & closed eyes
|
|
||||||
1F924;drooling face
|
1F924;drooling face
|
||||||
1F612;unamused face
|
1F612;unamused face
|
||||||
1F613;face with cold sweat
|
1F613;downcast face with sweat
|
||||||
1F614;pensive face
|
1F614;pensive face
|
||||||
1F615;confused face
|
1F615;confused face
|
||||||
1F643;upside-down face
|
1F643;upside-down face
|
||||||
@@ -57,22 +58,30 @@
|
|||||||
1F627;anguished face
|
1F627;anguished face
|
||||||
1F628;fearful face
|
1F628;fearful face
|
||||||
1F629;weary face
|
1F629;weary face
|
||||||
|
1F92F;exploding head
|
||||||
1F62C;grimacing face
|
1F62C;grimacing face
|
||||||
1F630;face with open mouth & cold sweat
|
1F630;anxious face with sweat
|
||||||
1F631;face screaming in fear
|
1F631;face screaming in fear
|
||||||
1F633;flushed face
|
1F633;flushed face
|
||||||
|
1F92A;crazy face
|
||||||
1F635;dizzy face
|
1F635;dizzy face
|
||||||
1F621;pouting face
|
1F621;pouting face
|
||||||
1F620;angry face
|
1F620;angry face
|
||||||
1F607;smiling face with halo
|
1F92C;face with symbols on mouth
|
||||||
1F920;cowboy hat face
|
|
||||||
1F921;clown face
|
|
||||||
1F925;lying face
|
|
||||||
1F637;face with medical mask
|
1F637;face with medical mask
|
||||||
1F912;face with thermometer
|
1F912;face with thermometer
|
||||||
1F915;face with head-bandage
|
1F915;face with head-bandage
|
||||||
1F922;nauseated face
|
1F922;nauseated face
|
||||||
|
1F92E;face vomiting
|
||||||
1F927;sneezing face
|
1F927;sneezing face
|
||||||
|
1F607;smiling face with halo
|
||||||
|
1F920;cowboy hat face
|
||||||
|
1F921;clown face
|
||||||
|
1F925;lying face
|
||||||
|
1F92B;shushing face
|
||||||
|
1F92D;face with hand over mouth
|
||||||
|
1F9D0;face with monocle
|
||||||
|
1F913;nerd face
|
||||||
1F608;smiling face with horns
|
1F608;smiling face with horns
|
||||||
1F47F;angry face with horns
|
1F47F;angry face with horns
|
||||||
1F479;ogre
|
1F479;ogre
|
||||||
@@ -84,35 +93,40 @@
|
|||||||
1F47E;alien monster
|
1F47E;alien monster
|
||||||
1F916;robot face
|
1F916;robot face
|
||||||
1F4A9;pile of poo
|
1F4A9;pile of poo
|
||||||
1F63A;smiling cat face with open mouth
|
1F63A;grinning cat face
|
||||||
1F638;grinning cat face with smiling eyes
|
1F638;grinning cat face with smiling eyes
|
||||||
1F639;cat face with tears of joy
|
1F639;cat face with tears of joy
|
||||||
1F63B;smiling cat face with heart-eyes
|
1F63B;smiling cat face with heart-eyes
|
||||||
1F63C;cat face with wry smile
|
1F63C;cat face with wry smile
|
||||||
1F63D;kissing cat face with closed eyes
|
1F63D;kissing cat face
|
||||||
1F640;weary cat face
|
1F640;weary cat face
|
||||||
1F63F;crying cat face
|
1F63F;crying cat face
|
||||||
1F63E;pouting cat face
|
1F63E;pouting cat face
|
||||||
1F648;see-no-evil monkey
|
1F648;see-no-evil monkey
|
||||||
1F649;hear-no-evil monkey
|
1F649;hear-no-evil monkey
|
||||||
1F64A;speak-no-evil monkey
|
1F64A;speak-no-evil monkey
|
||||||
@1 enable skin tones
|
@
|
||||||
|
#1 enable skin tones
|
||||||
|
1F476;baby
|
||||||
|
1F476 $;baby
|
||||||
|
1F9D2;child
|
||||||
|
1F9D2 $;child
|
||||||
1F466;boy
|
1F466;boy
|
||||||
1F466 $;boy
|
1F466 $;boy
|
||||||
1F467;girl
|
1F467;girl
|
||||||
1F467 $;girl
|
1F467 $;girl
|
||||||
|
1F9D1;adult
|
||||||
|
1F9D1 $;adult
|
||||||
1F468;man
|
1F468;man
|
||||||
1F468 $;man
|
1F468 $;man
|
||||||
1F469;woman
|
1F469;woman
|
||||||
1F469 $;woman
|
1F469 $;woman
|
||||||
|
1F9D3;older adult
|
||||||
|
1F9D3 $;older adult
|
||||||
1F474;old man
|
1F474;old man
|
||||||
1F474 $;old man
|
1F474 $;old man
|
||||||
1F475;old woman
|
1F475;old woman
|
||||||
1F475 $;old woman
|
1F475 $;old woman
|
||||||
1F476;baby
|
|
||||||
1F476 $;baby
|
|
||||||
1F47C;baby angel
|
|
||||||
1F47C $;baby angel
|
|
||||||
1F468 200D 2695 FE0F;man health worker
|
1F468 200D 2695 FE0F;man health worker
|
||||||
1F468 $ 200D 2695 FE0F;man health worker
|
1F468 $ 200D 2695 FE0F;man health worker
|
||||||
1F469 200D 2695 FE0F;woman health worker
|
1F469 200D 2695 FE0F;woman health worker
|
||||||
@@ -193,30 +207,62 @@
|
|||||||
1F477 $ 200D 2642 FE0F;man construction worker
|
1F477 $ 200D 2642 FE0F;man construction worker
|
||||||
1F477 200D 2640 FE0F;woman construction worker
|
1F477 200D 2640 FE0F;woman construction worker
|
||||||
1F477 $ 200D 2640 FE0F;woman construction worker
|
1F477 $ 200D 2640 FE0F;woman construction worker
|
||||||
|
1F934;prince
|
||||||
|
1F934 $;prince
|
||||||
|
1F478;princess
|
||||||
|
1F478 $;princess
|
||||||
1F473 200D 2642 FE0F;man wearing turban
|
1F473 200D 2642 FE0F;man wearing turban
|
||||||
1F473 $ 200D 2642 FE0F;man wearing turban
|
1F473 $ 200D 2642 FE0F;man wearing turban
|
||||||
1F473 200D 2640 FE0F;woman wearing turban
|
1F473 200D 2640 FE0F;woman wearing turban
|
||||||
1F473 $ 200D 2640 FE0F;woman wearing turban
|
1F473 $ 200D 2640 FE0F;woman wearing turban
|
||||||
|
1F472;man with Chinese cap
|
||||||
|
1F472 $;man with Chinese cap
|
||||||
|
1F9D5;woman with headscarf
|
||||||
|
1F9D5 $;woman with headscarf
|
||||||
|
1F9D4;bearded person
|
||||||
|
1F9D4 $;bearded person
|
||||||
1F471 200D 2642 FE0F;blond-haired man
|
1F471 200D 2642 FE0F;blond-haired man
|
||||||
1F471 $ 200D 2642 FE0F;blond-haired man
|
1F471 $ 200D 2642 FE0F;blond-haired man
|
||||||
1F471 200D 2640 FE0F;blond-haired woman
|
1F471 200D 2640 FE0F;blond-haired woman
|
||||||
1F471 $ 200D 2640 FE0F;blond-haired woman
|
1F471 $ 200D 2640 FE0F;blond-haired woman
|
||||||
|
1F935;man in tuxedo
|
||||||
|
1F935 $;man in tuxedo
|
||||||
|
1F470;bride with veil
|
||||||
|
1F470 $;bride with veil
|
||||||
|
1F930;pregnant woman
|
||||||
|
1F930 $;pregnant woman
|
||||||
|
1F931;breast-feeding
|
||||||
|
1F931 $;breast-feeding
|
||||||
|
1F47C;baby angel
|
||||||
|
1F47C $;baby angel
|
||||||
1F385;Santa Claus
|
1F385;Santa Claus
|
||||||
1F385 $;Santa Claus
|
1F385 $;Santa Claus
|
||||||
1F936;Mrs. Claus
|
1F936;Mrs. Claus
|
||||||
1F936 $;Mrs. Claus
|
1F936 $;Mrs. Claus
|
||||||
1F478;princess
|
1F9D9 200D 2640 FE0F;woman mage
|
||||||
1F478 $;princess
|
1F9D9 $ 200D 2640 FE0F;woman mage
|
||||||
1F934;prince
|
1F9D9 200D 2642 FE0F;man mage
|
||||||
1F934 $;prince
|
1F9D9 $ 200D 2642 FE0F;man mage
|
||||||
1F470;bride with veil
|
1F9DA 200D 2640 FE0F;woman fairy
|
||||||
1F470 $;bride with veil
|
1F9DA $ 200D 2640 FE0F;woman fairy
|
||||||
1F935;man in tuxedo
|
1F9DA 200D 2642 FE0F;man fairy
|
||||||
1F935 $;man in tuxedo
|
1F9DA $ 200D 2642 FE0F;man fairy
|
||||||
1F930;pregnant woman
|
1F9DB 200D 2640 FE0F;woman vampire
|
||||||
1F930 $;pregnant woman
|
1F9DB $ 200D 2640 FE0F;woman vampire
|
||||||
1F472;man with Chinese cap
|
1F9DB 200D 2642 FE0F;man vampire
|
||||||
1F472 $;man with Chinese cap
|
1F9DB $ 200D 2642 FE0F;man vampire
|
||||||
|
1F9DC 200D 2640 FE0F;mermaid
|
||||||
|
1F9DC $ 200D 2640 FE0F;mermaid
|
||||||
|
1F9DC 200D 2642 FE0F;merman
|
||||||
|
1F9DC $ 200D 2642 FE0F;merman
|
||||||
|
1F9DD 200D 2640 FE0F;woman elf
|
||||||
|
1F9DD $ 200D 2640 FE0F;woman elf
|
||||||
|
1F9DD 200D 2642 FE0F;man elf
|
||||||
|
1F9DD $ 200D 2642 FE0F;man elf
|
||||||
|
1F9DE 200D 2640 FE0F;woman genie
|
||||||
|
1F9DE 200D 2642 FE0F;man genie
|
||||||
|
1F9DF 200D 2640 FE0F;woman zombie
|
||||||
|
1F9DF 200D 2642 FE0F;man zombie
|
||||||
1F64D 200D 2642 FE0F;man frowning
|
1F64D 200D 2642 FE0F;man frowning
|
||||||
1F64D $ 200D 2642 FE0F;man frowning
|
1F64D $ 200D 2642 FE0F;man frowning
|
||||||
1F64D 200D 2640 FE0F;woman frowning
|
1F64D 200D 2640 FE0F;woman frowning
|
||||||
@@ -273,10 +319,26 @@
|
|||||||
1F483 $;woman dancing
|
1F483 $;woman dancing
|
||||||
1F57A;man dancing
|
1F57A;man dancing
|
||||||
1F57A $;man dancing
|
1F57A $;man dancing
|
||||||
1F46F 200D 2642 FE0F;men with bunny ears partying
|
1F46F 200D 2642 FE0F;men with bunny ears
|
||||||
1F46F 200D 2640 FE0F;women with bunny ears partying
|
1F46F 200D 2640 FE0F;women with bunny ears
|
||||||
1F574;man in business suit levitating
|
1F9D6 200D 2640 FE0F;woman in steamy room
|
||||||
1F574 $;man in business suit levitating
|
1F9D6 $ 200D 2640 FE0F;woman in steamy room
|
||||||
|
1F9D6 200D 2642 FE0F;man in steamy room
|
||||||
|
1F9D6 $ 200D 2642 FE0F;man in steamy room
|
||||||
|
1F9D7 200D 2640 FE0F;woman climbing
|
||||||
|
1F9D7 $ 200D 2640 FE0F;woman climbing
|
||||||
|
1F9D7 200D 2642 FE0F;man climbing
|
||||||
|
1F9D7 $ 200D 2642 FE0F;man climbing
|
||||||
|
1F9D8 200D 2640 FE0F;woman in lotus position
|
||||||
|
1F9D8 $ 200D 2640 FE0F;woman in lotus position
|
||||||
|
1F9D8 200D 2642 FE0F;man in lotus position
|
||||||
|
1F9D8 $ 200D 2642 FE0F;man in lotus position
|
||||||
|
1F6C0;person taking bath
|
||||||
|
1F6C0 $;person taking bath
|
||||||
|
1F6CC;person in bed
|
||||||
|
1F6CC $;person in bed
|
||||||
|
1F574;man in suit levitating
|
||||||
|
1F574 $;man in suit levitating
|
||||||
1F5E3;speaking head
|
1F5E3;speaking head
|
||||||
1F464;bust in silhouette
|
1F464;bust in silhouette
|
||||||
1F465;busts in silhouette
|
1F465;busts in silhouette
|
||||||
@@ -338,52 +400,48 @@
|
|||||||
1F939 $ 200D 2642 FE0F;man juggling
|
1F939 $ 200D 2642 FE0F;man juggling
|
||||||
1F939 200D 2640 FE0F;woman juggling
|
1F939 200D 2640 FE0F;woman juggling
|
||||||
1F939 $ 200D 2640 FE0F;woman juggling
|
1F939 $ 200D 2640 FE0F;woman juggling
|
||||||
1F6CC;person in bed
|
|
||||||
1F6CC $;person in bed
|
|
||||||
1F6C0;person taking bath
|
|
||||||
1F6C0 $;person taking bath
|
|
||||||
1F46B;man and woman holding hands
|
1F46B;man and woman holding hands
|
||||||
1F46C;two men holding hands
|
1F46C;two men holding hands
|
||||||
1F46D;two women holding hands
|
1F46D;two women holding hands
|
||||||
1F48F;kiss
|
1F48F;kiss
|
||||||
1F469 200D 2764 FE0F 200D 1F48B 200D 1F468;kiss
|
1F469 200D 2764 FE0F 200D 1F48B 200D 1F468;kiss woman man
|
||||||
1F468 200D 2764 FE0F 200D 1F48B 200D 1F468;kiss
|
1F468 200D 2764 FE0F 200D 1F48B 200D 1F468;kiss man man
|
||||||
1F469 200D 2764 FE0F 200D 1F48B 200D 1F469;kiss
|
1F469 200D 2764 FE0F 200D 1F48B 200D 1F469;kiss woman woman
|
||||||
1F491;couple with heart
|
1F491;couple with heart
|
||||||
1F469 200D 2764 FE0F 200D 1F468;couple with heart
|
1F469 200D 2764 FE0F 200D 1F468;couple with heart woman man
|
||||||
1F468 200D 2764 FE0F 200D 1F468;couple with heart
|
1F468 200D 2764 FE0F 200D 1F468;couple with heart man man
|
||||||
1F469 200D 2764 FE0F 200D 1F469;couple with heart
|
1F469 200D 2764 FE0F 200D 1F469;couple with heart woman woman
|
||||||
1F46A;family
|
1F46A;family
|
||||||
1F468 200D 1F469 200D 1F466;family
|
1F468 200D 1F469 200D 1F466;family man woman boy
|
||||||
1F468 200D 1F469 200D 1F467;family
|
1F468 200D 1F469 200D 1F467;family man woman girl
|
||||||
1F468 200D 1F469 200D 1F467 200D 1F466;family
|
1F468 200D 1F469 200D 1F467 200D 1F466;family man woman girl boy
|
||||||
1F468 200D 1F469 200D 1F466 200D 1F466;family
|
1F468 200D 1F469 200D 1F466 200D 1F466;family man woman boy boy
|
||||||
1F468 200D 1F469 200D 1F467 200D 1F467;family
|
1F468 200D 1F469 200D 1F467 200D 1F467;family man woman girl girl
|
||||||
1F468 200D 1F468 200D 1F466;family
|
1F468 200D 1F468 200D 1F466;family man man boy
|
||||||
1F468 200D 1F468 200D 1F467;family
|
1F468 200D 1F468 200D 1F467;family man man girl
|
||||||
1F468 200D 1F468 200D 1F467 200D 1F466;family
|
1F468 200D 1F468 200D 1F467 200D 1F466;family man man girl boy
|
||||||
1F468 200D 1F468 200D 1F466 200D 1F466;family
|
1F468 200D 1F468 200D 1F466 200D 1F466;family man man boy boy
|
||||||
1F468 200D 1F468 200D 1F467 200D 1F467;family
|
1F468 200D 1F468 200D 1F467 200D 1F467;family man man girl girl
|
||||||
1F469 200D 1F469 200D 1F466;family
|
1F469 200D 1F469 200D 1F466;family woman woman boy
|
||||||
1F469 200D 1F469 200D 1F467;family
|
1F469 200D 1F469 200D 1F467;family woman woman girl
|
||||||
1F469 200D 1F469 200D 1F467 200D 1F466;family
|
1F469 200D 1F469 200D 1F467 200D 1F466;family woman woman girl boy
|
||||||
1F469 200D 1F469 200D 1F466 200D 1F466;family
|
1F469 200D 1F469 200D 1F466 200D 1F466;family woman woman boy boy
|
||||||
1F469 200D 1F469 200D 1F467 200D 1F467;family
|
1F469 200D 1F469 200D 1F467 200D 1F467;family woman woman girl girl
|
||||||
1F468 200D 1F466;family
|
1F468 200D 1F466;family man boy
|
||||||
1F468 200D 1F466 200D 1F466;family
|
1F468 200D 1F466 200D 1F466;family man boy boy
|
||||||
1F468 200D 1F467;family
|
1F468 200D 1F467;family man girl
|
||||||
1F468 200D 1F467 200D 1F466;family
|
1F468 200D 1F467 200D 1F466;family man girl boy
|
||||||
1F468 200D 1F467 200D 1F467;family
|
1F468 200D 1F467 200D 1F467;family man girl girl
|
||||||
1F469 200D 1F466;family
|
1F469 200D 1F466;family woman boy
|
||||||
1F469 200D 1F466 200D 1F466;family
|
1F469 200D 1F466 200D 1F466;family woman boy boy
|
||||||
1F469 200D 1F467;family
|
1F469 200D 1F467;family woman girl
|
||||||
1F469 200D 1F467 200D 1F466;family
|
1F469 200D 1F467 200D 1F466;family woman girl boy
|
||||||
1F469 200D 1F467 200D 1F467;family
|
1F469 200D 1F467 200D 1F467;family woman girl girl
|
||||||
@
|
@
|
||||||
1F4AA;flexed biceps
|
|
||||||
1F4AA $;flexed biceps
|
|
||||||
1F933;selfie
|
1F933;selfie
|
||||||
1F933 $;selfie
|
1F933 $;selfie
|
||||||
|
1F4AA;flexed biceps
|
||||||
|
1F4AA $;flexed biceps
|
||||||
1F448;backhand index pointing left
|
1F448;backhand index pointing left
|
||||||
1F448 $;backhand index pointing left
|
1F448 $;backhand index pointing left
|
||||||
1F449;backhand index pointing right
|
1F449;backhand index pointing right
|
||||||
@@ -406,8 +464,8 @@
|
|||||||
1F918 $;sign of the horns
|
1F918 $;sign of the horns
|
||||||
1F919;call me hand
|
1F919;call me hand
|
||||||
1F919 $;call me hand
|
1F919 $;call me hand
|
||||||
1F590;raised hand with fingers splayed
|
1F590;hand with fingers splayed
|
||||||
1F590 $;raised hand with fingers splayed
|
1F590 $;hand with fingers splayed
|
||||||
270B;raised hand
|
270B;raised hand
|
||||||
270B $;raised hand
|
270B $;raised hand
|
||||||
1F44C;OK hand
|
1F44C;OK hand
|
||||||
@@ -428,14 +486,18 @@
|
|||||||
1F91A $;raised back of hand
|
1F91A $;raised back of hand
|
||||||
1F44B;waving hand
|
1F44B;waving hand
|
||||||
1F44B $;waving hand
|
1F44B $;waving hand
|
||||||
1F44F;clapping hands
|
1F91F;love-you gesture
|
||||||
1F44F $;clapping hands
|
1F91F $;love-you gesture
|
||||||
270D;writing hand
|
270D;writing hand
|
||||||
270D $;writing hand
|
270D $;writing hand
|
||||||
|
1F44F;clapping hands
|
||||||
|
1F44F $;clapping hands
|
||||||
1F450;open hands
|
1F450;open hands
|
||||||
1F450 $;open hands
|
1F450 $;open hands
|
||||||
1F64C;raising hands
|
1F64C;raising hands
|
||||||
1F64C $;raising hands
|
1F64C $;raising hands
|
||||||
|
1F932;palms up together
|
||||||
|
1F932 $;palms up together
|
||||||
1F64F;folded hands
|
1F64F;folded hands
|
||||||
1F64F $;folded hands
|
1F64F $;folded hands
|
||||||
1F485;nail polish
|
1F485;nail polish
|
||||||
@@ -444,10 +506,12 @@
|
|||||||
1F442 $;ear
|
1F442 $;ear
|
||||||
1F443;nose
|
1F443;nose
|
||||||
1F443 $;nose
|
1F443 $;nose
|
||||||
|
#2 no more skin tones beyond this point
|
||||||
1F91D;handshake
|
1F91D;handshake
|
||||||
1F463;footprints
|
1F463;footprints
|
||||||
1F440;eyes
|
1F440;eyes
|
||||||
1F441;eye
|
1F441;eye
|
||||||
|
1F9E0;brain
|
||||||
1F445;tongue
|
1F445;tongue
|
||||||
1F444;mouth
|
1F444;mouth
|
||||||
1F48B;kiss mark
|
1F48B;kiss mark
|
||||||
@@ -461,6 +525,7 @@
|
|||||||
1F499;blue heart
|
1F499;blue heart
|
||||||
1F49A;green heart
|
1F49A;green heart
|
||||||
1F49B;yellow heart
|
1F49B;yellow heart
|
||||||
|
1F9E1;orange heart
|
||||||
1F49C;purple heart
|
1F49C;purple heart
|
||||||
1F5A4;black heart
|
1F5A4;black heart
|
||||||
1F49D;heart with ribbon
|
1F49D;heart with ribbon
|
||||||
@@ -485,6 +550,10 @@
|
|||||||
1F454;necktie
|
1F454;necktie
|
||||||
1F455;t-shirt
|
1F455;t-shirt
|
||||||
1F456;jeans
|
1F456;jeans
|
||||||
|
1F9E3;scarf
|
||||||
|
1F9E4;gloves
|
||||||
|
1F9E5;coat
|
||||||
|
1F9E6;socks
|
||||||
1F457;dress
|
1F457;dress
|
||||||
1F458;kimono
|
1F458;kimono
|
||||||
1F459;bikini
|
1F459;bikini
|
||||||
@@ -503,12 +572,13 @@
|
|||||||
1F452;woman’s hat
|
1F452;woman’s hat
|
||||||
1F3A9;top hat
|
1F3A9;top hat
|
||||||
1F393;graduation cap
|
1F393;graduation cap
|
||||||
|
1F9E2;billed cap
|
||||||
26D1;rescue worker’s helmet
|
26D1;rescue worker’s helmet
|
||||||
1F4FF;prayer beads
|
1F4FF;prayer beads
|
||||||
1F484;lipstick
|
1F484;lipstick
|
||||||
1F48D;ring
|
1F48D;ring
|
||||||
1F48E;gem stone
|
1F48E;gem stone
|
||||||
@2 no more skin tones beyond this point
|
@
|
||||||
1F435;monkey face
|
1F435;monkey face
|
||||||
1F412;monkey
|
1F412;monkey
|
||||||
1F98D;gorilla
|
1F98D;gorilla
|
||||||
@@ -525,8 +595,9 @@
|
|||||||
1F406;leopard
|
1F406;leopard
|
||||||
1F434;horse face
|
1F434;horse face
|
||||||
1F40E;horse
|
1F40E;horse
|
||||||
1F98C;deer
|
|
||||||
1F984;unicorn face
|
1F984;unicorn face
|
||||||
|
1F993;zebra
|
||||||
|
1F98C;deer
|
||||||
1F42E;cow face
|
1F42E;cow face
|
||||||
1F402;ox
|
1F402;ox
|
||||||
1F403;water buffalo
|
1F403;water buffalo
|
||||||
@@ -540,6 +611,7 @@
|
|||||||
1F410;goat
|
1F410;goat
|
||||||
1F42A;camel
|
1F42A;camel
|
||||||
1F42B;two-hump camel
|
1F42B;two-hump camel
|
||||||
|
1F992;giraffe
|
||||||
1F418;elephant
|
1F418;elephant
|
||||||
1F98F;rhinoceros
|
1F98F;rhinoceros
|
||||||
1F42D;mouse face
|
1F42D;mouse face
|
||||||
@@ -549,6 +621,7 @@
|
|||||||
1F430;rabbit face
|
1F430;rabbit face
|
||||||
1F407;rabbit
|
1F407;rabbit
|
||||||
1F43F;chipmunk
|
1F43F;chipmunk
|
||||||
|
1F994;hedgehog
|
||||||
1F987;bat
|
1F987;bat
|
||||||
1F43B;bear face
|
1F43B;bear face
|
||||||
1F428;koala
|
1F428;koala
|
||||||
@@ -573,6 +646,8 @@
|
|||||||
1F40D;snake
|
1F40D;snake
|
||||||
1F432;dragon face
|
1F432;dragon face
|
||||||
1F409;dragon
|
1F409;dragon
|
||||||
|
1F995;sauropod
|
||||||
|
1F996;T-Rex
|
||||||
1F433;spouting whale
|
1F433;spouting whale
|
||||||
1F40B;whale
|
1F40B;whale
|
||||||
1F42C;dolphin
|
1F42C;dolphin
|
||||||
@@ -585,12 +660,13 @@
|
|||||||
1F980;crab
|
1F980;crab
|
||||||
1F990;shrimp
|
1F990;shrimp
|
||||||
1F991;squid
|
1F991;squid
|
||||||
1F98B;butterfly
|
|
||||||
1F40C;snail
|
1F40C;snail
|
||||||
|
1F98B;butterfly
|
||||||
1F41B;bug
|
1F41B;bug
|
||||||
1F41C;ant
|
1F41C;ant
|
||||||
1F41D;honeybee
|
1F41D;honeybee
|
||||||
1F41E;lady beetle
|
1F41E;lady beetle
|
||||||
|
1F997;cricket
|
||||||
1F577;spider
|
1F577;spider
|
||||||
1F578;spider web
|
1F578;spider web
|
||||||
1F982;scorpion
|
1F982;scorpion
|
||||||
@@ -618,7 +694,6 @@
|
|||||||
1F342;fallen leaf
|
1F342;fallen leaf
|
||||||
1F343;leaf fluttering in wind
|
1F343;leaf fluttering in wind
|
||||||
1F347;grapes
|
1F347;grapes
|
||||||
@
|
|
||||||
1F348;melon
|
1F348;melon
|
||||||
1F349;watermelon
|
1F349;watermelon
|
||||||
1F34A;tangerine
|
1F34A;tangerine
|
||||||
@@ -633,6 +708,7 @@
|
|||||||
1F353;strawberry
|
1F353;strawberry
|
||||||
1F95D;kiwi fruit
|
1F95D;kiwi fruit
|
||||||
1F345;tomato
|
1F345;tomato
|
||||||
|
1F965;coconut
|
||||||
1F951;avocado
|
1F951;avocado
|
||||||
1F346;eggplant
|
1F346;eggplant
|
||||||
1F954;potato
|
1F954;potato
|
||||||
@@ -640,21 +716,25 @@
|
|||||||
1F33D;ear of corn
|
1F33D;ear of corn
|
||||||
1F336;hot pepper
|
1F336;hot pepper
|
||||||
1F952;cucumber
|
1F952;cucumber
|
||||||
|
1F966;broccoli
|
||||||
1F344;mushroom
|
1F344;mushroom
|
||||||
1F95C;peanuts
|
1F95C;peanuts
|
||||||
1F330;chestnut
|
1F330;chestnut
|
||||||
1F35E;bread
|
1F35E;bread
|
||||||
1F950;croissant
|
1F950;croissant
|
||||||
1F956;baguette bread
|
1F956;baguette bread
|
||||||
|
1F968;pretzel
|
||||||
1F95E;pancakes
|
1F95E;pancakes
|
||||||
1F9C0;cheese wedge
|
1F9C0;cheese wedge
|
||||||
1F356;meat on bone
|
1F356;meat on bone
|
||||||
1F357;poultry leg
|
1F357;poultry leg
|
||||||
|
1F969;cut of meat
|
||||||
1F953;bacon
|
1F953;bacon
|
||||||
1F354;hamburger
|
1F354;hamburger
|
||||||
1F35F;french fries
|
1F35F;french fries
|
||||||
1F355;pizza
|
1F355;pizza
|
||||||
1F32D;hot dog
|
1F32D;hot dog
|
||||||
|
1F96A;sandwich
|
||||||
1F32E;taco
|
1F32E;taco
|
||||||
1F32F;burrito
|
1F32F;burrito
|
||||||
1F959;stuffed flatbread
|
1F959;stuffed flatbread
|
||||||
@@ -662,8 +742,10 @@
|
|||||||
1F373;cooking
|
1F373;cooking
|
||||||
1F958;shallow pan of food
|
1F958;shallow pan of food
|
||||||
1F372;pot of food
|
1F372;pot of food
|
||||||
|
1F963;bowl with spoon
|
||||||
1F957;green salad
|
1F957;green salad
|
||||||
1F37F;popcorn
|
1F37F;popcorn
|
||||||
|
1F96B;canned food
|
||||||
1F371;bento box
|
1F371;bento box
|
||||||
1F358;rice cracker
|
1F358;rice cracker
|
||||||
1F359;rice ball
|
1F359;rice ball
|
||||||
@@ -677,6 +759,9 @@
|
|||||||
1F364;fried shrimp
|
1F364;fried shrimp
|
||||||
1F365;fish cake with swirl
|
1F365;fish cake with swirl
|
||||||
1F361;dango
|
1F361;dango
|
||||||
|
1F95F;dumpling
|
||||||
|
1F960;fortune cookie
|
||||||
|
1F961;takeout box
|
||||||
1F366;soft ice cream
|
1F366;soft ice cream
|
||||||
1F367;shaved ice
|
1F367;shaved ice
|
||||||
1F368;ice cream
|
1F368;ice cream
|
||||||
@@ -684,6 +769,7 @@
|
|||||||
1F36A;cookie
|
1F36A;cookie
|
||||||
1F382;birthday cake
|
1F382;birthday cake
|
||||||
1F370;shortcake
|
1F370;shortcake
|
||||||
|
1F967;pie
|
||||||
1F36B;chocolate bar
|
1F36B;chocolate bar
|
||||||
1F36C;candy
|
1F36C;candy
|
||||||
1F36D;lollipop
|
1F36D;lollipop
|
||||||
@@ -702,6 +788,8 @@
|
|||||||
1F37B;clinking beer mugs
|
1F37B;clinking beer mugs
|
||||||
1F942;clinking glasses
|
1F942;clinking glasses
|
||||||
1F943;tumbler glass
|
1F943;tumbler glass
|
||||||
|
1F964;cup with straw
|
||||||
|
1F962;chopsticks
|
||||||
1F37D;fork and knife with plate
|
1F37D;fork and knife with plate
|
||||||
1F374;fork and knife
|
1F374;fork and knife
|
||||||
1F944;spoon
|
1F944;spoon
|
||||||
@@ -726,7 +814,7 @@
|
|||||||
1F3DF;stadium
|
1F3DF;stadium
|
||||||
1F3DB;classical building
|
1F3DB;classical building
|
||||||
1F3D7;building construction
|
1F3D7;building construction
|
||||||
1F3D8;house
|
1F3D8;houses
|
||||||
1F3D9;cityscape
|
1F3D9;cityscape
|
||||||
1F3DA;derelict house
|
1F3DA;derelict house
|
||||||
1F3E0;house
|
1F3E0;house
|
||||||
@@ -775,7 +863,7 @@
|
|||||||
1F682;locomotive
|
1F682;locomotive
|
||||||
1F683;railway car
|
1F683;railway car
|
||||||
1F684;high-speed train
|
1F684;high-speed train
|
||||||
1F685;high-speed train with bullet nose
|
1F685;bullet train
|
||||||
1F686;train
|
1F686;train
|
||||||
1F687;metro
|
1F687;metro
|
||||||
1F688;light rail
|
1F688;light rail
|
||||||
@@ -829,8 +917,9 @@
|
|||||||
1F69F;suspension railway
|
1F69F;suspension railway
|
||||||
1F6A0;mountain cableway
|
1F6A0;mountain cableway
|
||||||
1F6A1;aerial tramway
|
1F6A1;aerial tramway
|
||||||
1F680;rocket
|
|
||||||
1F6F0;satellite
|
1F6F0;satellite
|
||||||
|
1F680;rocket
|
||||||
|
1F6F8;flying saucer
|
||||||
1F6CE;bellhop bell
|
1F6CE;bellhop bell
|
||||||
1F6AA;door
|
1F6AA;door
|
||||||
1F6CF;bed
|
1F6CF;bed
|
||||||
@@ -839,36 +928,36 @@
|
|||||||
1F6BF;shower
|
1F6BF;shower
|
||||||
1F6C1;bathtub
|
1F6C1;bathtub
|
||||||
@
|
@
|
||||||
231B;hourglass
|
231B;hourglass done
|
||||||
23F3;hourglass with flowing sand
|
23F3;hourglass not done
|
||||||
231A;watch
|
231A;watch
|
||||||
23F0;alarm clock
|
23F0;alarm clock
|
||||||
23F1;stopwatch
|
23F1;stopwatch
|
||||||
23F2;timer clock
|
23F2;timer clock
|
||||||
1F570;mantelpiece clock
|
1F570;mantelpiece clock
|
||||||
1F55B;twelve o’clock
|
1F55B;twelve clock
|
||||||
1F567;twelve-thirty
|
1F567;twelve-thirty
|
||||||
1F550;one o’clock
|
1F550;one clock
|
||||||
1F55C;one-thirty
|
1F55C;one-thirty
|
||||||
1F551;two o’clock
|
1F551;two clock
|
||||||
1F55D;two-thirty
|
1F55D;two-thirty
|
||||||
1F552;three o’clock
|
1F552;three clock
|
||||||
1F55E;three-thirty
|
1F55E;three-thirty
|
||||||
1F553;four o’clock
|
1F553;four clock
|
||||||
1F55F;four-thirty
|
1F55F;four-thirty
|
||||||
1F554;five o’clock
|
1F554;five clock
|
||||||
1F560;five-thirty
|
1F560;five-thirty
|
||||||
1F555;six o’clock
|
1F555;six clock
|
||||||
1F561;six-thirty
|
1F561;six-thirty
|
||||||
1F556;seven o’clock
|
1F556;seven clock
|
||||||
1F562;seven-thirty
|
1F562;seven-thirty
|
||||||
1F557;eight o’clock
|
1F557;eight clock
|
||||||
1F563;eight-thirty
|
1F563;eight-thirty
|
||||||
1F558;nine o’clock
|
1F558;nine clock
|
||||||
1F564;nine-thirty
|
1F564;nine-thirty
|
||||||
1F559;ten o’clock
|
1F559;ten clock
|
||||||
1F565;ten-thirty
|
1F565;ten-thirty
|
||||||
1F55A;eleven o’clock
|
1F55A;eleven clock
|
||||||
1F566;eleven-thirty
|
1F566;eleven-thirty
|
||||||
1F311;new moon
|
1F311;new moon
|
||||||
1F312;waxing crescent moon
|
1F312;waxing crescent moon
|
||||||
@@ -880,11 +969,11 @@
|
|||||||
1F318;waning crescent moon
|
1F318;waning crescent moon
|
||||||
1F319;crescent moon
|
1F319;crescent moon
|
||||||
1F31A;new moon face
|
1F31A;new moon face
|
||||||
1F31B;first quarter moon with face
|
1F31B;first quarter moon face
|
||||||
1F31C;last quarter moon with face
|
1F31C;last quarter moon face
|
||||||
1F321;thermometer
|
1F321;thermometer
|
||||||
2600;sun
|
2600;sun
|
||||||
1F31D;full moon with face
|
1F31D;full moon face
|
||||||
1F31E;sun with face
|
1F31E;sun with face
|
||||||
2B50;white medium star
|
2B50;white medium star
|
||||||
1F31F;glowing star
|
1F31F;glowing star
|
||||||
@@ -949,7 +1038,7 @@
|
|||||||
1F3BE;tennis
|
1F3BE;tennis
|
||||||
1F3B1;pool 8 ball
|
1F3B1;pool 8 ball
|
||||||
1F3B3;bowling
|
1F3B3;bowling
|
||||||
1F3CF;cricket
|
1F3CF;cricket game
|
||||||
1F3D1;field hockey
|
1F3D1;field hockey
|
||||||
1F3D2;ice hockey
|
1F3D2;ice hockey
|
||||||
1F3D3;ping pong
|
1F3D3;ping pong
|
||||||
@@ -963,6 +1052,8 @@
|
|||||||
1F3A3;fishing pole
|
1F3A3;fishing pole
|
||||||
1F3BD;running shirt
|
1F3BD;running shirt
|
||||||
1F3BF;skis
|
1F3BF;skis
|
||||||
|
1F6F7;sled
|
||||||
|
1F94C;curling stone
|
||||||
1F3AE;video game
|
1F3AE;video game
|
||||||
1F579;joystick
|
1F579;joystick
|
||||||
1F3B2;game die
|
1F3B2;game die
|
||||||
@@ -1024,8 +1115,8 @@
|
|||||||
1F4F8;camera with flash
|
1F4F8;camera with flash
|
||||||
1F4F9;video camera
|
1F4F9;video camera
|
||||||
1F4FC;videocassette
|
1F4FC;videocassette
|
||||||
1F50D;left-pointing magnifying glass
|
1F50D;magnifying glass tilted left
|
||||||
1F50E;right-pointing magnifying glass
|
1F50E;magnifying glass tilted right
|
||||||
1F52C;microscope
|
1F52C;microscope
|
||||||
1F52D;telescope
|
1F52D;telescope
|
||||||
1F4E1;satellite antenna
|
1F4E1;satellite antenna
|
||||||
@@ -1177,7 +1268,7 @@
|
|||||||
2934;right arrow curving up
|
2934;right arrow curving up
|
||||||
2935;right arrow curving down
|
2935;right arrow curving down
|
||||||
1F503;clockwise vertical arrows
|
1F503;clockwise vertical arrows
|
||||||
1F504;anticlockwise arrows button
|
1F504;counterclockwise arrows button
|
||||||
1F519;BACK arrow
|
1F519;BACK arrow
|
||||||
1F51A;END arrow
|
1F51A;END arrow
|
||||||
1F51B;ON! arrow
|
1F51B;ON! arrow
|
||||||
@@ -1232,11 +1323,14 @@
|
|||||||
1F4F6;antenna bars
|
1F4F6;antenna bars
|
||||||
1F4F3;vibration mode
|
1F4F3;vibration mode
|
||||||
1F4F4;mobile phone off
|
1F4F4;mobile phone off
|
||||||
|
2640;female sign
|
||||||
|
2642;male sign
|
||||||
|
2695;medical symbol
|
||||||
267B;recycling symbol
|
267B;recycling symbol
|
||||||
1F4DB;name badge
|
|
||||||
269C;fleur-de-lis
|
269C;fleur-de-lis
|
||||||
1F530;Japanese symbol for beginner
|
|
||||||
1F531;trident emblem
|
1F531;trident emblem
|
||||||
|
1F4DB;name badge
|
||||||
|
1F530;Japanese symbol for beginner
|
||||||
2B55;heavy large circle
|
2B55;heavy large circle
|
||||||
2705;white heavy check mark
|
2705;white heavy check mark
|
||||||
2611;ballot box with check
|
2611;ballot box with check
|
||||||
@@ -1245,9 +1339,6 @@
|
|||||||
274C;cross mark
|
274C;cross mark
|
||||||
274E;cross mark button
|
274E;cross mark button
|
||||||
2795;heavy plus sign
|
2795;heavy plus sign
|
||||||
2640;female sign
|
|
||||||
2642;male sign
|
|
||||||
2695;medical symbol
|
|
||||||
2796;heavy minus sign
|
2796;heavy minus sign
|
||||||
2797;heavy division sign
|
2797;heavy division sign
|
||||||
27B0;curly loop
|
27B0;curly loop
|
||||||
@@ -1263,18 +1354,18 @@
|
|||||||
2755;white exclamation mark
|
2755;white exclamation mark
|
||||||
2757;exclamation mark
|
2757;exclamation mark
|
||||||
3030;wavy dash
|
3030;wavy dash
|
||||||
0023 FE0F 20E3;keycap
|
0023 FE0F 20E3;keycap #
|
||||||
002A FE0F 20E3;keycap
|
002A FE0F 20E3;keycap *
|
||||||
0030 FE0F 20E3;keycap
|
0030 FE0F 20E3;keycap 0
|
||||||
0031 FE0F 20E3;keycap
|
0031 FE0F 20E3;keycap 1
|
||||||
0032 FE0F 20E3;keycap
|
0032 FE0F 20E3;keycap 2
|
||||||
0033 FE0F 20E3;keycap
|
0033 FE0F 20E3;keycap 3
|
||||||
0034 FE0F 20E3;keycap
|
0034 FE0F 20E3;keycap 4
|
||||||
0035 FE0F 20E3;keycap
|
0035 FE0F 20E3;keycap 5
|
||||||
0036 FE0F 20E3;keycap
|
0036 FE0F 20E3;keycap 6
|
||||||
0037 FE0F 20E3;keycap
|
0037 FE0F 20E3;keycap 7
|
||||||
0038 FE0F 20E3;keycap
|
0038 FE0F 20E3;keycap 8
|
||||||
0039 FE0F 20E3;keycap
|
0039 FE0F 20E3;keycap 9
|
||||||
1F51F;keycap 10
|
1F51F;keycap 10
|
||||||
1F4AF;hundred points
|
1F4AF;hundred points
|
||||||
1F520;input latin uppercase
|
1F520;input latin uppercase
|
||||||
@@ -1299,23 +1390,23 @@
|
|||||||
1F198;SOS button
|
1F198;SOS button
|
||||||
1F199;UP! button
|
1F199;UP! button
|
||||||
1F19A;VS button
|
1F19A;VS button
|
||||||
1F201;Japanese “here” button
|
1F201;Japanese here button
|
||||||
1F202;Japanese “service charge” button
|
1F202;Japanese service charge button
|
||||||
1F237;Japanese “monthly amount” button
|
1F237;Japanese monthly amount button
|
||||||
1F236;Japanese “not free of charge” button
|
1F236;Japanese not free of charge button
|
||||||
1F22F;Japanese “reserved” button
|
1F22F;Japanese reserved button
|
||||||
1F250;Japanese “bargain” button
|
1F250;Japanese bargain button
|
||||||
1F239;Japanese “discount” button
|
1F239;Japanese discount button
|
||||||
1F21A;Japanese “free of charge” button
|
1F21A;Japanese free of charge button
|
||||||
1F232;Japanese “prohibited” button
|
1F232;Japanese prohibited button
|
||||||
1F251;Japanese “acceptable” button
|
1F251;Japanese acceptable button
|
||||||
1F238;Japanese “application” button
|
1F238;Japanese application button
|
||||||
1F234;Japanese “passing grade” button
|
1F234;Japanese passing grade button
|
||||||
1F233;Japanese “vacancy” button
|
1F233;Japanese vacancy button
|
||||||
3297;Japanese “congratulations” button
|
3297;Japanese congratulations button
|
||||||
3299;Japanese “secret” button
|
3299;Japanese secret button
|
||||||
1F23A;Japanese “open for business” button
|
1F23A;Japanese open for business button
|
||||||
1F235;Japanese “no vacancy” button
|
1F235;Japanese no vacancy button
|
||||||
25AA;black small square
|
25AA;black small square
|
||||||
25AB;white small square
|
25AB;white small square
|
||||||
25FB;white medium square
|
25FB;white medium square
|
||||||
@@ -1349,7 +1440,7 @@
|
|||||||
1F1E6 1F1E9;Andorra
|
1F1E6 1F1E9;Andorra
|
||||||
1F1E6 1F1EA;United Arab Emirates
|
1F1E6 1F1EA;United Arab Emirates
|
||||||
1F1E6 1F1EB;Afghanistan
|
1F1E6 1F1EB;Afghanistan
|
||||||
1F1E6 1F1EC;Antigua & Barbuda
|
1F1E6 1F1EC;Antigua Barbuda
|
||||||
1F1E6 1F1EE;Anguilla
|
1F1E6 1F1EE;Anguilla
|
||||||
1F1E6 1F1F1;Albania
|
1F1E6 1F1F1;Albania
|
||||||
1F1E6 1F1F2;Armenia
|
1F1E6 1F1F2;Armenia
|
||||||
@@ -1362,7 +1453,7 @@
|
|||||||
1F1E6 1F1FC;Aruba
|
1F1E6 1F1FC;Aruba
|
||||||
1F1E6 1F1FD;Åland Islands
|
1F1E6 1F1FD;Åland Islands
|
||||||
1F1E6 1F1FF;Azerbaijan
|
1F1E6 1F1FF;Azerbaijan
|
||||||
1F1E7 1F1E6;Bosnia & Herzegovina
|
1F1E7 1F1E6;Bosnia Herzegovina
|
||||||
1F1E7 1F1E7;Barbados
|
1F1E7 1F1E7;Barbados
|
||||||
1F1E7 1F1E9;Bangladesh
|
1F1E7 1F1E9;Bangladesh
|
||||||
1F1E7 1F1EA;Belgium
|
1F1E7 1F1EA;Belgium
|
||||||
@@ -1402,7 +1493,7 @@
|
|||||||
1F1E8 1F1FC;Curaçao
|
1F1E8 1F1FC;Curaçao
|
||||||
1F1E8 1F1FD;Christmas Island
|
1F1E8 1F1FD;Christmas Island
|
||||||
1F1E8 1F1FE;Cyprus
|
1F1E8 1F1FE;Cyprus
|
||||||
1F1E8 1F1FF;Czech Republic
|
1F1E8 1F1FF;Czechia
|
||||||
1F1E9 1F1EA;Germany
|
1F1E9 1F1EA;Germany
|
||||||
1F1E9 1F1EC;Diego Garcia
|
1F1E9 1F1EC;Diego Garcia
|
||||||
1F1E9 1F1EF;Djibouti
|
1F1E9 1F1EF;Djibouti
|
||||||
@@ -1410,7 +1501,7 @@
|
|||||||
1F1E9 1F1F2;Dominica
|
1F1E9 1F1F2;Dominica
|
||||||
1F1E9 1F1F4;Dominican Republic
|
1F1E9 1F1F4;Dominican Republic
|
||||||
1F1E9 1F1FF;Algeria
|
1F1E9 1F1FF;Algeria
|
||||||
1F1EA 1F1E6;Ceuta & Melilla
|
1F1EA 1F1E6;Ceuta Melilla
|
||||||
1F1EA 1F1E8;Ecuador
|
1F1EA 1F1E8;Ecuador
|
||||||
1F1EA 1F1EA;Estonia
|
1F1EA 1F1EA;Estonia
|
||||||
1F1EA 1F1EC;Egypt
|
1F1EA 1F1EC;Egypt
|
||||||
@@ -1439,13 +1530,13 @@
|
|||||||
1F1EC 1F1F5;Guadeloupe
|
1F1EC 1F1F5;Guadeloupe
|
||||||
1F1EC 1F1F6;Equatorial Guinea
|
1F1EC 1F1F6;Equatorial Guinea
|
||||||
1F1EC 1F1F7;Greece
|
1F1EC 1F1F7;Greece
|
||||||
1F1EC 1F1F8;South Georgia & South Sandwich Islands
|
1F1EC 1F1F8;South Georgia South Sandwich Islands
|
||||||
1F1EC 1F1F9;Guatemala
|
1F1EC 1F1F9;Guatemala
|
||||||
1F1EC 1F1FA;Guam
|
1F1EC 1F1FA;Guam
|
||||||
1F1EC 1F1FC;Guinea-Bissau
|
1F1EC 1F1FC;Guinea-Bissau
|
||||||
1F1EC 1F1FE;Guyana
|
1F1EC 1F1FE;Guyana
|
||||||
1F1ED 1F1F0;Hong Kong SAR China
|
1F1ED 1F1F0;Hong Kong SAR China
|
||||||
1F1ED 1F1F2;Heard & McDonald Islands
|
1F1ED 1F1F2;Heard McDonald Islands
|
||||||
1F1ED 1F1F3;Honduras
|
1F1ED 1F1F3;Honduras
|
||||||
1F1ED 1F1F7;Croatia
|
1F1ED 1F1F7;Croatia
|
||||||
1F1ED 1F1F9;Haiti
|
1F1ED 1F1F9;Haiti
|
||||||
@@ -1470,7 +1561,7 @@
|
|||||||
1F1F0 1F1ED;Cambodia
|
1F1F0 1F1ED;Cambodia
|
||||||
1F1F0 1F1EE;Kiribati
|
1F1F0 1F1EE;Kiribati
|
||||||
1F1F0 1F1F2;Comoros
|
1F1F0 1F1F2;Comoros
|
||||||
1F1F0 1F1F3;St. Kitts & Nevis
|
1F1F0 1F1F3;St. Kitts Nevis
|
||||||
1F1F0 1F1F5;North Korea
|
1F1F0 1F1F5;North Korea
|
||||||
1F1F0 1F1F7;South Korea
|
1F1F0 1F1F7;South Korea
|
||||||
1F1F0 1F1FC;Kuwait
|
1F1F0 1F1FC;Kuwait
|
||||||
@@ -1530,7 +1621,7 @@
|
|||||||
1F1F5 1F1ED;Philippines
|
1F1F5 1F1ED;Philippines
|
||||||
1F1F5 1F1F0;Pakistan
|
1F1F5 1F1F0;Pakistan
|
||||||
1F1F5 1F1F1;Poland
|
1F1F5 1F1F1;Poland
|
||||||
1F1F5 1F1F2;St. Pierre & Miquelon
|
1F1F5 1F1F2;St. Pierre Miquelon
|
||||||
1F1F5 1F1F3;Pitcairn Islands
|
1F1F5 1F1F3;Pitcairn Islands
|
||||||
1F1F5 1F1F7;Puerto Rico
|
1F1F5 1F1F7;Puerto Rico
|
||||||
1F1F5 1F1F8;Palestinian Territories
|
1F1F5 1F1F8;Palestinian Territories
|
||||||
@@ -1551,7 +1642,7 @@
|
|||||||
1F1F8 1F1EC;Singapore
|
1F1F8 1F1EC;Singapore
|
||||||
1F1F8 1F1ED;St. Helena
|
1F1F8 1F1ED;St. Helena
|
||||||
1F1F8 1F1EE;Slovenia
|
1F1F8 1F1EE;Slovenia
|
||||||
1F1F8 1F1EF;Svalbard & Jan Mayen
|
1F1F8 1F1EF;Svalbard Jan Mayen
|
||||||
1F1F8 1F1F0;Slovakia
|
1F1F8 1F1F0;Slovakia
|
||||||
1F1F8 1F1F1;Sierra Leone
|
1F1F8 1F1F1;Sierra Leone
|
||||||
1F1F8 1F1F2;San Marino
|
1F1F8 1F1F2;San Marino
|
||||||
@@ -1559,13 +1650,13 @@
|
|||||||
1F1F8 1F1F4;Somalia
|
1F1F8 1F1F4;Somalia
|
||||||
1F1F8 1F1F7;Suriname
|
1F1F8 1F1F7;Suriname
|
||||||
1F1F8 1F1F8;South Sudan
|
1F1F8 1F1F8;South Sudan
|
||||||
1F1F8 1F1F9;São Tomé & Príncipe
|
1F1F8 1F1F9;São Tomé Príncipe
|
||||||
1F1F8 1F1FB;El Salvador
|
1F1F8 1F1FB;El Salvador
|
||||||
1F1F8 1F1FD;Sint Maarten
|
1F1F8 1F1FD;Sint Maarten
|
||||||
1F1F8 1F1FE;Syria
|
1F1F8 1F1FE;Syria
|
||||||
1F1F8 1F1FF;Swaziland
|
1F1F8 1F1FF;Swaziland
|
||||||
1F1F9 1F1E6;Tristan da Cunha
|
1F1F9 1F1E6;Tristan da Cunha
|
||||||
1F1F9 1F1E8;Turks & Caicos Islands
|
1F1F9 1F1E8;Turks Caicos Islands
|
||||||
1F1F9 1F1E9;Chad
|
1F1F9 1F1E9;Chad
|
||||||
1F1F9 1F1EB;French Southern Territories
|
1F1F9 1F1EB;French Southern Territories
|
||||||
1F1F9 1F1EC;Togo
|
1F1F9 1F1EC;Togo
|
||||||
@@ -1577,7 +1668,7 @@
|
|||||||
1F1F9 1F1F3;Tunisia
|
1F1F9 1F1F3;Tunisia
|
||||||
1F1F9 1F1F4;Tonga
|
1F1F9 1F1F4;Tonga
|
||||||
1F1F9 1F1F7;Turkey
|
1F1F9 1F1F7;Turkey
|
||||||
1F1F9 1F1F9;Trinidad & Tobago
|
1F1F9 1F1F9;Trinidad Tobago
|
||||||
1F1F9 1F1FB;Tuvalu
|
1F1F9 1F1FB;Tuvalu
|
||||||
1F1F9 1F1FC;Taiwan
|
1F1F9 1F1FC;Taiwan
|
||||||
1F1F9 1F1FF;Tanzania
|
1F1F9 1F1FF;Tanzania
|
||||||
@@ -1589,13 +1680,13 @@
|
|||||||
1F1FA 1F1FE;Uruguay
|
1F1FA 1F1FE;Uruguay
|
||||||
1F1FA 1F1FF;Uzbekistan
|
1F1FA 1F1FF;Uzbekistan
|
||||||
1F1FB 1F1E6;Vatican City
|
1F1FB 1F1E6;Vatican City
|
||||||
1F1FB 1F1E8;St. Vincent & Grenadines
|
1F1FB 1F1E8;St. Vincent Grenadines
|
||||||
1F1FB 1F1EA;Venezuela
|
1F1FB 1F1EA;Venezuela
|
||||||
1F1FB 1F1EC;British Virgin Islands
|
1F1FB 1F1EC;British Virgin Islands
|
||||||
1F1FB 1F1EE;U.S. Virgin Islands
|
1F1FB 1F1EE;U.S. Virgin Islands
|
||||||
1F1FB 1F1F3;Vietnam
|
1F1FB 1F1F3;Vietnam
|
||||||
1F1FB 1F1FA;Vanuatu
|
1F1FB 1F1FA;Vanuatu
|
||||||
1F1FC 1F1EB;Wallis & Futuna
|
1F1FC 1F1EB;Wallis Futuna
|
||||||
1F1FC 1F1F8;Samoa
|
1F1FC 1F1F8;Samoa
|
||||||
1F1FD 1F1F0;Kosovo
|
1F1FD 1F1F0;Kosovo
|
||||||
1F1FE 1F1EA;Yemen
|
1F1FE 1F1EA;Yemen
|
||||||
@@ -1603,3 +1694,6 @@
|
|||||||
1F1FF 1F1E6;South Africa
|
1F1FF 1F1E6;South Africa
|
||||||
1F1FF 1F1F2;Zambia
|
1F1FF 1F1F2;Zambia
|
||||||
1F1FF 1F1FC;Zimbabwe
|
1F1FF 1F1FC;Zimbabwe
|
||||||
|
1F3F4 E0067 E0062 E0065 E006E E0067 E007F;England
|
||||||
|
1F3F4 E0067 E0062 E0073 E0063 E0074 E007F;Scotland
|
||||||
|
1F3F4 E0067 E0062 E0077 E006C E0073 E007F;Wales
|
||||||
@@ -6,9 +6,7 @@ enabled(){
|
|||||||
this.lastSelectedAccount = null;
|
this.lastSelectedAccount = null;
|
||||||
|
|
||||||
this.uiComposeTweetEvent = (e, data) => {
|
this.uiComposeTweetEvent = (e, data) => {
|
||||||
if (data.type !== "reply" || data.popFromInline || !("element" in data)){
|
return if data.type !== "reply" || data.popFromInline || !("element" in data);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var query;
|
var query;
|
||||||
|
|
||||||
@@ -83,9 +81,7 @@ enabled(){
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case "#last":
|
case "#last":
|
||||||
if (this.lastSelectedAccount === null){
|
return if this.lastSelectedAccount === null;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
identifier = this.lastSelectedAccount;
|
identifier = this.lastSelectedAccount;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ Templates
|
|||||||
chylex
|
chylex
|
||||||
|
|
||||||
[version]
|
[version]
|
||||||
1.0
|
1.0.2
|
||||||
|
|
||||||
[website]
|
[website]
|
||||||
https://tweetduck.chylex.com
|
https://tweetduck.chylex.com
|
||||||
|
|||||||
@@ -53,8 +53,10 @@ enabled(){
|
|||||||
this.css.insert(".templates-modal-wrap { width: 100%; height: 100%; padding: 49px; position: absolute; z-index: 999; box-sizing: border-box; background-color: rgba(0, 0, 0, 0.5); }");
|
this.css.insert(".templates-modal-wrap { width: 100%; height: 100%; padding: 49px; position: absolute; z-index: 999; box-sizing: border-box; background-color: rgba(0, 0, 0, 0.5); }");
|
||||||
this.css.insert(".templates-modal { width: 100%; height: 100%; background-color: #fff; display: flex; }");
|
this.css.insert(".templates-modal { width: 100%; height: 100%; background-color: #fff; display: flex; }");
|
||||||
this.css.insert(".templates-modal > div { display: flex; flex-direction: column; }");
|
this.css.insert(".templates-modal > div { display: flex; flex-direction: column; }");
|
||||||
this.css.insert(".templates-modal-bottom { flex: 0 0 auto; padding: 16px; text-align: right; }");
|
|
||||||
this.css.insert(".templates-modal-bottom button { margin-left: 4px; }");
|
this.css.insert(".templates-modal-bottom { flex: 0 0 auto; padding: 16px; }");
|
||||||
|
this.css.insert(".template-list .templates-modal-bottom { display: flex; justify-content: space-between; }");
|
||||||
|
this.css.insert(".template-editor .templates-modal-bottom { text-align: right; }");
|
||||||
|
|
||||||
this.css.insert(".template-list { height: 100%; flex: 1 1 auto; }");
|
this.css.insert(".template-list { height: 100%; flex: 1 1 auto; }");
|
||||||
this.css.insert(".template-list ul { list-style-type: none; font-size: 24px; color: #222; flex: 1 1 auto; padding: 12px; overflow-y: auto; }");
|
this.css.insert(".template-list ul { list-style-type: none; font-size: 24px; color: #222; flex: 1 1 auto; padding: 12px; overflow-y: auto; }");
|
||||||
@@ -175,7 +177,7 @@ enabled(){
|
|||||||
|
|
||||||
var useTemplate = (contents, append) => {
|
var useTemplate = (contents, append) => {
|
||||||
let ele = $(".js-compose-text");
|
let ele = $(".js-compose-text");
|
||||||
if (ele.length === 0)return;
|
return if ele.length === 0;
|
||||||
|
|
||||||
let value = append ? ele.val()+contents : contents;
|
let value = append ? ele.val()+contents : contents;
|
||||||
let prevLength = value.length;
|
let prevLength = value.length;
|
||||||
@@ -265,6 +267,7 @@ enabled(){
|
|||||||
<ul></ul>
|
<ul></ul>
|
||||||
|
|
||||||
<div class="templates-modal-bottom">
|
<div class="templates-modal-bottom">
|
||||||
|
<button data-action="close" class="btn"><i class="icon icon-close icon-small padding-rs"></i><span class="label">Close</span></button>
|
||||||
<button data-action="new-template" class="btn btn-positive"><i class="icon icon-plus icon-small padding-rs"></i><span class="label">New Template</span></button>
|
<button data-action="new-template" class="btn btn-positive"><i class="icon icon-plus icon-small padding-rs"></i><span class="label">New Template</span></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -298,7 +301,7 @@ enabled(){
|
|||||||
|
|
||||||
<div class="templates-modal-bottom">
|
<div class="templates-modal-bottom">
|
||||||
<button data-action="editor-cancel" class="btn"><i class="icon icon-close icon-small padding-rs"></i><span class="label">Cancel</span></button>
|
<button data-action="editor-cancel" class="btn"><i class="icon icon-close icon-small padding-rs"></i><span class="label">Cancel</span></button>
|
||||||
<button data-action="editor-confirm" class="btn btn-positive"><i class="icon icon-check icon-small padding-rs"></i><span class="label">Confirm</span></button>
|
<button data-action="editor-confirm" class="btn btn-positive" style="margin-left:4px"><i class="icon icon-check icon-small padding-rs"></i><span class="label">Confirm</span></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -392,6 +395,10 @@ enabled(){
|
|||||||
toggleEditor();
|
toggleEditor();
|
||||||
onTemplatesUpdated(true);
|
onTemplatesUpdated(true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "close":
|
||||||
|
hideTemplateModal();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$(this).blur();
|
$(this).blur();
|
||||||
|
|||||||
23
Resources/PostBuild.ps1
Normal file
23
Resources/PostBuild.ps1
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
Param([Parameter(Mandatory = $True, Position = 1)][string] $dir)
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
Set-Location $dir
|
||||||
|
|
||||||
|
function Rewrite-File{
|
||||||
|
[CmdletBinding()]
|
||||||
|
Param([Parameter(Mandatory = $True, ValueFromPipeline = $True)][array] $lines, [Parameter(Mandatory = $True, Position = 1)] $file)
|
||||||
|
|
||||||
|
$lines | Where { $_ -ne '' } | Set-Content -Path $file.FullName
|
||||||
|
Write-Host "Processed" $file.FullName.Substring($dir.Length)
|
||||||
|
}
|
||||||
|
|
||||||
|
ForEach($file in Get-ChildItem -Include *.js -Recurse){
|
||||||
|
$lines = Get-Content -Path $file.FullName
|
||||||
|
$lines = ($lines | % { $_.TrimStart() }) -Replace '^(.*?)((?<=^|[;{}()])\s?//(?:\s.*|$))?$', '$1' -Replace '(?<!\w)return(\s.*?)? if (.*?);', 'if ($2)return$1;'
|
||||||
|
,$lines | Rewrite-File $file
|
||||||
|
}
|
||||||
|
|
||||||
|
ForEach($file in Get-ChildItem -Include *.html -Recurse){
|
||||||
|
$lines = Get-Content -Path $file.FullName
|
||||||
|
,($lines | % { $_.TrimStart() }) | Rewrite-File $file
|
||||||
|
}
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
using CefSharp;
|
using CefSharp;
|
||||||
using CefSharp.WinForms;
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Windows.Forms;
|
using TweetDuck.Core.Other;
|
||||||
|
|
||||||
namespace TweetDuck.Resources{
|
namespace TweetDuck.Resources{
|
||||||
static class ScriptLoader{
|
static class ScriptLoader{
|
||||||
@@ -14,35 +13,23 @@ namespace TweetDuck.Resources{
|
|||||||
return File.ReadAllText(Path.Combine(Program.ScriptPath, name), Encoding.UTF8);
|
return File.ReadAllText(Path.Combine(Program.ScriptPath, name), Encoding.UTF8);
|
||||||
}catch(Exception ex){
|
}catch(Exception ex){
|
||||||
if (!silent){
|
if (!silent){
|
||||||
MessageBox.Show("Unfortunately, "+Program.BrandName+" could not load the "+name+" file. The program will continue running with limited functionality.\r\n\r\n"+ex.Message, Program.BrandName+" Has Failed :(", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
FormMessage.Error("TweetDuck Has Failed :(", "Unfortunately, TweetDuck could not load the "+name+" file. The program will continue running with limited functionality.\n\n"+ex.Message, FormMessage.OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ExecuteFile(ChromiumWebBrowser browser, string file){
|
|
||||||
ExecuteScript(browser, LoadResource(file), GetRootIdentifier(file));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ExecuteFile(IFrame frame, string file){
|
public static void ExecuteFile(IFrame frame, string file){
|
||||||
ExecuteScript(frame, LoadResource(file), GetRootIdentifier(file));
|
ExecuteScript(frame, LoadResource(file), GetRootIdentifier(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ExecuteScript(ChromiumWebBrowser browser, string script, string identifier){
|
public static void ExecuteScript(IFrame frame, string script, string identifier){
|
||||||
if (script == null)return;
|
if (script != null){
|
||||||
|
|
||||||
using(IFrame frame = browser.GetMainFrame()){
|
|
||||||
frame.ExecuteJavaScriptAsync(script, UrlPrefix+identifier, 1);
|
frame.ExecuteJavaScriptAsync(script, UrlPrefix+identifier, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ExecuteScript(IFrame frame, string script, string identifier){
|
|
||||||
if (script == null)return;
|
|
||||||
|
|
||||||
frame.ExecuteJavaScriptAsync(script, UrlPrefix+identifier, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetRootIdentifier(string file){
|
public static string GetRootIdentifier(string file){
|
||||||
return "root:"+Path.GetFileNameWithoutExtension(file);
|
return "root:"+Path.GetFileNameWithoutExtension(file);
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user