1
0
mirror of https://github.com/chylex/TweetDuck.git synced 2025-09-14 10:32:10 +02:00

Compare commits

...

149 Commits
1.3 ... 1.4.1

Author SHA1 Message Date
28cc60d007 Tweak CommandLineArgsParser to slightly improve reliability with quoted strings 2016-09-27 18:11:50 +02:00
1fa69bdb3b Add invalid data folder error reporting and env variable expansion for absolute paths 2016-09-27 18:00:05 +02:00
11f5f9b72e Add Reporter.HandleEarlyFailure for non-recoverable errors before Reporter is setup 2016-09-27 17:58:13 +02:00
61f6543041 Change Source Code link to Tips & Tricks 2016-09-27 17:37:19 +02:00
0c9ab32f37 Add additional functionality to the back mouse button (inline and drawer composer, fix order) 2016-09-27 04:16:50 +02:00
fde984d02b Add a hover class to Notification body element and only display skip button when over 2016-09-27 03:48:14 +02:00
f23db31306 Increase default size of Edit CSS dialog 2016-09-26 17:20:25 +02:00
8dce99b8b3 Add support for ctrl+a shortcut in multiline textboxes 2016-09-26 17:20:10 +02:00
342ac51cda Tweak the skip button position in Notification window 2016-09-26 17:14:17 +02:00
ba31f7ae01 Update TweetDuck website to https 2016-09-26 16:31:06 +02:00
8d0fa030f8 Remove d3dcompiler_43.dll that used to be required for Win XP 2016-09-26 16:16:20 +02:00
d030a79c81 Fix Release build compliation error 2016-09-26 16:10:21 +02:00
6690efc4d9 Address code analysis issues 2016-09-26 15:53:08 +02:00
afa8098463 Add two restart buttons to Settings - Advanced 2016-09-26 14:28:14 +02:00
c064e579d2 Add Program.Restart and fix restarting causing loss of initial program arguments 2016-09-26 14:05:44 +02:00
01dc4e4714 Update CommandLineArgsParser to use CommandLineArgs 2016-09-26 13:58:59 +02:00
6fbc246b12 Use CommandLineArgs in Program and fix broken -locale parameter 2016-09-26 13:56:13 +02:00
1efe2a02f7 Add a CommandLineArgs class for easy arg management 2016-09-26 13:52:55 +02:00
ab2ab06f60 Reorganize utility classes and methods in TweetDeck Migration code 2016-09-26 04:19:50 +02:00
a71d889682 Replace FormMigrationQuestion with a custom FormMessage 2016-09-25 23:10:18 +02:00
2252d85b27 Add MessageBoxIcon.Question support to FormMessage 2016-09-25 23:09:36 +02:00
3f09100177 Update version of the design-revert plugin 2016-09-25 19:01:39 +02:00
6b79c89f42 Add a button to skip current notification 2016-09-25 16:53:00 +02:00
5f249d4603 Add a <body> td-example-notification attribute for example notifications 2016-09-25 16:52:22 +02:00
fa64309909 Hide the old TweetDeck column Clear button when clear-columns plugin is enabled 2016-09-25 15:35:15 +02:00
c46aafdab6 Add column resetting to clear-columns plugin 2016-09-25 15:31:18 +02:00
3e9c397494 Fix inconsistency with loadConfigurationFile's onFailure parameter 2016-09-25 03:21:44 +02:00
47935165db Update current plugins to use the new CSS functionality 2016-09-25 02:37:16 +02:00
be77f753e7 Add a global function to easily add custom styles in plugins 2016-09-25 02:34:19 +02:00
2c30613279 Fix reply-account plugin for docked replies 2016-09-23 22:09:06 +02:00
d83eaec987 Update reply-account plugin version 2016-09-23 21:14:53 +02:00
e6f199a224 Implement #last in reply-account and fix bugs in its code 2016-09-23 21:13:54 +02:00
6636a2aa9e Add an experimental timeline-polls plugin 2016-09-23 19:49:56 +02:00
221bdc58fe Tweak support for old configs in reply-account plugin 2016-09-23 16:05:53 +02:00
e48a068e9d Redesign reply-account plugin configuration and add WIP last account setting 2016-09-23 15:55:13 +02:00
3371c31e63 Release 1.4 2016-09-22 00:58:27 +02:00
aa2c60f7e9 Move cookie import code to ExportManager 2016-09-20 17:15:34 +02:00
f7dc200684 Kill process instead of Environment.FailFast if possible 2016-09-20 17:11:36 +02:00
ad306c56c7 Move log file to storage path 2016-09-20 16:58:00 +02:00
86aba1eb52 Update all exception handlers 2016-09-20 16:55:51 +02:00
826f1aba7a Tweak exception handling in Reporter (use Environment.FailFast and change log button title) 2016-09-20 16:51:29 +02:00
8abf4364c6 Replace calls to Program.Log 2016-09-20 16:39:31 +02:00
4c45d91d4e Move logging to Reporter and make a static instance of it in Program 2016-09-20 16:38:15 +02:00
3176b6cb8f Create a Reporter class with improved HandleException code 2016-09-20 16:34:56 +02:00
27971e09cd Add new Control related functionality to FormMessage 2016-09-20 16:31:53 +02:00
bf95ae00de Tweak the message for missing write permission 2016-09-20 16:10:46 +02:00
0dbfa7e101 Improve reliability of directory write perm checking 2016-09-20 16:00:41 +02:00
85d09c4b5e Add a FormMessage class for custom message boxes 2016-09-20 07:47:16 +02:00
4f9bc40476 Update cache clearing for CEF 53 and clear old cache files on first startup 2016-09-19 14:03:37 +02:00
757ccbf9d3 Switch locale to English and add spell check setting
Closes #62
2016-09-19 13:33:44 +02:00
4cf9730130 Make sure the config is not loaded before old process exits when restarting 2016-09-19 13:24:23 +02:00
1dd0d70ab9 Reorganize Program.LogFile from property to readonly field 2016-09-19 13:08:27 +02:00
340eaece0f Update CefSharp to 53 and update the readme 2016-09-19 00:12:48 +02:00
c151e7cd37 Remove everything TweetDick related 2016-09-18 23:24:11 +02:00
ca0baae832 Release 1.3.3 2016-09-18 22:23:48 +02:00
478e6ed8df Move config loading and migration execution to an earlier point 2016-09-18 22:19:02 +02:00
7388eb07ca Increase process close timeouts to 20 seconds from 10 2016-09-18 21:49:41 +02:00
c38d507e50 Hopefully fix an occasional crash when importing session and restarting 2016-09-18 21:48:40 +02:00
5aee087a57 Add a Program Files warning if not running as administrator 2016-09-18 21:17:30 +02:00
0cda3702ea Change notification scroll trigger cursor position for windows without side borders 2016-09-18 21:08:09 +02:00
ce55226d0c Add error handling to logging 2016-09-18 20:50:43 +02:00
0bab0a9963 Update reply-account plugin metadata and default config warning 2016-09-18 20:29:49 +02:00
34955b7083 Rewrite Plugin default config handling and config error reporting 2016-09-18 20:29:31 +02:00
ba6ce072ac Add support for a -datafolder command line argument 2016-09-18 19:42:00 +02:00
f39fd00697 Add a TweetDick warning and migration prompt 2016-09-18 19:41:37 +02:00
58fc1be1d5 Add CefSharp version to NuGet instructions 2016-09-12 14:29:48 +02:00
1fdf9bffb6 Disable global context menu items when in textarea or text selection 2016-09-07 21:47:31 +02:00
2ad179ef8e Rename configuration file in reply-account plugin 2016-09-07 15:03:06 +02:00
d42cc5b762 Remove .js file block in Plugin folders 2016-09-07 15:01:08 +02:00
403658f622 Add config file to reply-account plugin metadata 2016-09-07 14:43:17 +02:00
6ca35685db Add button for easy Plugin config file access 2016-09-07 14:42:21 +02:00
bfc6822f69 Fix Plugins window title 2016-09-07 14:20:35 +02:00
b0386937d7 Remove lnk updater from the TweetDeck migration system 2016-09-05 21:25:25 +02:00
9436540b2f Add CultureInfo.CurrentCulture to time in logger 2016-09-05 20:45:39 +02:00
45b0ece342 Update Plugin sorting and design (separator, colors) 2016-09-05 20:44:58 +02:00
5c147d3648 Fix missing and unfocused scrollbar in Plugins window 2016-09-05 20:44:07 +02:00
8fa68c428f Add 'Open (quoted) tweet in browser' context menu items and move them down 2016-09-05 16:17:23 +02:00
e2be90191e Standardize context menu items using Chrome's style 2016-09-05 14:30:07 +02:00
339da2f1a9 Add unsupported system notification
Closes #58
2016-09-04 20:30:35 +02:00
b2cc5d50bd Address code analysis issues and fix unused parameter in CombinedFileStream.WriteToFile 2016-09-04 19:32:33 +02:00
f38b188046 Add a safety net to plugin file export in case the path is too long 2016-09-04 18:10:50 +02:00
9e45628e87 Rename 'Export/Import Settings' to Profile and add Plugin exporting 2016-09-04 16:58:03 +02:00
bf76398627 Optimize icon loading and size in Forms, fix Plugins Form title inconsistency 2016-09-04 15:32:00 +02:00
81d5728964 Add scrollbars to Edit CSS window 2016-09-04 13:57:38 +02:00
d76027558b Add a custom alert function with customizable icon and no text length limit 2016-09-04 13:19:52 +02:00
1450cc24a3 Reorganize global context menu items into a sub-menu 2016-09-04 13:10:32 +02:00
f41523c1b2 Remove redundant 'using' directives 2016-09-04 04:53:37 +02:00
4019463e68 Fix occasional incorrect location of child windows 2016-09-04 04:52:59 +02:00
5e2e239f5e Force Reload All button in Plugins window to trigger the event even without changes 2016-09-04 04:44:08 +02:00
bc7856b6c0 Tweak wording in reply-account configuration file 2016-09-04 04:39:19 +02:00
e2bba8d9e1 Update reply-account plugin to use default config and swap event order when enabled late 2016-09-04 04:36:01 +02:00
520db2c32e Rewrite loadConfigurationFile in plugins.js to accept default config file 2016-09-04 04:33:55 +02:00
da71f2de2b Add CheckFileExists function to PluginBridge 2016-09-04 04:33:13 +02:00
6dd2c6678b Add custom exception messages to file reading in PluginBridge 2016-09-04 04:01:40 +02:00
fb3d9e6d6b Add a reply-account plugin with config 2016-09-04 04:01:00 +02:00
36473c2df9 Add a global plugins.js file with loadConfigurationFile utility function 2016-09-04 03:59:37 +02:00
81aa30b2ec Allow custom .js files in plugin folders when prefixed by 'user.' 2016-09-04 03:50:35 +02:00
85d5160782 Add plugin identifier to the object itself 2016-09-04 03:38:42 +02:00
44bf7b870e Rewrote notification mouse hook to only be active if the window is visible
Closes #57
2016-09-04 00:59:13 +02:00
44da2e6082 Add debug example notification padding for scrollbar testing 2016-09-04 00:58:47 +02:00
d576bc3972 Work around Scheduled column update delay bug in TweetDeck
Closes #53
2016-09-03 16:15:28 +02:00
dbeb4c7205 Reorganize path constants and allow the program to be portable 2016-09-02 20:59:03 +02:00
e5223a852e Fix profile picture in example tweet 2016-09-02 20:58:18 +02:00
f3884315c0 Fix visual issues with clear-columns plugin (icon in temp columns, misaligned Messages icons) 2016-09-02 20:46:54 +02:00
53cd9dc9a6 Move script files to /scripts/ folder and exclude them from Visual Studio 2016-09-02 19:05:48 +02:00
a3666a7ab2 Fix formatting in JavaScript files 2016-09-02 17:35:21 +02:00
ca4eb17308 Add a clear-columns plugins 2016-09-02 17:28:32 +02:00
b729dca2e5 Seal PluginConfig and add a method to disable official plugins from config 2016-09-02 17:25:25 +02:00
21354e675a Fix TweetDeck keyboard shortcut list alignment 2016-09-02 17:17:08 +02:00
2085ddd347 Update website in design-revert plugin to https 2016-09-02 16:41:52 +02:00
31a475861b Reformat (missing space after comma), minor code tweaks 2016-09-02 13:34:41 +02:00
45bdd95dc8 Release 1.3.2 2016-08-13 13:20:13 +02:00
569fdec380 Fix middle-clicking on certain user links opening the page in TweetDuck window 2016-08-13 03:26:00 +02:00
3f15ff1c06 Fix missing default value for NotificationDurationValue causing crash with fresh install 2016-08-13 03:16:28 +02:00
506cd52255 Change the Reload context menu action to return to TD website 2016-08-12 22:30:36 +02:00
300c0c6195 Minor TweetDeck UI fixes (cut off account names and verified badge icons) 2016-08-10 17:16:39 +02:00
7689fe97b0 Fix unimplemented cache param in plugin bridge and clear cache on plugin reload 2016-08-10 16:53:26 +02:00
99ed077dbc Fix missing url word break in notifications 2016-08-10 15:48:50 +02:00
65aeb6656f Add a very basic setting for custom CSS in browser and notifications 2016-08-10 15:45:56 +02:00
e4f5766ffb Change font and check for null in CEF argument dialog 2016-08-10 15:03:15 +02:00
353ac7c76b Add setting for CEF command line args
Closes #52
2016-08-10 14:46:24 +02:00
8073fa206b Add a safety net to track bar value setting in case the config file is faulty 2016-08-10 12:47:56 +02:00
1a1cfa2220 Force visibility for fav/rt tweet actions in design-revert in detail view 2016-08-10 01:27:38 +02:00
5e93d866ad Fix missing fav/rt indicators in design-revert plugin
Closes #51
2016-08-10 01:17:26 +02:00
f29e03e250 Release 1.3.1 2016-08-06 03:35:59 +02:00
3775b5968d Fix potential memory leaks with FlatProgressBar color brush 2016-08-04 13:43:32 +02:00
ba6242e09d Change notification duration setting to a track bar 2016-07-28 21:58:42 +02:00
f6b0ddddb9 Add FlatButton for generic buttons without double border on focus 2016-07-28 21:51:49 +02:00
9e8d5c6768 Unify group boxes and layout in Settings tab 2016-07-28 15:21:29 +02:00
ef78496d5d Add notification count down toggle option 2016-07-28 14:24:43 +02:00
41a45a14df Change tray highlight to trigger on any notification and reset on window focus
Closes #33
2016-07-27 04:48:09 +02:00
5d3721ad04 Move Import/Export/Reset buttons to Advanced tab and replace them with a hint label 2016-07-26 23:45:20 +02:00
f75bcb505c Unify Settings tab size in Designer for easier manipulation 2016-07-26 23:43:48 +02:00
fa365794a0 Rewrite plugin file methods for the JS promise system 2016-07-24 21:26:24 +02:00
a747ab700e Fix blank space in Plugin Control description when none is provided 2016-07-24 19:28:17 +02:00
b51b017005 Fix plugin script generator causing JS syntax error 2016-07-24 18:14:56 +02:00
b9e1dd5950 Add combined tray icon behavior option
Closes #46
2016-07-24 16:48:26 +02:00
56bf33229b Add a legacy notification load system option 2016-07-24 16:30:58 +02:00
5740dd8c8a Add plugin bridge object to script variable list 2016-07-24 15:35:17 +02:00
d3f205287c Reset Notification Settings example timer when switching tabs 2016-07-24 15:17:08 +02:00
53518cd6e6 Change Notification Settings labels and add edge distance label 2016-07-24 15:13:51 +02:00
0ff3896d51 Add tooltips to Settings items 2016-07-24 14:27:00 +02:00
c64e16471b Add plugin utility bridge with file functionality and a plugin token system 2016-07-23 18:36:58 +02:00
2f2e2b82b8 Reset notification mouse hook variable when disposing 2016-07-11 15:55:18 +02:00
af9a503f3c Fix call order with notification scroll focus 2016-07-10 17:33:21 +02:00
971affa607 Fix unreliable unfocused notification window scroll wheel hook 2016-07-09 21:33:46 +02:00
2de5d6206e Change notification window scrollbar width and remove border radius 2016-07-09 20:54:13 +02:00
34e0bcc56a Focus notification window when mouse wheel is scrolled above it 2016-07-09 19:37:27 +02:00
bb9f09c11f Make sure all Settings controls are disposed when closing 2016-07-09 16:46:08 +02:00
91 changed files with 3455 additions and 2021 deletions

