mirror of
https://github.com/chylex/TweetDuck.git
synced 2025-09-14 10:32:10 +02:00
Compare commits
385 Commits
Author | SHA1 | Date | |
---|---|---|---|
1f27d96ac9 | |||
93e9f28d69 | |||
ec2e26752a | |||
fadd95f3e6 | |||
00acc677e6 | |||
1a799881e8 | |||
f75677593a | |||
19e3bd19f0 | |||
85701b0a3c | |||
014cb18dcb | |||
e71e1c853f | |||
ee9d9196f5 | |||
53c8272e01 | |||
7f7b6b1e2a | |||
405777e0f5 | |||
df2b624cb5 | |||
8a48d5c2f9 | |||
c55ee71442 | |||
3f82745f5b | |||
404187a1ae | |||
2b7b3f586b | |||
04959a3493 | |||
97cf4932ae | |||
b0d88a0a37 | |||
67a2e40622 | |||
3a28556c7f | |||
9ecc92b9a5 | |||
ca023be98a | |||
11a1423f76 | |||
79f6df121b | |||
71eade7e86 | |||
5f81d29036 | |||
ec1cb5dc5f | |||
fd969e2d55 | |||
37e33b77ff | |||
f7ed7703b4 | |||
4bb35295ca | |||
1e4f673f9e | |||
7cadb1c403 | |||
37148f5093 | |||
f6bc26789f | |||
b3f5a88525 | |||
1e538d2b28 | |||
7d7bfb7b01 | |||
41d86ba440 | |||
3df474a8a5 | |||
a50d6e8f47 | |||
6081e5b9c1 | |||
66ccea920c | |||
470d63093f | |||
eae0507831 | |||
92af85d3bb | |||
7635af5730 | |||
a838e89695 | |||
b22289a8b9 | |||
45b3ff52c6 | |||
4464991f4c | |||
b0d2f77583 | |||
b211a4405d | |||
8823016d2c | |||
859fdc7ec1 | |||
028d5ed01f | |||
5fd5a2a436 | |||
79a7e7470c | |||
9ecef78aed | |||
65a837a6e1 | |||
6e4db4acea | |||
26fb977d05 | |||
b42cd1c048 | |||
467f7cd12f | |||
66699ce9df | |||
cf7d903932 | |||
a7ab67925c | |||
a474ba4260 | |||
09e5636e86 | |||
2295a875be | |||
82a2455afc | |||
268de676ee | |||
8fe26c07f1 | |||
da3921b1ca | |||
4dd2e787d1 | |||
ce005ae6c2 | |||
1513f46a11 | |||
7543eeb0f4 | |||
873242120c | |||
98f8095a65 | |||
785571a550 | |||
0c4bd4044e | |||
0319543dce | |||
82d70b2d7f | |||
62d18e010a | |||
fc77b85083 | |||
50a8893f4f | |||
9252b3040e | |||
d5141ed020 | |||
7ff9e23283 | |||
89854d527c | |||
6ff0cad2a8 | |||
349cfbd2d5 | |||
40303ef74a | |||
6c652122c2 | |||
3658e3a2aa | |||
2b20fcfcd1 | |||
554d427fef | |||
7cf5b23306 | |||
b26a6098eb | |||
7ad927bdaf | |||
4ed30b3619 | |||
edfa9264d5 | |||
f7516b593f | |||
83ff998f9d | |||
47381e0df4 | |||
ba62d57485 | |||
c014c4bc24 | |||
5d1a3fede2 | |||
53b584fe45 | |||
f53d974400 | |||
c4b4ef19cd | |||
3bfc360362 | |||
584f16d375 | |||
b3d84c3217 | |||
dd14ad470e | |||
85b90574b8 | |||
ee5d172468 | |||
7ca4b94361 | |||
31f1546483 | |||
d8a88a19af | |||
12af79de05 | |||
2260dd419d | |||
61a940cc82 | |||
1bbc1e0d7e | |||
921294eeb3 | |||
baaa90f49d | |||
4e25381770 | |||
272877d0ed | |||
555b947bf7 | |||
da29811b16 | |||
241f67fd4d | |||
eb4ce18e31 | |||
ae99fee440 | |||
d116ac5e56 | |||
28db1f4253 | |||
034312e676 | |||
da83d73ba6 | |||
02e8dc3440 | |||
cac6d1f889 | |||
68fa3294d4 | |||
9b983de8c9 | |||
3a37ee719b | |||
61359c2faa | |||
7f7c5ab35b | |||
a1b483d20a | |||
04cd662d78 | |||
da597f076f | |||
fab3efdcf5 | |||
a55509a34d | |||
84fb1c5b2b | |||
391a90e1df | |||
e0fe39195d | |||
385fead81a | |||
648d1b9aa9 | |||
3f0028913d | |||
45e6ec8b0f | |||
a3fbaa0b34 | |||
7102cbfb3b | |||
c3db3ce0f2 | |||
7a1e7637ff | |||
04a78a02d3 | |||
cb61dc742f | |||
cd53f6e757 | |||
c64f7daa8d | |||
e70d792654 | |||
9ae533f907 | |||
cfe92f18e3 | |||
e2a34ea28e | |||
ec8000360e | |||
57b0821e19 | |||
09d39df15a | |||
1f9db3bda6 | |||
b7104c8828 | |||
5da02b4092 | |||
802f1e3042 | |||
66db0df45a | |||
650c2e2eb7 | |||
6ab3754129 | |||
7dca0a8cab | |||
7cd0b4ad54 | |||
97acb41eee | |||
b916b9726e | |||
32d3990ace | |||
cb1fd109cc | |||
0e68dd6185 | |||
fb6502bc65 | |||
c7e7403781 | |||
bf224408a3 | |||
84b7078873 | |||
89e5943d8f | |||
b78c4cb8f0 | |||
976ba074a8 | |||
5205d59b96 | |||
e8394b9c08 | |||
9cd00239f9 | |||
b6b26142f8 | |||
4ee99376fd | |||
b0261342ff | |||
87fd2a521e | |||
334793c6f6 | |||
3e70d991bb | |||
feec96fc5c | |||
765984709e | |||
c7c9931f68 | |||
ae64573510 | |||
d675af5aa4 | |||
9480d17cfc | |||
5ac1df2283 | |||
20119db883 | |||
a4006deb8c | |||
25fa3cefab | |||
bb5161eb34 | |||
1bfc403a98 | |||
720d10e543 | |||
30c117672e | |||
6d1b5c77d1 | |||
d1cbf608e0 | |||
7e3014c52d | |||
82beb1f5a7 | |||
657dc81300 | |||
8e22192dd3 | |||
dc0b7d58e3 | |||
6919e5bdb0 | |||
9728a62efa | |||
276e070759 | |||
fadea54f8d | |||
523d340ade | |||
96fa7efb66 | |||
03591f8317 | |||
28cc60d007 | |||
1fa69bdb3b | |||
11f5f9b72e | |||
61f6543041 | |||
0c9ab32f37 | |||
fde984d02b | |||
f23db31306 | |||
8dce99b8b3 | |||
342ac51cda | |||
ba31f7ae01 | |||
8d0fa030f8 | |||
d030a79c81 | |||
6690efc4d9 | |||
afa8098463 | |||
c064e579d2 | |||
01dc4e4714 | |||
6fbc246b12 | |||
1efe2a02f7 | |||
ab2ab06f60 | |||
a71d889682 | |||
2252d85b27 | |||
3f09100177 | |||
6b79c89f42 | |||
5f249d4603 | |||
fa64309909 | |||
c46aafdab6 | |||
3e9c397494 | |||
47935165db | |||
be77f753e7 | |||
2c30613279 | |||
d83eaec987 | |||
e6f199a224 | |||
6636a2aa9e | |||
221bdc58fe | |||
e48a068e9d | |||
3371c31e63 | |||
aa2c60f7e9 | |||
f7dc200684 | |||
ad306c56c7 | |||
86aba1eb52 | |||
826f1aba7a | |||
8abf4364c6 | |||
4c45d91d4e | |||
3176b6cb8f | |||
27971e09cd | |||
bf95ae00de | |||
0dbfa7e101 | |||
85d09c4b5e | |||
4f9bc40476 | |||
757ccbf9d3 | |||
4cf9730130 | |||
1dd0d70ab9 | |||
340eaece0f | |||
c151e7cd37 | |||
ca0baae832 | |||
478e6ed8df | |||
7388eb07ca | |||
c38d507e50 | |||
5aee087a57 | |||
0cda3702ea | |||
ce55226d0c | |||
0bab0a9963 | |||
34955b7083 | |||
ba6ce072ac | |||
f39fd00697 | |||
58fc1be1d5 | |||
1fdf9bffb6 | |||
2ad179ef8e | |||
d42cc5b762 | |||
403658f622 | |||
6ca35685db | |||
bfc6822f69 | |||
b0386937d7 | |||
9436540b2f | |||
45b0ece342 | |||
5c147d3648 | |||
8fa68c428f | |||
e2be90191e | |||
339da2f1a9 | |||
b2cc5d50bd | |||
f38b188046 | |||
9e45628e87 | |||
bf76398627 | |||
81d5728964 | |||
d76027558b | |||
1450cc24a3 | |||
f41523c1b2 | |||
4019463e68 | |||
5e2e239f5e | |||
bc7856b6c0 | |||
e2bba8d9e1 | |||
520db2c32e | |||
da71f2de2b | |||
6dd2c6678b | |||
fb3d9e6d6b | |||
36473c2df9 | |||
81aa30b2ec | |||
85d5160782 | |||
44bf7b870e | |||
44da2e6082 | |||
d576bc3972 | |||
dbeb4c7205 | |||
e5223a852e | |||
f3884315c0 | |||
53cd9dc9a6 | |||
a3666a7ab2 | |||
ca4eb17308 | |||
b729dca2e5 | |||
21354e675a | |||
2085ddd347 | |||
31a475861b | |||
45bdd95dc8 | |||
569fdec380 | |||
3f15ff1c06 | |||
506cd52255 | |||
300c0c6195 | |||
7689fe97b0 | |||
99ed077dbc | |||
65aeb6656f | |||
e4f5766ffb | |||
353ac7c76b | |||
8073fa206b | |||
1a1cfa2220 | |||
5e93d866ad | |||
f29e03e250 | |||
3775b5968d | |||
ba6242e09d | |||
f6b0ddddb9 | |||
9e8d5c6768 | |||
ef78496d5d | |||
41a45a14df | |||
5d3721ad04 | |||
f75bcb505c | |||
fa365794a0 | |||
a747ab700e | |||
b51b017005 | |||
b9e1dd5950 | |||
56bf33229b | |||
5740dd8c8a | |||
d3f205287c | |||
53518cd6e6 | |||
0ff3896d51 | |||
c64e16471b | |||
2f2e2b82b8 | |||
af9a503f3c | |||
971affa607 | |||
2de5d6206e | |||
34e0bcc56a | |||
bb9f09c11f |
6
.gitignore
vendored
6
.gitignore
vendored
@@ -17,9 +17,13 @@
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
bld/*
|
||||
!bld/gen_full.iss
|
||||
!bld/gen_port.iss
|
||||
!bld/gen_upd.iss
|
||||
!bld/Resources
|
||||
|
||||
# Visual Studio 2015 cache/options directory
|
||||
.vs/
|
||||
|
@@ -1,10 +1,15 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using TweetDck.Core.Utils;
|
||||
|
||||
namespace TweetDck.Configuration{
|
||||
class LockManager{
|
||||
sealed class LockManager{
|
||||
public enum Result{
|
||||
Success, HasProcess, Fail
|
||||
}
|
||||
|
||||
public Process LockingProcess { get; private set; }
|
||||
|
||||
private readonly string file;
|
||||
@@ -14,111 +19,158 @@ namespace TweetDck.Configuration{
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
private bool CreateLockFile(){
|
||||
private void CreateLockFileStream(){
|
||||
lockStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read);
|
||||
WriteIntToStream(lockStream, GetCurrentProcessId());
|
||||
lockStream.Flush(true);
|
||||
}
|
||||
|
||||
private bool ReleaseLockFileStream(){
|
||||
if (lockStream != null){
|
||||
lockStream.Dispose();
|
||||
lockStream = null;
|
||||
return true;
|
||||
}
|
||||
else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private Result TryCreateLockFile(){
|
||||
if (lockStream != null){
|
||||
throw new InvalidOperationException("Lock file already exists.");
|
||||
}
|
||||
|
||||
try{
|
||||
lockStream = new FileStream(file,FileMode.Create,FileAccess.Write,FileShare.Read);
|
||||
|
||||
byte[] id = BitConverter.GetBytes(Process.GetCurrentProcess().Id);
|
||||
lockStream.Write(id,0,id.Length);
|
||||
lockStream.Flush();
|
||||
|
||||
if (LockingProcess != null){
|
||||
LockingProcess.Close();
|
||||
LockingProcess = null;
|
||||
CreateLockFileStream();
|
||||
return Result.Success;
|
||||
}catch(DirectoryNotFoundException){
|
||||
try{
|
||||
CreateLockFileStream();
|
||||
return Result.Success;
|
||||
}catch{
|
||||
ReleaseLockFileStream();
|
||||
return Result.Fail;
|
||||
}
|
||||
|
||||
return true;
|
||||
}catch(Exception){
|
||||
if (lockStream != null){
|
||||
lockStream.Close();
|
||||
lockStream.Dispose();
|
||||
}
|
||||
|
||||
return false;
|
||||
}catch(IOException){
|
||||
return Result.HasProcess;
|
||||
}catch{
|
||||
ReleaseLockFileStream();
|
||||
return Result.Fail;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Lock(){
|
||||
if (lockStream != null)return true;
|
||||
|
||||
try{
|
||||
byte[] bytes = new byte[4];
|
||||
|
||||
using(FileStream fileStream = new FileStream(file,FileMode.Open,FileAccess.Read,FileShare.ReadWrite)){
|
||||
fileStream.Read(bytes,0,4);
|
||||
}
|
||||
|
||||
int pid = BitConverter.ToInt32(bytes,0);
|
||||
|
||||
try{
|
||||
Process foundProcess = Process.GetProcessById(pid);
|
||||
|
||||
using(Process currentProcess = Process.GetCurrentProcess()){
|
||||
if (foundProcess.ProcessName == currentProcess.ProcessName){
|
||||
LockingProcess = foundProcess;
|
||||
}
|
||||
}
|
||||
}catch(ArgumentException){}
|
||||
|
||||
return LockingProcess == null && CreateLockFile();
|
||||
}catch(DirectoryNotFoundException){
|
||||
string dir = Path.GetDirectoryName(file);
|
||||
|
||||
if (dir != null){
|
||||
Directory.CreateDirectory(dir);
|
||||
return CreateLockFile();
|
||||
}
|
||||
}catch(FileNotFoundException){
|
||||
return CreateLockFile();
|
||||
}catch(Exception){
|
||||
return false;
|
||||
public Result Lock(){
|
||||
if (lockStream != null){
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
return false;
|
||||
Result initialResult = TryCreateLockFile();
|
||||
|
||||
if (initialResult == Result.HasProcess){
|
||||
try{
|
||||
int pid;
|
||||
|
||||
using(FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)){
|
||||
pid = ReadIntFromStream(fileStream);
|
||||
}
|
||||
|
||||
try{
|
||||
Process foundProcess = Process.GetProcessById(pid);
|
||||
|
||||
using(Process currentProcess = Process.GetCurrentProcess()){
|
||||
if (foundProcess.MainModule.FileVersionInfo.InternalName == currentProcess.MainModule.FileVersionInfo.InternalName){
|
||||
LockingProcess = foundProcess;
|
||||
}
|
||||
else{
|
||||
foundProcess.Close();
|
||||
}
|
||||
}
|
||||
}catch{
|
||||
// GetProcessById throws ArgumentException if the process is missing
|
||||
// Process.MainModule can throw exceptions in some cases
|
||||
}
|
||||
|
||||
return LockingProcess == null ? Result.Fail : Result.HasProcess;
|
||||
}catch{
|
||||
return Result.Fail;
|
||||
}
|
||||
}
|
||||
|
||||
return initialResult;
|
||||
}
|
||||
|
||||
public bool Unlock(){
|
||||
bool result = true;
|
||||
|
||||
if (lockStream != null){
|
||||
lockStream.Dispose();
|
||||
|
||||
if (ReleaseLockFileStream()){
|
||||
try{
|
||||
File.Delete(file);
|
||||
}catch(Exception e){
|
||||
Program.Log(e.ToString());
|
||||
Program.Reporter.Log(e.ToString());
|
||||
result = false;
|
||||
}
|
||||
|
||||
lockStream = null;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public bool CloseLockingProcess(int timeout){
|
||||
public bool CloseLockingProcess(int closeTimeout, int killTimeout){
|
||||
if (LockingProcess != null){
|
||||
LockingProcess.CloseMainWindow();
|
||||
try{
|
||||
if (LockingProcess.CloseMainWindow()){
|
||||
WindowsUtils.TrySleepUntil(CheckLockingProcessExited, closeTimeout, 250);
|
||||
}
|
||||
|
||||
for(int waited = 0; waited < timeout && !LockingProcess.HasExited;){
|
||||
LockingProcess.Refresh();
|
||||
if (!LockingProcess.HasExited){
|
||||
LockingProcess.Kill();
|
||||
WindowsUtils.TrySleepUntil(CheckLockingProcessExited, killTimeout, 250);
|
||||
}
|
||||
|
||||
Thread.Sleep(100);
|
||||
waited += 100;
|
||||
}
|
||||
if (LockingProcess.HasExited){
|
||||
LockingProcess.Dispose();
|
||||
LockingProcess = null;
|
||||
return true;
|
||||
}
|
||||
}catch(Exception ex){
|
||||
if (ex is InvalidOperationException || ex is Win32Exception){
|
||||
if (LockingProcess != null){
|
||||
LockingProcess.Refresh();
|
||||
|
||||
if (LockingProcess.HasExited){
|
||||
LockingProcess.Dispose();
|
||||
LockingProcess = null;
|
||||
return true;
|
||||
bool hasExited = LockingProcess.HasExited;
|
||||
LockingProcess.Dispose();
|
||||
return hasExited;
|
||||
}
|
||||
}
|
||||
else throw;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool CheckLockingProcessExited(){
|
||||
LockingProcess.Refresh();
|
||||
return LockingProcess.HasExited;
|
||||
}
|
||||
|
||||
// Utility functions
|
||||
|
||||
private static void WriteIntToStream(Stream stream, int value){
|
||||
byte[] id = BitConverter.GetBytes(value);
|
||||
stream.Write(id, 0, id.Length);
|
||||
}
|
||||
|
||||
private static int ReadIntFromStream(Stream stream){
|
||||
byte[] bytes = new byte[4];
|
||||
stream.Read(bytes, 0, 4);
|
||||
return BitConverter.ToInt32(bytes, 0);
|
||||
}
|
||||
|
||||
private static int GetCurrentProcessId(){
|
||||
using(Process process = Process.GetCurrentProcess()){
|
||||
return process.Id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,47 +1,51 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using TweetDck.Core;
|
||||
using TweetDck.Core.Handling;
|
||||
using TweetDck.Core.Controls;
|
||||
using TweetDck.Core.Notification;
|
||||
using TweetDck.Core.Utils;
|
||||
using TweetDck.Plugins;
|
||||
|
||||
namespace TweetDck.Configuration{
|
||||
[Serializable]
|
||||
sealed class UserConfig{
|
||||
private static readonly IFormatter Formatter = new BinaryFormatter{
|
||||
Binder = new SerializationCompatibilityHandler()
|
||||
};
|
||||
private static readonly IFormatter Formatter = new BinaryFormatter{ Binder = new CustomBinder() };
|
||||
|
||||
private const int CurrentFileVersion = 3;
|
||||
private const int CurrentFileVersion = 6;
|
||||
|
||||
// START OF CONFIGURATION
|
||||
|
||||
public bool IgnoreMigration { get; set; }
|
||||
public bool IgnoreUninstallCheck { get; set; }
|
||||
|
||||
public WindowState BrowserWindow { get; set; }
|
||||
public bool DisplayNotificationTimer { get; set; }
|
||||
public bool NotificationTimerCountDown { get; set; }
|
||||
|
||||
public TweetNotification.Duration NotificationDuration { get; set; }
|
||||
public TweetNotification.Position NotificationPosition { get; set; }
|
||||
public Point CustomNotificationPosition { get; set; }
|
||||
public int NotificationEdgeDistance { get; set; }
|
||||
public int NotificationDisplay { get; set; }
|
||||
public int NotificationDurationValue { get; set; }
|
||||
|
||||
public bool EnableSpellCheck { get; set; }
|
||||
public bool ExpandLinksOnHover { get; set; }
|
||||
public bool ShowScreenshotBorder { get; set; }
|
||||
public bool EnableTrayHighlight { get; set; }
|
||||
|
||||
public bool EnableUpdateCheck { get; set; }
|
||||
public string DismissedUpdate { get; set; }
|
||||
public bool ExpandLinksOnHover { get; set; }
|
||||
|
||||
public PluginConfig Plugins { get; private set; }
|
||||
public WindowState PluginsWindow { get; set; }
|
||||
|
||||
public string CustomCefArgs { get; set; }
|
||||
public string CustomBrowserCSS { get; set; }
|
||||
public string CustomNotificationCSS { get; set; }
|
||||
|
||||
public bool IsCustomNotificationPositionSet{
|
||||
get{
|
||||
return CustomNotificationPosition.X != -32000 && CustomNotificationPosition.X != 32000;
|
||||
return CustomNotificationPosition != ControlExtensions.InvisibleLocation;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,11 +60,21 @@ namespace TweetDck.Configuration{
|
||||
muteNotifications = value;
|
||||
|
||||
if (MuteToggled != null){
|
||||
MuteToggled(this,new EventArgs());
|
||||
MuteToggled(this, new EventArgs());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string NotificationSoundPath{
|
||||
get{
|
||||
return string.IsNullOrEmpty(notificationSoundPath) ? string.Empty : notificationSoundPath;
|
||||
}
|
||||
|
||||
set{
|
||||
notificationSoundPath = value;
|
||||
}
|
||||
}
|
||||
|
||||
public TrayIcon.Behavior TrayBehavior{
|
||||
get{
|
||||
return trayBehavior;
|
||||
@@ -72,7 +86,7 @@ namespace TweetDck.Configuration{
|
||||
trayBehavior = value;
|
||||
|
||||
if (TrayBehaviorChanged != null){
|
||||
TrayBehaviorChanged(this,new EventArgs());
|
||||
TrayBehaviorChanged(this, new EventArgs());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,6 +104,7 @@ namespace TweetDck.Configuration{
|
||||
|
||||
private int fileVersion;
|
||||
private bool muteNotifications;
|
||||
private string notificationSoundPath;
|
||||
private TrayIcon.Behavior trayBehavior;
|
||||
|
||||
private UserConfig(string file){
|
||||
@@ -97,14 +112,19 @@ namespace TweetDck.Configuration{
|
||||
|
||||
BrowserWindow = new WindowState();
|
||||
DisplayNotificationTimer = true;
|
||||
NotificationDuration = TweetNotification.Duration.Medium;
|
||||
NotificationPosition = TweetNotification.Position.TopRight;
|
||||
CustomNotificationPosition = new Point(-32000,-32000);
|
||||
CustomNotificationPosition = ControlExtensions.InvisibleLocation;
|
||||
NotificationEdgeDistance = 8;
|
||||
NotificationDurationValue = 25;
|
||||
EnableUpdateCheck = true;
|
||||
ExpandLinksOnHover = true;
|
||||
ShowScreenshotBorder = true;
|
||||
EnableTrayHighlight = true;
|
||||
Plugins = new PluginConfig();
|
||||
PluginsWindow = new WindowState();
|
||||
|
||||
Plugins.DisableOfficialFromConfig("clear-columns");
|
||||
Plugins.DisableOfficialFromConfig("reply-account");
|
||||
}
|
||||
|
||||
private void UpgradeFile(){
|
||||
@@ -131,6 +151,23 @@ namespace TweetDck.Configuration{
|
||||
++fileVersion;
|
||||
}
|
||||
|
||||
if (fileVersion == 3){
|
||||
EnableTrayHighlight = true;
|
||||
NotificationDurationValue = 25;
|
||||
++fileVersion;
|
||||
}
|
||||
|
||||
if (fileVersion == 4){
|
||||
Plugins.DisableOfficialFromConfig("clear-columns");
|
||||
Plugins.DisableOfficialFromConfig("reply-account");
|
||||
++fileVersion;
|
||||
}
|
||||
|
||||
if (fileVersion == 5){
|
||||
ShowScreenshotBorder = true;
|
||||
++fileVersion;
|
||||
}
|
||||
|
||||
// update the version
|
||||
fileVersion = CurrentFileVersion;
|
||||
Save();
|
||||
@@ -146,26 +183,27 @@ namespace TweetDck.Configuration{
|
||||
if (File.Exists(file)){
|
||||
string backupFile = GetBackupFile(file);
|
||||
File.Delete(backupFile);
|
||||
File.Move(file,backupFile);
|
||||
File.Move(file, backupFile);
|
||||
}
|
||||
|
||||
using(Stream stream = new FileStream(file,FileMode.Create,FileAccess.Write,FileShare.None)){
|
||||
Formatter.Serialize(stream,this);
|
||||
using(Stream stream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None)){
|
||||
Formatter.Serialize(stream, this);
|
||||
}
|
||||
|
||||
return true;
|
||||
}catch(Exception e){
|
||||
Program.HandleException("Could not save the configuration file.",e);
|
||||
Program.Reporter.HandleException("Configuration Error", "Could not save the configuration file.", true, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static UserConfig Load(string file){
|
||||
UserConfig config = null;
|
||||
Exception firstException = null;
|
||||
|
||||
for(int attempt = 0; attempt < 2; attempt++){
|
||||
try{
|
||||
using(Stream stream = new FileStream(attempt == 0 ? file : GetBackupFile(file),FileMode.Open,FileAccess.Read,FileShare.Read)){
|
||||
using(Stream stream = new FileStream(attempt == 0 ? file : GetBackupFile(file), FileMode.Open, FileAccess.Read, FileShare.Read)){
|
||||
if ((config = Formatter.Deserialize(stream) as UserConfig) != null){
|
||||
config.file = file;
|
||||
}
|
||||
@@ -177,11 +215,23 @@ namespace TweetDck.Configuration{
|
||||
|
||||
break;
|
||||
}catch(FileNotFoundException){
|
||||
}catch(DirectoryNotFoundException){
|
||||
break;
|
||||
}catch(Exception e){
|
||||
Program.HandleException("Could not open the configuration file.",e);
|
||||
if (attempt == 0){
|
||||
firstException = e;
|
||||
Program.Reporter.Log(e.ToString());
|
||||
}
|
||||
else if (firstException != null){
|
||||
Program.Reporter.HandleException("Configuration Error", "Could not open the backup configuration file. If you continue, you may lose your settings and list of enabled plugins.", true, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (firstException != null && config == null){
|
||||
Program.Reporter.HandleException("Configuration Error", "Could not open the configuration file.", true, firstException);
|
||||
}
|
||||
|
||||
return config ?? new UserConfig(file);
|
||||
}
|
||||
|
||||
@@ -189,16 +239,13 @@ namespace TweetDck.Configuration{
|
||||
return file+".bak";
|
||||
}
|
||||
|
||||
private class SerializationCompatibilityHandler : SerializationBinder{
|
||||
private sealed class CustomBinder : SerializationBinder{
|
||||
public override Type BindToType(string assemblyName, string typeName){
|
||||
#if DUCK
|
||||
assemblyName = assemblyName.Replace("TweetDick","TweetDuck");
|
||||
#else
|
||||
assemblyName = assemblyName.Replace("TweetDuck","TweetDick");
|
||||
#endif
|
||||
if (typeName == "TweetDck.Core.Handling.TweetNotification+Position"){
|
||||
return typeof(TweetNotification.Position);
|
||||
}
|
||||
|
||||
typeName = typeName.Replace("TweetDick","TweetDck");
|
||||
return Type.GetType(string.Format(CultureInfo.CurrentCulture,"{0}, {1}",typeName,assemblyName));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,3 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0,Profile=Client"/></startup></configuration>
|
||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2"/></startup></configuration>
|
||||
|
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<package id="cef.redist.x64" version="3.2623.1396" targetFramework="net40-Client" />
|
||||
<package id="cef.redist.x86" version="3.2623.1396" targetFramework="net40-Client" />
|
||||
<package id="CefSharp.Common" version="49.0.0-pre02" targetFramework="net40-Client" />
|
||||
<package id="CefSharp.WinForms" version="49.0.0-pre02" targetFramework="net40-Client" />
|
||||
<package id="Microsoft.VC120.CRT.JetBrains" version="12.0.21005.2" targetFramework="net40-Client" />
|
||||
<package id="cef.redist.x64" version="3.2883.1552" targetFramework="net452" xmlns="" />
|
||||
<package id="cef.redist.x86" version="3.2883.1552" targetFramework="net452" xmlns="" />
|
||||
<package id="CefSharp.Common" version="55.0.0" targetFramework="net452" xmlns="" />
|
||||
<package id="CefSharp.WinForms" version="55.0.0" targetFramework="net452" xmlns="" />
|
||||
<package id="Microsoft.VC120.CRT.JetBrains" version="12.0.21005.2" targetFramework="net452" xmlns="" />
|
||||
</packages>
|
19
Core/Bridge/CallbackBridge.cs
Normal file
19
Core/Bridge/CallbackBridge.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using TweetDck.Core.Controls;
|
||||
|
||||
namespace TweetDck.Core.Bridge{
|
||||
sealed class CallbackBridge{
|
||||
private readonly Control owner;
|
||||
private readonly Action safeCallback;
|
||||
|
||||
public CallbackBridge(Control owner, Action safeCallback){
|
||||
this.owner = owner;
|
||||
this.safeCallback = safeCallback;
|
||||
}
|
||||
|
||||
public void Trigger(){
|
||||
owner.InvokeSafe(safeCallback);
|
||||
}
|
||||
}
|
||||
}
|
34
Core/Bridge/PropertyBridge.cs
Normal file
34
Core/Bridge/PropertyBridge.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace TweetDck.Core.Bridge{
|
||||
static class PropertyBridge{
|
||||
[Flags]
|
||||
public enum Properties{
|
||||
ExpandLinksOnHover = 1,
|
||||
MuteNotifications = 2,
|
||||
HasCustomNotificationSound = 4,
|
||||
All = ExpandLinksOnHover | MuteNotifications | HasCustomNotificationSound
|
||||
}
|
||||
|
||||
public static string GenerateScript(Properties properties = Properties.All){
|
||||
StringBuilder build = new StringBuilder();
|
||||
build.Append("(function(c){");
|
||||
|
||||
if (properties.HasFlag(Properties.ExpandLinksOnHover)){
|
||||
build.Append("c.expandLinksOnHover=").Append(Program.UserConfig.ExpandLinksOnHover ? "true;" : "false;");
|
||||
}
|
||||
|
||||
if (properties.HasFlag(Properties.MuteNotifications)){
|
||||
build.Append("c.muteNotifications=").Append(Program.UserConfig.MuteNotifications ? "true;" : "false;");
|
||||
}
|
||||
|
||||
if (properties.HasFlag(Properties.HasCustomNotificationSound)){
|
||||
build.Append("c.hasCustomNotificationSound=").Append(Program.UserConfig.NotificationSoundPath.Length > 0 ? "true;" : "false;");
|
||||
}
|
||||
|
||||
build.Append("})(window.$TDX=window.$TDX||{})");
|
||||
return build.ToString();
|
||||
}
|
||||
}
|
||||
}
|
@@ -3,107 +3,91 @@ using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
using TweetDck.Core.Utils;
|
||||
using TweetDck.Core.Controls;
|
||||
using TweetDck.Core.Notification;
|
||||
using TweetDck.Core.Utils;
|
||||
|
||||
namespace TweetDck.Core.Handling{
|
||||
class TweetDeckBridge{
|
||||
namespace TweetDck.Core.Bridge{
|
||||
sealed class TweetDeckBridge{
|
||||
public static string LastRightClickedLink = string.Empty;
|
||||
public static string LastHighlightedTweet = string.Empty;
|
||||
public static string LastHighlightedTweetEmbedded = string.Empty;
|
||||
public static string NotificationTweetEmbedded = string.Empty;
|
||||
public static string LastHighlightedQuotedTweet = string.Empty;
|
||||
public static string ClipboardImagePath = string.Empty;
|
||||
|
||||
public static void ResetStaticProperties(){
|
||||
LastRightClickedLink = LastHighlightedTweet = LastHighlightedQuotedTweet = ClipboardImagePath = string.Empty;
|
||||
}
|
||||
|
||||
private readonly FormBrowser form;
|
||||
private readonly FormNotification notification;
|
||||
|
||||
public string BrandName{
|
||||
get{
|
||||
return Program.BrandName;
|
||||
}
|
||||
}
|
||||
|
||||
public string VersionTag{
|
||||
get{
|
||||
return Program.VersionTag;
|
||||
}
|
||||
}
|
||||
|
||||
public bool MuteNotifications{
|
||||
get{
|
||||
return Program.UserConfig.MuteNotifications;
|
||||
}
|
||||
}
|
||||
|
||||
public bool ExpandLinksOnHover{
|
||||
get{
|
||||
return Program.UserConfig.ExpandLinksOnHover;
|
||||
}
|
||||
}
|
||||
|
||||
public TweetDeckBridge(FormBrowser form, FormNotification notification){
|
||||
this.form = form;
|
||||
this.notification = notification;
|
||||
}
|
||||
|
||||
public void LoadFontSizeClass(string fsClass){
|
||||
form.InvokeSafe(() => {
|
||||
form.InvokeAsyncSafe(() => {
|
||||
TweetNotification.SetFontSizeClass(fsClass);
|
||||
});
|
||||
}
|
||||
|
||||
public void LoadNotificationHeadContents(string headContents){
|
||||
form.InvokeSafe(() => {
|
||||
form.InvokeAsyncSafe(() => {
|
||||
TweetNotification.SetHeadTag(headContents);
|
||||
});
|
||||
}
|
||||
|
||||
public void SetLastRightClickedLink(string link){
|
||||
form.InvokeSafe(() => LastRightClickedLink = link);
|
||||
form.InvokeAsyncSafe(() => LastRightClickedLink = link);
|
||||
}
|
||||
|
||||
public void SetLastHighlightedTweet(string link, string embeddedLink){
|
||||
form.InvokeSafe(() => {
|
||||
public void SetLastHighlightedTweet(string link, string quotedLink){
|
||||
form.InvokeAsyncSafe(() => {
|
||||
LastHighlightedTweet = link;
|
||||
LastHighlightedTweetEmbedded = embeddedLink;
|
||||
LastHighlightedQuotedTweet = quotedLink;
|
||||
});
|
||||
}
|
||||
|
||||
public void SetNotificationTweetEmbedded(string link){
|
||||
form.InvokeSafe(() => NotificationTweetEmbedded = link);
|
||||
public void SetNotificationQuotedTweet(string link){
|
||||
notification.InvokeAsyncSafe(() => notification.CurrentQuotedTweetUrl = link);
|
||||
}
|
||||
|
||||
public void OpenSettingsMenu(){
|
||||
form.InvokeSafe(form.OpenSettings);
|
||||
form.InvokeAsyncSafe(form.OpenSettings);
|
||||
}
|
||||
|
||||
public void OpenPluginsMenu(){
|
||||
form.InvokeSafe(form.OpenPlugins);
|
||||
form.InvokeAsyncSafe(form.OpenPlugins);
|
||||
}
|
||||
|
||||
public void OnTweetPopup(string tweetHtml, string tweetUrl, int tweetCharacters){
|
||||
notification.InvokeSafe(() => {
|
||||
notification.ShowNotification(new TweetNotification(tweetHtml,tweetUrl,tweetCharacters));
|
||||
notification.InvokeAsyncSafe(() => {
|
||||
form.OnTweetNotification();
|
||||
notification.ShowNotification(new TweetNotification(tweetHtml, tweetUrl, tweetCharacters));
|
||||
});
|
||||
}
|
||||
|
||||
public void OnTweetSound(){
|
||||
form.InvokeSafe(form.OnTweetSound);
|
||||
}
|
||||
|
||||
public void OnNotificationReady(){
|
||||
notification.InvokeSafe(notification.OnNotificationReady);
|
||||
form.InvokeAsyncSafe(() => {
|
||||
form.OnTweetNotification();
|
||||
form.PlayNotificationSound();
|
||||
});
|
||||
}
|
||||
|
||||
public void DisplayTooltip(string text, bool showInNotification){
|
||||
if (showInNotification){
|
||||
notification.InvokeSafe(() => notification.DisplayTooltip(text));
|
||||
notification.InvokeAsyncSafe(() => notification.DisplayTooltip(text));
|
||||
}
|
||||
else{
|
||||
form.InvokeSafe(() => form.DisplayTooltip(text));
|
||||
form.InvokeAsyncSafe(() => form.DisplayTooltip(text));
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadNextNotification(){
|
||||
notification.InvokeAsyncSafe(notification.FinishCurrentTweet);
|
||||
}
|
||||
|
||||
public void TryPasteImage(){
|
||||
form.InvokeSafe(() => {
|
||||
if (Clipboard.ContainsImage()){
|
||||
@@ -113,33 +97,46 @@ namespace TweetDck.Core.Handling{
|
||||
try{
|
||||
Directory.CreateDirectory(Program.TemporaryPath);
|
||||
|
||||
ClipboardImagePath = Path.Combine(Program.TemporaryPath,"TD-Img-"+DateTime.Now.Ticks+".png");
|
||||
img.Save(ClipboardImagePath,ImageFormat.Png);
|
||||
ClipboardImagePath = Path.Combine(Program.TemporaryPath, "TD-Img-"+DateTime.Now.Ticks+".png");
|
||||
img.Save(ClipboardImagePath, ImageFormat.Png);
|
||||
|
||||
form.OnImagePasted();
|
||||
}catch(Exception e){
|
||||
Program.HandleException("Could not paste image from clipboard.",e);
|
||||
Program.Reporter.HandleException("Clipboard Image Error", "Could not paste image from clipboard.", true, e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void ClickUploadImage(int offsetX, int offsetY){
|
||||
form.InvokeSafe(() => {
|
||||
Point prevPos = Cursor.Position;
|
||||
form.InvokeAsyncSafe(() => form.TriggerImageUpload(offsetX, offsetY));
|
||||
}
|
||||
|
||||
Cursor.Position = form.PointToScreen(new Point(offsetX,offsetY));
|
||||
NativeMethods.SimulateMouseClick(NativeMethods.MouseButton.Left);
|
||||
Cursor.Position = prevPos;
|
||||
public void ScreenshotTweet(string html, int width, int height){
|
||||
form.InvokeAsyncSafe(() => form.OnTweetScreenshotReady(html, width, height));
|
||||
}
|
||||
|
||||
form.OnImagePastedFinish();
|
||||
});
|
||||
public void FixClipboard(){
|
||||
form.InvokeAsyncSafe(WindowsUtils.ClipboardStripHtmlStyles);
|
||||
}
|
||||
|
||||
public void OpenBrowser(string url){
|
||||
BrowserUtils.OpenExternalBrowser(url);
|
||||
}
|
||||
|
||||
public void Alert(string type, string contents){
|
||||
MessageBoxIcon icon;
|
||||
|
||||
switch(type){
|
||||
case "error": icon = MessageBoxIcon.Error; break;
|
||||
case "warning": icon = MessageBoxIcon.Warning; break;
|
||||
case "info": icon = MessageBoxIcon.Information; break;
|
||||
default: icon = MessageBoxIcon.None; break;
|
||||
}
|
||||
|
||||
MessageBox.Show(contents, Program.BrandName+" Browser Message", MessageBoxButtons.OK, icon);
|
||||
}
|
||||
|
||||
public void Log(string data){
|
||||
System.Diagnostics.Debug.WriteLine(data);
|
||||
}
|
@@ -1,9 +1,12 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
using TweetDck.Core.Utils;
|
||||
|
||||
namespace TweetDck.Core.Controls{
|
||||
static class ControlExtensions{
|
||||
public static readonly Point InvisibleLocation = new Point(-32000, -32000);
|
||||
|
||||
public static void InvokeSafe(this Control control, Action func){
|
||||
if (control.InvokeRequired){
|
||||
control.Invoke(func);
|
||||
@@ -13,8 +16,12 @@ namespace TweetDck.Core.Controls{
|
||||
}
|
||||
}
|
||||
|
||||
public static void InvokeAsyncSafe(this Control control, Action func){
|
||||
control.BeginInvoke(func);
|
||||
}
|
||||
|
||||
public static void MoveToCenter(this Form targetForm, Form parentForm){
|
||||
targetForm.Location = new Point(parentForm.Location.X+parentForm.Width/2-targetForm.Width/2,parentForm.Location.Y+parentForm.Height/2-targetForm.Height/2);
|
||||
targetForm.Location = new Point(parentForm.Location.X+parentForm.Width/2-targetForm.Width/2, parentForm.Location.Y+parentForm.Height/2-targetForm.Height/2);
|
||||
}
|
||||
|
||||
public static void SetValueInstant(this ProgressBar bar, int value){
|
||||
@@ -28,5 +35,27 @@ namespace TweetDck.Core.Controls{
|
||||
bar.Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetValueSafe(this TrackBar trackBar, int value){
|
||||
if (value >= trackBar.Minimum && value <= trackBar.Maximum){
|
||||
trackBar.Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
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){
|
||||
textBox.KeyDown += (sender, args) => {
|
||||
if (args.Control && args.KeyCode == Keys.A){
|
||||
((TextBox)sender).SelectAll();
|
||||
args.SuppressKeyPress = true;
|
||||
args.Handled = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
20
Core/Controls/FlatButton.cs
Normal file
20
Core/Controls/FlatButton.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace TweetDck.Core.Controls{
|
||||
class FlatButton : Button{
|
||||
protected override bool ShowFocusCues{
|
||||
get{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public FlatButton(){
|
||||
GotFocus += FlatButton_GotFocus;
|
||||
}
|
||||
|
||||
private void FlatButton_GotFocus(object sender, EventArgs e){ // removes extra border when focused
|
||||
NotifyDefault(false);
|
||||
}
|
||||
}
|
||||
}
|
@@ -2,21 +2,23 @@
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace TweetDck.Core.Controls{
|
||||
public partial class FlatProgressBar : ProgressBar{
|
||||
private SolidBrush brush;
|
||||
sealed partial class FlatProgressBar : ProgressBar{
|
||||
private readonly SolidBrush brush;
|
||||
|
||||
public FlatProgressBar(){
|
||||
SetStyle(ControlStyles.UserPaint,true);
|
||||
SetStyle(ControlStyles.OptimizedDoubleBuffer,true);
|
||||
brush = new SolidBrush(Color.White);
|
||||
|
||||
SetStyle(ControlStyles.UserPaint, true);
|
||||
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
|
||||
}
|
||||
|
||||
public void SetValueInstant(int value){
|
||||
ControlExtensions.SetValueInstant(this,value);
|
||||
ControlExtensions.SetValueInstant(this, value);
|
||||
}
|
||||
|
||||
protected override void OnPaint(PaintEventArgs e){
|
||||
if (brush == null || brush.Color != ForeColor){
|
||||
brush = new SolidBrush(ForeColor);
|
||||
if (brush.Color != ForeColor){
|
||||
brush.Color = ForeColor;
|
||||
}
|
||||
|
||||
Rectangle rect = e.ClipRectangle;
|
||||
@@ -25,9 +27,11 @@ namespace TweetDck.Core.Controls{
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing){
|
||||
if (brush != null)brush.Dispose();
|
||||
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing){
|
||||
brush.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,19 +3,9 @@ using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace TweetDck.Core.Controls{
|
||||
sealed partial class TabButton : Button{
|
||||
protected override bool ShowFocusCues{
|
||||
get{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
sealed partial class TabButton : FlatButton{
|
||||
public Action Callback { get; private set; }
|
||||
|
||||
public TabButton(){
|
||||
GotFocus += TabButton_GotFocus;
|
||||
}
|
||||
|
||||
public void SetupButton(int locationX, int sizeWidth, string title, Action callback){
|
||||
Callback = callback;
|
||||
|
||||
@@ -24,16 +14,12 @@ namespace TweetDck.Core.Controls{
|
||||
FlatAppearance.MouseDownBackColor = Color.White;
|
||||
FlatAppearance.MouseOverBackColor = Color.White;
|
||||
FlatStyle = FlatStyle.Flat;
|
||||
Location = new Point(locationX,0);
|
||||
Location = new Point(locationX, 0);
|
||||
Margin = new Padding(0);
|
||||
Size = new Size(sizeWidth,30);
|
||||
Size = new Size(sizeWidth, 30);
|
||||
Text = title;
|
||||
UseVisualStyleBackColor = true;
|
||||
ResumeLayout(true);
|
||||
}
|
||||
|
||||
private void TabButton_GotFocus(object sender, EventArgs e){ // removes extra border when focused
|
||||
NotifyDefault(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -33,7 +33,7 @@ namespace TweetDck.Core.Controls{
|
||||
|
||||
public TabButton AddButton(string title, Action callback){
|
||||
TabButton button = new TabButton();
|
||||
button.SetupButton((btnWidth-1)*panelButtons.Controls.Count,btnWidth,title,callback);
|
||||
button.SetupButton((btnWidth-1)*panelButtons.Controls.Count, btnWidth, title, callback);
|
||||
button.Click += (sender, args) => SelectTab((TabButton)sender);
|
||||
|
||||
panelButtons.Controls.Add(button);
|
||||
|
6
Core/FormBrowser.Designer.cs
generated
6
Core/FormBrowser.Designer.cs
generated
@@ -24,7 +24,6 @@
|
||||
/// </summary>
|
||||
private void InitializeComponent() {
|
||||
this.components = new System.ComponentModel.Container();
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FormBrowser));
|
||||
this.trayIcon = new TweetDck.Core.TrayIcon();
|
||||
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
||||
this.SuspendLayout();
|
||||
@@ -38,11 +37,12 @@
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(324, 386);
|
||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
||||
this.Location = new System.Drawing.Point(-32000, -32000);
|
||||
this.Icon = Properties.Resources.icon;
|
||||
this.Location = TweetDck.Core.Controls.ControlExtensions.InvisibleLocation;
|
||||
this.MinimumSize = new System.Drawing.Size(340, 424);
|
||||
this.Name = "FormBrowser";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
|
||||
this.Activated += new System.EventHandler(this.FormBrowser_Activated);
|
||||
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FormBrowser_FormClosing);
|
||||
this.ResizeEnd += new System.EventHandler(this.FormBrowser_ResizeEnd);
|
||||
this.Resize += new System.EventHandler(this.FormBrowser_Resize);
|
||||
|
@@ -8,9 +8,16 @@ using TweetDck.Core.Other;
|
||||
using TweetDck.Resources;
|
||||
using TweetDck.Core.Controls;
|
||||
using System.Drawing;
|
||||
using TweetDck.Core.Utils;
|
||||
using TweetDck.Updates;
|
||||
using TweetDck.Plugins;
|
||||
using TweetDck.Plugins.Enums;
|
||||
using TweetDck.Plugins.Events;
|
||||
using TweetDck.Core.Bridge;
|
||||
using TweetDck.Core.Notification;
|
||||
using TweetDck.Core.Notification.Screenshot;
|
||||
using TweetDck.Updates.Events;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace TweetDck.Core{
|
||||
sealed partial class FormBrowser : Form{
|
||||
@@ -25,6 +32,7 @@ namespace TweetDck.Core{
|
||||
private readonly ChromiumWebBrowser browser;
|
||||
private readonly PluginManager plugins;
|
||||
private readonly UpdateHandler updates;
|
||||
private readonly FormNotification notification;
|
||||
|
||||
private FormSettings currentFormSettings;
|
||||
private FormAbout currentFormAbout;
|
||||
@@ -33,32 +41,57 @@ namespace TweetDck.Core{
|
||||
|
||||
private FormWindowState prevState;
|
||||
|
||||
public FormBrowser(PluginManager pluginManager){
|
||||
private TweetScreenshotManager notificationScreenshotManager;
|
||||
private SoundNotification soundNotification;
|
||||
|
||||
public FormBrowser(PluginManager pluginManager, UpdaterSettings updaterSettings){
|
||||
InitializeComponent();
|
||||
|
||||
Text = Program.BrandName;
|
||||
|
||||
this.plugins = pluginManager;
|
||||
this.plugins.Reloaded += plugins_Reloaded;
|
||||
this.plugins.Config.PluginChangedState += plugins_PluginChangedState;
|
||||
this.plugins.PluginChangedState += plugins_PluginChangedState;
|
||||
|
||||
FormNotification notification = CreateNotificationForm(true);
|
||||
notification.CanMoveWindow = () => false;
|
||||
notification.Show();
|
||||
this.notification = CreateNotificationForm(NotificationFlags.AutoHide | NotificationFlags.TopMost);
|
||||
#if DEBUG
|
||||
this.notification.CanMoveWindow = () => (ModifierKeys & Keys.Alt) == Keys.Alt;
|
||||
#else
|
||||
this.notification.CanMoveWindow = () => false;
|
||||
#endif
|
||||
this.notification.Show();
|
||||
|
||||
this.browser = new ChromiumWebBrowser("https://tweetdeck.twitter.com/"){
|
||||
MenuHandler = new ContextMenuBrowser(this),
|
||||
DialogHandler = new DialogHandlerBrowser(this),
|
||||
DialogHandler = new FileDialogHandler(this),
|
||||
JsDialogHandler = new JavaScriptDialogHandler(),
|
||||
LifeSpanHandler = new LifeSpanHandler()
|
||||
};
|
||||
|
||||
#if DEBUG
|
||||
this.browser.ConsoleMessage += BrowserUtils.HandleConsoleMessage;
|
||||
#endif
|
||||
|
||||
this.browser.LoadingStateChanged += Browser_LoadingStateChanged;
|
||||
this.browser.FrameLoadEnd += Browser_FrameLoadEnd;
|
||||
this.browser.RegisterJsObject("$TD",new TweetDeckBridge(this,notification));
|
||||
this.browser.RegisterAsyncJsObject("$TD", new TweetDeckBridge(this, notification));
|
||||
this.browser.RegisterAsyncJsObject("$TDP", plugins.Bridge);
|
||||
|
||||
Controls.Add(browser);
|
||||
|
||||
Disposed += (sender, args) => browser.Dispose();
|
||||
Controls.Add(new MenuStrip{ Visible = false }); // fixes Alt freezing the program in Win 10 Anniversary Update
|
||||
|
||||
Disposed += (sender, args) => {
|
||||
browser.Dispose();
|
||||
|
||||
if (notificationScreenshotManager != null){
|
||||
notificationScreenshotManager.Dispose();
|
||||
}
|
||||
|
||||
if (soundNotification != null){
|
||||
soundNotification.Dispose();
|
||||
}
|
||||
};
|
||||
|
||||
this.trayIcon.ClickRestore += trayIcon_ClickRestore;
|
||||
this.trayIcon.ClickClose += trayIcon_ClickClose;
|
||||
@@ -66,56 +99,76 @@ namespace TweetDck.Core{
|
||||
|
||||
UpdateTrayIcon();
|
||||
|
||||
this.updates = new UpdateHandler(browser,this);
|
||||
Config.MuteToggled += Config_MuteToggled;
|
||||
|
||||
this.updates = new UpdateHandler(browser, this, updaterSettings);
|
||||
this.updates.UpdateAccepted += updates_UpdateAccepted;
|
||||
this.updates.UpdateDismissed += updates_UpdateDismissed;
|
||||
}
|
||||
|
||||
private void ShowChildForm(Form form){
|
||||
form.VisibleChanged += (sender, args) => form.MoveToCenter(this);
|
||||
form.Show(this);
|
||||
form.MoveToCenter(this);
|
||||
}
|
||||
|
||||
private void ForceClose(){
|
||||
public void ForceClose(){
|
||||
trayIcon.Visible = false; // checked in FormClosing event
|
||||
Close();
|
||||
}
|
||||
|
||||
public FormNotification CreateNotificationForm(bool autoHide){
|
||||
return new FormNotification(this,plugins,trayIcon,autoHide);
|
||||
}
|
||||
|
||||
// window setup
|
||||
|
||||
private void SetupWindow(){
|
||||
Config.BrowserWindow.Restore(this,true);
|
||||
Config.BrowserWindow.Restore(this, true);
|
||||
prevState = WindowState;
|
||||
isLoaded = true;
|
||||
}
|
||||
|
||||
private void UpdateTrayIcon(){
|
||||
trayIcon.Visible = Config.TrayBehavior != TrayIcon.Behavior.Disabled;
|
||||
trayIcon.Visible = Config.TrayBehavior.ShouldDisplayIcon();
|
||||
}
|
||||
|
||||
// active event handlers
|
||||
|
||||
private void Browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
|
||||
if (!e.IsLoading){
|
||||
browser.AddWordToDictionary("tweetdeck");
|
||||
browser.AddWordToDictionary("TweetDeck");
|
||||
browser.AddWordToDictionary("tweetduck");
|
||||
browser.AddWordToDictionary("TweetDuck");
|
||||
browser.AddWordToDictionary("TD");
|
||||
|
||||
Invoke(new Action(SetupWindow));
|
||||
browser.LoadingStateChanged -= Browser_LoadingStateChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
|
||||
if (e.Frame.IsMain){
|
||||
ScriptLoader.ExecuteFile(e.Frame,"code.js");
|
||||
if (e.Frame.IsMain && BrowserUtils.IsTweetDeckWebsite(e.Frame)){
|
||||
UpdateProperties();
|
||||
ScriptLoader.ExecuteFile(e.Frame, "code.js");
|
||||
ReinjectCustomCSS(Config.CustomBrowserCSS);
|
||||
|
||||
#if DEBUG
|
||||
ScriptLoader.ExecuteFile(e.Frame, "debug.js");
|
||||
#endif
|
||||
|
||||
if (plugins.HasAnyPlugin(PluginEnvironment.Browser)){
|
||||
ScriptLoader.ExecuteFile(e.Frame,PluginManager.PluginBrowserScriptFile);
|
||||
plugins.ExecutePlugins(e.Frame,PluginEnvironment.Browser,true);
|
||||
ScriptLoader.ExecuteFile(e.Frame, PluginManager.PluginBrowserScriptFile);
|
||||
ScriptLoader.ExecuteFile(e.Frame, PluginManager.PluginGlobalScriptFile);
|
||||
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Browser, true);
|
||||
}
|
||||
|
||||
TweetDeckBridge.ResetStaticProperties();
|
||||
}
|
||||
}
|
||||
|
||||
private void FormBrowser_Activated(object sender, EventArgs e){
|
||||
if (!isLoaded)return;
|
||||
|
||||
trayIcon.HasNotifications = false;
|
||||
}
|
||||
|
||||
private void FormBrowser_Resize(object sender, EventArgs e){
|
||||
if (!isLoaded)return;
|
||||
|
||||
@@ -123,12 +176,12 @@ namespace TweetDck.Core{
|
||||
prevState = WindowState;
|
||||
|
||||
if (WindowState == FormWindowState.Minimized){
|
||||
if (Config.TrayBehavior == TrayIcon.Behavior.MinimizeToTray){
|
||||
if (Config.TrayBehavior.ShouldHideOnMinimize()){
|
||||
Hide(); // hides taskbar too?! welp that works I guess
|
||||
}
|
||||
}
|
||||
else{
|
||||
FormBrowser_ResizeEnd(sender,e);
|
||||
FormBrowser_ResizeEnd(sender, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -136,7 +189,7 @@ namespace TweetDck.Core{
|
||||
private void FormBrowser_ResizeEnd(object sender, EventArgs e){ // also triggers when the window moves
|
||||
if (!isLoaded)return;
|
||||
|
||||
if (Location.X != -32000){
|
||||
if (Location != ControlExtensions.InvisibleLocation){
|
||||
Config.BrowserWindow.Save(this);
|
||||
Config.Save();
|
||||
}
|
||||
@@ -145,12 +198,16 @@ namespace TweetDck.Core{
|
||||
private void FormBrowser_FormClosing(object sender, FormClosingEventArgs e){
|
||||
if (!isLoaded)return;
|
||||
|
||||
if (Config.TrayBehavior == TrayIcon.Behavior.CloseToTray && trayIcon.Visible && e.CloseReason == CloseReason.UserClosing){
|
||||
if (Config.TrayBehavior.ShouldHideOnClose() && trayIcon.Visible && e.CloseReason == CloseReason.UserClosing){
|
||||
Hide(); // hides taskbar too?! welp that works I guess
|
||||
e.Cancel = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void Config_MuteToggled(object sender, EventArgs e){
|
||||
UpdateProperties(PropertyBridge.Properties.MuteNotifications);
|
||||
}
|
||||
|
||||
private void Config_TrayBehaviorChanged(object sender, EventArgs e){
|
||||
if (!isLoaded)return;
|
||||
|
||||
@@ -174,11 +231,11 @@ namespace TweetDck.Core{
|
||||
}
|
||||
|
||||
private void plugins_Reloaded(object sender, PluginLoadEventArgs e){
|
||||
browser.ExecuteScriptAsync("window.location.reload()");
|
||||
browser.GetBrowser().Reload();
|
||||
}
|
||||
|
||||
private void plugins_PluginChangedState(object sender, PluginChangedStateEventArgs e){
|
||||
browser.ExecuteScriptAsync("window.TDPF_setPluginState",e.Plugin,e.IsEnabled ? 1 : 0); // ExecuteScriptAsync cannot handle boolean values as of yet
|
||||
browser.ExecuteScriptAsync("window.TDPF_setPluginState", e.Plugin, e.IsEnabled ? 1 : 0); // ExecuteScriptAsync cannot handle boolean values as of yet
|
||||
}
|
||||
|
||||
private void updates_UpdateAccepted(object sender, UpdateAcceptedEventArgs e){
|
||||
@@ -187,6 +244,7 @@ namespace TweetDck.Core{
|
||||
FormUpdateDownload downloadForm = new FormUpdateDownload(e.UpdateInfo);
|
||||
downloadForm.MoveToCenter(this);
|
||||
downloadForm.ShowDialog();
|
||||
downloadForm.Dispose();
|
||||
|
||||
if (downloadForm.UpdateStatus == FormUpdateDownload.Status.Succeeded){
|
||||
UpdateInstallerPath = downloadForm.InstallerPath;
|
||||
@@ -200,39 +258,85 @@ namespace TweetDck.Core{
|
||||
}
|
||||
}
|
||||
|
||||
private void updates_UpdateDismissed(object sender, UpdateDismissedEventArgs e){
|
||||
Config.DismissedUpdate = e.VersionTag;
|
||||
Config.Save();
|
||||
}
|
||||
|
||||
protected override void WndProc(ref Message m){
|
||||
if (isLoaded && m.Msg == Program.WindowRestoreMessage){
|
||||
trayIcon_ClickRestore(trayIcon,new EventArgs());
|
||||
using(Process process = Process.GetCurrentProcess()){
|
||||
if (process.Id == m.WParam.ToInt32()){
|
||||
trayIcon_ClickRestore(trayIcon, new EventArgs());
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (isLoaded && m.Msg == 0x210 && (m.WParam.ToInt32() & 0xFFFF) == 0x020B){ // WM_PARENTNOTIFY, WM_XBUTTONDOWN
|
||||
browser.ExecuteScriptAsync("TDGF_onMouseClickExtra",(m.WParam.ToInt32() >> 16) & 0xFFFF);
|
||||
browser.ExecuteScriptAsync("TDGF_onMouseClickExtra", (m.WParam.ToInt32() >> 16) & 0xFFFF);
|
||||
return;
|
||||
}
|
||||
|
||||
base.WndProc(ref m);
|
||||
}
|
||||
|
||||
// notification helpers
|
||||
|
||||
public FormNotification CreateNotificationForm(NotificationFlags flags){
|
||||
return new FormNotification(this, plugins, flags);
|
||||
}
|
||||
|
||||
public void PauseNotification(){
|
||||
notification.PauseNotification();
|
||||
}
|
||||
|
||||
public void ResumeNotification(){
|
||||
notification.ResumeNotification();
|
||||
}
|
||||
|
||||
// javascript calls
|
||||
|
||||
public void ReinjectCustomCSS(string css){
|
||||
browser.ExecuteScriptAsync("TDGF_reinjectCustomCSS", css == null ? string.Empty : css.Replace(Environment.NewLine, " "));
|
||||
}
|
||||
|
||||
public void UpdateProperties(PropertyBridge.Properties properties = PropertyBridge.Properties.All){
|
||||
browser.ExecuteScriptAsync(PropertyBridge.GenerateScript(properties));
|
||||
}
|
||||
|
||||
// callback handlers
|
||||
|
||||
public void OpenSettings(){
|
||||
OpenSettings(0);
|
||||
}
|
||||
|
||||
public void OpenSettings(int tabIndex){
|
||||
if (currentFormSettings != null){
|
||||
currentFormSettings.BringToFront();
|
||||
}
|
||||
else{
|
||||
bool prevEnableUpdateCheck = Config.EnableUpdateCheck;
|
||||
|
||||
currentFormSettings = new FormSettings(this,updates);
|
||||
currentFormSettings = new FormSettings(this, plugins, updates, tabIndex);
|
||||
|
||||
currentFormSettings.FormClosed += (sender, args) => {
|
||||
currentFormSettings = null;
|
||||
|
||||
if (!prevEnableUpdateCheck && Config.EnableUpdateCheck){
|
||||
updates.Settings.DismissedUpdate = string.Empty;
|
||||
Config.DismissedUpdate = string.Empty;
|
||||
Config.Save();
|
||||
|
||||
updates.Check(false);
|
||||
}
|
||||
|
||||
if (!Config.EnableTrayHighlight){
|
||||
trayIcon.HasNotifications = false;
|
||||
}
|
||||
|
||||
UpdateProperties(PropertyBridge.Properties.ExpandLinksOnHover | PropertyBridge.Properties.HasCustomNotificationSound);
|
||||
};
|
||||
|
||||
ShowChildForm(currentFormSettings);
|
||||
@@ -261,8 +365,30 @@ namespace TweetDck.Core{
|
||||
}
|
||||
}
|
||||
|
||||
public void OnTweetSound(){
|
||||
|
||||
public void OnTweetNotification(){ // may be called multiple times, once for each type of notification
|
||||
if (Config.EnableTrayHighlight && !ContainsFocus){
|
||||
trayIcon.HasNotifications = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void PlayNotificationSound(){
|
||||
if (Config.NotificationSoundPath.Length == 0){
|
||||
return;
|
||||
}
|
||||
|
||||
if (soundNotification == null){
|
||||
soundNotification = new SoundNotification(this);
|
||||
}
|
||||
|
||||
soundNotification.Play(Config.NotificationSoundPath);
|
||||
}
|
||||
|
||||
public void OnTweetScreenshotReady(string html, int width, int height){
|
||||
if (notificationScreenshotManager == null){
|
||||
notificationScreenshotManager = new TweetScreenshotManager(this);
|
||||
}
|
||||
|
||||
notificationScreenshotManager.Trigger(html, width, height);
|
||||
}
|
||||
|
||||
public void DisplayTooltip(string text){
|
||||
@@ -271,17 +397,23 @@ namespace TweetDck.Core{
|
||||
}
|
||||
else{
|
||||
Point position = PointToClient(Cursor.Position);
|
||||
position.Offset(20,10);
|
||||
toolTip.Show(text,this,position);
|
||||
position.Offset(20, 10);
|
||||
toolTip.Show(text, this, position);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnImagePasted(){
|
||||
browser.ExecuteScriptAsync("TDGF_tryPasteImage",new object[0]);
|
||||
browser.ExecuteScriptAsync("TDGF_tryPasteImage()");
|
||||
}
|
||||
|
||||
public void OnImagePastedFinish(){
|
||||
browser.ExecuteScriptAsync("TDGF_tryPasteImageFinish",new object[0]);
|
||||
public void TriggerImageUpload(int offsetX, int offsetY){
|
||||
IBrowserHost host = browser.GetBrowser().GetHost();
|
||||
host.SendMouseClickEvent(offsetX, offsetY, MouseButtonType.Left, false, 1, CefEventFlags.None);
|
||||
host.SendMouseClickEvent(offsetX, offsetY, MouseButtonType.Left, true, 1, CefEventFlags.None);
|
||||
}
|
||||
|
||||
public void TriggerTweetScreenshot(){
|
||||
browser.ExecuteScriptAsync("TDGF_triggerScreenshot()");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -117,267 +117,11 @@
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<metadata name="trayIcon.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
<metadata name="toolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>112, 17</value>
|
||||
</metadata>
|
||||
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
AAABAAMAMDAAAAEAIACoJQAANgAAACAgAAABACAAqBAAAN4lAAAQEAAAAQAgAGgEAACGNgAAKAAAADAA
|
||||
AABgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqHg5IrF+OlS+hjyMw4g7tcmM
|
||||
PM/MjTzezY485s2OPObMjjzfyIw80MOJPLe8hDyNs387WKx8OyQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApHU4FrB8OGXGijzAzo889dKR
|
||||
PP/SkTz/0pE8/9GRPP/RkDz/0JA8/9CQPP/RkDz/0ZE8/9KRPP/TkTz/0pE8/86PPPbGijzCt4E8bKp6
|
||||
OxoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKN1OB+9hTuLzI486NOR
|
||||
PP/SkTz/0ZA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9GQ
|
||||
PP/SkTz/0pE8/82OPOu9hTuOq3w8IgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACidTkFuoM8csyN
|
||||
POrTkjz/0ZE8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9GRPP/Tkjz/zY887bqEPHimeDsHAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKp6
|
||||
OynDiDvA05I8/9GRPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0ZA8/9ORPP/GijzFr308LgAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAArXw7UM2OPOvTkTz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/SkTz/zo887rOAPFYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAACxfjxm0ZA8/NGRPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0ZE8/9GQPP64gjxtAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAALaBPGXRkTz/0ZE8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9GQPP/SkTz/uIM8bAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqnk6TtCQPPzRkTz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/RkDz/0ZA8/rSA
|
||||
PFUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACtfDwmzo887dKRPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0ZE8/86PPPGtfDwrAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKZ4PAXFijzC0pE8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CURf/TpGT/2baJ/9/GpP/l0bf/59W//+fVvf/kz7P/3sOg/9mz
|
||||
hP/ToWH/0JND/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9KRPP/EiTvJo3Y7CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALiC
|
||||
PG/Tkjz/0JA8/9CQPP/QkDz/0JA8/9CSQf/Vq3P/5tO7//j07v//////////////////////////////
|
||||
////////////////////////9vHq/+TQtf/UqG7/0JA+/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/Tkjz/uYM8dgAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAArXw8Gc2OPOzRkTz/0JA8/9CQPP/QlEX/27qP//fz7P//////////////////////////////
|
||||
////////////////////////////////////////////////////////8ObZ/9auev/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/RkTz/zI488J1x
|
||||
OB8AAAAAAAAAAAAAAAAAAAAAuoM7h9OSPP/QkDz/0JA8/9GXTf/TpGb/27uS/9u6kP/ewZz/59W9//Xv
|
||||
5///////////////////////////////////////////////////////////////////////////////
|
||||
///r3cr/0ZxV/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/1JI8/7mDO5AAAAAAAAAAAAAAAADFj0cR0pNB6s+PO//Ojjv/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CRQP/TpGf/5NC2//38+v//////////////////////////////////////////////
|
||||
////////////////////////+vf0/9auev/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0ZE8/8uOPO6SajYVAAAAAAAAAADzr1df7qxV/+WkT//ZmUT/0JA9/86O
|
||||
O//Pjzv/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9GaUv/l07r/////////////////////////
|
||||
///////////////////////////////////////////////////buo//0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9OSPP+ldjdoAAAAAAAAAADurFW67qxV//Ct
|
||||
Vv/vrVb/6adR/92dSP/Skz//zo47/8+PO//QkDz/0JA8/9CQPP/QkDz/0JJB/9Smaf/dv5j/+fXw////
|
||||
////////////////////////////////////////////////////////////////////////2riM/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9KRPP/FiTzGoHQ6Ae6s
|
||||
VRrurFXz7qxV/+6sVf/urFX/761W//CuVv/sqlT/4qFM/9aVQv/Pjzv/zo47/9CQPP/VqnH/8+vh////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////9aueP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9GQ
|
||||
PP/Ojzz4oHM5Iu6sVU3urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/vrVX/8K5W/+6sVf/mpU//2plF/9q6
|
||||
j///////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////n18P/Rm1P/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/SkTz/rXs6Vu6sVYPurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/wrVb/6r2B////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////o18L/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/SkTz/v4Y8j+6sVa3urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFb/9Onb///////+/v3/////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////1qx2/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/SkTz/wYc7uO6sVcrurFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/trlr/67Zy/+m+h//y5NP/////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////7+TW/9CQPf/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/RkTz/yYw80e6s
|
||||
VdvurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/6sme//v49P//////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////9arc//QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/RkDz/zI083+6sVeLurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/u2b3/////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////+fVvv/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/zo885u6sVeLurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+vL
|
||||
of//////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////r28v/Rl0r/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/zo885u6sVdrurFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7LBj//r28f//////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///Vq3P/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/RkDz/zI083+6sVcrurFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/6cKO////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////ewp7/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/RkTz/yYw80O6s
|
||||
VazurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7NOy//z59v/u2b7/+fPr////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////n1sD/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/SkTz/xIo8uO6sVYHurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/67Zx/+2uWf/qy6H/////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////v5dj/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/SkTz/vYU8je6sVUvurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+nC
|
||||
kP///////////////////////////////////////////////////////fz6//fu5P/w4Mv/793F//7+
|
||||
/v/////////////////////////////////////////////////////////////////17eT/0JA//86O
|
||||
Ov/Pjzv/0JA8/9CQPP/QkDz/0JA8/9CQPP/SkTz/tYA8Vu6sVRnurFXy7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7a5b//fw5///////////////////////////////////////+/jz/+7av//pwIr/67Jn/+6t
|
||||
Vv/urFX/6rd1////////////////////////////////////////////////////////////////////
|
||||
///48ej/6KhU/92cR//Tkj7/zo47/8+PO//QkDz/0JA8/9GQPP/Pjzz3rXw8IAAAAADurFW37qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/6b2D//////////////////////////////////nz6//qyZ3/7K9h/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/6rl6////////////////////////////////////////////////////
|
||||
///////////////////9/Pr/6r6C//CuV//sqlT/4aBL/9aWQv/Pjzv/zo47/9GRPP/GijzCp3g8AQAA
|
||||
AADurFVb7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/68yk///////////////////////+/fz/68+q/+2v
|
||||
XP/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/6rNr//7+/f//////////////////////////////
|
||||
/////////////////////////////////////////////+nEk//vrVX/8K5W/+6sVf/lpE//2ZlF/9KR
|
||||
Pf+zfzpoAAAAAAAAAADurFUP7qxV5u6sVf/urFX/7qxV/+6sVf/urFX/7NGu//////////////////fu
|
||||
5P/quHb/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxW//Xr3///////////////
|
||||
///////////////////////////////////////////////////7+PP/+vXu///////pxJL/7qxV/+6s
|
||||
Vf/wrVb/8K1W/+mnUevUmUsUAAAAAAAAAAAAAAAA7qxVgO6sVf/urFX/7qxV/+6sVf/urFX/6sqg////
|
||||
////////8eLO/+yvX//urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+nF
|
||||
lf/////////////////////////////////////////////////////////////////8+vf/6cWV/+q5
|
||||
ev/s07H/6rd1/+6sVf/urFX/7qxV/++tVogAAAAAAAAAAAAAAAAAAAAA7qxVFe6sVejurFX/7qxV/+6s
|
||||
Vf/urFX/6bp8///////w4Mn/7a1Z/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/x4cz////////////////////////////////////////////////////+//7+
|
||||
/v/////////+/+m8gP/urFX/7a1Y/+6sVf/urFX/7qxV7O6sVRoAAAAAAAAAAAAAAAAAAAAAAAAAAO6s
|
||||
VWburFX/7qxV/+6sVf/urFX/7a5b/+zTs//sr1//7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/sr1//8uPQ////////////////////////////////////
|
||||
////////6suj/+qzav/pxpj/9Onb//n07f/rr2D/7qxV/+6sVf/urFX/7qxVbwAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAO6sVQPurFW67qxV/+6sVf/urFX/7qxV/+6sVv/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7axY/+nGl//27uP/////////
|
||||
/////////fz6//Lj0P/qunv/7qxV/+6sVf/urFX/7qxV/+m9gv/qt3X/7qxV/+6sVf/urFXB7qxVBQAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADurFUg7qxV6O6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/qtnP/6sqg/+3WuP/s07P/6cSS/+uyZf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVv/trVr/7qxV/+6s
|
||||
VezurFUlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7qxVRu6sVfjurFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV++6sVUwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO6s
|
||||
VVzurFX97qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxVYwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAADurFVd7qxV+O6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVfvurFVjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7qxVR+6sVeXurFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV6O6sVU0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO6sVSPurFW37qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFW77qxVJgAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AADurFUB7qxVae6sVeTurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV5+6sVW3urFUDAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAO6sVRjurFWA7qxV4u6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVeTurFWE7qxVGwAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7qxVEu6sVV7urFW17qxV7+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVfDurFW47qxVX+6s
|
||||
VRMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAA7qxVG+6sVUzurFWA7qxVrO6sVcnurFXa7qxV4u6sVeLurFXa7qxVye6sVa3urFWC7qxVTe6s
|
||||
VR0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAP//gAH//6gr//wAAD//qCv/8AAAD/+oK//AAAAD/6gr/4AAAAH/qCv/AAAAAP+oK/4A
|
||||
AAAAf6gr/AAAAAA/qCv4AAAAAB+oK/AAAAAAD6gr4AAAAAAHqCvgAAAAAAeoK8AAAAAAA6grwAAAAAAD
|
||||
qCuAAAAAAAGoK4AAAAAAAagrgAAAAAAAqCsAAAAAAACoKwAAAAAAAKgrAAAAAAAAqCsAAAAAAACoKwAA
|
||||
AAAAAKgrAAAAAAAAqCsAAAAAAACoKwAAAAAAAKgrAAAAAAAAqCsAAAAAAACoKwAAAAAAAKgrAAAAAAAA
|
||||
qCsAAAAAAACoKwAAAAAAAKgrgAAAAAAAqCuAAAAAAAGoK4AAAAAAAagrwAAAAAADqCvAAAAAAAOoK+AA
|
||||
AAAAB6gr4AAAAAAHqCvwAAAAAA+oK/gAAAAAH6gr/AAAAAA/qCv+AAAAAH+oK/8AAAAA/6gr/4AAAAH/
|
||||
qCv/wAAAA/+oK//wAAAP/6gr//wAAD//qCv//4AB//+oKygAAAAgAAAAQAAAAAEAIAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApHY4FrR/
|
||||
Ole+hjuVxos7w8uNPNvOjzznzo8858uNPNzGijzEvoU7l7WBO1mpejsYAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqHg5Kr6F
|
||||
O5XLjTvo0pE8/9ORPP/SkTz/0ZE8/9CQPP/QkDz/0ZE8/9KRPP/TkTz/0pE8/82OPOq/hjyZrXw7LAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAo3U6CbuE
|
||||
O4bPjzz005I8/9GRPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0ZE8/9OS
|
||||
PP/PkDz1vYU8iah6PAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKR3
|
||||
OibGizvG05E8/9GQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9GQPP/TkTz/yIs8yqx8PCkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AACsezsxy40849KRPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/SkTz/zI485rKAPTUAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAApXc6JMuNPOTSkTz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/SkTz/zI485659PCcAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAKd4OwnHizzJ0pE8/9CQPP/QkDz/0JA9/9OgYP/YtIX/38Sh/+LMr//iy67/3sOf/9iy
|
||||
gv/Tn13/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/SkTz/x4s8zaV4
|
||||
OwsAAAAAAAAAAAAAAAAAAAAAu4Q8hNOSPP/QkDz/0JA8/9GXS//YtIX/6NfC//Xv5//8+vj/////////
|
||||
///8+vf/9O3k/+fVvv/YsYH/0JRF/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/Tkjz/u4Q7iQAAAAAAAAAAAAAAAKx7OyTPjzz10ZA8/9GXS//Ztoj/8une////////////////////
|
||||
///////////////////////////////////38uz/3L2V/9CRP//QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9GRPP/Ojzz3onU5KQAAAAAAAAAAxo1BlNGQO//Ojjv/0JNC/9GWSP/QlET/0ZpR/9au
|
||||
eP/n1b3//v79////////////////////////////////////////////7+TV/9GdWP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9SSPP+4gjqaAAAAAPGuVhHvrVXq5qRO/9qZRP/QkT3/zo46/8+P
|
||||
O//QkDz/0JA8/9CQPP/To2P/8+vh////////////////////////////////////////////+fXw/9Oh
|
||||
Yf/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0pE8/8qMO+2WbTYV7qxVUe6sVf/wrVb/761W/+mo
|
||||
Uv/enUj/05M//86PO//PkD7/3cCZ//Lp3v/7+PT/////////////////////////////////////////
|
||||
////////+PTv/9GcVf/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/05I8/699OljurFWR7qxV/+6s
|
||||
Vf/urFX/761W//CuV//sqlT/4qJQ/+zdyf//////////////////////////////////////////////
|
||||
////////////////////////7+TV/9CRP//QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/Tkjz/vIQ7mO6s
|
||||
Vb/urFX/7qxV/+6sVf/urFX/7qxV/++tVf/ry5///v79//7+/f//////////////////////////////
|
||||
////////////////////////////////////////2rqO/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9KR
|
||||
PP/GijvF7qxV2u6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+u1bv/t1bb//fv4////////////////////
|
||||
///////////////////////////////////////////////////48+3/0ZRG/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0ZA8/8uNPNzurFXl7qxV/+6sVf/urFX/7qxV/+6sVf/rsWP/9OjZ////////////////////
|
||||
///////////////////////////////////////////////////////////////////YtIX/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/zo886O6sVeXurFX/7qxV/+6sVf/urFX/7qxW//Hiz///////////////
|
||||
/////////////////////////////////////////////////////////////////////////////+rb
|
||||
yP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/Ojzzo7qxV2e6sVf/urFX/7qxV/+6sVf/quHb/////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////+fXw/9CURf/QkDz/0JA8/9CQPP/QkDz/0ZA8/8yOPNzurFW/7qxV/+6sVf/urFX/7qxV/+nA
|
||||
if/s1LT/9Onb////////////////////////////////////////////////////////////////////
|
||||
////////////////////////0Z5b/9CQPP/QkDz/0JA8/9CQPP/SkTz/x4s8xe6sVY/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+nEk//+/fz////////////////////////////+/v3/+PHo//To2f/+/v7/////////
|
||||
///////////////////////////////////VqGz/zo46/8+PO//QkDz/0JA8/9KRPP+/hjyX7qxVUO6s
|
||||
Vf/urFX/7qxV/+6sVf/ssGH//Pr2///////////////////////y49D/6cOR/+qyaf/urVb/6rJp////
|
||||
/////////////////////////////////////////////+e7f//goEr/1ZVB/8+PO//Ojzv/0pE8/7WB
|
||||
PFfurFUQ7qxV6O6sVf/urFX/7qxV/+m+hv/////////////////z5tX/6rl6/+6sVf/urFX/7qxV/+6s
|
||||
Vf/qs2r////+////////////////////////////////////////////+PLp/+y4c//urFX/5aRO/9qZ
|
||||
RP/Mjjztp3g6FQAAAADurFWQ7qxV/+6sVf/urFX/6cKP////////////682m/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+2sV//17N////////////////////////////////////////7+/v/06Nn/8uTT/+qz
|
||||
a//wrVb/761W/+alUZYAAAAAAAAAAO6sVSHurFX07qxV/+6sVf/quXj//////+nDkf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+m+hP////////////////////////////////////////////jy
|
||||
6v/qtnH/67Fj/+6sVf/urFX2761WJQAAAAAAAAAAAAAAAO6sVX7urFX/7qxV/+2vW//pvID/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+nEkv/7+PT//////////////////fz6/+rJ
|
||||
n//rtW7/682n/+zTs//urFb/7qxV/+6sVYQAAAAAAAAAAAAAAAAAAAAA7qxVB+6sVcTurFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+u0bP/qy6H/7da4/+vN
|
||||
pv/qtnP/7qxV/+6sVf/urFX/7K9f/+2sWP/urFXI7qxVCQAAAAAAAAAAAAAAAAAAAAAAAAAA7qxVIO6s
|
||||
VeDurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV4+6sVSMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAA7qxVLO6sVd/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVeLurFUwAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAA7qxVIu6sVcHurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFXE7qxVJAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7qxVBu6sVX/urFXw7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFXy7qxVgu6sVQgAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO6sVSXurFWP7qxV5e6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFXl7qxVke6sVScAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AADurFUS7qxVUO6sVY7urFW97qxV2O6sVeXurFXl7qxV2O6sVb7urFWP7qxVUu6sVRMAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8AD//8AAP/8AAA/+AAAH/AAAA/gAAAHwAAAA8AA
|
||||
AAOAAAABgAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAA
|
||||
AAGAAAABwAAAA8AAAAPgAAAH8AAAD/gAAB/8AAA//wAA///AA/8oAAAAEAAAACAAAAABACAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKx7Oja9hTqTyYw8zs6PPOfOjzznyYw8zr6G
|
||||
PJSvfjw3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAo3Y7CbyEO5PRkDz805I8/9GRPP/QkDz/0JA8/9GR
|
||||
PP/TkTz/0ZA8/b+GPJWrfDwKAAAAAAAAAAAAAAAAp3g8CcSJPLfTkjz/0ZA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/Tkjz/xoo8uqx8PQoAAAAAAAAAAL2FPJPUk0D/06Rm/92/mf/izK//4sut/9u6
|
||||
j//Snlr/0JA8/9CQPP/QkDz/0JA8/9OSPP+9hTyWAAAAALiEQDLQkDz90JpS/93Amf/u4tP/////////
|
||||
/////////fv5/+DIqP/QkkH/0JA8/9CQPP/RkDz/0JA8/qN1OTTtq1WS56VP/9qaRf/RkT3/0JdO/+rc
|
||||
yP//////////////////////693L/9CSQf/QkDz/0JA8/9SSPP+3gTqW761Vz++tVv/wrVb/6K5h//fy
|
||||
6v/////////////////////////////////gx6b/0JA8/9CQPP/RkTz/yYw80e6sVefurFX/7qxV/+q+
|
||||
hP/69vD//////////////////////////////////fz7/9GbU//QkDz/0JA8/8+PPOnurFXn7qxV/+yw
|
||||
Y//79/P////////////////////////////////////////////ZuIz/0JA8/9CQPP/Pjzzp7qxVz+6s
|
||||
Vf/sr2H/8eLO/////////////v7+//n07f/+/v7/////////////////48yu/8+PO//Qjzv/yYs80e6s
|
||||
VZLurFX/67Jm//7+/f/8+fb/68yl/+qza//rsmf//v37//////////////////Pn2P/ip1n/2phC/8CH
|
||||
PJXurFUw7qxV/eq2c//169//6rNr/+6sVf/urFX/7q1W//Pm1f/////////////////8+fX/67+G/++t
|
||||
Vf3lpVEyAAAAAO6sVZHtrVr/7a9c/+6sVf/urFX/7qxV/+6sVf/rsWP/7de5//Lj0P/pxJL/67Zy/+qz
|
||||
av/urFWTAAAAAAAAAADurFUI7qxVtO6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFW37qxVCQAAAAAAAAAAAAAAAO6sVQjurFWQ7qxV++6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
VfvurFWS7qxVCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO6sVTPurFWQ7qxVzO6sVeburFXm7qxVzO6s
|
||||
VZDurFU0AAAAAAAAAAAAAAAAAAAAAPAPrEHAA6xBgAGsQYABrEEAAKxBAACsQQAArEEAAKxBAACsQQAA
|
||||
rEEAAKxBAACsQYABrEGAAaxBwAOsQfAPrEE=
|
||||
</value>
|
||||
<data name="trayIcon.TrayLocation" type="System.Drawing.Point, System.Drawing">
|
||||
<value>17, 17</value>
|
||||
</data>
|
||||
<data name="toolTip.TrayLocation" type="System.Drawing.Point, System.Drawing">
|
||||
<value>112, 17</value>
|
||||
</data>
|
||||
</root>
|
11
Core/FormNotification.Designer.cs
generated
11
Core/FormNotification.Designer.cs
generated
@@ -1,7 +1,7 @@
|
||||
using TweetDck.Core.Controls;
|
||||
|
||||
namespace TweetDck.Core {
|
||||
sealed partial class FormNotification {
|
||||
partial class FormNotification {
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
@@ -30,6 +30,7 @@ namespace TweetDck.Core {
|
||||
this.timerProgress = new System.Windows.Forms.Timer(this.components);
|
||||
this.progressBarTimer = new TweetDck.Core.Controls.FlatProgressBar();
|
||||
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
||||
this.timerDisplayDelay = new System.Windows.Forms.Timer(this.components);
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// panelBrowser
|
||||
@@ -61,6 +62,11 @@ namespace TweetDck.Core {
|
||||
this.progressBarTimer.Size = new System.Drawing.Size(284, 4);
|
||||
this.progressBarTimer.TabIndex = 1;
|
||||
//
|
||||
// timerDisplayDelay
|
||||
//
|
||||
this.timerDisplayDelay.Interval = 17;
|
||||
this.timerDisplayDelay.Tick += new System.EventHandler(this.timerDisplayDelay_Tick);
|
||||
//
|
||||
// FormNotification
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
@@ -70,7 +76,7 @@ namespace TweetDck.Core {
|
||||
this.Controls.Add(this.progressBarTimer);
|
||||
this.Controls.Add(this.panelBrowser);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
|
||||
this.Location = new System.Drawing.Point(-32000, -32000);
|
||||
this.Location = TweetDck.Core.Controls.ControlExtensions.InvisibleLocation;
|
||||
this.MaximizeBox = false;
|
||||
this.MinimizeBox = false;
|
||||
this.Name = "FormNotification";
|
||||
@@ -88,5 +94,6 @@ namespace TweetDck.Core {
|
||||
private Controls.FlatProgressBar progressBarTimer;
|
||||
private System.Windows.Forms.Timer timerProgress;
|
||||
private System.Windows.Forms.ToolTip toolTip;
|
||||
private System.Windows.Forms.Timer timerDisplayDelay;
|
||||
}
|
||||
}
|
@@ -5,13 +5,17 @@ using System.Windows.Forms;
|
||||
using CefSharp;
|
||||
using CefSharp.WinForms;
|
||||
using TweetDck.Configuration;
|
||||
using TweetDck.Core.Bridge;
|
||||
using TweetDck.Core.Handling;
|
||||
using TweetDck.Resources;
|
||||
using TweetDck.Core.Utils;
|
||||
using TweetDck.Plugins;
|
||||
using TweetDck.Plugins.Enums;
|
||||
using TweetDck.Core.Controls;
|
||||
using TweetDck.Core.Notification;
|
||||
|
||||
namespace TweetDck.Core{
|
||||
sealed partial class FormNotification : Form{
|
||||
partial class FormNotification : Form{
|
||||
private const string NotificationScriptFile = "notification.js";
|
||||
|
||||
private static readonly string NotificationScriptIdentifier = ScriptLoader.GetRootIdentifier(NotificationScriptFile);
|
||||
@@ -19,14 +23,32 @@ namespace TweetDck.Core{
|
||||
|
||||
public Func<bool> CanMoveWindow = () => true;
|
||||
|
||||
private readonly Form owner;
|
||||
public bool IsNotificationVisible{
|
||||
get{
|
||||
return Location != ControlExtensions.InvisibleLocation;
|
||||
}
|
||||
}
|
||||
|
||||
public new Point Location{
|
||||
get{
|
||||
return base.Location;
|
||||
}
|
||||
|
||||
set{
|
||||
Visible = (base.Location = value) != ControlExtensions.InvisibleLocation;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Control owner;
|
||||
private readonly PluginManager plugins;
|
||||
private readonly TrayIcon trayIcon;
|
||||
private readonly ChromiumWebBrowser browser;
|
||||
protected readonly NotificationFlags flags;
|
||||
protected readonly ChromiumWebBrowser browser;
|
||||
|
||||
private readonly Queue<TweetNotification> tweetQueue = new Queue<TweetNotification>(4);
|
||||
private readonly bool autoHide;
|
||||
private int timeLeft, totalTime;
|
||||
|
||||
private readonly NativeMethods.HookProc mouseHookDelegate;
|
||||
private IntPtr mouseHook;
|
||||
|
||||
private bool? prevDisplayTimer;
|
||||
private int? prevFontSize;
|
||||
@@ -60,9 +82,18 @@ namespace TweetDck.Core{
|
||||
public bool FreezeTimer { get; set; }
|
||||
public bool ContextMenuOpen { get; set; }
|
||||
public string CurrentUrl { get; private set; }
|
||||
public string CurrentQuotedTweetUrl { get; set; }
|
||||
|
||||
public EventHandler Initialized;
|
||||
private bool isInitialized;
|
||||
|
||||
private int pauseCounter;
|
||||
private bool pausedDuringNotification;
|
||||
|
||||
public bool IsPaused{
|
||||
get{
|
||||
return pauseCounter > 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static int BaseClientWidth{
|
||||
get{
|
||||
@@ -78,38 +109,52 @@ namespace TweetDck.Core{
|
||||
}
|
||||
}
|
||||
|
||||
public FormNotification(FormBrowser owner, PluginManager plugins, TrayIcon trayIcon, bool autoHide){
|
||||
public FormNotification(FormBrowser owner, PluginManager pluginManager, NotificationFlags flags){
|
||||
InitializeComponent();
|
||||
|
||||
Text = Program.BrandName;
|
||||
|
||||
this.owner = owner;
|
||||
this.plugins = plugins;
|
||||
this.trayIcon = trayIcon;
|
||||
this.autoHide = autoHide;
|
||||
this.plugins = pluginManager;
|
||||
this.flags = flags;
|
||||
|
||||
owner.FormClosed += (sender, args) => Close();
|
||||
|
||||
notificationJS = ScriptLoader.LoadResource(NotificationScriptFile);
|
||||
pluginJS = ScriptLoader.LoadResource(PluginManager.PluginNotificationScriptFile);
|
||||
|
||||
browser = new ChromiumWebBrowser("about:blank"){
|
||||
MenuHandler = new ContextMenuNotification(this,autoHide),
|
||||
MenuHandler = new ContextMenuNotification(this, !flags.HasFlag(NotificationFlags.DisableContextMenu)),
|
||||
LifeSpanHandler = new LifeSpanHandler()
|
||||
};
|
||||
|
||||
#if DEBUG
|
||||
browser.ConsoleMessage += BrowserUtils.HandleConsoleMessage;
|
||||
#endif
|
||||
|
||||
browser.IsBrowserInitializedChanged += Browser_IsBrowserInitializedChanged;
|
||||
browser.LoadingStateChanged += Browser_LoadingStateChanged;
|
||||
browser.FrameLoadEnd += Browser_FrameLoadEnd;
|
||||
browser.RegisterJsObject("$TD",new TweetDeckBridge(owner,this));
|
||||
|
||||
if (!flags.HasFlag(NotificationFlags.DisableScripts)){
|
||||
notificationJS = ScriptLoader.LoadResource(NotificationScriptFile);
|
||||
browser.RegisterAsyncJsObject("$TD", new TweetDeckBridge(owner, this));
|
||||
|
||||
if (plugins != null){
|
||||
pluginJS = ScriptLoader.LoadResource(PluginManager.PluginNotificationScriptFile);
|
||||
browser.RegisterAsyncJsObject("$TDP", plugins.Bridge);
|
||||
}
|
||||
}
|
||||
|
||||
panelBrowser.Controls.Add(browser);
|
||||
|
||||
if (autoHide){
|
||||
if (flags.HasFlag(NotificationFlags.AutoHide)){
|
||||
Program.UserConfig.MuteToggled += Config_MuteToggled;
|
||||
Disposed += (sender, args) => Program.UserConfig.MuteToggled -= Config_MuteToggled;
|
||||
|
||||
if (Program.UserConfig.MuteNotifications){
|
||||
PauseNotification();
|
||||
}
|
||||
}
|
||||
|
||||
Disposed += (sender, args) => browser.Dispose();
|
||||
mouseHookDelegate = MouseHookProc;
|
||||
|
||||
Disposed += FormNotification_Disposed;
|
||||
}
|
||||
|
||||
protected override void WndProc(ref Message m){
|
||||
@@ -120,13 +165,47 @@ namespace TweetDck.Core{
|
||||
base.WndProc(ref m);
|
||||
}
|
||||
|
||||
// mouse wheel hook
|
||||
|
||||
private void StartMouseHook(){
|
||||
if (mouseHook == IntPtr.Zero){
|
||||
mouseHook = NativeMethods.SetWindowsHookEx(NativeMethods.WH_MOUSE_LL, mouseHookDelegate, IntPtr.Zero, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void StopMouseHook(){
|
||||
if (mouseHook != IntPtr.Zero){
|
||||
NativeMethods.UnhookWindowsHookEx(mouseHook);
|
||||
mouseHook = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
private IntPtr MouseHookProc(int nCode, IntPtr wParam, IntPtr lParam){
|
||||
if (!ContainsFocus && wParam.ToInt32() == NativeMethods.WH_MOUSEWHEEL && browser.Bounds.Contains(PointToClient(Cursor.Position))){
|
||||
// fuck it, Activate() doesn't work with this
|
||||
Point prevPos = Cursor.Position;
|
||||
Cursor.Position = PointToScreen(new Point(0, -1));
|
||||
NativeMethods.SimulateMouseClick(NativeMethods.MouseButton.Left);
|
||||
Cursor.Position = prevPos;
|
||||
}
|
||||
|
||||
return NativeMethods.CallNextHookEx(mouseHook, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
// event handlers
|
||||
|
||||
private void timerDisplayDelay_Tick(object sender, EventArgs e){
|
||||
OnNotificationReady();
|
||||
timerDisplayDelay.Stop();
|
||||
}
|
||||
|
||||
private void timerHideProgress_Tick(object sender, EventArgs e){
|
||||
if (Bounds.Contains(Cursor.Position) || FreezeTimer || ContextMenuOpen)return;
|
||||
|
||||
timeLeft -= timerProgress.Interval;
|
||||
progressBarTimer.SetValueInstant((int)Math.Min(1000,Math.Round(1025.0*(totalTime-timeLeft)/totalTime)));
|
||||
|
||||
int value = (int)Math.Round(1025.0*(totalTime-timeLeft)/totalTime);
|
||||
progressBarTimer.SetValueInstant(Math.Min(1000, Math.Max(0, Program.UserConfig.NotificationTimerCountDown ? 1000-value : value)));
|
||||
|
||||
if (timeLeft <= 0){
|
||||
FinishCurrentTweet();
|
||||
@@ -135,39 +214,37 @@ namespace TweetDck.Core{
|
||||
|
||||
private void Config_MuteToggled(object sender, EventArgs e){
|
||||
if (Program.UserConfig.MuteNotifications){
|
||||
HideNotification(true);
|
||||
PauseNotification();
|
||||
}
|
||||
else{
|
||||
if (tweetQueue.Count > 0){
|
||||
LoadNextNotification();
|
||||
}
|
||||
|
||||
trayIcon.HasNotifications = false;
|
||||
ResumeNotification();
|
||||
}
|
||||
}
|
||||
|
||||
private void Browser_IsBrowserInitializedChanged(object sender, IsBrowserInitializedChangedEventArgs e){
|
||||
if (e.IsBrowserInitialized && Initialized != null){
|
||||
Initialized(this,new EventArgs());
|
||||
Initialized(this, new EventArgs());
|
||||
}
|
||||
}
|
||||
|
||||
private void Browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
|
||||
if (!e.IsLoading && browser.Address != "about:blank" && !flags.HasFlag(NotificationFlags.ManualDisplay)){
|
||||
this.InvokeSafe(() => {
|
||||
Visible = true; // ensures repaint before moving the window to a visible location
|
||||
timerDisplayDelay.Start();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
|
||||
if (!e.Frame.IsMain)return;
|
||||
if (e.Frame.IsMain && notificationJS != null && browser.Address != "about:blank" && !flags.HasFlag(NotificationFlags.DisableScripts)){
|
||||
e.Frame.ExecuteJavaScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Properties.ExpandLinksOnHover));
|
||||
ScriptLoader.ExecuteScript(e.Frame, notificationJS, NotificationScriptIdentifier);
|
||||
|
||||
if (!isInitialized){
|
||||
isInitialized = true;
|
||||
|
||||
if (Initialized != null){
|
||||
Initialized(this,new EventArgs());
|
||||
}
|
||||
}
|
||||
else if (notificationJS != null && browser.Address != "about:blank"){
|
||||
ScriptLoader.ExecuteScript(e.Frame,notificationJS,NotificationScriptIdentifier);
|
||||
|
||||
if (plugins.HasAnyPlugin(PluginEnvironment.Notification)){
|
||||
ScriptLoader.ExecuteScript(e.Frame,pluginJS,PluginScriptIdentifier);
|
||||
plugins.ExecutePlugins(e.Frame,PluginEnvironment.Notification,false);
|
||||
if (plugins != null && plugins.HasAnyPlugin(PluginEnvironment.Notification)){
|
||||
ScriptLoader.ExecuteScript(e.Frame, pluginJS, PluginScriptIdentifier);
|
||||
ScriptLoader.ExecuteFile(e.Frame, PluginManager.PluginGlobalScriptFile);
|
||||
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Notification, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -180,18 +257,22 @@ namespace TweetDck.Core{
|
||||
}
|
||||
}
|
||||
|
||||
private void FormNotification_Disposed(object sender, EventArgs e){
|
||||
browser.Dispose();
|
||||
StopMouseHook();
|
||||
}
|
||||
|
||||
// notification methods
|
||||
|
||||
public void ShowNotification(TweetNotification notification){
|
||||
if (Program.UserConfig.MuteNotifications){
|
||||
if (IsPaused){
|
||||
tweetQueue.Enqueue(notification);
|
||||
trayIcon.HasNotifications = true;
|
||||
}
|
||||
else{
|
||||
tweetQueue.Enqueue(notification);
|
||||
UpdateTitle();
|
||||
|
||||
if (!timerProgress.Enabled){
|
||||
if (totalTime == 0){
|
||||
LoadNextNotification();
|
||||
}
|
||||
}
|
||||
@@ -202,31 +283,28 @@ namespace TweetDck.Core{
|
||||
LoadTweet(TweetNotification.ExampleTweet);
|
||||
}
|
||||
else{
|
||||
MoveToVisibleLocation();
|
||||
PrepareAndDisplayWindow();
|
||||
}
|
||||
}
|
||||
|
||||
public void HideNotification(bool loadBlank){
|
||||
if (loadBlank){
|
||||
browser.LoadHtml("","about:blank");
|
||||
browser.LoadHtml("", "about:blank");
|
||||
}
|
||||
|
||||
Location = new Point(-32000,-32000);
|
||||
progressBarTimer.Value = 0;
|
||||
Location = ControlExtensions.InvisibleLocation;
|
||||
progressBarTimer.Value = Program.UserConfig.NotificationTimerCountDown ? 1000 : 0;
|
||||
timerProgress.Stop();
|
||||
}
|
||||
totalTime = 0;
|
||||
|
||||
public void OnNotificationReady(){
|
||||
UpdateTitle();
|
||||
MoveToVisibleLocation();
|
||||
timerProgress.Start();
|
||||
StopMouseHook();
|
||||
}
|
||||
|
||||
public void FinishCurrentTweet(){
|
||||
if (tweetQueue.Count > 0){
|
||||
LoadNextNotification();
|
||||
}
|
||||
else if (autoHide){
|
||||
else if (flags.HasFlag(NotificationFlags.AutoHide)){
|
||||
HideNotification(true);
|
||||
}
|
||||
else{
|
||||
@@ -234,67 +312,101 @@ namespace TweetDck.Core{
|
||||
}
|
||||
}
|
||||
|
||||
public void PauseNotification(){
|
||||
if (pauseCounter++ == 0){
|
||||
pausedDuringNotification = IsNotificationVisible;
|
||||
|
||||
if (IsNotificationVisible){
|
||||
Location = ControlExtensions.InvisibleLocation;
|
||||
timerProgress.Stop();
|
||||
StopMouseHook();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ResumeNotification(){
|
||||
if (pauseCounter > 0 && --pauseCounter == 0){
|
||||
if (pausedDuringNotification){
|
||||
OnNotificationReady();
|
||||
}
|
||||
else if (tweetQueue.Count > 0){
|
||||
LoadNextNotification();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadNextNotification(){
|
||||
LoadTweet(tweetQueue.Dequeue());
|
||||
}
|
||||
|
||||
private void LoadTweet(TweetNotification tweet){
|
||||
CurrentUrl = tweet.Url;
|
||||
CurrentQuotedTweetUrl = string.Empty; // load from JS
|
||||
|
||||
timerProgress.Stop();
|
||||
totalTime = timeLeft = tweet.GetDisplayDuration(Program.UserConfig.NotificationDuration);
|
||||
progressBarTimer.Value = 0;
|
||||
totalTime = timeLeft = tweet.GetDisplayDuration(Program.UserConfig.NotificationDurationValue);
|
||||
progressBarTimer.Value = Program.UserConfig.NotificationTimerCountDown ? 1000 : 0;
|
||||
|
||||
browser.LoadHtml(tweet.GenerateHtml(),"http://tweetdeck.twitter.com/?"+DateTime.Now.Ticks);
|
||||
string bodyClasses = browser.Bounds.Contains(PointToClient(Cursor.Position)) ? "td-hover" : string.Empty;
|
||||
|
||||
browser.LoadHtml(tweet.GenerateHtml(bodyClasses), "http://tweetdeck.twitter.com/?"+DateTime.Now.Ticks);
|
||||
}
|
||||
|
||||
private void MoveToVisibleLocation(){
|
||||
UserConfig config = Program.UserConfig;
|
||||
|
||||
private void PrepareAndDisplayWindow(){
|
||||
if (RequiresResize){
|
||||
RequiresResize = false;
|
||||
|
||||
if (config.DisplayNotificationTimer){
|
||||
ClientSize = new Size(BaseClientWidth,BaseClientHeight+4);
|
||||
progressBarTimer.Visible = true;
|
||||
}
|
||||
else{
|
||||
ClientSize = new Size(BaseClientWidth,BaseClientHeight);
|
||||
progressBarTimer.Visible = false;
|
||||
}
|
||||
|
||||
panelBrowser.Height = BaseClientHeight;
|
||||
SetNotificationSize(BaseClientWidth, BaseClientHeight, Program.UserConfig.DisplayNotificationTimer);
|
||||
}
|
||||
|
||||
MoveToVisibleLocation();
|
||||
StartMouseHook();
|
||||
}
|
||||
|
||||
protected void SetNotificationSize(int width, int height, bool displayTimer){
|
||||
if (displayTimer){
|
||||
ClientSize = new Size(width, height+4);
|
||||
progressBarTimer.Visible = true;
|
||||
}
|
||||
else{
|
||||
ClientSize = new Size(width, height);
|
||||
progressBarTimer.Visible = false;
|
||||
}
|
||||
|
||||
panelBrowser.Height = height;
|
||||
}
|
||||
|
||||
protected void MoveToVisibleLocation(){
|
||||
UserConfig config = Program.UserConfig;
|
||||
|
||||
Screen screen = Screen.FromControl(owner);
|
||||
|
||||
if (config.NotificationDisplay > 0 && config.NotificationDisplay <= Screen.AllScreens.Length){
|
||||
screen = Screen.AllScreens[config.NotificationDisplay-1];
|
||||
}
|
||||
|
||||
bool needsReactivating = Location.X == -32000;
|
||||
bool needsReactivating = Location == ControlExtensions.InvisibleLocation;
|
||||
int edgeDist = config.NotificationEdgeDistance;
|
||||
|
||||
switch(config.NotificationPosition){
|
||||
case TweetNotification.Position.TopLeft:
|
||||
Location = new Point(screen.WorkingArea.X+edgeDist,screen.WorkingArea.Y+edgeDist);
|
||||
Location = new Point(screen.WorkingArea.X+edgeDist, screen.WorkingArea.Y+edgeDist);
|
||||
break;
|
||||
|
||||
case TweetNotification.Position.TopRight:
|
||||
Location = new Point(screen.WorkingArea.X+screen.WorkingArea.Width-edgeDist-Width,screen.WorkingArea.Y+edgeDist);
|
||||
Location = new Point(screen.WorkingArea.X+screen.WorkingArea.Width-edgeDist-Width, screen.WorkingArea.Y+edgeDist);
|
||||
break;
|
||||
|
||||
case TweetNotification.Position.BottomLeft:
|
||||
Location = new Point(screen.WorkingArea.X+edgeDist,screen.WorkingArea.Y+screen.WorkingArea.Height-edgeDist-Height);
|
||||
Location = new Point(screen.WorkingArea.X+edgeDist, screen.WorkingArea.Y+screen.WorkingArea.Height-edgeDist-Height);
|
||||
break;
|
||||
|
||||
case TweetNotification.Position.BottomRight:
|
||||
Location = new Point(screen.WorkingArea.X+screen.WorkingArea.Width-edgeDist-Width,screen.WorkingArea.Y+screen.WorkingArea.Height-edgeDist-Height);
|
||||
Location = new Point(screen.WorkingArea.X+screen.WorkingArea.Width-edgeDist-Width, screen.WorkingArea.Y+screen.WorkingArea.Height-edgeDist-Height);
|
||||
break;
|
||||
|
||||
case TweetNotification.Position.Custom:
|
||||
if (!config.IsCustomNotificationPositionSet){
|
||||
config.CustomNotificationPosition = new Point(screen.WorkingArea.X+screen.WorkingArea.Width-edgeDist-Width,screen.WorkingArea.Y+edgeDist);
|
||||
config.CustomNotificationPosition = new Point(screen.WorkingArea.X+screen.WorkingArea.Width-edgeDist-Width, screen.WorkingArea.Y+edgeDist);
|
||||
config.Save();
|
||||
}
|
||||
|
||||
@@ -302,23 +414,29 @@ namespace TweetDck.Core{
|
||||
break;
|
||||
}
|
||||
|
||||
if (needsReactivating){
|
||||
NativeMethods.SetFormPos(this,NativeMethods.HWND_TOPMOST,NativeMethods.SWP_NOACTIVATE);
|
||||
if (needsReactivating && flags.HasFlag(NotificationFlags.TopMost)){
|
||||
NativeMethods.SetFormPos(this, NativeMethods.HWND_TOPMOST, NativeMethods.SWP_NOACTIVATE);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTitle(){
|
||||
protected void UpdateTitle(){
|
||||
Text = tweetQueue.Count > 0 ? Program.BrandName+" ("+tweetQueue.Count+" more left)" : Program.BrandName;
|
||||
}
|
||||
|
||||
protected void OnNotificationReady(){
|
||||
UpdateTitle();
|
||||
PrepareAndDisplayWindow();
|
||||
timerProgress.Start();
|
||||
}
|
||||
|
||||
public void DisplayTooltip(string text){
|
||||
if (string.IsNullOrEmpty(text)){
|
||||
toolTip.Hide(this);
|
||||
}
|
||||
else{
|
||||
Point position = PointToClient(Cursor.Position);
|
||||
position.Offset(20,5);
|
||||
toolTip.Show(text,this,position);
|
||||
position.Offset(20, 5);
|
||||
toolTip.Show(text, this, position); // TODO figure out flickering when moving the mouse
|
||||
}
|
||||
}
|
||||
}
|
||||
|
16
Core/Handling/BrowserProcessHandler.cs
Normal file
16
Core/Handling/BrowserProcessHandler.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using CefSharp;
|
||||
using System;
|
||||
|
||||
namespace TweetDck.Core.Handling{
|
||||
class BrowserProcessHandler : IBrowserProcessHandler{
|
||||
void IBrowserProcessHandler.OnContextInitialized(){
|
||||
using(IRequestContext ctx = Cef.GetGlobalRequestContext()){
|
||||
string err;
|
||||
ctx.SetPreference("browser.enable_spellchecking", Program.UserConfig.EnableSpellCheck, out err);
|
||||
}
|
||||
}
|
||||
|
||||
void IBrowserProcessHandler.OnScheduleMessagePumpWork(long delay){}
|
||||
void IDisposable.Dispose(){}
|
||||
}
|
||||
}
|
@@ -2,42 +2,58 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
using TweetDck.Core.Bridge;
|
||||
using TweetDck.Core.Controls;
|
||||
using TweetDck.Core.Utils;
|
||||
|
||||
namespace TweetDck.Core.Handling{
|
||||
abstract class ContextMenuBase : IContextMenuHandler{
|
||||
private const int MenuOpenUrlInBrowser = 26500;
|
||||
private const int MenuCopyUrl = 26501;
|
||||
private const int MenuOpenImageInBrowser = 26502;
|
||||
private const int MenuOpenLinkUrl = 26500;
|
||||
private const int MenuCopyLinkUrl = 26501;
|
||||
private const int MenuOpenImage = 26502;
|
||||
private const int MenuSaveImage = 26503;
|
||||
private const int MenuCopyImageUrl = 26504;
|
||||
|
||||
#if DEBUG
|
||||
private const int MenuOpenDevTools = 26599;
|
||||
|
||||
protected void AddDebugMenuItems(IMenuModel model){
|
||||
model.AddItem((CefMenuCommand)MenuOpenDevTools, "Open dev tools");
|
||||
}
|
||||
#endif
|
||||
|
||||
private readonly Form form;
|
||||
|
||||
protected ContextMenuBase(Form form){
|
||||
this.form = form;
|
||||
}
|
||||
|
||||
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)){
|
||||
model.AddItem((CefMenuCommand)MenuOpenUrlInBrowser,"Open in browser");
|
||||
model.AddItem((CefMenuCommand)MenuCopyUrl,"Copy link address");
|
||||
if (parameters.TypeFlags.HasFlag(ContextMenuType.Link) && !parameters.UnfilteredLinkUrl.EndsWith("tweetdeck.twitter.com/#", StringComparison.Ordinal)){
|
||||
model.AddItem((CefMenuCommand)MenuOpenLinkUrl, "Open link in browser");
|
||||
model.AddItem((CefMenuCommand)MenuCopyLinkUrl, "Copy link address");
|
||||
model.AddSeparator();
|
||||
}
|
||||
|
||||
if (parameters.TypeFlags.HasFlag(ContextMenuType.Media) && parameters.HasImageContents){
|
||||
model.AddItem((CefMenuCommand)MenuOpenImageInBrowser,"Open image in browser");
|
||||
model.AddItem((CefMenuCommand)MenuSaveImage,"Save image as...");
|
||||
model.AddItem((CefMenuCommand)MenuCopyImageUrl,"Copy image URL");
|
||||
model.AddItem((CefMenuCommand)MenuOpenImage, "Open image in browser");
|
||||
model.AddItem((CefMenuCommand)MenuSaveImage, "Save image as...");
|
||||
model.AddItem((CefMenuCommand)MenuCopyImageUrl, "Copy image address");
|
||||
model.AddSeparator();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){
|
||||
switch((int)commandId){
|
||||
case MenuOpenUrlInBrowser:
|
||||
case MenuOpenLinkUrl:
|
||||
BrowserUtils.OpenExternalBrowser(parameters.LinkUrl);
|
||||
break;
|
||||
|
||||
case MenuCopyUrl:
|
||||
Clipboard.SetText(string.IsNullOrEmpty(TweetDeckBridge.LastRightClickedLink) ? parameters.UnfilteredLinkUrl : TweetDeckBridge.LastRightClickedLink,TextDataFormat.UnicodeText);
|
||||
case MenuCopyLinkUrl:
|
||||
SetClipboardText(string.IsNullOrEmpty(TweetDeckBridge.LastRightClickedLink) ? parameters.UnfilteredLinkUrl : TweetDeckBridge.LastRightClickedLink);
|
||||
break;
|
||||
|
||||
case MenuOpenImageInBrowser:
|
||||
case MenuOpenImage:
|
||||
BrowserUtils.OpenExternalBrowser(parameters.SourceUrl);
|
||||
break;
|
||||
|
||||
@@ -57,16 +73,22 @@ namespace TweetDck.Core.Handling{
|
||||
}
|
||||
|
||||
if (saveTarget != null){
|
||||
BrowserUtils.DownloadFileAsync(parameters.SourceUrl,saveTarget,ex => {
|
||||
MessageBox.Show("An error occurred while downloading the image: "+ex.Message,Program.BrandName+" Has Failed :(",MessageBoxButtons.OK,MessageBoxIcon.Error);
|
||||
BrowserUtils.DownloadFileAsync(parameters.SourceUrl, saveTarget, ex => {
|
||||
MessageBox.Show("An error occurred while downloading the image: "+ex.Message, Program.BrandName+" Has Failed :(", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
});
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case MenuCopyImageUrl:
|
||||
Clipboard.SetText(parameters.SourceUrl,TextDataFormat.UnicodeText);
|
||||
SetClipboardText(parameters.SourceUrl);
|
||||
break;
|
||||
|
||||
#if DEBUG
|
||||
case MenuOpenDevTools:
|
||||
browserControl.ShowDevTools();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -78,21 +100,31 @@ namespace TweetDck.Core.Handling{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void SetClipboardText(string text){
|
||||
form.InvokeAsyncSafe(() => WindowsUtils.SetClipboard(text, TextDataFormat.UnicodeText));
|
||||
}
|
||||
|
||||
protected static void RemoveSeparatorIfLast(IMenuModel model){
|
||||
if (model.Count > 0 && model.GetTypeAt(model.Count-1) == MenuItemType.Separator){
|
||||
model.RemoveAt(model.Count-1);
|
||||
}
|
||||
}
|
||||
|
||||
protected static void AddSeparator(IMenuModel model){
|
||||
if (model.Count > 0 && model.GetTypeAt(model.Count-1) != MenuItemType.Separator){ // do not add separators if there is nothing to separate
|
||||
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);
|
||||
int colon = url.IndexOf(':', dot);
|
||||
|
||||
if (colon != -1){
|
||||
url = url.Substring(0,colon);
|
||||
url = url.Substring(0, colon);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,19 +1,28 @@
|
||||
using CefSharp;
|
||||
using System.Windows.Forms;
|
||||
using TweetDck.Core.Bridge;
|
||||
using TweetDck.Core.Controls;
|
||||
using TweetDck.Core.Utils;
|
||||
|
||||
namespace TweetDck.Core.Handling{
|
||||
class ContextMenuBrowser : ContextMenuBase{
|
||||
private const int MenuSettings = 26600;
|
||||
private const int MenuPlugins = 26005;
|
||||
private const int MenuAbout = 26601;
|
||||
private const int MenuMute = 26602;
|
||||
private const int MenuCopyTweetUrl = 26603;
|
||||
private const int MenuCopyTweetEmbeddedUrl = 26604;
|
||||
private const int MenuGlobal = 26600;
|
||||
private const int MenuMute = 26601;
|
||||
private const int MenuSettings = 26602;
|
||||
private const int MenuPlugins = 26003;
|
||||
private const int MenuAbout = 26604;
|
||||
|
||||
private const int MenuOpenTweetUrl = 26610;
|
||||
private const int MenuCopyTweetUrl = 26611;
|
||||
private const int MenuOpenQuotedTweetUrl = 26612;
|
||||
private const int MenuCopyQuotedTweetUrl = 26613;
|
||||
private const int MenuScreenshotTweet = 26614;
|
||||
|
||||
private readonly FormBrowser form;
|
||||
|
||||
public ContextMenuBrowser(FormBrowser form){
|
||||
private string lastHighlightedTweet;
|
||||
private string lastHighlightedQuotedTweet;
|
||||
|
||||
public ContextMenuBrowser(FormBrowser form) : base(form){
|
||||
this.form = form;
|
||||
}
|
||||
|
||||
@@ -24,65 +33,105 @@ namespace TweetDck.Core.Handling{
|
||||
model.Remove(CefMenuCommand.ViewSource);
|
||||
RemoveSeparatorIfLast(model);
|
||||
|
||||
if (!string.IsNullOrEmpty(TweetDeckBridge.LastHighlightedTweet)){
|
||||
model.AddItem((CefMenuCommand)MenuCopyTweetUrl,"Copy tweet address");
|
||||
if (parameters.TypeFlags.HasFlag(ContextMenuType.Selection)){
|
||||
model.AddSeparator();
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(TweetDeckBridge.LastHighlightedTweetEmbedded)){
|
||||
model.AddItem((CefMenuCommand)MenuCopyTweetEmbeddedUrl,"Copy quoted tweet address");
|
||||
base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);
|
||||
|
||||
lastHighlightedTweet = TweetDeckBridge.LastHighlightedTweet;
|
||||
lastHighlightedQuotedTweet = TweetDeckBridge.LastHighlightedQuotedTweet;
|
||||
|
||||
if (!BrowserUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){
|
||||
lastHighlightedTweet = string.Empty;
|
||||
lastHighlightedQuotedTweet = string.Empty;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(lastHighlightedTweet) && (parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){
|
||||
model.AddItem((CefMenuCommand)MenuOpenTweetUrl, "Open tweet in browser");
|
||||
model.AddItem((CefMenuCommand)MenuCopyTweetUrl, "Copy tweet address");
|
||||
model.AddItem((CefMenuCommand)MenuScreenshotTweet, "Screenshot tweet to clipboard");
|
||||
|
||||
if (!string.IsNullOrEmpty(lastHighlightedQuotedTweet)){
|
||||
model.AddSeparator();
|
||||
model.AddItem((CefMenuCommand)MenuOpenQuotedTweetUrl, "Open quoted tweet in browser");
|
||||
model.AddItem((CefMenuCommand)MenuCopyQuotedTweetUrl, "Copy quoted tweet address");
|
||||
}
|
||||
|
||||
model.AddSeparator();
|
||||
}
|
||||
|
||||
base.OnBeforeContextMenu(browserControl,browser,frame,parameters,model);
|
||||
if ((parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){
|
||||
AddSeparator(model);
|
||||
|
||||
if (model.Count > 0){
|
||||
RemoveSeparatorIfLast(model);
|
||||
model.AddSeparator();
|
||||
}
|
||||
IMenuModel globalMenu = model.Count == 0 ? model : model.AddSubMenu((CefMenuCommand)MenuGlobal, Program.BrandName);
|
||||
|
||||
model.AddItem(CefMenuCommand.Reload,"Reload");
|
||||
model.AddCheckItem((CefMenuCommand)MenuMute,"Mute notifications");
|
||||
model.SetChecked((CefMenuCommand)MenuMute,Program.UserConfig.MuteNotifications);
|
||||
model.AddSeparator();
|
||||
globalMenu.AddItem(CefMenuCommand.Reload, "Reload browser");
|
||||
globalMenu.AddCheckItem((CefMenuCommand)MenuMute, "Mute notifications");
|
||||
globalMenu.SetChecked((CefMenuCommand)MenuMute, Program.UserConfig.MuteNotifications);
|
||||
globalMenu.AddSeparator();
|
||||
|
||||
model.AddItem((CefMenuCommand)MenuSettings,"Settings");
|
||||
model.AddItem((CefMenuCommand)MenuPlugins,"Plugins");
|
||||
model.AddItem((CefMenuCommand)MenuAbout,"About "+Program.BrandName);
|
||||
globalMenu.AddItem((CefMenuCommand)MenuSettings, "Settings");
|
||||
globalMenu.AddItem((CefMenuCommand)MenuPlugins, "Plugins");
|
||||
globalMenu.AddItem((CefMenuCommand)MenuAbout, "About "+Program.BrandName);
|
||||
|
||||
#if DEBUG
|
||||
globalMenu.AddSeparator();
|
||||
AddDebugMenuItems(globalMenu);
|
||||
#endif
|
||||
}
|
||||
|
||||
RemoveSeparatorIfLast(model);
|
||||
}
|
||||
|
||||
public override bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){
|
||||
if (base.OnContextMenuCommand(browserControl,browser,frame,parameters,commandId,eventFlags)){
|
||||
if (base.OnContextMenuCommand(browserControl, browser, frame, parameters, commandId, eventFlags)){
|
||||
return true;
|
||||
}
|
||||
|
||||
switch((int)commandId){
|
||||
case (int)CefMenuCommand.Reload:
|
||||
frame.ExecuteJavaScriptAsync("window.location.href = 'https://tweetdeck.twitter.com'");
|
||||
return true;
|
||||
|
||||
case MenuSettings:
|
||||
form.InvokeSafe(form.OpenSettings);
|
||||
form.InvokeAsyncSafe(form.OpenSettings);
|
||||
return true;
|
||||
|
||||
case MenuAbout:
|
||||
form.InvokeSafe(form.OpenAbout);
|
||||
form.InvokeAsyncSafe(form.OpenAbout);
|
||||
return true;
|
||||
|
||||
case MenuPlugins:
|
||||
form.InvokeSafe(form.OpenPlugins);
|
||||
form.InvokeAsyncSafe(form.OpenPlugins);
|
||||
return true;
|
||||
|
||||
case MenuMute:
|
||||
form.InvokeSafe(() => {
|
||||
form.InvokeAsyncSafe(() => {
|
||||
Program.UserConfig.MuteNotifications = !Program.UserConfig.MuteNotifications;
|
||||
Program.UserConfig.Save();
|
||||
});
|
||||
|
||||
return true;
|
||||
|
||||
case MenuCopyTweetUrl:
|
||||
Clipboard.SetText(TweetDeckBridge.LastHighlightedTweet,TextDataFormat.UnicodeText);
|
||||
case MenuOpenTweetUrl:
|
||||
BrowserUtils.OpenExternalBrowser(lastHighlightedTweet);
|
||||
return true;
|
||||
|
||||
case MenuCopyTweetEmbeddedUrl:
|
||||
Clipboard.SetText(TweetDeckBridge.LastHighlightedTweetEmbedded,TextDataFormat.UnicodeText);
|
||||
case MenuCopyTweetUrl:
|
||||
SetClipboardText(lastHighlightedTweet);
|
||||
return true;
|
||||
|
||||
case MenuScreenshotTweet:
|
||||
form.InvokeAsyncSafe(form.TriggerTweetScreenshot);
|
||||
return true;
|
||||
|
||||
case MenuOpenQuotedTweetUrl:
|
||||
BrowserUtils.OpenExternalBrowser(lastHighlightedQuotedTweet);
|
||||
return true;
|
||||
|
||||
case MenuCopyQuotedTweetUrl:
|
||||
SetClipboardText(lastHighlightedQuotedTweet);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,4 @@
|
||||
using System.Windows.Forms;
|
||||
using CefSharp;
|
||||
using CefSharp;
|
||||
using TweetDck.Core.Controls;
|
||||
|
||||
namespace TweetDck.Core.Handling{
|
||||
@@ -7,12 +6,12 @@ namespace TweetDck.Core.Handling{
|
||||
private const int MenuSkipTweet = 26600;
|
||||
private const int MenuFreeze = 26601;
|
||||
private const int MenuCopyTweetUrl = 26602;
|
||||
private const int MenuCopyTweetEmbeddedUrl = 26603;
|
||||
private const int MenuCopyQuotedTweetUrl = 26603;
|
||||
|
||||
private readonly FormNotification form;
|
||||
private readonly bool enableCustomMenu;
|
||||
|
||||
public ContextMenuNotification(FormNotification form, bool enableCustomMenu){
|
||||
public ContextMenuNotification(FormNotification form, bool enableCustomMenu) : base(form){
|
||||
this.form = form;
|
||||
this.enableCustomMenu = enableCustomMenu;
|
||||
}
|
||||
@@ -20,49 +19,59 @@ namespace TweetDck.Core.Handling{
|
||||
public override void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
|
||||
model.Clear();
|
||||
|
||||
if (parameters.TypeFlags.HasFlag(ContextMenuType.Selection)){
|
||||
model.AddItem(CefMenuCommand.Copy, "Copy");
|
||||
model.AddSeparator();
|
||||
}
|
||||
|
||||
base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);
|
||||
|
||||
if (enableCustomMenu){
|
||||
model.AddItem((CefMenuCommand)MenuSkipTweet,"Skip tweet");
|
||||
model.AddCheckItem((CefMenuCommand)MenuFreeze,"Freeze");
|
||||
model.SetChecked((CefMenuCommand)MenuFreeze,form.FreezeTimer);
|
||||
model.AddItem((CefMenuCommand)MenuSkipTweet, "Skip tweet");
|
||||
model.AddCheckItem((CefMenuCommand)MenuFreeze, "Freeze");
|
||||
model.SetChecked((CefMenuCommand)MenuFreeze, form.FreezeTimer);
|
||||
model.AddSeparator();
|
||||
|
||||
if (!string.IsNullOrEmpty(form.CurrentUrl)){
|
||||
model.AddItem((CefMenuCommand)MenuCopyTweetUrl,"Copy tweet address");
|
||||
model.AddItem((CefMenuCommand)MenuCopyTweetUrl, "Copy tweet address");
|
||||
|
||||
if (!string.IsNullOrEmpty(TweetDeckBridge.NotificationTweetEmbedded)){
|
||||
model.AddItem((CefMenuCommand)MenuCopyTweetEmbeddedUrl,"Copy quoted tweet address");
|
||||
if (!string.IsNullOrEmpty(form.CurrentQuotedTweetUrl)){
|
||||
model.AddItem((CefMenuCommand)MenuCopyQuotedTweetUrl, "Copy quoted tweet address");
|
||||
}
|
||||
|
||||
model.AddSeparator();
|
||||
}
|
||||
}
|
||||
|
||||
base.OnBeforeContextMenu(browserControl,browser,frame,parameters,model);
|
||||
#if DEBUG
|
||||
AddDebugMenuItems(model);
|
||||
#endif
|
||||
|
||||
RemoveSeparatorIfLast(model);
|
||||
|
||||
form.InvokeSafe(() => form.ContextMenuOpen = true);
|
||||
form.InvokeAsyncSafe(() => form.ContextMenuOpen = true);
|
||||
}
|
||||
|
||||
public override bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){
|
||||
if (base.OnContextMenuCommand(browserControl,browser,frame,parameters,commandId,eventFlags)){
|
||||
if (base.OnContextMenuCommand(browserControl, browser, frame, parameters, commandId, eventFlags)){
|
||||
return true;
|
||||
}
|
||||
|
||||
switch((int)commandId){
|
||||
case MenuSkipTweet:
|
||||
form.InvokeSafe(form.FinishCurrentTweet);
|
||||
form.InvokeAsyncSafe(form.FinishCurrentTweet);
|
||||
return true;
|
||||
|
||||
case MenuFreeze:
|
||||
form.InvokeSafe(() => form.FreezeTimer = !form.FreezeTimer);
|
||||
form.InvokeAsyncSafe(() => form.FreezeTimer = !form.FreezeTimer);
|
||||
return true;
|
||||
|
||||
case MenuCopyTweetUrl:
|
||||
Clipboard.SetText(form.CurrentUrl,TextDataFormat.UnicodeText);
|
||||
SetClipboardText(form.CurrentUrl);
|
||||
return true;
|
||||
|
||||
case MenuCopyTweetEmbeddedUrl:
|
||||
Clipboard.SetText(TweetDeckBridge.NotificationTweetEmbedded,TextDataFormat.UnicodeText);
|
||||
case MenuCopyQuotedTweetUrl:
|
||||
SetClipboardText(form.CurrentQuotedTweetUrl);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -70,8 +79,8 @@ namespace TweetDck.Core.Handling{
|
||||
}
|
||||
|
||||
public override void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame){
|
||||
base.OnContextMenuDismissed(browserControl,browser,frame);
|
||||
form.InvokeSafe(() => form.ContextMenuOpen = false);
|
||||
base.OnContextMenuDismissed(browserControl, browser, frame);
|
||||
form.InvokeAsyncSafe(() => form.ContextMenuOpen = false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,18 +1,19 @@
|
||||
using CefSharp;
|
||||
using System.Collections.Generic;
|
||||
using TweetDck.Core.Bridge;
|
||||
using TweetDck.Core.Controls;
|
||||
|
||||
namespace TweetDck.Core.Handling{
|
||||
class DialogHandlerBrowser : IDialogHandler{
|
||||
class FileDialogHandler : IDialogHandler{
|
||||
private readonly FormBrowser form;
|
||||
|
||||
public DialogHandlerBrowser(FormBrowser form){
|
||||
public FileDialogHandler(FormBrowser form){
|
||||
this.form = form;
|
||||
}
|
||||
|
||||
public bool OnFileDialog(IWebBrowser browserControl, IBrowser browser, CefFileDialogMode mode, string title, string defaultFilePath, List<string> acceptFilters, int selectedAcceptFilter, IFileDialogCallback callback){
|
||||
if (!string.IsNullOrEmpty(TweetDeckBridge.ClipboardImagePath)){
|
||||
callback.Continue(selectedAcceptFilter,new List<string>{ TweetDeckBridge.ClipboardImagePath });
|
||||
callback.Continue(selectedAcceptFilter, new List<string>{ TweetDeckBridge.ClipboardImagePath });
|
||||
|
||||
form.InvokeSafe(() => {
|
||||
TweetDeckBridge.ClipboardImagePath = string.Empty;
|
49
Core/Handling/JavaScriptDialogHandler.cs
Normal file
49
Core/Handling/JavaScriptDialogHandler.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using CefSharp;
|
||||
using CefSharp.WinForms;
|
||||
using System.Windows.Forms;
|
||||
using TweetDck.Core.Controls;
|
||||
using TweetDck.Core.Other;
|
||||
|
||||
namespace TweetDck.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){
|
||||
if (dialogType != CefJsDialogType.Alert && dialogType != CefJsDialogType.Confirm){
|
||||
return false;
|
||||
}
|
||||
|
||||
((ChromiumWebBrowser)browserControl).InvokeSafe(() => {
|
||||
FormMessage form = new FormMessage(Program.BrandName, messageText, MessageBoxIcon.None);
|
||||
Button btnConfirm;
|
||||
|
||||
if (dialogType == CefJsDialogType.Alert){
|
||||
btnConfirm = form.AddButton("OK");
|
||||
}
|
||||
else if (dialogType == CefJsDialogType.Confirm){
|
||||
form.AddButton("No");
|
||||
btnConfirm = form.AddButton("Yes");
|
||||
}
|
||||
else{
|
||||
return;
|
||||
}
|
||||
|
||||
if (form.ShowDialog() == DialogResult.OK && form.ClickedButton == btnConfirm){
|
||||
callback.Continue(true);
|
||||
}
|
||||
else{
|
||||
callback.Continue(false);
|
||||
}
|
||||
|
||||
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){}
|
||||
}
|
||||
}
|
@@ -7,10 +7,11 @@ namespace TweetDck.Core.Handling{
|
||||
newBrowser = null;
|
||||
|
||||
switch(targetDisposition){
|
||||
case WindowOpenDisposition.SingletonTab: // TODO remove when CefSharp is updated to 57; enums don't line up in 55
|
||||
case WindowOpenDisposition.NewBackgroundTab:
|
||||
case WindowOpenDisposition.NewForegroundTab:
|
||||
case WindowOpenDisposition.NewPopup:
|
||||
case WindowOpenDisposition.NewWindow:
|
||||
// TODO case WindowOpenDisposition.NewWindow:
|
||||
BrowserUtils.OpenExternalBrowser(targetUrl);
|
||||
return true;
|
||||
|
||||
|
13
Core/Notification/NotificationFlags.cs
Normal file
13
Core/Notification/NotificationFlags.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
|
||||
namespace TweetDck.Core.Notification{
|
||||
[Flags]
|
||||
public enum NotificationFlags{
|
||||
None = 0,
|
||||
AutoHide = 1,
|
||||
DisableScripts = 2,
|
||||
DisableContextMenu = 4,
|
||||
TopMost = 8,
|
||||
ManualDisplay = 16
|
||||
}
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using CefSharp;
|
||||
using TweetDck.Core.Bridge;
|
||||
using TweetDck.Core.Controls;
|
||||
using TweetDck.Resources;
|
||||
|
||||
namespace TweetDck.Core.Notification.Screenshot{
|
||||
sealed class FormNotificationScreenshotable : FormNotification{
|
||||
public FormNotificationScreenshotable(Action callback, FormBrowser owner, NotificationFlags flags) : base(owner, null, flags){
|
||||
browser.RegisterAsyncJsObject("$TD_NotificationScreenshot", new CallbackBridge(this, callback));
|
||||
|
||||
browser.FrameLoadEnd += (sender, args) => {
|
||||
if (args.Frame.IsMain && browser.Address != "about:blank"){
|
||||
ScriptLoader.ExecuteScript(args.Frame, "window.setTimeout($TD_NotificationScreenshot.trigger, 25)", "gen:screenshot");
|
||||
}
|
||||
};
|
||||
|
||||
UpdateTitle();
|
||||
}
|
||||
|
||||
public void LoadNotificationForScreenshot(TweetNotification tweet, int width, int height){
|
||||
browser.LoadHtml(tweet.GenerateHtml(enableCustomCSS: false), "http://tweetdeck.twitter.com/?"+DateTime.Now.Ticks);
|
||||
|
||||
Location = ControlExtensions.InvisibleLocation;
|
||||
FormBorderStyle = Program.UserConfig.ShowScreenshotBorder ? FormBorderStyle.FixedToolWindow : FormBorderStyle.None;
|
||||
|
||||
SetNotificationSize(width, height, false);
|
||||
}
|
||||
|
||||
public void TakeScreenshotAndHide(){
|
||||
MoveToVisibleLocation();
|
||||
Activate();
|
||||
SendKeys.SendWait("%{PRTSC}");
|
||||
Reset();
|
||||
}
|
||||
|
||||
public void Reset(){
|
||||
Location = ControlExtensions.InvisibleLocation;
|
||||
browser.LoadHtml("", "about:blank");
|
||||
}
|
||||
}
|
||||
}
|
45
Core/Notification/Screenshot/TweetScreenshotManager.cs
Normal file
45
Core/Notification/Screenshot/TweetScreenshotManager.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using TweetDck.Core.Utils;
|
||||
|
||||
namespace TweetDck.Core.Notification.Screenshot{
|
||||
sealed class TweetScreenshotManager : IDisposable{
|
||||
private readonly FormBrowser browser;
|
||||
private readonly FormNotificationScreenshotable screenshot;
|
||||
private readonly Timer timeout;
|
||||
|
||||
public TweetScreenshotManager(FormBrowser browser){
|
||||
this.browser = browser;
|
||||
|
||||
this.screenshot = new FormNotificationScreenshotable(Callback, browser, NotificationFlags.DisableScripts | NotificationFlags.DisableContextMenu | NotificationFlags.TopMost | NotificationFlags.ManualDisplay){
|
||||
CanMoveWindow = () => false
|
||||
};
|
||||
|
||||
this.timeout = WindowsUtils.CreateSingleTickTimer(10000);
|
||||
this.timeout.Tick += (sender, args) => screenshot.Reset();
|
||||
}
|
||||
|
||||
public void Trigger(string html, int width, int height){
|
||||
screenshot.LoadNotificationForScreenshot(new TweetNotification(html, string.Empty, 0), width, height);
|
||||
screenshot.Show();
|
||||
timeout.Start();
|
||||
}
|
||||
|
||||
private void Callback(){
|
||||
if (!timeout.Enabled){
|
||||
return;
|
||||
}
|
||||
|
||||
timeout.Stop();
|
||||
|
||||
browser.PauseNotification();
|
||||
screenshot.TakeScreenshotAndHide();
|
||||
browser.ResumeNotification();
|
||||
}
|
||||
|
||||
public void Dispose(){
|
||||
timeout.Dispose();
|
||||
screenshot.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
66
Core/Notification/SoundNotification.cs
Normal file
66
Core/Notification/SoundNotification.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Media;
|
||||
using System.Windows.Forms;
|
||||
using TweetDck.Core.Other;
|
||||
|
||||
namespace TweetDck.Core.Notification{
|
||||
class SoundNotification : IDisposable{
|
||||
private readonly FormBrowser browserForm;
|
||||
|
||||
private SoundPlayer notificationSound;
|
||||
private bool ignoreNotificationSoundError;
|
||||
|
||||
public SoundNotification(FormBrowser browserForm){
|
||||
this.browserForm = browserForm;
|
||||
}
|
||||
|
||||
public void Play(string file){
|
||||
if (notificationSound == null){
|
||||
notificationSound = new SoundPlayer{
|
||||
LoadTimeout = 5000
|
||||
};
|
||||
}
|
||||
|
||||
if (notificationSound.SoundLocation != file){
|
||||
notificationSound.SoundLocation = file;
|
||||
ignoreNotificationSoundError = false;
|
||||
}
|
||||
|
||||
try{
|
||||
notificationSound.Play();
|
||||
}catch(FileNotFoundException e){
|
||||
OnNotificationSoundError("File not found: "+e.FileName);
|
||||
}catch(InvalidOperationException){
|
||||
OnNotificationSoundError("File is not a valid sound file.");
|
||||
}catch(TimeoutException){
|
||||
OnNotificationSoundError("File took too long to load.");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnNotificationSoundError(string message){
|
||||
if (!ignoreNotificationSoundError){
|
||||
ignoreNotificationSoundError = true;
|
||||
|
||||
using(FormMessage form = new FormMessage("Notification Sound Error", "Could not play custom notification sound."+Environment.NewLine+message, MessageBoxIcon.Error)){
|
||||
form.AddButton("Ignore");
|
||||
|
||||
Button btnOpenSettings = form.AddButton("Open Settings");
|
||||
btnOpenSettings.Width += 16;
|
||||
btnOpenSettings.Location = new Point(btnOpenSettings.Location.X-16, btnOpenSettings.Location.Y);
|
||||
|
||||
if (form.ShowDialog() == DialogResult.OK && form.ClickedButton == btnOpenSettings){
|
||||
browserForm.OpenSettings(FormSettings.TabIndexNotification);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose(){
|
||||
if (notificationSound != null){
|
||||
notificationSound.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,22 +1,16 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace TweetDck.Core.Handling{
|
||||
namespace TweetDck.Core.Notification{
|
||||
sealed class TweetNotification{
|
||||
private static string FontSizeClass { get; set; }
|
||||
private static string HeadTag { get; set; }
|
||||
|
||||
private static string DefaultFontSizeClass{
|
||||
get{
|
||||
return "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'>";
|
||||
|
||||
private static string DefaultHeadTag{
|
||||
get{
|
||||
return @"<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'>";
|
||||
}
|
||||
}
|
||||
private const string FixedCSS = @"a[data-full-url]{word-break:break-all}.txt-base-smallest .badge-verified:before{height:13px!important}";
|
||||
private const string CustomCSS = @".scroll-styled-v::-webkit-scrollbar{width:8px}.scroll-styled-v::-webkit-scrollbar-thumb{border-radius:0}#td-skip{opacity:0;cursor:pointer;transition:opacity 0.15s ease}.td-hover #td-skip{opacity:0.75}#td-skip:hover{opacity:1}";
|
||||
|
||||
public static int FontSizeLevel{
|
||||
get{
|
||||
@@ -37,14 +31,19 @@ namespace TweetDck.Core.Handling{
|
||||
build.Append(@"<header class='tweet-header'>");
|
||||
build.Append(@"<time class='tweet-timestamp js-timestamp pull-right txt-mute'><a target='_blank' rel='url' href='https://twitter.com/chylexmc' class='txt-small'>0s</a></time>");
|
||||
build.Append(@"<a target='_blank' rel='user' href='https://twitter.com/chylexmc' class='account-link link-complex block'>");
|
||||
build.Append(@"<div class='obj-left item-img tweet-img'><img width='48' height='48' alt='chylexmc's avatar' src='https://pbs.twimg.com/profile_images/645532929930608642/J56NBJVY_normal.png' class='tweet-avatar avatar pull-right'></div>");
|
||||
build.Append(@"<div class='obj-left item-img tweet-img'><img width='48' height='48' alt='chylexmc's avatar' src='https://pbs.twimg.com/profile_images/765161905312980992/AhDP9iY-_normal.jpg' class='tweet-avatar avatar pull-right'></div>");
|
||||
build.Append(@"<div class='nbfc'><span class='account-inline txt-ellipsis'><b class='fullname link-complex-target'>chylex</b> <span class='username txt-mute'>@chylexmc</span></span></div>");
|
||||
build.Append(@"</a>");
|
||||
build.Append(@"</header>");
|
||||
build.Append(@"<div class='tweet-body'><p class='js-tweet-text tweet-text with-linebreaks'>This is an example tweet, which lets you test the location and duration of popup notifications.</p></div>");
|
||||
|
||||
#if DEBUG
|
||||
build.Append(@"<div style='margin-top:64px'>Scrollbar test padding...</div>");
|
||||
#endif
|
||||
|
||||
build.Append(@"</div></div></article>");
|
||||
|
||||
return new TweetNotification(build.ToString(),"",95);
|
||||
return new TweetNotification(build.ToString(), "", 95, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,10 +59,6 @@ namespace TweetDck.Core.Handling{
|
||||
TopLeft, TopRight, BottomLeft, BottomRight, Custom
|
||||
}
|
||||
|
||||
public enum Duration{
|
||||
Short, Medium, Long, VeryLong
|
||||
}
|
||||
|
||||
public string Url{
|
||||
get{
|
||||
return url;
|
||||
@@ -73,32 +68,46 @@ namespace TweetDck.Core.Handling{
|
||||
private readonly string html;
|
||||
private readonly string url;
|
||||
private readonly int characters;
|
||||
private readonly bool isExample;
|
||||
|
||||
public TweetNotification(string html, string url, int characters){
|
||||
public TweetNotification(string html, string url, int characters) : this(html, url, characters, false){}
|
||||
|
||||
private TweetNotification(string html, string url, int characters, bool isExample){
|
||||
this.html = html;
|
||||
this.url = url;
|
||||
this.characters = characters;
|
||||
this.isExample = isExample;
|
||||
}
|
||||
|
||||
public int GetDisplayDuration(Duration modifier){
|
||||
int multiplier;
|
||||
|
||||
switch(modifier){
|
||||
case Duration.Short: multiplier = 15; break;
|
||||
case Duration.Long: multiplier = 35; break;
|
||||
case Duration.VeryLong: multiplier = 45; break;
|
||||
default: multiplier = 25; break;
|
||||
}
|
||||
|
||||
return 2000+Math.Max(1000,multiplier*characters);
|
||||
public int GetDisplayDuration(int value){
|
||||
return 2000+Math.Max(1000, value*characters);
|
||||
}
|
||||
|
||||
public string GenerateHtml(){
|
||||
public string GenerateHtml(string bodyClasses = null, bool enableCustomCSS = true){
|
||||
StringBuilder build = new StringBuilder();
|
||||
build.Append("<!DOCTYPE html>");
|
||||
build.Append("<html class='os-windows txt-base-").Append(FontSizeClass ?? DefaultFontSizeClass).Append("'>");
|
||||
build.Append("<head>").Append(HeadTag ?? DefaultHeadTag).Append("</head>");
|
||||
build.Append("<body class='hearty'><div class='app-columns-container'><div class='column scroll-styled-v' style='width:100%;overflow-y:auto'>");
|
||||
build.Append("<head>").Append(HeadTag ?? DefaultHeadTag);
|
||||
|
||||
if (enableCustomCSS){
|
||||
build.Append("<style type='text/css'>").Append(FixedCSS).Append(CustomCSS).Append("</style>");
|
||||
|
||||
if (!string.IsNullOrEmpty(Program.UserConfig.CustomNotificationCSS)){
|
||||
build.Append("<style type='text/css'>").Append(Program.UserConfig.CustomNotificationCSS).Append("</style>");
|
||||
}
|
||||
}
|
||||
else{
|
||||
build.Append("<style type='text/css'>").Append(FixedCSS).Append("</style>");
|
||||
}
|
||||
|
||||
build.Append("</head>");
|
||||
build.Append("<body class='hearty");
|
||||
|
||||
if (!string.IsNullOrEmpty(bodyClasses)){
|
||||
build.Append(' ').Append(bodyClasses);
|
||||
}
|
||||
|
||||
build.Append('\'').Append(isExample ? " td-example-notification" : "").Append("><div class='app-columns-container'><div class='column scroll-styled-v' style='width:100%;overflow-y:auto'>");
|
||||
build.Append(html);
|
||||
build.Append("</div></div></body>");
|
||||
build.Append("</html>");
|
30
Core/Other/FormAbout.Designer.cs
generated
30
Core/Other/FormAbout.Designer.cs
generated
@@ -28,7 +28,7 @@ namespace TweetDck.Core.Other {
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FormAbout));
|
||||
this.pictureLogo = new System.Windows.Forms.PictureBox();
|
||||
this.labelDescription = new System.Windows.Forms.Label();
|
||||
this.labelSourceCode = new System.Windows.Forms.LinkLabel();
|
||||
this.labelTips = new System.Windows.Forms.LinkLabel();
|
||||
this.labelWebsite = new System.Windows.Forms.LinkLabel();
|
||||
this.tablePanelLinks = new System.Windows.Forms.TableLayoutPanel();
|
||||
this.labelIssues = new System.Windows.Forms.LinkLabel();
|
||||
@@ -59,19 +59,19 @@ namespace TweetDck.Core.Other {
|
||||
this.labelDescription.Size = new System.Drawing.Size(232, 109);
|
||||
this.labelDescription.TabIndex = 1;
|
||||
//
|
||||
// labelSourceCode
|
||||
// labelTips
|
||||
//
|
||||
this.labelSourceCode.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.labelSourceCode.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
||||
this.labelSourceCode.LinkArea = new System.Windows.Forms.LinkArea(0, 0);
|
||||
this.labelSourceCode.Location = new System.Drawing.Point(117, 0);
|
||||
this.labelSourceCode.Margin = new System.Windows.Forms.Padding(0);
|
||||
this.labelSourceCode.Name = "labelSourceCode";
|
||||
this.labelSourceCode.Size = new System.Drawing.Size(99, 16);
|
||||
this.labelSourceCode.TabIndex = 3;
|
||||
this.labelSourceCode.Text = "Source Code";
|
||||
this.labelSourceCode.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||
this.labelSourceCode.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.OnLinkClicked);
|
||||
this.labelTips.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.labelTips.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
||||
this.labelTips.LinkArea = new System.Windows.Forms.LinkArea(0, 0);
|
||||
this.labelTips.Location = new System.Drawing.Point(117, 0);
|
||||
this.labelTips.Margin = new System.Windows.Forms.Padding(0);
|
||||
this.labelTips.Name = "labelTips";
|
||||
this.labelTips.Size = new System.Drawing.Size(99, 16);
|
||||
this.labelTips.TabIndex = 3;
|
||||
this.labelTips.Text = "Tips && Tricks";
|
||||
this.labelTips.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||
this.labelTips.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.OnLinkClicked);
|
||||
//
|
||||
// labelWebsite
|
||||
//
|
||||
@@ -98,7 +98,7 @@ namespace TweetDck.Core.Other {
|
||||
this.tablePanelLinks.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 35.14F));
|
||||
this.tablePanelLinks.Controls.Add(this.labelIssues, 2, 0);
|
||||
this.tablePanelLinks.Controls.Add(this.labelWebsite, 0, 0);
|
||||
this.tablePanelLinks.Controls.Add(this.labelSourceCode, 1, 0);
|
||||
this.tablePanelLinks.Controls.Add(this.labelTips, 1, 0);
|
||||
this.tablePanelLinks.Location = new System.Drawing.Point(12, 124);
|
||||
this.tablePanelLinks.Name = "tablePanelLinks";
|
||||
this.tablePanelLinks.RowCount = 1;
|
||||
@@ -146,7 +146,7 @@ namespace TweetDck.Core.Other {
|
||||
|
||||
private System.Windows.Forms.PictureBox pictureLogo;
|
||||
private System.Windows.Forms.Label labelDescription;
|
||||
private System.Windows.Forms.LinkLabel labelSourceCode;
|
||||
private System.Windows.Forms.LinkLabel labelTips;
|
||||
private System.Windows.Forms.LinkLabel labelWebsite;
|
||||
private System.Windows.Forms.TableLayoutPanel tablePanelLinks;
|
||||
private System.Windows.Forms.LinkLabel labelIssues;
|
||||
|
@@ -3,7 +3,7 @@ using TweetDck.Core.Utils;
|
||||
|
||||
namespace TweetDck.Core.Other{
|
||||
sealed partial class FormAbout : Form{
|
||||
private const string GitHubLink = "https://github.com/chylex/TweetDuck";
|
||||
private const string TipsLink = "https://github.com/chylex/TweetDuck/wiki";
|
||||
private const string IssuesLink = "https://github.com/chylex/TweetDuck/issues";
|
||||
|
||||
public FormAbout(){
|
||||
@@ -13,9 +13,9 @@ namespace TweetDck.Core.Other{
|
||||
|
||||
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.";
|
||||
|
||||
labelWebsite.Links.Add(new LinkLabel.Link(0,labelWebsite.Text.Length,Program.Website));
|
||||
labelSourceCode.Links.Add(new LinkLabel.Link(0,labelSourceCode.Text.Length,GitHubLink));
|
||||
labelIssues.Links.Add(new LinkLabel.Link(0,labelIssues.Text.Length,IssuesLink));
|
||||
labelWebsite.Links.Add(new LinkLabel.Link(0, labelWebsite.Text.Length, Program.Website));
|
||||
labelTips.Links.Add(new LinkLabel.Link(0, labelTips.Text.Length, TipsLink));
|
||||
labelIssues.Links.Add(new LinkLabel.Link(0, labelIssues.Text.Length, IssuesLink));
|
||||
}
|
||||
|
||||
private void OnLinkClicked(object sender, LinkLabelLinkClickedEventArgs e){
|
||||
|
79
Core/Other/FormMessage.Designer.cs
generated
Normal file
79
Core/Other/FormMessage.Designer.cs
generated
Normal file
@@ -0,0 +1,79 @@
|
||||
namespace TweetDck.Core.Other {
|
||||
partial class FormMessage {
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing) {
|
||||
if (disposing && (components != null)) {
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent() {
|
||||
this.panelActions = new System.Windows.Forms.Panel();
|
||||
this.labelMessage = new System.Windows.Forms.Label();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// panelActions
|
||||
//
|
||||
this.panelActions.BackColor = System.Drawing.SystemColors.Control;
|
||||
this.panelActions.Dock = System.Windows.Forms.DockStyle.Bottom;
|
||||
this.panelActions.Location = new System.Drawing.Point(0, 84);
|
||||
this.panelActions.Name = "panelActions";
|
||||
this.panelActions.Size = new System.Drawing.Size(233, 49);
|
||||
this.panelActions.TabIndex = 0;
|
||||
//
|
||||
// labelMessage
|
||||
//
|
||||
this.labelMessage.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.labelMessage.AutoSize = true;
|
||||
this.labelMessage.Font = System.Drawing.SystemFonts.MessageBoxFont;
|
||||
this.labelMessage.Location = new System.Drawing.Point(62, 34);
|
||||
this.labelMessage.Margin = new System.Windows.Forms.Padding(53, 24, 27, 24);
|
||||
this.labelMessage.MaximumSize = new System.Drawing.Size(600, 0);
|
||||
this.labelMessage.MinimumSize = new System.Drawing.Size(0, 24);
|
||||
this.labelMessage.Name = "labelMessage";
|
||||
this.labelMessage.Size = new System.Drawing.Size(0, 24);
|
||||
this.labelMessage.TabIndex = 1;
|
||||
this.labelMessage.SizeChanged += new System.EventHandler(this.labelMessage_SizeChanged);
|
||||
//
|
||||
// FormMessage
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.BackColor = System.Drawing.Color.White;
|
||||
this.ClientSize = new System.Drawing.Size(98, 133);
|
||||
this.Controls.Add(this.labelMessage);
|
||||
this.Controls.Add(this.panelActions);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
|
||||
this.MaximizeBox = false;
|
||||
this.MinimizeBox = false;
|
||||
this.Name = "FormMessage";
|
||||
this.ShowIcon = false;
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.Panel panelActions;
|
||||
private System.Windows.Forms.Label labelMessage;
|
||||
}
|
||||
}
|
119
Core/Other/FormMessage.cs
Normal file
119
Core/Other/FormMessage.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace TweetDck.Core.Other{
|
||||
sealed partial class FormMessage : Form{
|
||||
public Button ClickedButton { get; private set; }
|
||||
|
||||
private readonly Icon icon;
|
||||
private readonly bool isReady;
|
||||
|
||||
private int realFormWidth, minFormWidth;
|
||||
private int buttonCount;
|
||||
private int prevLabelWidth, prevLabelHeight;
|
||||
private bool wasLabelMultiline;
|
||||
|
||||
public FormMessage(string caption, string text, MessageBoxIcon messageIcon){
|
||||
InitializeComponent();
|
||||
|
||||
this.prevLabelWidth = labelMessage.Width;
|
||||
this.prevLabelHeight = labelMessage.Height;
|
||||
this.minFormWidth = 18;
|
||||
|
||||
switch(messageIcon){
|
||||
case MessageBoxIcon.Information:
|
||||
icon = SystemIcons.Information;
|
||||
break;
|
||||
|
||||
case MessageBoxIcon.Warning:
|
||||
icon = SystemIcons.Warning;
|
||||
break;
|
||||
|
||||
case MessageBoxIcon.Error:
|
||||
icon = SystemIcons.Error;
|
||||
break;
|
||||
|
||||
case MessageBoxIcon.Question:
|
||||
icon = SystemIcons.Question;
|
||||
break;
|
||||
|
||||
default:
|
||||
icon = null;
|
||||
labelMessage.Location = new Point(labelMessage.Location.X-37, labelMessage.Location.Y);
|
||||
break;
|
||||
}
|
||||
|
||||
this.isReady = true;
|
||||
|
||||
this.Text = caption;
|
||||
this.labelMessage.Text = text;
|
||||
}
|
||||
|
||||
public Button AddButton(string title){
|
||||
Button button = new Button{
|
||||
Anchor = AnchorStyles.Bottom | AnchorStyles.Right,
|
||||
Font = SystemFonts.MessageBoxFont,
|
||||
Location = new Point(Width-112-buttonCount*96, 12),
|
||||
Size = new Size(88, 26),
|
||||
TabIndex = buttonCount,
|
||||
Text = title,
|
||||
UseVisualStyleBackColor = true
|
||||
};
|
||||
|
||||
button.Click += (sender, args) => {
|
||||
ClickedButton = (Button)sender;
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
};
|
||||
|
||||
panelActions.Controls.Add(button);
|
||||
|
||||
minFormWidth += 96;
|
||||
Width = Math.Max(realFormWidth, minFormWidth);
|
||||
|
||||
++buttonCount;
|
||||
return button;
|
||||
}
|
||||
|
||||
public void AddActionControl(Control control){
|
||||
panelActions.Controls.Add(control);
|
||||
|
||||
minFormWidth += control.Width+control.Margin.Horizontal;
|
||||
Width = Math.Max(realFormWidth, minFormWidth);
|
||||
}
|
||||
|
||||
private void labelMessage_SizeChanged(object sender, EventArgs e){
|
||||
if (!isReady){
|
||||
return;
|
||||
}
|
||||
|
||||
bool isMultiline = labelMessage.Height > labelMessage.MinimumSize.Height;
|
||||
|
||||
if (isMultiline && !wasLabelMultiline){
|
||||
labelMessage.Location = new Point(labelMessage.Location.X, labelMessage.Location.Y-8);
|
||||
prevLabelHeight += 8;
|
||||
}
|
||||
else if (!isMultiline && wasLabelMultiline){
|
||||
labelMessage.Location = new Point(labelMessage.Location.X, labelMessage.Location.Y+8);
|
||||
prevLabelHeight -= 8;
|
||||
}
|
||||
|
||||
realFormWidth = Width-(icon == null ? 32+35+(labelMessage.Margin.Left-labelMessage.Margin.Right) : 0)+labelMessage.Margin.Right+labelMessage.Width-prevLabelWidth;
|
||||
Width = Math.Max(realFormWidth, minFormWidth);
|
||||
Height += labelMessage.Height-prevLabelHeight;
|
||||
|
||||
prevLabelWidth = labelMessage.Width;
|
||||
prevLabelHeight = labelMessage.Height;
|
||||
wasLabelMultiline = isMultiline;
|
||||
}
|
||||
|
||||
protected override void OnPaint(PaintEventArgs e){
|
||||
if (icon != null){
|
||||
e.Graphics.DrawIcon(icon, 25, 26);
|
||||
}
|
||||
|
||||
base.OnPaint(e);
|
||||
}
|
||||
}
|
||||
}
|
6
Core/Other/FormPlugins.Designer.cs
generated
6
Core/Other/FormPlugins.Designer.cs
generated
@@ -23,7 +23,6 @@
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent() {
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FormPlugins));
|
||||
this.btnClose = new System.Windows.Forms.Button();
|
||||
this.btnReload = new System.Windows.Forms.Button();
|
||||
this.btnOpenFolder = new System.Windows.Forms.Button();
|
||||
@@ -69,7 +68,7 @@
|
||||
this.btnOpenFolder.UseVisualStyleBackColor = true;
|
||||
this.btnOpenFolder.Click += new System.EventHandler(this.btnOpenFolder_Click);
|
||||
//
|
||||
// pluginList
|
||||
// tabPanelPlugins
|
||||
//
|
||||
this.tabPanelPlugins.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
@@ -88,11 +87,10 @@
|
||||
this.Controls.Add(this.btnOpenFolder);
|
||||
this.Controls.Add(this.btnReload);
|
||||
this.Controls.Add(this.btnClose);
|
||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
||||
this.Icon = global::TweetDck.Properties.Resources.icon;
|
||||
this.MinimumSize = new System.Drawing.Size(480, 320);
|
||||
this.Name = "FormPlugins";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.Text = "Plugins";
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
|
@@ -1,13 +1,16 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using TweetDck.Core.Controls;
|
||||
using TweetDck.Plugins;
|
||||
using TweetDck.Plugins.Controls;
|
||||
using TweetDck.Plugins.Enums;
|
||||
using TweetDck.Plugins.Events;
|
||||
|
||||
namespace TweetDck.Core.Other{
|
||||
partial class FormPlugins : Form{
|
||||
sealed partial class FormPlugins : Form{
|
||||
private readonly PluginManager pluginManager;
|
||||
private readonly TabButton tabBtnOfficial, tabBtnCustom;
|
||||
private readonly PluginListFlowLayout flowLayoutPlugins;
|
||||
@@ -16,6 +19,8 @@ namespace TweetDck.Core.Other{
|
||||
|
||||
public FormPlugins(){
|
||||
InitializeComponent();
|
||||
|
||||
Text = Program.BrandName+" Plugins";
|
||||
}
|
||||
|
||||
public FormPlugins(PluginManager pluginManager) : this(){
|
||||
@@ -28,14 +33,14 @@ namespace TweetDck.Core.Other{
|
||||
this.tabPanelPlugins.SetupTabPanel(90);
|
||||
this.tabPanelPlugins.ReplaceContent(flowLayoutPlugins);
|
||||
|
||||
this.tabBtnOfficial = tabPanelPlugins.AddButton("",() => SelectGroup(PluginGroup.Official));
|
||||
this.tabBtnCustom = tabPanelPlugins.AddButton("",() => SelectGroup(PluginGroup.Custom));
|
||||
this.tabBtnOfficial = tabPanelPlugins.AddButton("", () => SelectGroup(PluginGroup.Official));
|
||||
this.tabBtnCustom = tabPanelPlugins.AddButton("", () => SelectGroup(PluginGroup.Custom));
|
||||
|
||||
this.tabPanelPlugins.SelectTab(tabBtnOfficial);
|
||||
this.pluginManager_Reloaded(pluginManager,null);
|
||||
this.pluginManager_Reloaded(pluginManager, null);
|
||||
|
||||
Shown += (sender, args) => {
|
||||
Program.UserConfig.PluginsWindow.Restore(this,false);
|
||||
Program.UserConfig.PluginsWindow.Restore(this, false);
|
||||
};
|
||||
|
||||
FormClosed += (sender, args) => {
|
||||
@@ -58,11 +63,20 @@ namespace TweetDck.Core.Other{
|
||||
flowLayoutPlugins.SuspendLayout();
|
||||
flowLayoutPlugins.Controls.Clear();
|
||||
|
||||
foreach(Plugin plugin in pluginManager.GetPluginsByGroup(selectedGroup.Value)){
|
||||
flowLayoutPlugins.Controls.Add(new PluginControl(pluginManager,plugin));
|
||||
Plugin[] plugins = pluginManager.GetPluginsByGroup(selectedGroup.Value).OrderBy(plugin => !plugin.CanRun ? 0 : pluginManager.Config.IsEnabled(plugin) ? 1 : 2).ThenBy(plugin => plugin.Name).ToArray();
|
||||
|
||||
for(int index = 0; index < plugins.Length; index++){
|
||||
flowLayoutPlugins.Controls.Add(new PluginControl(pluginManager, plugins[index]));
|
||||
|
||||
if (index < plugins.Length-1){
|
||||
flowLayoutPlugins.Controls.Add(new Panel{
|
||||
BackColor = Color.DimGray,
|
||||
Size = new Size(1, 1)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
flowLayoutPlugins_Resize(flowLayoutPlugins,new EventArgs());
|
||||
flowLayoutPlugins_Resize(flowLayoutPlugins, new EventArgs());
|
||||
flowLayoutPlugins.ResumeLayout(true);
|
||||
}
|
||||
|
||||
@@ -72,20 +86,33 @@ namespace TweetDck.Core.Other{
|
||||
}
|
||||
|
||||
private void flowLayoutPlugins_Resize(object sender, EventArgs e){
|
||||
int horizontalOffset = 8+(flowLayoutPlugins.VerticalScroll.Visible ? SystemInformation.VerticalScrollBarWidth : 0);
|
||||
if (flowLayoutPlugins.Controls.Count == 0){
|
||||
return;
|
||||
}
|
||||
|
||||
Control lastControl = flowLayoutPlugins.Controls[flowLayoutPlugins.Controls.Count-1];
|
||||
bool showScrollBar = lastControl.Location.Y+lastControl.Height >= flowLayoutPlugins.Height;
|
||||
int horizontalOffset = showScrollBar ? SystemInformation.VerticalScrollBarWidth : 0;
|
||||
|
||||
flowLayoutPlugins.AutoScroll = showScrollBar;
|
||||
flowLayoutPlugins.VerticalScroll.Visible = showScrollBar;
|
||||
|
||||
foreach(Control control in flowLayoutPlugins.Controls){
|
||||
control.Width = flowLayoutPlugins.Width-control.Margin.Horizontal-horizontalOffset;
|
||||
}
|
||||
|
||||
flowLayoutPlugins.Focus();
|
||||
}
|
||||
|
||||
private void btnOpenFolder_Click(object sender, EventArgs e){
|
||||
using(Process.Start("explorer.exe","\""+pluginManager.PathCustomPlugins+"\"")){}
|
||||
using(Process.Start("explorer.exe", "\""+pluginManager.PathCustomPlugins+"\"")){}
|
||||
}
|
||||
|
||||
private void btnReload_Click(object sender, EventArgs e){
|
||||
pluginManager.Reload();
|
||||
ReloadPluginTab();
|
||||
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){
|
||||
pluginManager.Reload();
|
||||
ReloadPluginTab();
|
||||
}
|
||||
}
|
||||
|
||||
private void btnClose_Click(object sender, EventArgs e){
|
||||
|
@@ -117,261 +117,4 @@
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
AAABAAMAMDAAAAEAIACoJQAANgAAACAgAAABACAAqBAAAN4lAAAQEAAAAQAgAGgEAACGNgAAKAAAADAA
|
||||
AABgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqHg5IrF+OlS+hjyMw4g7tcmM
|
||||
PM/MjTzezY485s2OPObMjjzfyIw80MOJPLe8hDyNs387WKx8OyQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApHU4FrB8OGXGijzAzo889dKR
|
||||
PP/SkTz/0pE8/9GRPP/RkDz/0JA8/9CQPP/RkDz/0ZE8/9KRPP/TkTz/0pE8/86PPPbGijzCt4E8bKp6
|
||||
OxoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKN1OB+9hTuLzI486NOR
|
||||
PP/SkTz/0ZA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9GQ
|
||||
PP/SkTz/0pE8/82OPOu9hTuOq3w8IgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACidTkFuoM8csyN
|
||||
POrTkjz/0ZE8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9GRPP/Tkjz/zY887bqEPHimeDsHAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKp6
|
||||
OynDiDvA05I8/9GRPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0ZA8/9ORPP/GijzFr308LgAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAArXw7UM2OPOvTkTz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/SkTz/zo887rOAPFYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAACxfjxm0ZA8/NGRPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0ZE8/9GQPP64gjxtAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAALaBPGXRkTz/0ZE8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9GQPP/SkTz/uIM8bAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqnk6TtCQPPzRkTz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/RkDz/0ZA8/rSA
|
||||
PFUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACtfDwmzo887dKRPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0ZE8/86PPPGtfDwrAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKZ4PAXFijzC0pE8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CURf/TpGT/2baJ/9/GpP/l0bf/59W//+fVvf/kz7P/3sOg/9mz
|
||||
hP/ToWH/0JND/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9KRPP/EiTvJo3Y7CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALiC
|
||||
PG/Tkjz/0JA8/9CQPP/QkDz/0JA8/9CSQf/Vq3P/5tO7//j07v//////////////////////////////
|
||||
////////////////////////9vHq/+TQtf/UqG7/0JA+/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/Tkjz/uYM8dgAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAArXw8Gc2OPOzRkTz/0JA8/9CQPP/QlEX/27qP//fz7P//////////////////////////////
|
||||
////////////////////////////////////////////////////////8ObZ/9auev/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/RkTz/zI488J1x
|
||||
OB8AAAAAAAAAAAAAAAAAAAAAuoM7h9OSPP/QkDz/0JA8/9GXTf/TpGb/27uS/9u6kP/ewZz/59W9//Xv
|
||||
5///////////////////////////////////////////////////////////////////////////////
|
||||
///r3cr/0ZxV/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/1JI8/7mDO5AAAAAAAAAAAAAAAADFj0cR0pNB6s+PO//Ojjv/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CRQP/TpGf/5NC2//38+v//////////////////////////////////////////////
|
||||
////////////////////////+vf0/9auev/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0ZE8/8uOPO6SajYVAAAAAAAAAADzr1df7qxV/+WkT//ZmUT/0JA9/86O
|
||||
O//Pjzv/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9GaUv/l07r/////////////////////////
|
||||
///////////////////////////////////////////////////buo//0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9OSPP+ldjdoAAAAAAAAAADurFW67qxV//Ct
|
||||
Vv/vrVb/6adR/92dSP/Skz//zo47/8+PO//QkDz/0JA8/9CQPP/QkDz/0JJB/9Smaf/dv5j/+fXw////
|
||||
////////////////////////////////////////////////////////////////////////2riM/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9KRPP/FiTzGoHQ6Ae6s
|
||||
VRrurFXz7qxV/+6sVf/urFX/761W//CuVv/sqlT/4qFM/9aVQv/Pjzv/zo47/9CQPP/VqnH/8+vh////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////9aueP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9GQ
|
||||
PP/Ojzz4oHM5Iu6sVU3urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/vrVX/8K5W/+6sVf/mpU//2plF/9q6
|
||||
j///////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////n18P/Rm1P/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/SkTz/rXs6Vu6sVYPurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/wrVb/6r2B////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////o18L/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/SkTz/v4Y8j+6sVa3urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFb/9Onb///////+/v3/////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////1qx2/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/SkTz/wYc7uO6sVcrurFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/trlr/67Zy/+m+h//y5NP/////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////7+TW/9CQPf/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/RkTz/yYw80e6s
|
||||
VdvurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/6sme//v49P//////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////9arc//QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/RkDz/zI083+6sVeLurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/u2b3/////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////+fVvv/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/zo885u6sVeLurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+vL
|
||||
of//////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////r28v/Rl0r/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/zo885u6sVdrurFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7LBj//r28f//////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///Vq3P/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/RkDz/zI083+6sVcrurFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/6cKO////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////ewp7/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/RkTz/yYw80O6s
|
||||
VazurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7NOy//z59v/u2b7/+fPr////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////n1sD/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/SkTz/xIo8uO6sVYHurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/67Zx/+2uWf/qy6H/////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////v5dj/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/SkTz/vYU8je6sVUvurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+nC
|
||||
kP///////////////////////////////////////////////////////fz6//fu5P/w4Mv/793F//7+
|
||||
/v/////////////////////////////////////////////////////////////////17eT/0JA//86O
|
||||
Ov/Pjzv/0JA8/9CQPP/QkDz/0JA8/9CQPP/SkTz/tYA8Vu6sVRnurFXy7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7a5b//fw5///////////////////////////////////////+/jz/+7av//pwIr/67Jn/+6t
|
||||
Vv/urFX/6rd1////////////////////////////////////////////////////////////////////
|
||||
///48ej/6KhU/92cR//Tkj7/zo47/8+PO//QkDz/0JA8/9GQPP/Pjzz3rXw8IAAAAADurFW37qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/6b2D//////////////////////////////////nz6//qyZ3/7K9h/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/6rl6////////////////////////////////////////////////////
|
||||
///////////////////9/Pr/6r6C//CuV//sqlT/4aBL/9aWQv/Pjzv/zo47/9GRPP/GijzCp3g8AQAA
|
||||
AADurFVb7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/68yk///////////////////////+/fz/68+q/+2v
|
||||
XP/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/6rNr//7+/f//////////////////////////////
|
||||
/////////////////////////////////////////////+nEk//vrVX/8K5W/+6sVf/lpE//2ZlF/9KR
|
||||
Pf+zfzpoAAAAAAAAAADurFUP7qxV5u6sVf/urFX/7qxV/+6sVf/urFX/7NGu//////////////////fu
|
||||
5P/quHb/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxW//Xr3///////////////
|
||||
///////////////////////////////////////////////////7+PP/+vXu///////pxJL/7qxV/+6s
|
||||
Vf/wrVb/8K1W/+mnUevUmUsUAAAAAAAAAAAAAAAA7qxVgO6sVf/urFX/7qxV/+6sVf/urFX/6sqg////
|
||||
////////8eLO/+yvX//urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+nF
|
||||
lf/////////////////////////////////////////////////////////////////8+vf/6cWV/+q5
|
||||
ev/s07H/6rd1/+6sVf/urFX/7qxV/++tVogAAAAAAAAAAAAAAAAAAAAA7qxVFe6sVejurFX/7qxV/+6s
|
||||
Vf/urFX/6bp8///////w4Mn/7a1Z/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/x4cz////////////////////////////////////////////////////+//7+
|
||||
/v/////////+/+m8gP/urFX/7a1Y/+6sVf/urFX/7qxV7O6sVRoAAAAAAAAAAAAAAAAAAAAAAAAAAO6s
|
||||
VWburFX/7qxV/+6sVf/urFX/7a5b/+zTs//sr1//7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/sr1//8uPQ////////////////////////////////////
|
||||
////////6suj/+qzav/pxpj/9Onb//n07f/rr2D/7qxV/+6sVf/urFX/7qxVbwAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAO6sVQPurFW67qxV/+6sVf/urFX/7qxV/+6sVv/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7axY/+nGl//27uP/////////
|
||||
/////////fz6//Lj0P/qunv/7qxV/+6sVf/urFX/7qxV/+m9gv/qt3X/7qxV/+6sVf/urFXB7qxVBQAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADurFUg7qxV6O6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/qtnP/6sqg/+3WuP/s07P/6cSS/+uyZf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVv/trVr/7qxV/+6s
|
||||
VezurFUlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7qxVRu6sVfjurFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV++6sVUwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO6s
|
||||
VVzurFX97qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxVYwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAADurFVd7qxV+O6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVfvurFVjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7qxVR+6sVeXurFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV6O6sVU0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO6sVSPurFW37qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFW77qxVJgAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AADurFUB7qxVae6sVeTurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV5+6sVW3urFUDAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAO6sVRjurFWA7qxV4u6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVeTurFWE7qxVGwAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7qxVEu6sVV7urFW17qxV7+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVfDurFW47qxVX+6s
|
||||
VRMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAA7qxVG+6sVUzurFWA7qxVrO6sVcnurFXa7qxV4u6sVeLurFXa7qxVye6sVa3urFWC7qxVTe6s
|
||||
VR0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAP//gAH//6gr//wAAD//qCv/8AAAD/+oK//AAAAD/6gr/4AAAAH/qCv/AAAAAP+oK/4A
|
||||
AAAAf6gr/AAAAAA/qCv4AAAAAB+oK/AAAAAAD6gr4AAAAAAHqCvgAAAAAAeoK8AAAAAAA6grwAAAAAAD
|
||||
qCuAAAAAAAGoK4AAAAAAAagrgAAAAAAAqCsAAAAAAACoKwAAAAAAAKgrAAAAAAAAqCsAAAAAAACoKwAA
|
||||
AAAAAKgrAAAAAAAAqCsAAAAAAACoKwAAAAAAAKgrAAAAAAAAqCsAAAAAAACoKwAAAAAAAKgrAAAAAAAA
|
||||
qCsAAAAAAACoKwAAAAAAAKgrgAAAAAAAqCuAAAAAAAGoK4AAAAAAAagrwAAAAAADqCvAAAAAAAOoK+AA
|
||||
AAAAB6gr4AAAAAAHqCvwAAAAAA+oK/gAAAAAH6gr/AAAAAA/qCv+AAAAAH+oK/8AAAAA/6gr/4AAAAH/
|
||||
qCv/wAAAA/+oK//wAAAP/6gr//wAAD//qCv//4AB//+oKygAAAAgAAAAQAAAAAEAIAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApHY4FrR/
|
||||
Ole+hjuVxos7w8uNPNvOjzznzo8858uNPNzGijzEvoU7l7WBO1mpejsYAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqHg5Kr6F
|
||||
O5XLjTvo0pE8/9ORPP/SkTz/0ZE8/9CQPP/QkDz/0ZE8/9KRPP/TkTz/0pE8/82OPOq/hjyZrXw7LAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAo3U6CbuE
|
||||
O4bPjzz005I8/9GRPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0ZE8/9OS
|
||||
PP/PkDz1vYU8iah6PAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKR3
|
||||
OibGizvG05E8/9GQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9GQPP/TkTz/yIs8yqx8PCkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AACsezsxy40849KRPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/SkTz/zI485rKAPTUAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAApXc6JMuNPOTSkTz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/SkTz/zI485659PCcAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAKd4OwnHizzJ0pE8/9CQPP/QkDz/0JA9/9OgYP/YtIX/38Sh/+LMr//iy67/3sOf/9iy
|
||||
gv/Tn13/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/SkTz/x4s8zaV4
|
||||
OwsAAAAAAAAAAAAAAAAAAAAAu4Q8hNOSPP/QkDz/0JA8/9GXS//YtIX/6NfC//Xv5//8+vj/////////
|
||||
///8+vf/9O3k/+fVvv/YsYH/0JRF/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/Tkjz/u4Q7iQAAAAAAAAAAAAAAAKx7OyTPjzz10ZA8/9GXS//Ztoj/8une////////////////////
|
||||
///////////////////////////////////38uz/3L2V/9CRP//QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9GRPP/Ojzz3onU5KQAAAAAAAAAAxo1BlNGQO//Ojjv/0JNC/9GWSP/QlET/0ZpR/9au
|
||||
eP/n1b3//v79////////////////////////////////////////////7+TV/9GdWP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9SSPP+4gjqaAAAAAPGuVhHvrVXq5qRO/9qZRP/QkT3/zo46/8+P
|
||||
O//QkDz/0JA8/9CQPP/To2P/8+vh////////////////////////////////////////////+fXw/9Oh
|
||||
Yf/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0pE8/8qMO+2WbTYV7qxVUe6sVf/wrVb/761W/+mo
|
||||
Uv/enUj/05M//86PO//PkD7/3cCZ//Lp3v/7+PT/////////////////////////////////////////
|
||||
////////+PTv/9GcVf/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/05I8/699OljurFWR7qxV/+6s
|
||||
Vf/urFX/761W//CuV//sqlT/4qJQ/+zdyf//////////////////////////////////////////////
|
||||
////////////////////////7+TV/9CRP//QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/Tkjz/vIQ7mO6s
|
||||
Vb/urFX/7qxV/+6sVf/urFX/7qxV/++tVf/ry5///v79//7+/f//////////////////////////////
|
||||
////////////////////////////////////////2rqO/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9KR
|
||||
PP/GijvF7qxV2u6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+u1bv/t1bb//fv4////////////////////
|
||||
///////////////////////////////////////////////////48+3/0ZRG/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0ZA8/8uNPNzurFXl7qxV/+6sVf/urFX/7qxV/+6sVf/rsWP/9OjZ////////////////////
|
||||
///////////////////////////////////////////////////////////////////YtIX/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/zo886O6sVeXurFX/7qxV/+6sVf/urFX/7qxW//Hiz///////////////
|
||||
/////////////////////////////////////////////////////////////////////////////+rb
|
||||
yP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/Ojzzo7qxV2e6sVf/urFX/7qxV/+6sVf/quHb/////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////+fXw/9CURf/QkDz/0JA8/9CQPP/QkDz/0ZA8/8yOPNzurFW/7qxV/+6sVf/urFX/7qxV/+nA
|
||||
if/s1LT/9Onb////////////////////////////////////////////////////////////////////
|
||||
////////////////////////0Z5b/9CQPP/QkDz/0JA8/9CQPP/SkTz/x4s8xe6sVY/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+nEk//+/fz////////////////////////////+/v3/+PHo//To2f/+/v7/////////
|
||||
///////////////////////////////////VqGz/zo46/8+PO//QkDz/0JA8/9KRPP+/hjyX7qxVUO6s
|
||||
Vf/urFX/7qxV/+6sVf/ssGH//Pr2///////////////////////y49D/6cOR/+qyaf/urVb/6rJp////
|
||||
/////////////////////////////////////////////+e7f//goEr/1ZVB/8+PO//Ojzv/0pE8/7WB
|
||||
PFfurFUQ7qxV6O6sVf/urFX/7qxV/+m+hv/////////////////z5tX/6rl6/+6sVf/urFX/7qxV/+6s
|
||||
Vf/qs2r////+////////////////////////////////////////////+PLp/+y4c//urFX/5aRO/9qZ
|
||||
RP/Mjjztp3g6FQAAAADurFWQ7qxV/+6sVf/urFX/6cKP////////////682m/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+2sV//17N////////////////////////////////////////7+/v/06Nn/8uTT/+qz
|
||||
a//wrVb/761W/+alUZYAAAAAAAAAAO6sVSHurFX07qxV/+6sVf/quXj//////+nDkf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+m+hP////////////////////////////////////////////jy
|
||||
6v/qtnH/67Fj/+6sVf/urFX2761WJQAAAAAAAAAAAAAAAO6sVX7urFX/7qxV/+2vW//pvID/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+nEkv/7+PT//////////////////fz6/+rJ
|
||||
n//rtW7/682n/+zTs//urFb/7qxV/+6sVYQAAAAAAAAAAAAAAAAAAAAA7qxVB+6sVcTurFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+u0bP/qy6H/7da4/+vN
|
||||
pv/qtnP/7qxV/+6sVf/urFX/7K9f/+2sWP/urFXI7qxVCQAAAAAAAAAAAAAAAAAAAAAAAAAA7qxVIO6s
|
||||
VeDurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV4+6sVSMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAA7qxVLO6sVd/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVeLurFUwAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAA7qxVIu6sVcHurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFXE7qxVJAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7qxVBu6sVX/urFXw7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFXy7qxVgu6sVQgAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO6sVSXurFWP7qxV5e6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFXl7qxVke6sVScAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AADurFUS7qxVUO6sVY7urFW97qxV2O6sVeXurFXl7qxV2O6sVb7urFWP7qxVUu6sVRMAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8AD//8AAP/8AAA/+AAAH/AAAA/gAAAHwAAAA8AA
|
||||
AAOAAAABgAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAA
|
||||
AAGAAAABwAAAA8AAAAPgAAAH8AAAD/gAAB/8AAA//wAA///AA/8oAAAAEAAAACAAAAABACAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKx7Oja9hTqTyYw8zs6PPOfOjzznyYw8zr6G
|
||||
PJSvfjw3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAo3Y7CbyEO5PRkDz805I8/9GRPP/QkDz/0JA8/9GR
|
||||
PP/TkTz/0ZA8/b+GPJWrfDwKAAAAAAAAAAAAAAAAp3g8CcSJPLfTkjz/0ZA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/Tkjz/xoo8uqx8PQoAAAAAAAAAAL2FPJPUk0D/06Rm/92/mf/izK//4sut/9u6
|
||||
j//Snlr/0JA8/9CQPP/QkDz/0JA8/9OSPP+9hTyWAAAAALiEQDLQkDz90JpS/93Amf/u4tP/////////
|
||||
/////////fv5/+DIqP/QkkH/0JA8/9CQPP/RkDz/0JA8/qN1OTTtq1WS56VP/9qaRf/RkT3/0JdO/+rc
|
||||
yP//////////////////////693L/9CSQf/QkDz/0JA8/9SSPP+3gTqW761Vz++tVv/wrVb/6K5h//fy
|
||||
6v/////////////////////////////////gx6b/0JA8/9CQPP/RkTz/yYw80e6sVefurFX/7qxV/+q+
|
||||
hP/69vD//////////////////////////////////fz7/9GbU//QkDz/0JA8/8+PPOnurFXn7qxV/+yw
|
||||
Y//79/P////////////////////////////////////////////ZuIz/0JA8/9CQPP/Pjzzp7qxVz+6s
|
||||
Vf/sr2H/8eLO/////////////v7+//n07f/+/v7/////////////////48yu/8+PO//Qjzv/yYs80e6s
|
||||
VZLurFX/67Jm//7+/f/8+fb/68yl/+qza//rsmf//v37//////////////////Pn2P/ip1n/2phC/8CH
|
||||
PJXurFUw7qxV/eq2c//169//6rNr/+6sVf/urFX/7q1W//Pm1f/////////////////8+fX/67+G/++t
|
||||
Vf3lpVEyAAAAAO6sVZHtrVr/7a9c/+6sVf/urFX/7qxV/+6sVf/rsWP/7de5//Lj0P/pxJL/67Zy/+qz
|
||||
av/urFWTAAAAAAAAAADurFUI7qxVtO6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFW37qxVCQAAAAAAAAAAAAAAAO6sVQjurFWQ7qxV++6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
VfvurFWS7qxVCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO6sVTPurFWQ7qxVzO6sVeburFXm7qxVzO6s
|
||||
VZDurFU0AAAAAAAAAAAAAAAAAAAAAPAPrEHAA6xBgAGsQYABrEEAAKxBAACsQQAArEEAAKxBAACsQQAA
|
||||
rEEAAKxBAACsQYABrEGAAaxBwAOsQfAPrEE=
|
||||
</value>
|
||||
</data>
|
||||
</root>
|
77
Core/Other/FormSettings.Designer.cs
generated
77
Core/Other/FormSettings.Designer.cs
generated
@@ -23,24 +23,11 @@
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent() {
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FormSettings));
|
||||
this.tabPanel = new TweetDck.Core.Controls.TabPanel();
|
||||
this.btnClose = new System.Windows.Forms.Button();
|
||||
this.btnExport = new System.Windows.Forms.Button();
|
||||
this.btnImport = new System.Windows.Forms.Button();
|
||||
this.btnReset = new System.Windows.Forms.Button();
|
||||
this.labelTip = new System.Windows.Forms.Label();
|
||||
this.tabPanel = new TweetDck.Core.Controls.TabPanel();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// tabPanel
|
||||
//
|
||||
this.tabPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.tabPanel.Location = new System.Drawing.Point(12, 12);
|
||||
this.tabPanel.Name = "tabPanel";
|
||||
this.tabPanel.Size = new System.Drawing.Size(480, 313);
|
||||
this.tabPanel.TabIndex = 3;
|
||||
//
|
||||
// btnClose
|
||||
//
|
||||
this.btnClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
@@ -54,57 +41,35 @@
|
||||
this.btnClose.UseVisualStyleBackColor = true;
|
||||
this.btnClose.Click += new System.EventHandler(this.btnClose_Click);
|
||||
//
|
||||
// btnExport
|
||||
// labelTip
|
||||
//
|
||||
this.btnExport.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.btnExport.AutoSize = true;
|
||||
this.btnExport.Location = new System.Drawing.Point(12, 331);
|
||||
this.btnExport.Name = "btnExport";
|
||||
this.btnExport.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||
this.btnExport.Size = new System.Drawing.Size(94, 23);
|
||||
this.btnExport.TabIndex = 5;
|
||||
this.btnExport.Text = "Export Settings";
|
||||
this.btnExport.UseVisualStyleBackColor = true;
|
||||
this.btnExport.Click += new System.EventHandler(this.btnExport_Click);
|
||||
this.labelTip.AutoSize = true;
|
||||
this.labelTip.Location = new System.Drawing.Point(12, 333);
|
||||
this.labelTip.Name = "labelTip";
|
||||
this.labelTip.Size = new System.Drawing.Size(310, 13);
|
||||
this.labelTip.TabIndex = 5;
|
||||
this.labelTip.Text = "Tip: Move your cursor over an option to see detailed explanation";
|
||||
//
|
||||
// btnImport
|
||||
// tabPanel
|
||||
//
|
||||
this.btnImport.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.btnImport.AutoSize = true;
|
||||
this.btnImport.Location = new System.Drawing.Point(112, 331);
|
||||
this.btnImport.Name = "btnImport";
|
||||
this.btnImport.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||
this.btnImport.Size = new System.Drawing.Size(94, 23);
|
||||
this.btnImport.TabIndex = 6;
|
||||
this.btnImport.Text = "Import Settings";
|
||||
this.btnImport.UseVisualStyleBackColor = true;
|
||||
this.btnImport.Click += new System.EventHandler(this.btnImport_Click);
|
||||
//
|
||||
// btnReset
|
||||
//
|
||||
this.btnReset.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.btnReset.AutoSize = true;
|
||||
this.btnReset.Location = new System.Drawing.Point(212, 331);
|
||||
this.btnReset.Name = "btnReset";
|
||||
this.btnReset.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||
this.btnReset.Size = new System.Drawing.Size(102, 23);
|
||||
this.btnReset.TabIndex = 7;
|
||||
this.btnReset.Text = "Restore Defaults";
|
||||
this.btnReset.UseVisualStyleBackColor = true;
|
||||
this.btnReset.Click += new System.EventHandler(this.btnReset_Click);
|
||||
this.tabPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.tabPanel.Location = new System.Drawing.Point(12, 12);
|
||||
this.tabPanel.Name = "tabPanel";
|
||||
this.tabPanel.Size = new System.Drawing.Size(480, 313);
|
||||
this.tabPanel.TabIndex = 3;
|
||||
//
|
||||
// FormSettings
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(504, 366);
|
||||
this.Controls.Add(this.btnReset);
|
||||
this.Controls.Add(this.btnImport);
|
||||
this.Controls.Add(this.btnExport);
|
||||
this.Controls.Add(this.labelTip);
|
||||
this.Controls.Add(this.btnClose);
|
||||
this.Controls.Add(this.tabPanel);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
|
||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
||||
this.Icon = Properties.Resources.icon;
|
||||
this.MaximizeBox = false;
|
||||
this.MinimizeBox = false;
|
||||
this.Name = "FormSettings";
|
||||
@@ -119,8 +84,6 @@
|
||||
|
||||
private Controls.TabPanel tabPanel;
|
||||
private System.Windows.Forms.Button btnClose;
|
||||
private System.Windows.Forms.Button btnExport;
|
||||
private System.Windows.Forms.Button btnImport;
|
||||
private System.Windows.Forms.Button btnReset;
|
||||
private System.Windows.Forms.Label labelTip;
|
||||
}
|
||||
}
|
@@ -2,25 +2,35 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using TweetDck.Core.Notification;
|
||||
using TweetDck.Core.Other.Settings;
|
||||
using TweetDck.Core.Other.Settings.Export;
|
||||
using TweetDck.Plugins;
|
||||
using TweetDck.Updates;
|
||||
|
||||
namespace TweetDck.Core.Other{
|
||||
sealed partial class FormSettings : Form{
|
||||
private readonly Dictionary<Type,BaseTabSettings> tabs = new Dictionary<Type,BaseTabSettings>(4);
|
||||
public const int TabIndexNotification = 1;
|
||||
|
||||
public FormSettings(FormBrowser browserForm, UpdateHandler updates){
|
||||
private readonly FormBrowser browser;
|
||||
private readonly Dictionary<Type, BaseTabSettings> tabs = new Dictionary<Type, BaseTabSettings>(4);
|
||||
private readonly bool hasFinishedLoading;
|
||||
|
||||
public FormSettings(FormBrowser browser, PluginManager plugins, UpdateHandler updates, int startTabIndex = 0){
|
||||
InitializeComponent();
|
||||
|
||||
Text = Program.BrandName+" Settings";
|
||||
|
||||
this.browser = browser;
|
||||
this.browser.PauseNotification();
|
||||
|
||||
this.tabPanel.SetupTabPanel(100);
|
||||
this.tabPanel.AddButton("General",SelectTab<TabSettingsGeneral>);
|
||||
this.tabPanel.AddButton("Notifications",() => SelectTab(() => new TabSettingsNotifications(browserForm.CreateNotificationForm(false))));
|
||||
this.tabPanel.AddButton("Updates",() => SelectTab(() => new TabSettingsUpdates(updates)));
|
||||
this.tabPanel.AddButton("Advanced",SelectTab<TabSettingsAdvanced>);
|
||||
this.tabPanel.SelectTab(tabPanel.Buttons.First());
|
||||
this.tabPanel.AddButton("General", SelectTab<TabSettingsGeneral>);
|
||||
this.tabPanel.AddButton("Notifications", () => SelectTab(() => new TabSettingsNotifications(browser.CreateNotificationForm(NotificationFlags.DisableContextMenu), !hasFinishedLoading)));
|
||||
this.tabPanel.AddButton("Updates", () => SelectTab(() => new TabSettingsUpdates(updates)));
|
||||
this.tabPanel.AddButton("Advanced", () => SelectTab(() => new TabSettingsAdvanced(browser.ReinjectCustomCSS, plugins)));
|
||||
|
||||
this.tabPanel.SelectTab(tabPanel.Buttons.ElementAt(startTabIndex));
|
||||
hasFinishedLoading = true;
|
||||
}
|
||||
|
||||
private void SelectTab<T>() where T : BaseTabSettings, new(){
|
||||
@@ -30,7 +40,7 @@ namespace TweetDck.Core.Other{
|
||||
private void SelectTab<T>(Func<T> constructor) where T : BaseTabSettings{
|
||||
BaseTabSettings control;
|
||||
|
||||
if (tabs.TryGetValue(typeof(T),out control)){
|
||||
if (tabs.TryGetValue(typeof(T), out control)){
|
||||
tabPanel.ReplaceContent(control);
|
||||
}
|
||||
else{
|
||||
@@ -41,75 +51,24 @@ namespace TweetDck.Core.Other{
|
||||
}
|
||||
|
||||
private void FormSettings_FormClosing(object sender, FormClosingEventArgs e){
|
||||
foreach(BaseTabSettings control in tabs.Values){
|
||||
control.OnClosing();
|
||||
}
|
||||
|
||||
Program.UserConfig.Save();
|
||||
}
|
||||
|
||||
private void btnExport_Click(object sender, EventArgs e){
|
||||
DialogResult resultSaveCredentials = MessageBox.Show("Do you want to include your login session? This will not save your password into the file, but it will allow anyone with the file to login into TweetDeck as you.","Export "+Program.BrandName+" Settings",MessageBoxButtons.YesNoCancel,MessageBoxIcon.Question,MessageBoxDefaultButton.Button3);
|
||||
if (resultSaveCredentials == DialogResult.Cancel)return;
|
||||
|
||||
bool saveCredentials = resultSaveCredentials == DialogResult.Yes;
|
||||
string file;
|
||||
|
||||
using(SaveFileDialog dialog = new SaveFileDialog{
|
||||
AddExtension = true,
|
||||
AutoUpgradeEnabled = true,
|
||||
OverwritePrompt = true,
|
||||
DefaultExt = "tdsettings",
|
||||
FileName = Program.BrandName+".tdsettings",
|
||||
Title = "Export "+Program.BrandName+" Settings",
|
||||
Filter = Program.BrandName+" Settings (*.tdsettings)|*.tdsettings"
|
||||
}){
|
||||
file = dialog.ShowDialog() == DialogResult.OK ? dialog.FileName : null;
|
||||
foreach(BaseTabSettings control in tabs.Values){
|
||||
control.Dispose();
|
||||
}
|
||||
|
||||
if (file != null){
|
||||
Program.UserConfig.Save();
|
||||
|
||||
ExportManager manager = new ExportManager(file);
|
||||
|
||||
if (!manager.Export(saveCredentials)){
|
||||
Program.HandleException("An exception happened while exporting "+Program.BrandName+" settings.",manager.LastException);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void btnImport_Click(object sender, EventArgs e){
|
||||
string file;
|
||||
|
||||
using(OpenFileDialog dialog = new OpenFileDialog{
|
||||
AutoUpgradeEnabled = true,
|
||||
DereferenceLinks = true,
|
||||
Title = "Import "+Program.BrandName+" Settings",
|
||||
Filter = Program.BrandName+" Settings (*.tdsettings)|*.tdsettings"
|
||||
}){
|
||||
file = dialog.ShowDialog() == DialogResult.OK ? dialog.FileName : null;
|
||||
}
|
||||
|
||||
if (file != null){
|
||||
ExportManager manager = new ExportManager(file);
|
||||
|
||||
if (manager.Import()){
|
||||
ReloadUI();
|
||||
}
|
||||
else{
|
||||
Program.HandleException("An exception happened while importing "+Program.BrandName+" settings.",manager.LastException);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void btnReset_Click(object sender, EventArgs e){
|
||||
if (MessageBox.Show("This will reset all of your settings, including disabled plugins. Do you want to proceed?","Reset "+Program.BrandName+" Settings",MessageBoxButtons.YesNo,MessageBoxIcon.Warning,MessageBoxDefaultButton.Button2) == DialogResult.Yes){
|
||||
Program.ResetConfig();
|
||||
ReloadUI();
|
||||
}
|
||||
browser.ResumeNotification();
|
||||
}
|
||||
|
||||
private void btnClose_Click(object sender, EventArgs e){
|
||||
Close();
|
||||
}
|
||||
|
||||
private void ReloadUI(){
|
||||
public void ReloadUI(){
|
||||
tabs.Clear();
|
||||
tabPanel.Content.Controls.Clear();
|
||||
tabPanel.ActiveButton.Callback();
|
||||
|
@@ -117,261 +117,4 @@
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
AAABAAMAMDAAAAEAIACoJQAANgAAACAgAAABACAAqBAAAN4lAAAQEAAAAQAgAGgEAACGNgAAKAAAADAA
|
||||
AABgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqHg5IrF+OlS+hjyMw4g7tcmM
|
||||
PM/MjTzezY485s2OPObMjjzfyIw80MOJPLe8hDyNs387WKx8OyQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApHU4FrB8OGXGijzAzo889dKR
|
||||
PP/SkTz/0pE8/9GRPP/RkDz/0JA8/9CQPP/RkDz/0ZE8/9KRPP/TkTz/0pE8/86PPPbGijzCt4E8bKp6
|
||||
OxoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKN1OB+9hTuLzI486NOR
|
||||
PP/SkTz/0ZA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9GQ
|
||||
PP/SkTz/0pE8/82OPOu9hTuOq3w8IgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACidTkFuoM8csyN
|
||||
POrTkjz/0ZE8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9GRPP/Tkjz/zY887bqEPHimeDsHAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKp6
|
||||
OynDiDvA05I8/9GRPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0ZA8/9ORPP/GijzFr308LgAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAArXw7UM2OPOvTkTz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/SkTz/zo887rOAPFYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAACxfjxm0ZA8/NGRPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0ZE8/9GQPP64gjxtAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAALaBPGXRkTz/0ZE8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9GQPP/SkTz/uIM8bAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqnk6TtCQPPzRkTz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/RkDz/0ZA8/rSA
|
||||
PFUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACtfDwmzo887dKRPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0ZE8/86PPPGtfDwrAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKZ4PAXFijzC0pE8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CURf/TpGT/2baJ/9/GpP/l0bf/59W//+fVvf/kz7P/3sOg/9mz
|
||||
hP/ToWH/0JND/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9KRPP/EiTvJo3Y7CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALiC
|
||||
PG/Tkjz/0JA8/9CQPP/QkDz/0JA8/9CSQf/Vq3P/5tO7//j07v//////////////////////////////
|
||||
////////////////////////9vHq/+TQtf/UqG7/0JA+/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/Tkjz/uYM8dgAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAArXw8Gc2OPOzRkTz/0JA8/9CQPP/QlEX/27qP//fz7P//////////////////////////////
|
||||
////////////////////////////////////////////////////////8ObZ/9auev/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/RkTz/zI488J1x
|
||||
OB8AAAAAAAAAAAAAAAAAAAAAuoM7h9OSPP/QkDz/0JA8/9GXTf/TpGb/27uS/9u6kP/ewZz/59W9//Xv
|
||||
5///////////////////////////////////////////////////////////////////////////////
|
||||
///r3cr/0ZxV/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/1JI8/7mDO5AAAAAAAAAAAAAAAADFj0cR0pNB6s+PO//Ojjv/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CRQP/TpGf/5NC2//38+v//////////////////////////////////////////////
|
||||
////////////////////////+vf0/9auev/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0ZE8/8uOPO6SajYVAAAAAAAAAADzr1df7qxV/+WkT//ZmUT/0JA9/86O
|
||||
O//Pjzv/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9GaUv/l07r/////////////////////////
|
||||
///////////////////////////////////////////////////buo//0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9OSPP+ldjdoAAAAAAAAAADurFW67qxV//Ct
|
||||
Vv/vrVb/6adR/92dSP/Skz//zo47/8+PO//QkDz/0JA8/9CQPP/QkDz/0JJB/9Smaf/dv5j/+fXw////
|
||||
////////////////////////////////////////////////////////////////////////2riM/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9KRPP/FiTzGoHQ6Ae6s
|
||||
VRrurFXz7qxV/+6sVf/urFX/761W//CuVv/sqlT/4qFM/9aVQv/Pjzv/zo47/9CQPP/VqnH/8+vh////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////9aueP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9GQ
|
||||
PP/Ojzz4oHM5Iu6sVU3urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/vrVX/8K5W/+6sVf/mpU//2plF/9q6
|
||||
j///////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////n18P/Rm1P/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/SkTz/rXs6Vu6sVYPurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/wrVb/6r2B////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////o18L/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/SkTz/v4Y8j+6sVa3urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFb/9Onb///////+/v3/////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////1qx2/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/SkTz/wYc7uO6sVcrurFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/trlr/67Zy/+m+h//y5NP/////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////7+TW/9CQPf/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/RkTz/yYw80e6s
|
||||
VdvurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/6sme//v49P//////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////9arc//QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/RkDz/zI083+6sVeLurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/u2b3/////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////+fVvv/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/zo885u6sVeLurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+vL
|
||||
of//////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////r28v/Rl0r/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/zo885u6sVdrurFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7LBj//r28f//////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///Vq3P/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/RkDz/zI083+6sVcrurFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/6cKO////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////ewp7/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/RkTz/yYw80O6s
|
||||
VazurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7NOy//z59v/u2b7/+fPr////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////n1sD/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/SkTz/xIo8uO6sVYHurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/67Zx/+2uWf/qy6H/////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////v5dj/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/SkTz/vYU8je6sVUvurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+nC
|
||||
kP///////////////////////////////////////////////////////fz6//fu5P/w4Mv/793F//7+
|
||||
/v/////////////////////////////////////////////////////////////////17eT/0JA//86O
|
||||
Ov/Pjzv/0JA8/9CQPP/QkDz/0JA8/9CQPP/SkTz/tYA8Vu6sVRnurFXy7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7a5b//fw5///////////////////////////////////////+/jz/+7av//pwIr/67Jn/+6t
|
||||
Vv/urFX/6rd1////////////////////////////////////////////////////////////////////
|
||||
///48ej/6KhU/92cR//Tkj7/zo47/8+PO//QkDz/0JA8/9GQPP/Pjzz3rXw8IAAAAADurFW37qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/6b2D//////////////////////////////////nz6//qyZ3/7K9h/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/6rl6////////////////////////////////////////////////////
|
||||
///////////////////9/Pr/6r6C//CuV//sqlT/4aBL/9aWQv/Pjzv/zo47/9GRPP/GijzCp3g8AQAA
|
||||
AADurFVb7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/68yk///////////////////////+/fz/68+q/+2v
|
||||
XP/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/6rNr//7+/f//////////////////////////////
|
||||
/////////////////////////////////////////////+nEk//vrVX/8K5W/+6sVf/lpE//2ZlF/9KR
|
||||
Pf+zfzpoAAAAAAAAAADurFUP7qxV5u6sVf/urFX/7qxV/+6sVf/urFX/7NGu//////////////////fu
|
||||
5P/quHb/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxW//Xr3///////////////
|
||||
///////////////////////////////////////////////////7+PP/+vXu///////pxJL/7qxV/+6s
|
||||
Vf/wrVb/8K1W/+mnUevUmUsUAAAAAAAAAAAAAAAA7qxVgO6sVf/urFX/7qxV/+6sVf/urFX/6sqg////
|
||||
////////8eLO/+yvX//urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+nF
|
||||
lf/////////////////////////////////////////////////////////////////8+vf/6cWV/+q5
|
||||
ev/s07H/6rd1/+6sVf/urFX/7qxV/++tVogAAAAAAAAAAAAAAAAAAAAA7qxVFe6sVejurFX/7qxV/+6s
|
||||
Vf/urFX/6bp8///////w4Mn/7a1Z/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/x4cz////////////////////////////////////////////////////+//7+
|
||||
/v/////////+/+m8gP/urFX/7a1Y/+6sVf/urFX/7qxV7O6sVRoAAAAAAAAAAAAAAAAAAAAAAAAAAO6s
|
||||
VWburFX/7qxV/+6sVf/urFX/7a5b/+zTs//sr1//7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/sr1//8uPQ////////////////////////////////////
|
||||
////////6suj/+qzav/pxpj/9Onb//n07f/rr2D/7qxV/+6sVf/urFX/7qxVbwAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAO6sVQPurFW67qxV/+6sVf/urFX/7qxV/+6sVv/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7axY/+nGl//27uP/////////
|
||||
/////////fz6//Lj0P/qunv/7qxV/+6sVf/urFX/7qxV/+m9gv/qt3X/7qxV/+6sVf/urFXB7qxVBQAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADurFUg7qxV6O6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/qtnP/6sqg/+3WuP/s07P/6cSS/+uyZf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVv/trVr/7qxV/+6s
|
||||
VezurFUlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7qxVRu6sVfjurFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV++6sVUwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO6s
|
||||
VVzurFX97qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxVYwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAADurFVd7qxV+O6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVfvurFVjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7qxVR+6sVeXurFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV6O6sVU0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO6sVSPurFW37qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFW77qxVJgAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AADurFUB7qxVae6sVeTurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV5+6sVW3urFUDAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAO6sVRjurFWA7qxV4u6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVeTurFWE7qxVGwAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7qxVEu6sVV7urFW17qxV7+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVfDurFW47qxVX+6s
|
||||
VRMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAA7qxVG+6sVUzurFWA7qxVrO6sVcnurFXa7qxV4u6sVeLurFXa7qxVye6sVa3urFWC7qxVTe6s
|
||||
VR0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAP//gAH//6gr//wAAD//qCv/8AAAD/+oK//AAAAD/6gr/4AAAAH/qCv/AAAAAP+oK/4A
|
||||
AAAAf6gr/AAAAAA/qCv4AAAAAB+oK/AAAAAAD6gr4AAAAAAHqCvgAAAAAAeoK8AAAAAAA6grwAAAAAAD
|
||||
qCuAAAAAAAGoK4AAAAAAAagrgAAAAAAAqCsAAAAAAACoKwAAAAAAAKgrAAAAAAAAqCsAAAAAAACoKwAA
|
||||
AAAAAKgrAAAAAAAAqCsAAAAAAACoKwAAAAAAAKgrAAAAAAAAqCsAAAAAAACoKwAAAAAAAKgrAAAAAAAA
|
||||
qCsAAAAAAACoKwAAAAAAAKgrgAAAAAAAqCuAAAAAAAGoK4AAAAAAAagrwAAAAAADqCvAAAAAAAOoK+AA
|
||||
AAAAB6gr4AAAAAAHqCvwAAAAAA+oK/gAAAAAH6gr/AAAAAA/qCv+AAAAAH+oK/8AAAAA/6gr/4AAAAH/
|
||||
qCv/wAAAA/+oK//wAAAP/6gr//wAAD//qCv//4AB//+oKygAAAAgAAAAQAAAAAEAIAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApHY4FrR/
|
||||
Ole+hjuVxos7w8uNPNvOjzznzo8858uNPNzGijzEvoU7l7WBO1mpejsYAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqHg5Kr6F
|
||||
O5XLjTvo0pE8/9ORPP/SkTz/0ZE8/9CQPP/QkDz/0ZE8/9KRPP/TkTz/0pE8/82OPOq/hjyZrXw7LAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAo3U6CbuE
|
||||
O4bPjzz005I8/9GRPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0ZE8/9OS
|
||||
PP/PkDz1vYU8iah6PAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKR3
|
||||
OibGizvG05E8/9GQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9GQPP/TkTz/yIs8yqx8PCkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AACsezsxy40849KRPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/SkTz/zI485rKAPTUAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAApXc6JMuNPOTSkTz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/SkTz/zI485659PCcAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAKd4OwnHizzJ0pE8/9CQPP/QkDz/0JA9/9OgYP/YtIX/38Sh/+LMr//iy67/3sOf/9iy
|
||||
gv/Tn13/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/SkTz/x4s8zaV4
|
||||
OwsAAAAAAAAAAAAAAAAAAAAAu4Q8hNOSPP/QkDz/0JA8/9GXS//YtIX/6NfC//Xv5//8+vj/////////
|
||||
///8+vf/9O3k/+fVvv/YsYH/0JRF/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/Tkjz/u4Q7iQAAAAAAAAAAAAAAAKx7OyTPjzz10ZA8/9GXS//Ztoj/8une////////////////////
|
||||
///////////////////////////////////38uz/3L2V/9CRP//QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9GRPP/Ojzz3onU5KQAAAAAAAAAAxo1BlNGQO//Ojjv/0JNC/9GWSP/QlET/0ZpR/9au
|
||||
eP/n1b3//v79////////////////////////////////////////////7+TV/9GdWP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9SSPP+4gjqaAAAAAPGuVhHvrVXq5qRO/9qZRP/QkT3/zo46/8+P
|
||||
O//QkDz/0JA8/9CQPP/To2P/8+vh////////////////////////////////////////////+fXw/9Oh
|
||||
Yf/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0pE8/8qMO+2WbTYV7qxVUe6sVf/wrVb/761W/+mo
|
||||
Uv/enUj/05M//86PO//PkD7/3cCZ//Lp3v/7+PT/////////////////////////////////////////
|
||||
////////+PTv/9GcVf/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/05I8/699OljurFWR7qxV/+6s
|
||||
Vf/urFX/761W//CuV//sqlT/4qJQ/+zdyf//////////////////////////////////////////////
|
||||
////////////////////////7+TV/9CRP//QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/Tkjz/vIQ7mO6s
|
||||
Vb/urFX/7qxV/+6sVf/urFX/7qxV/++tVf/ry5///v79//7+/f//////////////////////////////
|
||||
////////////////////////////////////////2rqO/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9KR
|
||||
PP/GijvF7qxV2u6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+u1bv/t1bb//fv4////////////////////
|
||||
///////////////////////////////////////////////////48+3/0ZRG/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0ZA8/8uNPNzurFXl7qxV/+6sVf/urFX/7qxV/+6sVf/rsWP/9OjZ////////////////////
|
||||
///////////////////////////////////////////////////////////////////YtIX/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/QkDz/zo886O6sVeXurFX/7qxV/+6sVf/urFX/7qxW//Hiz///////////////
|
||||
/////////////////////////////////////////////////////////////////////////////+rb
|
||||
yP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/Ojzzo7qxV2e6sVf/urFX/7qxV/+6sVf/quHb/////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////+fXw/9CURf/QkDz/0JA8/9CQPP/QkDz/0ZA8/8yOPNzurFW/7qxV/+6sVf/urFX/7qxV/+nA
|
||||
if/s1LT/9Onb////////////////////////////////////////////////////////////////////
|
||||
////////////////////////0Z5b/9CQPP/QkDz/0JA8/9CQPP/SkTz/x4s8xe6sVY/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+nEk//+/fz////////////////////////////+/v3/+PHo//To2f/+/v7/////////
|
||||
///////////////////////////////////VqGz/zo46/8+PO//QkDz/0JA8/9KRPP+/hjyX7qxVUO6s
|
||||
Vf/urFX/7qxV/+6sVf/ssGH//Pr2///////////////////////y49D/6cOR/+qyaf/urVb/6rJp////
|
||||
/////////////////////////////////////////////+e7f//goEr/1ZVB/8+PO//Ojzv/0pE8/7WB
|
||||
PFfurFUQ7qxV6O6sVf/urFX/7qxV/+m+hv/////////////////z5tX/6rl6/+6sVf/urFX/7qxV/+6s
|
||||
Vf/qs2r////+////////////////////////////////////////////+PLp/+y4c//urFX/5aRO/9qZ
|
||||
RP/Mjjztp3g6FQAAAADurFWQ7qxV/+6sVf/urFX/6cKP////////////682m/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+2sV//17N////////////////////////////////////////7+/v/06Nn/8uTT/+qz
|
||||
a//wrVb/761W/+alUZYAAAAAAAAAAO6sVSHurFX07qxV/+6sVf/quXj//////+nDkf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+m+hP////////////////////////////////////////////jy
|
||||
6v/qtnH/67Fj/+6sVf/urFX2761WJQAAAAAAAAAAAAAAAO6sVX7urFX/7qxV/+2vW//pvID/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+nEkv/7+PT//////////////////fz6/+rJ
|
||||
n//rtW7/682n/+zTs//urFb/7qxV/+6sVYQAAAAAAAAAAAAAAAAAAAAA7qxVB+6sVcTurFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+u0bP/qy6H/7da4/+vN
|
||||
pv/qtnP/7qxV/+6sVf/urFX/7K9f/+2sWP/urFXI7qxVCQAAAAAAAAAAAAAAAAAAAAAAAAAA7qxVIO6s
|
||||
VeDurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV4+6sVSMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAA7qxVLO6sVd/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVeLurFUwAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAA7qxVIu6sVcHurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFXE7qxVJAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7qxVBu6sVX/urFXw7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFXy7qxVgu6sVQgAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO6sVSXurFWP7qxV5e6s
|
||||
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFXl7qxVke6sVScAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AADurFUS7qxVUO6sVY7urFW97qxV2O6sVeXurFXl7qxV2O6sVb7urFWP7qxVUu6sVRMAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8AD//8AAP/8AAA/+AAAH/AAAA/gAAAHwAAAA8AA
|
||||
AAOAAAABgAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAA
|
||||
AAGAAAABwAAAA8AAAAPgAAAH8AAAD/gAAB/8AAA//wAA///AA/8oAAAAEAAAACAAAAABACAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKx7Oja9hTqTyYw8zs6PPOfOjzznyYw8zr6G
|
||||
PJSvfjw3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAo3Y7CbyEO5PRkDz805I8/9GRPP/QkDz/0JA8/9GR
|
||||
PP/TkTz/0ZA8/b+GPJWrfDwKAAAAAAAAAAAAAAAAp3g8CcSJPLfTkjz/0ZA8/9CQPP/QkDz/0JA8/9CQ
|
||||
PP/QkDz/0JA8/9CQPP/Tkjz/xoo8uqx8PQoAAAAAAAAAAL2FPJPUk0D/06Rm/92/mf/izK//4sut/9u6
|
||||
j//Snlr/0JA8/9CQPP/QkDz/0JA8/9OSPP+9hTyWAAAAALiEQDLQkDz90JpS/93Amf/u4tP/////////
|
||||
/////////fv5/+DIqP/QkkH/0JA8/9CQPP/RkDz/0JA8/qN1OTTtq1WS56VP/9qaRf/RkT3/0JdO/+rc
|
||||
yP//////////////////////693L/9CSQf/QkDz/0JA8/9SSPP+3gTqW761Vz++tVv/wrVb/6K5h//fy
|
||||
6v/////////////////////////////////gx6b/0JA8/9CQPP/RkTz/yYw80e6sVefurFX/7qxV/+q+
|
||||
hP/69vD//////////////////////////////////fz7/9GbU//QkDz/0JA8/8+PPOnurFXn7qxV/+yw
|
||||
Y//79/P////////////////////////////////////////////ZuIz/0JA8/9CQPP/Pjzzp7qxVz+6s
|
||||
Vf/sr2H/8eLO/////////////v7+//n07f/+/v7/////////////////48yu/8+PO//Qjzv/yYs80e6s
|
||||
VZLurFX/67Jm//7+/f/8+fb/68yl/+qza//rsmf//v37//////////////////Pn2P/ip1n/2phC/8CH
|
||||
PJXurFUw7qxV/eq2c//169//6rNr/+6sVf/urFX/7q1W//Pm1f/////////////////8+fX/67+G/++t
|
||||
Vf3lpVEyAAAAAO6sVZHtrVr/7a9c/+6sVf/urFX/7qxV/+6sVf/rsWP/7de5//Lj0P/pxJL/67Zy/+qz
|
||||
av/urFWTAAAAAAAAAADurFUI7qxVtO6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
Vf/urFW37qxVCQAAAAAAAAAAAAAAAO6sVQjurFWQ7qxV++6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
|
||||
VfvurFWS7qxVCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO6sVTPurFWQ7qxVzO6sVeburFXm7qxVzO6s
|
||||
VZDurFU0AAAAAAAAAAAAAAAAAAAAAPAPrEHAA6xBgAGsQYABrEEAAKxBAACsQQAArEEAAKxBAACsQQAA
|
||||
rEEAAKxBAACsQYABrEGAAaxBwAOsQfAPrEE=
|
||||
</value>
|
||||
</data>
|
||||
</root>
|
@@ -12,7 +12,15 @@ namespace TweetDck.Core.Other.Settings{
|
||||
public bool Ready { get; set; }
|
||||
|
||||
public BaseTabSettings(){
|
||||
Padding = new Padding(6,6,6,6);
|
||||
Padding = new Padding(6);
|
||||
}
|
||||
|
||||
public virtual void OnClosing(){}
|
||||
|
||||
protected static void PromptRestart(){
|
||||
if (MessageBox.Show("The application must restart for the setting to take place. Do you want to restart now?", Program.BrandName+" Settings", MessageBoxButtons.YesNo, MessageBoxIcon.Information) == DialogResult.Yes){
|
||||
Program.Restart();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
208
Core/Other/Settings/Dialogs/DialogSettingsCSS.Designer.cs
generated
Normal file
208
Core/Other/Settings/Dialogs/DialogSettingsCSS.Designer.cs
generated
Normal file
@@ -0,0 +1,208 @@
|
||||
namespace TweetDck.Core.Other.Settings.Dialogs {
|
||||
partial class DialogSettingsCSS {
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing) {
|
||||
if (disposing && (components != null)) {
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent() {
|
||||
this.components = new System.ComponentModel.Container();
|
||||
this.textBoxBrowserCSS = new System.Windows.Forms.TextBox();
|
||||
this.btnCancel = new System.Windows.Forms.Button();
|
||||
this.btnApply = new System.Windows.Forms.Button();
|
||||
this.splitContainer = new System.Windows.Forms.SplitContainer();
|
||||
this.labelBrowser = new System.Windows.Forms.Label();
|
||||
this.labelNotification = new System.Windows.Forms.Label();
|
||||
this.textBoxNotificationCSS = new System.Windows.Forms.TextBox();
|
||||
this.labelWarning = new System.Windows.Forms.Label();
|
||||
this.btnOpenWiki = new System.Windows.Forms.Button();
|
||||
this.timerTestBrowser = new System.Windows.Forms.Timer(this.components);
|
||||
((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit();
|
||||
this.splitContainer.Panel1.SuspendLayout();
|
||||
this.splitContainer.Panel2.SuspendLayout();
|
||||
this.splitContainer.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// textBoxBrowserCSS
|
||||
//
|
||||
this.textBoxBrowserCSS.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.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.Margin = new System.Windows.Forms.Padding(0, 3, 0, 0);
|
||||
this.textBoxBrowserCSS.Multiline = true;
|
||||
this.textBoxBrowserCSS.Name = "textBoxBrowserCSS";
|
||||
this.textBoxBrowserCSS.ScrollBars = System.Windows.Forms.ScrollBars.Both;
|
||||
this.textBoxBrowserCSS.Size = new System.Drawing.Size(373, 253);
|
||||
this.textBoxBrowserCSS.TabIndex = 0;
|
||||
this.textBoxBrowserCSS.WordWrap = false;
|
||||
this.textBoxBrowserCSS.KeyUp += new System.Windows.Forms.KeyEventHandler(this.textBoxBrowserCSS_KeyUp);
|
||||
//
|
||||
// btnCancel
|
||||
//
|
||||
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnCancel.Location = new System.Drawing.Point(654, 287);
|
||||
this.btnCancel.Name = "btnCancel";
|
||||
this.btnCancel.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||
this.btnCancel.Size = new System.Drawing.Size(56, 23);
|
||||
this.btnCancel.TabIndex = 1;
|
||||
this.btnCancel.Text = "Cancel";
|
||||
this.btnCancel.UseVisualStyleBackColor = true;
|
||||
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
|
||||
//
|
||||
// btnApply
|
||||
//
|
||||
this.btnApply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnApply.Location = new System.Drawing.Point(716, 287);
|
||||
this.btnApply.Name = "btnApply";
|
||||
this.btnApply.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||
this.btnApply.Size = new System.Drawing.Size(56, 23);
|
||||
this.btnApply.TabIndex = 2;
|
||||
this.btnApply.Text = "Apply";
|
||||
this.btnApply.UseVisualStyleBackColor = true;
|
||||
this.btnApply.Click += new System.EventHandler(this.btnApply_Click);
|
||||
//
|
||||
// splitContainer
|
||||
//
|
||||
this.splitContainer.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.splitContainer.Location = new System.Drawing.Point(12, 12);
|
||||
this.splitContainer.Name = "splitContainer";
|
||||
//
|
||||
// splitContainer.Panel1
|
||||
//
|
||||
this.splitContainer.Panel1.Controls.Add(this.labelBrowser);
|
||||
this.splitContainer.Panel1.Controls.Add(this.textBoxBrowserCSS);
|
||||
this.splitContainer.Panel1MinSize = 64;
|
||||
//
|
||||
// splitContainer.Panel2
|
||||
//
|
||||
this.splitContainer.Panel2.Controls.Add(this.labelNotification);
|
||||
this.splitContainer.Panel2.Controls.Add(this.textBoxNotificationCSS);
|
||||
this.splitContainer.Panel2MinSize = 64;
|
||||
this.splitContainer.Size = new System.Drawing.Size(760, 269);
|
||||
this.splitContainer.SplitterDistance = 373;
|
||||
this.splitContainer.SplitterWidth = 5;
|
||||
this.splitContainer.TabIndex = 5;
|
||||
//
|
||||
// labelBrowser
|
||||
//
|
||||
this.labelBrowser.AutoSize = true;
|
||||
this.labelBrowser.Location = new System.Drawing.Point(-3, 0);
|
||||
this.labelBrowser.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
|
||||
this.labelBrowser.Name = "labelBrowser";
|
||||
this.labelBrowser.Size = new System.Drawing.Size(45, 13);
|
||||
this.labelBrowser.TabIndex = 1;
|
||||
this.labelBrowser.Text = "Browser";
|
||||
//
|
||||
// labelNotification
|
||||
//
|
||||
this.labelNotification.AutoSize = true;
|
||||
this.labelNotification.Location = new System.Drawing.Point(-3, 0);
|
||||
this.labelNotification.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
|
||||
this.labelNotification.Name = "labelNotification";
|
||||
this.labelNotification.Size = new System.Drawing.Size(60, 13);
|
||||
this.labelNotification.TabIndex = 2;
|
||||
this.labelNotification.Text = "Notification";
|
||||
//
|
||||
// textBoxNotificationCSS
|
||||
//
|
||||
this.textBoxNotificationCSS.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.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.Margin = new System.Windows.Forms.Padding(0, 3, 0, 0);
|
||||
this.textBoxNotificationCSS.Multiline = true;
|
||||
this.textBoxNotificationCSS.Name = "textBoxNotificationCSS";
|
||||
this.textBoxNotificationCSS.ScrollBars = System.Windows.Forms.ScrollBars.Both;
|
||||
this.textBoxNotificationCSS.Size = new System.Drawing.Size(373, 253);
|
||||
this.textBoxNotificationCSS.TabIndex = 1;
|
||||
this.textBoxNotificationCSS.WordWrap = false;
|
||||
//
|
||||
// labelWarning
|
||||
//
|
||||
this.labelWarning.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.labelWarning.AutoSize = true;
|
||||
this.labelWarning.Location = new System.Drawing.Point(91, 292);
|
||||
this.labelWarning.Name = "labelWarning";
|
||||
this.labelWarning.Size = new System.Drawing.Size(341, 13);
|
||||
this.labelWarning.TabIndex = 6;
|
||||
this.labelWarning.Text = "The code is not validated, please make sure there are no syntax errors.";
|
||||
//
|
||||
// btnOpenWiki
|
||||
//
|
||||
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.Name = "btnOpenWiki";
|
||||
this.btnOpenWiki.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||
this.btnOpenWiki.Size = new System.Drawing.Size(73, 23);
|
||||
this.btnOpenWiki.TabIndex = 7;
|
||||
this.btnOpenWiki.Text = "Open Wiki";
|
||||
this.btnOpenWiki.UseVisualStyleBackColor = true;
|
||||
this.btnOpenWiki.Click += new System.EventHandler(this.btnOpenWiki_Click);
|
||||
//
|
||||
// timerTestBrowser
|
||||
//
|
||||
this.timerTestBrowser.Interval = 500;
|
||||
this.timerTestBrowser.Tick += new System.EventHandler(this.timerTestBrowser_Tick);
|
||||
//
|
||||
// DialogSettingsCSS
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(784, 322);
|
||||
this.Controls.Add(this.btnOpenWiki);
|
||||
this.Controls.Add(this.labelWarning);
|
||||
this.Controls.Add(this.splitContainer);
|
||||
this.Controls.Add(this.btnApply);
|
||||
this.Controls.Add(this.btnCancel);
|
||||
this.MinimumSize = new System.Drawing.Size(600, 160);
|
||||
this.Name = "DialogSettingsCSS";
|
||||
this.ShowIcon = false;
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.splitContainer.Panel1.ResumeLayout(false);
|
||||
this.splitContainer.Panel1.PerformLayout();
|
||||
this.splitContainer.Panel2.ResumeLayout(false);
|
||||
this.splitContainer.Panel2.PerformLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.splitContainer)).EndInit();
|
||||
this.splitContainer.ResumeLayout(false);
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.TextBox textBoxBrowserCSS;
|
||||
private System.Windows.Forms.Button btnCancel;
|
||||
private System.Windows.Forms.Button btnApply;
|
||||
private System.Windows.Forms.SplitContainer splitContainer;
|
||||
private System.Windows.Forms.TextBox textBoxNotificationCSS;
|
||||
private System.Windows.Forms.Label labelBrowser;
|
||||
private System.Windows.Forms.Label labelNotification;
|
||||
private System.Windows.Forms.Label labelWarning;
|
||||
private System.Windows.Forms.Button btnOpenWiki;
|
||||
private System.Windows.Forms.Timer timerTestBrowser;
|
||||
}
|
||||
}
|
60
Core/Other/Settings/Dialogs/DialogSettingsCSS.cs
Normal file
60
Core/Other/Settings/Dialogs/DialogSettingsCSS.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using TweetDck.Core.Controls;
|
||||
using TweetDck.Core.Utils;
|
||||
|
||||
namespace TweetDck.Core.Other.Settings.Dialogs{
|
||||
sealed partial class DialogSettingsCSS : Form{
|
||||
public string BrowserCSS{
|
||||
get{
|
||||
return textBoxBrowserCSS.Text;
|
||||
}
|
||||
}
|
||||
|
||||
public string NotificationCSS{
|
||||
get{
|
||||
return textBoxNotificationCSS.Text;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Action<string> reinjectBrowserCSS;
|
||||
|
||||
public DialogSettingsCSS(Action<string> reinjectBrowserCSS){
|
||||
InitializeComponent();
|
||||
|
||||
Text = Program.BrandName+" Settings - CSS";
|
||||
|
||||
this.reinjectBrowserCSS = reinjectBrowserCSS;
|
||||
|
||||
textBoxBrowserCSS.EnableMultilineShortcuts();
|
||||
textBoxBrowserCSS.Text = Program.UserConfig.CustomBrowserCSS ?? "";
|
||||
|
||||
textBoxNotificationCSS.EnableMultilineShortcuts();
|
||||
textBoxNotificationCSS.Text = Program.UserConfig.CustomNotificationCSS ?? "";
|
||||
}
|
||||
|
||||
private void textBoxBrowserCSS_KeyUp(object sender, KeyEventArgs e){
|
||||
timerTestBrowser.Stop();
|
||||
timerTestBrowser.Start();
|
||||
}
|
||||
|
||||
private void timerTestBrowser_Tick(object sender, EventArgs e){
|
||||
reinjectBrowserCSS(textBoxBrowserCSS.Text);
|
||||
timerTestBrowser.Stop();
|
||||
}
|
||||
|
||||
private void btnOpenWiki_Click(object sender, EventArgs e){
|
||||
BrowserUtils.OpenExternalBrowser("https://github.com/chylex/TweetDuck/wiki");
|
||||
}
|
||||
|
||||
private void btnApply_Click(object sender, EventArgs e){
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void btnCancel_Click(object sender, EventArgs e){
|
||||
DialogResult = DialogResult.Cancel;
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
120
Core/Other/Settings/Dialogs/DialogSettingsCefArgs.Designer.cs
generated
Normal file
120
Core/Other/Settings/Dialogs/DialogSettingsCefArgs.Designer.cs
generated
Normal file
@@ -0,0 +1,120 @@
|
||||
namespace TweetDck.Core.Other.Settings.Dialogs {
|
||||
partial class DialogSettingsCefArgs {
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing) {
|
||||
if (disposing && (components != null)) {
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent() {
|
||||
this.textBoxArgs = new System.Windows.Forms.TextBox();
|
||||
this.btnCancel = new System.Windows.Forms.Button();
|
||||
this.btnApply = new System.Windows.Forms.Button();
|
||||
this.btnHelp = new System.Windows.Forms.Button();
|
||||
this.labelWarning = new System.Windows.Forms.Label();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// textBoxArgs
|
||||
//
|
||||
this.textBoxArgs.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.textBoxArgs.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
||||
this.textBoxArgs.Location = new System.Drawing.Point(12, 28);
|
||||
this.textBoxArgs.Multiline = true;
|
||||
this.textBoxArgs.Name = "textBoxArgs";
|
||||
this.textBoxArgs.Size = new System.Drawing.Size(460, 193);
|
||||
this.textBoxArgs.TabIndex = 0;
|
||||
//
|
||||
// btnCancel
|
||||
//
|
||||
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnCancel.Location = new System.Drawing.Point(354, 227);
|
||||
this.btnCancel.Name = "btnCancel";
|
||||
this.btnCancel.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||
this.btnCancel.Size = new System.Drawing.Size(56, 23);
|
||||
this.btnCancel.TabIndex = 1;
|
||||
this.btnCancel.Text = "Cancel";
|
||||
this.btnCancel.UseVisualStyleBackColor = true;
|
||||
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
|
||||
//
|
||||
// btnApply
|
||||
//
|
||||
this.btnApply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnApply.Location = new System.Drawing.Point(416, 227);
|
||||
this.btnApply.Name = "btnApply";
|
||||
this.btnApply.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||
this.btnApply.Size = new System.Drawing.Size(56, 23);
|
||||
this.btnApply.TabIndex = 2;
|
||||
this.btnApply.Text = "Apply";
|
||||
this.btnApply.UseVisualStyleBackColor = true;
|
||||
this.btnApply.Click += new System.EventHandler(this.btnApply_Click);
|
||||
//
|
||||
// btnHelp
|
||||
//
|
||||
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.Name = "btnHelp";
|
||||
this.btnHelp.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||
this.btnHelp.Size = new System.Drawing.Size(124, 23);
|
||||
this.btnHelp.TabIndex = 3;
|
||||
this.btnHelp.Text = "List of Chromium Args";
|
||||
this.btnHelp.UseVisualStyleBackColor = true;
|
||||
this.btnHelp.Click += new System.EventHandler(this.btnHelp_Click);
|
||||
//
|
||||
// labelWarning
|
||||
//
|
||||
this.labelWarning.AutoSize = true;
|
||||
this.labelWarning.Location = new System.Drawing.Point(12, 9);
|
||||
this.labelWarning.Margin = new System.Windows.Forms.Padding(3, 0, 3, 3);
|
||||
this.labelWarning.Name = "labelWarning";
|
||||
this.labelWarning.Size = new System.Drawing.Size(423, 13);
|
||||
this.labelWarning.TabIndex = 4;
|
||||
this.labelWarning.Text = "Warning: Some arguments may cause the program to stop working, edit at your own r" +
|
||||
"isk.";
|
||||
//
|
||||
// DialogSettingsCefArgs
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(484, 262);
|
||||
this.Controls.Add(this.labelWarning);
|
||||
this.Controls.Add(this.btnHelp);
|
||||
this.Controls.Add(this.btnApply);
|
||||
this.Controls.Add(this.btnCancel);
|
||||
this.Controls.Add(this.textBoxArgs);
|
||||
this.MinimumSize = new System.Drawing.Size(500, 160);
|
||||
this.Name = "DialogSettingsCefArgs";
|
||||
this.ShowIcon = false;
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.TextBox textBoxArgs;
|
||||
private System.Windows.Forms.Button btnCancel;
|
||||
private System.Windows.Forms.Button btnApply;
|
||||
private System.Windows.Forms.Button btnHelp;
|
||||
private System.Windows.Forms.Label labelWarning;
|
||||
}
|
||||
}
|
51
Core/Other/Settings/Dialogs/DialogSettingsCefArgs.cs
Normal file
51
Core/Other/Settings/Dialogs/DialogSettingsCefArgs.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using TweetDck.Core.Controls;
|
||||
using TweetDck.Core.Utils;
|
||||
|
||||
namespace TweetDck.Core.Other.Settings.Dialogs{
|
||||
sealed partial class DialogSettingsCefArgs : Form{
|
||||
public string CefArgs{
|
||||
get{
|
||||
return textBoxArgs.Text;
|
||||
}
|
||||
}
|
||||
|
||||
public DialogSettingsCefArgs(){
|
||||
InitializeComponent();
|
||||
|
||||
Text = Program.BrandName+" Settings - CEF Arguments";
|
||||
|
||||
textBoxArgs.EnableMultilineShortcuts();
|
||||
textBoxArgs.Text = Program.UserConfig.CustomCefArgs ?? "";
|
||||
textBoxArgs.Select(textBoxArgs.Text.Length, 0);
|
||||
}
|
||||
|
||||
private void btnHelp_Click(object sender, EventArgs e){
|
||||
BrowserUtils.OpenExternalBrowser("http://peter.sh/experiments/chromium-command-line-switches/");
|
||||
}
|
||||
|
||||
private void btnApply_Click(object sender, EventArgs e){
|
||||
string prevArgs = Program.UserConfig.CustomCefArgs;
|
||||
|
||||
if (CefArgs == prevArgs){
|
||||
DialogResult = DialogResult.Cancel;
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
|
||||
int count = CommandLineArgsParser.ReadCefArguments(CefArgs).Count;
|
||||
string prompt = count == 0 && !string.IsNullOrWhiteSpace(prevArgs) ? "All arguments will be removed from the settings. Continue?" : count+(count == 1 ? " argument" : " arguments")+" will be added to the settings. Continue?";
|
||||
|
||||
if (MessageBox.Show(prompt, "Confirm CEF Arguments", MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.OK){
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void btnCancel_Click(object sender, EventArgs e){
|
||||
DialogResult = DialogResult.Cancel;
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
126
Core/Other/Settings/Dialogs/DialogSettingsExport.Designer.cs
generated
Normal file
126
Core/Other/Settings/Dialogs/DialogSettingsExport.Designer.cs
generated
Normal file
@@ -0,0 +1,126 @@
|
||||
namespace TweetDck.Core.Other.Settings.Dialogs {
|
||||
partial class DialogSettingsExport {
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing) {
|
||||
if (disposing && (components != null)) {
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent() {
|
||||
this.components = new System.ComponentModel.Container();
|
||||
this.btnCancel = new System.Windows.Forms.Button();
|
||||
this.btnApply = new System.Windows.Forms.Button();
|
||||
this.cbConfig = new System.Windows.Forms.CheckBox();
|
||||
this.cbSession = new System.Windows.Forms.CheckBox();
|
||||
this.cbPluginData = new System.Windows.Forms.CheckBox();
|
||||
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// btnCancel
|
||||
//
|
||||
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.Name = "btnCancel";
|
||||
this.btnCancel.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||
this.btnCancel.Size = new System.Drawing.Size(56, 23);
|
||||
this.btnCancel.TabIndex = 0;
|
||||
this.btnCancel.Text = "Cancel";
|
||||
this.btnCancel.UseVisualStyleBackColor = true;
|
||||
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
|
||||
//
|
||||
// btnApply
|
||||
//
|
||||
this.btnApply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnApply.AutoSize = true;
|
||||
this.btnApply.Location = new System.Drawing.Point(117, 97);
|
||||
this.btnApply.Name = "btnApply";
|
||||
this.btnApply.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||
this.btnApply.Size = new System.Drawing.Size(53, 23);
|
||||
this.btnApply.TabIndex = 1;
|
||||
this.btnApply.UseVisualStyleBackColor = true;
|
||||
this.btnApply.Click += new System.EventHandler(this.btnApply_Click);
|
||||
//
|
||||
// cbConfig
|
||||
//
|
||||
this.cbConfig.AutoSize = true;
|
||||
this.cbConfig.Location = new System.Drawing.Point(13, 13);
|
||||
this.cbConfig.Name = "cbConfig";
|
||||
this.cbConfig.Size = new System.Drawing.Size(106, 17);
|
||||
this.cbConfig.TabIndex = 2;
|
||||
this.cbConfig.Text = "Program Settings";
|
||||
this.toolTip.SetToolTip(this.cbConfig, "Interface, notification, and update settings.\r\nIncludes a list of disabled plugin" +
|
||||
"s.");
|
||||
this.cbConfig.UseVisualStyleBackColor = true;
|
||||
this.cbConfig.CheckedChanged += new System.EventHandler(this.cbConfig_CheckedChanged);
|
||||
//
|
||||
// cbSession
|
||||
//
|
||||
this.cbSession.AutoSize = true;
|
||||
this.cbSession.Location = new System.Drawing.Point(13, 37);
|
||||
this.cbSession.Name = "cbSession";
|
||||
this.cbSession.Size = new System.Drawing.Size(92, 17);
|
||||
this.cbSession.TabIndex = 3;
|
||||
this.cbSession.Text = "Login Session";
|
||||
this.toolTip.SetToolTip(this.cbSession, "A token that allows logging into the\r\ncurrent TweetDeck account.");
|
||||
this.cbSession.UseVisualStyleBackColor = true;
|
||||
this.cbSession.CheckedChanged += new System.EventHandler(this.cbSession_CheckedChanged);
|
||||
//
|
||||
// cbPluginData
|
||||
//
|
||||
this.cbPluginData.AutoSize = true;
|
||||
this.cbPluginData.Location = new System.Drawing.Point(13, 61);
|
||||
this.cbPluginData.Name = "cbPluginData";
|
||||
this.cbPluginData.Size = new System.Drawing.Size(81, 17);
|
||||
this.cbPluginData.TabIndex = 4;
|
||||
this.cbPluginData.Text = "Plugin Data";
|
||||
this.toolTip.SetToolTip(this.cbPluginData, "Data files generated by plugins.\r\nDoes not include the plugins themselves.");
|
||||
this.cbPluginData.UseVisualStyleBackColor = true;
|
||||
this.cbPluginData.CheckedChanged += new System.EventHandler(this.cbPluginData_CheckedChanged);
|
||||
//
|
||||
// DialogSettingsExport
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(244, 132);
|
||||
this.Controls.Add(this.cbPluginData);
|
||||
this.Controls.Add(this.cbSession);
|
||||
this.Controls.Add(this.cbConfig);
|
||||
this.Controls.Add(this.btnApply);
|
||||
this.Controls.Add(this.btnCancel);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
|
||||
this.MinimumSize = new System.Drawing.Size(200, 170);
|
||||
this.Name = "DialogSettingsExport";
|
||||
this.ShowIcon = false;
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.Button btnCancel;
|
||||
private System.Windows.Forms.Button btnApply;
|
||||
private System.Windows.Forms.CheckBox cbConfig;
|
||||
private System.Windows.Forms.CheckBox cbSession;
|
||||
private System.Windows.Forms.CheckBox cbPluginData;
|
||||
private System.Windows.Forms.ToolTip toolTip;
|
||||
}
|
||||
}
|
80
Core/Other/Settings/Dialogs/DialogSettingsExport.cs
Normal file
80
Core/Other/Settings/Dialogs/DialogSettingsExport.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using TweetDck.Core.Other.Settings.Export;
|
||||
|
||||
namespace TweetDck.Core.Other.Settings.Dialogs{
|
||||
sealed partial class DialogSettingsExport : Form{
|
||||
public static DialogSettingsExport Import(ExportFileFlags flags){
|
||||
return new DialogSettingsExport(flags);
|
||||
}
|
||||
|
||||
public static DialogSettingsExport Export(){
|
||||
return new DialogSettingsExport(ExportFileFlags.None);
|
||||
}
|
||||
|
||||
public ExportFileFlags Flags{
|
||||
get{
|
||||
return selectedFlags;
|
||||
}
|
||||
|
||||
set{
|
||||
selectedFlags = value;
|
||||
btnApply.Enabled = selectedFlags != ExportFileFlags.None;
|
||||
|
||||
cbConfig.Checked = selectedFlags.HasFlag(ExportFileFlags.Config);
|
||||
cbSession.Checked = selectedFlags.HasFlag(ExportFileFlags.Session);
|
||||
cbPluginData.Checked = selectedFlags.HasFlag(ExportFileFlags.PluginData);
|
||||
}
|
||||
}
|
||||
|
||||
private ExportFileFlags selectedFlags = ExportFileFlags.None;
|
||||
|
||||
private DialogSettingsExport(ExportFileFlags importFlags){
|
||||
InitializeComponent();
|
||||
|
||||
bool isExporting = importFlags == ExportFileFlags.None;
|
||||
|
||||
if (isExporting){
|
||||
Text = "Export Profile";
|
||||
btnApply.Text = "Export";
|
||||
Flags = ExportFileFlags.All & ~ExportFileFlags.Session;
|
||||
}
|
||||
else{
|
||||
Text = "Import Profile";
|
||||
btnApply.Text = "Import";
|
||||
Flags = importFlags;
|
||||
|
||||
cbConfig.Enabled = cbConfig.Checked;
|
||||
cbSession.Enabled = cbSession.Checked;
|
||||
cbPluginData.Enabled = cbPluginData.Checked;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetFlag(ExportFileFlags flag, bool enable){
|
||||
selectedFlags = enable ? selectedFlags | flag : selectedFlags & ~flag;
|
||||
btnApply.Enabled = selectedFlags != ExportFileFlags.None;
|
||||
}
|
||||
|
||||
private void cbConfig_CheckedChanged(object sender, EventArgs e){
|
||||
SetFlag(ExportFileFlags.Config, cbConfig.Checked);
|
||||
}
|
||||
|
||||
private void cbSession_CheckedChanged(object sender, EventArgs e){
|
||||
SetFlag(ExportFileFlags.Session, cbSession.Checked);
|
||||
}
|
||||
|
||||
private void cbPluginData_CheckedChanged(object sender, EventArgs e){
|
||||
SetFlag(ExportFileFlags.PluginData, cbPluginData.Checked);
|
||||
}
|
||||
|
||||
private void btnApply_Click(object sender, EventArgs e){
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void btnCancel_Click(object sender, EventArgs e){
|
||||
DialogResult = DialogResult.Cancel;
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
@@ -4,35 +4,44 @@ using System.Text;
|
||||
|
||||
namespace TweetDck.Core.Other.Settings.Export{
|
||||
class CombinedFileStream : IDisposable{
|
||||
public const char KeySeparator = '|';
|
||||
|
||||
private readonly Stream stream;
|
||||
|
||||
public CombinedFileStream(Stream stream){
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
public void WriteFile(string[] identifier, string path){
|
||||
WriteFile(string.Join(KeySeparator.ToString(), identifier), path);
|
||||
}
|
||||
|
||||
public void WriteFile(string identifier, string path){
|
||||
byte[] name = Encoding.UTF8.GetBytes(identifier);
|
||||
|
||||
if (name.Length > 255){
|
||||
throw new ArgumentOutOfRangeException("Identifier cannot be 256 or more characters long: "+identifier);
|
||||
}
|
||||
|
||||
byte[] contents;
|
||||
|
||||
using(FileStream fileStream = new FileStream(path,FileMode.Open,FileAccess.Read,FileShare.ReadWrite)){
|
||||
using(FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)){
|
||||
int index = 0;
|
||||
int left = (int)fileStream.Length;
|
||||
|
||||
contents = new byte[left];
|
||||
|
||||
while(left > 0){
|
||||
int read = fileStream.Read(contents,index,left);
|
||||
int read = fileStream.Read(contents, index, left);
|
||||
index += read;
|
||||
left -= read;
|
||||
}
|
||||
}
|
||||
|
||||
byte[] name = Encoding.UTF8.GetBytes(identifier);
|
||||
byte[] contentsLength = BitConverter.GetBytes(contents.Length);
|
||||
|
||||
stream.WriteByte((byte)name.Length);
|
||||
stream.Write(name,0,name.Length);
|
||||
stream.Write(contentsLength,0,4);
|
||||
stream.Write(contents,0,contents.Length);
|
||||
stream.Write(name, 0, name.Length);
|
||||
stream.Write(BitConverter.GetBytes(contents.Length), 0, 4);
|
||||
stream.Write(contents, 0, contents.Length);
|
||||
}
|
||||
|
||||
public Entry ReadFile(){
|
||||
@@ -43,15 +52,35 @@ namespace TweetDck.Core.Other.Settings.Export{
|
||||
}
|
||||
|
||||
byte[] name = new byte[nameLength];
|
||||
stream.Read(name,0,nameLength);
|
||||
stream.Read(name, 0, nameLength);
|
||||
|
||||
byte[] contentLength = new byte[4];
|
||||
stream.Read(contentLength,0,4);
|
||||
stream.Read(contentLength, 0, 4);
|
||||
|
||||
byte[] contents = new byte[BitConverter.ToInt32(contentLength,0)];
|
||||
stream.Read(contents,0,contents.Length);
|
||||
byte[] contents = new byte[BitConverter.ToInt32(contentLength, 0)];
|
||||
stream.Read(contents, 0, contents.Length);
|
||||
|
||||
return new Entry(Encoding.UTF8.GetString(name),contents);
|
||||
return new Entry(Encoding.UTF8.GetString(name), contents);
|
||||
}
|
||||
|
||||
public string SkipFile(){
|
||||
int nameLength = stream.ReadByte();
|
||||
|
||||
if (nameLength == -1){
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] name = new byte[nameLength];
|
||||
stream.Read(name, 0, nameLength);
|
||||
|
||||
byte[] contentLength = new byte[4];
|
||||
stream.Read(contentLength, 0, 4);
|
||||
|
||||
stream.Position += BitConverter.ToInt32(contentLength, 0);
|
||||
|
||||
string keyName = Encoding.UTF8.GetString(name);
|
||||
int separatorIndex = keyName.IndexOf(KeySeparator);
|
||||
return separatorIndex == -1 ? keyName : keyName.Substring(0, separatorIndex);
|
||||
}
|
||||
|
||||
public void Flush(){
|
||||
@@ -65,6 +94,20 @@ namespace TweetDck.Core.Other.Settings.Export{
|
||||
public class Entry{
|
||||
public string Identifier { get; private set; }
|
||||
|
||||
public string KeyName{
|
||||
get{
|
||||
int index = Identifier.IndexOf(KeySeparator);
|
||||
return index == -1 ? Identifier : Identifier.Substring(0, index);
|
||||
}
|
||||
}
|
||||
|
||||
public string[] KeyValue{
|
||||
get{
|
||||
int index = Identifier.IndexOf(KeySeparator);
|
||||
return index == -1 ? new string[0] : Identifier.Substring(index+1).Split(KeySeparator);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly byte[] contents;
|
||||
|
||||
public Entry(string identifier, byte[] contents){
|
||||
@@ -73,7 +116,19 @@ namespace TweetDck.Core.Other.Settings.Export{
|
||||
}
|
||||
|
||||
public void WriteToFile(string path){
|
||||
File.WriteAllBytes(path,contents);
|
||||
File.WriteAllBytes(path, contents);
|
||||
}
|
||||
|
||||
public void WriteToFile(string path, bool createDirectory){
|
||||
if (createDirectory){
|
||||
string dir = Path.GetDirectoryName(path);
|
||||
|
||||
if (!string.IsNullOrEmpty(dir)){
|
||||
Directory.CreateDirectory(dir);
|
||||
}
|
||||
}
|
||||
|
||||
File.WriteAllBytes(path, contents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
12
Core/Other/Settings/Export/ExportFileFlags.cs
Normal file
12
Core/Other/Settings/Export/ExportFileFlags.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace TweetDck.Core.Other.Settings.Export{
|
||||
[Flags]
|
||||
enum ExportFileFlags{
|
||||
None = 0,
|
||||
Config = 1,
|
||||
Session = 2,
|
||||
PluginData = 4,
|
||||
All = Config|Session|PluginData
|
||||
}
|
||||
}
|
@@ -1,28 +1,48 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using TweetDck.Plugins;
|
||||
using TweetDck.Plugins.Enums;
|
||||
|
||||
namespace TweetDck.Core.Other.Settings.Export{
|
||||
sealed class ExportManager{
|
||||
public static readonly string CookiesPath = Path.Combine(Program.StoragePath,"Cookies");
|
||||
public static readonly string TempCookiesPath = Path.Combine(Program.StoragePath,"CookiesTmp");
|
||||
private static readonly string CookiesPath = Path.Combine(Program.StoragePath, "Cookies");
|
||||
private static readonly string TempCookiesPath = Path.Combine(Program.StoragePath, "CookiesTmp");
|
||||
|
||||
public bool IsRestarting { get; private set; }
|
||||
public Exception LastException { get; private set; }
|
||||
|
||||
private readonly string file;
|
||||
private readonly PluginManager plugins;
|
||||
|
||||
public ExportManager(string file){
|
||||
public ExportManager(string file, PluginManager plugins){
|
||||
this.file = file;
|
||||
this.plugins = plugins;
|
||||
}
|
||||
|
||||
public bool Export(bool includeSession){
|
||||
public bool Export(ExportFileFlags flags){
|
||||
try{
|
||||
using(CombinedFileStream stream = new CombinedFileStream(new FileStream(file,FileMode.Create,FileAccess.Write,FileShare.None))){
|
||||
stream.WriteFile("config",Program.ConfigFilePath);
|
||||
using(CombinedFileStream stream = new CombinedFileStream(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))){
|
||||
if (flags.HasFlag(ExportFileFlags.Config)){
|
||||
stream.WriteFile("config", Program.ConfigFilePath);
|
||||
}
|
||||
|
||||
if (includeSession){
|
||||
stream.WriteFile("cookies",CookiesPath);
|
||||
if (flags.HasFlag(ExportFileFlags.PluginData)){
|
||||
foreach(Plugin plugin in plugins.Plugins){
|
||||
foreach(PathInfo path in EnumerateFilesRelative(plugin.GetPluginFolder(PluginFolder.Data))){
|
||||
try{
|
||||
stream.WriteFile(new string[]{ "plugin.data", plugin.Identifier, path.Relative }, path.Full);
|
||||
}catch(ArgumentOutOfRangeException e){
|
||||
MessageBox.Show("Could not include a plugin file in the export. "+e.Message, "Export Profile", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (flags.HasFlag(ExportFileFlags.Session)){
|
||||
stream.WriteFile("cookies", CookiesPath);
|
||||
}
|
||||
|
||||
stream.Flush();
|
||||
@@ -35,25 +55,71 @@ namespace TweetDck.Core.Other.Settings.Export{
|
||||
}
|
||||
}
|
||||
|
||||
public bool Import(){
|
||||
try{
|
||||
using(CombinedFileStream stream = new CombinedFileStream(new FileStream(file,FileMode.Open,FileAccess.Read,FileShare.None))){
|
||||
CombinedFileStream.Entry entry;
|
||||
public ExportFileFlags GetImportFlags(){
|
||||
ExportFileFlags flags = ExportFileFlags.None;
|
||||
|
||||
while((entry = stream.ReadFile()) != null){
|
||||
switch(entry.Identifier){
|
||||
try{
|
||||
using(CombinedFileStream stream = new CombinedFileStream(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.None))){
|
||||
string key;
|
||||
|
||||
while((key = stream.SkipFile()) != null){
|
||||
switch(key){
|
||||
case "config":
|
||||
entry.WriteToFile(Program.ConfigFilePath);
|
||||
Program.ReloadConfig();
|
||||
flags |= ExportFileFlags.Config;
|
||||
break;
|
||||
|
||||
case "plugin.data":
|
||||
flags |= ExportFileFlags.PluginData;
|
||||
break;
|
||||
|
||||
case "cookies":
|
||||
if (MessageBox.Show("Do you want to import the login session? This will restart "+Program.BrandName+".","Importing "+Program.BrandName+" Settings",MessageBoxButtons.YesNo,MessageBoxIcon.Question,MessageBoxDefaultButton.Button2) == DialogResult.Yes){
|
||||
entry.WriteToFile(Path.Combine(Program.StoragePath,TempCookiesPath));
|
||||
flags |= ExportFileFlags.Session;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}catch(Exception e){
|
||||
LastException = e;
|
||||
flags = ExportFileFlags.None;
|
||||
}
|
||||
|
||||
// okay to and restart, 'cookies' is always the last entry
|
||||
Process.Start(Application.ExecutablePath,"-restart -importcookies");
|
||||
Application.Exit();
|
||||
return flags;
|
||||
}
|
||||
|
||||
public bool Import(ExportFileFlags flags){
|
||||
try{
|
||||
HashSet<string> missingPlugins = new HashSet<string>();
|
||||
|
||||
using(CombinedFileStream stream = new CombinedFileStream(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.None))){
|
||||
CombinedFileStream.Entry entry;
|
||||
|
||||
while((entry = stream.ReadFile()) != null){
|
||||
switch(entry.KeyName){
|
||||
case "config":
|
||||
if (flags.HasFlag(ExportFileFlags.Config)){
|
||||
entry.WriteToFile(Program.ConfigFilePath);
|
||||
Program.ReloadConfig();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "plugin.data":
|
||||
if (flags.HasFlag(ExportFileFlags.PluginData)){
|
||||
string[] value = entry.KeyValue;
|
||||
|
||||
entry.WriteToFile(Path.Combine(Program.PluginDataPath, value[0], value[1]), true);
|
||||
|
||||
if (!plugins.IsPluginInstalled(value[0])){
|
||||
missingPlugins.Add(value[0]);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "cookies":
|
||||
if (flags.HasFlag(ExportFileFlags.Session) && MessageBox.Show("Do you want to import the login session? This will restart "+Program.BrandName+".", "Importing "+Program.BrandName+" Profile", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.Yes){
|
||||
entry.WriteToFile(Path.Combine(Program.StoragePath, TempCookiesPath));
|
||||
IsRestarting = true;
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -61,11 +127,48 @@ namespace TweetDck.Core.Other.Settings.Export{
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (IsRestarting){
|
||||
Program.Restart(new string[]{ "-importcookies" });
|
||||
}
|
||||
else{
|
||||
plugins.Reload();
|
||||
}
|
||||
|
||||
return true;
|
||||
}catch(Exception e){
|
||||
LastException = e;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void ImportCookies(){
|
||||
if (File.Exists(TempCookiesPath)){
|
||||
try{
|
||||
if (File.Exists(CookiesPath)){
|
||||
File.Delete(CookiesPath);
|
||||
}
|
||||
|
||||
File.Move(TempCookiesPath, CookiesPath);
|
||||
}catch(Exception e){
|
||||
Program.Reporter.HandleException("Profile Import Error", "Could not import the cookie file to restore login session.", true, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<PathInfo> EnumerateFilesRelative(string root){
|
||||
return Directory.Exists(root) ? Directory.EnumerateFiles(root, "*.*", SearchOption.AllDirectories).Select(fullPath => new PathInfo{
|
||||
Full = fullPath,
|
||||
Relative = fullPath.Substring(root.Length).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) // strip leading separator character
|
||||
}) : Enumerable.Empty<PathInfo>();
|
||||
}
|
||||
|
||||
private class PathInfo{
|
||||
public string Full { get; set; }
|
||||
public string Relative { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
209
Core/Other/Settings/TabSettingsAdvanced.Designer.cs
generated
209
Core/Other/Settings/TabSettingsAdvanced.Designer.cs
generated
@@ -23,51 +23,212 @@
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent() {
|
||||
this.components = new System.ComponentModel.Container();
|
||||
this.btnClearCache = new System.Windows.Forms.Button();
|
||||
this.labelMiscellaneous = new System.Windows.Forms.Label();
|
||||
this.checkHardwareAcceleration = new System.Windows.Forms.CheckBox();
|
||||
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
||||
this.btnEditCefArgs = new System.Windows.Forms.Button();
|
||||
this.btnEditCSS = new System.Windows.Forms.Button();
|
||||
this.btnRestartLog = new System.Windows.Forms.Button();
|
||||
this.btnRestart = new System.Windows.Forms.Button();
|
||||
this.btnOpenAppFolder = new System.Windows.Forms.Button();
|
||||
this.btnOpenDataFolder = new System.Windows.Forms.Button();
|
||||
this.btnReset = new System.Windows.Forms.Button();
|
||||
this.btnImport = new System.Windows.Forms.Button();
|
||||
this.btnExport = new System.Windows.Forms.Button();
|
||||
this.groupPerformance = new System.Windows.Forms.GroupBox();
|
||||
this.groupConfiguration = new System.Windows.Forms.GroupBox();
|
||||
this.groupApp = new System.Windows.Forms.GroupBox();
|
||||
this.groupPerformance.SuspendLayout();
|
||||
this.groupConfiguration.SuspendLayout();
|
||||
this.groupApp.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// btnClearCache
|
||||
//
|
||||
this.btnClearCache.Location = new System.Drawing.Point(12, 56);
|
||||
this.btnClearCache.Location = new System.Drawing.Point(6, 44);
|
||||
this.btnClearCache.Name = "btnClearCache";
|
||||
this.btnClearCache.Size = new System.Drawing.Size(171, 23);
|
||||
this.btnClearCache.TabIndex = 14;
|
||||
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.btnClearCache.UseVisualStyleBackColor = true;
|
||||
this.btnClearCache.Click += new System.EventHandler(this.btnClearCache_Click);
|
||||
//
|
||||
// labelMiscellaneous
|
||||
//
|
||||
this.labelMiscellaneous.AutoSize = true;
|
||||
this.labelMiscellaneous.Location = new System.Drawing.Point(9, 40);
|
||||
this.labelMiscellaneous.Margin = new System.Windows.Forms.Padding(3, 11, 3, 0);
|
||||
this.labelMiscellaneous.Name = "labelMiscellaneous";
|
||||
this.labelMiscellaneous.Size = new System.Drawing.Size(74, 13);
|
||||
this.labelMiscellaneous.TabIndex = 13;
|
||||
this.labelMiscellaneous.Text = "Miscellaneous";
|
||||
//
|
||||
// checkHardwareAcceleration
|
||||
//
|
||||
this.checkHardwareAcceleration.AutoSize = true;
|
||||
this.checkHardwareAcceleration.Location = new System.Drawing.Point(9, 9);
|
||||
this.checkHardwareAcceleration.Location = new System.Drawing.Point(6, 21);
|
||||
this.checkHardwareAcceleration.Margin = new System.Windows.Forms.Padding(3, 5, 3, 3);
|
||||
this.checkHardwareAcceleration.Name = "checkHardwareAcceleration";
|
||||
this.checkHardwareAcceleration.Size = new System.Drawing.Size(134, 17);
|
||||
this.checkHardwareAcceleration.TabIndex = 12;
|
||||
this.checkHardwareAcceleration.Text = "Hardware Acceleration";
|
||||
this.toolTip.SetToolTip(this.checkHardwareAcceleration, "Uses your graphics card to improve performance.\r\nDisable if you experience issues" +
|
||||
" with rendering.");
|
||||
this.checkHardwareAcceleration.UseVisualStyleBackColor = true;
|
||||
this.checkHardwareAcceleration.CheckedChanged += new System.EventHandler(this.checkHardwareAcceleration_CheckedChanged);
|
||||
//
|
||||
// btnEditCefArgs
|
||||
//
|
||||
this.btnEditCefArgs.Location = new System.Drawing.Point(6, 19);
|
||||
this.btnEditCefArgs.Name = "btnEditCefArgs";
|
||||
this.btnEditCefArgs.Size = new System.Drawing.Size(171, 23);
|
||||
this.btnEditCefArgs.TabIndex = 15;
|
||||
this.btnEditCefArgs.Text = "Edit CEF Arguments";
|
||||
this.toolTip.SetToolTip(this.btnEditCefArgs, "Set custom command line arguments for Chromium Embedded Framework.");
|
||||
this.btnEditCefArgs.UseVisualStyleBackColor = true;
|
||||
this.btnEditCefArgs.Click += new System.EventHandler(this.btnEditCefArgs_Click);
|
||||
//
|
||||
// btnEditCSS
|
||||
//
|
||||
this.btnEditCSS.Location = new System.Drawing.Point(6, 48);
|
||||
this.btnEditCSS.Name = "btnEditCSS";
|
||||
this.btnEditCSS.Size = new System.Drawing.Size(171, 23);
|
||||
this.btnEditCSS.TabIndex = 16;
|
||||
this.btnEditCSS.Text = "Edit CSS";
|
||||
this.toolTip.SetToolTip(this.btnEditCSS, "Set custom CSS for browser and notification windows.");
|
||||
this.btnEditCSS.UseVisualStyleBackColor = true;
|
||||
this.btnEditCSS.Click += new System.EventHandler(this.btnEditCSS_Click);
|
||||
//
|
||||
// btnRestartLog
|
||||
//
|
||||
this.btnRestartLog.Location = new System.Drawing.Point(6, 106);
|
||||
this.btnRestartLog.Name = "btnRestartLog";
|
||||
this.btnRestartLog.Size = new System.Drawing.Size(171, 23);
|
||||
this.btnRestartLog.TabIndex = 18;
|
||||
this.btnRestartLog.Text = "Restart with Logging";
|
||||
this.toolTip.SetToolTip(this.btnRestartLog, "Restarts the program and enables logging\r\ninto a debug.txt file in the data folde" +
|
||||
"r.");
|
||||
this.btnRestartLog.UseVisualStyleBackColor = true;
|
||||
this.btnRestartLog.Click += new System.EventHandler(this.btnRestartLog_Click);
|
||||
//
|
||||
// btnRestart
|
||||
//
|
||||
this.btnRestart.Location = new System.Drawing.Point(6, 77);
|
||||
this.btnRestart.Name = "btnRestart";
|
||||
this.btnRestart.Size = new System.Drawing.Size(171, 23);
|
||||
this.btnRestart.TabIndex = 17;
|
||||
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" +
|
||||
"nch.");
|
||||
this.btnRestart.UseVisualStyleBackColor = true;
|
||||
this.btnRestart.Click += new System.EventHandler(this.btnRestart_Click);
|
||||
//
|
||||
// btnOpenAppFolder
|
||||
//
|
||||
this.btnOpenAppFolder.Location = new System.Drawing.Point(6, 19);
|
||||
this.btnOpenAppFolder.Name = "btnOpenAppFolder";
|
||||
this.btnOpenAppFolder.Size = new System.Drawing.Size(171, 23);
|
||||
this.btnOpenAppFolder.TabIndex = 16;
|
||||
this.btnOpenAppFolder.Text = "Open Program Folder";
|
||||
this.toolTip.SetToolTip(this.btnOpenAppFolder, "Opens the folder where the app is located.");
|
||||
this.btnOpenAppFolder.UseVisualStyleBackColor = true;
|
||||
this.btnOpenAppFolder.Click += new System.EventHandler(this.btnOpenAppFolder_Click);
|
||||
//
|
||||
// btnOpenDataFolder
|
||||
//
|
||||
this.btnOpenDataFolder.Location = new System.Drawing.Point(6, 48);
|
||||
this.btnOpenDataFolder.Name = "btnOpenDataFolder";
|
||||
this.btnOpenDataFolder.Size = new System.Drawing.Size(171, 23);
|
||||
this.btnOpenDataFolder.TabIndex = 19;
|
||||
this.btnOpenDataFolder.Text = "Open Data Folder";
|
||||
this.toolTip.SetToolTip(this.btnOpenDataFolder, "Opens the folder where your profile data is located.");
|
||||
this.btnOpenDataFolder.UseVisualStyleBackColor = true;
|
||||
this.btnOpenDataFolder.Click += new System.EventHandler(this.btnOpenDataFolder_Click);
|
||||
//
|
||||
// btnReset
|
||||
//
|
||||
this.btnReset.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.btnReset.AutoSize = true;
|
||||
this.btnReset.Location = new System.Drawing.Point(190, 250);
|
||||
this.btnReset.Name = "btnReset";
|
||||
this.btnReset.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||
this.btnReset.Size = new System.Drawing.Size(102, 23);
|
||||
this.btnReset.TabIndex = 17;
|
||||
this.btnReset.Text = "Restore Defaults";
|
||||
this.btnReset.UseVisualStyleBackColor = true;
|
||||
this.btnReset.Click += new System.EventHandler(this.btnReset_Click);
|
||||
//
|
||||
// btnImport
|
||||
//
|
||||
this.btnImport.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.btnImport.AutoSize = true;
|
||||
this.btnImport.Location = new System.Drawing.Point(100, 250);
|
||||
this.btnImport.Name = "btnImport";
|
||||
this.btnImport.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||
this.btnImport.Size = new System.Drawing.Size(84, 23);
|
||||
this.btnImport.TabIndex = 16;
|
||||
this.btnImport.Text = "Import Profile";
|
||||
this.btnImport.UseVisualStyleBackColor = true;
|
||||
this.btnImport.Click += new System.EventHandler(this.btnImport_Click);
|
||||
//
|
||||
// btnExport
|
||||
//
|
||||
this.btnExport.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.btnExport.AutoSize = true;
|
||||
this.btnExport.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
|
||||
this.btnExport.Location = new System.Drawing.Point(9, 250);
|
||||
this.btnExport.Name = "btnExport";
|
||||
this.btnExport.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||
this.btnExport.Size = new System.Drawing.Size(85, 23);
|
||||
this.btnExport.TabIndex = 15;
|
||||
this.btnExport.Text = "Export Profile";
|
||||
this.btnExport.UseVisualStyleBackColor = true;
|
||||
this.btnExport.Click += new System.EventHandler(this.btnExport_Click);
|
||||
//
|
||||
// groupPerformance
|
||||
//
|
||||
this.groupPerformance.Controls.Add(this.checkHardwareAcceleration);
|
||||
this.groupPerformance.Controls.Add(this.btnClearCache);
|
||||
this.groupPerformance.Location = new System.Drawing.Point(9, 9);
|
||||
this.groupPerformance.Name = "groupPerformance";
|
||||
this.groupPerformance.Size = new System.Drawing.Size(183, 74);
|
||||
this.groupPerformance.TabIndex = 18;
|
||||
this.groupPerformance.TabStop = false;
|
||||
this.groupPerformance.Text = "Performance";
|
||||
//
|
||||
// groupConfiguration
|
||||
//
|
||||
this.groupConfiguration.Controls.Add(this.btnEditCSS);
|
||||
this.groupConfiguration.Controls.Add(this.btnEditCefArgs);
|
||||
this.groupConfiguration.Location = new System.Drawing.Point(9, 89);
|
||||
this.groupConfiguration.Name = "groupConfiguration";
|
||||
this.groupConfiguration.Size = new System.Drawing.Size(183, 77);
|
||||
this.groupConfiguration.TabIndex = 19;
|
||||
this.groupConfiguration.TabStop = false;
|
||||
this.groupConfiguration.Text = "Configuration";
|
||||
//
|
||||
// groupApp
|
||||
//
|
||||
this.groupApp.Controls.Add(this.btnOpenDataFolder);
|
||||
this.groupApp.Controls.Add(this.btnOpenAppFolder);
|
||||
this.groupApp.Controls.Add(this.btnRestartLog);
|
||||
this.groupApp.Controls.Add(this.btnRestart);
|
||||
this.groupApp.Location = new System.Drawing.Point(198, 9);
|
||||
this.groupApp.Name = "groupApp";
|
||||
this.groupApp.Size = new System.Drawing.Size(183, 135);
|
||||
this.groupApp.TabIndex = 20;
|
||||
this.groupApp.TabStop = false;
|
||||
this.groupApp.Text = "App";
|
||||
//
|
||||
// TabSettingsAdvanced
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.Controls.Add(this.btnClearCache);
|
||||
this.Controls.Add(this.labelMiscellaneous);
|
||||
this.Controls.Add(this.checkHardwareAcceleration);
|
||||
this.Controls.Add(this.groupApp);
|
||||
this.Controls.Add(this.groupConfiguration);
|
||||
this.Controls.Add(this.groupPerformance);
|
||||
this.Controls.Add(this.btnReset);
|
||||
this.Controls.Add(this.btnImport);
|
||||
this.Controls.Add(this.btnExport);
|
||||
this.Name = "TabSettingsAdvanced";
|
||||
this.Size = new System.Drawing.Size(239, 120);
|
||||
this.Size = new System.Drawing.Size(478, 282);
|
||||
this.groupPerformance.ResumeLayout(false);
|
||||
this.groupPerformance.PerformLayout();
|
||||
this.groupConfiguration.ResumeLayout(false);
|
||||
this.groupApp.ResumeLayout(false);
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
@@ -76,7 +237,19 @@
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.Button btnClearCache;
|
||||
private System.Windows.Forms.Label labelMiscellaneous;
|
||||
private System.Windows.Forms.CheckBox checkHardwareAcceleration;
|
||||
private System.Windows.Forms.ToolTip toolTip;
|
||||
private System.Windows.Forms.Button btnReset;
|
||||
private System.Windows.Forms.Button btnImport;
|
||||
private System.Windows.Forms.Button btnExport;
|
||||
private System.Windows.Forms.GroupBox groupPerformance;
|
||||
private System.Windows.Forms.GroupBox groupConfiguration;
|
||||
private System.Windows.Forms.Button btnEditCefArgs;
|
||||
private System.Windows.Forms.Button btnEditCSS;
|
||||
private System.Windows.Forms.GroupBox groupApp;
|
||||
private System.Windows.Forms.Button btnRestartLog;
|
||||
private System.Windows.Forms.Button btnRestart;
|
||||
private System.Windows.Forms.Button btnOpenAppFolder;
|
||||
private System.Windows.Forms.Button btnOpenDataFolder;
|
||||
}
|
||||
}
|
||||
|
@@ -2,13 +2,22 @@
|
||||
using System.Diagnostics;
|
||||
using System.Windows.Forms;
|
||||
using TweetDck.Core.Controls;
|
||||
using TweetDck.Core.Other.Settings.Dialogs;
|
||||
using TweetDck.Core.Other.Settings.Export;
|
||||
using TweetDck.Core.Utils;
|
||||
using TweetDck.Plugins;
|
||||
|
||||
namespace TweetDck.Core.Other.Settings{
|
||||
partial class TabSettingsAdvanced : BaseTabSettings{
|
||||
public TabSettingsAdvanced(){
|
||||
private readonly Action<string> reinjectBrowserCSS;
|
||||
private readonly PluginManager plugins;
|
||||
|
||||
public TabSettingsAdvanced(Action<string> reinjectBrowserCSS, PluginManager plugins){
|
||||
InitializeComponent();
|
||||
|
||||
this.reinjectBrowserCSS = reinjectBrowserCSS;
|
||||
this.plugins = plugins;
|
||||
|
||||
checkHardwareAcceleration.Checked = HardwareAcceleration.IsEnabled;
|
||||
|
||||
BrowserCache.CalculateCacheSize(bytes => this.InvokeSafe(() => {
|
||||
@@ -27,7 +36,7 @@ namespace TweetDck.Core.Other.Settings{
|
||||
btnClearCache.Enabled = false;
|
||||
BrowserCache.SetClearOnExit();
|
||||
|
||||
MessageBox.Show("Cache will be automatically cleared when "+Program.BrandName+" exits.","Clear Cache",MessageBoxButtons.OK,MessageBoxIcon.Information);
|
||||
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){
|
||||
@@ -40,22 +49,143 @@ namespace TweetDck.Core.Other.Settings{
|
||||
succeeded = HardwareAcceleration.Enable();
|
||||
}
|
||||
else{
|
||||
MessageBox.Show("Cannot enable hardware acceleration, the libraries libEGL.dll and libGLESv2.dll could not be restored.",Program.BrandName+" Settings",MessageBoxButtons.OK,MessageBoxIcon.Error);
|
||||
MessageBox.Show("Cannot enable hardware acceleration, the libraries libEGL.dll and libGLESv2.dll could not be restored.", Program.BrandName+" Settings", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
else{
|
||||
succeeded = HardwareAcceleration.Disable();
|
||||
}
|
||||
|
||||
if (succeeded && MessageBox.Show("The application must restart for the setting to take place. Do you want to restart now?",Program.BrandName+" Settings",MessageBoxButtons.YesNo,MessageBoxIcon.Information) == DialogResult.Yes){ // TODO
|
||||
Process.Start(Application.ExecutablePath,"-restart");
|
||||
Application.Exit();
|
||||
if (succeeded){
|
||||
PromptRestart();
|
||||
}
|
||||
else if (!succeeded){
|
||||
else{
|
||||
checkHardwareAcceleration.CheckedChanged -= checkHardwareAcceleration_CheckedChanged;
|
||||
checkHardwareAcceleration.Checked = HardwareAcceleration.IsEnabled;
|
||||
checkHardwareAcceleration.CheckedChanged += checkHardwareAcceleration_CheckedChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private void btnEditCefArgs_Click(object sender, EventArgs e){
|
||||
DialogSettingsCefArgs form = new DialogSettingsCefArgs();
|
||||
|
||||
if (form.ShowDialog(ParentForm) == DialogResult.OK){
|
||||
Config.CustomCefArgs = form.CefArgs;
|
||||
form.Dispose();
|
||||
PromptRestart();
|
||||
}
|
||||
else{
|
||||
form.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void btnEditCSS_Click(object sender, EventArgs e){
|
||||
using(DialogSettingsCSS form = new DialogSettingsCSS(reinjectBrowserCSS)){
|
||||
if (form.ShowDialog(ParentForm) == DialogResult.OK){
|
||||
Config.CustomBrowserCSS = form.BrowserCSS;
|
||||
Config.CustomNotificationCSS = form.NotificationCSS;
|
||||
}
|
||||
|
||||
reinjectBrowserCSS(Config.CustomBrowserCSS); // reinject on cancel too, because the CSS is updated while typing
|
||||
}
|
||||
}
|
||||
|
||||
private void btnExport_Click(object sender, EventArgs e){
|
||||
ExportFileFlags flags;
|
||||
|
||||
using(DialogSettingsExport dialog = DialogSettingsExport.Export()){
|
||||
if (dialog.ShowDialog() != DialogResult.OK){
|
||||
return;
|
||||
}
|
||||
|
||||
flags = dialog.Flags;
|
||||
}
|
||||
|
||||
string file;
|
||||
|
||||
using(SaveFileDialog dialog = new SaveFileDialog{
|
||||
AddExtension = true,
|
||||
AutoUpgradeEnabled = true,
|
||||
OverwritePrompt = true,
|
||||
DefaultExt = "tdsettings",
|
||||
FileName = Program.BrandName+".tdsettings",
|
||||
Title = "Export "+Program.BrandName+" Settings",
|
||||
Filter = Program.BrandName+" Settings (*.tdsettings)|*.tdsettings"
|
||||
}){
|
||||
if (dialog.ShowDialog() != DialogResult.OK){
|
||||
return;
|
||||
}
|
||||
|
||||
file = dialog.FileName;
|
||||
}
|
||||
|
||||
Program.UserConfig.Save();
|
||||
|
||||
ExportManager manager = new ExportManager(file, plugins);
|
||||
|
||||
if (!manager.Export(flags)){
|
||||
Program.Reporter.HandleException("Profile Export Error", "An exception happened while exporting "+Program.BrandName+" settings.", true, manager.LastException);
|
||||
}
|
||||
}
|
||||
|
||||
private void btnImport_Click(object sender, EventArgs e){
|
||||
string file;
|
||||
|
||||
using(OpenFileDialog dialog = new OpenFileDialog{
|
||||
AutoUpgradeEnabled = true,
|
||||
DereferenceLinks = true,
|
||||
Title = "Import "+Program.BrandName+" Settings",
|
||||
Filter = Program.BrandName+" Settings (*.tdsettings)|*.tdsettings"
|
||||
}){
|
||||
if (dialog.ShowDialog() != DialogResult.OK){
|
||||
return;
|
||||
}
|
||||
|
||||
file = dialog.FileName;
|
||||
}
|
||||
|
||||
ExportManager manager = new ExportManager(file, plugins);
|
||||
ExportFileFlags flags;
|
||||
|
||||
using(DialogSettingsExport dialog = DialogSettingsExport.Import(manager.GetImportFlags())){
|
||||
if (dialog.ShowDialog() != DialogResult.OK){
|
||||
return;
|
||||
}
|
||||
|
||||
flags = dialog.Flags;
|
||||
}
|
||||
|
||||
if (manager.Import(flags)){
|
||||
if (!manager.IsRestarting){
|
||||
((FormSettings)ParentForm).ReloadUI();
|
||||
}
|
||||
}
|
||||
else{
|
||||
Program.Reporter.HandleException("Profile Import Error", "An exception happened while importing "+Program.BrandName+" settings.", true, manager.LastException);
|
||||
}
|
||||
}
|
||||
|
||||
private void btnReset_Click(object sender, EventArgs e){
|
||||
if (MessageBox.Show("This will reset all of your settings, including disabled plugins. Do you want to proceed?", "Reset "+Program.BrandName+" Settings", MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == DialogResult.Yes){
|
||||
Program.ResetConfig();
|
||||
((FormSettings)ParentForm).ReloadUI();
|
||||
}
|
||||
}
|
||||
|
||||
private void btnOpenAppFolder_Click(object sender, EventArgs e){
|
||||
using(Process.Start("explorer.exe", "\""+Program.ProgramPath+"\"")){}
|
||||
}
|
||||
|
||||
private void btnOpenDataFolder_Click(object sender, EventArgs e){
|
||||
using(Process.Start("explorer.exe", "\""+Program.StoragePath+"\"")){}
|
||||
}
|
||||
|
||||
private void btnRestart_Click(object sender, EventArgs e){
|
||||
Program.Restart();
|
||||
}
|
||||
|
||||
private void btnRestartLog_Click(object sender, EventArgs e){
|
||||
Program.Restart(new string[]{ "-log" });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
117
Core/Other/Settings/TabSettingsGeneral.Designer.cs
generated
117
Core/Other/Settings/TabSettingsGeneral.Designer.cs
generated
@@ -23,19 +23,31 @@
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent() {
|
||||
this.components = new System.ComponentModel.Container();
|
||||
this.checkExpandLinks = new System.Windows.Forms.CheckBox();
|
||||
this.comboBoxTrayType = new System.Windows.Forms.ComboBox();
|
||||
this.labelTrayType = new System.Windows.Forms.Label();
|
||||
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
||||
this.checkTrayHighlight = new System.Windows.Forms.CheckBox();
|
||||
this.checkSpellCheck = new System.Windows.Forms.CheckBox();
|
||||
this.checkScreenshotBorder = new System.Windows.Forms.CheckBox();
|
||||
this.groupTray = new System.Windows.Forms.GroupBox();
|
||||
this.labelTrayIcon = new System.Windows.Forms.Label();
|
||||
this.groupInterface = new System.Windows.Forms.GroupBox();
|
||||
this.groupTray.SuspendLayout();
|
||||
this.groupInterface.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// checkExpandLinks
|
||||
//
|
||||
this.checkExpandLinks.AutoSize = true;
|
||||
this.checkExpandLinks.Location = new System.Drawing.Point(9, 9);
|
||||
this.checkExpandLinks.Location = new System.Drawing.Point(9, 21);
|
||||
this.checkExpandLinks.Margin = new System.Windows.Forms.Padding(3, 5, 3, 3);
|
||||
this.checkExpandLinks.Name = "checkExpandLinks";
|
||||
this.checkExpandLinks.Size = new System.Drawing.Size(166, 17);
|
||||
this.checkExpandLinks.TabIndex = 14;
|
||||
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" +
|
||||
"p instead.");
|
||||
this.checkExpandLinks.UseVisualStyleBackColor = true;
|
||||
this.checkExpandLinks.CheckedChanged += new System.EventHandler(this.checkExpandLinks_CheckedChanged);
|
||||
//
|
||||
@@ -43,33 +55,98 @@
|
||||
//
|
||||
this.comboBoxTrayType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||
this.comboBoxTrayType.FormattingEnabled = true;
|
||||
this.comboBoxTrayType.Location = new System.Drawing.Point(9, 56);
|
||||
this.comboBoxTrayType.Location = new System.Drawing.Point(6, 19);
|
||||
this.comboBoxTrayType.Name = "comboBoxTrayType";
|
||||
this.comboBoxTrayType.Size = new System.Drawing.Size(171, 21);
|
||||
this.comboBoxTrayType.TabIndex = 13;
|
||||
this.toolTip.SetToolTip(this.comboBoxTrayType, "Changes behavior of the Tray icon.\r\nRight-click the icon for an action menu.");
|
||||
this.comboBoxTrayType.SelectedIndexChanged += new System.EventHandler(this.comboBoxTrayType_SelectedIndexChanged);
|
||||
//
|
||||
// labelTrayType
|
||||
// checkTrayHighlight
|
||||
//
|
||||
this.labelTrayType.AutoSize = true;
|
||||
this.labelTrayType.Location = new System.Drawing.Point(6, 40);
|
||||
this.labelTrayType.Margin = new System.Windows.Forms.Padding(3, 11, 3, 0);
|
||||
this.labelTrayType.Name = "labelTrayType";
|
||||
this.labelTrayType.Size = new System.Drawing.Size(52, 13);
|
||||
this.labelTrayType.TabIndex = 12;
|
||||
this.labelTrayType.Text = "Tray Icon";
|
||||
this.checkTrayHighlight.AutoSize = true;
|
||||
this.checkTrayHighlight.Location = new System.Drawing.Point(9, 70);
|
||||
this.checkTrayHighlight.Margin = new System.Windows.Forms.Padding(3, 5, 3, 3);
|
||||
this.checkTrayHighlight.Name = "checkTrayHighlight";
|
||||
this.checkTrayHighlight.Size = new System.Drawing.Size(103, 17);
|
||||
this.checkTrayHighlight.TabIndex = 15;
|
||||
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" +
|
||||
"pup or audio notifications.\r\nThe icon resets when the main window is restored.");
|
||||
this.checkTrayHighlight.UseVisualStyleBackColor = true;
|
||||
this.checkTrayHighlight.CheckedChanged += new System.EventHandler(this.checkTrayHighlight_CheckedChanged);
|
||||
//
|
||||
// checkSpellCheck
|
||||
//
|
||||
this.checkSpellCheck.AutoSize = true;
|
||||
this.checkSpellCheck.Location = new System.Drawing.Point(9, 44);
|
||||
this.checkSpellCheck.Name = "checkSpellCheck";
|
||||
this.checkSpellCheck.Size = new System.Drawing.Size(119, 17);
|
||||
this.checkSpellCheck.TabIndex = 15;
|
||||
this.checkSpellCheck.Text = "Enable Spell Check";
|
||||
this.toolTip.SetToolTip(this.checkSpellCheck, "Underlines words that are spelled incorrectly.");
|
||||
this.checkSpellCheck.UseVisualStyleBackColor = true;
|
||||
this.checkSpellCheck.CheckedChanged += new System.EventHandler(this.checkSpellCheck_CheckedChanged);
|
||||
//
|
||||
// checkScreenshotBorder
|
||||
//
|
||||
this.checkScreenshotBorder.AutoSize = true;
|
||||
this.checkScreenshotBorder.Location = new System.Drawing.Point(9, 67);
|
||||
this.checkScreenshotBorder.Name = "checkScreenshotBorder";
|
||||
this.checkScreenshotBorder.Size = new System.Drawing.Size(169, 17);
|
||||
this.checkScreenshotBorder.TabIndex = 16;
|
||||
this.checkScreenshotBorder.Text = "Include Border In Screenshots";
|
||||
this.toolTip.SetToolTip(this.checkScreenshotBorder, "Shows the window border in tweet screenshots.");
|
||||
this.checkScreenshotBorder.UseVisualStyleBackColor = true;
|
||||
this.checkScreenshotBorder.CheckedChanged += new System.EventHandler(this.checkScreenshotBorder_CheckedChanged);
|
||||
//
|
||||
// groupTray
|
||||
//
|
||||
this.groupTray.Controls.Add(this.checkTrayHighlight);
|
||||
this.groupTray.Controls.Add(this.labelTrayIcon);
|
||||
this.groupTray.Controls.Add(this.comboBoxTrayType);
|
||||
this.groupTray.Location = new System.Drawing.Point(9, 109);
|
||||
this.groupTray.Name = "groupTray";
|
||||
this.groupTray.Size = new System.Drawing.Size(183, 93);
|
||||
this.groupTray.TabIndex = 15;
|
||||
this.groupTray.TabStop = false;
|
||||
this.groupTray.Text = "System Tray";
|
||||
//
|
||||
// labelTrayIcon
|
||||
//
|
||||
this.labelTrayIcon.AutoSize = true;
|
||||
this.labelTrayIcon.Location = new System.Drawing.Point(6, 52);
|
||||
this.labelTrayIcon.Margin = new System.Windows.Forms.Padding(3, 9, 3, 0);
|
||||
this.labelTrayIcon.Name = "labelTrayIcon";
|
||||
this.labelTrayIcon.Size = new System.Drawing.Size(52, 13);
|
||||
this.labelTrayIcon.TabIndex = 14;
|
||||
this.labelTrayIcon.Text = "Tray Icon";
|
||||
//
|
||||
// groupInterface
|
||||
//
|
||||
this.groupInterface.Controls.Add(this.checkScreenshotBorder);
|
||||
this.groupInterface.Controls.Add(this.checkSpellCheck);
|
||||
this.groupInterface.Controls.Add(this.checkExpandLinks);
|
||||
this.groupInterface.Location = new System.Drawing.Point(9, 9);
|
||||
this.groupInterface.Name = "groupInterface";
|
||||
this.groupInterface.Size = new System.Drawing.Size(183, 90);
|
||||
this.groupInterface.TabIndex = 16;
|
||||
this.groupInterface.TabStop = false;
|
||||
this.groupInterface.Text = "User Interface";
|
||||
//
|
||||
// TabSettingsGeneral
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.Controls.Add(this.checkExpandLinks);
|
||||
this.Controls.Add(this.comboBoxTrayType);
|
||||
this.Controls.Add(this.labelTrayType);
|
||||
this.Controls.Add(this.groupInterface);
|
||||
this.Controls.Add(this.groupTray);
|
||||
this.Name = "TabSettingsGeneral";
|
||||
this.Size = new System.Drawing.Size(219, 99);
|
||||
this.Size = new System.Drawing.Size(478, 282);
|
||||
this.groupTray.ResumeLayout(false);
|
||||
this.groupTray.PerformLayout();
|
||||
this.groupInterface.ResumeLayout(false);
|
||||
this.groupInterface.PerformLayout();
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
@@ -77,6 +154,12 @@
|
||||
|
||||
private System.Windows.Forms.CheckBox checkExpandLinks;
|
||||
private System.Windows.Forms.ComboBox comboBoxTrayType;
|
||||
private System.Windows.Forms.Label labelTrayType;
|
||||
private System.Windows.Forms.ToolTip toolTip;
|
||||
private System.Windows.Forms.GroupBox groupTray;
|
||||
private System.Windows.Forms.GroupBox groupInterface;
|
||||
private System.Windows.Forms.Label labelTrayIcon;
|
||||
private System.Windows.Forms.CheckBox checkTrayHighlight;
|
||||
private System.Windows.Forms.CheckBox checkSpellCheck;
|
||||
private System.Windows.Forms.CheckBox checkScreenshotBorder;
|
||||
}
|
||||
}
|
||||
|
@@ -9,9 +9,13 @@ namespace TweetDck.Core.Other.Settings{
|
||||
comboBoxTrayType.Items.Add("Display Icon Only");
|
||||
comboBoxTrayType.Items.Add("Minimize to Tray");
|
||||
comboBoxTrayType.Items.Add("Close to Tray");
|
||||
comboBoxTrayType.SelectedIndex = Math.Min(Math.Max((int)Config.TrayBehavior,0),comboBoxTrayType.Items.Count-1);
|
||||
comboBoxTrayType.Items.Add("Combined");
|
||||
comboBoxTrayType.SelectedIndex = Math.Min(Math.Max((int)Config.TrayBehavior, 0), comboBoxTrayType.Items.Count-1);
|
||||
|
||||
checkExpandLinks.Checked = Program.UserConfig.ExpandLinksOnHover;
|
||||
checkExpandLinks.Checked = Config.ExpandLinksOnHover;
|
||||
checkSpellCheck.Checked = Config.EnableSpellCheck;
|
||||
checkScreenshotBorder.Checked = Config.ShowScreenshotBorder;
|
||||
checkTrayHighlight.Checked = Config.EnableTrayHighlight;
|
||||
}
|
||||
|
||||
private void checkExpandLinks_CheckedChanged(object sender, EventArgs e){
|
||||
@@ -20,10 +24,29 @@ namespace TweetDck.Core.Other.Settings{
|
||||
Config.ExpandLinksOnHover = checkExpandLinks.Checked;
|
||||
}
|
||||
|
||||
private void checkSpellCheck_CheckedChanged(object sender, EventArgs e){
|
||||
if (!Ready)return;
|
||||
|
||||
Config.EnableSpellCheck = checkSpellCheck.Checked;
|
||||
PromptRestart();
|
||||
}
|
||||
|
||||
private void checkScreenshotBorder_CheckedChanged(object sender, EventArgs e){
|
||||
if (!Ready)return;
|
||||
|
||||
Config.ShowScreenshotBorder = checkScreenshotBorder.Checked;
|
||||
}
|
||||
|
||||
private void comboBoxTrayType_SelectedIndexChanged(object sender, EventArgs e){
|
||||
if (!Ready)return;
|
||||
|
||||
Config.TrayBehavior = (TrayIcon.Behavior)comboBoxTrayType.SelectedIndex;
|
||||
}
|
||||
|
||||
private void checkTrayHighlight_CheckedChanged(object sender, EventArgs e){
|
||||
if (!Ready)return;
|
||||
|
||||
Config.EnableTrayHighlight = checkTrayHighlight.Checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
272
Core/Other/Settings/TabSettingsNotifications.Designer.cs
generated
272
Core/Other/Settings/TabSettingsNotifications.Designer.cs
generated
@@ -23,7 +23,9 @@
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent() {
|
||||
this.components = new System.ComponentModel.Container();
|
||||
this.groupNotificationLocation = new System.Windows.Forms.GroupBox();
|
||||
this.labelEdgeDistanceValue = new System.Windows.Forms.Label();
|
||||
this.labelDisplay = new System.Windows.Forms.Label();
|
||||
this.comboBoxDisplay = new System.Windows.Forms.ComboBox();
|
||||
this.labelEdgeDistance = new System.Windows.Forms.Label();
|
||||
@@ -34,20 +36,32 @@
|
||||
this.radioLocTL = new System.Windows.Forms.RadioButton();
|
||||
this.trackBarEdgeDistance = new System.Windows.Forms.TrackBar();
|
||||
this.groupNotificationDuration = new System.Windows.Forms.GroupBox();
|
||||
this.radioDurVeryLong = new System.Windows.Forms.RadioButton();
|
||||
this.radioDurLong = new System.Windows.Forms.RadioButton();
|
||||
this.radioDurMedium = new System.Windows.Forms.RadioButton();
|
||||
this.radioDurShort = new System.Windows.Forms.RadioButton();
|
||||
this.tableLayoutDurationButtons = new System.Windows.Forms.TableLayoutPanel();
|
||||
this.btnDurationMedium = new TweetDck.Core.Controls.FlatButton();
|
||||
this.btnDurationLong = new TweetDck.Core.Controls.FlatButton();
|
||||
this.btnDurationShort = new TweetDck.Core.Controls.FlatButton();
|
||||
this.labelDurationValue = new System.Windows.Forms.Label();
|
||||
this.trackBarDuration = new System.Windows.Forms.TrackBar();
|
||||
this.groupUserInterface = new System.Windows.Forms.GroupBox();
|
||||
this.checkTimerCountDown = new System.Windows.Forms.CheckBox();
|
||||
this.checkNotificationTimer = new System.Windows.Forms.CheckBox();
|
||||
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
||||
this.groupCustomSound = new System.Windows.Forms.GroupBox();
|
||||
this.btnResetSound = new System.Windows.Forms.Button();
|
||||
this.btnBrowseSound = new System.Windows.Forms.Button();
|
||||
this.tbCustomSound = new System.Windows.Forms.TextBox();
|
||||
this.groupNotificationLocation.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.trackBarEdgeDistance)).BeginInit();
|
||||
this.groupNotificationDuration.SuspendLayout();
|
||||
this.tableLayoutDurationButtons.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.trackBarDuration)).BeginInit();
|
||||
this.groupUserInterface.SuspendLayout();
|
||||
this.groupCustomSound.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// groupNotificationLocation
|
||||
//
|
||||
this.groupNotificationLocation.Controls.Add(this.labelEdgeDistanceValue);
|
||||
this.groupNotificationLocation.Controls.Add(this.labelDisplay);
|
||||
this.groupNotificationLocation.Controls.Add(this.comboBoxDisplay);
|
||||
this.groupNotificationLocation.Controls.Add(this.labelEdgeDistance);
|
||||
@@ -57,12 +71,23 @@
|
||||
this.groupNotificationLocation.Controls.Add(this.radioLocTR);
|
||||
this.groupNotificationLocation.Controls.Add(this.radioLocTL);
|
||||
this.groupNotificationLocation.Controls.Add(this.trackBarEdgeDistance);
|
||||
this.groupNotificationLocation.Location = new System.Drawing.Point(9, 9);
|
||||
this.groupNotificationLocation.Location = new System.Drawing.Point(198, 9);
|
||||
this.groupNotificationLocation.Name = "groupNotificationLocation";
|
||||
this.groupNotificationLocation.Size = new System.Drawing.Size(183, 264);
|
||||
this.groupNotificationLocation.TabIndex = 1;
|
||||
this.groupNotificationLocation.TabStop = false;
|
||||
this.groupNotificationLocation.Text = "Notification Location";
|
||||
this.groupNotificationLocation.Text = "Location";
|
||||
//
|
||||
// labelEdgeDistanceValue
|
||||
//
|
||||
this.labelEdgeDistanceValue.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.labelEdgeDistanceValue.Location = new System.Drawing.Point(143, 214);
|
||||
this.labelEdgeDistanceValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
|
||||
this.labelEdgeDistanceValue.Name = "labelEdgeDistanceValue";
|
||||
this.labelEdgeDistanceValue.Size = new System.Drawing.Size(34, 13);
|
||||
this.labelEdgeDistanceValue.TabIndex = 11;
|
||||
this.labelEdgeDistanceValue.Text = "0 px";
|
||||
this.labelEdgeDistanceValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
|
||||
//
|
||||
// labelDisplay
|
||||
//
|
||||
@@ -105,6 +130,7 @@
|
||||
this.radioLocCustom.TabIndex = 4;
|
||||
this.radioLocCustom.TabStop = true;
|
||||
this.radioLocCustom.Text = "Custom";
|
||||
this.toolTip.SetToolTip(this.radioLocCustom, "Drag the notification window to the desired location.");
|
||||
this.radioLocCustom.UseVisualStyleBackColor = true;
|
||||
this.radioLocCustom.CheckedChanged += new System.EventHandler(this.radioLoc_CheckedChanged);
|
||||
//
|
||||
@@ -165,87 +191,140 @@
|
||||
this.trackBarEdgeDistance.Maximum = 40;
|
||||
this.trackBarEdgeDistance.Minimum = 8;
|
||||
this.trackBarEdgeDistance.Name = "trackBarEdgeDistance";
|
||||
this.trackBarEdgeDistance.Size = new System.Drawing.Size(171, 45);
|
||||
this.trackBarEdgeDistance.Size = new System.Drawing.Size(141, 45);
|
||||
this.trackBarEdgeDistance.SmallChange = 2;
|
||||
this.trackBarEdgeDistance.TabIndex = 5;
|
||||
this.trackBarEdgeDistance.TickFrequency = 2;
|
||||
this.trackBarEdgeDistance.TickFrequency = 4;
|
||||
this.trackBarEdgeDistance.Value = 8;
|
||||
this.trackBarEdgeDistance.ValueChanged += new System.EventHandler(this.trackBarEdgeDistance_ValueChanged);
|
||||
//
|
||||
// groupNotificationDuration
|
||||
//
|
||||
this.groupNotificationDuration.Controls.Add(this.radioDurVeryLong);
|
||||
this.groupNotificationDuration.Controls.Add(this.radioDurLong);
|
||||
this.groupNotificationDuration.Controls.Add(this.radioDurMedium);
|
||||
this.groupNotificationDuration.Controls.Add(this.radioDurShort);
|
||||
this.groupNotificationDuration.Location = new System.Drawing.Point(198, 9);
|
||||
this.groupNotificationDuration.Controls.Add(this.tableLayoutDurationButtons);
|
||||
this.groupNotificationDuration.Controls.Add(this.labelDurationValue);
|
||||
this.groupNotificationDuration.Controls.Add(this.trackBarDuration);
|
||||
this.groupNotificationDuration.Location = new System.Drawing.Point(9, 83);
|
||||
this.groupNotificationDuration.Name = "groupNotificationDuration";
|
||||
this.groupNotificationDuration.Size = new System.Drawing.Size(183, 119);
|
||||
this.groupNotificationDuration.Size = new System.Drawing.Size(183, 89);
|
||||
this.groupNotificationDuration.TabIndex = 9;
|
||||
this.groupNotificationDuration.TabStop = false;
|
||||
this.groupNotificationDuration.Text = "Notification Duration";
|
||||
this.groupNotificationDuration.Text = "Duration";
|
||||
//
|
||||
// radioDurVeryLong
|
||||
// tableLayoutDurationButtons
|
||||
//
|
||||
this.radioDurVeryLong.AutoSize = true;
|
||||
this.radioDurVeryLong.Location = new System.Drawing.Point(6, 92);
|
||||
this.radioDurVeryLong.Name = "radioDurVeryLong";
|
||||
this.radioDurVeryLong.Size = new System.Drawing.Size(73, 17);
|
||||
this.radioDurVeryLong.TabIndex = 3;
|
||||
this.radioDurVeryLong.TabStop = true;
|
||||
this.radioDurVeryLong.Text = "Very Long";
|
||||
this.radioDurVeryLong.UseVisualStyleBackColor = true;
|
||||
this.radioDurVeryLong.CheckedChanged += new System.EventHandler(this.radioDur_CheckedChanged);
|
||||
this.radioDurVeryLong.Click += new System.EventHandler(this.radioDur_Click);
|
||||
this.tableLayoutDurationButtons.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.tableLayoutDurationButtons.ColumnCount = 3;
|
||||
this.tableLayoutDurationButtons.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 32F));
|
||||
this.tableLayoutDurationButtons.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 36F));
|
||||
this.tableLayoutDurationButtons.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 32F));
|
||||
this.tableLayoutDurationButtons.Controls.Add(this.btnDurationMedium, 0, 0);
|
||||
this.tableLayoutDurationButtons.Controls.Add(this.btnDurationLong, 1, 0);
|
||||
this.tableLayoutDurationButtons.Controls.Add(this.btnDurationShort, 0, 0);
|
||||
this.tableLayoutDurationButtons.Location = new System.Drawing.Point(6, 56);
|
||||
this.tableLayoutDurationButtons.Name = "tableLayoutDurationButtons";
|
||||
this.tableLayoutDurationButtons.RowCount = 1;
|
||||
this.tableLayoutDurationButtons.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||
this.tableLayoutDurationButtons.Size = new System.Drawing.Size(171, 27);
|
||||
this.tableLayoutDurationButtons.TabIndex = 5;
|
||||
//
|
||||
// radioDurLong
|
||||
// btnDurationMedium
|
||||
//
|
||||
this.radioDurLong.AutoSize = true;
|
||||
this.radioDurLong.Location = new System.Drawing.Point(6, 68);
|
||||
this.radioDurLong.Name = "radioDurLong";
|
||||
this.radioDurLong.Size = new System.Drawing.Size(49, 17);
|
||||
this.radioDurLong.TabIndex = 2;
|
||||
this.radioDurLong.TabStop = true;
|
||||
this.radioDurLong.Text = "Long";
|
||||
this.radioDurLong.UseVisualStyleBackColor = true;
|
||||
this.radioDurLong.CheckedChanged += new System.EventHandler(this.radioDur_CheckedChanged);
|
||||
this.radioDurLong.Click += new System.EventHandler(this.radioDur_Click);
|
||||
this.btnDurationMedium.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.btnDurationMedium.FlatAppearance.BorderColor = System.Drawing.Color.Gray;
|
||||
this.btnDurationMedium.FlatAppearance.MouseDownBackColor = System.Drawing.SystemColors.ControlLight;
|
||||
this.btnDurationMedium.FlatAppearance.MouseOverBackColor = System.Drawing.Color.White;
|
||||
this.btnDurationMedium.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
||||
this.btnDurationMedium.Location = new System.Drawing.Point(55, 1);
|
||||
this.btnDurationMedium.Margin = new System.Windows.Forms.Padding(1);
|
||||
this.btnDurationMedium.Name = "btnDurationMedium";
|
||||
this.btnDurationMedium.Size = new System.Drawing.Size(59, 25);
|
||||
this.btnDurationMedium.TabIndex = 2;
|
||||
this.btnDurationMedium.Text = "Medium";
|
||||
this.btnDurationMedium.UseVisualStyleBackColor = true;
|
||||
this.btnDurationMedium.Click += new System.EventHandler(this.btnDurationMedium_Click);
|
||||
//
|
||||
// radioDurMedium
|
||||
// btnDurationLong
|
||||
//
|
||||
this.radioDurMedium.AutoSize = true;
|
||||
this.radioDurMedium.Location = new System.Drawing.Point(6, 44);
|
||||
this.radioDurMedium.Name = "radioDurMedium";
|
||||
this.radioDurMedium.Size = new System.Drawing.Size(62, 17);
|
||||
this.radioDurMedium.TabIndex = 1;
|
||||
this.radioDurMedium.TabStop = true;
|
||||
this.radioDurMedium.Text = "Medium";
|
||||
this.radioDurMedium.UseVisualStyleBackColor = true;
|
||||
this.radioDurMedium.CheckedChanged += new System.EventHandler(this.radioDur_CheckedChanged);
|
||||
this.radioDurMedium.Click += new System.EventHandler(this.radioDur_Click);
|
||||
this.btnDurationLong.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.btnDurationLong.FlatAppearance.BorderColor = System.Drawing.Color.Gray;
|
||||
this.btnDurationLong.FlatAppearance.MouseDownBackColor = System.Drawing.SystemColors.ControlLight;
|
||||
this.btnDurationLong.FlatAppearance.MouseOverBackColor = System.Drawing.Color.White;
|
||||
this.btnDurationLong.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
||||
this.btnDurationLong.Location = new System.Drawing.Point(116, 1);
|
||||
this.btnDurationLong.Margin = new System.Windows.Forms.Padding(1);
|
||||
this.btnDurationLong.Name = "btnDurationLong";
|
||||
this.btnDurationLong.Size = new System.Drawing.Size(54, 25);
|
||||
this.btnDurationLong.TabIndex = 1;
|
||||
this.btnDurationLong.Text = "Long";
|
||||
this.btnDurationLong.UseVisualStyleBackColor = true;
|
||||
this.btnDurationLong.Click += new System.EventHandler(this.btnDurationLong_Click);
|
||||
//
|
||||
// radioDurShort
|
||||
// btnDurationShort
|
||||
//
|
||||
this.radioDurShort.AutoSize = true;
|
||||
this.radioDurShort.Location = new System.Drawing.Point(6, 20);
|
||||
this.radioDurShort.Name = "radioDurShort";
|
||||
this.radioDurShort.Size = new System.Drawing.Size(50, 17);
|
||||
this.radioDurShort.TabIndex = 0;
|
||||
this.radioDurShort.TabStop = true;
|
||||
this.radioDurShort.Text = "Short";
|
||||
this.radioDurShort.UseVisualStyleBackColor = true;
|
||||
this.radioDurShort.CheckedChanged += new System.EventHandler(this.radioDur_CheckedChanged);
|
||||
this.radioDurShort.Click += new System.EventHandler(this.radioDur_Click);
|
||||
this.btnDurationShort.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.btnDurationShort.FlatAppearance.BorderColor = System.Drawing.Color.Gray;
|
||||
this.btnDurationShort.FlatAppearance.MouseDownBackColor = System.Drawing.SystemColors.ControlLight;
|
||||
this.btnDurationShort.FlatAppearance.MouseOverBackColor = System.Drawing.Color.White;
|
||||
this.btnDurationShort.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
||||
this.btnDurationShort.Location = new System.Drawing.Point(1, 1);
|
||||
this.btnDurationShort.Margin = new System.Windows.Forms.Padding(1);
|
||||
this.btnDurationShort.Name = "btnDurationShort";
|
||||
this.btnDurationShort.Size = new System.Drawing.Size(52, 25);
|
||||
this.btnDurationShort.TabIndex = 0;
|
||||
this.btnDurationShort.Text = "Short";
|
||||
this.btnDurationShort.UseVisualStyleBackColor = true;
|
||||
this.btnDurationShort.Click += new System.EventHandler(this.btnDurationShort_Click);
|
||||
//
|
||||
// labelDurationValue
|
||||
//
|
||||
this.labelDurationValue.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.labelDurationValue.BackColor = System.Drawing.Color.Transparent;
|
||||
this.labelDurationValue.Location = new System.Drawing.Point(129, 20);
|
||||
this.labelDurationValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
|
||||
this.labelDurationValue.Name = "labelDurationValue";
|
||||
this.labelDurationValue.Size = new System.Drawing.Size(48, 13);
|
||||
this.labelDurationValue.TabIndex = 13;
|
||||
this.labelDurationValue.Text = "0 ms/c";
|
||||
this.labelDurationValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
|
||||
this.toolTip.SetToolTip(this.labelDurationValue, "Milliseconds per character.");
|
||||
//
|
||||
// trackBarDuration
|
||||
//
|
||||
this.trackBarDuration.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.trackBarDuration.Location = new System.Drawing.Point(6, 19);
|
||||
this.trackBarDuration.Maximum = 60;
|
||||
this.trackBarDuration.Minimum = 10;
|
||||
this.trackBarDuration.Name = "trackBarDuration";
|
||||
this.trackBarDuration.Size = new System.Drawing.Size(128, 45);
|
||||
this.trackBarDuration.TabIndex = 12;
|
||||
this.trackBarDuration.TickFrequency = 5;
|
||||
this.trackBarDuration.Value = 25;
|
||||
this.trackBarDuration.ValueChanged += new System.EventHandler(this.trackBarDuration_ValueChanged);
|
||||
//
|
||||
// groupUserInterface
|
||||
//
|
||||
this.groupUserInterface.Controls.Add(this.checkTimerCountDown);
|
||||
this.groupUserInterface.Controls.Add(this.checkNotificationTimer);
|
||||
this.groupUserInterface.Location = new System.Drawing.Point(198, 134);
|
||||
this.groupUserInterface.Location = new System.Drawing.Point(9, 9);
|
||||
this.groupUserInterface.Name = "groupUserInterface";
|
||||
this.groupUserInterface.Size = new System.Drawing.Size(183, 48);
|
||||
this.groupUserInterface.Size = new System.Drawing.Size(183, 68);
|
||||
this.groupUserInterface.TabIndex = 10;
|
||||
this.groupUserInterface.TabStop = false;
|
||||
this.groupUserInterface.Text = "User Interface";
|
||||
this.groupUserInterface.Text = "General";
|
||||
//
|
||||
// checkTimerCountDown
|
||||
//
|
||||
this.checkTimerCountDown.AutoSize = true;
|
||||
this.checkTimerCountDown.Location = new System.Drawing.Point(6, 44);
|
||||
this.checkTimerCountDown.Name = "checkTimerCountDown";
|
||||
this.checkTimerCountDown.Size = new System.Drawing.Size(119, 17);
|
||||
this.checkTimerCountDown.TabIndex = 6;
|
||||
this.checkTimerCountDown.Text = "Timer Counts Down";
|
||||
this.toolTip.SetToolTip(this.checkTimerCountDown, "The notification timer counts down instead of up.");
|
||||
this.checkTimerCountDown.UseVisualStyleBackColor = true;
|
||||
this.checkTimerCountDown.CheckedChanged += new System.EventHandler(this.checkTimerCountDown_CheckedChanged);
|
||||
//
|
||||
// checkNotificationTimer
|
||||
//
|
||||
@@ -256,26 +335,76 @@
|
||||
this.checkNotificationTimer.Size = new System.Drawing.Size(145, 17);
|
||||
this.checkNotificationTimer.TabIndex = 4;
|
||||
this.checkNotificationTimer.Text = "Display Notification Timer";
|
||||
this.toolTip.SetToolTip(this.checkNotificationTimer, "Shows how much time is left before the current notification disappears.");
|
||||
this.checkNotificationTimer.UseVisualStyleBackColor = true;
|
||||
this.checkNotificationTimer.CheckedChanged += new System.EventHandler(this.checkNotificationTimer_CheckedChanged);
|
||||
//
|
||||
// groupCustomSound
|
||||
//
|
||||
this.groupCustomSound.Controls.Add(this.btnResetSound);
|
||||
this.groupCustomSound.Controls.Add(this.btnBrowseSound);
|
||||
this.groupCustomSound.Controls.Add(this.tbCustomSound);
|
||||
this.groupCustomSound.Location = new System.Drawing.Point(9, 178);
|
||||
this.groupCustomSound.Name = "groupCustomSound";
|
||||
this.groupCustomSound.Size = new System.Drawing.Size(183, 72);
|
||||
this.groupCustomSound.TabIndex = 11;
|
||||
this.groupCustomSound.TabStop = false;
|
||||
this.groupCustomSound.Text = "Custom Sound";
|
||||
//
|
||||
// btnResetSound
|
||||
//
|
||||
this.btnResetSound.AutoSize = true;
|
||||
this.btnResetSound.Location = new System.Drawing.Point(126, 43);
|
||||
this.btnResetSound.Name = "btnResetSound";
|
||||
this.btnResetSound.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||
this.btnResetSound.Size = new System.Drawing.Size(51, 23);
|
||||
this.btnResetSound.TabIndex = 2;
|
||||
this.btnResetSound.Text = "Reset";
|
||||
this.btnResetSound.UseVisualStyleBackColor = true;
|
||||
this.btnResetSound.Click += new System.EventHandler(this.btnResetSound_Click);
|
||||
//
|
||||
// btnBrowseSound
|
||||
//
|
||||
this.btnBrowseSound.AutoSize = true;
|
||||
this.btnBrowseSound.Location = new System.Drawing.Point(53, 43);
|
||||
this.btnBrowseSound.Name = "btnBrowseSound";
|
||||
this.btnBrowseSound.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||
this.btnBrowseSound.Size = new System.Drawing.Size(67, 23);
|
||||
this.btnBrowseSound.TabIndex = 1;
|
||||
this.btnBrowseSound.Text = "Browse...";
|
||||
this.btnBrowseSound.UseVisualStyleBackColor = true;
|
||||
this.btnBrowseSound.Click += new System.EventHandler(this.btnBrowseSound_Click);
|
||||
//
|
||||
// tbCustomSound
|
||||
//
|
||||
this.tbCustomSound.Location = new System.Drawing.Point(6, 19);
|
||||
this.tbCustomSound.Name = "tbCustomSound";
|
||||
this.tbCustomSound.Size = new System.Drawing.Size(170, 20);
|
||||
this.tbCustomSound.TabIndex = 0;
|
||||
this.tbCustomSound.TextChanged += new System.EventHandler(this.tbCustomSound_TextChanged);
|
||||
//
|
||||
// TabSettingsNotifications
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.Controls.Add(this.groupCustomSound);
|
||||
this.Controls.Add(this.groupUserInterface);
|
||||
this.Controls.Add(this.groupNotificationDuration);
|
||||
this.Controls.Add(this.groupNotificationLocation);
|
||||
this.Name = "TabSettingsNotifications";
|
||||
this.Size = new System.Drawing.Size(397, 282);
|
||||
this.Size = new System.Drawing.Size(478, 282);
|
||||
this.ParentChanged += new System.EventHandler(this.TabSettingsNotifications_ParentChanged);
|
||||
this.groupNotificationLocation.ResumeLayout(false);
|
||||
this.groupNotificationLocation.PerformLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.trackBarEdgeDistance)).EndInit();
|
||||
this.groupNotificationDuration.ResumeLayout(false);
|
||||
this.groupNotificationDuration.PerformLayout();
|
||||
this.tableLayoutDurationButtons.ResumeLayout(false);
|
||||
((System.ComponentModel.ISupportInitialize)(this.trackBarDuration)).EndInit();
|
||||
this.groupUserInterface.ResumeLayout(false);
|
||||
this.groupUserInterface.PerformLayout();
|
||||
this.groupCustomSound.ResumeLayout(false);
|
||||
this.groupCustomSound.PerformLayout();
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
@@ -293,11 +422,20 @@
|
||||
private System.Windows.Forms.RadioButton radioLocTR;
|
||||
private System.Windows.Forms.RadioButton radioLocTL;
|
||||
private System.Windows.Forms.GroupBox groupNotificationDuration;
|
||||
private System.Windows.Forms.RadioButton radioDurVeryLong;
|
||||
private System.Windows.Forms.RadioButton radioDurLong;
|
||||
private System.Windows.Forms.RadioButton radioDurMedium;
|
||||
private System.Windows.Forms.RadioButton radioDurShort;
|
||||
private System.Windows.Forms.GroupBox groupUserInterface;
|
||||
private System.Windows.Forms.CheckBox checkNotificationTimer;
|
||||
private System.Windows.Forms.ToolTip toolTip;
|
||||
private System.Windows.Forms.Label labelEdgeDistanceValue;
|
||||
private System.Windows.Forms.CheckBox checkTimerCountDown;
|
||||
private System.Windows.Forms.Label labelDurationValue;
|
||||
private System.Windows.Forms.TrackBar trackBarDuration;
|
||||
private System.Windows.Forms.TableLayoutPanel tableLayoutDurationButtons;
|
||||
private TweetDck.Core.Controls.FlatButton btnDurationMedium;
|
||||
private TweetDck.Core.Controls.FlatButton btnDurationLong;
|
||||
private TweetDck.Core.Controls.FlatButton btnDurationShort;
|
||||
private System.Windows.Forms.GroupBox groupCustomSound;
|
||||
private System.Windows.Forms.Button btnResetSound;
|
||||
private System.Windows.Forms.Button btnBrowseSound;
|
||||
private System.Windows.Forms.TextBox tbCustomSound;
|
||||
}
|
||||
}
|
||||
|
@@ -1,12 +1,18 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
using TweetDck.Core.Handling;
|
||||
using TweetDck.Core.Controls;
|
||||
using TweetDck.Core.Notification;
|
||||
using TweetDck.Core.Utils;
|
||||
|
||||
namespace TweetDck.Core.Other.Settings{
|
||||
partial class TabSettingsNotifications : BaseTabSettings{
|
||||
private readonly FormNotification notification;
|
||||
private readonly Point initCursorPosition;
|
||||
|
||||
public TabSettingsNotifications(FormNotification notification){
|
||||
public TabSettingsNotifications(FormNotification notification, bool ignoreAutoClick){
|
||||
InitializeComponent();
|
||||
|
||||
this.notification = notification;
|
||||
@@ -19,11 +25,14 @@ namespace TweetDck.Core.Other.Settings{
|
||||
};
|
||||
|
||||
this.notification.Initialized += (sender, args) => {
|
||||
this.notification.ShowNotificationForSettings(true);
|
||||
this.InvokeSafe(() => this.notification.ShowNotificationForSettings(true));
|
||||
};
|
||||
|
||||
this.notification.Activated += notification_Activated;
|
||||
this.notification.Show(this);
|
||||
|
||||
initCursorPosition = ignoreAutoClick ? ControlExtensions.InvisibleLocation : Cursor.Position;
|
||||
|
||||
switch(Config.NotificationPosition){
|
||||
case TweetNotification.Position.TopLeft: radioLocTL.Checked = true; break;
|
||||
case TweetNotification.Position.TopRight: radioLocTR.Checked = true; break;
|
||||
@@ -32,12 +41,8 @@ namespace TweetDck.Core.Other.Settings{
|
||||
case TweetNotification.Position.Custom: radioLocCustom.Checked = true; break;
|
||||
}
|
||||
|
||||
switch(Config.NotificationDuration){
|
||||
case TweetNotification.Duration.Short: radioDurShort.Checked = true; break;
|
||||
case TweetNotification.Duration.Medium: radioDurMedium.Checked = true; break;
|
||||
case TweetNotification.Duration.Long: radioDurLong.Checked = true; break;
|
||||
case TweetNotification.Duration.VeryLong: radioDurVeryLong.Checked = true; break;
|
||||
}
|
||||
trackBarDuration.SetValueSafe(Config.NotificationDurationValue);
|
||||
labelDurationValue.Text = Config.NotificationDurationValue+" ms/c";
|
||||
|
||||
comboBoxDisplay.Items.Add("(Same As "+Program.BrandName+")");
|
||||
|
||||
@@ -45,10 +50,22 @@ namespace TweetDck.Core.Other.Settings{
|
||||
comboBoxDisplay.Items.Add(screen.DeviceName+" ("+screen.Bounds.Width+"x"+screen.Bounds.Height+")");
|
||||
}
|
||||
|
||||
comboBoxDisplay.SelectedIndex = Math.Min(comboBoxDisplay.Items.Count-1,Config.NotificationDisplay);
|
||||
comboBoxDisplay.SelectedIndex = Math.Min(comboBoxDisplay.Items.Count-1, Config.NotificationDisplay);
|
||||
|
||||
checkNotificationTimer.Checked = Config.DisplayNotificationTimer;
|
||||
trackBarEdgeDistance.Value = Config.NotificationEdgeDistance;
|
||||
checkTimerCountDown.Enabled = checkNotificationTimer.Checked;
|
||||
checkTimerCountDown.Checked = Config.NotificationTimerCountDown;
|
||||
|
||||
trackBarEdgeDistance.SetValueSafe(Config.NotificationEdgeDistance);
|
||||
labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value.ToString(CultureInfo.InvariantCulture)+" px";
|
||||
|
||||
tbCustomSound.Text = Config.NotificationSoundPath;
|
||||
|
||||
Disposed += (sender, args) => this.notification.Dispose();
|
||||
}
|
||||
|
||||
public override void OnClosing(){
|
||||
Config.NotificationSoundPath = tbCustomSound.Text;
|
||||
}
|
||||
|
||||
private void TabSettingsNotifications_ParentChanged(object sender, EventArgs e){
|
||||
@@ -56,10 +73,25 @@ namespace TweetDck.Core.Other.Settings{
|
||||
notification.HideNotification(false);
|
||||
}
|
||||
else{
|
||||
notification.ShowNotificationForSettings(false);
|
||||
notification.ShowNotificationForSettings(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void notification_Activated(object sender, EventArgs e){
|
||||
if (Cursor.Position == initCursorPosition && initCursorPosition != ControlExtensions.InvisibleLocation){
|
||||
Timer delay = WindowsUtils.CreateSingleTickTimer(1);
|
||||
|
||||
delay.Tick += (sender2, args2) => { // here you can see a disgusting hack to force the freshly opened notification window out of focus
|
||||
NativeMethods.SimulateMouseClick(NativeMethods.MouseButton.Left); // because for some reason, the stupid thing keeps stealing it
|
||||
delay.Dispose(); // even after using ShowWithoutActivation, the CreateParams bullshit, and about a million different combinations
|
||||
}; // of trying to force the original form back into focus in various events, so you will have to fucking deal with it, alright
|
||||
|
||||
delay.Start();
|
||||
}
|
||||
|
||||
notification.Activated -= notification_Activated;
|
||||
}
|
||||
|
||||
private void radioLoc_CheckedChanged(object sender, EventArgs e){
|
||||
if (!Ready)return;
|
||||
|
||||
@@ -79,27 +111,45 @@ namespace TweetDck.Core.Other.Settings{
|
||||
notification.ShowNotificationForSettings(false);
|
||||
}
|
||||
|
||||
private void radioDur_CheckedChanged(object sender, EventArgs e){
|
||||
private void trackBarDuration_ValueChanged(object sender, EventArgs e){
|
||||
if (!Ready)return;
|
||||
|
||||
if (radioDurShort.Checked)Config.NotificationDuration = TweetNotification.Duration.Short;
|
||||
else if (radioDurMedium.Checked)Config.NotificationDuration = TweetNotification.Duration.Medium;
|
||||
else if (radioDurLong.Checked)Config.NotificationDuration = TweetNotification.Duration.Long;
|
||||
else if (radioDurVeryLong.Checked)Config.NotificationDuration = TweetNotification.Duration.VeryLong;
|
||||
|
||||
Config.NotificationDurationValue = trackBarDuration.Value;
|
||||
labelDurationValue.Text = Config.NotificationDurationValue+" ms/c";
|
||||
|
||||
notification.ShowNotificationForSettings(true);
|
||||
}
|
||||
|
||||
private void radioDur_Click(object sender, EventArgs e){
|
||||
private void btnDurationShort_Click(object sender, EventArgs e){
|
||||
if (!Ready)return;
|
||||
|
||||
notification.ShowNotificationForSettings(true);
|
||||
trackBarDuration.Value = 15;
|
||||
}
|
||||
|
||||
private void btnDurationMedium_Click(object sender, EventArgs e){
|
||||
if (!Ready)return;
|
||||
|
||||
trackBarDuration.Value = 25;
|
||||
}
|
||||
|
||||
private void btnDurationLong_Click(object sender, EventArgs e){
|
||||
if (!Ready)return;
|
||||
|
||||
trackBarDuration.Value = 35;
|
||||
}
|
||||
|
||||
private void checkNotificationTimer_CheckedChanged(object sender, EventArgs e){
|
||||
if (!Ready)return;
|
||||
|
||||
Config.DisplayNotificationTimer = checkNotificationTimer.Checked;
|
||||
checkTimerCountDown.Enabled = checkNotificationTimer.Checked;
|
||||
notification.ShowNotificationForSettings(true);
|
||||
}
|
||||
|
||||
private void checkTimerCountDown_CheckedChanged(object sender, EventArgs e){
|
||||
if (!Ready)return;
|
||||
|
||||
Config.NotificationTimerCountDown = checkTimerCountDown.Checked;
|
||||
notification.ShowNotificationForSettings(true);
|
||||
}
|
||||
|
||||
@@ -112,9 +162,34 @@ namespace TweetDck.Core.Other.Settings{
|
||||
|
||||
private void trackBarEdgeDistance_ValueChanged(object sender, EventArgs e){
|
||||
if (!Ready)return;
|
||||
|
||||
|
||||
labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value.ToString(CultureInfo.InvariantCulture)+" px";
|
||||
Config.NotificationEdgeDistance = trackBarEdgeDistance.Value;
|
||||
notification.ShowNotificationForSettings(false);
|
||||
}
|
||||
|
||||
private void tbCustomSound_TextChanged(object sender, EventArgs e){
|
||||
// also runs when the control is created, i.e. when Ready is false
|
||||
|
||||
bool fileExists = string.IsNullOrEmpty(tbCustomSound.Text) || File.Exists(tbCustomSound.Text);
|
||||
tbCustomSound.ForeColor = fileExists ? SystemColors.WindowText : Color.Maroon;
|
||||
}
|
||||
|
||||
private void btnBrowseSound_Click(object sender, EventArgs e){
|
||||
using(OpenFileDialog dialog = new OpenFileDialog{
|
||||
AutoUpgradeEnabled = true,
|
||||
DereferenceLinks = true,
|
||||
Title = "Custom Notification Sound",
|
||||
Filter = "Wave file (*.wav)|*.wav"
|
||||
}){
|
||||
if (dialog.ShowDialog() == DialogResult.OK){
|
||||
tbCustomSound.Text = dialog.FileName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void btnResetSound_Click(object sender, EventArgs e){
|
||||
tbCustomSound.Text = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
33
Core/Other/Settings/TabSettingsUpdates.Designer.cs
generated
33
Core/Other/Settings/TabSettingsUpdates.Designer.cs
generated
@@ -23,41 +23,60 @@
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent() {
|
||||
this.components = new System.ComponentModel.Container();
|
||||
this.btnCheckUpdates = new System.Windows.Forms.Button();
|
||||
this.checkUpdateNotifications = new System.Windows.Forms.CheckBox();
|
||||
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
||||
this.groupGeneral = new System.Windows.Forms.GroupBox();
|
||||
this.groupGeneral.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// btnCheckUpdates
|
||||
//
|
||||
this.btnCheckUpdates.Location = new System.Drawing.Point(9, 32);
|
||||
this.btnCheckUpdates.Location = new System.Drawing.Point(6, 44);
|
||||
this.btnCheckUpdates.Name = "btnCheckUpdates";
|
||||
this.btnCheckUpdates.Size = new System.Drawing.Size(171, 23);
|
||||
this.btnCheckUpdates.TabIndex = 15;
|
||||
this.btnCheckUpdates.Text = "Check Updates Now";
|
||||
this.toolTip.SetToolTip(this.btnCheckUpdates, "Forces an update check, even for updates that had been dismissed.");
|
||||
this.btnCheckUpdates.UseVisualStyleBackColor = true;
|
||||
this.btnCheckUpdates.Click += new System.EventHandler(this.btnCheckUpdates_Click);
|
||||
//
|
||||
// checkUpdateNotifications
|
||||
//
|
||||
this.checkUpdateNotifications.AutoSize = true;
|
||||
this.checkUpdateNotifications.Location = new System.Drawing.Point(9, 9);
|
||||
this.checkUpdateNotifications.Location = new System.Drawing.Point(6, 21);
|
||||
this.checkUpdateNotifications.Margin = new System.Windows.Forms.Padding(3, 5, 3, 3);
|
||||
this.checkUpdateNotifications.Name = "checkUpdateNotifications";
|
||||
this.checkUpdateNotifications.Size = new System.Drawing.Size(165, 17);
|
||||
this.checkUpdateNotifications.TabIndex = 14;
|
||||
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" +
|
||||
"in.");
|
||||
this.checkUpdateNotifications.UseVisualStyleBackColor = true;
|
||||
this.checkUpdateNotifications.CheckedChanged += new System.EventHandler(this.checkUpdateNotifications_CheckedChanged);
|
||||
//
|
||||
// groupGeneral
|
||||
//
|
||||
this.groupGeneral.Controls.Add(this.checkUpdateNotifications);
|
||||
this.groupGeneral.Controls.Add(this.btnCheckUpdates);
|
||||
this.groupGeneral.Location = new System.Drawing.Point(9, 9);
|
||||
this.groupGeneral.Name = "groupGeneral";
|
||||
this.groupGeneral.Size = new System.Drawing.Size(183, 75);
|
||||
this.groupGeneral.TabIndex = 16;
|
||||
this.groupGeneral.TabStop = false;
|
||||
this.groupGeneral.Text = "General";
|
||||
//
|
||||
// TabSettingsUpdates
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.Controls.Add(this.btnCheckUpdates);
|
||||
this.Controls.Add(this.checkUpdateNotifications);
|
||||
this.Controls.Add(this.groupGeneral);
|
||||
this.Name = "TabSettingsUpdates";
|
||||
this.Size = new System.Drawing.Size(211, 78);
|
||||
this.Size = new System.Drawing.Size(478, 282);
|
||||
this.groupGeneral.ResumeLayout(false);
|
||||
this.groupGeneral.PerformLayout();
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
@@ -65,5 +84,7 @@
|
||||
|
||||
private System.Windows.Forms.Button btnCheckUpdates;
|
||||
private System.Windows.Forms.CheckBox checkUpdateNotifications;
|
||||
private System.Windows.Forms.ToolTip toolTip;
|
||||
private System.Windows.Forms.GroupBox groupGeneral;
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using TweetDck.Updates;
|
||||
using TweetDck.Updates.Events;
|
||||
|
||||
namespace TweetDck.Core.Other.Settings{
|
||||
partial class TabSettingsUpdates : BaseTabSettings{
|
||||
@@ -27,12 +28,18 @@ namespace TweetDck.Core.Other.Settings{
|
||||
private void btnCheckUpdates_Click(object sender, EventArgs e){
|
||||
if (!Ready)return;
|
||||
|
||||
Config.DismissedUpdate = string.Empty;
|
||||
Config.Save();
|
||||
|
||||
updateCheckEventId = updates.Check(true);
|
||||
|
||||
btnCheckUpdates.Enabled = false;
|
||||
if (updateCheckEventId == -1){
|
||||
MessageBox.Show("Sorry, your system is no longer supported.", "Unsupported System", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
}
|
||||
else{
|
||||
btnCheckUpdates.Enabled = false;
|
||||
|
||||
updates.Settings.DismissedUpdate = string.Empty;
|
||||
Config.DismissedUpdate = string.Empty;
|
||||
Config.Save();
|
||||
}
|
||||
}
|
||||
|
||||
private void updates_CheckFinished(object sender, UpdateCheckEventArgs e){
|
||||
@@ -40,7 +47,7 @@ namespace TweetDck.Core.Other.Settings{
|
||||
btnCheckUpdates.Enabled = true;
|
||||
|
||||
if (!e.UpdateAvailable){
|
||||
MessageBox.Show("Your version of "+Program.BrandName+" is up to date.","No Updates Available",MessageBoxButtons.OK,MessageBoxIcon.Information);
|
||||
MessageBox.Show("Your version of "+Program.BrandName+" is up to date.", "No Updates Available", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -5,7 +5,7 @@ using System.Windows.Forms;
|
||||
namespace TweetDck.Core{
|
||||
partial class TrayIcon : Component{
|
||||
public enum Behavior{ // keep order
|
||||
Disabled, DisplayOnly, MinimizeToTray, CloseToTray
|
||||
Disabled, DisplayOnly, MinimizeToTray, CloseToTray, Combined
|
||||
}
|
||||
|
||||
public event EventHandler ClickRestore;
|
||||
@@ -45,7 +45,7 @@ namespace TweetDck.Core{
|
||||
|
||||
private void trayIcon_MouseClick(object sender, MouseEventArgs e){
|
||||
if (e.Button == MouseButtons.Left){
|
||||
restoreToolStripMenuItem_Click(sender,e);
|
||||
restoreToolStripMenuItem_Click(sender, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace TweetDck.Core{
|
||||
|
||||
private void restoreToolStripMenuItem_Click(object sender, EventArgs e){
|
||||
if (ClickRestore != null){
|
||||
ClickRestore(this,e);
|
||||
ClickRestore(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,8 +71,22 @@ namespace TweetDck.Core{
|
||||
|
||||
private void closeToolStripMenuItem_Click(object sender, EventArgs e){
|
||||
if (ClickClose != null){
|
||||
ClickClose(this,e);
|
||||
ClickClose(this, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class BehaviorExtensions{
|
||||
public static bool ShouldDisplayIcon(this TrayIcon.Behavior behavior){
|
||||
return behavior != TrayIcon.Behavior.Disabled;
|
||||
}
|
||||
|
||||
public static bool ShouldHideOnMinimize(this TrayIcon.Behavior behavior){
|
||||
return behavior == TrayIcon.Behavior.MinimizeToTray || behavior == TrayIcon.Behavior.Combined;
|
||||
}
|
||||
|
||||
public static bool ShouldHideOnClose(this TrayIcon.Behavior behavior){
|
||||
return behavior == TrayIcon.Behavior.CloseToTray || behavior == TrayIcon.Behavior.Combined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -8,14 +8,11 @@ namespace TweetDck.Core.Utils{
|
||||
static class BrowserCache{
|
||||
private static bool ClearOnExit { get; set; }
|
||||
|
||||
private static readonly string IndexFile = Path.Combine(Program.StoragePath,"index");
|
||||
private static readonly string CacheFolder = Path.Combine(Program.StoragePath, "Cache");
|
||||
|
||||
private static IEnumerable<string> CacheFiles{
|
||||
get{
|
||||
return Directory.EnumerateFiles(Program.StoragePath).Where(path => {
|
||||
string file = Path.GetFileName(path);
|
||||
return file != null && (file.StartsWith("data_",StringComparison.Ordinal) || file.StartsWith("f_",StringComparison.Ordinal));
|
||||
}).Concat(new[]{ IndexFile });
|
||||
return Directory.EnumerateFiles(CacheFolder);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,10 +27,25 @@ namespace TweetDck.Core.Utils{
|
||||
}).Sum();
|
||||
});
|
||||
|
||||
task.ContinueWith(originalTask => callbackBytes(originalTask.Exception == null ? originalTask.Result : -1L),TaskContinuationOptions.ExecuteSynchronously);
|
||||
task.ContinueWith(originalTask => callbackBytes(originalTask.Exception == null ? originalTask.Result : -1L), TaskContinuationOptions.ExecuteSynchronously);
|
||||
task.Start();
|
||||
}
|
||||
|
||||
public static void ClearOldCacheFiles(){
|
||||
if (!Directory.Exists(CacheFolder)){
|
||||
foreach(string file in Directory.EnumerateFiles(Program.StoragePath).Where(path => {
|
||||
string file = Path.GetFileName(path);
|
||||
return file != null && (file.StartsWith("data_", StringComparison.Ordinal) || file.StartsWith("f_", StringComparison.Ordinal));
|
||||
}).Concat(new[]{ Path.Combine(Program.StoragePath, "index") })){
|
||||
try{
|
||||
File.Delete(file);
|
||||
}catch{
|
||||
// welp, too bad
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetClearOnExit(){
|
||||
ClearOnExit = true;
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Windows.Forms;
|
||||
using CefSharp;
|
||||
|
||||
namespace TweetDck.Core.Utils{
|
||||
static class BrowserUtils{
|
||||
@@ -27,7 +28,7 @@ namespace TweetDck.Core.Utils{
|
||||
}
|
||||
|
||||
public static void OpenExternalBrowser(string url){ // TODO implement mailto
|
||||
Process.Start(url);
|
||||
using(Process.Start(url)){}
|
||||
}
|
||||
|
||||
public static string GetFileNameFromUrl(string url){
|
||||
@@ -45,7 +46,17 @@ namespace TweetDck.Core.Utils{
|
||||
}
|
||||
};
|
||||
|
||||
client.DownloadFileAsync(new Uri(url),target);
|
||||
client.DownloadFileAsync(new Uri(url), target);
|
||||
}
|
||||
|
||||
public static bool IsTweetDeckWebsite(IFrame frame){
|
||||
return frame.Url.Contains("//tweetdeck.twitter.com/");
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
public static void HandleConsoleMessage(object sender, ConsoleMessageEventArgs e){
|
||||
Debug.WriteLine("[Console] {0} ({1}:{2})", e.Message, e.Source, e.Line);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
111
Core/Utils/CommandLineArgs.cs
Normal file
111
Core/Utils/CommandLineArgs.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace TweetDck.Core.Utils{
|
||||
class CommandLineArgs{
|
||||
public static CommandLineArgs FromStringArray(char entryChar, string[] array){
|
||||
CommandLineArgs args = new CommandLineArgs();
|
||||
ReadStringArray(entryChar, array, args);
|
||||
return args;
|
||||
}
|
||||
|
||||
public static void ReadStringArray(char entryChar, string[] array, CommandLineArgs targetArgs){
|
||||
for(int index = 0; index < array.Length; index++){
|
||||
string entry = array[index];
|
||||
|
||||
if (entry.Length > 0 && entry[0] == entryChar){
|
||||
if (index < array.Length-1){
|
||||
string potentialValue = array[index+1];
|
||||
|
||||
if (potentialValue.Length > 0 && potentialValue[0] == entryChar){
|
||||
targetArgs.AddFlag(entry);
|
||||
}
|
||||
else{
|
||||
targetArgs.SetValue(entry, potentialValue);
|
||||
++index;
|
||||
}
|
||||
}
|
||||
else{
|
||||
targetArgs.AddFlag(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly HashSet<string> flags = new HashSet<string>();
|
||||
private readonly Dictionary<string, string> values = new Dictionary<string, string>();
|
||||
|
||||
public int Count{
|
||||
get{
|
||||
return flags.Count+values.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddFlag(string flag){
|
||||
flags.Add(flag.ToLowerInvariant());
|
||||
}
|
||||
|
||||
public bool HasFlag(string flag){
|
||||
return flags.Contains(flag.ToLowerInvariant());
|
||||
}
|
||||
|
||||
public void RemoveFlag(string flag){
|
||||
flags.Remove(flag.ToLowerInvariant());
|
||||
}
|
||||
|
||||
public void SetValue(string key, string value){
|
||||
values[key.ToLowerInvariant()] = value;
|
||||
}
|
||||
|
||||
public bool HasValue(string key){
|
||||
return values.ContainsKey(key.ToLowerInvariant());
|
||||
}
|
||||
|
||||
public string GetValue(string key, string defaultValue){
|
||||
string val;
|
||||
return values.TryGetValue(key.ToLowerInvariant(), out val) ? val : defaultValue;
|
||||
}
|
||||
|
||||
public void RemoveValue(string key){
|
||||
values.Remove(key.ToLowerInvariant());
|
||||
}
|
||||
|
||||
public CommandLineArgs Clone(){
|
||||
CommandLineArgs copy = new CommandLineArgs();
|
||||
|
||||
foreach(string flag in flags){
|
||||
copy.AddFlag(flag);
|
||||
}
|
||||
|
||||
foreach(KeyValuePair<string, string> kvp in values){
|
||||
copy.SetValue(kvp.Key, kvp.Value);
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
public void ToDictionary(IDictionary<string, string> target){
|
||||
foreach(string flag in flags){
|
||||
target[flag] = "1";
|
||||
}
|
||||
|
||||
foreach(KeyValuePair<string, string> kvp in values){
|
||||
target[kvp.Key] = kvp.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString(){
|
||||
StringBuilder build = new StringBuilder();
|
||||
|
||||
foreach(string flag in flags){
|
||||
build.Append(flag).Append(' ');
|
||||
}
|
||||
|
||||
foreach(KeyValuePair<string, string> kvp in values){
|
||||
build.Append(kvp.Key).Append(" \"").Append(kvp.Value).Append("\" ");
|
||||
}
|
||||
|
||||
return build.Length == 0 ? string.Empty : build.Remove(build.Length-1, 1).ToString();
|
||||
}
|
||||
}
|
||||
}
|
43
Core/Utils/CommandLineArgsParser.cs
Normal file
43
Core/Utils/CommandLineArgsParser.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace TweetDck.Core.Utils{
|
||||
static class CommandLineArgsParser{
|
||||
private static Regex splitRegex;
|
||||
|
||||
private static Regex SplitRegex{
|
||||
get{
|
||||
return splitRegex ?? (splitRegex = new Regex(@"([^=\s]+(?:=(?:[^ ]*""[^""]*?""[^ ]*|[^ ]*))?)", RegexOptions.Compiled));
|
||||
}
|
||||
}
|
||||
|
||||
public static CommandLineArgs ReadCefArguments(string argumentString){
|
||||
CommandLineArgs args = new CommandLineArgs();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(argumentString)){
|
||||
return args;
|
||||
}
|
||||
|
||||
foreach(Match match in SplitRegex.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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -3,8 +3,8 @@ using System.IO;
|
||||
|
||||
namespace TweetDck.Core.Utils{
|
||||
static class HardwareAcceleration{
|
||||
private static readonly string LibEGL = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"libEGL.dll");
|
||||
private static readonly string LibGLES = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"libGLESv2.dll");
|
||||
private static readonly string LibEGL = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "libEGL.dll");
|
||||
private static readonly string LibGLES = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "libGLESv2.dll");
|
||||
|
||||
private static readonly string DisabledLibEGL = LibEGL+".bak";
|
||||
private static readonly string DisabledLibGLES = LibGLES+".bak";
|
||||
@@ -25,8 +25,8 @@ namespace TweetDck.Core.Utils{
|
||||
if (IsEnabled)return false;
|
||||
|
||||
try{
|
||||
File.Move(DisabledLibEGL,LibEGL);
|
||||
File.Move(DisabledLibGLES,LibGLES);
|
||||
File.Move(DisabledLibEGL, LibEGL);
|
||||
File.Move(DisabledLibGLES, LibGLES);
|
||||
return true;
|
||||
}catch{
|
||||
return false;
|
||||
@@ -49,8 +49,8 @@ namespace TweetDck.Core.Utils{
|
||||
}
|
||||
|
||||
try{
|
||||
File.Move(LibEGL,DisabledLibEGL);
|
||||
File.Move(LibGLES,DisabledLibGLES);
|
||||
File.Move(LibEGL, DisabledLibEGL);
|
||||
File.Move(LibGLES, DisabledLibGLES);
|
||||
return true;
|
||||
}catch{
|
||||
return false;
|
||||
|
@@ -15,19 +15,35 @@ namespace TweetDck.Core.Utils{
|
||||
public const int MOUSEEVENTF_RIGHTUP = 0x10;
|
||||
|
||||
public const int SB_HORZ = 0;
|
||||
public const int BCM_SETSHIELD = 0x160C;
|
||||
|
||||
public const int WH_MOUSE_LL = 14;
|
||||
public const int WH_MOUSEWHEEL = 0x020A;
|
||||
|
||||
public enum MouseButton{
|
||||
Left, Right
|
||||
}
|
||||
|
||||
[DllImport("Shell32.dll")]
|
||||
public static extern int SHChangeNotify(int eventId, int flags, IntPtr item1, IntPtr item2);
|
||||
private struct LASTINPUTINFO{
|
||||
public static readonly uint Size = (uint)Marshal.SizeOf(typeof(LASTINPUTINFO));
|
||||
|
||||
// ReSharper disable once NotAccessedField.Local
|
||||
public uint cbSize;
|
||||
#pragma warning disable 649
|
||||
public uint dwTime;
|
||||
#pragma warning restore 649
|
||||
}
|
||||
|
||||
public delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
|
||||
public static extern bool SetWindowPos(int hWnd, int hWndOrder, int x, int y, int width, int height, uint flags);
|
||||
private static extern bool SetWindowPos(int hWnd, int hWndOrder, int x, int y, int width, int height, uint flags);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);
|
||||
private static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool GetLastInputInfo(ref LASTINPUTINFO info);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam, IntPtr lParam);
|
||||
@@ -39,8 +55,17 @@ namespace TweetDck.Core.Utils{
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool ShowScrollBar(IntPtr hWnd, int wBar, bool bShow);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool UnhookWindowsHookEx(IntPtr idHook);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern IntPtr CallNextHookEx(IntPtr idHook, int nCode, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
public static void SetFormPos(Form form, int hWndOrder, uint flags){
|
||||
SetWindowPos(form.Handle.ToInt32(),hWndOrder,form.Left,form.Top,form.Width,form.Height,flags);
|
||||
SetWindowPos(form.Handle.ToInt32(), hWndOrder, form.Left, form.Top, form.Width, form.Height, flags);
|
||||
}
|
||||
|
||||
public static void SimulateMouseClick(MouseButton button){
|
||||
@@ -60,8 +85,26 @@ namespace TweetDck.Core.Utils{
|
||||
default: return;
|
||||
}
|
||||
|
||||
mouse_event(flagHold,Cursor.Position.X,Cursor.Position.Y,0,0);
|
||||
mouse_event(flagRelease,Cursor.Position.X,Cursor.Position.Y,0,0);
|
||||
mouse_event(flagHold, Cursor.Position.X, Cursor.Position.Y, 0, 0);
|
||||
mouse_event(flagRelease, Cursor.Position.X, Cursor.Position.Y, 0, 0);
|
||||
}
|
||||
|
||||
public static int GetIdleSeconds(){
|
||||
LASTINPUTINFO info = new LASTINPUTINFO();
|
||||
info.cbSize = LASTINPUTINFO.Size;
|
||||
|
||||
if (!GetLastInputInfo(ref info)){
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint ticks;
|
||||
|
||||
unchecked{
|
||||
ticks = (uint)Environment.TickCount;
|
||||
}
|
||||
|
||||
int seconds = (int)Math.Floor(TimeSpan.FromMilliseconds(ticks-info.dwTime).TotalSeconds);
|
||||
return Math.Max(0, seconds); // ignore rollover after several weeks of uptime
|
||||
}
|
||||
}
|
||||
}
|
||||
|
100
Core/Utils/TwoKeyDictionary.cs
Normal file
100
Core/Utils/TwoKeyDictionary.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace TweetDck.Core.Utils{
|
||||
class TwoKeyDictionary<K1, K2, V>{
|
||||
private readonly Dictionary<K1, Dictionary<K2, V>> dict;
|
||||
private readonly int innerCapacity;
|
||||
|
||||
public TwoKeyDictionary() : this(16, 16){}
|
||||
|
||||
public TwoKeyDictionary(int outerCapacity, int innerCapacity){
|
||||
this.dict = new Dictionary<K1, Dictionary<K2, V>>(outerCapacity);
|
||||
this.innerCapacity = innerCapacity;
|
||||
}
|
||||
|
||||
// Properties
|
||||
|
||||
public V this[K1 outerKey, K2 innerKey]{
|
||||
get{ // throws on missing key
|
||||
return dict[outerKey][innerKey];
|
||||
}
|
||||
|
||||
set{
|
||||
Dictionary<K2, V> innerDict;
|
||||
|
||||
if (!dict.TryGetValue(outerKey, out innerDict)){
|
||||
dict.Add(outerKey, innerDict = new Dictionary<K2, V>(innerCapacity));
|
||||
}
|
||||
|
||||
innerDict[innerKey] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Members
|
||||
|
||||
public void Add(K1 outerKey, K2 innerKey, V value){ // throws on duplicate
|
||||
Dictionary<K2, V> innerDict;
|
||||
|
||||
if (!dict.TryGetValue(outerKey, out innerDict)){
|
||||
dict.Add(outerKey, innerDict = new Dictionary<K2, V>(innerCapacity));
|
||||
}
|
||||
|
||||
innerDict.Add(innerKey, value);
|
||||
}
|
||||
|
||||
public void Clear(){
|
||||
this.dict.Clear();
|
||||
}
|
||||
|
||||
public void Clear(K1 outerKey){ // throws on missing key, but keeps the key unlike Remove(K1)
|
||||
dict[outerKey].Clear();
|
||||
}
|
||||
|
||||
public bool Contains(K1 outerKey){
|
||||
return dict.ContainsKey(outerKey);
|
||||
}
|
||||
|
||||
public bool Contains(K1 outerKey, K2 innerKey){
|
||||
Dictionary<K2, V> innerDict;
|
||||
return dict.TryGetValue(outerKey, out innerDict) && innerDict.ContainsKey(innerKey);
|
||||
}
|
||||
|
||||
public int Count(){
|
||||
return dict.Values.Sum(d => d.Count);
|
||||
}
|
||||
|
||||
public int Count(K1 outerKey){ // throws on missing key
|
||||
return dict[outerKey].Count;
|
||||
}
|
||||
|
||||
public bool Remove(K1 outerKey){
|
||||
return dict.Remove(outerKey);
|
||||
}
|
||||
|
||||
public bool Remove(K1 outerKey, K2 innerKey){
|
||||
Dictionary<K2, V> innerDict;
|
||||
|
||||
if (dict.TryGetValue(outerKey, out innerDict) && innerDict.Remove(innerKey)){
|
||||
if (innerDict.Count == 0){
|
||||
dict.Remove(outerKey);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
public bool TryGetValue(K1 outerKey, K2 innerKey, out V value){
|
||||
Dictionary<K2, V> innerDict;
|
||||
|
||||
if (dict.TryGetValue(outerKey, out innerDict)){
|
||||
return innerDict.TryGetValue(innerKey, out value);
|
||||
}
|
||||
else{
|
||||
value = default(V);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
100
Core/Utils/WindowsUtils.cs
Normal file
100
Core/Utils/WindowsUtils.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
using Timer = System.Windows.Forms.Timer;
|
||||
|
||||
namespace TweetDck.Core.Utils{
|
||||
static class WindowsUtils{
|
||||
private static readonly Regex RegexStripHtmlStyles = new Regex(@"\s?(?:style|class)="".*?""");
|
||||
private static readonly Regex RegexOffsetClipboardHtml = new Regex(@"(?<=EndHTML:|EndFragment:)(\d+)");
|
||||
|
||||
public static bool CheckFolderWritePermission(string path){
|
||||
string testFile = Path.Combine(path, ".test");
|
||||
|
||||
try{
|
||||
Directory.CreateDirectory(path);
|
||||
|
||||
using(File.Create(testFile)){}
|
||||
File.Delete(testFile);
|
||||
return true;
|
||||
}catch{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static Process StartProcess(string file, string arguments, bool runElevated){
|
||||
ProcessStartInfo processInfo = new ProcessStartInfo{
|
||||
FileName = file,
|
||||
Arguments = arguments
|
||||
};
|
||||
|
||||
if (runElevated){
|
||||
processInfo.Verb = "runas";
|
||||
}
|
||||
|
||||
return Process.Start(processInfo);
|
||||
}
|
||||
|
||||
public static Timer CreateSingleTickTimer(int timeout){
|
||||
Timer timer = new Timer{
|
||||
Interval = timeout
|
||||
};
|
||||
|
||||
timer.Tick += (sender, args) => timer.Stop();
|
||||
return timer;
|
||||
}
|
||||
|
||||
public static bool TrySleepUntil(Func<bool> test, int timeoutMillis, int timeStepMillis){
|
||||
for(int waited = 0; waited < timeoutMillis; waited += timeStepMillis){
|
||||
if (test()){
|
||||
return true;
|
||||
}
|
||||
|
||||
Thread.Sleep(timeStepMillis);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void ClipboardStripHtmlStyles(){
|
||||
if (!Clipboard.ContainsText(TextDataFormat.Html)){
|
||||
return;
|
||||
}
|
||||
|
||||
string originalText = Clipboard.GetText(TextDataFormat.UnicodeText);
|
||||
string originalHtml = Clipboard.GetText(TextDataFormat.Html);
|
||||
|
||||
string updatedHtml = RegexStripHtmlStyles.Replace(originalHtml, string.Empty);
|
||||
|
||||
int removed = originalHtml.Length-updatedHtml.Length;
|
||||
updatedHtml = RegexOffsetClipboardHtml.Replace(updatedHtml, match => (int.Parse(match.Value)-removed).ToString().PadLeft(match.Value.Length, '0'));
|
||||
|
||||
DataObject obj = new DataObject();
|
||||
obj.SetText(originalText, TextDataFormat.UnicodeText);
|
||||
obj.SetText(updatedHtml, TextDataFormat.Html);
|
||||
SetClipboardData(obj);
|
||||
}
|
||||
|
||||
public static void SetClipboard(string text, TextDataFormat format){
|
||||
if (string.IsNullOrEmpty(text)){
|
||||
return;
|
||||
}
|
||||
|
||||
DataObject obj = new DataObject();
|
||||
obj.SetText(text, format);
|
||||
SetClipboardData(obj);
|
||||
}
|
||||
|
||||
private static void SetClipboardData(DataObject obj){
|
||||
try{
|
||||
Clipboard.SetDataObject(obj);
|
||||
}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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
77
Migration/FormBackgroundWork.Designer.cs
generated
77
Migration/FormBackgroundWork.Designer.cs
generated
@@ -1,77 +0,0 @@
|
||||
using TweetDck.Core.Controls;
|
||||
|
||||
namespace TweetDck.Migration {
|
||||
partial class FormBackgroundWork {
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing) {
|
||||
if (disposing && (components != null)) {
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent() {
|
||||
this.progressBarUseless = new System.Windows.Forms.ProgressBar();
|
||||
this.labelDescription = new System.Windows.Forms.Label();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// progressBarUseless
|
||||
//
|
||||
this.progressBarUseless.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.progressBarUseless.Location = new System.Drawing.Point(15, 52);
|
||||
this.progressBarUseless.MarqueeAnimationSpeed = 10;
|
||||
this.progressBarUseless.Name = "progressBarUseless";
|
||||
this.progressBarUseless.Size = new System.Drawing.Size(477, 23);
|
||||
this.progressBarUseless.Style = System.Windows.Forms.ProgressBarStyle.Marquee;
|
||||
this.progressBarUseless.TabIndex = 0;
|
||||
//
|
||||
// labelDescription
|
||||
//
|
||||
this.labelDescription.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.labelDescription.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
||||
this.labelDescription.Location = new System.Drawing.Point(12, 12);
|
||||
this.labelDescription.Name = "labelDescription";
|
||||
this.labelDescription.Size = new System.Drawing.Size(480, 37);
|
||||
this.labelDescription.TabIndex = 1;
|
||||
this.labelDescription.Text = "Please, watch this informationless progress bar showcase while some magic happens" +
|
||||
" in the background...";
|
||||
//
|
||||
// FormBackgroundWork
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(504, 87);
|
||||
this.Controls.Add(this.labelDescription);
|
||||
this.Controls.Add(this.progressBarUseless);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
||||
this.Name = "FormBackgroundWork";
|
||||
this.ShowIcon = false;
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.Text = "TweetDeck Migration";
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.ProgressBar progressBarUseless;
|
||||
private System.Windows.Forms.Label labelDescription;
|
||||
}
|
||||
}
|
@@ -1,15 +0,0 @@
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace TweetDck.Migration{
|
||||
partial class FormBackgroundWork : Form{
|
||||
public FormBackgroundWork(){
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public void ShowWorkDialog(Action onBegin){
|
||||
Shown += (sender, args) => onBegin();
|
||||
ShowDialog();
|
||||
}
|
||||
}
|
||||
}
|
148
Migration/FormMigrationQuestion.Designer.cs
generated
148
Migration/FormMigrationQuestion.Designer.cs
generated
@@ -1,148 +0,0 @@
|
||||
using TweetDck.Core.Controls;
|
||||
|
||||
namespace TweetDck.Migration {
|
||||
partial class FormMigrationQuestion {
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing) {
|
||||
if (disposing && (components != null)) {
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent() {
|
||||
this.btnIgnore = new System.Windows.Forms.Button();
|
||||
this.panelButtons = new System.Windows.Forms.FlowLayoutPanel();
|
||||
this.btnAskLater = new System.Windows.Forms.Button();
|
||||
this.btnMigrate = new System.Windows.Forms.Button();
|
||||
this.btnMigrateUninstall = new System.Windows.Forms.Button();
|
||||
this.labelQuestion = new System.Windows.Forms.Label();
|
||||
this.panelButtons.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// btnIgnore
|
||||
//
|
||||
this.btnIgnore.AutoSize = true;
|
||||
this.btnIgnore.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
|
||||
this.btnIgnore.Location = new System.Drawing.Point(391, 0);
|
||||
this.btnIgnore.Margin = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||
this.btnIgnore.Name = "btnIgnore";
|
||||
this.btnIgnore.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||
this.btnIgnore.Size = new System.Drawing.Size(53, 23);
|
||||
this.btnIgnore.TabIndex = 1;
|
||||
this.btnIgnore.Text = "Ignore";
|
||||
this.btnIgnore.UseVisualStyleBackColor = true;
|
||||
this.btnIgnore.Click += new System.EventHandler(this.btnIgnore_Click);
|
||||
//
|
||||
// panelButtons
|
||||
//
|
||||
this.panelButtons.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.panelButtons.Controls.Add(this.btnAskLater);
|
||||
this.panelButtons.Controls.Add(this.btnIgnore);
|
||||
this.panelButtons.Controls.Add(this.btnMigrate);
|
||||
this.panelButtons.Controls.Add(this.btnMigrateUninstall);
|
||||
this.panelButtons.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft;
|
||||
this.panelButtons.Location = new System.Drawing.Point(12, 67);
|
||||
this.panelButtons.Name = "panelButtons";
|
||||
this.panelButtons.Size = new System.Drawing.Size(518, 23);
|
||||
this.panelButtons.TabIndex = 0;
|
||||
//
|
||||
// btnAskLater
|
||||
//
|
||||
this.btnAskLater.AutoSize = true;
|
||||
this.btnAskLater.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
|
||||
this.btnAskLater.Location = new System.Drawing.Point(450, 0);
|
||||
this.btnAskLater.Margin = new System.Windows.Forms.Padding(3, 0, 0, 0);
|
||||
this.btnAskLater.Name = "btnAskLater";
|
||||
this.btnAskLater.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||
this.btnAskLater.Size = new System.Drawing.Size(68, 23);
|
||||
this.btnAskLater.TabIndex = 0;
|
||||
this.btnAskLater.Text = "Ask Later";
|
||||
this.btnAskLater.UseVisualStyleBackColor = true;
|
||||
this.btnAskLater.Click += new System.EventHandler(this.btnAskLater_Click);
|
||||
//
|
||||
// btnMigrate
|
||||
//
|
||||
this.btnMigrate.AutoSize = true;
|
||||
this.btnMigrate.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
|
||||
this.btnMigrate.Location = new System.Drawing.Point(327, 0);
|
||||
this.btnMigrate.Margin = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||
this.btnMigrate.Name = "btnMigrate";
|
||||
this.btnMigrate.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||
this.btnMigrate.Size = new System.Drawing.Size(58, 23);
|
||||
this.btnMigrate.TabIndex = 3;
|
||||
this.btnMigrate.Text = "Migrate";
|
||||
this.btnMigrate.UseVisualStyleBackColor = true;
|
||||
this.btnMigrate.Click += new System.EventHandler(this.btnMigrate_Click);
|
||||
//
|
||||
// btnMigrateUninstall
|
||||
//
|
||||
this.btnMigrateUninstall.AutoSize = true;
|
||||
this.btnMigrateUninstall.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
|
||||
this.btnMigrateUninstall.Location = new System.Drawing.Point(223, 0);
|
||||
this.btnMigrateUninstall.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
|
||||
this.btnMigrateUninstall.Name = "btnMigrateUninstall";
|
||||
this.btnMigrateUninstall.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||
this.btnMigrateUninstall.Size = new System.Drawing.Size(98, 23);
|
||||
this.btnMigrateUninstall.TabIndex = 4;
|
||||
this.btnMigrateUninstall.Text = "Migrate && Purge";
|
||||
this.btnMigrateUninstall.UseVisualStyleBackColor = true;
|
||||
this.btnMigrateUninstall.Click += new System.EventHandler(this.btnMigrateUninstall_Click);
|
||||
//
|
||||
// labelQuestion
|
||||
//
|
||||
this.labelQuestion.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.labelQuestion.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
||||
this.labelQuestion.Location = new System.Drawing.Point(49, 9);
|
||||
this.labelQuestion.Margin = new System.Windows.Forms.Padding(40, 3, 3, 3);
|
||||
this.labelQuestion.Name = "labelQuestion";
|
||||
this.labelQuestion.Size = new System.Drawing.Size(481, 52);
|
||||
this.labelQuestion.TabIndex = 2;
|
||||
//
|
||||
// FormMigrationQuestion
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(542, 102);
|
||||
this.Controls.Add(this.labelQuestion);
|
||||
this.Controls.Add(this.panelButtons);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
||||
this.MaximizeBox = false;
|
||||
this.MinimizeBox = false;
|
||||
this.Name = "FormMigrationQuestion";
|
||||
this.ShowIcon = false;
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.Text = "TweetDeck Migration";
|
||||
this.panelButtons.ResumeLayout(false);
|
||||
this.panelButtons.PerformLayout();
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.Button btnIgnore;
|
||||
private System.Windows.Forms.FlowLayoutPanel panelButtons;
|
||||
private System.Windows.Forms.Button btnMigrate;
|
||||
private System.Windows.Forms.Label labelQuestion;
|
||||
private System.Windows.Forms.Button btnAskLater;
|
||||
private System.Windows.Forms.Button btnMigrateUninstall;
|
||||
}
|
||||
}
|
@@ -1,42 +0,0 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace TweetDck.Migration{
|
||||
partial class FormMigrationQuestion : Form{
|
||||
public MigrationDecision Decision { get; private set; }
|
||||
|
||||
public FormMigrationQuestion(){
|
||||
InitializeComponent();
|
||||
|
||||
labelQuestion.Text = "Hey there, I found some TweetDeck data! Do you want to »Migrate« it and delete the old data folder, »Ignore« the request forever, or try "+Program.BrandName+" out first?\r\nYou may also »Migrate && Purge« which uninstalls TweetDeck too!";
|
||||
}
|
||||
|
||||
protected override void OnPaint(PaintEventArgs e){
|
||||
e.Graphics.DrawIcon(SystemIcons.Question,10,10);
|
||||
base.OnPaint(e);
|
||||
}
|
||||
|
||||
private void btnMigrateUninstall_Click(object sender, EventArgs e){
|
||||
Close(MigrationDecision.MigratePurge);
|
||||
}
|
||||
|
||||
private void btnMigrate_Click(object sender, EventArgs e){
|
||||
Close(MigrationDecision.Migrate);
|
||||
}
|
||||
|
||||
private void btnIgnore_Click(object sender, EventArgs e){
|
||||
Close(MigrationDecision.Ignore);
|
||||
}
|
||||
|
||||
private void btnAskLater_Click(object sender, EventArgs e){
|
||||
Close(MigrationDecision.AskLater);
|
||||
}
|
||||
|
||||
private void Close(MigrationDecision decision){
|
||||
Decision = decision;
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,42 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Shell32;
|
||||
|
||||
namespace TweetDck.Migration.Helpers{
|
||||
sealed class LnkEditor{
|
||||
private readonly ShellLinkObject obj;
|
||||
|
||||
public LnkEditor(string file){
|
||||
try{
|
||||
Shell shell = new Shell();
|
||||
Folder folder = shell.NameSpace(Path.GetDirectoryName(file));
|
||||
FolderItem item = folder.Items().Item(Path.GetFileName(file));
|
||||
|
||||
obj = item.GetLink as ShellLinkObject;
|
||||
}catch(Exception){
|
||||
obj = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetComment(string newComment){
|
||||
if (obj == null)return;
|
||||
obj.Description = newComment;
|
||||
}
|
||||
|
||||
public void SetPath(string newPath){
|
||||
if (obj == null)return;
|
||||
obj.Path = newPath;
|
||||
obj.SetIconLocation(newPath,0);
|
||||
}
|
||||
|
||||
public void SetWorkingDirectory(string newWorkingDirectory){
|
||||
if (obj == null)return;
|
||||
obj.WorkingDirectory = newWorkingDirectory;
|
||||
}
|
||||
|
||||
public void Save(){
|
||||
if (obj == null)return;
|
||||
obj.Save();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,43 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace TweetDck.Migration.Helpers{
|
||||
static class ProgramRegistrySearch{
|
||||
public static string FindByDisplayName(string displayName){
|
||||
Predicate<RegistryKey> predicate = key => displayName.Equals(key.GetValue("DisplayName") as string,StringComparison.OrdinalIgnoreCase);
|
||||
string guid;
|
||||
|
||||
return FindMatchingSubKey(Registry.LocalMachine,@"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall",predicate,out guid) ||
|
||||
FindMatchingSubKey(Registry.LocalMachine,@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",predicate,out guid) ||
|
||||
FindMatchingSubKey(Registry.CurrentUser,@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",predicate,out guid)
|
||||
? guid : null;
|
||||
}
|
||||
|
||||
private static bool FindMatchingSubKey(RegistryKey keyHandle, string path, Predicate<RegistryKey> predicate, out string guid){
|
||||
string outputId = null;
|
||||
|
||||
try{
|
||||
RegistryKey parentKey = keyHandle.OpenSubKey(path,false);
|
||||
if (parentKey == null)throw new InvalidOperationException();
|
||||
|
||||
foreach(RegistryKey subKey in parentKey.GetSubKeyNames().Select(subName => parentKey.OpenSubKey(subName,false)).Where(subKey => subKey != null)){
|
||||
if (predicate(subKey)){
|
||||
outputId = subKey.Name.Substring(subKey.Name.LastIndexOf('\\')+1);
|
||||
subKey.Close();
|
||||
break;
|
||||
}
|
||||
|
||||
subKey.Close();
|
||||
}
|
||||
|
||||
parentKey.Close();
|
||||
}catch(Exception){
|
||||
guid = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return (guid = outputId) != null;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,23 +0,0 @@
|
||||
namespace TweetDck.Migration{
|
||||
enum MigrationDecision{
|
||||
/// <summary>
|
||||
/// Copies the important files and then deletes the TweetDeck folder.
|
||||
/// </summary>
|
||||
Migrate,
|
||||
|
||||
/// <summary>
|
||||
/// Does exactly what <see cref="Migrate"/> does, but also silently uninstalls TweetDeck.
|
||||
/// </summary>
|
||||
MigratePurge,
|
||||
|
||||
/// <summary>
|
||||
/// Does not copy any files and does not ask the user about data migration again.
|
||||
/// </summary>
|
||||
Ignore,
|
||||
|
||||
/// <summary>
|
||||
/// Does not copy any files but asks the user again when the program is re-ran.
|
||||
/// </summary>
|
||||
AskLater
|
||||
}
|
||||
}
|
@@ -1,209 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Microsoft.Win32;
|
||||
using TweetDck.Migration.Helpers;
|
||||
using TweetDck.Core.Utils;
|
||||
|
||||
namespace TweetDck.Migration{
|
||||
static class MigrationManager{
|
||||
private static readonly string TweetDeckPathParent = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"twitter");
|
||||
private static readonly string TweetDeckPath = Path.Combine(TweetDeckPathParent,"TweetDeck");
|
||||
|
||||
public static void Run(){
|
||||
if (!Program.UserConfig.IgnoreMigration && Directory.Exists(TweetDeckPath)){
|
||||
MigrationDecision decision;
|
||||
|
||||
using(FormMigrationQuestion formQuestion = new FormMigrationQuestion()){
|
||||
decision = formQuestion.ShowDialog() == DialogResult.OK ? formQuestion.Decision : MigrationDecision.AskLater;
|
||||
}
|
||||
|
||||
switch(decision){
|
||||
case MigrationDecision.MigratePurge:
|
||||
case MigrationDecision.Migrate:
|
||||
FormBackgroundWork formWait = new FormBackgroundWork();
|
||||
|
||||
formWait.ShowWorkDialog(() => {
|
||||
if (!BeginMigration(decision,ex => formWait.Invoke(new Action(() => {
|
||||
formWait.Close();
|
||||
|
||||
if (ex != null){
|
||||
Program.HandleException("An unexpected exception has occurred during the migration process.",ex);
|
||||
return;
|
||||
}
|
||||
|
||||
Program.UserConfig.IgnoreMigration = true;
|
||||
Program.UserConfig.Save();
|
||||
})))){
|
||||
formWait.Close();
|
||||
}
|
||||
});
|
||||
|
||||
break;
|
||||
|
||||
case MigrationDecision.Ignore:
|
||||
Program.UserConfig.IgnoreMigration = true;
|
||||
Program.UserConfig.Save();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (!Program.UserConfig.IgnoreUninstallCheck){
|
||||
string guid = ProgramRegistrySearch.FindByDisplayName("TweetDeck");
|
||||
|
||||
if (guid != null && MessageBox.Show("TweetDeck is still installed on your computer, do you want to uninstall it?","Uninstall TweetDeck",MessageBoxButtons.YesNo,MessageBoxIcon.Question,MessageBoxDefaultButton.Button2) == DialogResult.Yes){
|
||||
RunUninstaller(guid,0);
|
||||
CleanupTweetDeck();
|
||||
}
|
||||
|
||||
Program.UserConfig.IgnoreUninstallCheck = true;
|
||||
Program.UserConfig.Save();
|
||||
}
|
||||
}
|
||||
|
||||
private static bool BeginMigration(MigrationDecision decision, Action<Exception> onFinished){
|
||||
if (decision != MigrationDecision.MigratePurge && decision != MigrationDecision.Migrate){
|
||||
return false;
|
||||
}
|
||||
|
||||
Task task = new Task(() => {
|
||||
Directory.CreateDirectory(Program.StoragePath);
|
||||
Directory.CreateDirectory(Path.Combine(Program.StoragePath,"localStorage"));
|
||||
Directory.CreateDirectory(Path.Combine(Program.StoragePath,"Local Storage"));
|
||||
|
||||
CopyFile("Cookies");
|
||||
CopyFile("Cookies-journal");
|
||||
CopyFile("localStorage"+Path.DirectorySeparatorChar+"qrc__0.localstorage");
|
||||
CopyFile("Local Storage"+Path.DirectorySeparatorChar+"https_tweetdeck.twitter.com_0.localstorage");
|
||||
CopyFile("Local Storage"+Path.DirectorySeparatorChar+"https_tweetdeck.twitter.com_0.localstorage-journal");
|
||||
|
||||
if (decision == MigrationDecision.Migrate || decision == MigrationDecision.MigratePurge){
|
||||
// kill process if running
|
||||
Process runningProcess = null;
|
||||
|
||||
try{
|
||||
runningProcess = Process.GetProcessesByName("TweetDeck").FirstOrDefault(process => process.MainWindowHandle != IntPtr.Zero);
|
||||
}catch(Exception){
|
||||
// process not found
|
||||
}
|
||||
|
||||
if (runningProcess != null){
|
||||
runningProcess.CloseMainWindow();
|
||||
|
||||
for(int wait = 0; wait < 100 && !runningProcess.HasExited; wait++){ // 10 seconds
|
||||
runningProcess.Refresh();
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
|
||||
runningProcess.Close();
|
||||
}
|
||||
|
||||
// delete folders
|
||||
for(int wait = 0; wait < 50; wait++){
|
||||
try{
|
||||
Directory.Delete(TweetDeckPath,true);
|
||||
break;
|
||||
}catch(Exception){
|
||||
// browser subprocess not ended yet, wait
|
||||
Thread.Sleep(300);
|
||||
}
|
||||
}
|
||||
|
||||
try{
|
||||
Directory.Delete(TweetDeckPathParent,false);
|
||||
}catch(IOException){
|
||||
// most likely not empty, ignore
|
||||
}
|
||||
}
|
||||
|
||||
if (decision == MigrationDecision.MigratePurge){
|
||||
// update the lnk files wherever possible (desktop icons, pinned taskbar, start menu)
|
||||
foreach(string location in GetLnkDirectories()){
|
||||
if (string.IsNullOrEmpty(location))continue;
|
||||
|
||||
string linkFile = Path.Combine(location,"TweetDeck.lnk");
|
||||
|
||||
if (File.Exists(linkFile)){
|
||||
LnkEditor lnk = new LnkEditor(linkFile);
|
||||
lnk.SetPath(Application.ExecutablePath);
|
||||
lnk.SetWorkingDirectory(Environment.CurrentDirectory);
|
||||
lnk.SetComment(Program.BrandName+" client for Windows");
|
||||
lnk.Save();
|
||||
|
||||
string renamed = Path.Combine(location,Program.BrandName+".lnk");
|
||||
|
||||
try{
|
||||
if (!File.Exists(renamed)){
|
||||
File.Move(linkFile,renamed);
|
||||
}
|
||||
else{
|
||||
File.Delete(linkFile);
|
||||
}
|
||||
}catch{
|
||||
// eh, too bad
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NativeMethods.SHChangeNotify(0x8000000,0x1000,IntPtr.Zero,IntPtr.Zero); // refreshes desktop
|
||||
|
||||
// uninstall in the background
|
||||
string guid = ProgramRegistrySearch.FindByDisplayName("TweetDeck");
|
||||
|
||||
if (guid != null){
|
||||
RunUninstaller(guid,5000);
|
||||
}
|
||||
|
||||
// registry cleanup
|
||||
CleanupTweetDeck();
|
||||
|
||||
// migration finished like a boss
|
||||
}
|
||||
});
|
||||
|
||||
task.ContinueWith(originalTask => onFinished(originalTask.Exception),TaskContinuationOptions.ExecuteSynchronously);
|
||||
task.Start();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void CopyFile(string relativePath){
|
||||
try{
|
||||
File.Copy(Path.Combine(TweetDeckPath,relativePath),Path.Combine(Program.StoragePath,relativePath),true);
|
||||
}catch(FileNotFoundException){
|
||||
}catch(DirectoryNotFoundException){
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<string> GetLnkDirectories(){
|
||||
yield return Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
|
||||
yield return Environment.GetFolderPath(Environment.SpecialFolder.CommonDesktopDirectory);
|
||||
yield return Environment.ExpandEnvironmentVariables(@"%APPDATA%\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar");
|
||||
}
|
||||
|
||||
private static void RunUninstaller(string guid, int timeout){
|
||||
Process uninstaller = Process.Start("msiexec.exe","/x "+guid+" /quiet /qn");
|
||||
|
||||
if (uninstaller != null){
|
||||
if (timeout > 0){
|
||||
uninstaller.WaitForExit(timeout); // it appears that the process is restarted or something that triggers this, but it shouldn't be a problem
|
||||
}
|
||||
|
||||
uninstaller.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private static void CleanupTweetDeck(){
|
||||
try{
|
||||
Registry.CurrentUser.DeleteSubKeyTree(@"Software\Twitter\TweetDeck",true);
|
||||
Registry.CurrentUser.DeleteSubKey(@"Software\Twitter"); // only if empty
|
||||
}catch(Exception){
|
||||
// not found or too bad
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
22
Plugins/Controls/PluginControl.Designer.cs
generated
22
Plugins/Controls/PluginControl.Designer.cs
generated
@@ -31,6 +31,7 @@
|
||||
this.flowLayoutInfo = new System.Windows.Forms.FlowLayoutPanel();
|
||||
this.labelWebsite = new System.Windows.Forms.Label();
|
||||
this.labelVersion = new System.Windows.Forms.Label();
|
||||
this.btnOpenConfig = new System.Windows.Forms.Button();
|
||||
this.panelDescription.SuspendLayout();
|
||||
this.flowLayoutInfo.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
@@ -38,9 +39,9 @@
|
||||
// btnToggleState
|
||||
//
|
||||
this.btnToggleState.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnToggleState.Location = new System.Drawing.Point(449, 80);
|
||||
this.btnToggleState.Location = new System.Drawing.Point(459, 80);
|
||||
this.btnToggleState.Name = "btnToggleState";
|
||||
this.btnToggleState.Size = new System.Drawing.Size(75, 23);
|
||||
this.btnToggleState.Size = new System.Drawing.Size(65, 23);
|
||||
this.btnToggleState.TabIndex = 0;
|
||||
this.btnToggleState.Text = "Disable";
|
||||
this.btnToggleState.UseVisualStyleBackColor = true;
|
||||
@@ -99,7 +100,7 @@
|
||||
this.flowLayoutInfo.Controls.Add(this.labelWebsite);
|
||||
this.flowLayoutInfo.Location = new System.Drawing.Point(11, 85);
|
||||
this.flowLayoutInfo.Name = "flowLayoutInfo";
|
||||
this.flowLayoutInfo.Size = new System.Drawing.Size(432, 18);
|
||||
this.flowLayoutInfo.Size = new System.Drawing.Size(368, 18);
|
||||
this.flowLayoutInfo.TabIndex = 4;
|
||||
this.flowLayoutInfo.WrapContents = false;
|
||||
//
|
||||
@@ -128,17 +129,29 @@
|
||||
this.labelVersion.Text = "Version";
|
||||
this.labelVersion.TextAlign = System.Drawing.ContentAlignment.TopRight;
|
||||
//
|
||||
// btnOpenConfig
|
||||
//
|
||||
this.btnOpenConfig.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnOpenConfig.Location = new System.Drawing.Point(385, 80);
|
||||
this.btnOpenConfig.Name = "btnOpenConfig";
|
||||
this.btnOpenConfig.Size = new System.Drawing.Size(68, 23);
|
||||
this.btnOpenConfig.TabIndex = 6;
|
||||
this.btnOpenConfig.Text = "Configure";
|
||||
this.btnOpenConfig.UseVisualStyleBackColor = true;
|
||||
this.btnOpenConfig.Click += new System.EventHandler(this.btnOpenConfig_Click);
|
||||
//
|
||||
// PluginControl
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.Controls.Add(this.btnOpenConfig);
|
||||
this.Controls.Add(this.flowLayoutInfo);
|
||||
this.Controls.Add(this.panelDescription);
|
||||
this.Controls.Add(this.labelName);
|
||||
this.Controls.Add(this.btnToggleState);
|
||||
this.Controls.Add(this.labelVersion);
|
||||
this.MaximumSize = new System.Drawing.Size(65535, 109);
|
||||
this.MinimumSize = new System.Drawing.Size(0, 83);
|
||||
this.MinimumSize = new System.Drawing.Size(0, 61);
|
||||
this.Name = "PluginControl";
|
||||
this.Padding = new System.Windows.Forms.Padding(3);
|
||||
this.Size = new System.Drawing.Size(530, 109);
|
||||
@@ -161,5 +174,6 @@
|
||||
private System.Windows.Forms.FlowLayoutPanel flowLayoutInfo;
|
||||
private System.Windows.Forms.Label labelWebsite;
|
||||
private System.Windows.Forms.Label labelVersion;
|
||||
private System.Windows.Forms.Button btnOpenConfig;
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
using TweetDck.Core.Utils;
|
||||
|
||||
@@ -21,19 +23,24 @@ namespace TweetDck.Plugins.Controls{
|
||||
this.labelVersion.Text = plugin.Version;
|
||||
this.labelAuthor.Text = plugin.Author;
|
||||
this.labelWebsite.Text = plugin.Website;
|
||||
this.btnToggleState.Text = pluginManager.Config.IsEnabled(plugin) ? "Disable" : "Enable";
|
||||
|
||||
if (!plugin.CanRun){
|
||||
this.labelName.ForeColor = Color.DarkRed;
|
||||
this.btnToggleState.Enabled = false;
|
||||
UpdatePluginState();
|
||||
|
||||
if (labelDescription.Text.Length == 0){
|
||||
labelDescription.Visible = false;
|
||||
}
|
||||
|
||||
panelDescription_Resize(panelDescription,new EventArgs());
|
||||
panelDescription_Resize(panelDescription, new EventArgs());
|
||||
}
|
||||
|
||||
private void panelDescription_Resize(object sender, EventArgs e){
|
||||
labelDescription.MaximumSize = new Size(panelDescription.Width-SystemInformation.VerticalScrollBarWidth,0);
|
||||
Height = Math.Min(MinimumSize.Height+(labelDescription.Height-13),MaximumSize.Height);
|
||||
if (labelDescription.Text.Length == 0){
|
||||
Height = MinimumSize.Height;
|
||||
}
|
||||
else{
|
||||
labelDescription.MaximumSize = new Size(panelDescription.Width-SystemInformation.VerticalScrollBarWidth, 0);
|
||||
Height = Math.Min(MinimumSize.Height+9+labelDescription.Height, MaximumSize.Height);
|
||||
}
|
||||
}
|
||||
|
||||
private void labelWebsite_Click(object sender, EventArgs e){
|
||||
@@ -42,11 +49,38 @@ namespace TweetDck.Plugins.Controls{
|
||||
}
|
||||
}
|
||||
|
||||
private void btnOpenConfig_Click(object sender, EventArgs e){
|
||||
using(Process.Start("explorer.exe", "/select,\""+plugin.ConfigPath.Replace('/', '\\')+"\"")){}
|
||||
}
|
||||
|
||||
private void btnToggleState_Click(object sender, EventArgs e){
|
||||
bool newState = !pluginManager.Config.IsEnabled(plugin);
|
||||
pluginManager.Config.SetEnabled(plugin,newState);
|
||||
pluginManager.Config.SetEnabled(plugin, newState);
|
||||
|
||||
btnToggleState.Text = newState ? "Disable" : "Enable";
|
||||
UpdatePluginState();
|
||||
}
|
||||
|
||||
private void UpdatePluginState(){
|
||||
bool isEnabled = plugin.CanRun && pluginManager.Config.IsEnabled(plugin);
|
||||
Color textColor = isEnabled ? Color.Black : Color.FromArgb(90, 90, 90);
|
||||
|
||||
labelVersion.ForeColor = textColor;
|
||||
labelAuthor.ForeColor = textColor;
|
||||
labelWebsite.ForeColor = isEnabled ? Color.Blue : Color.FromArgb(90, 90, 249);
|
||||
|
||||
if (plugin.CanRun){
|
||||
labelName.ForeColor = textColor;
|
||||
labelDescription.ForeColor = textColor;
|
||||
btnToggleState.Text = isEnabled ? "Disable" : "Enable";
|
||||
btnOpenConfig.Visible = plugin.HasConfig;
|
||||
btnOpenConfig.Enabled = btnOpenConfig.Visible && File.Exists(plugin.ConfigPath);
|
||||
}
|
||||
else{
|
||||
labelName.ForeColor = Color.DarkRed;
|
||||
labelDescription.ForeColor = Color.DarkRed;
|
||||
btnToggleState.Visible = false;
|
||||
btnOpenConfig.Visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,8 +3,13 @@ using TweetDck.Core.Utils;
|
||||
|
||||
namespace TweetDck.Plugins.Controls{
|
||||
sealed partial class PluginListFlowLayout : FlowLayoutPanel{
|
||||
public PluginListFlowLayout(){
|
||||
FlowDirection = FlowDirection.TopDown;
|
||||
WrapContents = false;
|
||||
}
|
||||
|
||||
protected override void WndProc(ref Message m){
|
||||
NativeMethods.ShowScrollBar(Handle,NativeMethods.SB_HORZ,false); // basically fuck the horizontal scrollbar very much
|
||||
NativeMethods.ShowScrollBar(Handle, NativeMethods.SB_HORZ, false); // basically fuck the horizontal scrollbar very much
|
||||
base.WndProc(ref m);
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TweetDck.Plugins{
|
||||
namespace TweetDck.Plugins.Enums{
|
||||
[Flags]
|
||||
enum PluginEnvironment{
|
||||
None = 0,
|
||||
@@ -27,8 +27,8 @@ namespace TweetDck.Plugins{
|
||||
|
||||
public static string GetScriptVariables(this PluginEnvironment environment){
|
||||
switch(environment){
|
||||
case PluginEnvironment.Browser: return "$,$TD,TD";
|
||||
case PluginEnvironment.Notification: return "$TD";
|
||||
case PluginEnvironment.Browser: return "$,$TD,$TDP,TD";
|
||||
case PluginEnvironment.Notification: return "$TD,$TDP";
|
||||
default: return string.Empty;
|
||||
}
|
||||
}
|
5
Plugins/Enums/PluginFolder.cs
Normal file
5
Plugins/Enums/PluginFolder.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
namespace TweetDck.Plugins.Enums{
|
||||
enum PluginFolder{
|
||||
Root, Data
|
||||
}
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
namespace TweetDck.Plugins{
|
||||
namespace TweetDck.Plugins.Enums{
|
||||
enum PluginGroup{
|
||||
Official, Custom
|
||||
}
|
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using TweetDck.Plugins.Enums;
|
||||
|
||||
namespace TweetDck.Plugins{
|
||||
class Plugin{
|
||||
@@ -12,6 +13,8 @@ namespace TweetDck.Plugins{
|
||||
public string Author { get { return metadata["AUTHOR"]; } }
|
||||
public string Version { get { return metadata["VERSION"]; } }
|
||||
public string Website { get { return metadata["WEBSITE"]; } }
|
||||
public string ConfigFile { get { return metadata["CONFIGFILE"]; } }
|
||||
public string ConfigDefault { get { return metadata["CONFIGDEFAULT"]; } }
|
||||
public string RequiredVersion { get { return metadata["REQUIRES"]; } }
|
||||
public PluginGroup Group { get; private set; }
|
||||
public PluginEnvironment Environments { get; private set; }
|
||||
@@ -22,36 +25,130 @@ namespace TweetDck.Plugins{
|
||||
}
|
||||
}
|
||||
|
||||
private readonly string path;
|
||||
public bool HasConfig{
|
||||
get{
|
||||
return ConfigFile.Length > 0 && GetFullPathIfSafe(PluginFolder.Data, ConfigFile).Length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
public string ConfigPath{
|
||||
get{
|
||||
return HasConfig ? Path.Combine(GetPluginFolder(PluginFolder.Data), ConfigFile) : string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasDefaultConfig{
|
||||
get{
|
||||
return ConfigDefault.Length > 0 && GetFullPathIfSafe(PluginFolder.Root, ConfigDefault).Length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
public string DefaultConfigPath{
|
||||
get{
|
||||
return HasDefaultConfig ? Path.Combine(GetPluginFolder(PluginFolder.Root), ConfigDefault) : string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly string pathRoot;
|
||||
private readonly string pathData;
|
||||
private readonly string identifier;
|
||||
private readonly Dictionary<string,string> metadata = new Dictionary<string,string>(4){
|
||||
private readonly Dictionary<string, string> metadata = new Dictionary<string, string>(4){
|
||||
{ "NAME", "" },
|
||||
{ "DESCRIPTION", "" },
|
||||
{ "AUTHOR", "(anonymous)" },
|
||||
{ "VERSION", "(unknown)" },
|
||||
{ "WEBSITE", "" },
|
||||
{ "CONFIGFILE", "" },
|
||||
{ "CONFIGDEFAULT", "" },
|
||||
{ "REQUIRES", "*" }
|
||||
};
|
||||
|
||||
private bool? canRun;
|
||||
|
||||
private Plugin(string path, PluginGroup group){
|
||||
this.path = path;
|
||||
this.identifier = group.GetIdentifierPrefix()+Path.GetFileName(path);
|
||||
string name = Path.GetFileName(path);
|
||||
System.Diagnostics.Debug.Assert(name != null);
|
||||
|
||||
this.pathRoot = path;
|
||||
this.pathData = Path.Combine(Program.PluginDataPath, group.GetIdentifierPrefix(), name);
|
||||
|
||||
this.identifier = group.GetIdentifierPrefix()+name;
|
||||
this.Group = group;
|
||||
this.Environments = PluginEnvironment.None;
|
||||
}
|
||||
|
||||
private void OnMetadataLoaded(){
|
||||
string configPath = ConfigPath, defaultConfigPath = DefaultConfigPath;
|
||||
|
||||
if (configPath.Length > 0 && defaultConfigPath.Length > 0 && !File.Exists(configPath) && File.Exists(defaultConfigPath)){
|
||||
string dataFolder = GetPluginFolder(PluginFolder.Data);
|
||||
|
||||
if (!Directory.Exists(dataFolder)){ // config migration
|
||||
string originalFile = Path.Combine(GetPluginFolder(PluginFolder.Root), ConfigFile);
|
||||
|
||||
if (File.Exists(originalFile)){
|
||||
try{
|
||||
Directory.CreateDirectory(dataFolder);
|
||||
File.Copy(originalFile, configPath, false);
|
||||
File.Delete(originalFile); // will fail without write perms in program folder, ignore if so
|
||||
}catch{
|
||||
// ignore
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try{
|
||||
Directory.CreateDirectory(dataFolder);
|
||||
File.Copy(defaultConfigPath, configPath, false);
|
||||
}catch(Exception e){
|
||||
Program.Reporter.HandleException("Plugin Loading Error", "Could not generate a configuration file for '"+identifier+"' plugin.", true, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string GetScriptPath(PluginEnvironment environment){
|
||||
if (Environments.HasFlag(environment)){
|
||||
string file = environment.GetScriptFile();
|
||||
return file != null ? Path.Combine(path,file) : string.Empty;
|
||||
return file != null ? Path.Combine(pathRoot, file) : string.Empty;
|
||||
}
|
||||
else{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public string GetPluginFolder(PluginFolder folder){
|
||||
switch(folder){
|
||||
case PluginFolder.Root: return pathRoot;
|
||||
case PluginFolder.Data: return pathData;
|
||||
default: return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public string GetFullPathIfSafe(PluginFolder folder, string relativePath){
|
||||
string rootFolder = GetPluginFolder(folder);
|
||||
string fullPath = Path.Combine(rootFolder, relativePath);
|
||||
|
||||
try{
|
||||
string folderPathName = new DirectoryInfo(rootFolder).FullName;
|
||||
DirectoryInfo currentInfo = new DirectoryInfo(fullPath);
|
||||
|
||||
while(currentInfo.Parent != null){
|
||||
if (currentInfo.Parent.FullName == folderPathName){
|
||||
return fullPath;
|
||||
}
|
||||
|
||||
currentInfo = currentInfo.Parent;
|
||||
}
|
||||
}
|
||||
catch{
|
||||
// ignore
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public override string ToString(){
|
||||
return Identifier;
|
||||
}
|
||||
@@ -62,17 +159,17 @@ namespace TweetDck.Plugins{
|
||||
|
||||
public override bool Equals(object obj){
|
||||
Plugin plugin = obj as Plugin;
|
||||
return plugin != null && plugin.path.Equals(path);
|
||||
return plugin != null && plugin.identifier.Equals(identifier);
|
||||
}
|
||||
|
||||
public static Plugin CreateFromFolder(string path, PluginGroup group, out string error){
|
||||
Plugin plugin = new Plugin(path,group);
|
||||
Plugin plugin = new Plugin(path, group);
|
||||
|
||||
if (!LoadMetadata(path,plugin,out error)){
|
||||
if (!LoadMetadata(path, plugin, out error)){
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!LoadEnvironments(path,plugin,out error)){
|
||||
if (!LoadEnvironments(path, plugin, out error)){
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -81,16 +178,8 @@ namespace TweetDck.Plugins{
|
||||
}
|
||||
|
||||
private static bool LoadEnvironments(string path, Plugin plugin, out string error){
|
||||
foreach(string file in Directory.EnumerateFiles(path,"*.js",SearchOption.TopDirectoryOnly).Select(Path.GetFileName)){
|
||||
PluginEnvironment environment = PluginEnvironmentExtensions.Values.FirstOrDefault(env => file.Equals(env.GetScriptFile(),StringComparison.Ordinal));
|
||||
|
||||
if (environment != PluginEnvironment.None){
|
||||
plugin.Environments |= environment;
|
||||
}
|
||||
else{
|
||||
error = "Unknown script file: "+file;
|
||||
return false;
|
||||
}
|
||||
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));
|
||||
}
|
||||
|
||||
if (plugin.Environments == PluginEnvironment.None){
|
||||
@@ -105,14 +194,14 @@ namespace TweetDck.Plugins{
|
||||
private static readonly string[] endTag = { "[END]" };
|
||||
|
||||
private static bool LoadMetadata(string path, Plugin plugin, out string error){
|
||||
string metaFile = Path.Combine(path,".meta");
|
||||
string metaFile = Path.Combine(path, ".meta");
|
||||
|
||||
if (!File.Exists(metaFile)){
|
||||
error = "Missing .meta file.";
|
||||
return false;
|
||||
}
|
||||
|
||||
string[] lines = File.ReadAllLines(metaFile,Encoding.UTF8);
|
||||
string[] lines = File.ReadAllLines(metaFile, Encoding.UTF8);
|
||||
string currentTag = null, currentContents = "";
|
||||
|
||||
foreach(string line in lines.Concat(endTag).Select(line => line.TrimEnd()).Where(line => line.Length > 0)){
|
||||
@@ -121,7 +210,7 @@ namespace TweetDck.Plugins{
|
||||
plugin.metadata[currentTag] = currentContents;
|
||||
}
|
||||
|
||||
currentTag = line.Substring(1,line.Length-2).ToUpperInvariant();
|
||||
currentTag = line.Substring(1, line.Length-2).ToUpperInvariant();
|
||||
currentContents = "";
|
||||
|
||||
if (line.Equals(endTag[0])){
|
||||
@@ -149,17 +238,19 @@ namespace TweetDck.Plugins{
|
||||
|
||||
Version ver;
|
||||
|
||||
if (plugin.RequiredVersion.Length == 0 || !(plugin.RequiredVersion.Equals("*") || System.Version.TryParse(plugin.RequiredVersion,out ver))){
|
||||
if (plugin.RequiredVersion.Length == 0 || !(plugin.RequiredVersion.Equals("*") || System.Version.TryParse(plugin.RequiredVersion, out ver))){
|
||||
error = "Plugin contains invalid version: "+plugin.RequiredVersion;
|
||||
return false;
|
||||
}
|
||||
|
||||
plugin.OnMetadataLoaded();
|
||||
|
||||
error = string.Empty;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool CheckRequiredVersion(string requires){
|
||||
return requires.Equals("*",StringComparison.Ordinal) || Program.Version >= new Version(requires);
|
||||
return requires.Equals("*", StringComparison.Ordinal) || Program.Version >= new Version(requires);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
102
Plugins/PluginBridge.cs
Normal file
102
Plugins/PluginBridge.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using TweetDck.Core.Utils;
|
||||
using TweetDck.Plugins.Enums;
|
||||
using TweetDck.Plugins.Events;
|
||||
|
||||
namespace TweetDck.Plugins{
|
||||
class PluginBridge{
|
||||
private static string SanitizeCacheKey(string key){
|
||||
return key.Replace('\\', '/').Trim();
|
||||
}
|
||||
|
||||
private readonly PluginManager manager;
|
||||
private readonly TwoKeyDictionary<int, string, string> fileCache = new TwoKeyDictionary<int, string, string>(4, 2);
|
||||
|
||||
public PluginBridge(PluginManager manager){
|
||||
this.manager = manager;
|
||||
this.manager.Reloaded += manager_Reloaded;
|
||||
this.manager.PluginChangedState += manager_PluginChangedState;
|
||||
}
|
||||
|
||||
private void manager_Reloaded(object sender, PluginLoadEventArgs e){
|
||||
fileCache.Clear();
|
||||
}
|
||||
|
||||
private void manager_PluginChangedState(object sender, PluginChangedStateEventArgs e){
|
||||
if (!e.IsEnabled){
|
||||
fileCache.Remove(manager.GetTokenFromPlugin(e.Plugin));
|
||||
}
|
||||
}
|
||||
|
||||
private string GetFullPathOrThrow(int token, PluginFolder folder, string path){
|
||||
Plugin plugin = manager.GetPluginFromToken(token);
|
||||
string fullPath = plugin == null ? string.Empty : plugin.GetFullPathIfSafe(folder, path);
|
||||
|
||||
if (fullPath.Length == 0){
|
||||
switch(folder){
|
||||
case PluginFolder.Data: throw new Exception("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.");
|
||||
default: throw new Exception("Invalid folder type "+folder+", this is a "+Program.BrandName+" error.");
|
||||
}
|
||||
}
|
||||
else{
|
||||
return fullPath;
|
||||
}
|
||||
}
|
||||
|
||||
private string ReadFileUnsafe(int token, string cacheKey, string fullPath, bool readCached){
|
||||
cacheKey = SanitizeCacheKey(cacheKey);
|
||||
|
||||
string cachedContents;
|
||||
|
||||
if (readCached && fileCache.TryGetValue(token, cacheKey, out cachedContents)){
|
||||
return cachedContents;
|
||||
}
|
||||
|
||||
try{
|
||||
return fileCache[token, cacheKey] = File.ReadAllText(fullPath, Encoding.UTF8);
|
||||
}catch(FileNotFoundException){
|
||||
throw new Exception("File not found.");
|
||||
}catch(DirectoryNotFoundException){
|
||||
throw new Exception("Directory not found.");
|
||||
}
|
||||
}
|
||||
|
||||
// Public methods
|
||||
|
||||
public void WriteFile(int token, string path, string contents){
|
||||
string fullPath = GetFullPathOrThrow(token, PluginFolder.Data, path);
|
||||
|
||||
// ReSharper disable once AssignNullToNotNullAttribute
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(fullPath));
|
||||
|
||||
File.WriteAllText(fullPath, contents, Encoding.UTF8);
|
||||
fileCache[token, SanitizeCacheKey(path)] = contents;
|
||||
}
|
||||
|
||||
public string ReadFile(int token, string path, bool cache){
|
||||
return ReadFileUnsafe(token, path, GetFullPathOrThrow(token, PluginFolder.Data, path), cache);
|
||||
}
|
||||
|
||||
public void DeleteFile(int token, string path){
|
||||
string fullPath = GetFullPathOrThrow(token, PluginFolder.Data, path);
|
||||
|
||||
fileCache.Remove(token, SanitizeCacheKey(path));
|
||||
File.Delete(fullPath);
|
||||
}
|
||||
|
||||
public bool CheckFileExists(int token, string path){
|
||||
return File.Exists(GetFullPathOrThrow(token, PluginFolder.Data, path));
|
||||
}
|
||||
|
||||
public string ReadFileRoot(int token, string path){
|
||||
return ReadFileUnsafe(token, "root*"+path, GetFullPathOrThrow(token, PluginFolder.Root, path), true);
|
||||
}
|
||||
|
||||
public bool CheckFileExistsRoot(int token, string path){
|
||||
return File.Exists(GetFullPathOrThrow(token, PluginFolder.Root, path));
|
||||
}
|
||||
}
|
||||
}
|
@@ -4,9 +4,9 @@ using TweetDck.Plugins.Events;
|
||||
|
||||
namespace TweetDck.Plugins{
|
||||
[Serializable]
|
||||
class PluginConfig{
|
||||
sealed class PluginConfig{
|
||||
[field:NonSerialized]
|
||||
public event EventHandler<PluginChangedStateEventArgs> PluginChangedState;
|
||||
public event EventHandler<PluginChangedStateEventArgs> InternalPluginChangedState; // should only be accessed from PluginManager
|
||||
|
||||
public IEnumerable<string> DisabledPlugins{
|
||||
get{
|
||||
@@ -24,8 +24,8 @@ namespace TweetDck.Plugins{
|
||||
|
||||
public void SetEnabled(Plugin plugin, bool enabled){
|
||||
if ((enabled && Disabled.Remove(plugin.Identifier)) || (!enabled && Disabled.Add(plugin.Identifier))){
|
||||
if (PluginChangedState != null){
|
||||
PluginChangedState(this,new PluginChangedStateEventArgs(plugin,enabled));
|
||||
if (InternalPluginChangedState != null){
|
||||
InternalPluginChangedState(this, new PluginChangedStateEventArgs(plugin, enabled));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,5 +33,9 @@ namespace TweetDck.Plugins{
|
||||
public bool IsEnabled(Plugin plugin){
|
||||
return !Disabled.Contains(plugin.Identifier) && plugin.CanRun;
|
||||
}
|
||||
|
||||
public void DisableOfficialFromConfig(string pluginName){
|
||||
Disabled.Add("official/"+pluginName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using CefSharp;
|
||||
using TweetDck.Plugins.Enums;
|
||||
using TweetDck.Plugins.Events;
|
||||
using TweetDck.Resources;
|
||||
|
||||
@@ -10,23 +11,46 @@ namespace TweetDck.Plugins{
|
||||
class PluginManager{
|
||||
public const string PluginBrowserScriptFile = "plugins.browser.js";
|
||||
public const string PluginNotificationScriptFile = "plugins.notification.js";
|
||||
public const string PluginGlobalScriptFile = "plugins.js";
|
||||
|
||||
public string PathOfficialPlugins { get { return Path.Combine(rootPath,"official"); } }
|
||||
public string PathCustomPlugins { get { return Path.Combine(rootPath,"user"); } }
|
||||
private const int InvalidToken = 0;
|
||||
|
||||
public string PathOfficialPlugins { get { return Path.Combine(rootPath, "official"); } }
|
||||
public string PathCustomPlugins { get { return Path.Combine(rootPath, "user"); } }
|
||||
|
||||
public IEnumerable<Plugin> Plugins { get { return plugins; } }
|
||||
public PluginConfig Config { get; private set; }
|
||||
public PluginBridge Bridge { get; private set; }
|
||||
|
||||
public event EventHandler<PluginLoadEventArgs> Reloaded;
|
||||
public event EventHandler<PluginChangedStateEventArgs> PluginChangedState;
|
||||
|
||||
private readonly string rootPath;
|
||||
private readonly HashSet<Plugin> plugins = new HashSet<Plugin>();
|
||||
private readonly Dictionary<int, Plugin> tokens = new Dictionary<int, Plugin>();
|
||||
private readonly Random rand = new Random();
|
||||
|
||||
private List<string> loadErrors;
|
||||
|
||||
public PluginManager(string path, PluginConfig config){
|
||||
this.rootPath = path;
|
||||
this.SetConfig(config);
|
||||
this.Bridge = new PluginBridge(this);
|
||||
}
|
||||
|
||||
public void SetConfig(PluginConfig config){
|
||||
this.Config = config;
|
||||
this.Config.InternalPluginChangedState += Config_InternalPluginChangedState;
|
||||
}
|
||||
|
||||
private void Config_InternalPluginChangedState(object sender, PluginChangedStateEventArgs e){
|
||||
if (PluginChangedState != null){
|
||||
PluginChangedState(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsPluginInstalled(string identifier){
|
||||
return plugins.Any(plugin => plugin.Identifier.Equals(identifier));
|
||||
}
|
||||
|
||||
public IEnumerable<Plugin> GetPluginsByGroup(PluginGroup group){
|
||||
@@ -41,28 +65,43 @@ namespace TweetDck.Plugins{
|
||||
return plugins.Any(plugin => plugin.Environments.HasFlag(environment));
|
||||
}
|
||||
|
||||
public int GetTokenFromPlugin(Plugin plugin){
|
||||
foreach(KeyValuePair<int, Plugin> kvp in tokens){
|
||||
if (kvp.Value.Equals(plugin)){
|
||||
return kvp.Key;
|
||||
}
|
||||
}
|
||||
|
||||
return InvalidToken;
|
||||
}
|
||||
|
||||
public Plugin GetPluginFromToken(int token){
|
||||
Plugin plugin;
|
||||
return tokens.TryGetValue(token, out plugin) ? plugin : null;
|
||||
}
|
||||
|
||||
public void Reload(){
|
||||
HashSet<Plugin> prevPlugins = new HashSet<Plugin>(plugins);
|
||||
plugins.Clear();
|
||||
tokens.Clear();
|
||||
|
||||
loadErrors = new List<string>(2);
|
||||
|
||||
foreach(Plugin plugin in LoadPluginsFrom(PathOfficialPlugins,PluginGroup.Official)){
|
||||
foreach(Plugin plugin in LoadPluginsFrom(PathOfficialPlugins, PluginGroup.Official)){
|
||||
plugins.Add(plugin);
|
||||
}
|
||||
|
||||
foreach(Plugin plugin in LoadPluginsFrom(PathCustomPlugins,PluginGroup.Custom)){
|
||||
foreach(Plugin plugin in LoadPluginsFrom(PathCustomPlugins, PluginGroup.Custom)){
|
||||
plugins.Add(plugin);
|
||||
}
|
||||
|
||||
if (Reloaded != null && (loadErrors.Count > 0 || !prevPlugins.SetEquals(plugins))){
|
||||
Reloaded(this,new PluginLoadEventArgs(loadErrors));
|
||||
if (Reloaded != null){
|
||||
Reloaded(this, new PluginLoadEventArgs(loadErrors));
|
||||
}
|
||||
}
|
||||
|
||||
public void ExecutePlugins(IFrame frame, PluginEnvironment environment, bool includeDisabled){
|
||||
if (includeDisabled){
|
||||
ScriptLoader.ExecuteScript(frame,PluginScriptGenerator.GenerateConfig(Config),"gen:pluginconfig");
|
||||
ScriptLoader.ExecuteScript(frame, PluginScriptGenerator.GenerateConfig(Config), "gen:pluginconfig");
|
||||
}
|
||||
|
||||
foreach(Plugin plugin in Plugins){
|
||||
@@ -78,14 +117,28 @@ namespace TweetDck.Plugins{
|
||||
continue;
|
||||
}
|
||||
|
||||
ScriptLoader.ExecuteScript(frame,PluginScriptGenerator.GeneratePlugin(plugin.Identifier,script,environment),"plugin:"+plugin);
|
||||
int token;
|
||||
|
||||
if (tokens.ContainsValue(plugin)){
|
||||
token = GetTokenFromPlugin(plugin);
|
||||
}
|
||||
else{
|
||||
token = GenerateToken();
|
||||
tokens[token] = plugin;
|
||||
}
|
||||
|
||||
ScriptLoader.ExecuteScript(frame, PluginScriptGenerator.GeneratePlugin(plugin.Identifier, script, token, environment), "plugin:"+plugin);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<Plugin> LoadPluginsFrom(string path, PluginGroup group){
|
||||
foreach(string fullDir in Directory.EnumerateDirectories(path,"*",SearchOption.TopDirectoryOnly)){
|
||||
if (!Directory.Exists(path)){
|
||||
yield break;
|
||||
}
|
||||
|
||||
foreach(string fullDir in Directory.EnumerateDirectories(path, "*", SearchOption.TopDirectoryOnly)){
|
||||
string error;
|
||||
Plugin plugin = Plugin.CreateFromFolder(fullDir,group,out error);
|
||||
Plugin plugin = Plugin.CreateFromFolder(fullDir, group, out error);
|
||||
|
||||
if (plugin == null){
|
||||
loadErrors.Add(group.GetIdentifierPrefix()+Path.GetFileName(fullDir)+": "+error);
|
||||
@@ -95,5 +148,17 @@ namespace TweetDck.Plugins{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int GenerateToken(){
|
||||
for(int attempt = 0; attempt < 1000; attempt++){
|
||||
int token = rand.Next();
|
||||
|
||||
if (!tokens.ContainsKey(token) && token != InvalidToken){
|
||||
return token;
|
||||
}
|
||||
}
|
||||
|
||||
return -tokens.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,20 +1,25 @@
|
||||
using System.Text;
|
||||
using TweetDck.Plugins.Enums;
|
||||
|
||||
namespace TweetDck.Plugins{
|
||||
static class PluginScriptGenerator{
|
||||
public static string GenerateConfig(PluginConfig config){
|
||||
return config.AnyDisabled ? "window.TD_PLUGINS.disabled = [\""+string.Join("\",\"",config.DisabledPlugins)+"\"];" : string.Empty;
|
||||
return config.AnyDisabled ? "window.TD_PLUGINS.disabled = [\""+string.Join("\",\"", config.DisabledPlugins)+"\"];" : string.Empty;
|
||||
}
|
||||
|
||||
public static string GeneratePlugin(string pluginIdentifier, string pluginContents, PluginEnvironment environment){
|
||||
StringBuilder build = new StringBuilder(pluginIdentifier.Length+pluginContents.Length+110);
|
||||
public static string GeneratePlugin(string pluginIdentifier, string pluginContents, int pluginToken, PluginEnvironment environment){
|
||||
StringBuilder build = new StringBuilder(2*pluginIdentifier.Length+pluginContents.Length+165);
|
||||
|
||||
build.Append("(function(").Append(environment.GetScriptVariables()).Append("){");
|
||||
|
||||
build.Append("window.TD_PLUGINS.install({");
|
||||
build.Append("let tmp={");
|
||||
build.Append("id:\"").Append(pluginIdentifier).Append("\",");
|
||||
build.Append("obj:new class extends PluginBase{").Append(pluginContents).Append("}");
|
||||
build.Append("});");
|
||||
build.Append("};");
|
||||
|
||||
build.Append("tmp.obj.$id=\"").Append(pluginIdentifier).Append("\";");
|
||||
build.Append("tmp.obj.$token=").Append(pluginToken).Append(";");
|
||||
build.Append("window.TD_PLUGINS.install(tmp);");
|
||||
|
||||
build.Append("})(").Append(environment.GetScriptVariables()).Append(");");
|
||||
|
||||
|
244
Program.cs
244
Program.cs
@@ -1,55 +1,52 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using CefSharp;
|
||||
using TweetDck.Configuration;
|
||||
using TweetDck.Core;
|
||||
using TweetDck.Migration;
|
||||
using TweetDck.Core.Utils;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using TweetDck.Plugins;
|
||||
using TweetDck.Plugins.Events;
|
||||
using TweetDck.Core.Other.Settings.Export;
|
||||
using TweetDck.Core.Handling;
|
||||
using TweetDck.Core.Other;
|
||||
using TweetDck.Updates;
|
||||
|
||||
[assembly: CLSCompliant(true)]
|
||||
namespace TweetDck{
|
||||
static class Program{
|
||||
#if DUCK
|
||||
public const string BrandName = "TweetDuck";
|
||||
public const string Website = "http://tweetduck.chylex.com";
|
||||
#else
|
||||
public const string BrandName = "TweetDick";
|
||||
public const string Website = "http://tweetdick.chylex.com";
|
||||
#endif
|
||||
public const string Website = "https://tweetduck.chylex.com";
|
||||
|
||||
public const string BrowserSubprocess = BrandName+".Browser.exe";
|
||||
|
||||
public const string VersionTag = "1.3";
|
||||
public const string VersionFull = "1.3.0.0";
|
||||
public const string VersionTag = "1.6.6";
|
||||
public const string VersionFull = "1.6.6.0";
|
||||
|
||||
public static readonly Version Version = new Version(VersionTag);
|
||||
|
||||
public static readonly string StoragePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),BrandName);
|
||||
public static readonly string PluginPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"plugins");
|
||||
public static readonly string TemporaryPath = Path.Combine(Path.GetTempPath(),BrandName);
|
||||
public static readonly string ConfigFilePath = Path.Combine(StoragePath,"TD_UserConfig.cfg");
|
||||
public static readonly bool IsPortable = File.Exists("makeportable");
|
||||
private static readonly CommandLineArgs Args = CommandLineArgs.FromStringArray('-', Environment.GetCommandLineArgs());
|
||||
|
||||
public static readonly string ProgramPath = AppDomain.CurrentDomain.BaseDirectory;
|
||||
public static readonly string StoragePath = IsPortable ? Path.Combine(ProgramPath, "portable", "storage") : GetDataStoragePath();
|
||||
public static readonly string TemporaryPath = IsPortable ? Path.Combine(ProgramPath, "portable", "tmp") : Path.Combine(Path.GetTempPath(), BrandName+'_'+Path.GetRandomFileName().Substring(0, 6));
|
||||
|
||||
public static readonly string PluginDataPath = Path.Combine(StoragePath, "TD_Plugins");
|
||||
public static readonly string ConfigFilePath = Path.Combine(StoragePath, "TD_UserConfig.cfg");
|
||||
private static readonly string LogFilePath = Path.Combine(StoragePath, "TD_Log.txt");
|
||||
|
||||
public static readonly string ScriptPath = Path.Combine(ProgramPath, "scripts");
|
||||
public static readonly string PluginPath = Path.Combine(ProgramPath, "plugins");
|
||||
|
||||
public static uint WindowRestoreMessage;
|
||||
|
||||
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;
|
||||
|
||||
public static UserConfig UserConfig { get; private set; }
|
||||
|
||||
public static string LogFile{
|
||||
get{
|
||||
return Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"td-log.txt");
|
||||
}
|
||||
}
|
||||
public static Reporter Reporter { get; private set; }
|
||||
|
||||
[STAThread]
|
||||
private static void Main(){
|
||||
@@ -58,31 +55,61 @@ namespace TweetDck{
|
||||
|
||||
WindowRestoreMessage = NativeMethods.RegisterWindowMessage("TweetDuckRestore");
|
||||
|
||||
string[] programArguments = Environment.GetCommandLineArgs();
|
||||
if (!WindowsUtils.CheckFolderWritePermission(StoragePath)){
|
||||
MessageBox.Show(BrandName+" does not have write permissions to the storage folder: "+StoragePath, "Permission Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
if (programArguments.Contains("-restart")){
|
||||
Reporter = new Reporter(LogFilePath);
|
||||
Reporter.SetupUnhandledExceptionHandler(BrandName+" Has Failed :(");
|
||||
|
||||
if (Args.HasFlag("-restart")){
|
||||
for(int attempt = 0; attempt < 21; attempt++){
|
||||
if (LockManager.Lock()){
|
||||
LockManager.Result lockResult = LockManager.Lock();
|
||||
|
||||
if (lockResult == LockManager.Result.Success){
|
||||
break;
|
||||
}
|
||||
else if (attempt == 20){
|
||||
MessageBox.Show(BrandName+" is taking too long to close, please wait and then start the application again manually.",BrandName+" Cannot Restart",MessageBoxButtons.OK,MessageBoxIcon.Error);
|
||||
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;
|
||||
}
|
||||
else{
|
||||
Thread.Sleep(500);
|
||||
else if (attempt == 20){
|
||||
using(FormMessage form = new FormMessage(BrandName+" Cannot Restart", BrandName+" is taking too long to close.", MessageBoxIcon.Warning)){
|
||||
form.AddButton("Exit");
|
||||
Button btnRetry = form.AddButton("Retry");
|
||||
|
||||
if (form.ShowDialog() == DialogResult.OK){
|
||||
if (form.ClickedButton == btnRetry){
|
||||
attempt /= 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
else Thread.Sleep(500);
|
||||
}
|
||||
}
|
||||
else{
|
||||
if (!LockManager.Lock()){
|
||||
if (LockManager.LockingProcess.MainWindowHandle == IntPtr.Zero && LockManager.LockingProcess.Responding){ // restore if the original process is in tray
|
||||
NativeMethods.SendMessage(NativeMethods.HWND_BROADCAST,WindowRestoreMessage,0,IntPtr.Zero);
|
||||
return;
|
||||
LockManager.Result lockResult = LockManager.Lock();
|
||||
|
||||
if (lockResult == LockManager.Result.HasProcess){
|
||||
if (LockManager.LockingProcess.MainWindowHandle == IntPtr.Zero){ // restore if the original process is in tray
|
||||
NativeMethods.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
|
||||
}
|
||||
}
|
||||
else 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)){
|
||||
MessageBox.Show("Could not close the other process.",BrandName+" Has Failed :(",MessageBoxButtons.OK,MessageBoxIcon.Error);
|
||||
|
||||
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)){
|
||||
MessageBox.Show("Could not close the other process.", BrandName+" Has Failed :(", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -90,96 +117,92 @@ namespace TweetDck{
|
||||
}
|
||||
else return;
|
||||
}
|
||||
}
|
||||
|
||||
if (programArguments.Contains("-importcookies") && File.Exists(ExportManager.TempCookiesPath)){
|
||||
try{
|
||||
if (File.Exists(ExportManager.CookiesPath)){
|
||||
File.Delete(ExportManager.CookiesPath);
|
||||
}
|
||||
|
||||
File.Move(ExportManager.TempCookiesPath,ExportManager.CookiesPath);
|
||||
}catch(Exception e){
|
||||
HandleException("Could not import the cookie file to restore login session.",e);
|
||||
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);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ReloadConfig();
|
||||
|
||||
MigrationManager.Run();
|
||||
if (Args.HasFlag("-importcookies")){
|
||||
ExportManager.ImportCookies();
|
||||
}
|
||||
|
||||
Cef.OnContextInitialized = () => {
|
||||
using(IRequestContext ctx = Cef.GetGlobalRequestContext()){
|
||||
string err;
|
||||
ctx.SetPreference("browser.enable_spellchecking",false,out err);
|
||||
}
|
||||
};
|
||||
BrowserCache.ClearOldCacheFiles();
|
||||
|
||||
CefSharpSettings.WcfEnabled = false;
|
||||
|
||||
Cef.Initialize(new CefSettings{
|
||||
CefSettings settings = new CefSettings{
|
||||
AcceptLanguageList = BrowserUtils.HeaderAcceptLanguage,
|
||||
UserAgent = BrowserUtils.HeaderUserAgent,
|
||||
Locale = CultureInfo.CurrentCulture.TwoLetterISOLanguageName,
|
||||
Locale = Args.GetValue("-locale", "en"),
|
||||
CachePath = StoragePath,
|
||||
BrowserSubprocessPath = File.Exists(BrowserSubprocess) ? BrowserSubprocess : "CefSharp.BrowserSubprocess.exe",
|
||||
LogFile = Path.Combine(StoragePath, "TD_Console.txt"),
|
||||
#if !DEBUG
|
||||
LogSeverity = programArguments.Contains("-log") ? LogSeverity.Info : LogSeverity.Disable
|
||||
BrowserSubprocessPath = BrandName+".Browser.exe",
|
||||
LogSeverity = Args.HasFlag("-log") ? LogSeverity.Info : LogSeverity.Disable
|
||||
#endif
|
||||
});
|
||||
|
||||
AppDomain.CurrentDomain.UnhandledException += (sender, args) => {
|
||||
Exception ex = args.ExceptionObject as Exception;
|
||||
|
||||
if (ex != null){
|
||||
HandleException("An unhandled exception has occurred.",ex);
|
||||
}
|
||||
};
|
||||
|
||||
CommandLineArgsParser.ReadCefArguments(UserConfig.CustomCefArgs).ToDictionary(settings.CefCommandLineArgs);
|
||||
|
||||
if (!HardwareAcceleration.IsEnabled){
|
||||
settings.CefCommandLineArgs["disable-gpu"] = "1";
|
||||
settings.CefCommandLineArgs["disable-gpu-vsync"] = "1";
|
||||
}
|
||||
|
||||
Cef.Initialize(settings, false, new BrowserProcessHandler());
|
||||
|
||||
Application.ApplicationExit += (sender, args) => ExitCleanup();
|
||||
|
||||
PluginManager plugins = new PluginManager(PluginPath,UserConfig.Plugins);
|
||||
PluginManager plugins = new PluginManager(PluginPath, UserConfig.Plugins);
|
||||
plugins.Reloaded += plugins_Reloaded;
|
||||
plugins.Config.PluginChangedState += (sender, args) => UserConfig.Save();
|
||||
plugins.PluginChangedState += (sender, args) => UserConfig.Save();
|
||||
plugins.Reload();
|
||||
|
||||
FormBrowser mainForm = new FormBrowser(plugins);
|
||||
FormBrowser mainForm = new FormBrowser(plugins, new UpdaterSettings{
|
||||
AllowPreReleases = Args.HasFlag("-debugupdates"),
|
||||
DismissedUpdate = UserConfig.DismissedUpdate
|
||||
});
|
||||
|
||||
Application.Run(mainForm);
|
||||
|
||||
if (mainForm.UpdateInstallerPath != null){
|
||||
ExitCleanup();
|
||||
|
||||
Process.Start(mainForm.UpdateInstallerPath,"/SP- /SILENT /NOICONS /CLOSEAPPLICATIONS");
|
||||
// ProgramPath has a trailing backslash
|
||||
string updaterArgs = "/SP- /SILENT /CLOSEAPPLICATIONS /UPDATEPATH=\""+ProgramPath+"\" /RUNARGS=\""+GetArgsClean().ToString().Replace("\"", "^\"")+"\""+(IsPortable ? " /PORTABLE=1" : "");
|
||||
bool runElevated = !IsPortable || !WindowsUtils.CheckFolderWritePermission(ProgramPath);
|
||||
|
||||
WindowsUtils.StartProcess(mainForm.UpdateInstallerPath, updaterArgs, runElevated);
|
||||
Application.Exit();
|
||||
}
|
||||
}
|
||||
|
||||
private static void plugins_Reloaded(object sender, PluginLoadEventArgs e){
|
||||
if (!e.Success){
|
||||
MessageBox.Show("The following plugins will not be available until the issues are resolved:\n"+string.Join("\n",e.Errors),"Error Loading Plugins",MessageBoxButtons.OK,MessageBoxIcon.Warning);
|
||||
MessageBox.Show("The following plugins will not be available until the issues are resolved:\n"+string.Join("\n", e.Errors), "Error Loading Plugins", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
}
|
||||
|
||||
((PluginManager)sender).SetConfig(UserConfig.Plugins);
|
||||
}
|
||||
|
||||
public static void HandleException(string message, Exception e){
|
||||
Log(e.ToString());
|
||||
|
||||
if (MessageBox.Show(message+"\r\nDo you want to open the log file to report the issue?",BrandName+" Has Failed :(",MessageBoxButtons.YesNo,MessageBoxIcon.Error,MessageBoxDefaultButton.Button2) == DialogResult.Yes){
|
||||
Process.Start(LogFile);
|
||||
private static string GetDataStoragePath(){
|
||||
string custom = Args.GetValue("-datafolder", null);
|
||||
|
||||
if (custom != null && (custom.Contains(Path.DirectorySeparatorChar) || custom.Contains(Path.AltDirectorySeparatorChar))){
|
||||
if (Path.GetInvalidPathChars().Any(custom.Contains)){
|
||||
Reporter.HandleEarlyFailure("Data Folder Invalid", "The data folder contains invalid characters:\n"+custom);
|
||||
}
|
||||
else if (!Path.IsPathRooted(custom)){
|
||||
Reporter.HandleEarlyFailure("Data Folder Invalid", "The data folder has to be either a simple folder name, or a full path:\n"+custom);
|
||||
}
|
||||
|
||||
return Environment.ExpandEnvironmentVariables(custom);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Log(string data){
|
||||
StringBuilder build = new StringBuilder();
|
||||
|
||||
if (!File.Exists(LogFile)){
|
||||
build.Append("Please, report all issues to: https://github.com/chylex/TweetDuck/issues\r\n\r\n");
|
||||
}
|
||||
|
||||
build.Append("[").Append(DateTime.Now.ToString("G")).Append("]\r\n");
|
||||
build.Append(data).Append("\r\n\r\n");
|
||||
|
||||
try{
|
||||
File.AppendAllText(LogFile,build.ToString(),Encoding.UTF8);
|
||||
}catch{
|
||||
// oops
|
||||
else{
|
||||
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), custom ?? BrandName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,20 +215,49 @@ namespace TweetDck{
|
||||
File.Delete(ConfigFilePath);
|
||||
File.Delete(UserConfig.GetBackupFile(ConfigFilePath));
|
||||
}catch(Exception e){
|
||||
HandleException("Could not delete configuration files to reset the settings.",e);
|
||||
Reporter.HandleException("Configuration Reset Error", "Could not delete configuration files to reset the settings.", true, e);
|
||||
return;
|
||||
}
|
||||
|
||||
ReloadConfig();
|
||||
}
|
||||
|
||||
private static CommandLineArgs GetArgsClean(){
|
||||
CommandLineArgs args = Args.Clone();
|
||||
args.RemoveFlag("-importcookies");
|
||||
return args;
|
||||
}
|
||||
|
||||
public static void Restart(){
|
||||
Restart(new string[0]);
|
||||
}
|
||||
|
||||
public static void Restart(string[] extraArgs){
|
||||
FormBrowser browserForm = Application.OpenForms.OfType<FormBrowser>().FirstOrDefault();
|
||||
|
||||
if (browserForm == null){
|
||||
return;
|
||||
}
|
||||
|
||||
CommandLineArgs args = GetArgsClean();
|
||||
args.AddFlag("-restart");
|
||||
|
||||
CommandLineArgs.ReadStringArray('-', extraArgs, args);
|
||||
|
||||
browserForm.ForceClose();
|
||||
ExitCleanup();
|
||||
|
||||
Process.Start(Application.ExecutablePath, args.ToString());
|
||||
Application.Exit();
|
||||
}
|
||||
|
||||
private static void ExitCleanup(){
|
||||
if (HasCleanedUp)return;
|
||||
|
||||
UserConfig.Save();
|
||||
|
||||
try{
|
||||
Directory.Delete(TemporaryPath,true);
|
||||
Directory.Delete(TemporaryPath, true);
|
||||
}catch(DirectoryNotFoundException){
|
||||
}catch(Exception e){
|
||||
// welp, too bad
|
||||
|
@@ -36,4 +36,8 @@ using TweetDck;
|
||||
[assembly: AssemblyVersion(Program.VersionFull)]
|
||||
[assembly: AssemblyFileVersion(Program.VersionFull)]
|
||||
|
||||
[assembly: NeutralResourcesLanguageAttribute("en")]
|
||||
[assembly: NeutralResourcesLanguageAttribute("en")]
|
||||
|
||||
#if DEBUG
|
||||
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("UnitTests")]
|
||||
#endif
|
||||
|
10
README.md
10
README.md
@@ -1,13 +1,11 @@
|
||||
# Build Instructions
|
||||
|
||||
The program was build using Visual Studio 2013. After opening the solution, make sure you have **CefSharp.WinForms** and **Microsoft.VC120.CRT.JetBrains** included - if not, download them using NuGet. For **CefSharp**, you will need version 49 or newer currently available as a pre-release.
|
||||
The program was build using Visual Studio 2013. After opening the solution, make sure you have **CefSharp.WinForms** and **Microsoft.VC120.CRT.JetBrains** included - if not, download them using NuGet.
|
||||
```
|
||||
PM> Install-Package CefSharp.WinForms -Pre
|
||||
PM> Install-Package CefSharp.WinForms -Version 55.0.0
|
||||
PM> Install-Package Microsoft.VC120.CRT.JetBrains
|
||||
```
|
||||
|
||||
TweetD\*ck comes in two variants - TweetDick and TweetDuck. The solution includes both configurations under the names **Release Dick** and **Release Duck**, so make sure to select the correct one, or build both using Batch Build.
|
||||
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. Do not run both files consecutively, otherwise the program will crash - if you want to do both, rebuild the project before running each file.
|
||||
|
||||
After building, run **_postbuild.bat** which deletes unnecessary files that CefSharp adds after post-build events >_>
|
||||
|
||||
Built files are then available in **bin/x86** and/or **bin/x64**.
|
||||
Built files are then available in **bin/x86** and/or **bin/x64**, 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.
|
||||
|
103
Reporter.cs
Normal file
103
Reporter.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using TweetDck.Core.Other;
|
||||
|
||||
namespace TweetDck{
|
||||
class Reporter{
|
||||
private readonly string logFile;
|
||||
|
||||
public Reporter(string logFile){
|
||||
this.logFile = logFile;
|
||||
}
|
||||
|
||||
public void SetupUnhandledExceptionHandler(string caption){
|
||||
AppDomain.CurrentDomain.UnhandledException += (sender, args) => {
|
||||
Exception ex = args.ExceptionObject as Exception;
|
||||
|
||||
if (ex != null){
|
||||
HandleException(caption, "An unhandled exception has occurred.", false, ex);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public bool Log(string data){
|
||||
StringBuilder build = new StringBuilder();
|
||||
|
||||
if (!File.Exists(logFile)){
|
||||
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(data).Append("\r\n\r\n");
|
||||
|
||||
try{
|
||||
File.AppendAllText(logFile, build.ToString(), Encoding.UTF8);
|
||||
return true;
|
||||
}catch{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void HandleException(string caption, string message, bool canIgnore, Exception e){
|
||||
bool loggedSuccessfully = Log(e.ToString());
|
||||
|
||||
FormMessage form = new FormMessage(caption, message+"\r\nError: "+e.Message, canIgnore ? MessageBoxIcon.Warning : MessageBoxIcon.Error);
|
||||
|
||||
form.AddButton("Exit");
|
||||
Button btnIgnore = form.AddButton("Ignore");
|
||||
|
||||
Button btnOpenLog = new Button{
|
||||
Anchor = AnchorStyles.Bottom | AnchorStyles.Left,
|
||||
Enabled = loggedSuccessfully,
|
||||
Font = SystemFonts.MessageBoxFont,
|
||||
Location = new Point(12, 12),
|
||||
Margin = new Padding(0, 0, 48, 0),
|
||||
Size = new Size(88, 26),
|
||||
Text = "Show Error Log",
|
||||
UseVisualStyleBackColor = true
|
||||
};
|
||||
|
||||
btnOpenLog.Click += (sender, args) => {
|
||||
using(Process.Start(logFile)){}
|
||||
};
|
||||
|
||||
form.AddActionControl(btnOpenLog);
|
||||
|
||||
if (!canIgnore){
|
||||
btnIgnore.Enabled = false;
|
||||
}
|
||||
|
||||
if (form.ShowDialog() == DialogResult.OK){
|
||||
if (form.ClickedButton == btnIgnore){
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try{
|
||||
Process.GetCurrentProcess().Kill();
|
||||
}catch{
|
||||
Environment.FailFast(message, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void HandleEarlyFailure(string caption, string message){
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
|
||||
FormMessage form = new FormMessage(caption, message, MessageBoxIcon.Error);
|
||||
form.AddButton("Exit");
|
||||
form.ShowDialog();
|
||||
|
||||
try{
|
||||
Process.GetCurrentProcess().Kill();
|
||||
}catch{
|
||||
Environment.FailFast(message, new Exception(message));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
14
Resources/Plugins/.debug/.meta
Normal file
14
Resources/Plugins/.debug/.meta
Normal file
@@ -0,0 +1,14 @@
|
||||
[name]
|
||||
Debug plugin
|
||||
|
||||
[description]
|
||||
- Runs several tests on startup, only included in debug configuration
|
||||
|
||||
[author]
|
||||
chylex
|
||||
|
||||
[version]
|
||||
1.0
|
||||
|
||||
[website]
|
||||
https://tweetduck.chylex.com
|
3
Resources/Plugins/.debug/browser.js
Normal file
3
Resources/Plugins/.debug/browser.js
Normal file
@@ -0,0 +1,3 @@
|
||||
enabled(){
|
||||
|
||||
}
|
18
Resources/Plugins/clear-columns/.meta
Normal file
18
Resources/Plugins/clear-columns/.meta
Normal file
@@ -0,0 +1,18 @@
|
||||
[name]
|
||||
Clear columns
|
||||
|
||||
[description]
|
||||
- Adds buttons and keyboard shortcuts to quickly clear columns
|
||||
- Hold Shift when clicking or using a keyboard shortcut to reset the column instead
|
||||
|
||||
[author]
|
||||
chylex
|
||||
|
||||
[version]
|
||||
1.1.1
|
||||
|
||||
[website]
|
||||
https://tweetduck.chylex.com
|
||||
|
||||
[requires]
|
||||
1.4.1
|
128
Resources/Plugins/clear-columns/browser.js
Normal file
128
Resources/Plugins/clear-columns/browser.js
Normal file
@@ -0,0 +1,128 @@
|
||||
constructor(){
|
||||
super({
|
||||
requiresPageReload: true
|
||||
});
|
||||
}
|
||||
|
||||
enabled(){
|
||||
// prepare variables and functions
|
||||
var clearColumn = (columnName) => {
|
||||
TD.controller.columnManager.get(columnName).clear();
|
||||
TD.controller.stats.columnActionClick("clear");
|
||||
};
|
||||
|
||||
var resetColumn = (columnName) => {
|
||||
var col = TD.controller.columnManager.get(columnName);
|
||||
col.model.setClearedTimestamp(0);
|
||||
col.reloadTweets();
|
||||
};
|
||||
|
||||
var forEachColumn = (func) => {
|
||||
Object.keys(TD.controller.columnManager.getAll()).forEach(func);
|
||||
};
|
||||
|
||||
var replaceMustache = (key, search, replace) => {
|
||||
TD.mustaches[key] = TD.mustaches[key].replace(search, replace);
|
||||
};
|
||||
|
||||
var wasShiftPressed = false;
|
||||
|
||||
var updateShiftState = (pressed) => {
|
||||
if (pressed != wasShiftPressed){
|
||||
wasShiftPressed = pressed;
|
||||
|
||||
if (pressed){
|
||||
$(document).on("mousemove", this.eventKeyUp);
|
||||
}
|
||||
else{
|
||||
$(document).off("mousemove", this.eventKeyUp);
|
||||
}
|
||||
|
||||
$("#clear-columns-btn-all").text(pressed ? "Reset all" : "Clear all");
|
||||
}
|
||||
};
|
||||
|
||||
// prepare event handlers
|
||||
this.eventClickSingle = function(e){
|
||||
var name = $(this).closest(".js-column").attr("data-column");
|
||||
e.shiftKey ? resetColumn(name) : clearColumn(name);
|
||||
};
|
||||
|
||||
this.eventClickAll = function(e){
|
||||
forEachColumn(e.shiftKey ? resetColumn : clearColumn);
|
||||
};
|
||||
|
||||
this.eventKeyDown = function(e){
|
||||
if (!(document.activeElement === null || document.activeElement === document.body)){
|
||||
return;
|
||||
}
|
||||
|
||||
updateShiftState(e.shiftKey);
|
||||
|
||||
if (e.keyCode === 46){ // 46 = delete
|
||||
if (e.altKey){
|
||||
forEachColumn(e.shiftKey ? resetColumn : clearColumn);
|
||||
}
|
||||
else{
|
||||
var focusedColumn = $(".js-column.is-focused");
|
||||
|
||||
if (focusedColumn.length){
|
||||
var name = focusedColumn.attr("data-column");
|
||||
e.shiftKey ? resetColumn(name) : clearColumn(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.eventKeyUp = function(e){
|
||||
if (!e.shiftKey){
|
||||
updateShiftState(false);
|
||||
}
|
||||
};
|
||||
|
||||
// add column buttons and keyboard shortcut info to UI
|
||||
replaceMustache("column/column_header.mustache", "</header>", [
|
||||
'{{^isTemporary}}',
|
||||
'<a class="column-header-link" href="#" data-action="td-clearcolumns-dosingle" style="right:34px">',
|
||||
'<i class="icon icon-clear-timeline"></i>',
|
||||
'</a>',
|
||||
'{{/isTemporary}}',
|
||||
'</header>'
|
||||
].join(""));
|
||||
|
||||
replaceMustache("keyboard_shortcut_list.mustache", "</dl> <dl", [
|
||||
'<dd class="keyboard-shortcut-definition" style="white-space:nowrap">',
|
||||
'<span class="text-like-keyboard-key">1</span> … <span class="text-like-keyboard-key">9</span> + <span class="text-like-keyboard-key">Del</span> Clear column 1-9',
|
||||
'</dd><dd class="keyboard-shortcut-definition">',
|
||||
'<span class="text-like-keyboard-key">Alt</span> + <span class="text-like-keyboard-key">Del</span> Clear all',
|
||||
'</dd></dl><dl'
|
||||
].join(""));
|
||||
|
||||
// load custom style
|
||||
var css = window.TDPF_createCustomStyle(this);
|
||||
css.insert(".column-title { margin-right: 60px !important; }");
|
||||
css.insert(".column-type-message .column-title { margin-right: 115px !important; }");
|
||||
css.insert(".mark-all-read-link { right: 59px !important; }");
|
||||
css.insert(".open-compose-dm-link { right: 90px !important; }");
|
||||
css.insert("button[data-action='clear'].btn-options-tray { display: none !important; }");
|
||||
}
|
||||
|
||||
ready(){
|
||||
// setup events
|
||||
$(document).on("click", "[data-action='td-clearcolumns-dosingle']", this.eventClickSingle);
|
||||
$(document).on("click", "[data-action='td-clearcolumns-doall']", this.eventClickAll);
|
||||
$(document).on("keydown", this.eventKeyDown);
|
||||
$(document).on("keyup", this.eventKeyUp);
|
||||
|
||||
// add clear all button
|
||||
$("nav.app-navigator").first().append([
|
||||
'<a class="link-clean cf app-nav-link padding-h--10" data-title="Clear all" data-action="td-clearcolumns-doall">',
|
||||
'<div class="obj-left margin-l--2"><i class="icon icon-medium icon-clear-timeline"></i></div>',
|
||||
'<div id="clear-columns-btn-all" class="nbfc padding-ts hide-condensed txt-size--16">Clear all</div>',
|
||||
'</a></nav>'
|
||||
].join(""));
|
||||
}
|
||||
|
||||
disabled(){
|
||||
// not needed, plugin reloads the page when enabled or disabled
|
||||
}
|
@@ -2,6 +2,7 @@
|
||||
Revert TweetDeck design changes
|
||||
|
||||
[description]
|
||||
- Individually configurable options to revert and tweak TweetDeck design changes
|
||||
- Moves action menu to the right and hides it by default
|
||||
- Reverts interactive texts around tweets (such as 'Details' or 'Conversation')
|
||||
|
||||
@@ -9,7 +10,16 @@ Revert TweetDeck design changes
|
||||
chylex
|
||||
|
||||
[version]
|
||||
1.0
|
||||
1.2
|
||||
|
||||
[website]
|
||||
http://tweetduck.chylex.com
|
||||
https://tweetduck.chylex.com
|
||||
|
||||
[configfile]
|
||||
configuration.js
|
||||
|
||||
[configdefault]
|
||||
configuration.default.js
|
||||
|
||||
[requires]
|
||||
1.4.1
|
@@ -1,35 +1,46 @@
|
||||
enabled(){
|
||||
// add a stylesheet to change tweet actions
|
||||
var style = document.createElement("style");
|
||||
style.id = "design-revert";
|
||||
document.head.appendChild(style);
|
||||
|
||||
var sheet = style.sheet;
|
||||
sheet.insertRule(".tweet-actions { float: right !important; width: auto !important; visibility: hidden; }",0);
|
||||
sheet.insertRule(".tweet-actions:hover { visibility: visible; }",0);
|
||||
sheet.insertRule(".tweet-actions > li:nth-child(4) { margin-right: 2px !important; }",0);
|
||||
|
||||
// revert small links around the tweet
|
||||
this.css = window.TDPF_createCustomStyle(this);
|
||||
this.prevFooterMustache = TD.mustaches["status/tweet_single_footer.mustache"];
|
||||
|
||||
var footerLayout = TD.mustaches["status/tweet_single_footer.mustache"];
|
||||
footerLayout = footerLayout.replace('txt-mute txt-size--12','txt-mute txt-small');
|
||||
footerLayout = footerLayout.replace('{{#inReplyToID}}','{{^inReplyToID}} <a class="pull-left margin-txs txt-mute txt-small is-vishidden-narrow" href="#" rel="viewDetails">{{_i}}Details{{/i}}</a> <a class="pull-left margin-txs txt-mute txt-small is-vishidden is-visshown-narrow" href="#" rel="viewDetails">{{_i}}Open{{/i}}</a> {{/inReplyToID}} {{#inReplyToID}}');
|
||||
footerLayout = footerLayout.replace('<span class="link-complex-target"> {{_i}}View Conversation{{/i}}','<i class="icon icon-conversation icon-small-context"></i> <span class="link-complex-target"> <span class="is-vishidden-wide is-vishidden-narrow">{{_i}}View{{/i}}</span> <span class="is-vishidden is-visshown-wide">{{_i}}Conversation{{/i}}</span>');
|
||||
TD.mustaches["status/tweet_single_footer.mustache"] = footerLayout;
|
||||
|
||||
|
||||
// load configuration
|
||||
window.TDPF_loadConfigurationFile(this, "configuration.js", "configuration.default.js", config => {
|
||||
if (config.hideTweetActions){
|
||||
this.css.insert(".tweet-action { opacity: 0; }");
|
||||
this.css.insert(".is-favorite .tweet-action, .is-retweet .tweet-action { opacity: 0.5; visibility: visible !important; }");
|
||||
this.css.insert(".tweet:hover .tweet-action, .is-favorite .tweet-action[rel='favorite'], .is-retweet .tweet-action[rel='retweet'] { opacity: 1; visibility: visible !important; }");
|
||||
}
|
||||
|
||||
if (config.moveTweetActionsToRight){
|
||||
this.css.insert(".tweet-actions { float: right !important; width: auto !important; }");
|
||||
this.css.insert(".tweet-actions > li:nth-child(4) { margin-right: 2px !important; }");
|
||||
}
|
||||
|
||||
if (config.smallComposeTextSize){
|
||||
this.css.insert(".compose-text { font-size: 12px !important; height: 120px !important; }");
|
||||
}
|
||||
|
||||
if (config.revertConversationLinks){
|
||||
var footer = TD.mustaches["status/tweet_single_footer.mustache"];
|
||||
footer = footer.replace('txt-mute txt-size--12', 'txt-mute txt-small');
|
||||
footer = footer.replace('{{#inReplyToID}}', '{{^inReplyToID}} <a class="pull-left margin-txs txt-mute txt-small is-vishidden-narrow" href="#" rel="viewDetails">{{_i}}Details{{/i}}</a> <a class="pull-left margin-txs txt-mute txt-small is-vishidden is-visshown-narrow" href="#" rel="viewDetails">{{_i}}Open{{/i}}</a> {{/inReplyToID}} {{#inReplyToID}}');
|
||||
footer = footer.replace('<span class="link-complex-target"> {{_i}}View Conversation{{/i}}', '<i class="icon icon-conversation icon-small-context"></i> <span class="link-complex-target"> <span class="is-vishidden-wide is-vishidden-narrow">{{_i}}View{{/i}}</span> <span class="is-vishidden is-visshown-wide">{{_i}}Conversation{{/i}}</span>');
|
||||
TD.mustaches["status/tweet_single_footer.mustache"] = footer;
|
||||
}
|
||||
|
||||
if (config.moveTweetActionsToRight){
|
||||
$(document).on("uiShowActionsMenu", this.uiShowActionsMenuEvent);
|
||||
}
|
||||
});
|
||||
|
||||
// fix layout for right-aligned actions menu
|
||||
this.uiShowActionsMenuEvent = function(){
|
||||
$(".js-dropdown.pos-r").toggleClass("pos-r pos-l");
|
||||
};
|
||||
}
|
||||
|
||||
ready(){
|
||||
$(document).on("uiShowActionsMenu",this.uiShowActionsMenuEvent);
|
||||
}
|
||||
|
||||
disabled(){
|
||||
$("#design-revert").remove();
|
||||
$(document).off("uiShowActionsMenu",this.uiShowActionsMenuEvent);
|
||||
this.css.remove();
|
||||
TD.mustaches["status/tweet_single_footer.mustache"] = this.prevFooterMustache;
|
||||
}
|
||||
|
||||
$(document).off("uiShowActionsMenu", this.uiShowActionsMenuEvent);
|
||||
}
|
||||
|
22
Resources/Plugins/design-revert/configuration.default.js
Normal file
22
Resources/Plugins/design-revert/configuration.default.js
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
/*
|
||||
* Hides the action bar below each tweet.
|
||||
* The action bar fully appears when the mouse is over the tweet, or at half the opacity when the tweet is liked/retweeted.
|
||||
*/
|
||||
hideTweetActions: true,
|
||||
|
||||
/*
|
||||
* Moves the action bar to the right side of the tweet.
|
||||
*/
|
||||
moveTweetActionsToRight: true,
|
||||
|
||||
/*
|
||||
* Reverts New Tweet font size to a smaller one.
|
||||
*/
|
||||
smallComposeTextSize: false,
|
||||
|
||||
/*
|
||||
* Reverts design changes to the 'View Conversation' and 'Details' links.
|
||||
*/
|
||||
revertConversationLinks: true
|
||||
}
|
18
Resources/Plugins/emoji-keyboard/.meta
Normal file
18
Resources/Plugins/emoji-keyboard/.meta
Normal file
@@ -0,0 +1,18 @@
|
||||
[name]
|
||||
Emoji keyboard
|
||||
|
||||
[description]
|
||||
- Adds an emoji keyboard when writing tweets
|
||||
- Emoji list provided by http://unicode.org/emoji/charts/emoji-ordering.html
|
||||
|
||||
[author]
|
||||
chylex
|
||||
|
||||
[version]
|
||||
1.0
|
||||
|
||||
[website]
|
||||
https://tweetduck.chylex.com
|
||||
|
||||
[requires]
|
||||
1.5.3
|
178
Resources/Plugins/emoji-keyboard/browser.js
Normal file
178
Resources/Plugins/emoji-keyboard/browser.js
Normal file
@@ -0,0 +1,178 @@
|
||||
enabled(){
|
||||
this.emojiHTML = "";
|
||||
|
||||
var me = this;
|
||||
|
||||
// styles
|
||||
|
||||
this.css = window.TDPF_createCustomStyle(this);
|
||||
this.css.insert(".emoji-keyboard { position: absolute; width: 15.35em; height: 11.2em; background-color: white; overflow-y: auto; padding: 0.1em; box-sizing: border-box; border-radius: 2px; font-size: 24px; z-index: 9999 }");
|
||||
this.css.insert(".emoji-keyboard .separator { height: 26px; }");
|
||||
this.css.insert(".emoji-keyboard .emoji { padding: 0.1em !important; cursor: pointer }");
|
||||
|
||||
// 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>';
|
||||
|
||||
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);
|
||||
|
||||
var dockedComposePanel = $(".js-docked-compose");
|
||||
|
||||
if (dockedComposePanel.length){
|
||||
dockedComposePanel.find(".cf.margin-t--12.margin-b--30").first().append(buttonHTML);
|
||||
}
|
||||
|
||||
// keyboard generation
|
||||
|
||||
this.currentKeyboard = null;
|
||||
|
||||
var hideKeyboard = () => {
|
||||
$(this.currentKeyboard).remove();
|
||||
this.currentKeyboard = null;
|
||||
|
||||
$(".emoji-keyboard-popup-btn").removeClass("is-selected");
|
||||
};
|
||||
|
||||
this.generateKeyboardFor = (input, left, top) => {
|
||||
var created = document.createElement("div");
|
||||
document.body.appendChild(created);
|
||||
|
||||
created.classList.add("emoji-keyboard");
|
||||
created.style.left = left+"px";
|
||||
created.style.top = top+"px";
|
||||
created.innerHTML = this.emojiHTML;
|
||||
|
||||
created.addEventListener("click", function(e){
|
||||
if (e.target.tagName === "IMG"){
|
||||
input.val(input.val()+e.target.getAttribute("alt"));
|
||||
input.trigger("change");
|
||||
input.focus();
|
||||
}
|
||||
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
return created;
|
||||
};
|
||||
|
||||
this.prevTryPasteImage = window.TDGF_tryPasteImage;
|
||||
var prevTryPasteImageF = this.prevTryPasteImage;
|
||||
|
||||
window.TDGF_tryPasteImage = function(){
|
||||
if (me.currentKeyboard){
|
||||
hideKeyboard();
|
||||
}
|
||||
|
||||
return prevTryPasteImageF.apply(this, arguments);
|
||||
};
|
||||
|
||||
// event handlers
|
||||
|
||||
this.emojiKeyboardButtonClickEvent = function(e){
|
||||
if (me.currentKeyboard){
|
||||
hideKeyboard();
|
||||
}
|
||||
else{
|
||||
var pos = $(this).offset();
|
||||
me.currentKeyboard = me.generateKeyboardFor($(".js-compose-text").first(), pos.left, pos.top+$(this).outerHeight()+8);
|
||||
|
||||
$(this).addClass("is-selected");
|
||||
}
|
||||
|
||||
$(this).blur();
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
this.documentClickEvent = function(e){
|
||||
if (me.currentKeyboard && !e.target.classList.contains("js-compose-text")){
|
||||
hideKeyboard();
|
||||
}
|
||||
};
|
||||
|
||||
this.documentKeyEvent = function(e){
|
||||
if (me.currentKeyboard && e.keyCode === 27){ // escape
|
||||
hideKeyboard();
|
||||
e.stopPropagation();
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* TODO
|
||||
* ----
|
||||
* add emoji search if I can be bothered
|
||||
* lazy emoji loading
|
||||
*/
|
||||
}
|
||||
|
||||
ready(){
|
||||
$(".emoji-keyboard-popup-btn").on("click", this.emojiKeyboardButtonClickEvent);
|
||||
$(document).on("click", this.documentClickEvent);
|
||||
$(document).on("keydown", this.documentKeyEvent);
|
||||
|
||||
// HTML generation
|
||||
|
||||
var convUnicode = function(codePt){
|
||||
if (codePt > 0xFFFF){
|
||||
codePt -= 0x10000;
|
||||
return String.fromCharCode(0xD800+(codePt>>10), 0xDC00+(codePt&0x3FF));
|
||||
}
|
||||
else{
|
||||
return String.fromCharCode(codePt);
|
||||
}
|
||||
};
|
||||
|
||||
$TDP.readFileRoot(this.$token, "emoji-ordering.txt").then(contents => {
|
||||
let generated = [];
|
||||
|
||||
let addDeclaration = decl => {
|
||||
generated.push(decl.split(" ").map(pt => convUnicode(parseInt(pt, 16))).join(""));
|
||||
};
|
||||
|
||||
let skinTones = [
|
||||
"1F3FB", "1F3FC", "1F3FD", "1F3FE", "1F3FF"
|
||||
];
|
||||
|
||||
for(let line of contents.split("\n")){
|
||||
if (line[0] === '@'){
|
||||
generated.push("___");
|
||||
}
|
||||
else{
|
||||
let decl = line.slice(0, line.indexOf(";"));
|
||||
let skinIndex = decl.indexOf('$');
|
||||
|
||||
if (skinIndex !== -1){
|
||||
let declPre = decl.slice(0, skinIndex);
|
||||
let declPost = decl.slice(skinIndex+1);
|
||||
|
||||
skinTones.map(skinTone => declPre+skinTone+declPost).forEach(addDeclaration);
|
||||
}
|
||||
else{
|
||||
addDeclaration(decl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let start = "<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>";
|
||||
this.emojiHTML = start+TD.util.cleanWithEmoji(generated.join("")).replace(/___/g, "<div class='separator'></div>");
|
||||
}).catch(err => {
|
||||
$TD.alert("error", "Problem loading emoji keyboard: "+err.message);
|
||||
});
|
||||
}
|
||||
|
||||
disabled(){
|
||||
this.css.remove();
|
||||
|
||||
if (this.currentKeyboard){
|
||||
$(this.currentKeyboard).remove();
|
||||
}
|
||||
|
||||
window.TDGF_tryPasteImage = this.prevTryPasteImage;
|
||||
|
||||
$(".emoji-keyboard-popup-btn").off("click", this.emojiKeyboardButtonClickEvent);
|
||||
$(".emoji-keyboard-popup-btn").remove();
|
||||
|
||||
$(document).off("click", this.documentClickEvent);
|
||||
$(document).off("keydown", this.documentKeyEvent);
|
||||
TD.mustaches["compose/docked_compose.mustache"] = this.prevComposeMustache;
|
||||
}
|
1671
Resources/Plugins/emoji-keyboard/emoji-ordering.txt
Normal file
1671
Resources/Plugins/emoji-keyboard/emoji-ordering.txt
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user