View File

@@ -4,7 +4,7 @@ using System.IO;
using System.Threading;
namespace TweetDck.Configuration{
class LockManager{
sealed class LockManager{
public Process LockingProcess { get; private set; }
private readonly string file;
@@ -20,10 +20,10 @@ namespace TweetDck.Configuration{
}
try{
lockStream = new FileStream(file,FileMode.Create,FileAccess.Write,FileShare.Read);
lockStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read);
byte[] id = BitConverter.GetBytes(Process.GetCurrentProcess().Id);
lockStream.Write(id,0,id.Length);
lockStream.Write(id, 0, id.Length);
lockStream.Flush();
if (LockingProcess != null){
@@ -48,11 +48,11 @@ namespace TweetDck.Configuration{
try{
byte[] bytes = new byte[4];
using(FileStream fileStream = new FileStream(file,FileMode.Open,FileAccess.Read,FileShare.ReadWrite)){
fileStream.Read(bytes,0,4);
using(FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)){
fileStream.Read(bytes, 0, 4);
}
int pid = BitConverter.ToInt32(bytes,0);
int pid = BitConverter.ToInt32(bytes, 0);
try{
Process foundProcess = Process.GetProcessById(pid);
@@ -90,7 +90,7 @@ namespace TweetDck.Configuration{
try{
File.Delete(file);
}catch(Exception e){
Program.Log(e.ToString());
Program.Reporter.Log(e.ToString());
result = false;
}

View File

@@ -16,7 +16,7 @@ namespace TweetDck.Configuration{
Binder = new SerializationCompatibilityHandler()
};
private const int CurrentFileVersion = 3;
private const int CurrentFileVersion = 5;
// START OF CONFIGURATION
@@ -25,20 +25,30 @@ namespace TweetDck.Configuration{
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 NotificationLegacyLoad { get; set; }
public bool EnableSpellCheck { get; set; }
public bool ExpandLinksOnHover { 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;
@@ -56,7 +66,7 @@ namespace TweetDck.Configuration{
muteNotifications = value;
if (MuteToggled != null){
MuteToggled(this,new EventArgs());
MuteToggled(this, new EventArgs());
}
}
}
@@ -72,7 +82,7 @@ namespace TweetDck.Configuration{
trayBehavior = value;
if (TrayBehaviorChanged != null){
TrayBehaviorChanged(this,new EventArgs());
TrayBehaviorChanged(this, new EventArgs());
}
}
}
@@ -99,12 +109,17 @@ namespace TweetDck.Configuration{
DisplayNotificationTimer = true;
NotificationDuration = TweetNotification.Duration.Medium;
NotificationPosition = TweetNotification.Position.TopRight;
CustomNotificationPosition = new Point(-32000,-32000);
CustomNotificationPosition = new Point(-32000, -32000);
NotificationEdgeDistance = 8;
NotificationDurationValue = 25;
EnableUpdateCheck = true;
ExpandLinksOnHover = true;
EnableTrayHighlight = true;
Plugins = new PluginConfig();
PluginsWindow = new WindowState();
Plugins.DisableOfficialFromConfig("clear-columns");
Plugins.DisableOfficialFromConfig("reply-account");
}
private void UpgradeFile(){
@@ -131,6 +146,25 @@ namespace TweetDck.Configuration{
++fileVersion;
}
if (fileVersion == 3){
EnableTrayHighlight = true;
switch(NotificationDuration){
case TweetNotification.Duration.Short: NotificationDurationValue = 15; break;
case TweetNotification.Duration.Medium: NotificationDurationValue = 25; break;
case TweetNotification.Duration.Long: NotificationDurationValue = 35; break;
case TweetNotification.Duration.VeryLong: NotificationDurationValue = 45; break;
}
++fileVersion;
}
if (fileVersion == 4){
Plugins.DisableOfficialFromConfig("clear-columns");
Plugins.DisableOfficialFromConfig("reply-account");
++fileVersion;
}
// update the version
fileVersion = CurrentFileVersion;
Save();
@@ -146,26 +180,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,9 +212,21 @@ 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);
@@ -191,14 +238,9 @@ namespace TweetDck.Configuration{
private class SerializationCompatibilityHandler : SerializationBinder{
public override Type BindToType(string assemblyName, string typeName){
#if DUCK
assemblyName = assemblyName.Replace("TweetDick","TweetDuck");
#else
assemblyName = assemblyName.Replace("TweetDuck","TweetDick");
#endif
typeName = typeName.Replace("TweetDick","TweetDck");
return Type.GetType(string.Format(CultureInfo.CurrentCulture,"{0}, {1}",typeName,assemblyName));
assemblyName = assemblyName.Replace("TweetDick", "TweetDuck");
typeName = typeName.Replace("TweetDick", "TweetDck");
return Type.GetType(string.Format(CultureInfo.CurrentCulture, "{0}, {1}", typeName, assemblyName));
}
}
}

View File

@@ -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>

View File

@@ -1,8 +1,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.2785.1478" targetFramework="net452" />
<package id="cef.redist.x86" version="3.2785.1478" targetFramework="net452" />
<package id="CefSharp.Common" version="53.0.0-pre01" targetFramework="net452" />
<package id="CefSharp.WinForms" version="53.0.0-pre01" targetFramework="net452" />
<package id="Microsoft.VC120.CRT.JetBrains" version="12.0.21005.2" targetFramework="net452" />
</packages>

View File

@@ -14,7 +14,7 @@ namespace TweetDck.Core.Controls{
}
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 +28,21 @@ 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 EnableMultilineShortcuts(this TextBox textBox){
textBox.KeyDown += (sender, args) => {
if (args.Control && args.KeyCode == Keys.A){
((TextBox)sender).SelectAll();
args.SuppressKeyPress = true;
args.Handled = true;
}
};
}
}
}

View 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);
}
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);

View File

@@ -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.Icon = Properties.Resources.icon;
this.Location = new System.Drawing.Point(-32000, -32000);
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);

View File

@@ -54,7 +54,8 @@ namespace TweetDck.Core{
this.browser.LoadingStateChanged += Browser_LoadingStateChanged;
this.browser.FrameLoadEnd += Browser_FrameLoadEnd;
this.browser.RegisterJsObject("$TD",new TweetDeckBridge(this,notification));
this.browser.RegisterJsObject("$TD", new TweetDeckBridge(this, notification));
this.browser.RegisterAsyncJsObject("$TDP", plugins.Bridge);
Controls.Add(browser);
@@ -66,13 +67,13 @@ namespace TweetDck.Core{
UpdateTrayIcon();
this.updates = new UpdateHandler(browser,this);
this.updates = new UpdateHandler(browser, this);
this.updates.UpdateAccepted += updates_UpdateAccepted;
}
private void ShowChildForm(Form form){
form.Show(this);
form.MoveToCenter(this);
form.Shown += (sender, args) => form.MoveToCenter(this);
}
private void ForceClose(){
@@ -81,25 +82,31 @@ namespace TweetDck.Core{
}
public FormNotification CreateNotificationForm(bool autoHide){
return new FormNotification(this,plugins,trayIcon,autoHide);
return new FormNotification(this, plugins, 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;
}
@@ -107,15 +114,22 @@ namespace TweetDck.Core{
private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
if (e.Frame.IsMain){
ScriptLoader.ExecuteFile(e.Frame,"code.js");
ScriptLoader.ExecuteFile(e.Frame, "code.js");
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);
}
}
}
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 +137,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);
}
}
}
@@ -145,7 +159,7 @@ 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;
}
@@ -174,11 +188,11 @@ namespace TweetDck.Core{
}
private void plugins_Reloaded(object sender, PluginLoadEventArgs e){
browser.ExecuteScriptAsync("window.location.reload()");
ReloadBrowser();
}
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){
@@ -202,12 +216,12 @@ namespace TweetDck.Core{
protected override void WndProc(ref Message m){
if (isLoaded && m.Msg == Program.WindowRestoreMessage){
trayIcon_ClickRestore(trayIcon,new EventArgs());
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;
}
@@ -223,7 +237,7 @@ namespace TweetDck.Core{
else{
bool prevEnableUpdateCheck = Config.EnableUpdateCheck;
currentFormSettings = new FormSettings(this,updates);
currentFormSettings = new FormSettings(this, plugins, updates);
currentFormSettings.FormClosed += (sender, args) => {
currentFormSettings = null;
@@ -233,6 +247,10 @@ namespace TweetDck.Core{
Config.Save();
updates.Check(false);
}
if (!Config.EnableTrayHighlight){
trayIcon.HasNotifications = false;
}
};
ShowChildForm(currentFormSettings);
@@ -261,8 +279,10 @@ namespace TweetDck.Core{
}
}
public void OnTweetSound(){
public void OnTweetNotification(){
if (Config.EnableTrayHighlight && !ContainsFocus){
trayIcon.HasNotifications = true;
}
}
public void DisplayTooltip(string text){
@@ -271,17 +291,21 @@ 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", new object[0]);
}
public void OnImagePastedFinish(){
browser.ExecuteScriptAsync("TDGF_tryPasteImageFinish",new object[0]);
browser.ExecuteScriptAsync("TDGF_tryPasteImageFinish", new object[0]);
}
public void ReloadBrowser(){
browser.ExecuteScriptAsync("window.location.reload()");
}
}
}

View File

@@ -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>

View File

@@ -21,13 +21,15 @@ namespace TweetDck.Core{
private readonly Form owner;
private readonly PluginManager plugins;
private readonly TrayIcon trayIcon;
private 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;
@@ -78,14 +80,13 @@ namespace TweetDck.Core{
}
}
public FormNotification(FormBrowser owner, PluginManager plugins, TrayIcon trayIcon, bool autoHide){
public FormNotification(FormBrowser owner, PluginManager pluginManager, bool autoHide){
InitializeComponent();
Text = Program.BrandName;
this.owner = owner;
this.plugins = plugins;
this.trayIcon = trayIcon;
this.plugins = pluginManager;
this.autoHide = autoHide;
owner.FormClosed += (sender, args) => Close();
@@ -94,13 +95,14 @@ namespace TweetDck.Core{
pluginJS = ScriptLoader.LoadResource(PluginManager.PluginNotificationScriptFile);
browser = new ChromiumWebBrowser("about:blank"){
MenuHandler = new ContextMenuNotification(this,autoHide),
MenuHandler = new ContextMenuNotification(this, autoHide),
LifeSpanHandler = new LifeSpanHandler()
};
browser.IsBrowserInitializedChanged += Browser_IsBrowserInitializedChanged;
browser.FrameLoadEnd += Browser_FrameLoadEnd;
browser.RegisterJsObject("$TD",new TweetDeckBridge(owner,this));
browser.RegisterJsObject("$TD", new TweetDeckBridge(owner, this));
browser.RegisterAsyncJsObject("$TDP", plugins.Bridge);
panelBrowser.Controls.Add(browser);
@@ -109,7 +111,9 @@ namespace TweetDck.Core{
Disposed += (sender, args) => Program.UserConfig.MuteToggled -= Config_MuteToggled;
}
Disposed += (sender, args) => browser.Dispose();
mouseHookDelegate = MouseHookProc;
Disposed += FormNotification_Disposed;
}
protected override void WndProc(ref Message m){
@@ -120,13 +124,42 @@ 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 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();
@@ -137,37 +170,34 @@ namespace TweetDck.Core{
if (Program.UserConfig.MuteNotifications){
HideNotification(true);
}
else{
if (tweetQueue.Count > 0){
else if (tweetQueue.Count > 0){
LoadNextNotification();
}
trayIcon.HasNotifications = false;
}
}
private void Browser_IsBrowserInitializedChanged(object sender, IsBrowserInitializedChangedEventArgs e){
if (e.IsBrowserInitialized && Initialized != null){
Initialized(this,new EventArgs());
Initialized(this, new EventArgs());
}
}
private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
if (!e.Frame.IsMain)return;
if (!isInitialized){
if (!isInitialized && !Program.UserConfig.NotificationLegacyLoad){
isInitialized = true;
if (Initialized != null){
Initialized(this,new EventArgs());
Initialized(this, new EventArgs());
}
}
else if (notificationJS != null && browser.Address != "about:blank"){
ScriptLoader.ExecuteScript(e.Frame,notificationJS,NotificationScriptIdentifier);
ScriptLoader.ExecuteScript(e.Frame, notificationJS, NotificationScriptIdentifier);
if (plugins.HasAnyPlugin(PluginEnvironment.Notification)){
ScriptLoader.ExecuteScript(e.Frame,pluginJS,PluginScriptIdentifier);
plugins.ExecutePlugins(e.Frame,PluginEnvironment.Notification,false);
ScriptLoader.ExecuteScript(e.Frame, pluginJS, PluginScriptIdentifier);
ScriptLoader.ExecuteFile(e.Frame, PluginManager.PluginGlobalScriptFile);
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Notification, false);
}
}
}
@@ -180,12 +210,16 @@ 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){
tweetQueue.Enqueue(notification);
trayIcon.HasNotifications = true;
}
else{
tweetQueue.Enqueue(notification);
@@ -207,13 +241,15 @@ namespace TweetDck.Core{
}
public void HideNotification(bool loadBlank){
if (loadBlank){
browser.LoadHtml("","about:blank");
if (loadBlank || Program.UserConfig.NotificationLegacyLoad){
browser.LoadHtml("", "about:blank");
}
Location = new Point(-32000,-32000);
progressBarTimer.Value = 0;
Location = new Point(-32000, -32000);
progressBarTimer.Value = Program.UserConfig.NotificationTimerCountDown ? 1000 : 0;
timerProgress.Stop();
StopMouseHook();
}
public void OnNotificationReady(){
@@ -242,10 +278,14 @@ namespace TweetDck.Core{
CurrentUrl = tweet.Url;
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);
browser.LoadHtml(tweet.GenerateHtml(), "http://tweetdeck.twitter.com/?"+DateTime.Now.Ticks);
if (Program.UserConfig.NotificationLegacyLoad){
OnNotificationReady();
}
}
private void MoveToVisibleLocation(){
@@ -255,11 +295,11 @@ namespace TweetDck.Core{
RequiresResize = false;
if (config.DisplayNotificationTimer){
ClientSize = new Size(BaseClientWidth,BaseClientHeight+4);
ClientSize = new Size(BaseClientWidth, BaseClientHeight+4);
progressBarTimer.Visible = true;
}
else{
ClientSize = new Size(BaseClientWidth,BaseClientHeight);
ClientSize = new Size(BaseClientWidth, BaseClientHeight);
progressBarTimer.Visible = false;
}
@@ -277,24 +317,24 @@ namespace TweetDck.Core{
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();
}
@@ -303,8 +343,10 @@ namespace TweetDck.Core{
}
if (needsReactivating){
NativeMethods.SetFormPos(this,NativeMethods.HWND_TOPMOST,NativeMethods.SWP_NOACTIVATE);
NativeMethods.SetFormPos(this, NativeMethods.HWND_TOPMOST, NativeMethods.SWP_NOACTIVATE);
}
StartMouseHook();
}
private void UpdateTitle(){
@@ -317,8 +359,8 @@ namespace TweetDck.Core{
}
else{
Point position = PointToClient(Cursor.Position);
position.Offset(20,5);
toolTip.Show(text,this,position);
position.Offset(20, 5);
toolTip.Show(text, this, position);
}
}
}

View 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(){}
}
}

View File

@@ -6,38 +6,38 @@ 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;
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:
Clipboard.SetText(string.IsNullOrEmpty(TweetDeckBridge.LastRightClickedLink) ? parameters.UnfilteredLinkUrl : TweetDeckBridge.LastRightClickedLink, TextDataFormat.UnicodeText);
break;
case MenuOpenImageInBrowser:
case MenuOpenImage:
BrowserUtils.OpenExternalBrowser(parameters.SourceUrl);
break;
@@ -57,15 +57,15 @@ 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);
Clipboard.SetText(parameters.SourceUrl, TextDataFormat.UnicodeText);
break;
}
@@ -84,15 +84,21 @@ namespace TweetDck.Core.Handling{
}
}
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);
}
}

View File

@@ -1,15 +1,20 @@
using CefSharp;
using System.Windows.Forms;
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 readonly FormBrowser form;
@@ -24,39 +29,47 @@ namespace TweetDck.Core.Handling{
model.Remove(CefMenuCommand.ViewSource);
RemoveSeparatorIfLast(model);
if (!string.IsNullOrEmpty(TweetDeckBridge.LastHighlightedTweet)){
model.AddItem((CefMenuCommand)MenuCopyTweetUrl,"Copy tweet address");
base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);
if (!string.IsNullOrEmpty(TweetDeckBridge.LastHighlightedTweetEmbedded)){
model.AddItem((CefMenuCommand)MenuCopyTweetEmbeddedUrl,"Copy quoted tweet address");
if (!string.IsNullOrEmpty(TweetDeckBridge.LastHighlightedTweet) && (parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){
model.AddItem((CefMenuCommand)MenuOpenTweetUrl, "Open tweet in browser");
model.AddItem((CefMenuCommand)MenuCopyTweetUrl, "Copy tweet address");
if (!string.IsNullOrEmpty(TweetDeckBridge.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);
globalMenu.AddItem(CefMenuCommand.Reload, "Reload browser");
globalMenu.AddCheckItem((CefMenuCommand)MenuMute, "Mute notifications");
globalMenu.SetChecked((CefMenuCommand)MenuMute, Program.UserConfig.MuteNotifications);
globalMenu.AddSeparator();
globalMenu.AddItem((CefMenuCommand)MenuSettings, "Settings");
globalMenu.AddItem((CefMenuCommand)MenuPlugins, "Plugins");
globalMenu.AddItem((CefMenuCommand)MenuAbout, "About "+Program.BrandName);
}
model.AddItem(CefMenuCommand.Reload,"Reload");
model.AddCheckItem((CefMenuCommand)MenuMute,"Mute notifications");
model.SetChecked((CefMenuCommand)MenuMute,Program.UserConfig.MuteNotifications);
model.AddSeparator();
model.AddItem((CefMenuCommand)MenuSettings,"Settings");
model.AddItem((CefMenuCommand)MenuPlugins,"Plugins");
model.AddItem((CefMenuCommand)MenuAbout,"About "+Program.BrandName);
}
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);
return true;
@@ -77,12 +90,20 @@ namespace TweetDck.Core.Handling{
return true;
case MenuCopyTweetUrl:
Clipboard.SetText(TweetDeckBridge.LastHighlightedTweet,TextDataFormat.UnicodeText);
case MenuOpenTweetUrl:
BrowserUtils.OpenExternalBrowser(TweetDeckBridge.LastHighlightedTweet);
return true;
case MenuCopyTweetEmbeddedUrl:
Clipboard.SetText(TweetDeckBridge.LastHighlightedTweetEmbedded,TextDataFormat.UnicodeText);
case MenuCopyTweetUrl:
Clipboard.SetText(TweetDeckBridge.LastHighlightedTweet, TextDataFormat.UnicodeText);
return true;
case MenuOpenQuotedTweetUrl:
BrowserUtils.OpenExternalBrowser(TweetDeckBridge.LastHighlightedQuotedTweet);
return true;
case MenuCopyQuotedTweetUrl:
Clipboard.SetText(TweetDeckBridge.LastHighlightedQuotedTweet, TextDataFormat.UnicodeText);
return true;
}

View File

@@ -21,30 +21,30 @@ namespace TweetDck.Core.Handling{
model.Clear();
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");
model.AddItem((CefMenuCommand)MenuCopyTweetEmbeddedUrl, "Copy quoted tweet address");
}
model.AddSeparator();
}
}
base.OnBeforeContextMenu(browserControl,browser,frame,parameters,model);
base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);
RemoveSeparatorIfLast(model);
form.InvokeSafe(() => 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;
}
@@ -58,11 +58,11 @@ namespace TweetDck.Core.Handling{
return true;
case MenuCopyTweetUrl:
Clipboard.SetText(form.CurrentUrl,TextDataFormat.UnicodeText);
Clipboard.SetText(form.CurrentUrl, TextDataFormat.UnicodeText);
return true;
case MenuCopyTweetEmbeddedUrl:
Clipboard.SetText(TweetDeckBridge.NotificationTweetEmbedded,TextDataFormat.UnicodeText);
Clipboard.SetText(TweetDeckBridge.NotificationTweetEmbedded, TextDataFormat.UnicodeText);
return true;
}
@@ -70,7 +70,7 @@ namespace TweetDck.Core.Handling{
}
public override void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame){
base.OnContextMenuDismissed(browserControl,browser,frame);
base.OnContextMenuDismissed(browserControl, browser, frame);
form.InvokeSafe(() => form.ContextMenuOpen = false);
}
}

View File

@@ -12,7 +12,7 @@ namespace TweetDck.Core.Handling{
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;

View File

@@ -10,7 +10,7 @@ namespace TweetDck.Core.Handling{
class TweetDeckBridge{
public static string LastRightClickedLink = string.Empty;
public static string LastHighlightedTweet = string.Empty;
public static string LastHighlightedTweetEmbedded = string.Empty;
public static string LastHighlightedQuotedTweet = string.Empty;
public static string NotificationTweetEmbedded = string.Empty;
public static string ClipboardImagePath = string.Empty;
@@ -41,6 +41,18 @@ namespace TweetDck.Core.Handling{
}
}
public bool HasCustomBrowserCSS{
get{
return !string.IsNullOrEmpty(Program.UserConfig.CustomBrowserCSS);
}
}
public string CustomBrowserCSS{
get{
return Program.UserConfig.CustomBrowserCSS;
}
}
public TweetDeckBridge(FormBrowser form, FormNotification notification){
this.form = form;
this.notification = notification;
@@ -62,10 +74,10 @@ namespace TweetDck.Core.Handling{
form.InvokeSafe(() => LastRightClickedLink = link);
}
public void SetLastHighlightedTweet(string link, string embeddedLink){
public void SetLastHighlightedTweet(string link, string quotedLink){
form.InvokeSafe(() => {
LastHighlightedTweet = link;
LastHighlightedTweetEmbedded = embeddedLink;
LastHighlightedQuotedTweet = quotedLink;
});
}
@@ -83,17 +95,20 @@ namespace TweetDck.Core.Handling{
public void OnTweetPopup(string tweetHtml, string tweetUrl, int tweetCharacters){
notification.InvokeSafe(() => {
notification.ShowNotification(new TweetNotification(tweetHtml,tweetUrl,tweetCharacters));
form.OnTweetNotification();
notification.ShowNotification(new TweetNotification(tweetHtml, tweetUrl, tweetCharacters));
});
}
public void OnTweetSound(){
form.InvokeSafe(form.OnTweetSound);
form.InvokeSafe(form.OnTweetNotification);
}
public void OnNotificationReady(){
if (!Program.UserConfig.NotificationLegacyLoad){
notification.InvokeSafe(notification.OnNotificationReady);
}
}
public void DisplayTooltip(string text, bool showInNotification){
if (showInNotification){
@@ -104,6 +119,10 @@ namespace TweetDck.Core.Handling{
}
}
public void LoadNextNotification(){
notification.InvokeSafe(notification.FinishCurrentTweet);
}
public void TryPasteImage(){
form.InvokeSafe(() => {
if (Clipboard.ContainsImage()){
@@ -113,12 +132,12 @@ 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);
}
}
});
@@ -128,7 +147,7 @@ namespace TweetDck.Core.Handling{
form.InvokeSafe(() => {
Point prevPos = Cursor.Position;
Cursor.Position = form.PointToScreen(new Point(offsetX,offsetY));
Cursor.Position = form.PointToScreen(new Point(offsetX, offsetY));
NativeMethods.SimulateMouseClick(NativeMethods.MouseButton.Left);
Cursor.Position = prevPos;
@@ -140,6 +159,19 @@ namespace TweetDck.Core.Handling{
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);
}

View File

@@ -18,6 +18,12 @@ namespace TweetDck.Core.Handling{
}
}
private static string CustomCSS{
get{
return @".scroll-styled-v::-webkit-scrollbar{width:8px}.scroll-styled-v::-webkit-scrollbar-thumb{border-radius:0}a[data-full-url]{word-break:break-all}#td-skip{opacity:0;cursor:pointer;transition:opacity 0.15s ease}.td-hover #td-skip{opacity:0.75}#td-skip:hover{opacity:1}";
}
}
public static int FontSizeLevel{
get{
switch(FontSizeClass){
@@ -37,14 +43,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);
}
}
@@ -73,32 +84,33 @@ 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(){
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).Append("<style type='text/css'>").Append(CustomCSS).Append("</style>");
if (!string.IsNullOrEmpty(Program.UserConfig.CustomNotificationCSS)){
build.Append("<style type='text/css'>").Append(Program.UserConfig.CustomNotificationCSS).Append("</style>");
}
build.Append("</head>");
build.Append("<body class='hearty'").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>");

View File

@@ -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;

View File

@@ -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
View 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 = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelMessage.Location = new System.Drawing.Point(62, 33);
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;
}
}

118
Core/Other/FormMessage.cs Normal file
View File

@@ -0,0 +1,118 @@
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-32, 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,
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);
}
}
}

View File

@@ -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();

View File

@@ -1,5 +1,7 @@
using System;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using TweetDck.Core.Controls;
using TweetDck.Plugins;
@@ -7,7 +9,7 @@ using TweetDck.Plugins.Controls;
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 +18,8 @@ namespace TweetDck.Core.Other{
public FormPlugins(){
InitializeComponent();
Text = Program.BrandName+" Plugins";
}
public FormPlugins(PluginManager pluginManager) : this(){
@@ -28,14 +32,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 +62,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,21 +85,34 @@ 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){
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){
Close();

View File

@@ -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>

View File

@@ -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;
}
}

View File

@@ -3,23 +3,23 @@ using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
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);
private readonly Dictionary<Type, BaseTabSettings> tabs = new Dictionary<Type, BaseTabSettings>(4);
public FormSettings(FormBrowser browserForm, UpdateHandler updates){
public FormSettings(FormBrowser browserForm, PluginManager plugins, UpdateHandler updates){
InitializeComponent();
Text = Program.BrandName+" Settings";
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.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(() => new TabSettingsAdvanced(browserForm.ReloadBrowser, plugins)));
this.tabPanel.SelectTab(tabPanel.Buttons.First());
}
@@ -30,7 +30,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{
@@ -42,66 +42,9 @@ namespace TweetDck.Core.Other{
private void FormSettings_FormClosing(object sender, FormClosingEventArgs e){
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;
}
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();
foreach(BaseTabSettings control in tabs.Values){
control.Dispose();
}
}
@@ -109,7 +52,7 @@ namespace TweetDck.Core.Other{
Close();
}
private void ReloadUI(){
public void ReloadUI(){
tabs.Clear();
tabPanel.Content.Controls.Clear();
tabPanel.ActiveButton.Callback();

View File

@@ -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>

View File

@@ -12,7 +12,13 @@ namespace TweetDck.Core.Other.Settings{
public bool Ready { get; set; }
public BaseTabSettings(){
Padding = new Padding(6,6,6,6);
Padding = new Padding(6);
}
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();
}
}
}
}

View File

@@ -0,0 +1,183 @@
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.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();
((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;
//
// 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(378, 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(9, 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.";
//
// 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.labelWarning);
this.Controls.Add(this.splitContainer);
this.Controls.Add(this.btnApply);
this.Controls.Add(this.btnCancel);
this.MinimumSize = new System.Drawing.Size(500, 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;
}
}

View File

@@ -0,0 +1,41 @@
using System;
using System.Windows.Forms;
using TweetDck.Core.Controls;
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;
}
}
public DialogSettingsCSS(){
InitializeComponent();
Text = Program.BrandName+" Settings - CSS";
textBoxBrowserCSS.EnableMultilineShortcuts();
textBoxBrowserCSS.Text = Program.UserConfig.CustomBrowserCSS ?? "";
textBoxNotificationCSS.EnableMultilineShortcuts();
textBoxNotificationCSS.Text = Program.UserConfig.CustomNotificationCSS ?? "";
}
private void btnApply_Click(object sender, EventArgs e){
DialogResult = DialogResult.OK;
Close();
}
private void btnCancel_Click(object sender, EventArgs e){
DialogResult = DialogResult.Cancel;
Close();
}
}
}

View 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;
}
}

View 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();
}
}
}

View File

@@ -4,6 +4,8 @@ using System.Text;
namespace TweetDck.Core.Other.Settings.Export{
class CombinedFileStream : IDisposable{
public const char KeySeparator = '/';
private readonly Stream stream;
public CombinedFileStream(Stream stream){
@@ -11,28 +13,31 @@ namespace TweetDck.Core.Other.Settings.Export{
}
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 +48,15 @@ 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 void Flush(){
@@ -65,6 +70,13 @@ 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);
}
}
private readonly byte[] contents;
public Entry(string identifier, byte[] contents){
@@ -73,7 +85,16 @@ 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){
// ReSharper disable once AssignNullToNotNullAttribute
Directory.CreateDirectory(Path.GetDirectoryName(path));
}
File.WriteAllBytes(path, contents);
}
}
}

View File

@@ -1,28 +1,62 @@
using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using TweetDck.Plugins;
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){
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))){
stream.WriteFile("config", Program.ConfigFilePath);
foreach(PathInfo path in EnumerateFilesRelative(plugins.PathOfficialPlugins)){
string[] split = path.Relative.Split(CombinedFileStream.KeySeparator);
if (split.Length < 3){
continue;
}
else if (split.Length == 3){
if (split[2].Equals(".meta", StringComparison.OrdinalIgnoreCase) ||
split[2].Equals("browser.js", StringComparison.OrdinalIgnoreCase) ||
split[2].Equals("notification.js", StringComparison.OrdinalIgnoreCase)){
continue;
}
}
try{
stream.WriteFile("plugin.off"+path.Relative, path.Full);
}catch(ArgumentOutOfRangeException e){
MessageBox.Show("Could not include a file in the export. "+e.Message, "Export Profile", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
foreach(PathInfo path in EnumerateFilesRelative(plugins.PathCustomPlugins)){
try{
stream.WriteFile("plugin.usr"+path.Relative, path.Full);
}catch(ArgumentOutOfRangeException e){
MessageBox.Show("Could not include a file in the export. "+e.Message, "Export Profile", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
if (includeSession){
stream.WriteFile("cookies",CookiesPath);
stream.WriteFile("cookies", CookiesPath);
}
stream.Flush();
@@ -37,23 +71,40 @@ namespace TweetDck.Core.Other.Settings.Export{
public bool Import(){
try{
using(CombinedFileStream stream = new CombinedFileStream(new FileStream(file,FileMode.Open,FileAccess.Read,FileShare.None))){
bool updatedPlugins = false;
using(CombinedFileStream stream = new CombinedFileStream(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.None))){
CombinedFileStream.Entry entry;
while((entry = stream.ReadFile()) != null){
switch(entry.Identifier){
switch(entry.KeyName){
case "config":
entry.WriteToFile(Program.ConfigFilePath);
Program.ReloadConfig();
break;
case "plugin.off":
string root = Path.Combine(plugins.PathOfficialPlugins, entry.Identifier.Split(CombinedFileStream.KeySeparator)[1]);
if (Directory.Exists(root)){
entry.WriteToFile(Path.Combine(plugins.PathOfficialPlugins, entry.Identifier.Substring(entry.KeyName.Length+1)), true);
updatedPlugins = true;
}
break;
case "plugin.usr":
entry.WriteToFile(Path.Combine(plugins.PathCustomPlugins, entry.Identifier.Substring(entry.KeyName.Length+1)), true);
updatedPlugins = true;
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));
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));
// okay to and restart, 'cookies' is always the last entry
Process.Start(Application.ExecutablePath,"-restart -importcookies");
Application.Exit();
IsRestarting = true;
Program.Restart(new string[]{ "-importcookies" });
}
break;
@@ -61,11 +112,41 @@ namespace TweetDck.Core.Other.Settings.Export{
}
}
if (updatedPlugins){
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.EnumerateFiles(root, "*.*", SearchOption.AllDirectories).Select(fullPath => new PathInfo{
Full = fullPath,
Relative = fullPath.Substring(root.Length).Replace(Path.DirectorySeparatorChar, CombinedFileStream.KeySeparator) // includes leading separator character
});
}
private class PathInfo{
public string Full { get; set; }
public string Relative { get; set; }
}
}
}

View File

@@ -23,51 +23,186 @@
/// 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.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.btnRestartLog = new System.Windows.Forms.Button();
this.btnRestart = new System.Windows.Forms.Button();
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);
//
// 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.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, 77);
this.groupApp.TabIndex = 20;
this.groupApp.TabStop = false;
this.groupApp.Text = "App";
//
// btnRestartLog
//
this.btnRestartLog.Location = new System.Drawing.Point(6, 48);
this.btnRestartLog.Name = "btnRestartLog";
this.btnRestartLog.Size = new System.Drawing.Size(171, 23);
this.btnRestartLog.TabIndex = 17;
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 installati" +
"on folder.");
this.btnRestartLog.UseVisualStyleBackColor = true;
this.btnRestartLog.Click += new System.EventHandler(this.btnRestartLog_Click);
//
// btnRestart
//
this.btnRestart.Location = new System.Drawing.Point(6, 19);
this.btnRestart.Name = "btnRestart";
this.btnRestart.Size = new System.Drawing.Size(171, 23);
this.btnRestart.TabIndex = 16;
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);
//
// 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 +211,17 @@
#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;
}
}

View File

@@ -1,14 +1,22 @@
using System;
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 browserReloadAction;
private readonly PluginManager plugins;
public TabSettingsAdvanced(Action browserReloadAction, PluginManager plugins){
InitializeComponent();
this.browserReloadAction = browserReloadAction;
this.plugins = plugins;
checkHardwareAcceleration.Checked = HardwareAcceleration.IsEnabled;
BrowserCache.CalculateCacheSize(bytes => this.InvokeSafe(() => {
@@ -27,7 +35,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 +48,120 @@ 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()){
if (form.ShowDialog(ParentForm) == DialogResult.OK){
bool hasChangedBrowser = form.BrowserCSS != Config.CustomBrowserCSS;
Config.CustomBrowserCSS = form.BrowserCSS;
Config.CustomNotificationCSS = form.NotificationCSS;
if (hasChangedBrowser && MessageBox.Show("The browser CSS has changed, do you want to reload it?", "Browser CSS Changed", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.Yes){
browserReloadAction();
}
}
}
}
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;
}
if (file != null){
Program.UserConfig.Save();
ExportManager manager = new ExportManager(file, plugins);
if (!manager.Export(saveCredentials)){
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"
}){
file = dialog.ShowDialog() == DialogResult.OK ? dialog.FileName : null;
}
if (file != null){
ExportManager manager = new ExportManager(file, plugins);
if (manager.Import()){
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 btnRestart_Click(object sender, EventArgs e){
Program.Restart();
}
private void btnRestartLog_Click(object sender, EventArgs e){
Program.Restart(new string[]{ "-log" });
}
}
}

View File

@@ -23,19 +23,30 @@
/// 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.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 +54,85 @@
//
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);
//
// 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, 86);
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.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, 71);
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 +140,11 @@
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;
}
}

View File

@@ -9,9 +9,12 @@ 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;
checkTrayHighlight.Checked = Config.EnableTrayHighlight;
}
private void checkExpandLinks_CheckedChanged(object sender, EventArgs e){
@@ -20,10 +23,23 @@ 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 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;
}
}
}

View File

@@ -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,28 @@
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.checkLegacyLoad = new System.Windows.Forms.CheckBox();
this.checkNotificationTimer = new System.Windows.Forms.CheckBox();
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
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.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 +67,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 +126,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 +187,154 @@
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, 106);
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.checkLegacyLoad);
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, 91);
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);
//
// checkLegacyLoad
//
this.checkLegacyLoad.AutoSize = true;
this.checkLegacyLoad.Location = new System.Drawing.Point(6, 67);
this.checkLegacyLoad.Name = "checkLegacyLoad";
this.checkLegacyLoad.Size = new System.Drawing.Size(139, 17);
this.checkLegacyLoad.TabIndex = 5;
this.checkLegacyLoad.Text = "Legacy Loading System";
this.toolTip.SetToolTip(this.checkLegacyLoad, "Try enabling if notifications do not display.\r\nMight cause delays and visual arti" +
"facts.");
this.checkLegacyLoad.UseVisualStyleBackColor = true;
this.checkLegacyLoad.CheckedChanged += new System.EventHandler(this.checkLegacyLoad_CheckedChanged);
//
// checkNotificationTimer
//
@@ -256,6 +345,7 @@
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);
//
@@ -267,13 +357,15 @@
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.ResumeLayout(false);
@@ -293,11 +385,17 @@
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 checkLegacyLoad;
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;
}
}

View File

@@ -1,6 +1,8 @@
using System;
using System.Globalization;
using System.Windows.Forms;
using TweetDck.Core.Handling;
using TweetDck.Core.Controls;
namespace TweetDck.Core.Other.Settings{
partial class TabSettingsNotifications : BaseTabSettings{
@@ -19,7 +21,7 @@ namespace TweetDck.Core.Other.Settings{
};
this.notification.Initialized += (sender, args) => {
this.notification.ShowNotificationForSettings(true);
this.InvokeSafe(() => this.notification.ShowNotificationForSettings(true));
};
this.notification.Show(this);
@@ -32,12 +34,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 +43,17 @@ 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;
checkLegacyLoad.Checked = Config.NotificationLegacyLoad;
trackBarEdgeDistance.SetValueSafe(Config.NotificationEdgeDistance);
labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value.ToString(CultureInfo.InvariantCulture)+" px";
Disposed += (sender, args) => this.notification.Dispose();
}
private void TabSettingsNotifications_ParentChanged(object sender, EventArgs e){
@@ -56,7 +61,7 @@ namespace TweetDck.Core.Other.Settings{
notification.HideNotification(false);
}
else{
notification.ShowNotificationForSettings(false);
notification.ShowNotificationForSettings(true);
}
}
@@ -79,30 +84,54 @@ 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);
}
private void checkLegacyLoad_CheckedChanged(object sender, EventArgs e){
if (!Ready)return;
Config.NotificationLegacyLoad = checkLegacyLoad.Checked;
}
private void comboBoxDisplay_SelectedValueChanged(object sender, EventArgs e){
if (!Ready)return;
@@ -113,6 +142,7 @@ 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);
}

View File

@@ -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;
}
}

View File

@@ -40,7 +40,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);
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}

View File

@@ -45,7 +45,7 @@ namespace TweetDck.Core.Utils{
}
};
client.DownloadFileAsync(new Uri(url),target);
client.DownloadFileAsync(new Uri(url), target);
}
}
}

View File

@@ -0,0 +1,107 @@
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 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.Remove(build.Length-1, 1).ToString();
}
}
}

View 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;
}
}
}

View File

@@ -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;

View File

@@ -16,12 +16,14 @@ namespace TweetDck.Core.Utils{
public const int SB_HORZ = 0;
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);
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);
@@ -39,8 +41,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 +71,8 @@ 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);
}
}
}

View File

@@ -0,0 +1,33 @@
using System.IO;
using System.Linq;
using System.Security.AccessControl;
using System.Security.Principal;
namespace TweetDck.Core.Utils{
static class WindowsUtils{
public static bool CheckFolderPermission(string path, FileSystemRights right){
try{
AuthorizationRuleCollection rules = Directory.GetAccessControl(path).GetAccessRules(true, true, typeof(SecurityIdentifier));
WindowsIdentity identity = WindowsIdentity.GetCurrent();
if (identity == null || identity.Groups == null){
return false;
}
bool accessAllow = false, accessDeny = false;
foreach(FileSystemAccessRule rule in rules.Cast<FileSystemAccessRule>().Where(rule => identity.Groups.Contains(rule.IdentityReference) && (right & rule.FileSystemRights) == right)){
switch(rule.AccessControlType){
case AccessControlType.Allow: accessAllow = true; break;
case AccessControlType.Deny: accessDeny = true; break;
}
}
return accessAllow && !accessDeny;
}
catch{
return false;
}
}
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
@@ -7,20 +6,52 @@ using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.Win32;
using TweetDck.Migration.Helpers;
using TweetDck.Core.Utils;
using TweetDck.Core.Other;
using System.Drawing;
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");
private static readonly string TweetDeckPathParent = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "twitter");
private static readonly string TweetDeckPath = Path.Combine(TweetDeckPathParent, "TweetDeck");
private static readonly string TweetDickStorage = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "TweetDick");
public static void Run(){
if (!Program.IsPortable && Directory.Exists(TweetDickStorage) && !Directory.Exists(Program.StoragePath)){
if (MessageBox.Show("Welcome to TweetDuck! Would you like to move your old TweetDick configuration and login data?", "TweetDick Migration", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes){
try{
Directory.Move(TweetDickStorage, Program.StoragePath);
MessageBox.Show("All done! You can now uninstall TweetDick.", "TweetDick Migration", MessageBoxButtons.OK, MessageBoxIcon.Information);
}catch(Exception ex){
Program.Reporter.HandleException("Migration Error", "An unexpected error occurred during the migration process.", true, ex);
}
}
return;
}
if (!Program.UserConfig.IgnoreMigration && Directory.Exists(TweetDeckPath)){
MigrationDecision decision;
using(FormMigrationQuestion formQuestion = new FormMigrationQuestion()){
decision = formQuestion.ShowDialog() == DialogResult.OK ? formQuestion.Decision : MigrationDecision.AskLater;
const string prompt = "Hey there, I found some TweetDeck data! Do you want to »Migrate« it and delete the old data folder, »Ignore« the prompt, or try "+Program.BrandName+" out first? You may also »Migrate && Purge« which uninstalls TweetDeck too!";
using(FormMessage formQuestion = new FormMessage("TweetDeck Migration", prompt, MessageBoxIcon.Question)){
formQuestion.AddButton("Ask Later");
Button btnIgnore = formQuestion.AddButton("Ignore");
Button btnMigrate = formQuestion.AddButton("Migrate");
Button btnMigrateAndPurge = formQuestion.AddButton("Migrate && Purge");
btnMigrateAndPurge.Location = new Point(btnMigrateAndPurge.Location.X-18, btnMigrateAndPurge.Location.Y);
btnMigrateAndPurge.Width += 18;
if (formQuestion.ShowDialog() == DialogResult.OK){
decision = formQuestion.ClickedButton == btnMigrateAndPurge ? MigrationDecision.MigratePurge :
formQuestion.ClickedButton == btnMigrate ? MigrationDecision.Migrate :
formQuestion.ClickedButton == btnIgnore ? MigrationDecision.Ignore : MigrationDecision.AskLater;
}
else{
decision = MigrationDecision.AskLater;
}
}
switch(decision){
@@ -29,11 +60,11 @@ namespace TweetDck.Migration{
FormBackgroundWork formWait = new FormBackgroundWork();
formWait.ShowWorkDialog(() => {
if (!BeginMigration(decision,ex => formWait.Invoke(new Action(() => {
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);
Program.Reporter.HandleException("Migration Error", "An unexpected error occurred during the migration process.", true, ex);
return;
}
@@ -53,10 +84,10 @@ namespace TweetDck.Migration{
}
}
else if (!Program.UserConfig.IgnoreUninstallCheck){
string guid = ProgramRegistrySearch.FindByDisplayName("TweetDeck");
string guid = MigrationUtils.FindProgramGuidByDisplayName("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);
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){
MigrationUtils.RunUninstaller(guid, 0);
CleanupTweetDeck();
}
@@ -72,8 +103,8 @@ namespace TweetDck.Migration{
Task task = new Task(() => {
Directory.CreateDirectory(Program.StoragePath);
Directory.CreateDirectory(Path.Combine(Program.StoragePath,"localStorage"));
Directory.CreateDirectory(Path.Combine(Program.StoragePath,"Local Storage"));
Directory.CreateDirectory(Path.Combine(Program.StoragePath, "localStorage"));
Directory.CreateDirectory(Path.Combine(Program.StoragePath, "Local Storage"));
CopyFile("Cookies");
CopyFile("Cookies-journal");
@@ -105,7 +136,7 @@ namespace TweetDck.Migration{
// delete folders
for(int wait = 0; wait < 50; wait++){
try{
Directory.Delete(TweetDeckPath,true);
Directory.Delete(TweetDeckPath, true);
break;
}catch(Exception){
// browser subprocess not ended yet, wait
@@ -114,48 +145,18 @@ namespace TweetDck.Migration{
}
try{
Directory.Delete(TweetDeckPathParent,false);
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");
string guid = MigrationUtils.FindProgramGuidByDisplayName("TweetDeck");
if (guid != null){
RunUninstaller(guid,5000);
MigrationUtils.RunUninstaller(guid, 5000);
}
// registry cleanup
@@ -165,7 +166,7 @@ namespace TweetDck.Migration{
}
});
task.ContinueWith(originalTask => onFinished(originalTask.Exception),TaskContinuationOptions.ExecuteSynchronously);
task.ContinueWith(originalTask => onFinished(originalTask.Exception), TaskContinuationOptions.ExecuteSynchronously);
task.Start();
return true;
@@ -173,33 +174,15 @@ namespace TweetDck.Migration{
private static void CopyFile(string relativePath){
try{
File.Copy(Path.Combine(TweetDeckPath,relativePath),Path.Combine(Program.StoragePath,relativePath),true);
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.DeleteSubKeyTree(@"Software\Twitter\TweetDeck", true);
Registry.CurrentUser.DeleteSubKey(@"Software\Twitter"); // only if empty
}catch(Exception){
// not found or too bad

View File

@@ -0,0 +1,56 @@
using System;
using System.Diagnostics;
using System.Linq;
using Microsoft.Win32;
namespace TweetDck.Migration{
static class MigrationUtils{
public 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();
}
}
public static string FindProgramGuidByDisplayName(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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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+"\"")){}
}
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;
}
}
}
}

View File

@@ -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);
}
}

View File

@@ -12,6 +12,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,14 +24,40 @@ namespace TweetDck.Plugins{
}
}
public bool HasConfig{
get{
return ConfigFile.Length > 0 && GetFullPathIfSafe(ConfigFile).Length > 0;
}
}
public string ConfigPath{
get{
return HasConfig ? Path.Combine(path, ConfigFile) : string.Empty;
}
}
public bool HasDefaultConfig{
get{
return ConfigDefault.Length > 0 && GetFullPathIfSafe(ConfigDefault).Length > 0;
}
}
public string DefaultConfigPath{
get{
return HasDefaultConfig ? Path.Combine(path, ConfigDefault) : string.Empty;
}
}
private readonly string path;
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", "*" }
};
@@ -42,16 +70,50 @@ namespace TweetDck.Plugins{
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)){
try{
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(path, file) : string.Empty;
}
else{
return string.Empty;
}
}
public string GetFullPathIfSafe(string relativePath){
string fullPath = Path.Combine(path, relativePath);
try{
string folderPathName = new DirectoryInfo(path).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;
}
@@ -66,13 +128,13 @@ namespace TweetDck.Plugins{
}
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 +143,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 +159,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 +175,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 +203,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);
}
}
}

83
Plugins/PluginBridge.cs Normal file
View File

@@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using TweetDck.Plugins.Events;
namespace TweetDck.Plugins{
class PluginBridge{
private readonly PluginManager manager;
private readonly Dictionary<string, string> fileCache = new Dictionary<string, string>(2);
public PluginBridge(PluginManager manager){
this.manager = manager;
this.manager.Reloaded += manager_Reloaded;
}
private void manager_Reloaded(object sender, PluginLoadEventArgs e){
fileCache.Clear();
}
private string GetFullPathIfSafe(int token, string path){
Plugin plugin = manager.GetPluginFromToken(token);
return plugin == null ? string.Empty : plugin.GetFullPathIfSafe(path);
}
public void WriteFile(int token, string path, string contents){
string fullPath = GetFullPathIfSafe(token, path);
if (fullPath == string.Empty){
throw new Exception("File path has to be relative to the plugin folder.");
}
// ReSharper disable once AssignNullToNotNullAttribute
Directory.CreateDirectory(Path.GetDirectoryName(fullPath));
File.WriteAllText(fullPath, contents, Encoding.UTF8);
fileCache[fullPath] = contents;
}
public string ReadFile(int token, string path, bool cache){
string fullPath = GetFullPathIfSafe(token, path);
if (fullPath.Length == 0){
throw new Exception("File path has to be relative to the plugin folder.");
}
string cachedContents;
if (cache && fileCache.TryGetValue(fullPath, out cachedContents)){
return cachedContents;
}
try{
return fileCache[fullPath] = File.ReadAllText(fullPath, Encoding.UTF8);
}catch(FileNotFoundException){
throw new Exception("File not found.");
}catch(DirectoryNotFoundException){
throw new Exception("Directory not found.");
}
}
public void DeleteFile(int token, string path){
string fullPath = GetFullPathIfSafe(token, path);
if (fullPath.Length == 0){
throw new Exception("File path has to be relative to the plugin folder.");
}
fileCache.Remove(fullPath);
File.Delete(fullPath);
}
public bool CheckFileExists(int token, string path){
string fullPath = GetFullPathIfSafe(token, path);
if (fullPath.Length == 0){
throw new Exception("File path has to be relative to the plugin folder.");
}
return File.Exists(fullPath);
}
}
}

View File

@@ -4,7 +4,7 @@ using TweetDck.Plugins.Events;
namespace TweetDck.Plugins{
[Serializable]
class PluginConfig{
sealed class PluginConfig{
[field:NonSerialized]
public event EventHandler<PluginChangedStateEventArgs> PluginChangedState;
@@ -25,7 +25,7 @@ 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));
PluginChangedState(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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -10,23 +10,28 @@ 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"); } }
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;
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.Config = config;
this.Bridge = new PluginBridge(this);
}
public IEnumerable<Plugin> GetPluginsByGroup(PluginGroup group){
@@ -41,28 +46,33 @@ namespace TweetDck.Plugins{
return plugins.Any(plugin => plugin.Environments.HasFlag(environment));
}
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 +88,24 @@ namespace TweetDck.Plugins{
continue;
}
ScriptLoader.ExecuteScript(frame,PluginScriptGenerator.GeneratePlugin(plugin.Identifier,script,environment),"plugin:"+plugin);
int token;
if (tokens.ContainsValue(plugin)){
token = tokens.First(kvp => kvp.Value.Equals(plugin)).Key;
}
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)){
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 +115,17 @@ namespace TweetDck.Plugins{
}
}
}
private int GenerateToken(){
for(int attempt = 0; attempt < 1000; attempt++){
int token = rand.Next();
if (!tokens.ContainsKey(token)){
return token;
}
}
return -tokens.Count;
}
}
}

View File

@@ -3,18 +3,22 @@
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(");");

View File

@@ -1,8 +1,6 @@
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text;
using System.Windows.Forms;
using CefSharp;
using TweetDck.Configuration;
@@ -14,42 +12,42 @@ using System.Threading;
using TweetDck.Plugins;
using TweetDck.Plugins.Events;
using TweetDck.Core.Other.Settings.Export;
using TweetDck.Core.Handling;
using System.Security.AccessControl;
[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.4";
public const string VersionFull = "1.4.0.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 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 +56,49 @@ namespace TweetDck{
WindowRestoreMessage = NativeMethods.RegisterWindowMessage("TweetDuckRestore");
string[] programArguments = Environment.GetCommandLineArgs();
if (!WindowsUtils.CheckFolderPermission(ProgramPath, FileSystemRights.WriteData)){
MessageBox.Show(BrandName+" does not have write permissions to the program folder. If it is installed in Program Files, please run it as Administrator.", "Administrator Required", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
if (programArguments.Contains("-restart")){
for(int attempt = 0; attempt < 21; attempt++){
Reporter = new Reporter(LogFilePath);
AppDomain.CurrentDomain.UnhandledException += (sender, args) => {
Exception ex = args.ExceptionObject as Exception;
if (ex != null){
Reporter.HandleException(BrandName+" Has Failed :(", "An unhandled exception has occurred.", false, ex);
}
};
if (Args.HasFlag("-restart")){
for(int attempt = 0; attempt < 41; attempt++){
if (LockManager.Lock()){
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 (attempt == 40){
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);
return;
}
else{
Thread.Sleep(500);
}
}
ReloadConfig();
}
else{
ReloadConfig();
MigrationManager.Run();
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);
NativeMethods.SendMessage(NativeMethods.HWND_BROADCAST, WindowRestoreMessage, 0, IntPtr.Zero);
return;
}
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);
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(20000)){
MessageBox.Show("Could not close the other process.", BrandName+" Has Failed :(", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
@@ -92,51 +108,30 @@ namespace TweetDck{
}
}
if (programArguments.Contains("-importcookies") && File.Exists(ExportManager.TempCookiesPath)){
try{
if (File.Exists(ExportManager.CookiesPath)){
File.Delete(ExportManager.CookiesPath);
if (Args.HasFlag("-importcookies")){
ExportManager.ImportCookies();
}
File.Move(ExportManager.TempCookiesPath,ExportManager.CookiesPath);
}catch(Exception e){
HandleException("Could not import the cookie file to restore login session.",e);
}
}
BrowserCache.ClearOldCacheFiles();
ReloadConfig();
MigrationManager.Run();
Cef.OnContextInitialized = () => {
using(IRequestContext ctx = Cef.GetGlobalRequestContext()){
string err;
ctx.SetPreference("browser.enable_spellchecking",false,out err);
}
};
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",
#if !DEBUG
LogSeverity = programArguments.Contains("-log") ? LogSeverity.Info : LogSeverity.Disable
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);
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.Reload();
@@ -147,39 +142,32 @@ namespace TweetDck{
if (mainForm.UpdateInstallerPath != null){
ExitCleanup();
Process.Start(mainForm.UpdateInstallerPath,"/SP- /SILENT /NOICONS /CLOSEAPPLICATIONS");
Process.Start(mainForm.UpdateInstallerPath, "/SP- /SILENT /NOICONS /CLOSEAPPLICATIONS");
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);
}
}
public static void HandleException(string message, Exception e){
Log(e.ToString());
private static string GetDataStoragePath(){
string custom = Args.GetValue("-datafolder", null);
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);
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);
}
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");
return Environment.ExpandEnvironmentVariables(custom);
}
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 +180,35 @@ 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();
}
public static void Restart(){
Restart(new string[0]);
}
public static void Restart(string[] extraArgs){
CommandLineArgs args = Args.Clone();
args.AddFlag("-restart");
args.RemoveFlag("-importcookies");
CommandLineArgs.ReadStringArray('-', extraArgs, args);
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

View File

@@ -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. For **CefSharp**, you will need version 53 or newer currently available as a pre-release.
```
PM> Install-Package CefSharp.WinForms -Pre
PM> Install-Package CefSharp.WinForms -Pre -Version 53.0.0-pre01
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 **_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**.

89
Reporter.cs Normal file
View File

@@ -0,0 +1,89 @@
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 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){
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,
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) => 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));
}
}
}
}

View 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
[website]
https://tweetduck.chylex.com
[requires]
1.4.1

View 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 19',
'</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-hl" data-title="Clear all" data-action="td-clearcolumns-doall">',
'<div class="obj-left"><i class="icon icon-large icon-clear-timeline"></i></div>',
'<div id="clear-columns-btn-all" class="nbfc padding-ts hide-condensed">Clear all</div>',
'</a></nav>'
].join(""));
}
disabled(){
// not needed, plugin reloads the page when enabled or disabled
}

View File

@@ -9,7 +9,10 @@ Revert TweetDeck design changes
chylex
[version]
1.0
1.1
[website]
http://tweetduck.chylex.com
https://tweetduck.chylex.com
[requires]
1.4.1

View File

@@ -1,21 +1,19 @@
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);
this.css = window.TDPF_createCustomStyle(this);
this.css.insert(".tweet-actions { float: right !important; width: auto !important; }");
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; }");
this.css.insert(".tweet-actions > li:nth-child(4) { margin-right: 2px !important; }");
// revert small links around the tweet
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>');
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;
// fix layout for right-aligned actions menu
@@ -25,11 +23,12 @@ enabled(){
}
ready(){
$(document).on("uiShowActionsMenu",this.uiShowActionsMenuEvent);
$(document).on("uiShowActionsMenu", this.uiShowActionsMenuEvent);
}
disabled(){
$("#design-revert").remove();
$(document).off("uiShowActionsMenu",this.uiShowActionsMenuEvent);
this.css.remove();
$(document).off("uiShowActionsMenu", this.uiShowActionsMenuEvent);
TD.mustaches["status/tweet_single_footer.mustache"] = this.prevFooterMustache;
}

View File

@@ -0,0 +1,23 @@
[name]
Custom reply account
[description]
- Allows customizing the automatically selected reply account per column
[author]
chylex
[version]
1.1
[website]
https://tweetduck.chylex.com
[configfile]
configuration.js
[configdefault]
configuration.default.js
[requires]
1.3.3

View File

@@ -0,0 +1,120 @@
enabled(){
var configuration = { defaultAccount: "#preferred" };
window.TDPF_loadConfigurationFile(this, "configuration.js", "configuration.default.js", obj => configuration = obj);
this.lastSelectedAccount = null;
this.uiComposeTweetEvent = (e, data) => {
if (data.type !== "reply"){
return;
}
var query;
if (configuration.useAdvancedSelector){
if (configuration.customSelector){
var column = TD.controller.columnManager.get(data.element.closest("section.column").attr("data-column"));
query = configuration.customSelector(column);
}
else{
$TD.alert("warning", "Plugin reply-account has invalid configuration: useAdvancedSelector is true, but customSelector function is missing");
return;
}
}
else{
query = configuration.defaultAccount;
if (query === ""){
query = "#preferred";
}
else if (typeof query !== "string"){
query = "#default";
}
}
if (typeof query === "undefined"){
query = "#preferred";
}
if (typeof query !== "string"){
return;
}
else if (query.length === 0){
$TD.alert("warning", "Plugin reply-account has invalid configuration: the requested account is empty");
return;
}
else if (query[0] !== '@' && query[0] !== '#'){
$TD.alert("warning", "Plugin reply-account has invalid configuration: the requested account does not begin with @ or #: "+query);
return;
}
var identifier = null;
switch(query){
case "#preferred":
identifier = TD.storage.clientController.client.getDefaultAccount();
break;
case "#last":
if (this.lastSelectedAccount === null){
return;
}
identifier = this.lastSelectedAccount;
break;
case "#default":
return;
default:
if (query[0] === '@'){
var obj = TD.storage.accountController.getAccountFromUsername(query.substring(1));
if (obj.length === 0){
$TD.alert("warning", "Plugin reply-account has invalid configuration: requested account not found: "+query);
return;
}
else{
identifier = obj[0].privateState.key;
}
}
else{
$TD.alert("warning", "Plugin reply-account has invalid configuration: unknown requested account query: "+query);
return;
}
}
data.singleFrom = data.from = [ identifier ];
};
this.onSelectedAccountChanged = () => {
var selected = $(".js-account-item.is-selected", ".js-account-list");
this.lastSelectedAccount = selected.length === 1 ? selected.attr("data-account-key") : null;
};
}
ready(){
var events = $._data(document, "events");
for(var event of [ "uiInlineComposeTweet", "uiDockedComposeTweet" ]){
$(document).on(event, this.uiComposeTweetEvent);
var handlers = events[event];
var newHandler = handlers[handlers.length-1];
for(var index = handlers.length-1; index > 0; index--){
handlers[index] = handlers[index-1];
}
handlers[0] = newHandler;
}
$(document).on("click", ".js-account-list .js-account-item", this.onSelectedAccountChanged);
}
disabled(){
$(document).off("uiInlineComposeTweet", this.uiComposeTweetEvent);
$(document).off("uiDockedComposeTweet", this.uiComposeTweetEvent);
$(document).off("click", ".js-account-list .js-account-item", this.onSelectedAccountChanged);
}

View File

@@ -0,0 +1,80 @@
{
/*
* WARNING
* -------
*
* Make sure you are editing 'configuration.js' and not the default configuration file, as the default one will be replaced with each update.
*
*/
/*
* Simple way of configuring the plugin
* ------------------------------------
*
* Set value of 'defaultAccount' to one of the following values:
*
* "#preferred" to use your preferred TweetDeck account (the one used to log into TweetDeck)
* "#last" to specify the account that was selected last time (only updates if a single account is selected)
* "#default" to fall back to default TweetDeck behavior; useful for advanced configuration below, otherwise disable the plugin instead
* "@myAccount" to specify an account name to use; has to be one of your registered account names
*
*/
defaultAccount: "#preferred",
/*
* Advanced way of configuring the plugin
* --------------------------------------
*
* This assumes a basic knowledge of JavaScript and jQuery.
*
* 1. Set value of 'useAdvancedSelector' to true
* 2. Uncomment the 'customSelector' function, and replace the example code with your desired behavior
*
* The 'customSelector' function should return a string in one of the formats supported by 'defaultAccount'.
* If it returns anything else (for example, false or undefined), it falls back to 'defaultAccount' behavior.
*
* The 'column' parameter is a TweetDeck column object. If you want to see all properties of the object, open your browser, nagivate to TweetDeck,
* log in, and run the following code in your browser console, which will return an object containing all of the column objects mapped to their IDs:
* TD.controller.columnManager.getAll()
*
* The example below shows how to extract the column type, title, and account from the object.
* Column type is prefixed with col_, and may be one of the following:
*
* col_timeline, col_interactions, col_mentions, col_followers, col_search, col_list,
* col_customtimeline, col_messages, col_usertweets, col_favorites, col_activity,
* col_dataminr, col_home, col_me, col_inbox, col_scheduled, col_unknown
*
* Some of these appear to be unused (for example, Home columns are 'col_timeline' instead of 'col_home').
*
* If you want to see your column types, run this in your browser console:
* Object.values(TD.controller.columnManager.getAll()).forEach(obj => console.log(obj.getColumnType()));
*
* You can also get the jQuery column object using: $("section.column[data-column='"+column.ui.state.columnKey+"']")
*
*/
useAdvancedSelector: false,
/*customSelector: function(column){
var titleObj = $(column.getTitleHTML());
var columnType = column.getColumnType(); // col_timeline
var columnTitle = titleObj.siblings(".column-head-title").text(); // Home
var columnAccount = titleObj.siblings(".attribution").text(); // @chylexmc
if (columnType === "col_search" && columnTitle === "TweetDuck"){
// This is a search column that looks for 'TweetDuck' in the tweets,
// search columns are normally linked to the preferred account
// so this forces the @TryTweetDuck account to be used instead.
return "@TryTweetDuck";
}
else if (columnType === "col_timeline" && columnAccount === "@chylexcz"){
// This is a Home column of my test account @chylexcz,
// but I want to reply to tweets from my official account.
return "@chylexmc";
}
// otherwise returns 'undefined' which falls back to 'defaultAccount' behavior
}*/
}

View File

@@ -0,0 +1,18 @@
[name]
Polls in timelines
[description]
- Adds poll result display directly into timelines
- Experimental, may be buggy or break when TweetDeck updates
[author]
chylex
[version]
1.0
[website]
https://tweetduck.chylex.com
[requires]
1.4.1

View File

@@ -0,0 +1,30 @@
constructor(){
super({
requiresPageReload: true
});
}
enabled(){
// add a stylesheet
this.css = window.TDPF_createCustomStyle(this);
this.css.insert(".column-detail .timeline-poll-container { display: none }");
// setup layout injecting
this.prevMustaches = {};
var injectLayout = (mustache, onlyIfNotFound, search, replace) => {
if (TD.mustaches[mustache].indexOf(onlyIfNotFound) === -1){
this.prevMustaches[mustache] = TD.mustaches[mustache];
TD.mustaches[mustache] = TD.mustaches[mustache].replace(search, replace);
}
};
// add poll rendering to tweets
injectLayout("status/tweet_single.mustache", "status/poll", "{{/quotedTweetMissing}} {{#translation}}", "{{/quotedTweetMissing}} <div class='timeline-poll-container'>{{>duck/tweet_single/poll}}</div> {{#translation}}");
TD.mustaches["duck/tweet_single/poll.mustache"] = '<div class="js-poll margin-tl"> {{#poll}} <ul class="margin-b--12"> {{#choices}} <li class="position-rel margin-b--8 height-3"> <div class="poll-bar pin-top height-p--100 br-1 {{#isWinner}}poll-bar--winner{{/isWinner}} {{#hasTimeLeft}}br-left{{/hasTimeLeft}} width-p--{{percentage}}"/> <div class="poll-label position-rel padding-a--4"> <span class="txt-bold txt-right inline-block width-5 padding-r--4">{{percentage}}%</span> {{{label}}} {{#isSelectedChoice}} <i class="icon icon-check txt-size-variable--11"></i> {{/isSelectedChoice}} </div> </li> {{/choices}} </ul> <span class="inline-block txt-small padding-ls txt-seamful-deep-gray"> {{{prettyCount}}} &middot; {{#hasTimeLeft}} {{{prettyTimeLeft}}} {{/hasTimeLeft}} {{^hasTimeLeft}} {{_i}}Final results{{/i}} {{/hasTimeLeft}} </span> {{/poll}} </div>';
}
disabled(){
this.css.remove();
Object.keys(this.prevMustaches).forEach(mustache => TD.mustaches[mustache] = this.prevMustaches[mustache]);
}

View File

@@ -11,33 +11,33 @@ namespace TweetDck.Resources{
public static string LoadResource(string name){
try{
return File.ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory,name),Encoding.UTF8);
return File.ReadAllText(Path.Combine(Program.ScriptPath, name), Encoding.UTF8);
}catch(Exception ex){
MessageBox.Show("Unfortunately, "+Program.BrandName+" could not load the "+name+" file. The program will continue running with limited functionality.\r\n\r\n"+ex.Message,Program.BrandName+" Has Failed :(",MessageBoxButtons.OK,MessageBoxIcon.Error);
MessageBox.Show("Unfortunately, "+Program.BrandName+" could not load the "+name+" file. The program will continue running with limited functionality.\r\n\r\n"+ex.Message, Program.BrandName+" Has Failed :(", MessageBoxButtons.OK, MessageBoxIcon.Error);
return null;
}
}
public static void ExecuteFile(ChromiumWebBrowser browser, string file){
ExecuteScript(browser,LoadResource(file),GetRootIdentifier(file));
ExecuteScript(browser, LoadResource(file), GetRootIdentifier(file));
}
public static void ExecuteFile(IFrame frame, string file){
ExecuteScript(frame,LoadResource(file),GetRootIdentifier(file));
ExecuteScript(frame, LoadResource(file), GetRootIdentifier(file));
}
public static void ExecuteScript(ChromiumWebBrowser browser, string script, string identifier){
if (script == null)return;
using(IFrame frame = browser.GetMainFrame()){
frame.ExecuteJavaScriptAsync(script,UrlPrefix+identifier,1);
frame.ExecuteJavaScriptAsync(script, UrlPrefix+identifier, 1);
}
}
public static void ExecuteScript(IFrame frame, string script, string identifier){
if (script == null)return;
frame.ExecuteJavaScriptAsync(script,UrlPrefix+identifier,1);
frame.ExecuteJavaScriptAsync(script, UrlPrefix+identifier, 1);
}
public static string GetRootIdentifier(string file){

View File

@@ -1,4 +1,4 @@
(function($,$TD,TD){
(function($, $TD, TD){
//
// Variable: Current highlighted column jQuery object.
//
@@ -27,7 +27,7 @@
var buttons = menu.children("[data-std]");
buttons.on("click","a",function(){
buttons.on("click", "a", function(){
var action = $(this).attr("data-action");
if (action === "td-settings"){
@@ -40,25 +40,27 @@
buttons.hover(function(){
$(this).addClass("is-selected");
},function(){
}, function(){
$(this).removeClass("is-selected");
});
},0);
}, 0);
});
// Notification handling
$.subscribe("/notifications/new",function(obj){
$.subscribe("/notifications/new", function(obj){
for(let index = obj.items.length-1; index >= 0; index--){
onNewTweet(obj.column,obj.items[index]);
onNewTweet(obj.column, obj.items[index]);
}
});
// Setup video element replacement
// Setup video element replacement and fix missing target in user links
new MutationObserver(function(){
$("video").each(function(){
$(this).parent().replaceWith("<a href='"+$(this).attr("src")+"' rel='url' target='_blank' style='display:block; border:1px solid #555; padding:3px 6px'>&#9658; Open video in browser</a>");
});
}).observe($(".js-app-columns")[0],{
$("a[rel='user']").attr("target", "_blank");
}).observe($(".js-app-columns")[0], {
childList: true,
subtree: true
});
@@ -79,7 +81,7 @@
//
var prependToFunction = function(func, extension){
return function(){
return extension.apply(this,arguments) === true ? undefined : func.apply(this,arguments);
return extension.apply(this, arguments) === true ? undefined : func.apply(this, arguments);
};
};
@@ -88,8 +90,8 @@
//
var appendToFunction = function(func, extension){
return function(){
var res = func.apply(this,arguments);
extension.apply(this,arguments);
var res = func.apply(this, arguments);
extension.apply(this, arguments);
return res;
};
};
@@ -105,12 +107,12 @@
withMediaPreview: false
}));
html.css("border","0");
html.css("border", "0");
html.find(".tweet-body").first().children("footer").remove();
var url = html.find("time").first().children("a").first().attr("href") || "";
$TD.onTweetPopup(html.html(),url,tweet.text.length); // TODO column
$TD.onTweetPopup(html.html(), url, tweet.text.length); // TODO column
}
else if (column.model.getHasSound()){
$TD.onTweetSound(); // TODO disable original
@@ -142,7 +144,7 @@
else if (!window.TD_APP_READY && !app.hasClass("is-hidden")){
initializeTweetDck();
}
}).observe(app[0],{
}).observe(app[0], {
attributes: true,
attributeFilter: [ "class" ]
});
@@ -150,14 +152,14 @@
//
// Block: Hook into settings object to detect when the settings change.
//
TD.settings.setFontSize = appendToFunction(TD.settings.setFontSize,function(name){
TD.settings.setFontSize = appendToFunction(TD.settings.setFontSize, function(name){
$TD.loadFontSizeClass(name);
});
TD.settings.setTheme = appendToFunction(TD.settings.setTheme,function(){
TD.settings.setTheme = appendToFunction(TD.settings.setTheme, function(){
setTimeout(function(){
$TD.loadNotificationHeadContents(getNotificationHeadContents());
},0);
}, 0);
});
//
@@ -182,7 +184,7 @@
var prevMouseX = -1, prevMouseY = -1;
var tooltipTimer, tooltipDisplayed;
$(document.body).delegate("a[data-full-url]","mouseenter mouseleave mousemove",function(e){
$(document.body).delegate("a[data-full-url]", "mouseenter mouseleave mousemove", function(e){
var me = $(this);
if (e.type === "mouseenter"){
@@ -195,19 +197,19 @@
if ($TD.expandLinksOnHover){
tooltipTimer = window.setTimeout(function(){
var expanded = me.attr("data-full-url");
expanded = cutStart(expanded,"https://");
expanded = cutStart(expanded,"http://");
expanded = cutStart(expanded,"www.");
expanded = cutStart(expanded, "https://");
expanded = cutStart(expanded, "http://");
expanded = cutStart(expanded, "www.");
me.attr("td-prev-text",text);
me.attr("td-prev-text", text);
me.text(expanded);
},200);
}, 200);
}
else{
tooltipTimer = window.setTimeout(function(){
$TD.displayTooltip(me.attr("data-full-url"),false);
$TD.displayTooltip(me.attr("data-full-url"), false);
tooltipDisplayed = true;
},400);
}, 400);
}
}
else if (e.type === "mouseleave"){
@@ -223,12 +225,12 @@
if (tooltipDisplayed){
tooltipDisplayed = false;
$TD.displayTooltip(null,false);
$TD.displayTooltip(null, false);
}
}
else if (e.type === "mousemove"){
if (tooltipDisplayed && (prevMouseX !== e.clientX || prevMouseY !== e.clientY)){
$TD.displayTooltip(me.attr("data-full-url"),false);
$TD.displayTooltip(me.attr("data-full-url"), false);
prevMouseX = e.clientX;
prevMouseY = e.clientY;
}
@@ -239,7 +241,7 @@
//
// Block: Allow bypassing of t.co in context menus.
//
$(document.body).delegate("a","contextmenu",function(){
$(document.body).delegate("a", "contextmenu", function(){
$TD.setLastRightClickedLink($(this).attr("data-full-url") || "");
});
@@ -249,7 +251,7 @@
(function(){
var soundEle = document.getElementById("update-sound");
soundEle.play = prependToFunction(soundEle.play,function(){
soundEle.play = prependToFunction(soundEle.play, function(){
return $TD.muteNotifications;
});
})();
@@ -261,7 +263,7 @@
//
// Block: Update highlighted column.
//
app.delegate("section","mouseenter mouseleave",function(e){
app.delegate("section", "mouseenter mouseleave", function(e){
if (e.type === "mouseenter"){
highlightedColumnEle = $(this);
}
@@ -278,23 +280,23 @@
var updateHighlightedTweet = function(link, embeddedLink){
if (lastTweet !== link){
$TD.setLastHighlightedTweet(link,embeddedLink);
$TD.setLastHighlightedTweet(link, embeddedLink);
lastTweet = link;
}
};
app.delegate("article.js-stream-item","mouseenter mouseleave",function(e){
app.delegate("article.js-stream-item", "mouseenter mouseleave", function(e){
if (e.type === "mouseenter"){
highlightedTweetEle = $(this);
var link = $(this).find("time").first().children("a").first();
var embedded = $(this).find(".quoted-tweet[data-tweet-id]").first();
updateHighlightedTweet(link.length > 0 ? link.attr("href") : "",embedded.length > 0 ? embedded.find(".account-link").first().attr("href")+"/status/"+embedded.attr("data-tweet-id") : "");
updateHighlightedTweet(link.length > 0 ? link.attr("href") : "", embedded.length > 0 ? embedded.find(".account-link").first().attr("href")+"/status/"+embedded.attr("data-tweet-id") : "");
}
else if (e.type === "mouseleave"){
highlightedTweetEle = null;
updateHighlightedTweet("","");
updateHighlightedTweet("", "");
}
});
})();
@@ -320,10 +322,10 @@
scroller.scrollTop(button.offset().top); // scrolls the button into view
var buttonPos = button.children().first().offset(); // finds the camera icon offset
$TD.clickUploadImage(Math.floor(buttonPos.left),Math.floor(buttonPos.top));
$TD.clickUploadImage(Math.floor(buttonPos.left), Math.floor(buttonPos.top));
};
$(".js-app").delegate(".js-compose-text,.js-reply-tweetbox","paste",function(){
$(".js-app").delegate(".js-compose-text,.js-reply-tweetbox", "paste", function(){
lastPasteElement = $(this);
$TD.tryPasteImage();
});
@@ -353,7 +355,7 @@
else if (++counter >= 10){
clearInterval(interval);
}
},51);
}, 51);
}
else{
clickUpload();
@@ -367,7 +369,7 @@
setTimeout(function(){
getScroller().scrollTop(prevScrollTop);
$(".js-drawer").find(".js-compose-text").first()[0].focus();
},10);
}, 10);
};
})();
@@ -376,13 +378,19 @@
//
window.TDGF_onMouseClickExtra = function(button){
if (button === 1){ // back button
var modal = $("#open-modal");
var inlineComposer, drawerComposer, modal;
if (highlightedColumnEle && highlightedColumnEle.closest(".js-column").is(".is-shifted-1")){
if ((modal = $("#open-modal")).is(":visible")){
modal.find("a[rel=dismiss]").click();
}
else if ((inlineComposer = $(".js-inline-compose-close")).length === 1){
inlineComposer.click();
}
else if (highlightedColumnEle && highlightedColumnEle.closest(".js-column").is(".is-shifted-1")){
highlightedColumnEle.find(".js-column-back").first().click();
}
else if (modal.is(":visible")){
modal.find("a[rel=dismiss]").click();
else if ((drawerComposer = $(".js-app-content.is-open .js-drawer-close:visible")).length === 1){
drawerComposer.click();
}
else{
$(".js-column-back").click();
@@ -395,16 +403,37 @@
}
};
//
// Block: Fix scheduled tweets not showing up sometimes.
//
$(document).on("dataTweetSent", function(e, data){
if (data.response.state && data.response.state === "scheduled"){
var column = Object.values(TD.controller.columnManager.getAll()).find(column => column.model.state.type === "scheduled");
if (column){
setTimeout(function(){
column.reloadTweets();
}, 1000);
}
}
});
//
// Block: Inject custom CSS and layout into the page.
//
(function(){
var style = document.createElement("style");
document.head.appendChild(style);
var styleOfficial = document.createElement("style");
document.head.appendChild(styleOfficial);
var sheet = style.sheet;
styleOfficial.sheet.insertRule("a[data-full-url] { word-break: break-all; }", 0); // break long urls
styleOfficial.sheet.insertRule(".column-nav-link .attribution { position: absolute; }", 0); // fix cut off account names
styleOfficial.sheet.insertRule(".txt-base-smallest .badge-verified:before { height: 13px !important; }", 0); // fix cut off badge icon
styleOfficial.sheet.insertRule(".keyboard-shortcut-list { vertical-align: top; }", 0); // fix keyboard navigation alignment
// break long urls
sheet.insertRule("a[data-full-url] { word-break: break-all; }",0);
if ($TD.hasCustomBrowserCSS){
var styleCustom = document.createElement("style");
styleCustom.innerHTML = $TD.customBrowserCSS;
document.head.appendChild(styleCustom);
}
})();
})($,$TD,TD);
})($, $TD, TD);

View File

@@ -9,14 +9,14 @@
//
var addEventListener = function(collection, type, listener){
for(let index = 0; index < collection.length; index++){
collection[index].addEventListener(type,listener);
collection[index].addEventListener(type, listener);
}
};
//
// Block: Hook into links to bypass default open function.
//
addEventListener(links,"click",function(e){
addEventListener(links, "click", function(e){
$TD.openBrowser(e.currentTarget.getAttribute("href"));
e.preventDefault();
});
@@ -24,7 +24,7 @@
//
// Block: Allow bypassing of t.co in context menus.
//
addEventListener(links,"contextmenu",function(e){
addEventListener(links, "contextmenu", function(e){
$TD.setLastRightClickedLink(e.currentTarget.getAttribute("data-full-url") || "");
});
@@ -39,7 +39,7 @@
var prevMouseX = -1, prevMouseY = -1;
var tooltipTimer, tooltipDisplayed;
addEventListener(links,"mouseenter",function(e){
addEventListener(links, "mouseenter", function(e){
var me = e.currentTarget;
var url = me.getAttribute("data-full-url");
@@ -54,23 +54,23 @@
if ($TD.expandLinksOnHover){
tooltipTimer = window.setTimeout(function(){
var expanded = url;
expanded = cutStart(expanded,"https://");
expanded = cutStart(expanded,"http://");
expanded = cutStart(expanded,"www.");
expanded = cutStart(expanded, "https://");
expanded = cutStart(expanded, "http://");
expanded = cutStart(expanded, "www.");
me.setAttribute("td-prev-text",text);
me.setAttribute("td-prev-text", text);
me.innerHTML = expanded;
},200);
}, 200);
}
else{
tooltipTimer = window.setTimeout(function(){
$TD.displayTooltip(url,true);
$TD.displayTooltip(url, true);
tooltipDisplayed = true;
},400);
}, 400);
}
});
addEventListener(links,"mouseleave",function(e){
addEventListener(links, "mouseleave", function(e){
if (!e.currentTarget.hasAttribute("data-full-url"))return;
if ($TD.expandLinksOnHover){
@@ -85,16 +85,16 @@
if (tooltipDisplayed){
tooltipDisplayed = false;
$TD.displayTooltip(null,true);
$TD.displayTooltip(null, true);
}
});
addEventListener(links,"mousemove",function(e){
addEventListener(links, "mousemove", function(e){
if (tooltipDisplayed && (prevMouseX !== e.clientX || prevMouseY !== e.clientY)){
var url = e.currentTarget.getAttribute("data-full-url");
if (!url)return;
$TD.displayTooltip(url,true);
$TD.displayTooltip(url, true);
prevMouseX = e.clientX;
prevMouseY = e.clientY;
}
@@ -117,6 +117,36 @@
$TD.setNotificationTweetEmbedded(account[0].getAttribute("href")+"/status/"+tweetId);
})();
//
// Block: Setup a skip button.
//
(function(){
if (document.body.hasAttribute("td-example-notification")){
return;
}
document.body.insertAdjacentHTML("afterbegin", [
'<svg id="td-skip" xmlns="http://www.w3.org/2000/svg" width="10" height="17" viewBox="0 0 350 600" style="position:fixed;left:30px;bottom:10px;z-index:1000">',
'<path fill="#888" d="M0,151.656l102.208-102.22l247.777,247.775L102.208,544.986L0,442.758l145.546-145.547">',
'</svg>'
].join(""));
document.getElementById("td-skip").addEventListener("click", function(){
$TD.loadNextNotification();
});
})();
//
// Block: Setup a hover class on body.
//
document.body.addEventListener("mouseenter", function(){
document.body.classList.add("td-hover");
});
document.body.addEventListener("mouseleave", function(){
document.body.classList.remove("td-hover");
});
//
// Block: Page fully loaded.
//

View File

@@ -50,7 +50,7 @@
setState(plugin, enable){
if (enable && this.isDisabled(plugin)){
this.disabled.splice(this.disabled.indexOf(plugin.id),1);
this.disabled.splice(this.disabled.indexOf(plugin.id), 1);
plugin.obj.enabled();
this.runWhenReady(plugin);
}
@@ -75,6 +75,6 @@
// Block: Setup global function to change plugin state.
//
window.TDPF_setPluginState = function(identifier, enable){
window.TD_PLUGINS.setState(window.TD_PLUGINS.findObject(identifier),enable);
window.TD_PLUGINS.setState(window.TD_PLUGINS.findObject(identifier), enable);
};
})();

View File

@@ -0,0 +1,54 @@
(function($TDP){
//
// Block: Setup a simple JavaScript object configuration loader.
//
window.TDPF_loadConfigurationFile = function(pluginObject, fileNameUser, fileNameDefault, onSuccess, onFailure){
var identifier = pluginObject.$id;
var token = pluginObject.$token;
$TDP.checkFileExists(token, fileNameUser).then(exists => {
var fileName = exists ? fileNameUser : fileNameDefault;
$TDP.readFile(token, fileName, true).then(contents => {
var obj;
try{
obj = eval("("+contents+")");
}catch(err){
if (!(onFailure && onFailure(err))){
$TD.alert("warning", "Problem loading '"+fileName+"' file for '"+identifier+"' plugin, the JavaScript syntax is invalid: "+err.message);
}
return;
}
onSuccess && onSuccess(obj);
}).catch(err => {
if (!(onFailure && onFailure(err))){
$TD.alert("warning", "Problem loading '"+fileName+"' file for '"+identifier+"' plugin: "+err.message);
}
});
}).catch(err => {
if (!(onFailure && onFailure(err))){
$TD.alert("warning", "Problem checking '"+fileNameUser+"' file for '"+identifier+"' plugin: "+err.message);
}
});
};
//
// Block: Setup a function to add/remove custom CSS.
//
window.TDPF_createCustomStyle = function(pluginObject){
var element = document.createElement("style");
element.id = "plugin-"+pluginObject.$id+"-"+Math.random().toString(36).substring(2, 7);
document.head.appendChild(element);
var obj = {
insert: (rule) => element.sheet.insertRule(rule, 0),
remove: () => $(element).remove()
};
obj.element = element;
return obj;
};
})($TDP);

View File

@@ -1,4 +1,4 @@
(function($,$TDU){
(function($, $TDU){
//
// Variable: Current timeout ID for update checking.
//
@@ -18,6 +18,9 @@
// Function: Creates the update notification element. Removes the old one if already exists.
//
var createUpdateNotificationElement = function(version, download){
var outdated = version === "unsupported";
var tweetdick = version === "tweetdick";
var ele = $("#tweetdck-update");
var existed = ele.length > 0;
@@ -25,13 +28,31 @@
ele.remove();
}
var html = [
var html = outdated ? [
"<div id='tweetdck-update'>",
"<p class='tdu-title'>Unsupported System</p>",
"<p class='tdu-info'>You will not receive updates.</p>",
"<div class='tdu-buttons'>",
"<button class='btn btn-positive tdu-btn-unsupported'><span class='label'>Read More</span></button>",
"<button class='btn btn-negative tdu-btn-dismiss'><span class='label'>Dismiss</span></button>",
"</div>",
"</div>"
] : tweetdick ? [
"<div id='tweetdck-update'>",
"<p class='tdu-title'>TweetDick Ending</p>",
"<p class='tdu-info'>Please, move to TweetDuck.</p>",
"<div class='tdu-buttons'>",
"<button class='btn btn-positive tdu-btn-unsupported'><span class='label'>Read More</span></button>",
"<button class='btn btn-negative tdu-btn-dismiss'><span class='label'>Dismiss</span></button>",
"</div>",
"</div>"
] : [
"<div id='tweetdck-update'>",
"<p class='tdu-title'>"+$TDU.brandName+" Update</p>",
"<p class='tdu-info'>Version "+version+" is now available.</p>",
"<div class='tdu-buttons'>",
"<button class='btn btn-positive tdu-btn-download'><span class='label'>Download</button>",
"<button class='btn btn-negative tdu-btn-dismiss'><span class='label'>Dismiss</button>",
"<button class='btn btn-positive tdu-btn-download'><span class='label'>Download</span></button>",
"<button class='btn btn-negative tdu-btn-dismiss'><span class='label'>Dismiss</span></button>",
"</div>",
"</div>"
];
@@ -60,7 +81,7 @@
fontWeight: "bold",
textAlign: "center",
letterSpacing: "0.2px",
margin: "4px auto 2px"
margin: "5px auto 2px"
});
ele.children("p.tdu-info").first().css({
@@ -90,10 +111,18 @@
buttonDiv.children(".tdu-btn-download").click(function(){
ele.remove();
$TDU.onUpdateAccepted(version,download);
$TDU.onUpdateAccepted(version, download);
});
buttonDiv.children(".tdu-btn-dismiss").click(function(){
buttonDiv.children(".tdu-btn-unsupported").click(function(){
$TDU.openBrowser("https://github.com/chylex/TweetDuck/wiki/Supported-Systems");
});
buttonDiv.children(".tdu-btn-tweetdick").click(function(){
$TDU.openBrowser("https://github.com/chylex/TweetDick/wiki/Future-of-TweetDick");
});
buttonDiv.children(".tdu-btn-dismiss,.tdu-btn-unsupported").click(function(){
$TDU.onUpdateDismissed(version);
ele.slideUp(function(){ ele.remove(); });
});
@@ -109,22 +138,37 @@
// Function: Runs an update check and updates all DOM elements appropriately.
//
var runUpdateCheck = function(force, eventID){
if (!$TDU.isSystemSupported){
if ($TDU.dismissedVersionTag !== "unsupported"){
createUpdateNotificationElement("unsupported");
}
return;
}
else if ($TDU.brandName === "TweetDick"){
if ($TDU.dismissedVersionTag !== "tweetdick"){
createUpdateNotificationElement("tweetdick");
}
return;
}
clearTimeout(updateCheckTimeoutID);
updateCheckTimeoutID = setTimeout(runUpdateCheck,1000*60*60); // 1 hour
updateCheckTimeoutID = setTimeout(runUpdateCheck, 1000*60*60); // 1 hour
if (!$TDU.updateCheckEnabled && !force)return;
$.getJSON(updateCheckUrl,function(response){
$.getJSON(updateCheckUrl, function(response){
var tagName = response.tag_name;
var hasUpdate = tagName !== $TDU.versionTag && tagName !== $TDU.dismissedVersionTag && response.assets.length > 0;
if (hasUpdate){
var obj = response.assets.find(asset => asset.name === updateFileName) || response.assets[0];
createUpdateNotificationElement(tagName,obj.browser_download_url);
createUpdateNotificationElement(tagName, obj.browser_download_url);
}
if (eventID !== 0){
$TDU.onUpdateCheckFinished(eventID,hasUpdate,tagName);
$TDU.onUpdateCheckFinished(eventID, hasUpdate, tagName);
}
});
};
@@ -134,4 +178,4 @@
//
window.TDUF_runUpdateCheck = runUpdateCheck;
runUpdateCheck();
})($,$TDU);
})($, $TDU);

View File

@@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="packages\CefSharp.WinForms.53.0.0-pre01\build\CefSharp.WinForms.props" Condition="Exists('packages\CefSharp.WinForms.53.0.0-pre01\build\CefSharp.WinForms.props')" />
<Import Project="packages\CefSharp.Common.53.0.0-pre01\build\CefSharp.Common.props" Condition="Exists('packages\CefSharp.Common.53.0.0-pre01\build\CefSharp.Common.props')" />
<Import Project="packages\CefSharp.WinForms.49.0.0-pre02\build\CefSharp.WinForms.props" Condition="Exists('packages\CefSharp.WinForms.49.0.0-pre02\build\CefSharp.WinForms.props')" />
<Import Project="packages\CefSharp.Common.49.0.0-pre02\build\CefSharp.Common.props" Condition="Exists('packages\CefSharp.Common.49.0.0-pre02\build\CefSharp.Common.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
@@ -11,12 +13,12 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>TweetDck</RootNamespace>
<AssemblyName Condition=" '$(Configuration)' == 'Debug' ">TweetDick</AssemblyName>
<AssemblyName Condition=" '$(Configuration)' == 'Release Dick' ">TweetDick</AssemblyName>
<AssemblyName Condition=" '$(Configuration)' == 'Release Duck' ">TweetDuck</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<AssemblyName Condition=" '$(Configuration)' == 'Release' ">TweetDuck</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<NuGetPackageImportStamp>ff8ce4f3</NuGetPackageImportStamp>
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
<NuGetPackageImportStamp>e83161d1</NuGetPackageImportStamp>
<TargetFrameworkProfile>
</TargetFrameworkProfile>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
@@ -41,24 +43,13 @@
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release Dick|x86' ">
<OutputPath>bin\x86\Release Dick\</OutputPath>
<DefineConstants>
</DefineConstants>
<Optimize>true</Optimize>
<DebugType>none</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<UseVSHostingProcess>false</UseVSHostingProcess>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>Resources\icon.ico</ApplicationIcon>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release Duck|x86' ">
<OutputPath>bin\x86\Release Duck\</OutputPath>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<OutputPath>bin\x86\Release\</OutputPath>
<Optimize>true</Optimize>
<PlatformTarget>x86</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
@@ -66,7 +57,12 @@
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<DefineConstants>DUCK</DefineConstants>
<DefineConstants>
</DefineConstants>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<AssemblyName>TweetDuck</AssemblyName>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
@@ -78,6 +74,9 @@
<Compile Include="Configuration\LockManager.cs" />
<Compile Include="Configuration\UserConfig.cs" />
<Compile Include="Core\Controls\ControlExtensions.cs" />
<Compile Include="Core\Controls\FlatButton.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Core\Controls\FlatProgressBar.cs">
<SubType>Component</SubType>
</Compile>
@@ -96,6 +95,7 @@
<Compile Include="Core\Controls\TabPanel.Designer.cs">
<DependentUpon>TabPanel.cs</DependentUpon>
</Compile>
<Compile Include="Core\Handling\BrowserProcessHandler.cs" />
<Compile Include="Core\Handling\ContextMenuBase.cs" />
<Compile Include="Core\Handling\ContextMenuBrowser.cs" />
<Compile Include="Core\FormBrowser.cs">
@@ -120,12 +120,30 @@
<Compile Include="Core\Other\FormAbout.Designer.cs">
<DependentUpon>FormAbout.cs</DependentUpon>
</Compile>
<Compile Include="Core\Other\FormMessage.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Core\Other\FormMessage.Designer.cs">
<DependentUpon>FormMessage.cs</DependentUpon>
</Compile>
<Compile Include="Core\Other\FormPlugins.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Core\Other\FormPlugins.Designer.cs">
<DependentUpon>FormPlugins.cs</DependentUpon>
</Compile>
<Compile Include="Core\Other\Settings\Dialogs\DialogSettingsCSS.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Core\Other\Settings\Dialogs\DialogSettingsCSS.Designer.cs">
<DependentUpon>DialogSettingsCSS.cs</DependentUpon>
</Compile>
<Compile Include="Core\Other\Settings\Dialogs\DialogSettingsCefArgs.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Core\Other\Settings\Dialogs\DialogSettingsCefArgs.Designer.cs">
<DependentUpon>DialogSettingsCefArgs.cs</DependentUpon>
</Compile>
<Compile Include="Core\Other\Settings\Export\CombinedFileStream.cs" />
<Compile Include="Core\Other\Settings\Export\ExportManager.cs" />
<Compile Include="Core\Other\Settings\TabSettingsAdvanced.cs">
@@ -158,7 +176,10 @@
<Compile Include="Core\Other\Settings\TabSettingsUpdates.Designer.cs">
<DependentUpon>TabSettingsUpdates.cs</DependentUpon>
</Compile>
<Compile Include="Core\Utils\CommandLineArgs.cs" />
<Compile Include="Core\Utils\CommandLineArgsParser.cs" />
<Compile Include="Core\Utils\WindowState.cs" />
<Compile Include="Core\Utils\WindowsUtils.cs" />
<Compile Include="Migration\FormBackgroundWork.cs">
<SubType>Form</SubType>
</Compile>
@@ -172,6 +193,7 @@
<Compile Include="Core\Other\FormSettings.Designer.cs">
<DependentUpon>FormSettings.cs</DependentUpon>
</Compile>
<Compile Include="Migration\MigrationUtils.cs" />
<Compile Include="Plugins\Controls\PluginControl.cs">
<SubType>UserControl</SubType>
</Compile>
@@ -186,12 +208,14 @@
</Compile>
<Compile Include="Plugins\Plugin.cs" />
<Compile Include="Plugins\Events\PluginChangedStateEventArgs.cs" />
<Compile Include="Plugins\PluginBridge.cs" />
<Compile Include="Plugins\PluginConfig.cs" />
<Compile Include="Plugins\PluginEnvironment.cs" />
<Compile Include="Plugins\PluginGroup.cs" />
<Compile Include="Plugins\Events\PluginLoadEventArgs.cs" />
<Compile Include="Plugins\PluginManager.cs" />
<Compile Include="Plugins\PluginScriptGenerator.cs" />
<Compile Include="Reporter.cs" />
<Compile Include="Updates\FormUpdateDownload.cs">
<SubType>Form</SubType>
</Compile>
@@ -212,16 +236,8 @@
<Compile Include="Updates\UpdateCheckEventArgs.cs" />
<Compile Include="Updates\UpdateHandler.cs" />
<Compile Include="Updates\UpdateInfo.cs" />
<Compile Include="Migration\FormMigrationQuestion.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Migration\FormMigrationQuestion.Designer.cs">
<DependentUpon>FormMigrationQuestion.cs</DependentUpon>
</Compile>
<Compile Include="Migration\Helpers\LnkEditor.cs" />
<Compile Include="Migration\MigrationDecision.cs" />
<Compile Include="Migration\MigrationManager.cs" />
<Compile Include="Migration\Helpers\ProgramRegistrySearch.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Properties\Resources.Designer.cs">
@@ -255,17 +271,6 @@
<Install>true</Install>
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<COMReference Include="Shell32">
<Guid>{50A7E9B0-70EF-11D1-B75A-00A0C90564FE}</Guid>
<VersionMajor>1</VersionMajor>
<VersionMinor>0</VersionMinor>
<Lcid>0</Lcid>
<WrapperTool>tlbimp</WrapperTool>
<Isolated>False</Isolated>
<EmbedInteropTypes>True</EmbedInteropTypes>
</COMReference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Core\FormBrowser.resx">
<DependentUpon>FormBrowser.cs</DependentUpon>
@@ -293,36 +298,6 @@
<TargetPath>icon.ico</TargetPath>
</ContentWithTargetPath>
</ItemGroup>
<ItemGroup>
<ContentWithTargetPath Include="Resources\Scripts\code.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<TargetPath>code.js</TargetPath>
</ContentWithTargetPath>
</ItemGroup>
<ItemGroup>
<ContentWithTargetPath Include="Resources\Scripts\notification.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<TargetPath>notification.js</TargetPath>
</ContentWithTargetPath>
</ItemGroup>
<ItemGroup>
<ContentWithTargetPath Include="Resources\Scripts\plugins.browser.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<TargetPath>plugins.browser.js</TargetPath>
</ContentWithTargetPath>
</ItemGroup>
<ItemGroup>
<ContentWithTargetPath Include="Resources\Scripts\plugins.notification.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<TargetPath>plugins.notification.js</TargetPath>
</ContentWithTargetPath>
</ItemGroup>
<ItemGroup>
<ContentWithTargetPath Include="Resources\Scripts\update.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<TargetPath>update.js</TargetPath>
</ContentWithTargetPath>
</ItemGroup>
<ItemGroup>
<None Include="Resources\icon-small.ico" />
<None Include="Resources\icon-tray-new.ico" />
@@ -330,21 +305,20 @@
</ItemGroup>
<ItemGroup>
<Folder Include="Resources\Plugins\" />
<Folder Include="Resources\Scripts\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('packages\cef.redist.x86.3.2623.1396\build\cef.redist.x86.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x86.3.2623.1396\build\cef.redist.x86.targets'))" />
<Error Condition="!Exists('packages\cef.redist.x64.3.2623.1396\build\cef.redist.x64.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x64.3.2623.1396\build\cef.redist.x64.targets'))" />
<Error Condition="!Exists('packages\CefSharp.Common.49.0.0-pre02\build\CefSharp.Common.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.49.0.0-pre02\build\CefSharp.Common.props'))" />
<Error Condition="!Exists('packages\CefSharp.Common.49.0.0-pre02\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.49.0.0-pre02\build\CefSharp.Common.targets'))" />
<Error Condition="!Exists('packages\CefSharp.WinForms.49.0.0-pre02\build\CefSharp.WinForms.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.49.0.0-pre02\build\CefSharp.WinForms.props'))" />
<Error Condition="!Exists('packages\cef.redist.x86.3.2785.1478\build\cef.redist.x86.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x86.3.2785.1478\build\cef.redist.x86.targets'))" />
<Error Condition="!Exists('packages\cef.redist.x64.3.2785.1478\build\cef.redist.x64.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x64.3.2785.1478\build\cef.redist.x64.targets'))" />
<Error Condition="!Exists('packages\CefSharp.Common.53.0.0-pre01\build\CefSharp.Common.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.53.0.0-pre01\build\CefSharp.Common.props'))" />
<Error Condition="!Exists('packages\CefSharp.Common.53.0.0-pre01\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.53.0.0-pre01\build\CefSharp.Common.targets'))" />
<Error Condition="!Exists('packages\CefSharp.WinForms.53.0.0-pre01\build\CefSharp.WinForms.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.53.0.0-pre01\build\CefSharp.WinForms.props'))" />
<Error Condition="!Exists('packages\CefSharp.WinForms.53.0.0-pre01\build\CefSharp.WinForms.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.53.0.0-pre01\build\CefSharp.WinForms.targets'))" />
</Target>
<Import Project="packages\cef.redist.x86.3.2623.1396\build\cef.redist.x86.targets" Condition="Exists('packages\cef.redist.x86.3.2623.1396\build\cef.redist.x86.targets')" />
<Import Project="packages\cef.redist.x64.3.2623.1396\build\cef.redist.x64.targets" Condition="Exists('packages\cef.redist.x64.3.2623.1396\build\cef.redist.x64.targets')" />
<Import Project="packages\CefSharp.Common.49.0.0-pre02\build\CefSharp.Common.targets" Condition="Exists('packages\CefSharp.Common.49.0.0-pre02\build\CefSharp.Common.targets')" />
<PropertyGroup>
<PostBuildEvent>del "$(TargetPath).config"
xcopy "$(ProjectDir)LICENSE.md" "$(TargetDir)" /Y
@@ -353,6 +327,10 @@ ren "$(TargetDir)LICENSE.md" "LICENSE.txt"
xcopy "$(ProjectDir)Libraries\CEFSHARP-LICENSE.txt" "$(TargetDir)" /Y
xcopy "$(ProjectDir)packages\Microsoft.VC120.CRT.JetBrains.12.0.21005.2\DotFiles\msvcp120.dll" "$(TargetDir)" /Y
xcopy "$(ProjectDir)packages\Microsoft.VC120.CRT.JetBrains.12.0.21005.2\DotFiles\msvcr120.dll" "$(TargetDir)" /Y
rmdir "$(TargetDir)scripts"
mkdir "$(TargetDir)scripts"
xcopy "$(ProjectDir)Resources\Scripts\*" "$(TargetDir)scripts\" /E /Y
rmdir "$(TargetDir)plugins"
mkdir "$(TargetDir)plugins"
mkdir "$(TargetDir)plugins\official"
mkdir "$(TargetDir)plugins\user"
@@ -360,6 +338,10 @@ xcopy "$(ProjectDir)Resources\Plugins\*" "$(TargetDir)plugins\official\" /E /Y
rmdir "$(ProjectDir)\bin\Debug"
rmdir "$(ProjectDir)\bin\Release"</PostBuildEvent>
</PropertyGroup>
<Import Project="packages\cef.redist.x86.3.2785.1478\build\cef.redist.x86.targets" Condition="Exists('packages\cef.redist.x86.3.2785.1478\build\cef.redist.x86.targets')" />
<Import Project="packages\cef.redist.x64.3.2785.1478\build\cef.redist.x64.targets" Condition="Exists('packages\cef.redist.x64.3.2785.1478\build\cef.redist.x64.targets')" />
<Import Project="packages\CefSharp.Common.53.0.0-pre01\build\CefSharp.Common.targets" Condition="Exists('packages\CefSharp.Common.53.0.0-pre01\build\CefSharp.Common.targets')" />
<Import Project="packages\CefSharp.WinForms.53.0.0-pre01\build\CefSharp.WinForms.targets" Condition="Exists('packages\CefSharp.WinForms.53.0.0-pre01\build\CefSharp.WinForms.targets')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">

View File

@@ -1,4 +1,4 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.40629.0
@@ -8,17 +8,14 @@ EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x86 = Debug|x86
Release Dick|x86 = Release Dick|x86
Release Duck|x86 = Release Duck|x86
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{2389A7CD-E0D3-4706-8294-092929A33A2D}.Debug|x86.ActiveCfg = Debug|x86
{2389A7CD-E0D3-4706-8294-092929A33A2D}.Debug|x86.Build.0 = Debug|x86
{2389A7CD-E0D3-4706-8294-092929A33A2D}.Debug|x86.Deploy.0 = Debug|x86
{2389A7CD-E0D3-4706-8294-092929A33A2D}.Release Dick|x86.ActiveCfg = Release Dick|x86
{2389A7CD-E0D3-4706-8294-092929A33A2D}.Release Dick|x86.Build.0 = Release Dick|x86
{2389A7CD-E0D3-4706-8294-092929A33A2D}.Release Duck|x86.ActiveCfg = Release Duck|x86
{2389A7CD-E0D3-4706-8294-092929A33A2D}.Release Duck|x86.Build.0 = Release Duck|x86
{2389A7CD-E0D3-4706-8294-092929A33A2D}.Release|x86.ActiveCfg = Release|x86
{2389A7CD-E0D3-4706-8294-092929A33A2D}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -13,7 +13,7 @@ namespace TweetDck.Updates{
public string InstallerPath{
get{
return Path.Combine(Path.GetTempPath(),updateInfo.FileName);
return Path.Combine(Path.GetTempPath(), updateInfo.FileName);
}
}
@@ -45,7 +45,7 @@ namespace TweetDck.Updates{
}
private void FormUpdateDownload_Shown(object sender, EventArgs e){
webClient.DownloadFileAsync(new Uri(updateInfo.DownloadUrl),InstallerPath);
webClient.DownloadFileAsync(new Uri(updateInfo.DownloadUrl), InstallerPath);
}
private void btnCancel_Click(object sender, EventArgs e){
@@ -69,7 +69,7 @@ namespace TweetDck.Updates{
progressDownload.SetValueInstant(1000);
}
labelStatus.Text = (e.BytesReceived/BytesToMB).ToString("0.0",CultureInfo.CurrentCulture)+" MB";
labelStatus.Text = (e.BytesReceived/BytesToMB).ToString("0.0", CultureInfo.CurrentCulture)+" MB";
}
else{
if (progressDownload.Style != ProgressBarStyle.Continuous){
@@ -77,7 +77,7 @@ namespace TweetDck.Updates{
}
progressDownload.SetValueInstant(e.ProgressPercentage*10);
labelStatus.Text = (e.BytesReceived/BytesToMB).ToString("0.0",CultureInfo.CurrentCulture)+" / "+(e.TotalBytesToReceive/BytesToMB).ToString("0.0",CultureInfo.CurrentCulture)+" MB";
labelStatus.Text = (e.BytesReceived/BytesToMB).ToString("0.0", CultureInfo.CurrentCulture)+" / "+(e.TotalBytesToReceive/BytesToMB).ToString("0.0", CultureInfo.CurrentCulture)+" MB";
}
});
}
@@ -90,9 +90,9 @@ namespace TweetDck.Updates{
}
}
else if (e.Error != null){
Program.Log(e.Error.ToString());
Program.Reporter.Log(e.Error.ToString());
if (MessageBox.Show("Could not download the update: "+e.Error.Message+"\r\n\r\nDo you want to open the website and try downloading the update manually?","Update Has Failed",MessageBoxButtons.YesNo,MessageBoxIcon.Error,MessageBoxDefaultButton.Button1) == DialogResult.Yes){
if (MessageBox.Show("Could not download the update: "+e.Error.Message+"\r\n\r\nDo you want to open the website and try downloading the update manually?", "Update Has Failed", MessageBoxButtons.YesNo, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1) == DialogResult.Yes){
BrowserUtils.OpenExternalBrowser(Program.Website);
UpdateStatus = Status.Manual;
}

View File

@@ -3,6 +3,7 @@ using CefSharp;
using CefSharp.WinForms;
using TweetDck.Core;
using TweetDck.Core.Controls;
using TweetDck.Core.Utils;
using TweetDck.Resources;
namespace TweetDck.Updates{
@@ -19,29 +20,29 @@ namespace TweetDck.Updates{
this.browser = browser;
this.form = form;
browser.FrameLoadEnd += browser_FrameLoadEnd;
browser.RegisterJsObject("$TDU",new Bridge(this));
browser.RegisterJsObject("$TDU", new Bridge(this));
}
private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
if (e.Frame.IsMain){
ScriptLoader.ExecuteFile(e.Frame,"update.js");
ScriptLoader.ExecuteFile(e.Frame, "update.js");
}
}
public int Check(bool force){
browser.ExecuteScriptAsync("TDUF_runUpdateCheck",force,++lastEventId);
browser.ExecuteScriptAsync("TDUF_runUpdateCheck", force, ++lastEventId);
return lastEventId;
}
private void TriggerUpdateAcceptedEvent(UpdateAcceptedEventArgs args){
if (UpdateAccepted != null){
form.InvokeSafe(() => UpdateAccepted(this,args));
form.InvokeSafe(() => UpdateAccepted(this, args));
}
}
private void TriggerCheckFinishedEvent(UpdateCheckEventArgs args){
if (CheckFinished != null){
form.InvokeSafe(() => CheckFinished(this,args));
form.InvokeSafe(() => CheckFinished(this, args));
}
}
@@ -70,6 +71,12 @@ namespace TweetDck.Updates{
}
}
public bool IsSystemSupported{
get{
return Environment.OSVersion.Version >= new Version("6.1"); // 6.1 NT version = Windows 7
}
}
private readonly UpdateHandler owner;
public Bridge(UpdateHandler owner){
@@ -77,11 +84,11 @@ namespace TweetDck.Updates{
}
public void OnUpdateCheckFinished(int eventId, bool isUpdateAvailable, string latestVersion){
owner.TriggerCheckFinishedEvent(new UpdateCheckEventArgs(eventId,isUpdateAvailable,latestVersion));
owner.TriggerCheckFinishedEvent(new UpdateCheckEventArgs(eventId, isUpdateAvailable, latestVersion));
}
public void OnUpdateAccepted(string versionTag, string downloadUrl){
owner.TriggerUpdateAcceptedEvent(new UpdateAcceptedEventArgs(new UpdateInfo(versionTag,downloadUrl)));
owner.TriggerUpdateAcceptedEvent(new UpdateAcceptedEventArgs(new UpdateInfo(versionTag, downloadUrl)));
}
public void OnUpdateDismissed(string versionTag){
@@ -90,6 +97,10 @@ namespace TweetDck.Updates{
Program.UserConfig.Save();
});
}
public void OpenBrowser(string url){
BrowserUtils.OpenExternalBrowser(url);
}
}
}
}

View File

@@ -1,9 +1,6 @@
del "bin\x86\Release Dick\*.xml"
del "bin\x86\Release Dick\devtools_resources.pak"
del "bin\x86\Release Duck\*.xml"
del "bin\x86\Release Duck\devtools_resources.pak"
del "bin\x86\Release\*.xml"
del "bin\x86\Release\devtools_resources.pak"
del "bin\x86\Release\d3dcompiler_43.dll"
del "bin\x86\Release Dick\TweetDick.Browser.exe"
ren "bin\x86\Release Dick\CefSharp.BrowserSubprocess.exe" "TweetDick.Browser.exe"
del "bin\x86\Release Duck\TweetDuck.Browser.exe"
ren "bin\x86\Release Duck\CefSharp.BrowserSubprocess.exe" "TweetDuck.Browser.exe"
del "bin\x86\Release\TweetDuck.Browser.exe"
ren "bin\x86\Release\CefSharp.BrowserSubprocess.exe" "TweetDuck.Browser.exe"