mirror of
https://github.com/chylex/TweetDuck.git
synced 2025-09-14 10:32:10 +02:00
Compare commits
169 Commits
Author | SHA1 | Date | |
---|---|---|---|
6387ab41b3 | |||
4df16b7f15 | |||
ed387a2873 | |||
9e225530a6 | |||
7b23686dc6 | |||
4de31453fd | |||
4c59526e39 | |||
9ec1764194 | |||
47afa32902 | |||
2a09487b55 | |||
563c856dd3 | |||
69ea242408 | |||
d6e0e0726f | |||
73d460d40a | |||
1f27d96ac9 | |||
93e9f28d69 | |||
ec2e26752a | |||
fadd95f3e6 | |||
00acc677e6 | |||
1a799881e8 | |||
f75677593a | |||
19e3bd19f0 | |||
85701b0a3c | |||
014cb18dcb | |||
e71e1c853f | |||
ee9d9196f5 | |||
53c8272e01 | |||
7f7b6b1e2a | |||
405777e0f5 | |||
df2b624cb5 | |||
8a48d5c2f9 | |||
c55ee71442 | |||
3f82745f5b | |||
404187a1ae | |||
2b7b3f586b | |||
04959a3493 | |||
97cf4932ae | |||
b0d88a0a37 | |||
67a2e40622 | |||
3a28556c7f | |||
9ecc92b9a5 | |||
ca023be98a | |||
11a1423f76 | |||
79f6df121b | |||
71eade7e86 | |||
5f81d29036 | |||
ec1cb5dc5f | |||
fd969e2d55 | |||
37e33b77ff | |||
f7ed7703b4 | |||
4bb35295ca | |||
1e4f673f9e | |||
7cadb1c403 | |||
37148f5093 | |||
f6bc26789f | |||
b3f5a88525 | |||
1e538d2b28 | |||
7d7bfb7b01 | |||
41d86ba440 | |||
3df474a8a5 | |||
a50d6e8f47 | |||
6081e5b9c1 | |||
66ccea920c | |||
470d63093f | |||
eae0507831 | |||
92af85d3bb | |||
7635af5730 | |||
a838e89695 | |||
b22289a8b9 | |||
45b3ff52c6 | |||
4464991f4c | |||
b0d2f77583 | |||
b211a4405d | |||
8823016d2c | |||
859fdc7ec1 | |||
028d5ed01f | |||
5fd5a2a436 | |||
79a7e7470c | |||
9ecef78aed | |||
65a837a6e1 | |||
6e4db4acea | |||
26fb977d05 | |||
b42cd1c048 | |||
467f7cd12f | |||
66699ce9df | |||
cf7d903932 | |||
a7ab67925c | |||
a474ba4260 | |||
09e5636e86 | |||
2295a875be | |||
82a2455afc | |||
268de676ee | |||
8fe26c07f1 | |||
da3921b1ca | |||
4dd2e787d1 | |||
ce005ae6c2 | |||
1513f46a11 | |||
7543eeb0f4 | |||
873242120c | |||
98f8095a65 | |||
785571a550 | |||
0c4bd4044e | |||
0319543dce | |||
82d70b2d7f | |||
62d18e010a | |||
fc77b85083 | |||
50a8893f4f | |||
9252b3040e | |||
d5141ed020 | |||
7ff9e23283 | |||
89854d527c | |||
6ff0cad2a8 | |||
349cfbd2d5 | |||
40303ef74a | |||
6c652122c2 | |||
3658e3a2aa | |||
2b20fcfcd1 | |||
554d427fef | |||
7cf5b23306 | |||
b26a6098eb | |||
7ad927bdaf | |||
4ed30b3619 | |||
edfa9264d5 | |||
f7516b593f | |||
83ff998f9d | |||
47381e0df4 | |||
ba62d57485 | |||
c014c4bc24 | |||
5d1a3fede2 | |||
53b584fe45 | |||
f53d974400 | |||
c4b4ef19cd | |||
3bfc360362 | |||
584f16d375 | |||
b3d84c3217 | |||
dd14ad470e | |||
85b90574b8 | |||
ee5d172468 | |||
7ca4b94361 | |||
31f1546483 | |||
d8a88a19af | |||
12af79de05 | |||
2260dd419d | |||
61a940cc82 | |||
1bbc1e0d7e | |||
921294eeb3 | |||
baaa90f49d | |||
4e25381770 | |||
272877d0ed | |||
555b947bf7 | |||
da29811b16 | |||
241f67fd4d | |||
eb4ce18e31 | |||
ae99fee440 | |||
d116ac5e56 | |||
28db1f4253 | |||
034312e676 | |||
da83d73ba6 | |||
02e8dc3440 | |||
cac6d1f889 | |||
68fa3294d4 | |||
9b983de8c9 | |||
3a37ee719b | |||
61359c2faa | |||
7f7c5ab35b | |||
a1b483d20a | |||
c3db3ce0f2 | |||
7a1e7637ff | |||
04a78a02d3 |
@@ -1,7 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
using TweetDck.Core.Utils;
|
||||||
|
|
||||||
namespace TweetDck.Configuration{
|
namespace TweetDck.Configuration{
|
||||||
sealed class LockManager{
|
sealed class LockManager{
|
||||||
@@ -114,13 +115,16 @@ namespace TweetDck.Configuration{
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CloseLockingProcess(int timeout){
|
public bool CloseLockingProcess(int closeTimeout, int killTimeout){
|
||||||
if (LockingProcess != null){
|
if (LockingProcess != null){
|
||||||
LockingProcess.CloseMainWindow();
|
try{
|
||||||
|
if (LockingProcess.CloseMainWindow()){
|
||||||
|
WindowsUtils.TrySleepUntil(CheckLockingProcessExited, closeTimeout, 250);
|
||||||
|
}
|
||||||
|
|
||||||
for(int waited = 0; waited < timeout && !LockingProcess.HasExited; waited += 250){
|
if (!LockingProcess.HasExited){
|
||||||
LockingProcess.Refresh();
|
LockingProcess.Kill();
|
||||||
Thread.Sleep(250);
|
WindowsUtils.TrySleepUntil(CheckLockingProcessExited, killTimeout, 250);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LockingProcess.HasExited){
|
if (LockingProcess.HasExited){
|
||||||
@@ -128,11 +132,28 @@ namespace TweetDck.Configuration{
|
|||||||
LockingProcess = null;
|
LockingProcess = null;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}catch(Exception ex){
|
||||||
|
if (ex is InvalidOperationException || ex is Win32Exception){
|
||||||
|
if (LockingProcess != null){
|
||||||
|
LockingProcess.Refresh();
|
||||||
|
|
||||||
|
bool hasExited = LockingProcess.HasExited;
|
||||||
|
LockingProcess.Dispose();
|
||||||
|
return hasExited;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool CheckLockingProcessExited(){
|
||||||
|
LockingProcess.Refresh();
|
||||||
|
return LockingProcess.HasExited;
|
||||||
|
}
|
||||||
|
|
||||||
// Utility functions
|
// Utility functions
|
||||||
|
|
||||||
private static void WriteIntToStream(Stream stream, int value){
|
private static void WriteIntToStream(Stream stream, int value){
|
||||||
|
@@ -4,16 +4,17 @@ using System.IO;
|
|||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
using System.Runtime.Serialization.Formatters.Binary;
|
using System.Runtime.Serialization.Formatters.Binary;
|
||||||
using TweetDck.Core;
|
using TweetDck.Core;
|
||||||
using TweetDck.Core.Handling;
|
using TweetDck.Core.Controls;
|
||||||
|
using TweetDck.Core.Notification;
|
||||||
using TweetDck.Core.Utils;
|
using TweetDck.Core.Utils;
|
||||||
using TweetDck.Plugins;
|
using TweetDck.Plugins;
|
||||||
|
|
||||||
namespace TweetDck.Configuration{
|
namespace TweetDck.Configuration{
|
||||||
[Serializable]
|
[Serializable]
|
||||||
sealed class UserConfig{
|
sealed class UserConfig{
|
||||||
private static readonly IFormatter Formatter = new BinaryFormatter();
|
private static readonly IFormatter Formatter = new BinaryFormatter{ Binder = new CustomBinder() };
|
||||||
|
|
||||||
private const int CurrentFileVersion = 5;
|
private const int CurrentFileVersion = 6;
|
||||||
|
|
||||||
// START OF CONFIGURATION
|
// START OF CONFIGURATION
|
||||||
|
|
||||||
@@ -21,16 +22,15 @@ namespace TweetDck.Configuration{
|
|||||||
public bool DisplayNotificationTimer { get; set; }
|
public bool DisplayNotificationTimer { get; set; }
|
||||||
public bool NotificationTimerCountDown { get; set; }
|
public bool NotificationTimerCountDown { get; set; }
|
||||||
|
|
||||||
public TweetNotification.Duration NotificationDuration { get; set; }
|
|
||||||
public TweetNotification.Position NotificationPosition { get; set; }
|
public TweetNotification.Position NotificationPosition { get; set; }
|
||||||
public Point CustomNotificationPosition { get; set; }
|
public Point CustomNotificationPosition { get; set; }
|
||||||
public int NotificationEdgeDistance { get; set; }
|
public int NotificationEdgeDistance { get; set; }
|
||||||
public int NotificationDisplay { get; set; }
|
public int NotificationDisplay { get; set; }
|
||||||
public int NotificationDurationValue { get; set; }
|
public int NotificationDurationValue { get; set; }
|
||||||
public bool NotificationLegacyLoad { get; set; }
|
|
||||||
|
|
||||||
public bool EnableSpellCheck { get; set; }
|
public bool EnableSpellCheck { get; set; }
|
||||||
public bool ExpandLinksOnHover { get; set; }
|
public bool ExpandLinksOnHover { get; set; }
|
||||||
|
public bool ShowScreenshotBorder { get; set; }
|
||||||
public bool EnableTrayHighlight { get; set; }
|
public bool EnableTrayHighlight { get; set; }
|
||||||
|
|
||||||
public bool EnableUpdateCheck { get; set; }
|
public bool EnableUpdateCheck { get; set; }
|
||||||
@@ -45,7 +45,7 @@ namespace TweetDck.Configuration{
|
|||||||
|
|
||||||
public bool IsCustomNotificationPositionSet{
|
public bool IsCustomNotificationPositionSet{
|
||||||
get{
|
get{
|
||||||
return CustomNotificationPosition.X != -32000 && CustomNotificationPosition.X != 32000;
|
return CustomNotificationPosition != ControlExtensions.InvisibleLocation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,6 +65,16 @@ namespace TweetDck.Configuration{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string NotificationSoundPath{
|
||||||
|
get{
|
||||||
|
return string.IsNullOrEmpty(notificationSoundPath) ? string.Empty : notificationSoundPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
set{
|
||||||
|
notificationSoundPath = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public TrayIcon.Behavior TrayBehavior{
|
public TrayIcon.Behavior TrayBehavior{
|
||||||
get{
|
get{
|
||||||
return trayBehavior;
|
return trayBehavior;
|
||||||
@@ -94,6 +104,7 @@ namespace TweetDck.Configuration{
|
|||||||
|
|
||||||
private int fileVersion;
|
private int fileVersion;
|
||||||
private bool muteNotifications;
|
private bool muteNotifications;
|
||||||
|
private string notificationSoundPath;
|
||||||
private TrayIcon.Behavior trayBehavior;
|
private TrayIcon.Behavior trayBehavior;
|
||||||
|
|
||||||
private UserConfig(string file){
|
private UserConfig(string file){
|
||||||
@@ -101,13 +112,13 @@ namespace TweetDck.Configuration{
|
|||||||
|
|
||||||
BrowserWindow = new WindowState();
|
BrowserWindow = new WindowState();
|
||||||
DisplayNotificationTimer = true;
|
DisplayNotificationTimer = true;
|
||||||
NotificationDuration = TweetNotification.Duration.Medium;
|
|
||||||
NotificationPosition = TweetNotification.Position.TopRight;
|
NotificationPosition = TweetNotification.Position.TopRight;
|
||||||
CustomNotificationPosition = new Point(-32000, -32000);
|
CustomNotificationPosition = ControlExtensions.InvisibleLocation;
|
||||||
NotificationEdgeDistance = 8;
|
NotificationEdgeDistance = 8;
|
||||||
NotificationDurationValue = 25;
|
NotificationDurationValue = 25;
|
||||||
EnableUpdateCheck = true;
|
EnableUpdateCheck = true;
|
||||||
ExpandLinksOnHover = true;
|
ExpandLinksOnHover = true;
|
||||||
|
ShowScreenshotBorder = true;
|
||||||
EnableTrayHighlight = true;
|
EnableTrayHighlight = true;
|
||||||
Plugins = new PluginConfig();
|
Plugins = new PluginConfig();
|
||||||
PluginsWindow = new WindowState();
|
PluginsWindow = new WindowState();
|
||||||
@@ -142,14 +153,7 @@ namespace TweetDck.Configuration{
|
|||||||
|
|
||||||
if (fileVersion == 3){
|
if (fileVersion == 3){
|
||||||
EnableTrayHighlight = true;
|
EnableTrayHighlight = true;
|
||||||
|
NotificationDurationValue = 25;
|
||||||
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;
|
++fileVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,6 +163,11 @@ namespace TweetDck.Configuration{
|
|||||||
++fileVersion;
|
++fileVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fileVersion == 5){
|
||||||
|
ShowScreenshotBorder = true;
|
||||||
|
++fileVersion;
|
||||||
|
}
|
||||||
|
|
||||||
// update the version
|
// update the version
|
||||||
fileVersion = CurrentFileVersion;
|
fileVersion = CurrentFileVersion;
|
||||||
Save();
|
Save();
|
||||||
@@ -229,5 +238,15 @@ namespace TweetDck.Configuration{
|
|||||||
public static string GetBackupFile(string file){
|
public static string GetBackupFile(string file){
|
||||||
return file+".bak";
|
return file+".bak";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private sealed class CustomBinder : SerializationBinder{
|
||||||
|
public override Type BindToType(string assemblyName, string typeName){
|
||||||
|
if (typeName == "TweetDck.Core.Handling.TweetNotification+Position"){
|
||||||
|
return typeof(TweetNotification.Position);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<packages xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<package id="cef.redist.x64" version="3.2785.1478" targetFramework="net452" />
|
<package id="cef.redist.x64" version="3.2883.1552" targetFramework="net452" xmlns="" />
|
||||||
<package id="cef.redist.x86" version="3.2785.1478" targetFramework="net452" />
|
<package id="cef.redist.x86" version="3.2883.1552" targetFramework="net452" xmlns="" />
|
||||||
<package id="CefSharp.Common" version="53.0.0-pre01" targetFramework="net452" />
|
<package id="CefSharp.Common" version="55.0.0" targetFramework="net452" xmlns="" />
|
||||||
<package id="CefSharp.WinForms" version="53.0.0-pre01" targetFramework="net452" />
|
<package id="CefSharp.WinForms" version="55.0.0" targetFramework="net452" xmlns="" />
|
||||||
<package id="Microsoft.VC120.CRT.JetBrains" version="12.0.21005.2" targetFramework="net452" />
|
<package id="Microsoft.VC120.CRT.JetBrains" version="12.0.21005.2" targetFramework="net452" xmlns="" />
|
||||||
</packages>
|
</packages>
|
19
Core/Bridge/CallbackBridge.cs
Normal file
19
Core/Bridge/CallbackBridge.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using TweetDck.Core.Controls;
|
||||||
|
|
||||||
|
namespace TweetDck.Core.Bridge{
|
||||||
|
sealed class CallbackBridge{
|
||||||
|
private readonly Control owner;
|
||||||
|
private readonly Action safeCallback;
|
||||||
|
|
||||||
|
public CallbackBridge(Control owner, Action safeCallback){
|
||||||
|
this.owner = owner;
|
||||||
|
this.safeCallback = safeCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Trigger(){
|
||||||
|
owner.InvokeSafe(safeCallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
34
Core/Bridge/PropertyBridge.cs
Normal file
34
Core/Bridge/PropertyBridge.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace TweetDck.Core.Bridge{
|
||||||
|
static class PropertyBridge{
|
||||||
|
[Flags]
|
||||||
|
public enum Properties{
|
||||||
|
ExpandLinksOnHover = 1,
|
||||||
|
MuteNotifications = 2,
|
||||||
|
HasCustomNotificationSound = 4,
|
||||||
|
All = ExpandLinksOnHover | MuteNotifications | HasCustomNotificationSound
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GenerateScript(Properties properties = Properties.All){
|
||||||
|
StringBuilder build = new StringBuilder();
|
||||||
|
build.Append("(function(c){");
|
||||||
|
|
||||||
|
if (properties.HasFlag(Properties.ExpandLinksOnHover)){
|
||||||
|
build.Append("c.expandLinksOnHover=").Append(Program.UserConfig.ExpandLinksOnHover ? "true;" : "false;");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (properties.HasFlag(Properties.MuteNotifications)){
|
||||||
|
build.Append("c.muteNotifications=").Append(Program.UserConfig.MuteNotifications ? "true;" : "false;");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (properties.HasFlag(Properties.HasCustomNotificationSound)){
|
||||||
|
build.Append("c.hasCustomNotificationSound=").Append(Program.UserConfig.NotificationSoundPath.Length > 0 ? "true;" : "false;");
|
||||||
|
}
|
||||||
|
|
||||||
|
build.Append("})(window.$TDX=window.$TDX||{})");
|
||||||
|
return build.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -3,123 +3,89 @@ using System.Drawing;
|
|||||||
using System.Drawing.Imaging;
|
using System.Drawing.Imaging;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDck.Core.Utils;
|
|
||||||
using TweetDck.Core.Controls;
|
using TweetDck.Core.Controls;
|
||||||
|
using TweetDck.Core.Notification;
|
||||||
|
using TweetDck.Core.Utils;
|
||||||
|
|
||||||
namespace TweetDck.Core.Handling{
|
namespace TweetDck.Core.Bridge{
|
||||||
class TweetDeckBridge{
|
sealed class TweetDeckBridge{
|
||||||
public static string LastRightClickedLink = string.Empty;
|
public static string LastRightClickedLink = string.Empty;
|
||||||
public static string LastHighlightedTweet = string.Empty;
|
public static string LastHighlightedTweet = string.Empty;
|
||||||
public static string LastHighlightedQuotedTweet = string.Empty;
|
public static string LastHighlightedQuotedTweet = string.Empty;
|
||||||
public static string ClipboardImagePath = string.Empty;
|
public static string ClipboardImagePath = string.Empty;
|
||||||
|
|
||||||
|
public static void ResetStaticProperties(){
|
||||||
|
LastRightClickedLink = LastHighlightedTweet = LastHighlightedQuotedTweet = ClipboardImagePath = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
private readonly FormBrowser form;
|
private readonly FormBrowser form;
|
||||||
private readonly FormNotification notification;
|
private readonly FormNotificationMain notification;
|
||||||
|
|
||||||
public string BrandName{
|
public TweetDeckBridge(FormBrowser form, FormNotificationMain notification){
|
||||||
get{
|
|
||||||
return Program.BrandName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string VersionTag{
|
|
||||||
get{
|
|
||||||
return Program.VersionTag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool MuteNotifications{
|
|
||||||
get{
|
|
||||||
return Program.UserConfig.MuteNotifications;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ExpandLinksOnHover{
|
|
||||||
get{
|
|
||||||
return Program.UserConfig.ExpandLinksOnHover;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public 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.form = form;
|
||||||
this.notification = notification;
|
this.notification = notification;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadFontSizeClass(string fsClass){
|
public void LoadFontSizeClass(string fsClass){
|
||||||
form.InvokeSafe(() => {
|
form.InvokeAsyncSafe(() => {
|
||||||
TweetNotification.SetFontSizeClass(fsClass);
|
TweetNotification.SetFontSizeClass(fsClass);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadNotificationHeadContents(string headContents){
|
public void LoadNotificationHeadContents(string headContents){
|
||||||
form.InvokeSafe(() => {
|
form.InvokeAsyncSafe(() => {
|
||||||
TweetNotification.SetHeadTag(headContents);
|
TweetNotification.SetHeadTag(headContents);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetLastRightClickedLink(string link){
|
public void SetLastRightClickedLink(string link){
|
||||||
form.InvokeSafe(() => LastRightClickedLink = link);
|
form.InvokeAsyncSafe(() => LastRightClickedLink = link);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetLastHighlightedTweet(string link, string quotedLink){
|
public void SetLastHighlightedTweet(string link, string quotedLink){
|
||||||
form.InvokeSafe(() => {
|
form.InvokeAsyncSafe(() => {
|
||||||
LastHighlightedTweet = link;
|
LastHighlightedTweet = link;
|
||||||
LastHighlightedQuotedTweet = quotedLink;
|
LastHighlightedQuotedTweet = quotedLink;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetNotificationQuotedTweet(string link){
|
public void SetNotificationQuotedTweet(string link){
|
||||||
notification.InvokeSafe(() => notification.CurrentQuotedTweetUrl = link);
|
notification.InvokeAsyncSafe(() => notification.CurrentQuotedTweetUrl = link);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OpenSettingsMenu(){
|
public void OpenSettingsMenu(){
|
||||||
form.InvokeSafe(form.OpenSettings);
|
form.InvokeAsyncSafe(form.OpenSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OpenPluginsMenu(){
|
public void OpenPluginsMenu(){
|
||||||
form.InvokeSafe(form.OpenPlugins);
|
form.InvokeAsyncSafe(form.OpenPlugins);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnTweetPopup(string tweetHtml, string tweetUrl, int tweetCharacters){
|
public void OnTweetPopup(string tweetHtml, string tweetUrl, int tweetCharacters){
|
||||||
notification.InvokeSafe(() => {
|
notification.InvokeAsyncSafe(() => {
|
||||||
form.OnTweetNotification();
|
form.OnTweetNotification();
|
||||||
notification.ShowNotification(new TweetNotification(tweetHtml, tweetUrl, tweetCharacters));
|
notification.ShowNotification(new TweetNotification(tweetHtml, tweetUrl, tweetCharacters));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnTweetSound(){
|
public void OnTweetSound(){
|
||||||
form.InvokeSafe(form.OnTweetNotification);
|
form.InvokeAsyncSafe(() => {
|
||||||
}
|
form.OnTweetNotification();
|
||||||
|
form.PlayNotificationSound();
|
||||||
public void OnNotificationReady(){
|
});
|
||||||
if (!Program.UserConfig.NotificationLegacyLoad){
|
|
||||||
notification.InvokeSafe(notification.OnNotificationReady);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DisplayTooltip(string text, bool showInNotification){
|
public void DisplayTooltip(string text, bool showInNotification){
|
||||||
if (showInNotification){
|
if (showInNotification){
|
||||||
notification.InvokeSafe(() => notification.DisplayTooltip(text));
|
notification.InvokeAsyncSafe(() => notification.DisplayTooltip(text));
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
form.InvokeSafe(() => form.DisplayTooltip(text));
|
form.InvokeAsyncSafe(() => form.DisplayTooltip(text));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadNextNotification(){
|
public void LoadNextNotification(){
|
||||||
notification.InvokeSafe(notification.FinishCurrentTweet);
|
notification.InvokeAsyncSafe(notification.FinishCurrentNotification);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void TryPasteImage(){
|
public void TryPasteImage(){
|
||||||
@@ -143,15 +109,15 @@ namespace TweetDck.Core.Handling{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void ClickUploadImage(int offsetX, int offsetY){
|
public void ClickUploadImage(int offsetX, int offsetY){
|
||||||
form.InvokeSafe(() => {
|
form.InvokeAsyncSafe(() => form.TriggerImageUpload(offsetX, offsetY));
|
||||||
Point prevPos = Cursor.Position;
|
}
|
||||||
|
|
||||||
Cursor.Position = form.PointToScreen(new Point(offsetX, offsetY));
|
public void ScreenshotTweet(string html, int width, int height){
|
||||||
NativeMethods.SimulateMouseClick(NativeMethods.MouseButton.Left);
|
form.InvokeAsyncSafe(() => form.OnTweetScreenshotReady(html, width, height));
|
||||||
Cursor.Position = prevPos;
|
}
|
||||||
|
|
||||||
form.OnImagePastedFinish();
|
public void FixClipboard(){
|
||||||
});
|
form.InvokeAsyncSafe(WindowsUtils.ClipboardStripHtmlStyles);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OpenBrowser(string url){
|
public void OpenBrowser(string url){
|
@@ -5,6 +5,8 @@ using TweetDck.Core.Utils;
|
|||||||
|
|
||||||
namespace TweetDck.Core.Controls{
|
namespace TweetDck.Core.Controls{
|
||||||
static class ControlExtensions{
|
static class ControlExtensions{
|
||||||
|
public static readonly Point InvisibleLocation = new Point(-32000, -32000);
|
||||||
|
|
||||||
public static void InvokeSafe(this Control control, Action func){
|
public static void InvokeSafe(this Control control, Action func){
|
||||||
if (control.InvokeRequired){
|
if (control.InvokeRequired){
|
||||||
control.Invoke(func);
|
control.Invoke(func);
|
||||||
@@ -14,6 +16,10 @@ namespace TweetDck.Core.Controls{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void InvokeAsyncSafe(this Control control, Action func){
|
||||||
|
control.BeginInvoke(func);
|
||||||
|
}
|
||||||
|
|
||||||
public static void MoveToCenter(this Form targetForm, Form parentForm){
|
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);
|
||||||
}
|
}
|
||||||
|
2
Core/FormBrowser.Designer.cs
generated
2
Core/FormBrowser.Designer.cs
generated
@@ -38,7 +38,7 @@
|
|||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
this.ClientSize = new System.Drawing.Size(324, 386);
|
this.ClientSize = new System.Drawing.Size(324, 386);
|
||||||
this.Icon = Properties.Resources.icon;
|
this.Icon = Properties.Resources.icon;
|
||||||
this.Location = new System.Drawing.Point(-32000, -32000);
|
this.Location = TweetDck.Core.Controls.ControlExtensions.InvisibleLocation;
|
||||||
this.MinimumSize = new System.Drawing.Size(340, 424);
|
this.MinimumSize = new System.Drawing.Size(340, 424);
|
||||||
this.Name = "FormBrowser";
|
this.Name = "FormBrowser";
|
||||||
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
|
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
|
||||||
|
@@ -13,6 +13,11 @@ using TweetDck.Updates;
|
|||||||
using TweetDck.Plugins;
|
using TweetDck.Plugins;
|
||||||
using TweetDck.Plugins.Enums;
|
using TweetDck.Plugins.Enums;
|
||||||
using TweetDck.Plugins.Events;
|
using TweetDck.Plugins.Events;
|
||||||
|
using TweetDck.Core.Bridge;
|
||||||
|
using TweetDck.Core.Notification;
|
||||||
|
using TweetDck.Core.Notification.Screenshot;
|
||||||
|
using TweetDck.Updates.Events;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace TweetDck.Core{
|
namespace TweetDck.Core{
|
||||||
sealed partial class FormBrowser : Form{
|
sealed partial class FormBrowser : Form{
|
||||||
@@ -27,6 +32,7 @@ namespace TweetDck.Core{
|
|||||||
private readonly ChromiumWebBrowser browser;
|
private readonly ChromiumWebBrowser browser;
|
||||||
private readonly PluginManager plugins;
|
private readonly PluginManager plugins;
|
||||||
private readonly UpdateHandler updates;
|
private readonly UpdateHandler updates;
|
||||||
|
private readonly FormNotificationTweet notification;
|
||||||
|
|
||||||
private FormSettings currentFormSettings;
|
private FormSettings currentFormSettings;
|
||||||
private FormAbout currentFormAbout;
|
private FormAbout currentFormAbout;
|
||||||
@@ -35,6 +41,9 @@ namespace TweetDck.Core{
|
|||||||
|
|
||||||
private FormWindowState prevState;
|
private FormWindowState prevState;
|
||||||
|
|
||||||
|
private TweetScreenshotManager notificationScreenshotManager;
|
||||||
|
private SoundNotification soundNotification;
|
||||||
|
|
||||||
public FormBrowser(PluginManager pluginManager, UpdaterSettings updaterSettings){
|
public FormBrowser(PluginManager pluginManager, UpdaterSettings updaterSettings){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
@@ -44,13 +53,20 @@ namespace TweetDck.Core{
|
|||||||
this.plugins.Reloaded += plugins_Reloaded;
|
this.plugins.Reloaded += plugins_Reloaded;
|
||||||
this.plugins.PluginChangedState += plugins_PluginChangedState;
|
this.plugins.PluginChangedState += plugins_PluginChangedState;
|
||||||
|
|
||||||
FormNotification notification = CreateNotificationForm(true);
|
this.notification = new FormNotificationTweet(this, plugins, NotificationFlags.TopMost){
|
||||||
notification.CanMoveWindow = () => false;
|
#if DEBUG
|
||||||
notification.Show();
|
CanMoveWindow = () => (ModifierKeys & Keys.Alt) == Keys.Alt
|
||||||
|
#else
|
||||||
|
CanMoveWindow = () => false
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
this.notification.Show();
|
||||||
|
|
||||||
this.browser = new ChromiumWebBrowser("https://tweetdeck.twitter.com/"){
|
this.browser = new ChromiumWebBrowser("https://tweetdeck.twitter.com/"){
|
||||||
MenuHandler = new ContextMenuBrowser(this),
|
MenuHandler = new ContextMenuBrowser(this),
|
||||||
DialogHandler = new DialogHandlerBrowser(this),
|
DialogHandler = new FileDialogHandler(this),
|
||||||
|
JsDialogHandler = new JavaScriptDialogHandler(),
|
||||||
LifeSpanHandler = new LifeSpanHandler()
|
LifeSpanHandler = new LifeSpanHandler()
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -60,12 +76,24 @@ namespace TweetDck.Core{
|
|||||||
|
|
||||||
this.browser.LoadingStateChanged += Browser_LoadingStateChanged;
|
this.browser.LoadingStateChanged += Browser_LoadingStateChanged;
|
||||||
this.browser.FrameLoadEnd += Browser_FrameLoadEnd;
|
this.browser.FrameLoadEnd += Browser_FrameLoadEnd;
|
||||||
this.browser.RegisterJsObject("$TD", new TweetDeckBridge(this, notification));
|
this.browser.RegisterAsyncJsObject("$TD", new TweetDeckBridge(this, notification));
|
||||||
this.browser.RegisterAsyncJsObject("$TDP", plugins.Bridge);
|
this.browser.RegisterAsyncJsObject("$TDP", plugins.Bridge);
|
||||||
|
|
||||||
Controls.Add(browser);
|
Controls.Add(browser);
|
||||||
|
|
||||||
Disposed += (sender, args) => browser.Dispose();
|
Controls.Add(new MenuStrip{ Visible = false }); // fixes Alt freezing the program in Win 10 Anniversary Update
|
||||||
|
|
||||||
|
Disposed += (sender, args) => {
|
||||||
|
browser.Dispose();
|
||||||
|
|
||||||
|
if (notificationScreenshotManager != null){
|
||||||
|
notificationScreenshotManager.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (soundNotification != null){
|
||||||
|
soundNotification.Dispose();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
this.trayIcon.ClickRestore += trayIcon_ClickRestore;
|
this.trayIcon.ClickRestore += trayIcon_ClickRestore;
|
||||||
this.trayIcon.ClickClose += trayIcon_ClickClose;
|
this.trayIcon.ClickClose += trayIcon_ClickClose;
|
||||||
@@ -73,13 +101,16 @@ namespace TweetDck.Core{
|
|||||||
|
|
||||||
UpdateTrayIcon();
|
UpdateTrayIcon();
|
||||||
|
|
||||||
|
Config.MuteToggled += Config_MuteToggled;
|
||||||
|
|
||||||
this.updates = new UpdateHandler(browser, this, updaterSettings);
|
this.updates = new UpdateHandler(browser, this, updaterSettings);
|
||||||
this.updates.UpdateAccepted += updates_UpdateAccepted;
|
this.updates.UpdateAccepted += updates_UpdateAccepted;
|
||||||
|
this.updates.UpdateDismissed += updates_UpdateDismissed;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ShowChildForm(Form form){
|
private void ShowChildForm(Form form){
|
||||||
|
form.VisibleChanged += (sender, args) => form.MoveToCenter(this);
|
||||||
form.Show(this);
|
form.Show(this);
|
||||||
form.Shown += (sender, args) => form.MoveToCenter(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ForceClose(){
|
public void ForceClose(){
|
||||||
@@ -87,10 +118,6 @@ namespace TweetDck.Core{
|
|||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
public FormNotification CreateNotificationForm(bool autoHide){
|
|
||||||
return new FormNotification(this, plugins, autoHide);
|
|
||||||
}
|
|
||||||
|
|
||||||
// window setup
|
// window setup
|
||||||
|
|
||||||
private void SetupWindow(){
|
private void SetupWindow(){
|
||||||
@@ -120,13 +147,21 @@ namespace TweetDck.Core{
|
|||||||
|
|
||||||
private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
|
private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
|
||||||
if (e.Frame.IsMain && BrowserUtils.IsTweetDeckWebsite(e.Frame)){
|
if (e.Frame.IsMain && BrowserUtils.IsTweetDeckWebsite(e.Frame)){
|
||||||
|
UpdateProperties();
|
||||||
ScriptLoader.ExecuteFile(e.Frame, "code.js");
|
ScriptLoader.ExecuteFile(e.Frame, "code.js");
|
||||||
|
ReinjectCustomCSS(Config.CustomBrowserCSS);
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
ScriptLoader.ExecuteFile(e.Frame, "debug.js");
|
||||||
|
#endif
|
||||||
|
|
||||||
if (plugins.HasAnyPlugin(PluginEnvironment.Browser)){
|
if (plugins.HasAnyPlugin(PluginEnvironment.Browser)){
|
||||||
ScriptLoader.ExecuteFile(e.Frame, PluginManager.PluginBrowserScriptFile);
|
ScriptLoader.ExecuteFile(e.Frame, PluginManager.PluginBrowserScriptFile);
|
||||||
ScriptLoader.ExecuteFile(e.Frame, PluginManager.PluginGlobalScriptFile);
|
ScriptLoader.ExecuteFile(e.Frame, PluginManager.PluginGlobalScriptFile);
|
||||||
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Browser, true);
|
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Browser, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TweetDeckBridge.ResetStaticProperties();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,7 +191,7 @@ namespace TweetDck.Core{
|
|||||||
private void FormBrowser_ResizeEnd(object sender, EventArgs e){ // also triggers when the window moves
|
private void FormBrowser_ResizeEnd(object sender, EventArgs e){ // also triggers when the window moves
|
||||||
if (!isLoaded)return;
|
if (!isLoaded)return;
|
||||||
|
|
||||||
if (Location.X != -32000){
|
if (Location != ControlExtensions.InvisibleLocation){
|
||||||
Config.BrowserWindow.Save(this);
|
Config.BrowserWindow.Save(this);
|
||||||
Config.Save();
|
Config.Save();
|
||||||
}
|
}
|
||||||
@@ -171,6 +206,10 @@ namespace TweetDck.Core{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Config_MuteToggled(object sender, EventArgs e){
|
||||||
|
UpdateProperties(PropertyBridge.Properties.MuteNotifications);
|
||||||
|
}
|
||||||
|
|
||||||
private void Config_TrayBehaviorChanged(object sender, EventArgs e){
|
private void Config_TrayBehaviorChanged(object sender, EventArgs e){
|
||||||
if (!isLoaded)return;
|
if (!isLoaded)return;
|
||||||
|
|
||||||
@@ -194,7 +233,7 @@ namespace TweetDck.Core{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void plugins_Reloaded(object sender, PluginLoadEventArgs e){
|
private void plugins_Reloaded(object sender, PluginLoadEventArgs e){
|
||||||
ReloadBrowser();
|
browser.GetBrowser().Reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void plugins_PluginChangedState(object sender, PluginChangedStateEventArgs e){
|
private void plugins_PluginChangedState(object sender, PluginChangedStateEventArgs e){
|
||||||
@@ -207,6 +246,7 @@ namespace TweetDck.Core{
|
|||||||
FormUpdateDownload downloadForm = new FormUpdateDownload(e.UpdateInfo);
|
FormUpdateDownload downloadForm = new FormUpdateDownload(e.UpdateInfo);
|
||||||
downloadForm.MoveToCenter(this);
|
downloadForm.MoveToCenter(this);
|
||||||
downloadForm.ShowDialog();
|
downloadForm.ShowDialog();
|
||||||
|
downloadForm.Dispose();
|
||||||
|
|
||||||
if (downloadForm.UpdateStatus == FormUpdateDownload.Status.Succeeded){
|
if (downloadForm.UpdateStatus == FormUpdateDownload.Status.Succeeded){
|
||||||
UpdateInstallerPath = downloadForm.InstallerPath;
|
UpdateInstallerPath = downloadForm.InstallerPath;
|
||||||
@@ -220,9 +260,19 @@ namespace TweetDck.Core{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updates_UpdateDismissed(object sender, UpdateDismissedEventArgs e){
|
||||||
|
Config.DismissedUpdate = e.VersionTag;
|
||||||
|
Config.Save();
|
||||||
|
}
|
||||||
|
|
||||||
protected override void WndProc(ref Message m){
|
protected override void WndProc(ref Message m){
|
||||||
if (isLoaded && m.Msg == Program.WindowRestoreMessage){
|
if (isLoaded && m.Msg == Program.WindowRestoreMessage){
|
||||||
|
using(Process process = Process.GetCurrentProcess()){
|
||||||
|
if (process.Id == m.WParam.ToInt32()){
|
||||||
trayIcon_ClickRestore(trayIcon, new EventArgs());
|
trayIcon_ClickRestore(trayIcon, new EventArgs());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,29 +284,61 @@ namespace TweetDck.Core{
|
|||||||
base.WndProc(ref m);
|
base.WndProc(ref m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// notification helpers
|
||||||
|
|
||||||
|
public FormNotificationMain CreateNotificationForm(NotificationFlags flags){
|
||||||
|
return new FormNotificationMain(this, plugins, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PauseNotification(){
|
||||||
|
notification.PauseNotification();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ResumeNotification(){
|
||||||
|
notification.ResumeNotification();
|
||||||
|
}
|
||||||
|
|
||||||
|
// javascript calls
|
||||||
|
|
||||||
|
public void ReinjectCustomCSS(string css){
|
||||||
|
browser.ExecuteScriptAsync("TDGF_reinjectCustomCSS", css == null ? string.Empty : css.Replace(Environment.NewLine, " "));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateProperties(PropertyBridge.Properties properties = PropertyBridge.Properties.All){
|
||||||
|
browser.ExecuteScriptAsync(PropertyBridge.GenerateScript(properties));
|
||||||
|
}
|
||||||
|
|
||||||
// callback handlers
|
// callback handlers
|
||||||
|
|
||||||
public void OpenSettings(){
|
public void OpenSettings(){
|
||||||
|
OpenSettings(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OpenSettings(int tabIndex){
|
||||||
if (currentFormSettings != null){
|
if (currentFormSettings != null){
|
||||||
currentFormSettings.BringToFront();
|
currentFormSettings.BringToFront();
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
bool prevEnableUpdateCheck = Config.EnableUpdateCheck;
|
bool prevEnableUpdateCheck = Config.EnableUpdateCheck;
|
||||||
|
|
||||||
currentFormSettings = new FormSettings(this, plugins, updates);
|
currentFormSettings = new FormSettings(this, plugins, updates, tabIndex);
|
||||||
|
|
||||||
currentFormSettings.FormClosed += (sender, args) => {
|
currentFormSettings.FormClosed += (sender, args) => {
|
||||||
currentFormSettings = null;
|
currentFormSettings = null;
|
||||||
|
|
||||||
if (!prevEnableUpdateCheck && Config.EnableUpdateCheck){
|
if (!prevEnableUpdateCheck && Config.EnableUpdateCheck){
|
||||||
|
updates.Settings.DismissedUpdate = string.Empty;
|
||||||
Config.DismissedUpdate = string.Empty;
|
Config.DismissedUpdate = string.Empty;
|
||||||
Config.Save();
|
Config.Save();
|
||||||
|
|
||||||
updates.Check(false);
|
updates.Check(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Config.EnableTrayHighlight){
|
if (!Config.EnableTrayHighlight){
|
||||||
trayIcon.HasNotifications = false;
|
trayIcon.HasNotifications = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdateProperties(PropertyBridge.Properties.ExpandLinksOnHover | PropertyBridge.Properties.HasCustomNotificationSound);
|
||||||
};
|
};
|
||||||
|
|
||||||
ShowChildForm(currentFormSettings);
|
ShowChildForm(currentFormSettings);
|
||||||
@@ -285,12 +367,32 @@ namespace TweetDck.Core{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnTweetNotification(){
|
public void OnTweetNotification(){ // may be called multiple times, once for each type of notification
|
||||||
if (Config.EnableTrayHighlight && !ContainsFocus){
|
if (Config.EnableTrayHighlight && !ContainsFocus){
|
||||||
trayIcon.HasNotifications = true;
|
trayIcon.HasNotifications = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void PlayNotificationSound(){
|
||||||
|
if (Config.NotificationSoundPath.Length == 0){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (soundNotification == null){
|
||||||
|
soundNotification = new SoundNotification(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
soundNotification.Play(Config.NotificationSoundPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnTweetScreenshotReady(string html, int width, int height){
|
||||||
|
if (notificationScreenshotManager == null){
|
||||||
|
notificationScreenshotManager = new TweetScreenshotManager(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
notificationScreenshotManager.Trigger(html, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
public void DisplayTooltip(string text){
|
public void DisplayTooltip(string text){
|
||||||
if (string.IsNullOrEmpty(text)){
|
if (string.IsNullOrEmpty(text)){
|
||||||
toolTip.Hide(this);
|
toolTip.Hide(this);
|
||||||
@@ -306,12 +408,14 @@ namespace TweetDck.Core{
|
|||||||
browser.ExecuteScriptAsync("TDGF_tryPasteImage()");
|
browser.ExecuteScriptAsync("TDGF_tryPasteImage()");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnImagePastedFinish(){
|
public void TriggerImageUpload(int offsetX, int offsetY){
|
||||||
browser.ExecuteScriptAsync("TDGF_tryPasteImageFinish()");
|
IBrowserHost host = browser.GetBrowser().GetHost();
|
||||||
|
host.SendMouseClickEvent(offsetX, offsetY, MouseButtonType.Left, false, 1, CefEventFlags.None);
|
||||||
|
host.SendMouseClickEvent(offsetX, offsetY, MouseButtonType.Left, true, 1, CefEventFlags.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ReloadBrowser(){
|
public void TriggerTweetScreenshot(){
|
||||||
browser.ExecuteScriptAsync("window.location.reload()");
|
browser.ExecuteScriptAsync("TDGF_triggerScreenshot()");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,375 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.Windows.Forms;
|
|
||||||
using CefSharp;
|
|
||||||
using CefSharp.WinForms;
|
|
||||||
using TweetDck.Configuration;
|
|
||||||
using TweetDck.Core.Handling;
|
|
||||||
using TweetDck.Resources;
|
|
||||||
using TweetDck.Core.Utils;
|
|
||||||
using TweetDck.Plugins;
|
|
||||||
using TweetDck.Plugins.Enums;
|
|
||||||
|
|
||||||
namespace TweetDck.Core{
|
|
||||||
sealed partial class FormNotification : Form{
|
|
||||||
private const string NotificationScriptFile = "notification.js";
|
|
||||||
|
|
||||||
private static readonly string NotificationScriptIdentifier = ScriptLoader.GetRootIdentifier(NotificationScriptFile);
|
|
||||||
private static readonly string PluginScriptIdentifier = ScriptLoader.GetRootIdentifier(PluginManager.PluginNotificationScriptFile);
|
|
||||||
|
|
||||||
public Func<bool> CanMoveWindow = () => true;
|
|
||||||
|
|
||||||
private readonly Form owner;
|
|
||||||
private readonly PluginManager plugins;
|
|
||||||
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;
|
|
||||||
|
|
||||||
private bool RequiresResize{
|
|
||||||
get{
|
|
||||||
return !prevDisplayTimer.HasValue || !prevFontSize.HasValue || prevDisplayTimer != Program.UserConfig.DisplayNotificationTimer || prevFontSize != TweetNotification.FontSizeLevel;
|
|
||||||
}
|
|
||||||
|
|
||||||
set{
|
|
||||||
if (value){
|
|
||||||
prevDisplayTimer = null;
|
|
||||||
prevFontSize = null;
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
prevDisplayTimer = Program.UserConfig.DisplayNotificationTimer;
|
|
||||||
prevFontSize = TweetNotification.FontSizeLevel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly string notificationJS;
|
|
||||||
private readonly string pluginJS;
|
|
||||||
|
|
||||||
protected override bool ShowWithoutActivation{
|
|
||||||
get{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool FreezeTimer { get; set; }
|
|
||||||
public bool ContextMenuOpen { get; set; }
|
|
||||||
public string CurrentUrl { get; private set; }
|
|
||||||
public string CurrentQuotedTweetUrl { get; set; }
|
|
||||||
|
|
||||||
public EventHandler Initialized;
|
|
||||||
private bool isInitialized;
|
|
||||||
|
|
||||||
private static int BaseClientWidth{
|
|
||||||
get{
|
|
||||||
int level = TweetNotification.FontSizeLevel;
|
|
||||||
return level == 0 ? 284 : (int)Math.Round(284.0*(1.0+0.05*level));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int BaseClientHeight{
|
|
||||||
get{
|
|
||||||
int level = TweetNotification.FontSizeLevel;
|
|
||||||
return level == 0 ? 118 : (int)Math.Round(118.0*(1.0+0.075*level));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FormNotification(FormBrowser owner, PluginManager pluginManager, bool autoHide){
|
|
||||||
InitializeComponent();
|
|
||||||
|
|
||||||
Text = Program.BrandName;
|
|
||||||
|
|
||||||
this.owner = owner;
|
|
||||||
this.plugins = pluginManager;
|
|
||||||
this.autoHide = autoHide;
|
|
||||||
|
|
||||||
owner.FormClosed += (sender, args) => Close();
|
|
||||||
|
|
||||||
notificationJS = ScriptLoader.LoadResource(NotificationScriptFile);
|
|
||||||
pluginJS = ScriptLoader.LoadResource(PluginManager.PluginNotificationScriptFile);
|
|
||||||
|
|
||||||
browser = new ChromiumWebBrowser("about:blank"){
|
|
||||||
MenuHandler = new ContextMenuNotification(this, autoHide),
|
|
||||||
LifeSpanHandler = new LifeSpanHandler()
|
|
||||||
};
|
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
browser.ConsoleMessage += BrowserUtils.HandleConsoleMessage;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
browser.IsBrowserInitializedChanged += Browser_IsBrowserInitializedChanged;
|
|
||||||
browser.FrameLoadEnd += Browser_FrameLoadEnd;
|
|
||||||
browser.RegisterJsObject("$TD", new TweetDeckBridge(owner, this));
|
|
||||||
browser.RegisterAsyncJsObject("$TDP", plugins.Bridge);
|
|
||||||
|
|
||||||
panelBrowser.Controls.Add(browser);
|
|
||||||
|
|
||||||
if (autoHide){
|
|
||||||
Program.UserConfig.MuteToggled += Config_MuteToggled;
|
|
||||||
Disposed += (sender, args) => Program.UserConfig.MuteToggled -= Config_MuteToggled;
|
|
||||||
}
|
|
||||||
|
|
||||||
mouseHookDelegate = MouseHookProc;
|
|
||||||
|
|
||||||
Disposed += FormNotification_Disposed;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void WndProc(ref Message m){
|
|
||||||
if (m.Msg == 0x0112 && (m.WParam.ToInt32() & 0xFFF0) == 0xF010 && !CanMoveWindow()){ // WM_SYSCOMMAND, SC_MOVE
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Config_MuteToggled(object sender, EventArgs e){
|
|
||||||
if (Program.UserConfig.MuteNotifications){
|
|
||||||
HideNotification(true);
|
|
||||||
}
|
|
||||||
else if (tweetQueue.Count > 0){
|
|
||||||
LoadNextNotification();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Browser_IsBrowserInitializedChanged(object sender, IsBrowserInitializedChangedEventArgs e){
|
|
||||||
if (e.IsBrowserInitialized && Initialized != null){
|
|
||||||
Initialized(this, new EventArgs());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
|
|
||||||
if (!e.Frame.IsMain)return;
|
|
||||||
|
|
||||||
if (!isInitialized && !Program.UserConfig.NotificationLegacyLoad){
|
|
||||||
isInitialized = true;
|
|
||||||
|
|
||||||
if (Initialized != null){
|
|
||||||
Initialized(this, new EventArgs());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (notificationJS != null && browser.Address != "about:blank"){
|
|
||||||
ScriptLoader.ExecuteScript(e.Frame, notificationJS, NotificationScriptIdentifier);
|
|
||||||
|
|
||||||
if (plugins.HasAnyPlugin(PluginEnvironment.Notification)){
|
|
||||||
ScriptLoader.ExecuteScript(e.Frame, pluginJS, PluginScriptIdentifier);
|
|
||||||
ScriptLoader.ExecuteFile(e.Frame, PluginManager.PluginGlobalScriptFile);
|
|
||||||
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Notification, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void FormNotification_FormClosing(object sender, FormClosingEventArgs e){
|
|
||||||
if (e.CloseReason == CloseReason.UserClosing){
|
|
||||||
HideNotification(false);
|
|
||||||
tweetQueue.Clear();
|
|
||||||
e.Cancel = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
tweetQueue.Enqueue(notification);
|
|
||||||
UpdateTitle();
|
|
||||||
|
|
||||||
if (totalTime == 0){
|
|
||||||
LoadNextNotification();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ShowNotificationForSettings(bool reset){
|
|
||||||
if (reset){
|
|
||||||
LoadTweet(TweetNotification.ExampleTweet);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
MoveToVisibleLocation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void HideNotification(bool loadBlank){
|
|
||||||
if (loadBlank || Program.UserConfig.NotificationLegacyLoad){
|
|
||||||
browser.LoadHtml("", "about:blank");
|
|
||||||
}
|
|
||||||
|
|
||||||
Location = new Point(-32000, -32000);
|
|
||||||
progressBarTimer.Value = Program.UserConfig.NotificationTimerCountDown ? 1000 : 0;
|
|
||||||
timerProgress.Stop();
|
|
||||||
totalTime = 0;
|
|
||||||
|
|
||||||
StopMouseHook();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnNotificationReady(){
|
|
||||||
UpdateTitle();
|
|
||||||
MoveToVisibleLocation();
|
|
||||||
timerProgress.Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void FinishCurrentTweet(){
|
|
||||||
if (tweetQueue.Count > 0){
|
|
||||||
LoadNextNotification();
|
|
||||||
}
|
|
||||||
else if (autoHide){
|
|
||||||
HideNotification(true);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
timerProgress.Stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LoadNextNotification(){
|
|
||||||
LoadTweet(tweetQueue.Dequeue());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LoadTweet(TweetNotification tweet){
|
|
||||||
CurrentUrl = tweet.Url;
|
|
||||||
CurrentQuotedTweetUrl = string.Empty; // load from JS
|
|
||||||
|
|
||||||
timerProgress.Stop();
|
|
||||||
totalTime = timeLeft = tweet.GetDisplayDuration(Program.UserConfig.NotificationDurationValue);
|
|
||||||
progressBarTimer.Value = Program.UserConfig.NotificationTimerCountDown ? 1000 : 0;
|
|
||||||
|
|
||||||
browser.LoadHtml(tweet.GenerateHtml(), "http://tweetdeck.twitter.com/?"+DateTime.Now.Ticks);
|
|
||||||
|
|
||||||
if (Program.UserConfig.NotificationLegacyLoad){
|
|
||||||
OnNotificationReady();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void MoveToVisibleLocation(){
|
|
||||||
UserConfig config = Program.UserConfig;
|
|
||||||
|
|
||||||
if (RequiresResize){
|
|
||||||
RequiresResize = false;
|
|
||||||
|
|
||||||
if (config.DisplayNotificationTimer){
|
|
||||||
ClientSize = new Size(BaseClientWidth, BaseClientHeight+4);
|
|
||||||
progressBarTimer.Visible = true;
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
ClientSize = new Size(BaseClientWidth, BaseClientHeight);
|
|
||||||
progressBarTimer.Visible = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
panelBrowser.Height = BaseClientHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
Screen screen = Screen.FromControl(owner);
|
|
||||||
|
|
||||||
if (config.NotificationDisplay > 0 && config.NotificationDisplay <= Screen.AllScreens.Length){
|
|
||||||
screen = Screen.AllScreens[config.NotificationDisplay-1];
|
|
||||||
}
|
|
||||||
|
|
||||||
bool needsReactivating = Location.X == -32000;
|
|
||||||
int edgeDist = config.NotificationEdgeDistance;
|
|
||||||
|
|
||||||
switch(config.NotificationPosition){
|
|
||||||
case TweetNotification.Position.TopLeft:
|
|
||||||
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);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TweetNotification.Position.BottomLeft:
|
|
||||||
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);
|
|
||||||
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.Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
Location = config.CustomNotificationPosition;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (needsReactivating){
|
|
||||||
NativeMethods.SetFormPos(this, NativeMethods.HWND_TOPMOST, NativeMethods.SWP_NOACTIVATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
StartMouseHook();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateTitle(){
|
|
||||||
Text = tweetQueue.Count > 0 ? Program.BrandName+" ("+tweetQueue.Count+" more left)" : Program.BrandName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DisplayTooltip(string text){
|
|
||||||
if (string.IsNullOrEmpty(text)){
|
|
||||||
toolTip.Hide(this);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
Point position = PointToClient(Cursor.Position);
|
|
||||||
position.Offset(20, 5);
|
|
||||||
toolTip.Show(text, this, position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -2,6 +2,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using TweetDck.Core.Bridge;
|
||||||
|
using TweetDck.Core.Controls;
|
||||||
using TweetDck.Core.Utils;
|
using TweetDck.Core.Utils;
|
||||||
|
|
||||||
namespace TweetDck.Core.Handling{
|
namespace TweetDck.Core.Handling{
|
||||||
@@ -12,6 +14,20 @@ namespace TweetDck.Core.Handling{
|
|||||||
private const int MenuSaveImage = 26503;
|
private const int MenuSaveImage = 26503;
|
||||||
private const int MenuCopyImageUrl = 26504;
|
private const int MenuCopyImageUrl = 26504;
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
private const int MenuOpenDevTools = 26599;
|
||||||
|
|
||||||
|
protected void AddDebugMenuItems(IMenuModel model){
|
||||||
|
model.AddItem((CefMenuCommand)MenuOpenDevTools, "Open dev tools");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private readonly Form form;
|
||||||
|
|
||||||
|
protected ContextMenuBase(Form form){
|
||||||
|
this.form = form;
|
||||||
|
}
|
||||||
|
|
||||||
public virtual void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
|
public virtual void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
|
||||||
if (parameters.TypeFlags.HasFlag(ContextMenuType.Link) && !parameters.UnfilteredLinkUrl.EndsWith("tweetdeck.twitter.com/#", StringComparison.Ordinal)){
|
if (parameters.TypeFlags.HasFlag(ContextMenuType.Link) && !parameters.UnfilteredLinkUrl.EndsWith("tweetdeck.twitter.com/#", StringComparison.Ordinal)){
|
||||||
model.AddItem((CefMenuCommand)MenuOpenLinkUrl, "Open link in browser");
|
model.AddItem((CefMenuCommand)MenuOpenLinkUrl, "Open link in browser");
|
||||||
@@ -34,7 +50,7 @@ namespace TweetDck.Core.Handling{
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case MenuCopyLinkUrl:
|
case MenuCopyLinkUrl:
|
||||||
Clipboard.SetText(string.IsNullOrEmpty(TweetDeckBridge.LastRightClickedLink) ? parameters.UnfilteredLinkUrl : TweetDeckBridge.LastRightClickedLink, TextDataFormat.UnicodeText);
|
SetClipboardText(string.IsNullOrEmpty(TweetDeckBridge.LastRightClickedLink) ? parameters.UnfilteredLinkUrl : TweetDeckBridge.LastRightClickedLink);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MenuOpenImage:
|
case MenuOpenImage:
|
||||||
@@ -65,8 +81,14 @@ namespace TweetDck.Core.Handling{
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case MenuCopyImageUrl:
|
case MenuCopyImageUrl:
|
||||||
Clipboard.SetText(parameters.SourceUrl, TextDataFormat.UnicodeText);
|
SetClipboardText(parameters.SourceUrl);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
case MenuOpenDevTools:
|
||||||
|
browserControl.ShowDevTools();
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -78,6 +100,10 @@ namespace TweetDck.Core.Handling{
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void SetClipboardText(string text){
|
||||||
|
form.InvokeAsyncSafe(() => WindowsUtils.SetClipboard(text, TextDataFormat.UnicodeText));
|
||||||
|
}
|
||||||
|
|
||||||
protected static void RemoveSeparatorIfLast(IMenuModel model){
|
protected static void RemoveSeparatorIfLast(IMenuModel model){
|
||||||
if (model.Count > 0 && model.GetTypeAt(model.Count-1) == MenuItemType.Separator){
|
if (model.Count > 0 && model.GetTypeAt(model.Count-1) == MenuItemType.Separator){
|
||||||
model.RemoveAt(model.Count-1);
|
model.RemoveAt(model.Count-1);
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
using CefSharp;
|
using CefSharp;
|
||||||
using System.Windows.Forms;
|
using TweetDck.Core.Bridge;
|
||||||
using TweetDck.Core.Controls;
|
using TweetDck.Core.Controls;
|
||||||
using TweetDck.Core.Utils;
|
using TweetDck.Core.Utils;
|
||||||
|
|
||||||
@@ -15,13 +15,14 @@ namespace TweetDck.Core.Handling{
|
|||||||
private const int MenuCopyTweetUrl = 26611;
|
private const int MenuCopyTweetUrl = 26611;
|
||||||
private const int MenuOpenQuotedTweetUrl = 26612;
|
private const int MenuOpenQuotedTweetUrl = 26612;
|
||||||
private const int MenuCopyQuotedTweetUrl = 26613;
|
private const int MenuCopyQuotedTweetUrl = 26613;
|
||||||
|
private const int MenuScreenshotTweet = 26614;
|
||||||
|
|
||||||
private readonly FormBrowser form;
|
private readonly FormBrowser form;
|
||||||
|
|
||||||
private string lastHighlightedTweet;
|
private string lastHighlightedTweet;
|
||||||
private string lastHighlightedQuotedTweet;
|
private string lastHighlightedQuotedTweet;
|
||||||
|
|
||||||
public ContextMenuBrowser(FormBrowser form){
|
public ContextMenuBrowser(FormBrowser form) : base(form){
|
||||||
this.form = form;
|
this.form = form;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,12 +33,16 @@ namespace TweetDck.Core.Handling{
|
|||||||
model.Remove(CefMenuCommand.ViewSource);
|
model.Remove(CefMenuCommand.ViewSource);
|
||||||
RemoveSeparatorIfLast(model);
|
RemoveSeparatorIfLast(model);
|
||||||
|
|
||||||
|
if (parameters.TypeFlags.HasFlag(ContextMenuType.Selection)){
|
||||||
|
model.AddSeparator();
|
||||||
|
}
|
||||||
|
|
||||||
base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);
|
base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);
|
||||||
|
|
||||||
lastHighlightedTweet = TweetDeckBridge.LastHighlightedTweet;
|
lastHighlightedTweet = TweetDeckBridge.LastHighlightedTweet;
|
||||||
lastHighlightedQuotedTweet = TweetDeckBridge.LastHighlightedQuotedTweet;
|
lastHighlightedQuotedTweet = TweetDeckBridge.LastHighlightedQuotedTweet;
|
||||||
|
|
||||||
if (!BrowserUtils.IsTweetDeckWebsite(frame)){
|
if (!BrowserUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){
|
||||||
lastHighlightedTweet = string.Empty;
|
lastHighlightedTweet = string.Empty;
|
||||||
lastHighlightedQuotedTweet = string.Empty;
|
lastHighlightedQuotedTweet = string.Empty;
|
||||||
}
|
}
|
||||||
@@ -45,6 +50,7 @@ namespace TweetDck.Core.Handling{
|
|||||||
if (!string.IsNullOrEmpty(lastHighlightedTweet) && (parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){
|
if (!string.IsNullOrEmpty(lastHighlightedTweet) && (parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){
|
||||||
model.AddItem((CefMenuCommand)MenuOpenTweetUrl, "Open tweet in browser");
|
model.AddItem((CefMenuCommand)MenuOpenTweetUrl, "Open tweet in browser");
|
||||||
model.AddItem((CefMenuCommand)MenuCopyTweetUrl, "Copy tweet address");
|
model.AddItem((CefMenuCommand)MenuCopyTweetUrl, "Copy tweet address");
|
||||||
|
model.AddItem((CefMenuCommand)MenuScreenshotTweet, "Screenshot tweet to clipboard");
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(lastHighlightedQuotedTweet)){
|
if (!string.IsNullOrEmpty(lastHighlightedQuotedTweet)){
|
||||||
model.AddSeparator();
|
model.AddSeparator();
|
||||||
@@ -68,7 +74,14 @@ namespace TweetDck.Core.Handling{
|
|||||||
globalMenu.AddItem((CefMenuCommand)MenuSettings, "Settings");
|
globalMenu.AddItem((CefMenuCommand)MenuSettings, "Settings");
|
||||||
globalMenu.AddItem((CefMenuCommand)MenuPlugins, "Plugins");
|
globalMenu.AddItem((CefMenuCommand)MenuPlugins, "Plugins");
|
||||||
globalMenu.AddItem((CefMenuCommand)MenuAbout, "About "+Program.BrandName);
|
globalMenu.AddItem((CefMenuCommand)MenuAbout, "About "+Program.BrandName);
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
globalMenu.AddSeparator();
|
||||||
|
AddDebugMenuItems(globalMenu);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RemoveSeparatorIfLast(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){
|
public override bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){
|
||||||
@@ -82,19 +95,19 @@ namespace TweetDck.Core.Handling{
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MenuSettings:
|
case MenuSettings:
|
||||||
form.InvokeSafe(form.OpenSettings);
|
form.InvokeAsyncSafe(form.OpenSettings);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MenuAbout:
|
case MenuAbout:
|
||||||
form.InvokeSafe(form.OpenAbout);
|
form.InvokeAsyncSafe(form.OpenAbout);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MenuPlugins:
|
case MenuPlugins:
|
||||||
form.InvokeSafe(form.OpenPlugins);
|
form.InvokeAsyncSafe(form.OpenPlugins);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MenuMute:
|
case MenuMute:
|
||||||
form.InvokeSafe(() => {
|
form.InvokeAsyncSafe(() => {
|
||||||
Program.UserConfig.MuteNotifications = !Program.UserConfig.MuteNotifications;
|
Program.UserConfig.MuteNotifications = !Program.UserConfig.MuteNotifications;
|
||||||
Program.UserConfig.Save();
|
Program.UserConfig.Save();
|
||||||
});
|
});
|
||||||
@@ -106,7 +119,11 @@ namespace TweetDck.Core.Handling{
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MenuCopyTweetUrl:
|
case MenuCopyTweetUrl:
|
||||||
Clipboard.SetText(lastHighlightedTweet, TextDataFormat.UnicodeText);
|
SetClipboardText(lastHighlightedTweet);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MenuScreenshotTweet:
|
||||||
|
form.InvokeAsyncSafe(form.TriggerTweetScreenshot);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MenuOpenQuotedTweetUrl:
|
case MenuOpenQuotedTweetUrl:
|
||||||
@@ -114,7 +131,7 @@ namespace TweetDck.Core.Handling{
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MenuCopyQuotedTweetUrl:
|
case MenuCopyQuotedTweetUrl:
|
||||||
Clipboard.SetText(lastHighlightedQuotedTweet, TextDataFormat.UnicodeText);
|
SetClipboardText(lastHighlightedQuotedTweet);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
using System.Windows.Forms;
|
using CefSharp;
|
||||||
using CefSharp;
|
|
||||||
using TweetDck.Core.Controls;
|
using TweetDck.Core.Controls;
|
||||||
|
using TweetDck.Core.Notification;
|
||||||
|
|
||||||
namespace TweetDck.Core.Handling{
|
namespace TweetDck.Core.Handling{
|
||||||
class ContextMenuNotification : ContextMenuBase{
|
class ContextMenuNotification : ContextMenuBase{
|
||||||
@@ -9,10 +9,10 @@ namespace TweetDck.Core.Handling{
|
|||||||
private const int MenuCopyTweetUrl = 26602;
|
private const int MenuCopyTweetUrl = 26602;
|
||||||
private const int MenuCopyQuotedTweetUrl = 26603;
|
private const int MenuCopyQuotedTweetUrl = 26603;
|
||||||
|
|
||||||
private readonly FormNotification form;
|
private readonly FormNotificationBase form;
|
||||||
private readonly bool enableCustomMenu;
|
private readonly bool enableCustomMenu;
|
||||||
|
|
||||||
public ContextMenuNotification(FormNotification form, bool enableCustomMenu){
|
public ContextMenuNotification(FormNotificationBase form, bool enableCustomMenu) : base(form){
|
||||||
this.form = form;
|
this.form = form;
|
||||||
this.enableCustomMenu = enableCustomMenu;
|
this.enableCustomMenu = enableCustomMenu;
|
||||||
}
|
}
|
||||||
@@ -20,6 +20,13 @@ namespace TweetDck.Core.Handling{
|
|||||||
public override void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
|
public override void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
|
||||||
model.Clear();
|
model.Clear();
|
||||||
|
|
||||||
|
if (parameters.TypeFlags.HasFlag(ContextMenuType.Selection)){
|
||||||
|
model.AddItem(CefMenuCommand.Copy, "Copy");
|
||||||
|
model.AddSeparator();
|
||||||
|
}
|
||||||
|
|
||||||
|
base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);
|
||||||
|
|
||||||
if (enableCustomMenu){
|
if (enableCustomMenu){
|
||||||
model.AddItem((CefMenuCommand)MenuSkipTweet, "Skip tweet");
|
model.AddItem((CefMenuCommand)MenuSkipTweet, "Skip tweet");
|
||||||
model.AddCheckItem((CefMenuCommand)MenuFreeze, "Freeze");
|
model.AddCheckItem((CefMenuCommand)MenuFreeze, "Freeze");
|
||||||
@@ -37,10 +44,13 @@ namespace TweetDck.Core.Handling{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);
|
#if DEBUG
|
||||||
|
AddDebugMenuItems(model);
|
||||||
|
#endif
|
||||||
|
|
||||||
RemoveSeparatorIfLast(model);
|
RemoveSeparatorIfLast(model);
|
||||||
|
|
||||||
form.InvokeSafe(() => form.ContextMenuOpen = true);
|
form.InvokeAsyncSafe(() => form.ContextMenuOpen = true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){
|
public override bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){
|
||||||
@@ -50,19 +60,19 @@ namespace TweetDck.Core.Handling{
|
|||||||
|
|
||||||
switch((int)commandId){
|
switch((int)commandId){
|
||||||
case MenuSkipTweet:
|
case MenuSkipTweet:
|
||||||
form.InvokeSafe(form.FinishCurrentTweet);
|
form.InvokeAsyncSafe(form.FinishCurrentNotification);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MenuFreeze:
|
case MenuFreeze:
|
||||||
form.InvokeSafe(() => form.FreezeTimer = !form.FreezeTimer);
|
form.InvokeAsyncSafe(() => form.FreezeTimer = !form.FreezeTimer);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MenuCopyTweetUrl:
|
case MenuCopyTweetUrl:
|
||||||
Clipboard.SetText(form.CurrentUrl, TextDataFormat.UnicodeText);
|
SetClipboardText(form.CurrentUrl);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MenuCopyQuotedTweetUrl:
|
case MenuCopyQuotedTweetUrl:
|
||||||
Clipboard.SetText(form.CurrentQuotedTweetUrl, TextDataFormat.UnicodeText);
|
SetClipboardText(form.CurrentQuotedTweetUrl);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,7 +81,7 @@ namespace TweetDck.Core.Handling{
|
|||||||
|
|
||||||
public override void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame){
|
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);
|
form.InvokeAsyncSafe(() => form.ContextMenuOpen = false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
using CefSharp;
|
using CefSharp;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using TweetDck.Core.Bridge;
|
||||||
using TweetDck.Core.Controls;
|
using TweetDck.Core.Controls;
|
||||||
|
|
||||||
namespace TweetDck.Core.Handling{
|
namespace TweetDck.Core.Handling{
|
||||||
class DialogHandlerBrowser : IDialogHandler{
|
class FileDialogHandler : IDialogHandler{
|
||||||
private readonly FormBrowser form;
|
private readonly FormBrowser form;
|
||||||
|
|
||||||
public DialogHandlerBrowser(FormBrowser form){
|
public FileDialogHandler(FormBrowser form){
|
||||||
this.form = form;
|
this.form = form;
|
||||||
}
|
}
|
||||||
|
|
49
Core/Handling/JavaScriptDialogHandler.cs
Normal file
49
Core/Handling/JavaScriptDialogHandler.cs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
using CefSharp;
|
||||||
|
using CefSharp.WinForms;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using TweetDck.Core.Controls;
|
||||||
|
using TweetDck.Core.Other;
|
||||||
|
|
||||||
|
namespace TweetDck.Core.Handling {
|
||||||
|
class JavaScriptDialogHandler : IJsDialogHandler{
|
||||||
|
bool IJsDialogHandler.OnJSDialog(IWebBrowser browserControl, IBrowser browser, string originUrl, CefJsDialogType dialogType, string messageText, string defaultPromptText, IJsDialogCallback callback, ref bool suppressMessage){
|
||||||
|
if (dialogType != CefJsDialogType.Alert && dialogType != CefJsDialogType.Confirm){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
((ChromiumWebBrowser)browserControl).InvokeSafe(() => {
|
||||||
|
FormMessage form = new FormMessage(Program.BrandName, messageText, MessageBoxIcon.None);
|
||||||
|
Button btnConfirm;
|
||||||
|
|
||||||
|
if (dialogType == CefJsDialogType.Alert){
|
||||||
|
btnConfirm = form.AddButton("OK");
|
||||||
|
}
|
||||||
|
else if (dialogType == CefJsDialogType.Confirm){
|
||||||
|
form.AddButton("No");
|
||||||
|
btnConfirm = form.AddButton("Yes");
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (form.ShowDialog() == DialogResult.OK && form.ClickedButton == btnConfirm){
|
||||||
|
callback.Continue(true);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
callback.Continue(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
form.Dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IJsDialogHandler.OnJSBeforeUnload(IWebBrowser browserControl, IBrowser browser, string message, bool isReload, IJsDialogCallback callback){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IJsDialogHandler.OnResetDialogState(IWebBrowser browserControl, IBrowser browser){}
|
||||||
|
void IJsDialogHandler.OnDialogClosed(IWebBrowser browserControl, IBrowser browser){}
|
||||||
|
}
|
||||||
|
}
|
@@ -7,10 +7,11 @@ namespace TweetDck.Core.Handling{
|
|||||||
newBrowser = null;
|
newBrowser = null;
|
||||||
|
|
||||||
switch(targetDisposition){
|
switch(targetDisposition){
|
||||||
|
case WindowOpenDisposition.SingletonTab: // TODO remove when CefSharp is updated to 57; enums don't line up in 55
|
||||||
case WindowOpenDisposition.NewBackgroundTab:
|
case WindowOpenDisposition.NewBackgroundTab:
|
||||||
case WindowOpenDisposition.NewForegroundTab:
|
case WindowOpenDisposition.NewForegroundTab:
|
||||||
case WindowOpenDisposition.NewPopup:
|
case WindowOpenDisposition.NewPopup:
|
||||||
case WindowOpenDisposition.NewWindow:
|
// TODO case WindowOpenDisposition.NewWindow:
|
||||||
BrowserUtils.OpenExternalBrowser(targetUrl);
|
BrowserUtils.OpenExternalBrowser(targetUrl);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
66
Core/Notification/FormNotificationBase.Designer.cs
generated
Normal file
66
Core/Notification/FormNotificationBase.Designer.cs
generated
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
namespace TweetDck.Core.Notification {
|
||||||
|
partial class FormNotificationBase {
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up any resources being used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose(bool disposing) {
|
||||||
|
if (disposing && (components != null)) {
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Windows Form Designer generated code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent() {
|
||||||
|
this.components = new System.ComponentModel.Container();
|
||||||
|
this.panelBrowser = new System.Windows.Forms.Panel();
|
||||||
|
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// panelBrowser
|
||||||
|
//
|
||||||
|
this.panelBrowser.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.panelBrowser.BackColor = System.Drawing.Color.White;
|
||||||
|
this.panelBrowser.Location = new System.Drawing.Point(0, 0);
|
||||||
|
this.panelBrowser.Margin = new System.Windows.Forms.Padding(0);
|
||||||
|
this.panelBrowser.Name = "panelBrowser";
|
||||||
|
this.panelBrowser.Size = new System.Drawing.Size(284, 122);
|
||||||
|
this.panelBrowser.TabIndex = 0;
|
||||||
|
//
|
||||||
|
// FormNotification
|
||||||
|
//
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.BackColor = System.Drawing.SystemColors.Control;
|
||||||
|
this.ClientSize = new System.Drawing.Size(284, 122);
|
||||||
|
this.Controls.Add(this.panelBrowser);
|
||||||
|
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
|
||||||
|
this.Location = TweetDck.Core.Controls.ControlExtensions.InvisibleLocation;
|
||||||
|
this.MaximizeBox = false;
|
||||||
|
this.MinimizeBox = false;
|
||||||
|
this.Name = "FormNotification";
|
||||||
|
this.ShowIcon = false;
|
||||||
|
this.ShowInTaskbar = false;
|
||||||
|
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
protected System.Windows.Forms.Panel panelBrowser;
|
||||||
|
private System.Windows.Forms.ToolTip toolTip;
|
||||||
|
}
|
||||||
|
}
|
206
Core/Notification/FormNotificationBase.cs
Normal file
206
Core/Notification/FormNotificationBase.cs
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using CefSharp;
|
||||||
|
using CefSharp.WinForms;
|
||||||
|
using TweetDck.Configuration;
|
||||||
|
using TweetDck.Core.Controls;
|
||||||
|
using TweetDck.Core.Handling;
|
||||||
|
using TweetDck.Core.Utils;
|
||||||
|
|
||||||
|
namespace TweetDck.Core.Notification{
|
||||||
|
partial class FormNotificationBase : Form{
|
||||||
|
public bool IsNotificationVisible{
|
||||||
|
get{
|
||||||
|
return Location != ControlExtensions.InvisibleLocation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public new Point Location{
|
||||||
|
get{
|
||||||
|
return base.Location;
|
||||||
|
}
|
||||||
|
|
||||||
|
set{
|
||||||
|
Visible = (base.Location = value) != ControlExtensions.InvisibleLocation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Func<bool> CanMoveWindow = () => true;
|
||||||
|
|
||||||
|
private readonly Control owner;
|
||||||
|
protected readonly NotificationFlags flags;
|
||||||
|
protected readonly ChromiumWebBrowser browser;
|
||||||
|
|
||||||
|
private int pauseCounter;
|
||||||
|
|
||||||
|
public bool IsPaused{
|
||||||
|
get{
|
||||||
|
return pauseCounter > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool ShowWithoutActivation{
|
||||||
|
get{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool FreezeTimer { get; set; }
|
||||||
|
public bool ContextMenuOpen { get; set; }
|
||||||
|
public string CurrentUrl { get; private set; }
|
||||||
|
public string CurrentQuotedTweetUrl { get; set; }
|
||||||
|
|
||||||
|
public event EventHandler Initialized;
|
||||||
|
|
||||||
|
public FormNotificationBase(Form owner, NotificationFlags flags){
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
this.owner = owner;
|
||||||
|
this.flags = flags;
|
||||||
|
|
||||||
|
owner.FormClosed += owner_FormClosed;
|
||||||
|
|
||||||
|
browser = new ChromiumWebBrowser("about:blank"){
|
||||||
|
MenuHandler = new ContextMenuNotification(this, !flags.HasFlag(NotificationFlags.DisableContextMenu)),
|
||||||
|
LifeSpanHandler = new LifeSpanHandler()
|
||||||
|
};
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
browser.ConsoleMessage += BrowserUtils.HandleConsoleMessage;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
browser.IsBrowserInitializedChanged += Browser_IsBrowserInitializedChanged;
|
||||||
|
|
||||||
|
panelBrowser.Controls.Add(browser);
|
||||||
|
|
||||||
|
Disposed += (sender, args) => {
|
||||||
|
browser.Dispose();
|
||||||
|
owner.FormClosed -= owner_FormClosed;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ReSharper disable once VirtualMemberCallInContructor
|
||||||
|
UpdateTitle();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void WndProc(ref Message m){
|
||||||
|
if (m.Msg == 0x0112 && (m.WParam.ToInt32() & 0xFFF0) == 0xF010 && !CanMoveWindow()){ // WM_SYSCOMMAND, SC_MOVE
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
base.WndProc(ref m);
|
||||||
|
}
|
||||||
|
|
||||||
|
// event handlers
|
||||||
|
|
||||||
|
private void owner_FormClosed(object sender, FormClosedEventArgs e){
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Browser_IsBrowserInitializedChanged(object sender, IsBrowserInitializedChangedEventArgs e){
|
||||||
|
if (e.IsBrowserInitialized && Initialized != null){
|
||||||
|
Initialized(this, new EventArgs());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// notification methods
|
||||||
|
|
||||||
|
public virtual void HideNotification(bool loadBlank){
|
||||||
|
if (loadBlank){
|
||||||
|
browser.LoadHtml("", "about:blank");
|
||||||
|
}
|
||||||
|
|
||||||
|
Location = ControlExtensions.InvisibleLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void FinishCurrentNotification(){}
|
||||||
|
|
||||||
|
public virtual void PauseNotification(){
|
||||||
|
if (pauseCounter++ == 0 && IsNotificationVisible){
|
||||||
|
Location = ControlExtensions.InvisibleLocation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void ResumeNotification(){
|
||||||
|
if (pauseCounter > 0){
|
||||||
|
--pauseCounter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void LoadTweet(TweetNotification tweet){
|
||||||
|
CurrentUrl = tweet.Url;
|
||||||
|
CurrentQuotedTweetUrl = string.Empty; // load from JS
|
||||||
|
|
||||||
|
string bodyClasses = browser.Bounds.Contains(PointToClient(Cursor.Position)) ? "td-hover" : string.Empty;
|
||||||
|
browser.LoadHtml(tweet.GenerateHtml(bodyClasses), "http://tweetdeck.twitter.com/?"+DateTime.Now.Ticks);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void SetNotificationSize(int width, int height){
|
||||||
|
ClientSize = new Size(width, height);
|
||||||
|
panelBrowser.Height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void MoveToVisibleLocation(){
|
||||||
|
UserConfig config = Program.UserConfig;
|
||||||
|
|
||||||
|
Screen screen = Screen.FromControl(owner);
|
||||||
|
|
||||||
|
if (config.NotificationDisplay > 0 && config.NotificationDisplay <= Screen.AllScreens.Length){
|
||||||
|
screen = Screen.AllScreens[config.NotificationDisplay-1];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool needsReactivating = Location == ControlExtensions.InvisibleLocation;
|
||||||
|
int edgeDist = config.NotificationEdgeDistance;
|
||||||
|
|
||||||
|
switch(config.NotificationPosition){
|
||||||
|
case TweetNotification.Position.TopLeft:
|
||||||
|
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);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TweetNotification.Position.BottomLeft:
|
||||||
|
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);
|
||||||
|
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.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
Location = config.CustomNotificationPosition;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needsReactivating && flags.HasFlag(NotificationFlags.TopMost)){
|
||||||
|
NativeMethods.SetFormPos(this, NativeMethods.HWND_TOPMOST, NativeMethods.SWP_NOACTIVATE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnNotificationReady(){
|
||||||
|
MoveToVisibleLocation();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void UpdateTitle(){
|
||||||
|
Text = Program.BrandName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DisplayTooltip(string text){
|
||||||
|
if (string.IsNullOrEmpty(text)){
|
||||||
|
toolTip.Hide(this);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
Point position = PointToClient(Cursor.Position);
|
||||||
|
position.Offset(20, 5);
|
||||||
|
toolTip.Show(text, this, position); // TODO figure out flickering when moving the mouse
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,7 +1,5 @@
|
|||||||
using TweetDck.Core.Controls;
|
namespace TweetDck.Core.Notification {
|
||||||
|
partial class FormNotificationMain {
|
||||||
namespace TweetDck.Core {
|
|
||||||
sealed partial class FormNotification {
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Required designer variable.
|
/// Required designer variable.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -26,22 +24,15 @@ namespace TweetDck.Core {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void InitializeComponent() {
|
private void InitializeComponent() {
|
||||||
this.components = new System.ComponentModel.Container();
|
this.components = new System.ComponentModel.Container();
|
||||||
this.panelBrowser = new System.Windows.Forms.Panel();
|
this.timerDisplayDelay = new System.Windows.Forms.Timer(this.components);
|
||||||
this.timerProgress = new System.Windows.Forms.Timer(this.components);
|
this.timerProgress = new System.Windows.Forms.Timer(this.components);
|
||||||
this.progressBarTimer = new TweetDck.Core.Controls.FlatProgressBar();
|
this.progressBarTimer = new TweetDck.Core.Controls.FlatProgressBar();
|
||||||
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
//
|
//
|
||||||
// panelBrowser
|
// timerDisplayDelay
|
||||||
//
|
//
|
||||||
this.panelBrowser.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
this.timerDisplayDelay.Interval = 17;
|
||||||
| System.Windows.Forms.AnchorStyles.Right)));
|
this.timerDisplayDelay.Tick += new System.EventHandler(this.timerDisplayDelay_Tick);
|
||||||
this.panelBrowser.BackColor = System.Drawing.Color.White;
|
|
||||||
this.panelBrowser.Location = new System.Drawing.Point(0, 0);
|
|
||||||
this.panelBrowser.Margin = new System.Windows.Forms.Padding(0);
|
|
||||||
this.panelBrowser.Name = "panelBrowser";
|
|
||||||
this.panelBrowser.Size = new System.Drawing.Size(284, 118);
|
|
||||||
this.panelBrowser.TabIndex = 0;
|
|
||||||
//
|
//
|
||||||
// timerProgress
|
// timerProgress
|
||||||
//
|
//
|
||||||
@@ -68,15 +59,6 @@ namespace TweetDck.Core {
|
|||||||
this.BackColor = System.Drawing.SystemColors.Control;
|
this.BackColor = System.Drawing.SystemColors.Control;
|
||||||
this.ClientSize = new System.Drawing.Size(284, 122);
|
this.ClientSize = new System.Drawing.Size(284, 122);
|
||||||
this.Controls.Add(this.progressBarTimer);
|
this.Controls.Add(this.progressBarTimer);
|
||||||
this.Controls.Add(this.panelBrowser);
|
|
||||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
|
|
||||||
this.Location = new System.Drawing.Point(-32000, -32000);
|
|
||||||
this.MaximizeBox = false;
|
|
||||||
this.MinimizeBox = false;
|
|
||||||
this.Name = "FormNotification";
|
|
||||||
this.ShowIcon = false;
|
|
||||||
this.ShowInTaskbar = false;
|
|
||||||
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
|
|
||||||
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FormNotification_FormClosing);
|
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FormNotification_FormClosing);
|
||||||
this.ResumeLayout(false);
|
this.ResumeLayout(false);
|
||||||
|
|
||||||
@@ -84,9 +66,8 @@ namespace TweetDck.Core {
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private System.Windows.Forms.Panel panelBrowser;
|
private System.Windows.Forms.Timer timerDisplayDelay;
|
||||||
|
protected System.Windows.Forms.Timer timerProgress;
|
||||||
private Controls.FlatProgressBar progressBarTimer;
|
private Controls.FlatProgressBar progressBarTimer;
|
||||||
private System.Windows.Forms.Timer timerProgress;
|
|
||||||
private System.Windows.Forms.ToolTip toolTip;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
243
Core/Notification/FormNotificationMain.cs
Normal file
243
Core/Notification/FormNotificationMain.cs
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using CefSharp;
|
||||||
|
using TweetDck.Core.Bridge;
|
||||||
|
using TweetDck.Core.Controls;
|
||||||
|
using TweetDck.Core.Utils;
|
||||||
|
using TweetDck.Plugins;
|
||||||
|
using TweetDck.Plugins.Enums;
|
||||||
|
using TweetDck.Resources;
|
||||||
|
|
||||||
|
namespace TweetDck.Core.Notification{
|
||||||
|
partial class FormNotificationMain : FormNotificationBase{
|
||||||
|
private const string NotificationScriptFile = "notification.js";
|
||||||
|
|
||||||
|
private static readonly string NotificationScriptIdentifier = ScriptLoader.GetRootIdentifier(NotificationScriptFile);
|
||||||
|
private static readonly string PluginScriptIdentifier = ScriptLoader.GetRootIdentifier(PluginManager.PluginNotificationScriptFile);
|
||||||
|
|
||||||
|
private static readonly string NotificationJS, PluginJS;
|
||||||
|
|
||||||
|
static FormNotificationMain(){
|
||||||
|
NotificationJS = ScriptLoader.LoadResource(NotificationScriptFile);
|
||||||
|
PluginJS = ScriptLoader.LoadResource(PluginManager.PluginNotificationScriptFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int BaseClientWidth{
|
||||||
|
get{
|
||||||
|
int level = TweetNotification.FontSizeLevel;
|
||||||
|
return level == 0 ? 284 : (int)Math.Round(284.0*(1.0+0.05*level));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int BaseClientHeight{
|
||||||
|
get{
|
||||||
|
int level = TweetNotification.FontSizeLevel;
|
||||||
|
return level == 0 ? 118 : (int)Math.Round(118.0*(1.0+0.075*level));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly PluginManager plugins;
|
||||||
|
|
||||||
|
protected int timeLeft, totalTime;
|
||||||
|
protected bool pausedDuringNotification;
|
||||||
|
|
||||||
|
private readonly NativeMethods.HookProc mouseHookDelegate;
|
||||||
|
private IntPtr mouseHook;
|
||||||
|
|
||||||
|
private bool? prevDisplayTimer;
|
||||||
|
private int? prevFontSize;
|
||||||
|
|
||||||
|
private bool RequiresResize{
|
||||||
|
get{
|
||||||
|
return !prevDisplayTimer.HasValue || !prevFontSize.HasValue || prevDisplayTimer != Program.UserConfig.DisplayNotificationTimer || prevFontSize != TweetNotification.FontSizeLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
set{
|
||||||
|
if (value){
|
||||||
|
prevDisplayTimer = null;
|
||||||
|
prevFontSize = null;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
prevDisplayTimer = Program.UserConfig.DisplayNotificationTimer;
|
||||||
|
prevFontSize = TweetNotification.FontSizeLevel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public FormNotificationMain(FormBrowser owner, PluginManager pluginManager, NotificationFlags flags) : base(owner, flags){
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
this.plugins = pluginManager;
|
||||||
|
|
||||||
|
browser.RegisterAsyncJsObject("$TD", new TweetDeckBridge(owner, this));
|
||||||
|
browser.RegisterAsyncJsObject("$TDP", plugins.Bridge);
|
||||||
|
|
||||||
|
browser.LoadingStateChanged += Browser_LoadingStateChanged;
|
||||||
|
browser.FrameLoadEnd += Browser_FrameLoadEnd;
|
||||||
|
|
||||||
|
mouseHookDelegate = MouseHookProc;
|
||||||
|
Disposed += (sender, args) => StopMouseHook();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 FormNotification_FormClosing(object sender, FormClosingEventArgs e){
|
||||||
|
if (e.CloseReason == CloseReason.UserClosing){
|
||||||
|
HideNotification(false);
|
||||||
|
e.Cancel = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
|
||||||
|
if (!e.IsLoading && browser.Address != "about:blank"){
|
||||||
|
this.InvokeSafe(() => {
|
||||||
|
Visible = true; // ensures repaint before moving the window to a visible location
|
||||||
|
timerDisplayDelay.Start();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
|
||||||
|
if (e.Frame.IsMain && NotificationJS != null && browser.Address != "about:blank"){
|
||||||
|
e.Frame.ExecuteJavaScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Properties.ExpandLinksOnHover));
|
||||||
|
ScriptLoader.ExecuteScript(e.Frame, NotificationJS, NotificationScriptIdentifier);
|
||||||
|
|
||||||
|
if (plugins.HasAnyPlugin(PluginEnvironment.Notification)){
|
||||||
|
ScriptLoader.ExecuteScript(e.Frame, PluginJS, PluginScriptIdentifier);
|
||||||
|
ScriptLoader.ExecuteFile(e.Frame, PluginManager.PluginGlobalScriptFile);
|
||||||
|
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Notification, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void timerDisplayDelay_Tick(object sender, EventArgs e){
|
||||||
|
OnNotificationReady();
|
||||||
|
timerDisplayDelay.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void timerHideProgress_Tick(object sender, EventArgs e){
|
||||||
|
if (Bounds.Contains(Cursor.Position) || FreezeTimer || ContextMenuOpen)return;
|
||||||
|
|
||||||
|
timeLeft -= timerProgress.Interval;
|
||||||
|
|
||||||
|
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){
|
||||||
|
FinishCurrentNotification();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// notification methods
|
||||||
|
|
||||||
|
public virtual void ShowNotification(TweetNotification notification){
|
||||||
|
LoadTweet(notification);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowNotificationForSettings(bool reset){
|
||||||
|
if (reset){
|
||||||
|
LoadTweet(TweetNotification.ExampleTweet);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
PrepareAndDisplayWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void HideNotification(bool loadBlank){
|
||||||
|
base.HideNotification(loadBlank);
|
||||||
|
|
||||||
|
progressBarTimer.Value = Program.UserConfig.NotificationTimerCountDown ? 1000 : 0;
|
||||||
|
timerProgress.Stop();
|
||||||
|
totalTime = 0;
|
||||||
|
|
||||||
|
StopMouseHook();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void FinishCurrentNotification(){
|
||||||
|
timerProgress.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PauseNotification(){
|
||||||
|
if (!IsPaused){
|
||||||
|
pausedDuringNotification = IsNotificationVisible;
|
||||||
|
timerProgress.Stop();
|
||||||
|
StopMouseHook();
|
||||||
|
}
|
||||||
|
|
||||||
|
base.PauseNotification();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ResumeNotification(){
|
||||||
|
bool wasPaused = IsPaused;
|
||||||
|
base.ResumeNotification();
|
||||||
|
|
||||||
|
if (wasPaused && !IsPaused && pausedDuringNotification){
|
||||||
|
OnNotificationReady();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadTweet(TweetNotification tweet){
|
||||||
|
timerProgress.Stop();
|
||||||
|
totalTime = timeLeft = tweet.GetDisplayDuration(Program.UserConfig.NotificationDurationValue);
|
||||||
|
progressBarTimer.Value = Program.UserConfig.NotificationTimerCountDown ? 1000 : 0;
|
||||||
|
|
||||||
|
base.LoadTweet(tweet);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void SetNotificationSize(int width, int height){
|
||||||
|
if (Program.UserConfig.DisplayNotificationTimer){
|
||||||
|
ClientSize = new Size(width, height+4);
|
||||||
|
progressBarTimer.Visible = true;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
ClientSize = new Size(width, height);
|
||||||
|
progressBarTimer.Visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
panelBrowser.Height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PrepareAndDisplayWindow(){
|
||||||
|
if (RequiresResize){
|
||||||
|
RequiresResize = false;
|
||||||
|
SetNotificationSize(BaseClientWidth, BaseClientHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
MoveToVisibleLocation();
|
||||||
|
StartMouseHook();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnNotificationReady(){
|
||||||
|
PrepareAndDisplayWindow();
|
||||||
|
timerProgress.Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
85
Core/Notification/FormNotificationTweet.cs
Normal file
85
Core/Notification/FormNotificationTweet.cs
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using TweetDck.Plugins;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace TweetDck.Core.Notification{
|
||||||
|
sealed class FormNotificationTweet : FormNotificationMain{
|
||||||
|
private readonly Queue<TweetNotification> tweetQueue = new Queue<TweetNotification>(4);
|
||||||
|
|
||||||
|
public FormNotificationTweet(FormBrowser owner, PluginManager pluginManager, NotificationFlags flags) : base(owner, pluginManager, flags){
|
||||||
|
Program.UserConfig.MuteToggled += Config_MuteToggled;
|
||||||
|
Disposed += (sender, args) => Program.UserConfig.MuteToggled -= Config_MuteToggled;
|
||||||
|
|
||||||
|
if (Program.UserConfig.MuteNotifications){
|
||||||
|
PauseNotification();
|
||||||
|
}
|
||||||
|
|
||||||
|
FormClosing += FormNotificationTweet_FormClosing;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FormNotificationTweet_FormClosing(object sender, FormClosingEventArgs e){
|
||||||
|
if (e.CloseReason == CloseReason.UserClosing){
|
||||||
|
tweetQueue.Clear(); // already canceled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// event handlers
|
||||||
|
|
||||||
|
private void Config_MuteToggled(object sender, EventArgs e){
|
||||||
|
if (Program.UserConfig.MuteNotifications){
|
||||||
|
PauseNotification();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
ResumeNotification();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// notification methods
|
||||||
|
|
||||||
|
public override void ShowNotification(TweetNotification notification){
|
||||||
|
if (IsPaused){
|
||||||
|
tweetQueue.Enqueue(notification);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
tweetQueue.Enqueue(notification);
|
||||||
|
UpdateTitle();
|
||||||
|
|
||||||
|
if (totalTime == 0){
|
||||||
|
LoadNextNotification();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void FinishCurrentNotification(){
|
||||||
|
if (tweetQueue.Count > 0){
|
||||||
|
LoadNextNotification();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
HideNotification(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ResumeNotification(){
|
||||||
|
bool wasPaused = IsPaused;
|
||||||
|
base.ResumeNotification();
|
||||||
|
|
||||||
|
if (wasPaused && !IsPaused && !pausedDuringNotification && tweetQueue.Count > 0){
|
||||||
|
LoadNextNotification();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadNextNotification(){
|
||||||
|
LoadTweet(tweetQueue.Dequeue());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateTitle(){
|
||||||
|
Text = tweetQueue.Count > 0 ? Program.BrandName+" ("+tweetQueue.Count+" more left)" : Program.BrandName;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnNotificationReady(){
|
||||||
|
UpdateTitle();
|
||||||
|
base.OnNotificationReady();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
Core/Notification/NotificationFlags.cs
Normal file
10
Core/Notification/NotificationFlags.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace TweetDck.Core.Notification{
|
||||||
|
[Flags]
|
||||||
|
public enum NotificationFlags{
|
||||||
|
None = 0,
|
||||||
|
DisableContextMenu = 1,
|
||||||
|
TopMost = 2
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,59 @@
|
|||||||
|
using System;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using CefSharp;
|
||||||
|
using TweetDck.Core.Bridge;
|
||||||
|
using TweetDck.Core.Controls;
|
||||||
|
using TweetDck.Resources;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Drawing.Imaging;
|
||||||
|
using TweetDck.Core.Utils;
|
||||||
|
|
||||||
|
namespace TweetDck.Core.Notification.Screenshot{
|
||||||
|
sealed class FormNotificationScreenshotable : FormNotificationBase{
|
||||||
|
public FormNotificationScreenshotable(Action callback, Form owner, NotificationFlags flags) : base(owner, flags){
|
||||||
|
browser.RegisterAsyncJsObject("$TD_NotificationScreenshot", new CallbackBridge(this, callback));
|
||||||
|
|
||||||
|
browser.FrameLoadEnd += (sender, args) => {
|
||||||
|
if (args.Frame.IsMain && browser.Address != "about:blank"){
|
||||||
|
ScriptLoader.ExecuteScript(args.Frame, "window.setTimeout($TD_NotificationScreenshot.trigger, 25)", "gen:screenshot");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadNotificationForScreenshot(TweetNotification tweet, int width, int height){
|
||||||
|
browser.LoadHtml(tweet.GenerateHtml(enableCustomCSS: false), "http://tweetdeck.twitter.com/?"+DateTime.Now.Ticks);
|
||||||
|
|
||||||
|
Location = ControlExtensions.InvisibleLocation;
|
||||||
|
SetNotificationSize(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TakeScreenshotAndHide(){
|
||||||
|
MoveToVisibleLocation();
|
||||||
|
|
||||||
|
bool border = Program.UserConfig.ShowScreenshotBorder;
|
||||||
|
IntPtr context = NativeMethods.GetDeviceContext(this, border);
|
||||||
|
|
||||||
|
if (context == IntPtr.Zero){
|
||||||
|
MessageBox.Show("Could not retrieve a graphics context handle for the notification window to take the screenshot.", "Screenshot Failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
using(Bitmap bmp = new Bitmap(border ? Width : ClientSize.Width, border ? Height : ClientSize.Height, PixelFormat.Format32bppRgb)){
|
||||||
|
try{
|
||||||
|
NativeMethods.RenderSourceIntoBitmap(context, bmp);
|
||||||
|
}finally{
|
||||||
|
NativeMethods.ReleaseDeviceContext(this, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
Clipboard.SetImage(bmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset(){
|
||||||
|
Location = ControlExtensions.InvisibleLocation;
|
||||||
|
browser.LoadHtml("", "about:blank");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
39
Core/Notification/Screenshot/TweetScreenshotManager.cs
Normal file
39
Core/Notification/Screenshot/TweetScreenshotManager.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using System;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using TweetDck.Core.Utils;
|
||||||
|
|
||||||
|
namespace TweetDck.Core.Notification.Screenshot{
|
||||||
|
sealed class TweetScreenshotManager : IDisposable{
|
||||||
|
private readonly FormNotificationScreenshotable screenshot;
|
||||||
|
private readonly Timer timeout;
|
||||||
|
|
||||||
|
public TweetScreenshotManager(Form owner){
|
||||||
|
this.screenshot = new FormNotificationScreenshotable(Callback, owner, NotificationFlags.DisableContextMenu | NotificationFlags.TopMost){
|
||||||
|
CanMoveWindow = () => false
|
||||||
|
};
|
||||||
|
|
||||||
|
this.timeout = WindowsUtils.CreateSingleTickTimer(10000);
|
||||||
|
this.timeout.Tick += (sender, args) => screenshot.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Trigger(string html, int width, int height){
|
||||||
|
screenshot.LoadNotificationForScreenshot(new TweetNotification(html, string.Empty, 0), width, height);
|
||||||
|
screenshot.Show();
|
||||||
|
timeout.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Callback(){
|
||||||
|
if (!timeout.Enabled){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
timeout.Stop();
|
||||||
|
screenshot.TakeScreenshotAndHide();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose(){
|
||||||
|
screenshot.Dispose();
|
||||||
|
timeout.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
66
Core/Notification/SoundNotification.cs
Normal file
66
Core/Notification/SoundNotification.cs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.IO;
|
||||||
|
using System.Media;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using TweetDck.Core.Other;
|
||||||
|
|
||||||
|
namespace TweetDck.Core.Notification{
|
||||||
|
class SoundNotification : IDisposable{
|
||||||
|
private readonly FormBrowser browserForm;
|
||||||
|
|
||||||
|
private SoundPlayer notificationSound;
|
||||||
|
private bool ignoreNotificationSoundError;
|
||||||
|
|
||||||
|
public SoundNotification(FormBrowser browserForm){
|
||||||
|
this.browserForm = browserForm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Play(string file){
|
||||||
|
if (notificationSound == null){
|
||||||
|
notificationSound = new SoundPlayer{
|
||||||
|
LoadTimeout = 5000
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notificationSound.SoundLocation != file){
|
||||||
|
notificationSound.SoundLocation = file;
|
||||||
|
ignoreNotificationSoundError = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try{
|
||||||
|
notificationSound.Play();
|
||||||
|
}catch(FileNotFoundException e){
|
||||||
|
OnNotificationSoundError("File not found: "+e.FileName);
|
||||||
|
}catch(InvalidOperationException){
|
||||||
|
OnNotificationSoundError("File is not a valid sound file.");
|
||||||
|
}catch(TimeoutException){
|
||||||
|
OnNotificationSoundError("File took too long to load.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnNotificationSoundError(string message){
|
||||||
|
if (!ignoreNotificationSoundError){
|
||||||
|
ignoreNotificationSoundError = true;
|
||||||
|
|
||||||
|
using(FormMessage form = new FormMessage("Notification Sound Error", "Could not play custom notification sound."+Environment.NewLine+message, MessageBoxIcon.Error)){
|
||||||
|
form.AddButton("Ignore");
|
||||||
|
|
||||||
|
Button btnOpenSettings = form.AddButton("Open Settings");
|
||||||
|
btnOpenSettings.Width += 16;
|
||||||
|
btnOpenSettings.Location = new Point(btnOpenSettings.Location.X-16, btnOpenSettings.Location.Y);
|
||||||
|
|
||||||
|
if (form.ShowDialog() == DialogResult.OK && form.ClickedButton == btnOpenSettings){
|
||||||
|
browserForm.OpenSettings(FormSettings.TabIndexNotification);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose(){
|
||||||
|
if (notificationSound != null){
|
||||||
|
notificationSound.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,28 +1,16 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace TweetDck.Core.Handling{
|
namespace TweetDck.Core.Notification{
|
||||||
sealed class TweetNotification{
|
sealed class TweetNotification{
|
||||||
private static string FontSizeClass { get; set; }
|
private static string FontSizeClass { get; set; }
|
||||||
private static string HeadTag { get; set; }
|
private static string HeadTag { get; set; }
|
||||||
|
|
||||||
private static string DefaultFontSizeClass{
|
private const string DefaultFontSizeClass = "medium";
|
||||||
get{
|
private const string DefaultHeadTag = @"<meta charset='utf-8'><meta http-equiv='X-UA-Compatible' content='chrome=1'><link rel='stylesheet' href='https://ton.twimg.com/tweetdeck-web/web/css/font.5ef884f9f9.css'><link rel='stylesheet' href='https://ton.twimg.com/tweetdeck-web/web/css/app-dark.5631e0dd42.css'>";
|
||||||
return "medium";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string DefaultHeadTag{
|
private const string FixedCSS = @"a[data-full-url]{word-break:break-all}.txt-base-smallest .badge-verified:before{height:13px!important}";
|
||||||
get{
|
private const string CustomCSS = @".scroll-styled-v::-webkit-scrollbar{width:8px}.scroll-styled-v::-webkit-scrollbar-thumb{border-radius:0}#td-skip{opacity:0;cursor:pointer;transition:opacity 0.15s ease}.td-hover #td-skip{opacity:0.75}#td-skip:hover{opacity:1}";
|
||||||
return @"<meta charset='utf-8'><meta http-equiv='X-UA-Compatible' content='chrome=1'><link rel='stylesheet' href='https://ton.twimg.com/tweetdeck-web/web/css/font.5ef884f9f9.css'><link rel='stylesheet' href='https://ton.twimg.com/tweetdeck-web/web/css/app-dark.5631e0dd42.css'>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private 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{
|
public static int FontSizeLevel{
|
||||||
get{
|
get{
|
||||||
@@ -71,10 +59,6 @@ namespace TweetDck.Core.Handling{
|
|||||||
TopLeft, TopRight, BottomLeft, BottomRight, Custom
|
TopLeft, TopRight, BottomLeft, BottomRight, Custom
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Duration{
|
|
||||||
Short, Medium, Long, VeryLong
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Url{
|
public string Url{
|
||||||
get{
|
get{
|
||||||
return url;
|
return url;
|
||||||
@@ -99,18 +83,31 @@ namespace TweetDck.Core.Handling{
|
|||||||
return 2000+Math.Max(1000, value*characters);
|
return 2000+Math.Max(1000, value*characters);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GenerateHtml(){
|
public string GenerateHtml(string bodyClasses = null, bool enableCustomCSS = true){
|
||||||
StringBuilder build = new StringBuilder();
|
StringBuilder build = new StringBuilder();
|
||||||
build.Append("<!DOCTYPE html>");
|
build.Append("<!DOCTYPE html>");
|
||||||
build.Append("<html class='os-windows txt-base-").Append(FontSizeClass ?? DefaultFontSizeClass).Append("'>");
|
build.Append("<html class='os-windows txt-base-").Append(FontSizeClass ?? DefaultFontSizeClass).Append("'>");
|
||||||
build.Append("<head>").Append(HeadTag ?? DefaultHeadTag).Append("<style type='text/css'>").Append(CustomCSS).Append("</style>");
|
build.Append("<head>").Append(HeadTag ?? DefaultHeadTag);
|
||||||
|
|
||||||
|
if (enableCustomCSS){
|
||||||
|
build.Append("<style type='text/css'>").Append(FixedCSS).Append(CustomCSS).Append("</style>");
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(Program.UserConfig.CustomNotificationCSS)){
|
if (!string.IsNullOrEmpty(Program.UserConfig.CustomNotificationCSS)){
|
||||||
build.Append("<style type='text/css'>").Append(Program.UserConfig.CustomNotificationCSS).Append("</style>");
|
build.Append("<style type='text/css'>").Append(Program.UserConfig.CustomNotificationCSS).Append("</style>");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
build.Append("<style type='text/css'>").Append(FixedCSS).Append("</style>");
|
||||||
|
}
|
||||||
|
|
||||||
build.Append("</head>");
|
build.Append("</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("<body class='hearty");
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(bodyClasses)){
|
||||||
|
build.Append(' ').Append(bodyClasses);
|
||||||
|
}
|
||||||
|
|
||||||
|
build.Append('\'').Append(isExample ? " td-example-notification" : "").Append("><div class='app-columns-container'><div class='column scroll-styled-v' style='width:100%;overflow-y:auto'>");
|
||||||
build.Append(html);
|
build.Append(html);
|
||||||
build.Append("</div></div></body>");
|
build.Append("</div></div></body>");
|
||||||
build.Append("</html>");
|
build.Append("</html>");
|
@@ -19,7 +19,7 @@ namespace TweetDck.Core.Other{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void OnLinkClicked(object sender, LinkLabelLinkClickedEventArgs e){
|
private void OnLinkClicked(object sender, LinkLabelLinkClickedEventArgs e){
|
||||||
BrowserUtils.OpenExternalBrowser(e.Link.LinkData as string);
|
BrowserUtils.OpenExternalBrowserUnsafe(e.Link.LinkData as string);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -40,7 +40,7 @@ namespace TweetDck.Core.Other{
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
icon = null;
|
icon = null;
|
||||||
labelMessage.Location = new Point(labelMessage.Location.X-32, labelMessage.Location.Y);
|
labelMessage.Location = new Point(labelMessage.Location.X-37, labelMessage.Location.Y);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -36,17 +36,19 @@ namespace TweetDck.Core.Other{
|
|||||||
this.tabBtnOfficial = tabPanelPlugins.AddButton("", () => SelectGroup(PluginGroup.Official));
|
this.tabBtnOfficial = tabPanelPlugins.AddButton("", () => SelectGroup(PluginGroup.Official));
|
||||||
this.tabBtnCustom = tabPanelPlugins.AddButton("", () => SelectGroup(PluginGroup.Custom));
|
this.tabBtnCustom = tabPanelPlugins.AddButton("", () => SelectGroup(PluginGroup.Custom));
|
||||||
|
|
||||||
this.tabPanelPlugins.SelectTab(tabBtnOfficial);
|
|
||||||
this.pluginManager_Reloaded(pluginManager, null);
|
this.pluginManager_Reloaded(pluginManager, null);
|
||||||
|
|
||||||
Shown += (sender, args) => {
|
Shown += (sender, args) => {
|
||||||
Program.UserConfig.PluginsWindow.Restore(this, false);
|
Program.UserConfig.PluginsWindow.Restore(this, false);
|
||||||
|
this.tabPanelPlugins.SelectTab(tabBtnOfficial);
|
||||||
};
|
};
|
||||||
|
|
||||||
FormClosed += (sender, args) => {
|
FormClosed += (sender, args) => {
|
||||||
Program.UserConfig.PluginsWindow.Save(this);
|
Program.UserConfig.PluginsWindow.Save(this);
|
||||||
Program.UserConfig.Save();
|
Program.UserConfig.Save();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Disposed += (sender, args) => this.pluginManager.Reloaded -= pluginManager_Reloaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SelectGroup(PluginGroup group){
|
private void SelectGroup(PluginGroup group){
|
||||||
@@ -76,8 +78,8 @@ namespace TweetDck.Core.Other{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
flowLayoutPlugins_Resize(flowLayoutPlugins, new EventArgs());
|
|
||||||
flowLayoutPlugins.ResumeLayout(true);
|
flowLayoutPlugins.ResumeLayout(true);
|
||||||
|
flowLayoutPlugins_Resize(flowLayoutPlugins, new EventArgs());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pluginManager_Reloaded(object sender, PluginLoadEventArgs e){
|
private void pluginManager_Reloaded(object sender, PluginLoadEventArgs e){
|
||||||
|
@@ -2,25 +2,35 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using TweetDck.Core.Notification;
|
||||||
using TweetDck.Core.Other.Settings;
|
using TweetDck.Core.Other.Settings;
|
||||||
using TweetDck.Plugins;
|
using TweetDck.Plugins;
|
||||||
using TweetDck.Updates;
|
using TweetDck.Updates;
|
||||||
|
|
||||||
namespace TweetDck.Core.Other{
|
namespace TweetDck.Core.Other{
|
||||||
sealed partial class FormSettings : Form{
|
sealed partial class FormSettings : Form{
|
||||||
private readonly Dictionary<Type, BaseTabSettings> tabs = new Dictionary<Type, BaseTabSettings>(4);
|
public const int TabIndexNotification = 1;
|
||||||
|
|
||||||
public FormSettings(FormBrowser browserForm, PluginManager plugins, UpdateHandler updates){
|
private readonly FormBrowser browser;
|
||||||
|
private readonly Dictionary<Type, BaseTabSettings> tabs = new Dictionary<Type, BaseTabSettings>(4);
|
||||||
|
private readonly bool hasFinishedLoading;
|
||||||
|
|
||||||
|
public FormSettings(FormBrowser browser, PluginManager plugins, UpdateHandler updates, int startTabIndex = 0){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
Text = Program.BrandName+" Settings";
|
Text = Program.BrandName+" Settings";
|
||||||
|
|
||||||
|
this.browser = browser;
|
||||||
|
this.browser.PauseNotification();
|
||||||
|
|
||||||
this.tabPanel.SetupTabPanel(100);
|
this.tabPanel.SetupTabPanel(100);
|
||||||
this.tabPanel.AddButton("General", SelectTab<TabSettingsGeneral>);
|
this.tabPanel.AddButton("General", SelectTab<TabSettingsGeneral>);
|
||||||
this.tabPanel.AddButton("Notifications", () => SelectTab(() => new TabSettingsNotifications(browserForm.CreateNotificationForm(false))));
|
this.tabPanel.AddButton("Notifications", () => SelectTab(() => new TabSettingsNotifications(browser.CreateNotificationForm(NotificationFlags.DisableContextMenu), !hasFinishedLoading)));
|
||||||
this.tabPanel.AddButton("Updates", () => SelectTab(() => new TabSettingsUpdates(updates)));
|
this.tabPanel.AddButton("Updates", () => SelectTab(() => new TabSettingsUpdates(updates)));
|
||||||
this.tabPanel.AddButton("Advanced", () => SelectTab(() => new TabSettingsAdvanced(browserForm.ReloadBrowser, plugins)));
|
this.tabPanel.AddButton("Advanced", () => SelectTab(() => new TabSettingsAdvanced(browser.ReinjectCustomCSS, plugins)));
|
||||||
this.tabPanel.SelectTab(tabPanel.Buttons.First());
|
|
||||||
|
this.tabPanel.SelectTab(tabPanel.Buttons.ElementAt(startTabIndex));
|
||||||
|
hasFinishedLoading = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SelectTab<T>() where T : BaseTabSettings, new(){
|
private void SelectTab<T>() where T : BaseTabSettings, new(){
|
||||||
@@ -41,11 +51,17 @@ namespace TweetDck.Core.Other{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void FormSettings_FormClosing(object sender, FormClosingEventArgs e){
|
private void FormSettings_FormClosing(object sender, FormClosingEventArgs e){
|
||||||
|
foreach(BaseTabSettings control in tabs.Values){
|
||||||
|
control.OnClosing();
|
||||||
|
}
|
||||||
|
|
||||||
Program.UserConfig.Save();
|
Program.UserConfig.Save();
|
||||||
|
|
||||||
foreach(BaseTabSettings control in tabs.Values){
|
foreach(BaseTabSettings control in tabs.Values){
|
||||||
control.Dispose();
|
control.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
browser.ResumeNotification();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void btnClose_Click(object sender, EventArgs e){
|
private void btnClose_Click(object sender, EventArgs e){
|
||||||
|
@@ -15,6 +15,8 @@ namespace TweetDck.Core.Other.Settings{
|
|||||||
Padding = new Padding(6);
|
Padding = new Padding(6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual void OnClosing(){}
|
||||||
|
|
||||||
protected static void PromptRestart(){
|
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){
|
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();
|
Program.Restart();
|
||||||
|
@@ -23,6 +23,7 @@
|
|||||||
/// the contents of this method with the code editor.
|
/// the contents of this method with the code editor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void InitializeComponent() {
|
private void InitializeComponent() {
|
||||||
|
this.components = new System.ComponentModel.Container();
|
||||||
this.textBoxBrowserCSS = new System.Windows.Forms.TextBox();
|
this.textBoxBrowserCSS = new System.Windows.Forms.TextBox();
|
||||||
this.btnCancel = new System.Windows.Forms.Button();
|
this.btnCancel = new System.Windows.Forms.Button();
|
||||||
this.btnApply = new System.Windows.Forms.Button();
|
this.btnApply = new System.Windows.Forms.Button();
|
||||||
@@ -31,6 +32,8 @@
|
|||||||
this.labelNotification = new System.Windows.Forms.Label();
|
this.labelNotification = new System.Windows.Forms.Label();
|
||||||
this.textBoxNotificationCSS = new System.Windows.Forms.TextBox();
|
this.textBoxNotificationCSS = new System.Windows.Forms.TextBox();
|
||||||
this.labelWarning = new System.Windows.Forms.Label();
|
this.labelWarning = new System.Windows.Forms.Label();
|
||||||
|
this.btnOpenWiki = new System.Windows.Forms.Button();
|
||||||
|
this.timerTestBrowser = new System.Windows.Forms.Timer(this.components);
|
||||||
((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit();
|
((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit();
|
||||||
this.splitContainer.Panel1.SuspendLayout();
|
this.splitContainer.Panel1.SuspendLayout();
|
||||||
this.splitContainer.Panel2.SuspendLayout();
|
this.splitContainer.Panel2.SuspendLayout();
|
||||||
@@ -51,6 +54,7 @@
|
|||||||
this.textBoxBrowserCSS.Size = new System.Drawing.Size(373, 253);
|
this.textBoxBrowserCSS.Size = new System.Drawing.Size(373, 253);
|
||||||
this.textBoxBrowserCSS.TabIndex = 0;
|
this.textBoxBrowserCSS.TabIndex = 0;
|
||||||
this.textBoxBrowserCSS.WordWrap = false;
|
this.textBoxBrowserCSS.WordWrap = false;
|
||||||
|
this.textBoxBrowserCSS.KeyUp += new System.Windows.Forms.KeyEventHandler(this.textBoxBrowserCSS_KeyUp);
|
||||||
//
|
//
|
||||||
// btnCancel
|
// btnCancel
|
||||||
//
|
//
|
||||||
@@ -131,7 +135,7 @@
|
|||||||
this.textBoxNotificationCSS.Multiline = true;
|
this.textBoxNotificationCSS.Multiline = true;
|
||||||
this.textBoxNotificationCSS.Name = "textBoxNotificationCSS";
|
this.textBoxNotificationCSS.Name = "textBoxNotificationCSS";
|
||||||
this.textBoxNotificationCSS.ScrollBars = System.Windows.Forms.ScrollBars.Both;
|
this.textBoxNotificationCSS.ScrollBars = System.Windows.Forms.ScrollBars.Both;
|
||||||
this.textBoxNotificationCSS.Size = new System.Drawing.Size(378, 253);
|
this.textBoxNotificationCSS.Size = new System.Drawing.Size(373, 253);
|
||||||
this.textBoxNotificationCSS.TabIndex = 1;
|
this.textBoxNotificationCSS.TabIndex = 1;
|
||||||
this.textBoxNotificationCSS.WordWrap = false;
|
this.textBoxNotificationCSS.WordWrap = false;
|
||||||
//
|
//
|
||||||
@@ -139,22 +143,41 @@
|
|||||||
//
|
//
|
||||||
this.labelWarning.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
this.labelWarning.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||||
this.labelWarning.AutoSize = true;
|
this.labelWarning.AutoSize = true;
|
||||||
this.labelWarning.Location = new System.Drawing.Point(9, 292);
|
this.labelWarning.Location = new System.Drawing.Point(91, 292);
|
||||||
this.labelWarning.Name = "labelWarning";
|
this.labelWarning.Name = "labelWarning";
|
||||||
this.labelWarning.Size = new System.Drawing.Size(341, 13);
|
this.labelWarning.Size = new System.Drawing.Size(341, 13);
|
||||||
this.labelWarning.TabIndex = 6;
|
this.labelWarning.TabIndex = 6;
|
||||||
this.labelWarning.Text = "The code is not validated, please make sure there are no syntax errors.";
|
this.labelWarning.Text = "The code is not validated, please make sure there are no syntax errors.";
|
||||||
//
|
//
|
||||||
|
// btnOpenWiki
|
||||||
|
//
|
||||||
|
this.btnOpenWiki.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||||
|
this.btnOpenWiki.AutoSize = true;
|
||||||
|
this.btnOpenWiki.Location = new System.Drawing.Point(12, 287);
|
||||||
|
this.btnOpenWiki.Name = "btnOpenWiki";
|
||||||
|
this.btnOpenWiki.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||||
|
this.btnOpenWiki.Size = new System.Drawing.Size(73, 23);
|
||||||
|
this.btnOpenWiki.TabIndex = 7;
|
||||||
|
this.btnOpenWiki.Text = "Open Wiki";
|
||||||
|
this.btnOpenWiki.UseVisualStyleBackColor = true;
|
||||||
|
this.btnOpenWiki.Click += new System.EventHandler(this.btnOpenWiki_Click);
|
||||||
|
//
|
||||||
|
// timerTestBrowser
|
||||||
|
//
|
||||||
|
this.timerTestBrowser.Interval = 500;
|
||||||
|
this.timerTestBrowser.Tick += new System.EventHandler(this.timerTestBrowser_Tick);
|
||||||
|
//
|
||||||
// DialogSettingsCSS
|
// DialogSettingsCSS
|
||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
this.ClientSize = new System.Drawing.Size(784, 322);
|
this.ClientSize = new System.Drawing.Size(784, 322);
|
||||||
|
this.Controls.Add(this.btnOpenWiki);
|
||||||
this.Controls.Add(this.labelWarning);
|
this.Controls.Add(this.labelWarning);
|
||||||
this.Controls.Add(this.splitContainer);
|
this.Controls.Add(this.splitContainer);
|
||||||
this.Controls.Add(this.btnApply);
|
this.Controls.Add(this.btnApply);
|
||||||
this.Controls.Add(this.btnCancel);
|
this.Controls.Add(this.btnCancel);
|
||||||
this.MinimumSize = new System.Drawing.Size(500, 160);
|
this.MinimumSize = new System.Drawing.Size(600, 160);
|
||||||
this.Name = "DialogSettingsCSS";
|
this.Name = "DialogSettingsCSS";
|
||||||
this.ShowIcon = false;
|
this.ShowIcon = false;
|
||||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||||
@@ -179,5 +202,7 @@
|
|||||||
private System.Windows.Forms.Label labelBrowser;
|
private System.Windows.Forms.Label labelBrowser;
|
||||||
private System.Windows.Forms.Label labelNotification;
|
private System.Windows.Forms.Label labelNotification;
|
||||||
private System.Windows.Forms.Label labelWarning;
|
private System.Windows.Forms.Label labelWarning;
|
||||||
|
private System.Windows.Forms.Button btnOpenWiki;
|
||||||
|
private System.Windows.Forms.Timer timerTestBrowser;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDck.Core.Controls;
|
using TweetDck.Core.Controls;
|
||||||
|
using TweetDck.Core.Utils;
|
||||||
|
|
||||||
namespace TweetDck.Core.Other.Settings.Dialogs{
|
namespace TweetDck.Core.Other.Settings.Dialogs{
|
||||||
sealed partial class DialogSettingsCSS : Form{
|
sealed partial class DialogSettingsCSS : Form{
|
||||||
@@ -16,11 +17,15 @@ namespace TweetDck.Core.Other.Settings.Dialogs{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public DialogSettingsCSS(){
|
private readonly Action<string> reinjectBrowserCSS;
|
||||||
|
|
||||||
|
public DialogSettingsCSS(Action<string> reinjectBrowserCSS){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
Text = Program.BrandName+" Settings - CSS";
|
Text = Program.BrandName+" Settings - CSS";
|
||||||
|
|
||||||
|
this.reinjectBrowserCSS = reinjectBrowserCSS;
|
||||||
|
|
||||||
textBoxBrowserCSS.EnableMultilineShortcuts();
|
textBoxBrowserCSS.EnableMultilineShortcuts();
|
||||||
textBoxBrowserCSS.Text = Program.UserConfig.CustomBrowserCSS ?? "";
|
textBoxBrowserCSS.Text = Program.UserConfig.CustomBrowserCSS ?? "";
|
||||||
|
|
||||||
@@ -28,6 +33,20 @@ namespace TweetDck.Core.Other.Settings.Dialogs{
|
|||||||
textBoxNotificationCSS.Text = Program.UserConfig.CustomNotificationCSS ?? "";
|
textBoxNotificationCSS.Text = Program.UserConfig.CustomNotificationCSS ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void textBoxBrowserCSS_KeyUp(object sender, KeyEventArgs e){
|
||||||
|
timerTestBrowser.Stop();
|
||||||
|
timerTestBrowser.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void timerTestBrowser_Tick(object sender, EventArgs e){
|
||||||
|
reinjectBrowserCSS(textBoxBrowserCSS.Text);
|
||||||
|
timerTestBrowser.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btnOpenWiki_Click(object sender, EventArgs e){
|
||||||
|
BrowserUtils.OpenExternalBrowserUnsafe("https://github.com/chylex/TweetDuck/wiki");
|
||||||
|
}
|
||||||
|
|
||||||
private void btnApply_Click(object sender, EventArgs e){
|
private void btnApply_Click(object sender, EventArgs e){
|
||||||
DialogResult = DialogResult.OK;
|
DialogResult = DialogResult.OK;
|
||||||
Close();
|
Close();
|
||||||
|
@@ -22,7 +22,7 @@ namespace TweetDck.Core.Other.Settings.Dialogs{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void btnHelp_Click(object sender, EventArgs e){
|
private void btnHelp_Click(object sender, EventArgs e){
|
||||||
BrowserUtils.OpenExternalBrowser("http://peter.sh/experiments/chromium-command-line-switches/");
|
BrowserUtils.OpenExternalBrowserUnsafe("http://peter.sh/experiments/chromium-command-line-switches/");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void btnApply_Click(object sender, EventArgs e){
|
private void btnApply_Click(object sender, EventArgs e){
|
||||||
|
126
Core/Other/Settings/Dialogs/DialogSettingsExport.Designer.cs
generated
Normal file
126
Core/Other/Settings/Dialogs/DialogSettingsExport.Designer.cs
generated
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
namespace TweetDck.Core.Other.Settings.Dialogs {
|
||||||
|
partial class DialogSettingsExport {
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up any resources being used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose(bool disposing) {
|
||||||
|
if (disposing && (components != null)) {
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Windows Form Designer generated code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent() {
|
||||||
|
this.components = new System.ComponentModel.Container();
|
||||||
|
this.btnCancel = new System.Windows.Forms.Button();
|
||||||
|
this.btnApply = new System.Windows.Forms.Button();
|
||||||
|
this.cbConfig = new System.Windows.Forms.CheckBox();
|
||||||
|
this.cbSession = new System.Windows.Forms.CheckBox();
|
||||||
|
this.cbPluginData = new System.Windows.Forms.CheckBox();
|
||||||
|
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// btnCancel
|
||||||
|
//
|
||||||
|
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.btnCancel.AutoSize = true;
|
||||||
|
this.btnCancel.Location = new System.Drawing.Point(176, 97);
|
||||||
|
this.btnCancel.Name = "btnCancel";
|
||||||
|
this.btnCancel.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||||
|
this.btnCancel.Size = new System.Drawing.Size(56, 23);
|
||||||
|
this.btnCancel.TabIndex = 0;
|
||||||
|
this.btnCancel.Text = "Cancel";
|
||||||
|
this.btnCancel.UseVisualStyleBackColor = true;
|
||||||
|
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
|
||||||
|
//
|
||||||
|
// btnApply
|
||||||
|
//
|
||||||
|
this.btnApply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.btnApply.AutoSize = true;
|
||||||
|
this.btnApply.Location = new System.Drawing.Point(117, 97);
|
||||||
|
this.btnApply.Name = "btnApply";
|
||||||
|
this.btnApply.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||||
|
this.btnApply.Size = new System.Drawing.Size(53, 23);
|
||||||
|
this.btnApply.TabIndex = 1;
|
||||||
|
this.btnApply.UseVisualStyleBackColor = true;
|
||||||
|
this.btnApply.Click += new System.EventHandler(this.btnApply_Click);
|
||||||
|
//
|
||||||
|
// cbConfig
|
||||||
|
//
|
||||||
|
this.cbConfig.AutoSize = true;
|
||||||
|
this.cbConfig.Location = new System.Drawing.Point(13, 13);
|
||||||
|
this.cbConfig.Name = "cbConfig";
|
||||||
|
this.cbConfig.Size = new System.Drawing.Size(106, 17);
|
||||||
|
this.cbConfig.TabIndex = 2;
|
||||||
|
this.cbConfig.Text = "Program Settings";
|
||||||
|
this.toolTip.SetToolTip(this.cbConfig, "Interface, notification, and update settings.\r\nIncludes a list of disabled plugin" +
|
||||||
|
"s.");
|
||||||
|
this.cbConfig.UseVisualStyleBackColor = true;
|
||||||
|
this.cbConfig.CheckedChanged += new System.EventHandler(this.cbConfig_CheckedChanged);
|
||||||
|
//
|
||||||
|
// cbSession
|
||||||
|
//
|
||||||
|
this.cbSession.AutoSize = true;
|
||||||
|
this.cbSession.Location = new System.Drawing.Point(13, 37);
|
||||||
|
this.cbSession.Name = "cbSession";
|
||||||
|
this.cbSession.Size = new System.Drawing.Size(92, 17);
|
||||||
|
this.cbSession.TabIndex = 3;
|
||||||
|
this.cbSession.Text = "Login Session";
|
||||||
|
this.toolTip.SetToolTip(this.cbSession, "A token that allows logging into the\r\ncurrent TweetDeck account.");
|
||||||
|
this.cbSession.UseVisualStyleBackColor = true;
|
||||||
|
this.cbSession.CheckedChanged += new System.EventHandler(this.cbSession_CheckedChanged);
|
||||||
|
//
|
||||||
|
// cbPluginData
|
||||||
|
//
|
||||||
|
this.cbPluginData.AutoSize = true;
|
||||||
|
this.cbPluginData.Location = new System.Drawing.Point(13, 61);
|
||||||
|
this.cbPluginData.Name = "cbPluginData";
|
||||||
|
this.cbPluginData.Size = new System.Drawing.Size(81, 17);
|
||||||
|
this.cbPluginData.TabIndex = 4;
|
||||||
|
this.cbPluginData.Text = "Plugin Data";
|
||||||
|
this.toolTip.SetToolTip(this.cbPluginData, "Data files generated by plugins.\r\nDoes not include the plugins themselves.");
|
||||||
|
this.cbPluginData.UseVisualStyleBackColor = true;
|
||||||
|
this.cbPluginData.CheckedChanged += new System.EventHandler(this.cbPluginData_CheckedChanged);
|
||||||
|
//
|
||||||
|
// DialogSettingsExport
|
||||||
|
//
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.ClientSize = new System.Drawing.Size(244, 132);
|
||||||
|
this.Controls.Add(this.cbPluginData);
|
||||||
|
this.Controls.Add(this.cbSession);
|
||||||
|
this.Controls.Add(this.cbConfig);
|
||||||
|
this.Controls.Add(this.btnApply);
|
||||||
|
this.Controls.Add(this.btnCancel);
|
||||||
|
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
|
||||||
|
this.MinimumSize = new System.Drawing.Size(200, 170);
|
||||||
|
this.Name = "DialogSettingsExport";
|
||||||
|
this.ShowIcon = false;
|
||||||
|
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
this.PerformLayout();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private System.Windows.Forms.Button btnCancel;
|
||||||
|
private System.Windows.Forms.Button btnApply;
|
||||||
|
private System.Windows.Forms.CheckBox cbConfig;
|
||||||
|
private System.Windows.Forms.CheckBox cbSession;
|
||||||
|
private System.Windows.Forms.CheckBox cbPluginData;
|
||||||
|
private System.Windows.Forms.ToolTip toolTip;
|
||||||
|
}
|
||||||
|
}
|
80
Core/Other/Settings/Dialogs/DialogSettingsExport.cs
Normal file
80
Core/Other/Settings/Dialogs/DialogSettingsExport.cs
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
using System;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using TweetDck.Core.Other.Settings.Export;
|
||||||
|
|
||||||
|
namespace TweetDck.Core.Other.Settings.Dialogs{
|
||||||
|
sealed partial class DialogSettingsExport : Form{
|
||||||
|
public static DialogSettingsExport Import(ExportFileFlags flags){
|
||||||
|
return new DialogSettingsExport(flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DialogSettingsExport Export(){
|
||||||
|
return new DialogSettingsExport(ExportFileFlags.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExportFileFlags Flags{
|
||||||
|
get{
|
||||||
|
return selectedFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
set{
|
||||||
|
selectedFlags = value;
|
||||||
|
btnApply.Enabled = selectedFlags != ExportFileFlags.None;
|
||||||
|
|
||||||
|
cbConfig.Checked = selectedFlags.HasFlag(ExportFileFlags.Config);
|
||||||
|
cbSession.Checked = selectedFlags.HasFlag(ExportFileFlags.Session);
|
||||||
|
cbPluginData.Checked = selectedFlags.HasFlag(ExportFileFlags.PluginData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExportFileFlags selectedFlags = ExportFileFlags.None;
|
||||||
|
|
||||||
|
private DialogSettingsExport(ExportFileFlags importFlags){
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
bool isExporting = importFlags == ExportFileFlags.None;
|
||||||
|
|
||||||
|
if (isExporting){
|
||||||
|
Text = "Export Profile";
|
||||||
|
btnApply.Text = "Export";
|
||||||
|
Flags = ExportFileFlags.All & ~ExportFileFlags.Session;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
Text = "Import Profile";
|
||||||
|
btnApply.Text = "Import";
|
||||||
|
Flags = importFlags;
|
||||||
|
|
||||||
|
cbConfig.Enabled = cbConfig.Checked;
|
||||||
|
cbSession.Enabled = cbSession.Checked;
|
||||||
|
cbPluginData.Enabled = cbPluginData.Checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetFlag(ExportFileFlags flag, bool enable){
|
||||||
|
selectedFlags = enable ? selectedFlags | flag : selectedFlags & ~flag;
|
||||||
|
btnApply.Enabled = selectedFlags != ExportFileFlags.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cbConfig_CheckedChanged(object sender, EventArgs e){
|
||||||
|
SetFlag(ExportFileFlags.Config, cbConfig.Checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cbSession_CheckedChanged(object sender, EventArgs e){
|
||||||
|
SetFlag(ExportFileFlags.Session, cbSession.Checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cbPluginData_CheckedChanged(object sender, EventArgs e){
|
||||||
|
SetFlag(ExportFileFlags.PluginData, cbPluginData.Checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btnApply_Click(object sender, EventArgs e){
|
||||||
|
DialogResult = DialogResult.OK;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btnCancel_Click(object sender, EventArgs e){
|
||||||
|
DialogResult = DialogResult.Cancel;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -63,6 +63,26 @@ namespace TweetDck.Core.Other.Settings.Export{
|
|||||||
return new Entry(Encoding.UTF8.GetString(name), contents);
|
return new Entry(Encoding.UTF8.GetString(name), contents);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string SkipFile(){
|
||||||
|
int nameLength = stream.ReadByte();
|
||||||
|
|
||||||
|
if (nameLength == -1){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] name = new byte[nameLength];
|
||||||
|
stream.Read(name, 0, nameLength);
|
||||||
|
|
||||||
|
byte[] contentLength = new byte[4];
|
||||||
|
stream.Read(contentLength, 0, 4);
|
||||||
|
|
||||||
|
stream.Position += BitConverter.ToInt32(contentLength, 0);
|
||||||
|
|
||||||
|
string keyName = Encoding.UTF8.GetString(name);
|
||||||
|
int separatorIndex = keyName.IndexOf(KeySeparator);
|
||||||
|
return separatorIndex == -1 ? keyName : keyName.Substring(0, separatorIndex);
|
||||||
|
}
|
||||||
|
|
||||||
public void Flush(){
|
public void Flush(){
|
||||||
stream.Flush();
|
stream.Flush();
|
||||||
}
|
}
|
||||||
@@ -101,8 +121,11 @@ namespace TweetDck.Core.Other.Settings.Export{
|
|||||||
|
|
||||||
public void WriteToFile(string path, bool createDirectory){
|
public void WriteToFile(string path, bool createDirectory){
|
||||||
if (createDirectory){
|
if (createDirectory){
|
||||||
// ReSharper disable once AssignNullToNotNullAttribute
|
string dir = Path.GetDirectoryName(path);
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
|
||||||
|
if (!string.IsNullOrEmpty(dir)){
|
||||||
|
Directory.CreateDirectory(dir);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
File.WriteAllBytes(path, contents);
|
File.WriteAllBytes(path, contents);
|
||||||
|
12
Core/Other/Settings/Export/ExportFileFlags.cs
Normal file
12
Core/Other/Settings/Export/ExportFileFlags.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace TweetDck.Core.Other.Settings.Export{
|
||||||
|
[Flags]
|
||||||
|
enum ExportFileFlags{
|
||||||
|
None = 0,
|
||||||
|
Config = 1,
|
||||||
|
Session = 2,
|
||||||
|
PluginData = 4,
|
||||||
|
All = Config|Session|PluginData
|
||||||
|
}
|
||||||
|
}
|
@@ -22,11 +22,14 @@ namespace TweetDck.Core.Other.Settings.Export{
|
|||||||
this.plugins = plugins;
|
this.plugins = plugins;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Export(bool includeSession){
|
public bool Export(ExportFileFlags flags){
|
||||||
try{
|
try{
|
||||||
using(CombinedFileStream stream = new CombinedFileStream(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))){
|
using(CombinedFileStream stream = new CombinedFileStream(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))){
|
||||||
|
if (flags.HasFlag(ExportFileFlags.Config)){
|
||||||
stream.WriteFile("config", Program.ConfigFilePath);
|
stream.WriteFile("config", Program.ConfigFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags.HasFlag(ExportFileFlags.PluginData)){
|
||||||
foreach(Plugin plugin in plugins.Plugins){
|
foreach(Plugin plugin in plugins.Plugins){
|
||||||
foreach(PathInfo path in EnumerateFilesRelative(plugin.GetPluginFolder(PluginFolder.Data))){
|
foreach(PathInfo path in EnumerateFilesRelative(plugin.GetPluginFolder(PluginFolder.Data))){
|
||||||
try{
|
try{
|
||||||
@@ -36,8 +39,9 @@ namespace TweetDck.Core.Other.Settings.Export{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (includeSession){
|
if (flags.HasFlag(ExportFileFlags.Session)){
|
||||||
stream.WriteFile("cookies", CookiesPath);
|
stream.WriteFile("cookies", CookiesPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,7 +55,38 @@ namespace TweetDck.Core.Other.Settings.Export{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Import(){
|
public ExportFileFlags GetImportFlags(){
|
||||||
|
ExportFileFlags flags = ExportFileFlags.None;
|
||||||
|
|
||||||
|
try{
|
||||||
|
using(CombinedFileStream stream = new CombinedFileStream(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.None))){
|
||||||
|
string key;
|
||||||
|
|
||||||
|
while((key = stream.SkipFile()) != null){
|
||||||
|
switch(key){
|
||||||
|
case "config":
|
||||||
|
flags |= ExportFileFlags.Config;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "plugin.data":
|
||||||
|
flags |= ExportFileFlags.PluginData;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "cookies":
|
||||||
|
flags |= ExportFileFlags.Session;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}catch(Exception e){
|
||||||
|
LastException = e;
|
||||||
|
flags = ExportFileFlags.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Import(ExportFileFlags flags){
|
||||||
try{
|
try{
|
||||||
HashSet<string> missingPlugins = new HashSet<string>();
|
HashSet<string> missingPlugins = new HashSet<string>();
|
||||||
|
|
||||||
@@ -61,11 +96,15 @@ namespace TweetDck.Core.Other.Settings.Export{
|
|||||||
while((entry = stream.ReadFile()) != null){
|
while((entry = stream.ReadFile()) != null){
|
||||||
switch(entry.KeyName){
|
switch(entry.KeyName){
|
||||||
case "config":
|
case "config":
|
||||||
|
if (flags.HasFlag(ExportFileFlags.Config)){
|
||||||
entry.WriteToFile(Program.ConfigFilePath);
|
entry.WriteToFile(Program.ConfigFilePath);
|
||||||
Program.ReloadConfig();
|
Program.ReloadConfig();
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "plugin.data":
|
case "plugin.data":
|
||||||
|
if (flags.HasFlag(ExportFileFlags.PluginData)){
|
||||||
string[] value = entry.KeyValue;
|
string[] value = entry.KeyValue;
|
||||||
|
|
||||||
entry.WriteToFile(Path.Combine(Program.PluginDataPath, value[0], value[1]), true);
|
entry.WriteToFile(Path.Combine(Program.PluginDataPath, value[0], value[1]), true);
|
||||||
@@ -73,11 +112,12 @@ namespace TweetDck.Core.Other.Settings.Export{
|
|||||||
if (!plugins.IsPluginInstalled(value[0])){
|
if (!plugins.IsPluginInstalled(value[0])){
|
||||||
missingPlugins.Add(value[0]);
|
missingPlugins.Add(value[0]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "cookies":
|
case "cookies":
|
||||||
if (MessageBox.Show("Do you want to import the login session? This will restart "+Program.BrandName+".", "Importing "+Program.BrandName+" Profile", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.Yes){
|
if (flags.HasFlag(ExportFileFlags.Session) && MessageBox.Show("Do you want to import the login session? This will restart "+Program.BrandName+".", "Importing "+Program.BrandName+" Profile", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.Yes){
|
||||||
entry.WriteToFile(Path.Combine(Program.StoragePath, TempCookiesPath));
|
entry.WriteToFile(Path.Combine(Program.StoragePath, TempCookiesPath));
|
||||||
IsRestarting = true;
|
IsRestarting = true;
|
||||||
}
|
}
|
||||||
|
48
Core/Other/Settings/TabSettingsAdvanced.Designer.cs
generated
48
Core/Other/Settings/TabSettingsAdvanced.Designer.cs
generated
@@ -31,13 +31,14 @@
|
|||||||
this.btnEditCSS = new System.Windows.Forms.Button();
|
this.btnEditCSS = new System.Windows.Forms.Button();
|
||||||
this.btnRestartLog = new System.Windows.Forms.Button();
|
this.btnRestartLog = new System.Windows.Forms.Button();
|
||||||
this.btnRestart = new System.Windows.Forms.Button();
|
this.btnRestart = new System.Windows.Forms.Button();
|
||||||
|
this.btnOpenAppFolder = new System.Windows.Forms.Button();
|
||||||
|
this.btnOpenDataFolder = new System.Windows.Forms.Button();
|
||||||
this.btnReset = new System.Windows.Forms.Button();
|
this.btnReset = new System.Windows.Forms.Button();
|
||||||
this.btnImport = new System.Windows.Forms.Button();
|
this.btnImport = new System.Windows.Forms.Button();
|
||||||
this.btnExport = new System.Windows.Forms.Button();
|
this.btnExport = new System.Windows.Forms.Button();
|
||||||
this.groupPerformance = new System.Windows.Forms.GroupBox();
|
this.groupPerformance = new System.Windows.Forms.GroupBox();
|
||||||
this.groupConfiguration = new System.Windows.Forms.GroupBox();
|
this.groupConfiguration = new System.Windows.Forms.GroupBox();
|
||||||
this.groupApp = new System.Windows.Forms.GroupBox();
|
this.groupApp = new System.Windows.Forms.GroupBox();
|
||||||
this.btnOpenAppFolder = new System.Windows.Forms.Button();
|
|
||||||
this.groupPerformance.SuspendLayout();
|
this.groupPerformance.SuspendLayout();
|
||||||
this.groupConfiguration.SuspendLayout();
|
this.groupConfiguration.SuspendLayout();
|
||||||
this.groupApp.SuspendLayout();
|
this.groupApp.SuspendLayout();
|
||||||
@@ -93,19 +94,19 @@
|
|||||||
//
|
//
|
||||||
// btnRestartLog
|
// btnRestartLog
|
||||||
//
|
//
|
||||||
this.btnRestartLog.Location = new System.Drawing.Point(6, 77);
|
this.btnRestartLog.Location = new System.Drawing.Point(6, 106);
|
||||||
this.btnRestartLog.Name = "btnRestartLog";
|
this.btnRestartLog.Name = "btnRestartLog";
|
||||||
this.btnRestartLog.Size = new System.Drawing.Size(171, 23);
|
this.btnRestartLog.Size = new System.Drawing.Size(171, 23);
|
||||||
this.btnRestartLog.TabIndex = 18;
|
this.btnRestartLog.TabIndex = 18;
|
||||||
this.btnRestartLog.Text = "Restart with Logging";
|
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" +
|
this.toolTip.SetToolTip(this.btnRestartLog, "Restarts the program and enables logging\r\ninto a debug.txt file in the data folde" +
|
||||||
"on folder.");
|
"r.");
|
||||||
this.btnRestartLog.UseVisualStyleBackColor = true;
|
this.btnRestartLog.UseVisualStyleBackColor = true;
|
||||||
this.btnRestartLog.Click += new System.EventHandler(this.btnRestartLog_Click);
|
this.btnRestartLog.Click += new System.EventHandler(this.btnRestartLog_Click);
|
||||||
//
|
//
|
||||||
// btnRestart
|
// btnRestart
|
||||||
//
|
//
|
||||||
this.btnRestart.Location = new System.Drawing.Point(6, 48);
|
this.btnRestart.Location = new System.Drawing.Point(6, 77);
|
||||||
this.btnRestart.Name = "btnRestart";
|
this.btnRestart.Name = "btnRestart";
|
||||||
this.btnRestart.Size = new System.Drawing.Size(171, 23);
|
this.btnRestart.Size = new System.Drawing.Size(171, 23);
|
||||||
this.btnRestart.TabIndex = 17;
|
this.btnRestart.TabIndex = 17;
|
||||||
@@ -115,6 +116,28 @@
|
|||||||
this.btnRestart.UseVisualStyleBackColor = true;
|
this.btnRestart.UseVisualStyleBackColor = true;
|
||||||
this.btnRestart.Click += new System.EventHandler(this.btnRestart_Click);
|
this.btnRestart.Click += new System.EventHandler(this.btnRestart_Click);
|
||||||
//
|
//
|
||||||
|
// btnOpenAppFolder
|
||||||
|
//
|
||||||
|
this.btnOpenAppFolder.Location = new System.Drawing.Point(6, 19);
|
||||||
|
this.btnOpenAppFolder.Name = "btnOpenAppFolder";
|
||||||
|
this.btnOpenAppFolder.Size = new System.Drawing.Size(171, 23);
|
||||||
|
this.btnOpenAppFolder.TabIndex = 16;
|
||||||
|
this.btnOpenAppFolder.Text = "Open Program Folder";
|
||||||
|
this.toolTip.SetToolTip(this.btnOpenAppFolder, "Opens the folder where the app is located.");
|
||||||
|
this.btnOpenAppFolder.UseVisualStyleBackColor = true;
|
||||||
|
this.btnOpenAppFolder.Click += new System.EventHandler(this.btnOpenAppFolder_Click);
|
||||||
|
//
|
||||||
|
// btnOpenDataFolder
|
||||||
|
//
|
||||||
|
this.btnOpenDataFolder.Location = new System.Drawing.Point(6, 48);
|
||||||
|
this.btnOpenDataFolder.Name = "btnOpenDataFolder";
|
||||||
|
this.btnOpenDataFolder.Size = new System.Drawing.Size(171, 23);
|
||||||
|
this.btnOpenDataFolder.TabIndex = 19;
|
||||||
|
this.btnOpenDataFolder.Text = "Open Data Folder";
|
||||||
|
this.toolTip.SetToolTip(this.btnOpenDataFolder, "Opens the folder where your profile data is located.");
|
||||||
|
this.btnOpenDataFolder.UseVisualStyleBackColor = true;
|
||||||
|
this.btnOpenDataFolder.Click += new System.EventHandler(this.btnOpenDataFolder_Click);
|
||||||
|
//
|
||||||
// btnReset
|
// btnReset
|
||||||
//
|
//
|
||||||
this.btnReset.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
this.btnReset.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||||
@@ -179,27 +202,17 @@
|
|||||||
//
|
//
|
||||||
// groupApp
|
// groupApp
|
||||||
//
|
//
|
||||||
|
this.groupApp.Controls.Add(this.btnOpenDataFolder);
|
||||||
this.groupApp.Controls.Add(this.btnOpenAppFolder);
|
this.groupApp.Controls.Add(this.btnOpenAppFolder);
|
||||||
this.groupApp.Controls.Add(this.btnRestartLog);
|
this.groupApp.Controls.Add(this.btnRestartLog);
|
||||||
this.groupApp.Controls.Add(this.btnRestart);
|
this.groupApp.Controls.Add(this.btnRestart);
|
||||||
this.groupApp.Location = new System.Drawing.Point(198, 9);
|
this.groupApp.Location = new System.Drawing.Point(198, 9);
|
||||||
this.groupApp.Name = "groupApp";
|
this.groupApp.Name = "groupApp";
|
||||||
this.groupApp.Size = new System.Drawing.Size(183, 106);
|
this.groupApp.Size = new System.Drawing.Size(183, 135);
|
||||||
this.groupApp.TabIndex = 20;
|
this.groupApp.TabIndex = 20;
|
||||||
this.groupApp.TabStop = false;
|
this.groupApp.TabStop = false;
|
||||||
this.groupApp.Text = "App";
|
this.groupApp.Text = "App";
|
||||||
//
|
//
|
||||||
// btnOpenAppFolder
|
|
||||||
//
|
|
||||||
this.btnOpenAppFolder.Location = new System.Drawing.Point(6, 19);
|
|
||||||
this.btnOpenAppFolder.Name = "btnOpenAppFolder";
|
|
||||||
this.btnOpenAppFolder.Size = new System.Drawing.Size(171, 23);
|
|
||||||
this.btnOpenAppFolder.TabIndex = 16;
|
|
||||||
this.btnOpenAppFolder.Text = "Open Program Folder";
|
|
||||||
this.toolTip.SetToolTip(this.btnOpenAppFolder, "Opens the folder where the app is located.");
|
|
||||||
this.btnOpenAppFolder.UseVisualStyleBackColor = true;
|
|
||||||
this.btnOpenAppFolder.Click += new System.EventHandler(this.btnOpenAppFolder_Click);
|
|
||||||
//
|
|
||||||
// TabSettingsAdvanced
|
// TabSettingsAdvanced
|
||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
@@ -237,5 +250,6 @@
|
|||||||
private System.Windows.Forms.Button btnRestartLog;
|
private System.Windows.Forms.Button btnRestartLog;
|
||||||
private System.Windows.Forms.Button btnRestart;
|
private System.Windows.Forms.Button btnRestart;
|
||||||
private System.Windows.Forms.Button btnOpenAppFolder;
|
private System.Windows.Forms.Button btnOpenAppFolder;
|
||||||
|
private System.Windows.Forms.Button btnOpenDataFolder;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,13 +9,13 @@ using TweetDck.Plugins;
|
|||||||
|
|
||||||
namespace TweetDck.Core.Other.Settings{
|
namespace TweetDck.Core.Other.Settings{
|
||||||
partial class TabSettingsAdvanced : BaseTabSettings{
|
partial class TabSettingsAdvanced : BaseTabSettings{
|
||||||
private readonly Action browserReloadAction;
|
private readonly Action<string> reinjectBrowserCSS;
|
||||||
private readonly PluginManager plugins;
|
private readonly PluginManager plugins;
|
||||||
|
|
||||||
public TabSettingsAdvanced(Action browserReloadAction, PluginManager plugins){
|
public TabSettingsAdvanced(Action<string> reinjectBrowserCSS, PluginManager plugins){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
this.browserReloadAction = browserReloadAction;
|
this.reinjectBrowserCSS = reinjectBrowserCSS;
|
||||||
this.plugins = plugins;
|
this.plugins = plugins;
|
||||||
|
|
||||||
checkHardwareAcceleration.Checked = HardwareAcceleration.IsEnabled;
|
checkHardwareAcceleration.Checked = HardwareAcceleration.IsEnabled;
|
||||||
@@ -80,25 +80,27 @@ namespace TweetDck.Core.Other.Settings{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void btnEditCSS_Click(object sender, EventArgs e){
|
private void btnEditCSS_Click(object sender, EventArgs e){
|
||||||
using(DialogSettingsCSS form = new DialogSettingsCSS()){
|
using(DialogSettingsCSS form = new DialogSettingsCSS(reinjectBrowserCSS)){
|
||||||
if (form.ShowDialog(ParentForm) == DialogResult.OK){
|
if (form.ShowDialog(ParentForm) == DialogResult.OK){
|
||||||
bool hasChangedBrowser = form.BrowserCSS != Config.CustomBrowserCSS;
|
|
||||||
|
|
||||||
Config.CustomBrowserCSS = form.BrowserCSS;
|
Config.CustomBrowserCSS = form.BrowserCSS;
|
||||||
Config.CustomNotificationCSS = form.NotificationCSS;
|
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){
|
reinjectBrowserCSS(Config.CustomBrowserCSS); // reinject on cancel too, because the CSS is updated while typing
|
||||||
browserReloadAction();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void btnExport_Click(object sender, EventArgs e){
|
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);
|
ExportFileFlags flags;
|
||||||
if (resultSaveCredentials == DialogResult.Cancel)return;
|
|
||||||
|
using(DialogSettingsExport dialog = DialogSettingsExport.Export()){
|
||||||
|
if (dialog.ShowDialog() != DialogResult.OK){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
flags = dialog.Flags;
|
||||||
|
}
|
||||||
|
|
||||||
bool saveCredentials = resultSaveCredentials == DialogResult.Yes;
|
|
||||||
string file;
|
string file;
|
||||||
|
|
||||||
using(SaveFileDialog dialog = new SaveFileDialog{
|
using(SaveFileDialog dialog = new SaveFileDialog{
|
||||||
@@ -110,19 +112,21 @@ namespace TweetDck.Core.Other.Settings{
|
|||||||
Title = "Export "+Program.BrandName+" Settings",
|
Title = "Export "+Program.BrandName+" Settings",
|
||||||
Filter = Program.BrandName+" Settings (*.tdsettings)|*.tdsettings"
|
Filter = Program.BrandName+" Settings (*.tdsettings)|*.tdsettings"
|
||||||
}){
|
}){
|
||||||
file = dialog.ShowDialog() == DialogResult.OK ? dialog.FileName : null;
|
if (dialog.ShowDialog() != DialogResult.OK){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
file = dialog.FileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file != null){
|
|
||||||
Program.UserConfig.Save();
|
Program.UserConfig.Save();
|
||||||
|
|
||||||
ExportManager manager = new ExportManager(file, plugins);
|
ExportManager manager = new ExportManager(file, plugins);
|
||||||
|
|
||||||
if (!manager.Export(saveCredentials)){
|
if (!manager.Export(flags)){
|
||||||
Program.Reporter.HandleException("Profile Export Error", "An exception happened while exporting "+Program.BrandName+" settings.", true, manager.LastException);
|
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){
|
private void btnImport_Click(object sender, EventArgs e){
|
||||||
string file;
|
string file;
|
||||||
@@ -133,13 +137,25 @@ namespace TweetDck.Core.Other.Settings{
|
|||||||
Title = "Import "+Program.BrandName+" Settings",
|
Title = "Import "+Program.BrandName+" Settings",
|
||||||
Filter = Program.BrandName+" Settings (*.tdsettings)|*.tdsettings"
|
Filter = Program.BrandName+" Settings (*.tdsettings)|*.tdsettings"
|
||||||
}){
|
}){
|
||||||
file = dialog.ShowDialog() == DialogResult.OK ? dialog.FileName : null;
|
if (dialog.ShowDialog() != DialogResult.OK){
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file != null){
|
file = dialog.FileName;
|
||||||
ExportManager manager = new ExportManager(file, plugins);
|
}
|
||||||
|
|
||||||
if (manager.Import()){
|
ExportManager manager = new ExportManager(file, plugins);
|
||||||
|
ExportFileFlags flags;
|
||||||
|
|
||||||
|
using(DialogSettingsExport dialog = DialogSettingsExport.Import(manager.GetImportFlags())){
|
||||||
|
if (dialog.ShowDialog() != DialogResult.OK){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
flags = dialog.Flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (manager.Import(flags)){
|
||||||
if (!manager.IsRestarting){
|
if (!manager.IsRestarting){
|
||||||
((FormSettings)ParentForm).ReloadUI();
|
((FormSettings)ParentForm).ReloadUI();
|
||||||
}
|
}
|
||||||
@@ -148,7 +164,6 @@ namespace TweetDck.Core.Other.Settings{
|
|||||||
Program.Reporter.HandleException("Profile Import Error", "An exception happened while importing "+Program.BrandName+" settings.", true, manager.LastException);
|
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){
|
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){
|
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){
|
||||||
@@ -161,6 +176,10 @@ namespace TweetDck.Core.Other.Settings{
|
|||||||
using(Process.Start("explorer.exe", "\""+Program.ProgramPath+"\"")){}
|
using(Process.Start("explorer.exe", "\""+Program.ProgramPath+"\"")){}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void btnOpenDataFolder_Click(object sender, EventArgs e){
|
||||||
|
using(Process.Start("explorer.exe", "\""+Program.StoragePath+"\"")){}
|
||||||
|
}
|
||||||
|
|
||||||
private void btnRestart_Click(object sender, EventArgs e){
|
private void btnRestart_Click(object sender, EventArgs e){
|
||||||
Program.Restart();
|
Program.Restart();
|
||||||
}
|
}
|
||||||
|
19
Core/Other/Settings/TabSettingsGeneral.Designer.cs
generated
19
Core/Other/Settings/TabSettingsGeneral.Designer.cs
generated
@@ -29,6 +29,7 @@
|
|||||||
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
||||||
this.checkTrayHighlight = new System.Windows.Forms.CheckBox();
|
this.checkTrayHighlight = new System.Windows.Forms.CheckBox();
|
||||||
this.checkSpellCheck = new System.Windows.Forms.CheckBox();
|
this.checkSpellCheck = new System.Windows.Forms.CheckBox();
|
||||||
|
this.checkScreenshotBorder = new System.Windows.Forms.CheckBox();
|
||||||
this.groupTray = new System.Windows.Forms.GroupBox();
|
this.groupTray = new System.Windows.Forms.GroupBox();
|
||||||
this.labelTrayIcon = new System.Windows.Forms.Label();
|
this.labelTrayIcon = new System.Windows.Forms.Label();
|
||||||
this.groupInterface = new System.Windows.Forms.GroupBox();
|
this.groupInterface = new System.Windows.Forms.GroupBox();
|
||||||
@@ -87,12 +88,24 @@
|
|||||||
this.checkSpellCheck.UseVisualStyleBackColor = true;
|
this.checkSpellCheck.UseVisualStyleBackColor = true;
|
||||||
this.checkSpellCheck.CheckedChanged += new System.EventHandler(this.checkSpellCheck_CheckedChanged);
|
this.checkSpellCheck.CheckedChanged += new System.EventHandler(this.checkSpellCheck_CheckedChanged);
|
||||||
//
|
//
|
||||||
|
// checkScreenshotBorder
|
||||||
|
//
|
||||||
|
this.checkScreenshotBorder.AutoSize = true;
|
||||||
|
this.checkScreenshotBorder.Location = new System.Drawing.Point(9, 67);
|
||||||
|
this.checkScreenshotBorder.Name = "checkScreenshotBorder";
|
||||||
|
this.checkScreenshotBorder.Size = new System.Drawing.Size(169, 17);
|
||||||
|
this.checkScreenshotBorder.TabIndex = 16;
|
||||||
|
this.checkScreenshotBorder.Text = "Include Border In Screenshots";
|
||||||
|
this.toolTip.SetToolTip(this.checkScreenshotBorder, "Shows the window border in tweet screenshots.");
|
||||||
|
this.checkScreenshotBorder.UseVisualStyleBackColor = true;
|
||||||
|
this.checkScreenshotBorder.CheckedChanged += new System.EventHandler(this.checkScreenshotBorder_CheckedChanged);
|
||||||
|
//
|
||||||
// groupTray
|
// groupTray
|
||||||
//
|
//
|
||||||
this.groupTray.Controls.Add(this.checkTrayHighlight);
|
this.groupTray.Controls.Add(this.checkTrayHighlight);
|
||||||
this.groupTray.Controls.Add(this.labelTrayIcon);
|
this.groupTray.Controls.Add(this.labelTrayIcon);
|
||||||
this.groupTray.Controls.Add(this.comboBoxTrayType);
|
this.groupTray.Controls.Add(this.comboBoxTrayType);
|
||||||
this.groupTray.Location = new System.Drawing.Point(9, 86);
|
this.groupTray.Location = new System.Drawing.Point(9, 109);
|
||||||
this.groupTray.Name = "groupTray";
|
this.groupTray.Name = "groupTray";
|
||||||
this.groupTray.Size = new System.Drawing.Size(183, 93);
|
this.groupTray.Size = new System.Drawing.Size(183, 93);
|
||||||
this.groupTray.TabIndex = 15;
|
this.groupTray.TabIndex = 15;
|
||||||
@@ -111,11 +124,12 @@
|
|||||||
//
|
//
|
||||||
// groupInterface
|
// groupInterface
|
||||||
//
|
//
|
||||||
|
this.groupInterface.Controls.Add(this.checkScreenshotBorder);
|
||||||
this.groupInterface.Controls.Add(this.checkSpellCheck);
|
this.groupInterface.Controls.Add(this.checkSpellCheck);
|
||||||
this.groupInterface.Controls.Add(this.checkExpandLinks);
|
this.groupInterface.Controls.Add(this.checkExpandLinks);
|
||||||
this.groupInterface.Location = new System.Drawing.Point(9, 9);
|
this.groupInterface.Location = new System.Drawing.Point(9, 9);
|
||||||
this.groupInterface.Name = "groupInterface";
|
this.groupInterface.Name = "groupInterface";
|
||||||
this.groupInterface.Size = new System.Drawing.Size(183, 71);
|
this.groupInterface.Size = new System.Drawing.Size(183, 90);
|
||||||
this.groupInterface.TabIndex = 16;
|
this.groupInterface.TabIndex = 16;
|
||||||
this.groupInterface.TabStop = false;
|
this.groupInterface.TabStop = false;
|
||||||
this.groupInterface.Text = "User Interface";
|
this.groupInterface.Text = "User Interface";
|
||||||
@@ -146,5 +160,6 @@
|
|||||||
private System.Windows.Forms.Label labelTrayIcon;
|
private System.Windows.Forms.Label labelTrayIcon;
|
||||||
private System.Windows.Forms.CheckBox checkTrayHighlight;
|
private System.Windows.Forms.CheckBox checkTrayHighlight;
|
||||||
private System.Windows.Forms.CheckBox checkSpellCheck;
|
private System.Windows.Forms.CheckBox checkSpellCheck;
|
||||||
|
private System.Windows.Forms.CheckBox checkScreenshotBorder;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -14,6 +14,7 @@ namespace TweetDck.Core.Other.Settings{
|
|||||||
|
|
||||||
checkExpandLinks.Checked = Config.ExpandLinksOnHover;
|
checkExpandLinks.Checked = Config.ExpandLinksOnHover;
|
||||||
checkSpellCheck.Checked = Config.EnableSpellCheck;
|
checkSpellCheck.Checked = Config.EnableSpellCheck;
|
||||||
|
checkScreenshotBorder.Checked = Config.ShowScreenshotBorder;
|
||||||
checkTrayHighlight.Checked = Config.EnableTrayHighlight;
|
checkTrayHighlight.Checked = Config.EnableTrayHighlight;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,6 +31,12 @@ namespace TweetDck.Core.Other.Settings{
|
|||||||
PromptRestart();
|
PromptRestart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkScreenshotBorder_CheckedChanged(object sender, EventArgs e){
|
||||||
|
if (!Ready)return;
|
||||||
|
|
||||||
|
Config.ShowScreenshotBorder = checkScreenshotBorder.Checked;
|
||||||
|
}
|
||||||
|
|
||||||
private void comboBoxTrayType_SelectedIndexChanged(object sender, EventArgs e){
|
private void comboBoxTrayType_SelectedIndexChanged(object sender, EventArgs e){
|
||||||
if (!Ready)return;
|
if (!Ready)return;
|
||||||
|
|
||||||
|
@@ -44,15 +44,19 @@
|
|||||||
this.trackBarDuration = new System.Windows.Forms.TrackBar();
|
this.trackBarDuration = new System.Windows.Forms.TrackBar();
|
||||||
this.groupUserInterface = new System.Windows.Forms.GroupBox();
|
this.groupUserInterface = new System.Windows.Forms.GroupBox();
|
||||||
this.checkTimerCountDown = new System.Windows.Forms.CheckBox();
|
this.checkTimerCountDown = new System.Windows.Forms.CheckBox();
|
||||||
this.checkLegacyLoad = new System.Windows.Forms.CheckBox();
|
|
||||||
this.checkNotificationTimer = new System.Windows.Forms.CheckBox();
|
this.checkNotificationTimer = new System.Windows.Forms.CheckBox();
|
||||||
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
||||||
|
this.groupCustomSound = new System.Windows.Forms.GroupBox();
|
||||||
|
this.btnResetSound = new System.Windows.Forms.Button();
|
||||||
|
this.btnBrowseSound = new System.Windows.Forms.Button();
|
||||||
|
this.tbCustomSound = new System.Windows.Forms.TextBox();
|
||||||
this.groupNotificationLocation.SuspendLayout();
|
this.groupNotificationLocation.SuspendLayout();
|
||||||
((System.ComponentModel.ISupportInitialize)(this.trackBarEdgeDistance)).BeginInit();
|
((System.ComponentModel.ISupportInitialize)(this.trackBarEdgeDistance)).BeginInit();
|
||||||
this.groupNotificationDuration.SuspendLayout();
|
this.groupNotificationDuration.SuspendLayout();
|
||||||
this.tableLayoutDurationButtons.SuspendLayout();
|
this.tableLayoutDurationButtons.SuspendLayout();
|
||||||
((System.ComponentModel.ISupportInitialize)(this.trackBarDuration)).BeginInit();
|
((System.ComponentModel.ISupportInitialize)(this.trackBarDuration)).BeginInit();
|
||||||
this.groupUserInterface.SuspendLayout();
|
this.groupUserInterface.SuspendLayout();
|
||||||
|
this.groupCustomSound.SuspendLayout();
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
//
|
//
|
||||||
// groupNotificationLocation
|
// groupNotificationLocation
|
||||||
@@ -199,7 +203,7 @@
|
|||||||
this.groupNotificationDuration.Controls.Add(this.tableLayoutDurationButtons);
|
this.groupNotificationDuration.Controls.Add(this.tableLayoutDurationButtons);
|
||||||
this.groupNotificationDuration.Controls.Add(this.labelDurationValue);
|
this.groupNotificationDuration.Controls.Add(this.labelDurationValue);
|
||||||
this.groupNotificationDuration.Controls.Add(this.trackBarDuration);
|
this.groupNotificationDuration.Controls.Add(this.trackBarDuration);
|
||||||
this.groupNotificationDuration.Location = new System.Drawing.Point(9, 106);
|
this.groupNotificationDuration.Location = new System.Drawing.Point(9, 83);
|
||||||
this.groupNotificationDuration.Name = "groupNotificationDuration";
|
this.groupNotificationDuration.Name = "groupNotificationDuration";
|
||||||
this.groupNotificationDuration.Size = new System.Drawing.Size(183, 89);
|
this.groupNotificationDuration.Size = new System.Drawing.Size(183, 89);
|
||||||
this.groupNotificationDuration.TabIndex = 9;
|
this.groupNotificationDuration.TabIndex = 9;
|
||||||
@@ -302,11 +306,10 @@
|
|||||||
// groupUserInterface
|
// groupUserInterface
|
||||||
//
|
//
|
||||||
this.groupUserInterface.Controls.Add(this.checkTimerCountDown);
|
this.groupUserInterface.Controls.Add(this.checkTimerCountDown);
|
||||||
this.groupUserInterface.Controls.Add(this.checkLegacyLoad);
|
|
||||||
this.groupUserInterface.Controls.Add(this.checkNotificationTimer);
|
this.groupUserInterface.Controls.Add(this.checkNotificationTimer);
|
||||||
this.groupUserInterface.Location = new System.Drawing.Point(9, 9);
|
this.groupUserInterface.Location = new System.Drawing.Point(9, 9);
|
||||||
this.groupUserInterface.Name = "groupUserInterface";
|
this.groupUserInterface.Name = "groupUserInterface";
|
||||||
this.groupUserInterface.Size = new System.Drawing.Size(183, 91);
|
this.groupUserInterface.Size = new System.Drawing.Size(183, 68);
|
||||||
this.groupUserInterface.TabIndex = 10;
|
this.groupUserInterface.TabIndex = 10;
|
||||||
this.groupUserInterface.TabStop = false;
|
this.groupUserInterface.TabStop = false;
|
||||||
this.groupUserInterface.Text = "General";
|
this.groupUserInterface.Text = "General";
|
||||||
@@ -323,19 +326,6 @@
|
|||||||
this.checkTimerCountDown.UseVisualStyleBackColor = true;
|
this.checkTimerCountDown.UseVisualStyleBackColor = true;
|
||||||
this.checkTimerCountDown.CheckedChanged += new System.EventHandler(this.checkTimerCountDown_CheckedChanged);
|
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
|
// checkNotificationTimer
|
||||||
//
|
//
|
||||||
this.checkNotificationTimer.AutoSize = true;
|
this.checkNotificationTimer.AutoSize = true;
|
||||||
@@ -349,10 +339,55 @@
|
|||||||
this.checkNotificationTimer.UseVisualStyleBackColor = true;
|
this.checkNotificationTimer.UseVisualStyleBackColor = true;
|
||||||
this.checkNotificationTimer.CheckedChanged += new System.EventHandler(this.checkNotificationTimer_CheckedChanged);
|
this.checkNotificationTimer.CheckedChanged += new System.EventHandler(this.checkNotificationTimer_CheckedChanged);
|
||||||
//
|
//
|
||||||
|
// groupCustomSound
|
||||||
|
//
|
||||||
|
this.groupCustomSound.Controls.Add(this.btnResetSound);
|
||||||
|
this.groupCustomSound.Controls.Add(this.btnBrowseSound);
|
||||||
|
this.groupCustomSound.Controls.Add(this.tbCustomSound);
|
||||||
|
this.groupCustomSound.Location = new System.Drawing.Point(9, 178);
|
||||||
|
this.groupCustomSound.Name = "groupCustomSound";
|
||||||
|
this.groupCustomSound.Size = new System.Drawing.Size(183, 72);
|
||||||
|
this.groupCustomSound.TabIndex = 11;
|
||||||
|
this.groupCustomSound.TabStop = false;
|
||||||
|
this.groupCustomSound.Text = "Custom Sound";
|
||||||
|
//
|
||||||
|
// btnResetSound
|
||||||
|
//
|
||||||
|
this.btnResetSound.AutoSize = true;
|
||||||
|
this.btnResetSound.Location = new System.Drawing.Point(126, 43);
|
||||||
|
this.btnResetSound.Name = "btnResetSound";
|
||||||
|
this.btnResetSound.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||||
|
this.btnResetSound.Size = new System.Drawing.Size(51, 23);
|
||||||
|
this.btnResetSound.TabIndex = 2;
|
||||||
|
this.btnResetSound.Text = "Reset";
|
||||||
|
this.btnResetSound.UseVisualStyleBackColor = true;
|
||||||
|
this.btnResetSound.Click += new System.EventHandler(this.btnResetSound_Click);
|
||||||
|
//
|
||||||
|
// btnBrowseSound
|
||||||
|
//
|
||||||
|
this.btnBrowseSound.AutoSize = true;
|
||||||
|
this.btnBrowseSound.Location = new System.Drawing.Point(53, 43);
|
||||||
|
this.btnBrowseSound.Name = "btnBrowseSound";
|
||||||
|
this.btnBrowseSound.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||||
|
this.btnBrowseSound.Size = new System.Drawing.Size(67, 23);
|
||||||
|
this.btnBrowseSound.TabIndex = 1;
|
||||||
|
this.btnBrowseSound.Text = "Browse...";
|
||||||
|
this.btnBrowseSound.UseVisualStyleBackColor = true;
|
||||||
|
this.btnBrowseSound.Click += new System.EventHandler(this.btnBrowseSound_Click);
|
||||||
|
//
|
||||||
|
// tbCustomSound
|
||||||
|
//
|
||||||
|
this.tbCustomSound.Location = new System.Drawing.Point(6, 19);
|
||||||
|
this.tbCustomSound.Name = "tbCustomSound";
|
||||||
|
this.tbCustomSound.Size = new System.Drawing.Size(170, 20);
|
||||||
|
this.tbCustomSound.TabIndex = 0;
|
||||||
|
this.tbCustomSound.TextChanged += new System.EventHandler(this.tbCustomSound_TextChanged);
|
||||||
|
//
|
||||||
// TabSettingsNotifications
|
// TabSettingsNotifications
|
||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.Controls.Add(this.groupCustomSound);
|
||||||
this.Controls.Add(this.groupUserInterface);
|
this.Controls.Add(this.groupUserInterface);
|
||||||
this.Controls.Add(this.groupNotificationDuration);
|
this.Controls.Add(this.groupNotificationDuration);
|
||||||
this.Controls.Add(this.groupNotificationLocation);
|
this.Controls.Add(this.groupNotificationLocation);
|
||||||
@@ -368,6 +403,8 @@
|
|||||||
((System.ComponentModel.ISupportInitialize)(this.trackBarDuration)).EndInit();
|
((System.ComponentModel.ISupportInitialize)(this.trackBarDuration)).EndInit();
|
||||||
this.groupUserInterface.ResumeLayout(false);
|
this.groupUserInterface.ResumeLayout(false);
|
||||||
this.groupUserInterface.PerformLayout();
|
this.groupUserInterface.PerformLayout();
|
||||||
|
this.groupCustomSound.ResumeLayout(false);
|
||||||
|
this.groupCustomSound.PerformLayout();
|
||||||
this.ResumeLayout(false);
|
this.ResumeLayout(false);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -389,7 +426,6 @@
|
|||||||
private System.Windows.Forms.CheckBox checkNotificationTimer;
|
private System.Windows.Forms.CheckBox checkNotificationTimer;
|
||||||
private System.Windows.Forms.ToolTip toolTip;
|
private System.Windows.Forms.ToolTip toolTip;
|
||||||
private System.Windows.Forms.Label labelEdgeDistanceValue;
|
private System.Windows.Forms.Label labelEdgeDistanceValue;
|
||||||
private System.Windows.Forms.CheckBox checkLegacyLoad;
|
|
||||||
private System.Windows.Forms.CheckBox checkTimerCountDown;
|
private System.Windows.Forms.CheckBox checkTimerCountDown;
|
||||||
private System.Windows.Forms.Label labelDurationValue;
|
private System.Windows.Forms.Label labelDurationValue;
|
||||||
private System.Windows.Forms.TrackBar trackBarDuration;
|
private System.Windows.Forms.TrackBar trackBarDuration;
|
||||||
@@ -397,5 +433,9 @@
|
|||||||
private TweetDck.Core.Controls.FlatButton btnDurationMedium;
|
private TweetDck.Core.Controls.FlatButton btnDurationMedium;
|
||||||
private TweetDck.Core.Controls.FlatButton btnDurationLong;
|
private TweetDck.Core.Controls.FlatButton btnDurationLong;
|
||||||
private TweetDck.Core.Controls.FlatButton btnDurationShort;
|
private TweetDck.Core.Controls.FlatButton btnDurationShort;
|
||||||
|
private System.Windows.Forms.GroupBox groupCustomSound;
|
||||||
|
private System.Windows.Forms.Button btnResetSound;
|
||||||
|
private System.Windows.Forms.Button btnBrowseSound;
|
||||||
|
private System.Windows.Forms.TextBox tbCustomSound;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,14 +1,18 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDck.Core.Handling;
|
|
||||||
using TweetDck.Core.Controls;
|
using TweetDck.Core.Controls;
|
||||||
|
using TweetDck.Core.Notification;
|
||||||
|
using TweetDck.Core.Utils;
|
||||||
|
|
||||||
namespace TweetDck.Core.Other.Settings{
|
namespace TweetDck.Core.Other.Settings{
|
||||||
partial class TabSettingsNotifications : BaseTabSettings{
|
partial class TabSettingsNotifications : BaseTabSettings{
|
||||||
private readonly FormNotification notification;
|
private readonly FormNotificationMain notification;
|
||||||
|
private readonly Point initCursorPosition;
|
||||||
|
|
||||||
public TabSettingsNotifications(FormNotification notification){
|
public TabSettingsNotifications(FormNotificationMain notification, bool ignoreAutoClick){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
this.notification = notification;
|
this.notification = notification;
|
||||||
@@ -24,8 +28,11 @@ namespace TweetDck.Core.Other.Settings{
|
|||||||
this.InvokeSafe(() => this.notification.ShowNotificationForSettings(true));
|
this.InvokeSafe(() => this.notification.ShowNotificationForSettings(true));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.notification.Activated += notification_Activated;
|
||||||
this.notification.Show(this);
|
this.notification.Show(this);
|
||||||
|
|
||||||
|
initCursorPosition = ignoreAutoClick ? ControlExtensions.InvisibleLocation : Cursor.Position;
|
||||||
|
|
||||||
switch(Config.NotificationPosition){
|
switch(Config.NotificationPosition){
|
||||||
case TweetNotification.Position.TopLeft: radioLocTL.Checked = true; break;
|
case TweetNotification.Position.TopLeft: radioLocTL.Checked = true; break;
|
||||||
case TweetNotification.Position.TopRight: radioLocTR.Checked = true; break;
|
case TweetNotification.Position.TopRight: radioLocTR.Checked = true; break;
|
||||||
@@ -48,14 +55,19 @@ namespace TweetDck.Core.Other.Settings{
|
|||||||
checkNotificationTimer.Checked = Config.DisplayNotificationTimer;
|
checkNotificationTimer.Checked = Config.DisplayNotificationTimer;
|
||||||
checkTimerCountDown.Enabled = checkNotificationTimer.Checked;
|
checkTimerCountDown.Enabled = checkNotificationTimer.Checked;
|
||||||
checkTimerCountDown.Checked = Config.NotificationTimerCountDown;
|
checkTimerCountDown.Checked = Config.NotificationTimerCountDown;
|
||||||
checkLegacyLoad.Checked = Config.NotificationLegacyLoad;
|
|
||||||
|
|
||||||
trackBarEdgeDistance.SetValueSafe(Config.NotificationEdgeDistance);
|
trackBarEdgeDistance.SetValueSafe(Config.NotificationEdgeDistance);
|
||||||
labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value.ToString(CultureInfo.InvariantCulture)+" px";
|
labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value.ToString(CultureInfo.InvariantCulture)+" px";
|
||||||
|
|
||||||
|
tbCustomSound.Text = Config.NotificationSoundPath;
|
||||||
|
|
||||||
Disposed += (sender, args) => this.notification.Dispose();
|
Disposed += (sender, args) => this.notification.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void OnClosing(){
|
||||||
|
Config.NotificationSoundPath = tbCustomSound.Text;
|
||||||
|
}
|
||||||
|
|
||||||
private void TabSettingsNotifications_ParentChanged(object sender, EventArgs e){
|
private void TabSettingsNotifications_ParentChanged(object sender, EventArgs e){
|
||||||
if (Parent == null){
|
if (Parent == null){
|
||||||
notification.HideNotification(false);
|
notification.HideNotification(false);
|
||||||
@@ -65,6 +77,21 @@ namespace TweetDck.Core.Other.Settings{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void notification_Activated(object sender, EventArgs e){
|
||||||
|
if (Cursor.Position == initCursorPosition && initCursorPosition != ControlExtensions.InvisibleLocation){
|
||||||
|
Timer delay = WindowsUtils.CreateSingleTickTimer(1);
|
||||||
|
|
||||||
|
delay.Tick += (sender2, args2) => { // here you can see a disgusting hack to force the freshly opened notification window out of focus
|
||||||
|
NativeMethods.SimulateMouseClick(NativeMethods.MouseButton.Left); // because for some reason, the stupid thing keeps stealing it
|
||||||
|
delay.Dispose(); // even after using ShowWithoutActivation, the CreateParams bullshit, and about a million different combinations
|
||||||
|
}; // of trying to force the original form back into focus in various events, so you will have to fucking deal with it, alright
|
||||||
|
|
||||||
|
delay.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
notification.Activated -= notification_Activated;
|
||||||
|
}
|
||||||
|
|
||||||
private void radioLoc_CheckedChanged(object sender, EventArgs e){
|
private void radioLoc_CheckedChanged(object sender, EventArgs e){
|
||||||
if (!Ready)return;
|
if (!Ready)return;
|
||||||
|
|
||||||
@@ -126,12 +153,6 @@ namespace TweetDck.Core.Other.Settings{
|
|||||||
notification.ShowNotificationForSettings(true);
|
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){
|
private void comboBoxDisplay_SelectedValueChanged(object sender, EventArgs e){
|
||||||
if (!Ready)return;
|
if (!Ready)return;
|
||||||
|
|
||||||
@@ -146,5 +167,29 @@ namespace TweetDck.Core.Other.Settings{
|
|||||||
Config.NotificationEdgeDistance = trackBarEdgeDistance.Value;
|
Config.NotificationEdgeDistance = trackBarEdgeDistance.Value;
|
||||||
notification.ShowNotificationForSettings(false);
|
notification.ShowNotificationForSettings(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void tbCustomSound_TextChanged(object sender, EventArgs e){
|
||||||
|
// also runs when the control is created, i.e. when Ready is false
|
||||||
|
|
||||||
|
bool fileExists = string.IsNullOrEmpty(tbCustomSound.Text) || File.Exists(tbCustomSound.Text);
|
||||||
|
tbCustomSound.ForeColor = fileExists ? SystemColors.WindowText : Color.Maroon;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btnBrowseSound_Click(object sender, EventArgs e){
|
||||||
|
using(OpenFileDialog dialog = new OpenFileDialog{
|
||||||
|
AutoUpgradeEnabled = true,
|
||||||
|
DereferenceLinks = true,
|
||||||
|
Title = "Custom Notification Sound",
|
||||||
|
Filter = "Wave file (*.wav)|*.wav"
|
||||||
|
}){
|
||||||
|
if (dialog.ShowDialog() == DialogResult.OK){
|
||||||
|
tbCustomSound.Text = dialog.FileName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btnResetSound_Click(object sender, EventArgs e){
|
||||||
|
tbCustomSound.Text = string.Empty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDck.Updates;
|
using TweetDck.Updates;
|
||||||
|
using TweetDck.Updates.Events;
|
||||||
|
|
||||||
namespace TweetDck.Core.Other.Settings{
|
namespace TweetDck.Core.Other.Settings{
|
||||||
partial class TabSettingsUpdates : BaseTabSettings{
|
partial class TabSettingsUpdates : BaseTabSettings{
|
||||||
@@ -11,11 +12,11 @@ namespace TweetDck.Core.Other.Settings{
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
this.updates = updates;
|
this.updates = updates;
|
||||||
|
|
||||||
this.updates.CheckFinished += updates_CheckFinished;
|
this.updates.CheckFinished += updates_CheckFinished;
|
||||||
Disposed += (sender, args) => this.updates.CheckFinished -= updates_CheckFinished;
|
|
||||||
|
|
||||||
checkUpdateNotifications.Checked = Config.EnableUpdateCheck;
|
checkUpdateNotifications.Checked = Config.EnableUpdateCheck;
|
||||||
|
|
||||||
|
Disposed += (sender, args) => this.updates.CheckFinished -= updates_CheckFinished;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkUpdateNotifications_CheckedChanged(object sender, EventArgs e){
|
private void checkUpdateNotifications_CheckedChanged(object sender, EventArgs e){
|
||||||
@@ -27,12 +28,18 @@ namespace TweetDck.Core.Other.Settings{
|
|||||||
private void btnCheckUpdates_Click(object sender, EventArgs e){
|
private void btnCheckUpdates_Click(object sender, EventArgs e){
|
||||||
if (!Ready)return;
|
if (!Ready)return;
|
||||||
|
|
||||||
Config.DismissedUpdate = string.Empty;
|
|
||||||
Config.Save();
|
|
||||||
|
|
||||||
updateCheckEventId = updates.Check(true);
|
updateCheckEventId = updates.Check(true);
|
||||||
|
|
||||||
|
if (updateCheckEventId == -1){
|
||||||
|
MessageBox.Show("Sorry, your system is no longer supported.", "Unsupported System", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||||
|
}
|
||||||
|
else{
|
||||||
btnCheckUpdates.Enabled = false;
|
btnCheckUpdates.Enabled = false;
|
||||||
|
|
||||||
|
updates.Settings.DismissedUpdate = string.Empty;
|
||||||
|
Config.DismissedUpdate = string.Empty;
|
||||||
|
Config.Save();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updates_CheckFinished(object sender, UpdateCheckEventArgs e){
|
private void updates_CheckFinished(object sender, UpdateCheckEventArgs e){
|
||||||
|
@@ -27,7 +27,27 @@ namespace TweetDck.Core.Utils{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OpenExternalBrowser(string url){ // TODO implement mailto
|
public static bool IsValidUrl(string url){
|
||||||
|
Uri uri;
|
||||||
|
|
||||||
|
if (Uri.TryCreate(url, UriKind.Absolute, out uri)){
|
||||||
|
string scheme = uri.Scheme;
|
||||||
|
return scheme == Uri.UriSchemeHttp || scheme == Uri.UriSchemeHttps || scheme == Uri.UriSchemeFtp || scheme == Uri.UriSchemeMailto;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void OpenExternalBrowser(string url){
|
||||||
|
if (IsValidUrl(url)){
|
||||||
|
OpenExternalBrowserUnsafe(url);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
MessageBox.Show("A potentially malicious URL was blocked from opening:"+Environment.NewLine+url, "Blocked URL", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void OpenExternalBrowserUnsafe(string url){
|
||||||
using(Process.Start(url)){}
|
using(Process.Start(url)){}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -105,7 +105,7 @@ namespace TweetDck.Core.Utils{
|
|||||||
build.Append(kvp.Key).Append(" \"").Append(kvp.Value).Append("\" ");
|
build.Append(kvp.Key).Append(" \"").Append(kvp.Value).Append("\" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
return build.Remove(build.Length-1, 1).ToString();
|
return build.Length == 0 ? string.Empty : build.Remove(build.Length-1, 1).ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
@@ -24,13 +25,39 @@ namespace TweetDck.Core.Utils{
|
|||||||
Left, Right
|
Left, Right
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct LASTINPUTINFO{
|
||||||
|
public static readonly uint Size = (uint)Marshal.SizeOf(typeof(LASTINPUTINFO));
|
||||||
|
|
||||||
|
// ReSharper disable once NotAccessedField.Local
|
||||||
|
public uint cbSize;
|
||||||
|
#pragma warning disable 649
|
||||||
|
public uint dwTime;
|
||||||
|
#pragma warning restore 649
|
||||||
|
}
|
||||||
|
|
||||||
public delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);
|
public delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);
|
||||||
|
|
||||||
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
|
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
|
||||||
public static extern bool SetWindowPos(int hWnd, int hWndOrder, int x, int y, int width, int height, uint flags);
|
private static extern bool SetWindowPos(int hWnd, int hWndOrder, int x, int y, int width, int height, uint flags);
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
[DllImport("user32.dll")]
|
||||||
public static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);
|
private static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern bool GetLastInputInfo(ref LASTINPUTINFO info);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern IntPtr GetWindowDC(IntPtr hWnd);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern IntPtr GetDC(IntPtr hWnd);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);
|
||||||
|
|
||||||
|
[DllImport("gdi32.dll")]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop);
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
[DllImport("user32.dll")]
|
||||||
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam, IntPtr lParam);
|
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam, IntPtr lParam);
|
||||||
@@ -75,5 +102,43 @@ namespace TweetDck.Core.Utils{
|
|||||||
mouse_event(flagHold, 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);
|
mouse_event(flagRelease, Cursor.Position.X, Cursor.Position.Y, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int GetIdleSeconds(){
|
||||||
|
LASTINPUTINFO info = new LASTINPUTINFO();
|
||||||
|
info.cbSize = LASTINPUTINFO.Size;
|
||||||
|
|
||||||
|
if (!GetLastInputInfo(ref info)){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint ticks;
|
||||||
|
|
||||||
|
unchecked{
|
||||||
|
ticks = (uint)Environment.TickCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
int seconds = (int)Math.Floor(TimeSpan.FromMilliseconds(ticks-info.dwTime).TotalSeconds);
|
||||||
|
return Math.Max(0, seconds); // ignore rollover after several weeks of uptime
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IntPtr GetDeviceContext(Form form, bool includeBorder){
|
||||||
|
return includeBorder ? GetWindowDC(form.Handle) : GetDC(form.Handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RenderSourceIntoBitmap(IntPtr source, Bitmap target){
|
||||||
|
using(Graphics graphics = Graphics.FromImage(target)){
|
||||||
|
IntPtr graphicsHandle = graphics.GetHdc();
|
||||||
|
|
||||||
|
try{
|
||||||
|
BitBlt(graphicsHandle, 0, 0, target.Width, target.Height, source, 0, 0, 0x00CC0020);
|
||||||
|
}finally{
|
||||||
|
graphics.ReleaseHdc(graphicsHandle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ReleaseDeviceContext(Form form, IntPtr ctx){
|
||||||
|
ReleaseDC(form.Handle, ctx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
100
Core/Utils/TwoKeyDictionary.cs
Normal file
100
Core/Utils/TwoKeyDictionary.cs
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace TweetDck.Core.Utils{
|
||||||
|
class TwoKeyDictionary<K1, K2, V>{
|
||||||
|
private readonly Dictionary<K1, Dictionary<K2, V>> dict;
|
||||||
|
private readonly int innerCapacity;
|
||||||
|
|
||||||
|
public TwoKeyDictionary() : this(16, 16){}
|
||||||
|
|
||||||
|
public TwoKeyDictionary(int outerCapacity, int innerCapacity){
|
||||||
|
this.dict = new Dictionary<K1, Dictionary<K2, V>>(outerCapacity);
|
||||||
|
this.innerCapacity = innerCapacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Properties
|
||||||
|
|
||||||
|
public V this[K1 outerKey, K2 innerKey]{
|
||||||
|
get{ // throws on missing key
|
||||||
|
return dict[outerKey][innerKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
set{
|
||||||
|
Dictionary<K2, V> innerDict;
|
||||||
|
|
||||||
|
if (!dict.TryGetValue(outerKey, out innerDict)){
|
||||||
|
dict.Add(outerKey, innerDict = new Dictionary<K2, V>(innerCapacity));
|
||||||
|
}
|
||||||
|
|
||||||
|
innerDict[innerKey] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Members
|
||||||
|
|
||||||
|
public void Add(K1 outerKey, K2 innerKey, V value){ // throws on duplicate
|
||||||
|
Dictionary<K2, V> innerDict;
|
||||||
|
|
||||||
|
if (!dict.TryGetValue(outerKey, out innerDict)){
|
||||||
|
dict.Add(outerKey, innerDict = new Dictionary<K2, V>(innerCapacity));
|
||||||
|
}
|
||||||
|
|
||||||
|
innerDict.Add(innerKey, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear(){
|
||||||
|
this.dict.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear(K1 outerKey){ // throws on missing key, but keeps the key unlike Remove(K1)
|
||||||
|
dict[outerKey].Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(K1 outerKey){
|
||||||
|
return dict.ContainsKey(outerKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(K1 outerKey, K2 innerKey){
|
||||||
|
Dictionary<K2, V> innerDict;
|
||||||
|
return dict.TryGetValue(outerKey, out innerDict) && innerDict.ContainsKey(innerKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Count(){
|
||||||
|
return dict.Values.Sum(d => d.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Count(K1 outerKey){ // throws on missing key
|
||||||
|
return dict[outerKey].Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Remove(K1 outerKey){
|
||||||
|
return dict.Remove(outerKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Remove(K1 outerKey, K2 innerKey){
|
||||||
|
Dictionary<K2, V> innerDict;
|
||||||
|
|
||||||
|
if (dict.TryGetValue(outerKey, out innerDict) && innerDict.Remove(innerKey)){
|
||||||
|
if (innerDict.Count == 0){
|
||||||
|
dict.Remove(outerKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetValue(K1 outerKey, K2 innerKey, out V value){
|
||||||
|
Dictionary<K2, V> innerDict;
|
||||||
|
|
||||||
|
if (dict.TryGetValue(outerKey, out innerDict)){
|
||||||
|
return innerDict.TryGetValue(innerKey, out value);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
value = default(V);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,8 +1,17 @@
|
|||||||
using System.Diagnostics;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using Timer = System.Windows.Forms.Timer;
|
||||||
|
|
||||||
namespace TweetDck.Core.Utils{
|
namespace TweetDck.Core.Utils{
|
||||||
static class WindowsUtils{
|
static class WindowsUtils{
|
||||||
|
private static readonly Regex RegexStripHtmlStyles = new Regex(@"\s?(?:style|class)="".*?""");
|
||||||
|
private static readonly Regex RegexOffsetClipboardHtml = new Regex(@"(?<=EndHTML:|EndFragment:)(\d+)");
|
||||||
|
|
||||||
public static bool CheckFolderWritePermission(string path){
|
public static bool CheckFolderWritePermission(string path){
|
||||||
string testFile = Path.Combine(path, ".test");
|
string testFile = Path.Combine(path, ".test");
|
||||||
|
|
||||||
@@ -29,5 +38,63 @@ namespace TweetDck.Core.Utils{
|
|||||||
|
|
||||||
return Process.Start(processInfo);
|
return Process.Start(processInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Timer CreateSingleTickTimer(int timeout){
|
||||||
|
Timer timer = new Timer{
|
||||||
|
Interval = timeout
|
||||||
|
};
|
||||||
|
|
||||||
|
timer.Tick += (sender, args) => timer.Stop();
|
||||||
|
return timer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TrySleepUntil(Func<bool> test, int timeoutMillis, int timeStepMillis){
|
||||||
|
for(int waited = 0; waited < timeoutMillis; waited += timeStepMillis){
|
||||||
|
if (test()){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread.Sleep(timeStepMillis);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ClipboardStripHtmlStyles(){
|
||||||
|
if (!Clipboard.ContainsText(TextDataFormat.Html)){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string originalText = Clipboard.GetText(TextDataFormat.UnicodeText);
|
||||||
|
string originalHtml = Clipboard.GetText(TextDataFormat.Html);
|
||||||
|
|
||||||
|
string updatedHtml = RegexStripHtmlStyles.Replace(originalHtml, string.Empty);
|
||||||
|
|
||||||
|
int removed = originalHtml.Length-updatedHtml.Length;
|
||||||
|
updatedHtml = RegexOffsetClipboardHtml.Replace(updatedHtml, match => (int.Parse(match.Value)-removed).ToString().PadLeft(match.Value.Length, '0'));
|
||||||
|
|
||||||
|
DataObject obj = new DataObject();
|
||||||
|
obj.SetText(originalText, TextDataFormat.UnicodeText);
|
||||||
|
obj.SetText(updatedHtml, TextDataFormat.Html);
|
||||||
|
SetClipboardData(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetClipboard(string text, TextDataFormat format){
|
||||||
|
if (string.IsNullOrEmpty(text)){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataObject obj = new DataObject();
|
||||||
|
obj.SetText(text, format);
|
||||||
|
SetClipboardData(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetClipboardData(DataObject obj){
|
||||||
|
try{
|
||||||
|
Clipboard.SetDataObject(obj);
|
||||||
|
}catch(ExternalException e){
|
||||||
|
Program.Reporter.HandleException("Clipboard Error", Program.BrandName+" could not access the clipboard as it is currently used by another process.", true, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,24 +1,35 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using TweetDck.Core.Utils;
|
||||||
using TweetDck.Plugins.Enums;
|
using TweetDck.Plugins.Enums;
|
||||||
using TweetDck.Plugins.Events;
|
using TweetDck.Plugins.Events;
|
||||||
|
|
||||||
namespace TweetDck.Plugins{
|
namespace TweetDck.Plugins{
|
||||||
class PluginBridge{
|
class PluginBridge{
|
||||||
|
private static string SanitizeCacheKey(string key){
|
||||||
|
return key.Replace('\\', '/').Trim();
|
||||||
|
}
|
||||||
|
|
||||||
private readonly PluginManager manager;
|
private readonly PluginManager manager;
|
||||||
private readonly Dictionary<string, string> fileCache = new Dictionary<string, string>(2);
|
private readonly TwoKeyDictionary<int, string, string> fileCache = new TwoKeyDictionary<int, string, string>(4, 2);
|
||||||
|
|
||||||
public PluginBridge(PluginManager manager){
|
public PluginBridge(PluginManager manager){
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
this.manager.Reloaded += manager_Reloaded;
|
this.manager.Reloaded += manager_Reloaded;
|
||||||
|
this.manager.PluginChangedState += manager_PluginChangedState;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void manager_Reloaded(object sender, PluginLoadEventArgs e){
|
private void manager_Reloaded(object sender, PluginLoadEventArgs e){
|
||||||
fileCache.Clear();
|
fileCache.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void manager_PluginChangedState(object sender, PluginChangedStateEventArgs e){
|
||||||
|
if (!e.IsEnabled){
|
||||||
|
fileCache.Remove(manager.GetTokenFromPlugin(e.Plugin));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private string GetFullPathOrThrow(int token, PluginFolder folder, string path){
|
private string GetFullPathOrThrow(int token, PluginFolder folder, string path){
|
||||||
Plugin plugin = manager.GetPluginFromToken(token);
|
Plugin plugin = manager.GetPluginFromToken(token);
|
||||||
string fullPath = plugin == null ? string.Empty : plugin.GetFullPathIfSafe(folder, path);
|
string fullPath = plugin == null ? string.Empty : plugin.GetFullPathIfSafe(folder, path);
|
||||||
@@ -35,15 +46,17 @@ namespace TweetDck.Plugins{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ReadFileUnsafe(string fullPath, bool readCached){
|
private string ReadFileUnsafe(int token, string cacheKey, string fullPath, bool readCached){
|
||||||
|
cacheKey = SanitizeCacheKey(cacheKey);
|
||||||
|
|
||||||
string cachedContents;
|
string cachedContents;
|
||||||
|
|
||||||
if (readCached && fileCache.TryGetValue(fullPath, out cachedContents)){
|
if (readCached && fileCache.TryGetValue(token, cacheKey, out cachedContents)){
|
||||||
return cachedContents;
|
return cachedContents;
|
||||||
}
|
}
|
||||||
|
|
||||||
try{
|
try{
|
||||||
return fileCache[fullPath] = File.ReadAllText(fullPath, Encoding.UTF8);
|
return fileCache[token, cacheKey] = File.ReadAllText(fullPath, Encoding.UTF8);
|
||||||
}catch(FileNotFoundException){
|
}catch(FileNotFoundException){
|
||||||
throw new Exception("File not found.");
|
throw new Exception("File not found.");
|
||||||
}catch(DirectoryNotFoundException){
|
}catch(DirectoryNotFoundException){
|
||||||
@@ -60,17 +73,17 @@ namespace TweetDck.Plugins{
|
|||||||
Directory.CreateDirectory(Path.GetDirectoryName(fullPath));
|
Directory.CreateDirectory(Path.GetDirectoryName(fullPath));
|
||||||
|
|
||||||
File.WriteAllText(fullPath, contents, Encoding.UTF8);
|
File.WriteAllText(fullPath, contents, Encoding.UTF8);
|
||||||
fileCache[fullPath] = contents;
|
fileCache[token, SanitizeCacheKey(path)] = contents;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ReadFile(int token, string path, bool cache){
|
public string ReadFile(int token, string path, bool cache){
|
||||||
return ReadFileUnsafe(GetFullPathOrThrow(token, PluginFolder.Data, path), cache);
|
return ReadFileUnsafe(token, path, GetFullPathOrThrow(token, PluginFolder.Data, path), cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DeleteFile(int token, string path){
|
public void DeleteFile(int token, string path){
|
||||||
string fullPath = GetFullPathOrThrow(token, PluginFolder.Data, path);
|
string fullPath = GetFullPathOrThrow(token, PluginFolder.Data, path);
|
||||||
|
|
||||||
fileCache.Remove(fullPath);
|
fileCache.Remove(token, SanitizeCacheKey(path));
|
||||||
File.Delete(fullPath);
|
File.Delete(fullPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +92,7 @@ namespace TweetDck.Plugins{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public string ReadFileRoot(int token, string path){
|
public string ReadFileRoot(int token, string path){
|
||||||
return ReadFileUnsafe(GetFullPathOrThrow(token, PluginFolder.Root, path), true);
|
return ReadFileUnsafe(token, "root*"+path, GetFullPathOrThrow(token, PluginFolder.Root, path), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CheckFileExistsRoot(int token, string path){
|
public bool CheckFileExistsRoot(int token, string path){
|
||||||
|
@@ -13,6 +13,8 @@ namespace TweetDck.Plugins{
|
|||||||
public const string PluginNotificationScriptFile = "plugins.notification.js";
|
public const string PluginNotificationScriptFile = "plugins.notification.js";
|
||||||
public const string PluginGlobalScriptFile = "plugins.js";
|
public const string PluginGlobalScriptFile = "plugins.js";
|
||||||
|
|
||||||
|
private const int InvalidToken = 0;
|
||||||
|
|
||||||
public string PathOfficialPlugins { get { return Path.Combine(rootPath, "official"); } }
|
public string PathOfficialPlugins { get { return Path.Combine(rootPath, "official"); } }
|
||||||
public string PathCustomPlugins { get { return Path.Combine(rootPath, "user"); } }
|
public string PathCustomPlugins { get { return Path.Combine(rootPath, "user"); } }
|
||||||
|
|
||||||
@@ -37,6 +39,10 @@ namespace TweetDck.Plugins{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void SetConfig(PluginConfig config){
|
public void SetConfig(PluginConfig config){
|
||||||
|
if (this.Config != null){
|
||||||
|
this.Config.InternalPluginChangedState -= Config_InternalPluginChangedState;
|
||||||
|
}
|
||||||
|
|
||||||
this.Config = config;
|
this.Config = config;
|
||||||
this.Config.InternalPluginChangedState += Config_InternalPluginChangedState;
|
this.Config.InternalPluginChangedState += Config_InternalPluginChangedState;
|
||||||
}
|
}
|
||||||
@@ -63,6 +69,16 @@ namespace TweetDck.Plugins{
|
|||||||
return plugins.Any(plugin => plugin.Environments.HasFlag(environment));
|
return plugins.Any(plugin => plugin.Environments.HasFlag(environment));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int GetTokenFromPlugin(Plugin plugin){
|
||||||
|
foreach(KeyValuePair<int, Plugin> kvp in tokens){
|
||||||
|
if (kvp.Value.Equals(plugin)){
|
||||||
|
return kvp.Key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return InvalidToken;
|
||||||
|
}
|
||||||
|
|
||||||
public Plugin GetPluginFromToken(int token){
|
public Plugin GetPluginFromToken(int token){
|
||||||
Plugin plugin;
|
Plugin plugin;
|
||||||
return tokens.TryGetValue(token, out plugin) ? plugin : null;
|
return tokens.TryGetValue(token, out plugin) ? plugin : null;
|
||||||
@@ -108,7 +124,7 @@ namespace TweetDck.Plugins{
|
|||||||
int token;
|
int token;
|
||||||
|
|
||||||
if (tokens.ContainsValue(plugin)){
|
if (tokens.ContainsValue(plugin)){
|
||||||
token = tokens.First(kvp => kvp.Value.Equals(plugin)).Key;
|
token = GetTokenFromPlugin(plugin);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
token = GenerateToken();
|
token = GenerateToken();
|
||||||
@@ -141,7 +157,7 @@ namespace TweetDck.Plugins{
|
|||||||
for(int attempt = 0; attempt < 1000; attempt++){
|
for(int attempt = 0; attempt < 1000; attempt++){
|
||||||
int token = rand.Next();
|
int token = rand.Next();
|
||||||
|
|
||||||
if (!tokens.ContainsKey(token)){
|
if (!tokens.ContainsKey(token) && token != InvalidToken){
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
33
Program.cs
33
Program.cs
@@ -21,8 +21,8 @@ namespace TweetDck{
|
|||||||
public const string BrandName = "TweetDuck";
|
public const string BrandName = "TweetDuck";
|
||||||
public const string Website = "https://tweetduck.chylex.com";
|
public const string Website = "https://tweetduck.chylex.com";
|
||||||
|
|
||||||
public const string VersionTag = "1.5.1";
|
public const string VersionTag = "1.6.6";
|
||||||
public const string VersionFull = "1.5.1.0";
|
public const string VersionFull = "1.6.6.0";
|
||||||
|
|
||||||
public static readonly Version Version = new Version(VersionTag);
|
public static readonly Version Version = new Version(VersionTag);
|
||||||
|
|
||||||
@@ -89,21 +89,26 @@ namespace TweetDck{
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else{
|
else Thread.Sleep(500);
|
||||||
Thread.Sleep(500);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
LockManager.Result lockResult = LockManager.Lock();
|
LockManager.Result lockResult = LockManager.Lock();
|
||||||
|
|
||||||
if (lockResult == LockManager.Result.HasProcess){
|
if (lockResult == LockManager.Result.HasProcess){
|
||||||
if (LockManager.LockingProcess.MainWindowHandle == IntPtr.Zero && LockManager.LockingProcess.Responding){ // restore if the original process is in tray
|
if (LockManager.LockingProcess.MainWindowHandle == IntPtr.Zero){ // restore if the original process is in tray
|
||||||
NativeMethods.SendMessage(NativeMethods.HWND_BROADCAST, WindowRestoreMessage, 0, IntPtr.Zero);
|
NativeMethods.SendMessage(NativeMethods.HWND_BROADCAST, WindowRestoreMessage, LockManager.LockingProcess.Id, IntPtr.Zero);
|
||||||
return;
|
|
||||||
|
if (WindowsUtils.TrySleepUntil(() => {
|
||||||
|
LockManager.LockingProcess.Refresh();
|
||||||
|
return LockManager.LockingProcess.HasExited || (LockManager.LockingProcess.MainWindowHandle != IntPtr.Zero && LockManager.LockingProcess.Responding);
|
||||||
|
}, 2000, 250)){
|
||||||
|
return; // should trigger on first attempt if succeeded, but wait just in case
|
||||||
}
|
}
|
||||||
else if (MessageBox.Show("Another instance of "+BrandName+" is already running.\r\nDo you want to close it?", BrandName+" is Already Running", MessageBoxButtons.YesNo, MessageBoxIcon.Error, MessageBoxDefaultButton.Button2) == DialogResult.Yes){
|
}
|
||||||
if (!LockManager.CloseLockingProcess(30000)){
|
|
||||||
|
if (MessageBox.Show("Another instance of "+BrandName+" is already running.\r\nDo you want to close it?", BrandName+" is Already Running", MessageBoxButtons.YesNo, MessageBoxIcon.Error, MessageBoxDefaultButton.Button2) == DialogResult.Yes){
|
||||||
|
if (!LockManager.CloseLockingProcess(10000, 5000)){
|
||||||
MessageBox.Show("Could not close the other process.", BrandName+" Has Failed :(", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
MessageBox.Show("Could not close the other process.", BrandName+" Has Failed :(", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -126,6 +131,8 @@ namespace TweetDck{
|
|||||||
|
|
||||||
BrowserCache.ClearOldCacheFiles();
|
BrowserCache.ClearOldCacheFiles();
|
||||||
|
|
||||||
|
CefSharpSettings.WcfEnabled = false;
|
||||||
|
|
||||||
CefSettings settings = new CefSettings{
|
CefSettings settings = new CefSettings{
|
||||||
AcceptLanguageList = BrowserUtils.HeaderAcceptLanguage,
|
AcceptLanguageList = BrowserUtils.HeaderAcceptLanguage,
|
||||||
UserAgent = BrowserUtils.HeaderUserAgent,
|
UserAgent = BrowserUtils.HeaderUserAgent,
|
||||||
@@ -155,7 +162,8 @@ namespace TweetDck{
|
|||||||
plugins.Reload();
|
plugins.Reload();
|
||||||
|
|
||||||
FormBrowser mainForm = new FormBrowser(plugins, new UpdaterSettings{
|
FormBrowser mainForm = new FormBrowser(plugins, new UpdaterSettings{
|
||||||
AllowPreReleases = Args.HasFlag("-debugupdates")
|
AllowPreReleases = Args.HasFlag("-debugupdates"),
|
||||||
|
DismissedUpdate = UserConfig.DismissedUpdate
|
||||||
});
|
});
|
||||||
|
|
||||||
Application.Run(mainForm);
|
Application.Run(mainForm);
|
||||||
@@ -163,10 +171,11 @@ namespace TweetDck{
|
|||||||
if (mainForm.UpdateInstallerPath != null){
|
if (mainForm.UpdateInstallerPath != null){
|
||||||
ExitCleanup();
|
ExitCleanup();
|
||||||
|
|
||||||
|
// ProgramPath has a trailing backslash
|
||||||
string updaterArgs = "/SP- /SILENT /CLOSEAPPLICATIONS /UPDATEPATH=\""+ProgramPath+"\" /RUNARGS=\""+GetArgsClean().ToString().Replace("\"", "^\"")+"\""+(IsPortable ? " /PORTABLE=1" : "");
|
string updaterArgs = "/SP- /SILENT /CLOSEAPPLICATIONS /UPDATEPATH=\""+ProgramPath+"\" /RUNARGS=\""+GetArgsClean().ToString().Replace("\"", "^\"")+"\""+(IsPortable ? " /PORTABLE=1" : "");
|
||||||
bool runElevated = !IsPortable || !WindowsUtils.CheckFolderWritePermission(ProgramPath);
|
bool runElevated = !IsPortable || !WindowsUtils.CheckFolderWritePermission(ProgramPath);
|
||||||
|
|
||||||
WindowsUtils.StartProcess(mainForm.UpdateInstallerPath, updaterArgs, runElevated); // ProgramPath has a trailing backslash
|
WindowsUtils.StartProcess(mainForm.UpdateInstallerPath, updaterArgs, runElevated);
|
||||||
Application.Exit();
|
Application.Exit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -37,3 +37,7 @@ using TweetDck;
|
|||||||
[assembly: AssemblyFileVersion(Program.VersionFull)]
|
[assembly: AssemblyFileVersion(Program.VersionFull)]
|
||||||
|
|
||||||
[assembly: NeutralResourcesLanguageAttribute("en")]
|
[assembly: NeutralResourcesLanguageAttribute("en")]
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("UnitTests")]
|
||||||
|
#endif
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
# Build Instructions
|
# 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 53 or newer currently available as a pre-release.
|
The program was build using Visual Studio 2013. After opening the solution, make sure you have **CefSharp.WinForms** and **Microsoft.VC120.CRT.JetBrains** included - if not, download them using NuGet.
|
||||||
```
|
```
|
||||||
PM> Install-Package CefSharp.WinForms -Pre -Version 53.0.0-pre01
|
PM> Install-Package CefSharp.WinForms -Version 55.0.0
|
||||||
PM> Install-Package Microsoft.VC120.CRT.JetBrains
|
PM> Install-Package Microsoft.VC120.CRT.JetBrains
|
||||||
```
|
```
|
||||||
|
|
||||||
After building, run **_postbuild.bat** which deletes unnecessary files that CefSharp adds after post-build events >_>
|
After building, run either `_postbuild.bat` if you want to package the files yourself, or `bld/RUN BUILD.bat` to generate installer files using Inno Setup. Do not run both files consecutively, otherwise the program will crash - if you want to do both, rebuild the project before running each file.
|
||||||
|
|
||||||
Built files are then available in **bin/x86** and/or **bin/x64**.
|
Built files are then available in **bin/x86** and/or **bin/x64**, installer files are generated in **bld/Output**. If you decide to release a custom version publicly, please make it clear that it is not the original TweetDuck.
|
||||||
|
14
Resources/Plugins/.debug/.meta
Normal file
14
Resources/Plugins/.debug/.meta
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
[name]
|
||||||
|
Debug plugin
|
||||||
|
|
||||||
|
[description]
|
||||||
|
- Runs several tests on startup, only included in debug configuration
|
||||||
|
|
||||||
|
[author]
|
||||||
|
chylex
|
||||||
|
|
||||||
|
[version]
|
||||||
|
1.0
|
||||||
|
|
||||||
|
[website]
|
||||||
|
https://tweetduck.chylex.com
|
3
Resources/Plugins/.debug/browser.js
Normal file
3
Resources/Plugins/.debug/browser.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
enabled(){
|
||||||
|
|
||||||
|
}
|
@@ -9,7 +9,7 @@ Clear columns
|
|||||||
chylex
|
chylex
|
||||||
|
|
||||||
[version]
|
[version]
|
||||||
1.1
|
1.1.1
|
||||||
|
|
||||||
[website]
|
[website]
|
||||||
https://tweetduck.chylex.com
|
https://tweetduck.chylex.com
|
||||||
|
@@ -116,9 +116,9 @@ ready(){
|
|||||||
|
|
||||||
// add clear all button
|
// add clear all button
|
||||||
$("nav.app-navigator").first().append([
|
$("nav.app-navigator").first().append([
|
||||||
'<a class="link-clean cf app-nav-link padding-hl" data-title="Clear all" data-action="td-clearcolumns-doall">',
|
'<a class="link-clean cf app-nav-link padding-h--10" data-title="Clear all" data-action="td-clearcolumns-doall">',
|
||||||
'<div class="obj-left"><i class="icon icon-large icon-clear-timeline"></i></div>',
|
'<div class="obj-left margin-l--2"><i class="icon icon-medium icon-clear-timeline"></i></div>',
|
||||||
'<div id="clear-columns-btn-all" class="nbfc padding-ts hide-condensed">Clear all</div>',
|
'<div id="clear-columns-btn-all" class="nbfc padding-ts hide-condensed txt-size--16">Clear all</div>',
|
||||||
'</a></nav>'
|
'</a></nav>'
|
||||||
].join(""));
|
].join(""));
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
Revert TweetDeck design changes
|
Revert TweetDeck design changes
|
||||||
|
|
||||||
[description]
|
[description]
|
||||||
|
- Individually configurable options to revert and tweak TweetDeck design changes
|
||||||
- Moves action menu to the right and hides it by default
|
- Moves action menu to the right and hides it by default
|
||||||
- Reverts interactive texts around tweets (such as 'Details' or 'Conversation')
|
- Reverts interactive texts around tweets (such as 'Details' or 'Conversation')
|
||||||
|
|
||||||
@@ -9,10 +10,16 @@ Revert TweetDeck design changes
|
|||||||
chylex
|
chylex
|
||||||
|
|
||||||
[version]
|
[version]
|
||||||
1.1
|
1.2
|
||||||
|
|
||||||
[website]
|
[website]
|
||||||
https://tweetduck.chylex.com
|
https://tweetduck.chylex.com
|
||||||
|
|
||||||
|
[configfile]
|
||||||
|
configuration.js
|
||||||
|
|
||||||
|
[configdefault]
|
||||||
|
configuration.default.js
|
||||||
|
|
||||||
[requires]
|
[requires]
|
||||||
1.4.1
|
1.4.1
|
@@ -1,20 +1,36 @@
|
|||||||
enabled(){
|
enabled(){
|
||||||
// add a stylesheet to change tweet actions
|
|
||||||
this.css = window.TDPF_createCustomStyle(this);
|
this.css = window.TDPF_createCustomStyle(this);
|
||||||
this.css.insert(".tweet-actions { float: right !important; width: auto !important; }");
|
this.prevFooterMustache = TD.mustaches["status/tweet_single_footer.mustache"];
|
||||||
|
|
||||||
|
// load configuration
|
||||||
|
window.TDPF_loadConfigurationFile(this, "configuration.js", "configuration.default.js", config => {
|
||||||
|
if (config.hideTweetActions){
|
||||||
this.css.insert(".tweet-action { opacity: 0; }");
|
this.css.insert(".tweet-action { opacity: 0; }");
|
||||||
this.css.insert(".is-favorite .tweet-action, .is-retweet .tweet-action { opacity: 0.5; visibility: visible !important; }");
|
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:hover .tweet-action, .is-favorite .tweet-action[rel='favorite'], .is-retweet .tweet-action[rel='retweet'] { opacity: 1; visibility: visible !important; }");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.moveTweetActionsToRight){
|
||||||
|
this.css.insert(".tweet-actions { float: right !important; width: auto !important; }");
|
||||||
this.css.insert(".tweet-actions > li:nth-child(4) { margin-right: 2px !important; }");
|
this.css.insert(".tweet-actions > li:nth-child(4) { margin-right: 2px !important; }");
|
||||||
|
}
|
||||||
|
|
||||||
// revert small links around the tweet
|
if (config.smallComposeTextSize){
|
||||||
this.prevFooterMustache = TD.mustaches["status/tweet_single_footer.mustache"];
|
this.css.insert(".compose-text { font-size: 12px !important; height: 120px !important; }");
|
||||||
|
}
|
||||||
|
|
||||||
var footerLayout = TD.mustaches["status/tweet_single_footer.mustache"];
|
if (config.revertConversationLinks){
|
||||||
footerLayout = footerLayout.replace('txt-mute txt-size--12', 'txt-mute txt-small');
|
var footer = TD.mustaches["status/tweet_single_footer.mustache"];
|
||||||
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}}');
|
footer = footer.replace('txt-mute txt-size--12', 'txt-mute txt-small');
|
||||||
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>');
|
footer = footer.replace('{{#inReplyToID}}', '{{^inReplyToID}} <a class="pull-left margin-txs txt-mute txt-small is-vishidden-narrow" href="#" rel="viewDetails">{{_i}}Details{{/i}}</a> <a class="pull-left margin-txs txt-mute txt-small is-vishidden is-visshown-narrow" href="#" rel="viewDetails">{{_i}}Open{{/i}}</a> {{/inReplyToID}} {{#inReplyToID}}');
|
||||||
TD.mustaches["status/tweet_single_footer.mustache"] = footerLayout;
|
footer = footer.replace('<span class="link-complex-target"> {{_i}}View Conversation{{/i}}', '<i class="icon icon-conversation icon-small-context"></i> <span class="link-complex-target"> <span class="is-vishidden-wide is-vishidden-narrow">{{_i}}View{{/i}}</span> <span class="is-vishidden is-visshown-wide">{{_i}}Conversation{{/i}}</span>');
|
||||||
|
TD.mustaches["status/tweet_single_footer.mustache"] = footer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.moveTweetActionsToRight){
|
||||||
|
$(document).on("uiShowActionsMenu", this.uiShowActionsMenuEvent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// fix layout for right-aligned actions menu
|
// fix layout for right-aligned actions menu
|
||||||
this.uiShowActionsMenuEvent = function(){
|
this.uiShowActionsMenuEvent = function(){
|
||||||
@@ -22,13 +38,9 @@ enabled(){
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
ready(){
|
|
||||||
$(document).on("uiShowActionsMenu", this.uiShowActionsMenuEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
disabled(){
|
disabled(){
|
||||||
this.css.remove();
|
this.css.remove();
|
||||||
|
TD.mustaches["status/tweet_single_footer.mustache"] = this.prevFooterMustache;
|
||||||
|
|
||||||
$(document).off("uiShowActionsMenu", this.uiShowActionsMenuEvent);
|
$(document).off("uiShowActionsMenu", this.uiShowActionsMenuEvent);
|
||||||
TD.mustaches["status/tweet_single_footer.mustache"] = this.prevFooterMustache;
|
|
||||||
}
|
}
|
22
Resources/Plugins/design-revert/configuration.default.js
Normal file
22
Resources/Plugins/design-revert/configuration.default.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
/*
|
||||||
|
* Hides the action bar below each tweet.
|
||||||
|
* The action bar fully appears when the mouse is over the tweet, or at half the opacity when the tweet is liked/retweeted.
|
||||||
|
*/
|
||||||
|
hideTweetActions: true,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Moves the action bar to the right side of the tweet.
|
||||||
|
*/
|
||||||
|
moveTweetActionsToRight: true,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reverts New Tweet font size to a smaller one.
|
||||||
|
*/
|
||||||
|
smallComposeTextSize: false,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reverts design changes to the 'View Conversation' and 'Details' links.
|
||||||
|
*/
|
||||||
|
revertConversationLinks: true
|
||||||
|
}
|
18
Resources/Plugins/emoji-keyboard/.meta
Normal file
18
Resources/Plugins/emoji-keyboard/.meta
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
[name]
|
||||||
|
Emoji keyboard
|
||||||
|
|
||||||
|
[description]
|
||||||
|
- Adds an emoji keyboard when writing tweets
|
||||||
|
- Emoji list provided by http://unicode.org/emoji/charts/emoji-ordering.html
|
||||||
|
|
||||||
|
[author]
|
||||||
|
chylex
|
||||||
|
|
||||||
|
[version]
|
||||||
|
1.0
|
||||||
|
|
||||||
|
[website]
|
||||||
|
https://tweetduck.chylex.com
|
||||||
|
|
||||||
|
[requires]
|
||||||
|
1.5.3
|
289
Resources/Plugins/emoji-keyboard/browser.js
Normal file
289
Resources/Plugins/emoji-keyboard/browser.js
Normal file
@@ -0,0 +1,289 @@
|
|||||||
|
enabled(){
|
||||||
|
this.selectedSkinTone = "";
|
||||||
|
|
||||||
|
this.skinToneList = [
|
||||||
|
"", "1F3FB", "1F3FC", "1F3FD", "1F3FE", "1F3FF"
|
||||||
|
];
|
||||||
|
|
||||||
|
this.skinToneNonDefaultList = [
|
||||||
|
"1F3FB", "1F3FC", "1F3FD", "1F3FE", "1F3FF"
|
||||||
|
];
|
||||||
|
|
||||||
|
this.skinToneData = [
|
||||||
|
[ "", "#FFDD67" ],
|
||||||
|
[ "1F3FB", "#FFE1BD" ],
|
||||||
|
[ "1F3FC", "#FED0AC" ],
|
||||||
|
[ "1F3FD", "#D6A57C" ],
|
||||||
|
[ "1F3FE", "#B47D56" ],
|
||||||
|
[ "1F3FF", "#8A6859" ],
|
||||||
|
];
|
||||||
|
|
||||||
|
this.emojiHTML1 = ""; // no skin tones, prepended
|
||||||
|
this.emojiHTML2 = {}; // contains emojis with skin tones
|
||||||
|
this.emojiHTML3 = ""; // no skin tones, appended
|
||||||
|
|
||||||
|
var me = this;
|
||||||
|
|
||||||
|
// styles
|
||||||
|
|
||||||
|
this.css = window.TDPF_createCustomStyle(this);
|
||||||
|
this.css.insert(".emoji-keyboard { position: absolute; width: 15.35em; background-color: white; border-radius: 2px 2px 3px 3px; font-size: 24px; z-index: 9999 }");
|
||||||
|
this.css.insert(".emoji-keyboard-list { height: 10.14em; padding: 0.1em; box-sizing: border-box; overflow-y: auto }");
|
||||||
|
this.css.insert(".emoji-keyboard-list .separator { height: 26px }");
|
||||||
|
this.css.insert(".emoji-keyboard-list .emoji { padding: 0.1em !important; cursor: pointer }");
|
||||||
|
this.css.insert(".emoji-keyboard-skintones { height: 1.3em; text-align: center; background-color: #292f33; border-radius: 0 0 2px 2px }");
|
||||||
|
this.css.insert(".emoji-keyboard-skintones div { width: 0.8em; height: 0.8em; margin: 0.25em 0.1em; border-radius: 50%; display: inline-block; box-sizing: border-box; cursor: pointer }");
|
||||||
|
this.css.insert(".emoji-keyboard-skintones .sel { border: 2px solid rgba(0, 0, 0, 0.35); box-shadow: 0 0 2px 0 rgba(255, 255, 255, 0.65), 0 0 1px 0 rgba(255, 255, 255, 0.4) inset }");
|
||||||
|
this.css.insert(".emoji-keyboard-skintones :hover { border: 2px solid rgba(0, 0, 0, 0.25); box-shadow: 0 0 1px 0 rgba(255, 255, 255, 0.65), 0 0 1px 0 rgba(255, 255, 255, 0.25) inset }");
|
||||||
|
|
||||||
|
// layout
|
||||||
|
|
||||||
|
var buttonHTML = '<button class="needsclick btn btn-on-blue txt-left padding-v--9 emoji-keyboard-popup-btn"><i class="icon icon-heart"></i></button>';
|
||||||
|
|
||||||
|
this.prevComposeMustache = TD.mustaches["compose/docked_compose.mustache"];
|
||||||
|
TD.mustaches["compose/docked_compose.mustache"] = TD.mustaches["compose/docked_compose.mustache"].replace('<div class="cf margin-t--12 margin-b--30">', '<div class="cf margin-t--12 margin-b--30">'+buttonHTML);
|
||||||
|
|
||||||
|
var dockedComposePanel = $(".js-docked-compose");
|
||||||
|
|
||||||
|
if (dockedComposePanel.length){
|
||||||
|
dockedComposePanel.find(".cf.margin-t--12.margin-b--30").first().append(buttonHTML);
|
||||||
|
}
|
||||||
|
|
||||||
|
// keyboard generation
|
||||||
|
|
||||||
|
this.currentKeyboard = null;
|
||||||
|
|
||||||
|
var hideKeyboard = () => {
|
||||||
|
$(this.currentKeyboard).remove();
|
||||||
|
this.currentKeyboard = null;
|
||||||
|
|
||||||
|
$(".emoji-keyboard-popup-btn").removeClass("is-selected");
|
||||||
|
};
|
||||||
|
|
||||||
|
var generateEmojiHTML = skinTone => {
|
||||||
|
return this.emojiHTML1+this.emojiHTML2[skinTone]+this.emojiHTML3;
|
||||||
|
};
|
||||||
|
|
||||||
|
var selectSkinTone = skinTone => {
|
||||||
|
let selectedEle = this.currentKeyboard.children[1].querySelector("[data-tone='"+this.selectedSkinTone+"']");
|
||||||
|
selectedEle && selectedEle.classList.remove("sel");
|
||||||
|
|
||||||
|
this.selectedSkinTone = skinTone;
|
||||||
|
this.currentKeyboard.children[0].innerHTML = generateEmojiHTML(skinTone);
|
||||||
|
this.currentKeyboard.children[1].querySelector("[data-tone='"+this.selectedSkinTone+"']").classList.add("sel");
|
||||||
|
};
|
||||||
|
|
||||||
|
this.generateKeyboard = (input, left, top) => {
|
||||||
|
var outer = document.createElement("div");
|
||||||
|
outer.classList.add("emoji-keyboard");
|
||||||
|
outer.style.left = left+"px";
|
||||||
|
outer.style.top = top+"px";
|
||||||
|
|
||||||
|
var keyboard = document.createElement("div");
|
||||||
|
keyboard.classList.add("emoji-keyboard-list");
|
||||||
|
|
||||||
|
keyboard.addEventListener("click", function(e){
|
||||||
|
if (e.target.tagName === "IMG"){
|
||||||
|
input.val(input.val()+e.target.getAttribute("alt"));
|
||||||
|
input.trigger("change");
|
||||||
|
input.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
e.stopPropagation();
|
||||||
|
});
|
||||||
|
|
||||||
|
var skintones = document.createElement("div");
|
||||||
|
skintones.innerHTML = me.skinToneData.map(entry => "<div data-tone='"+entry[0]+"' style='background-color:"+entry[1]+"'></div>").join("");
|
||||||
|
skintones.classList.add("emoji-keyboard-skintones");
|
||||||
|
|
||||||
|
skintones.addEventListener("click", function(e){
|
||||||
|
if (e.target.hasAttribute("data-tone")){
|
||||||
|
selectSkinTone(e.target.getAttribute("data-tone") || "");
|
||||||
|
}
|
||||||
|
|
||||||
|
e.stopPropagation();
|
||||||
|
});
|
||||||
|
|
||||||
|
outer.appendChild(keyboard);
|
||||||
|
outer.appendChild(skintones);
|
||||||
|
document.body.appendChild(outer);
|
||||||
|
|
||||||
|
this.currentKeyboard = outer;
|
||||||
|
selectSkinTone(this.selectedSkinTone);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.prevTryPasteImage = window.TDGF_tryPasteImage;
|
||||||
|
var prevTryPasteImageF = this.prevTryPasteImage;
|
||||||
|
|
||||||
|
window.TDGF_tryPasteImage = function(){
|
||||||
|
if (me.currentKeyboard){
|
||||||
|
hideKeyboard();
|
||||||
|
}
|
||||||
|
|
||||||
|
return prevTryPasteImageF.apply(this, arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
// event handlers
|
||||||
|
|
||||||
|
this.emojiKeyboardButtonClickEvent = function(e){
|
||||||
|
if (me.currentKeyboard){
|
||||||
|
hideKeyboard();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
var pos = $(this).offset();
|
||||||
|
me.generateKeyboard($(".js-compose-text").first(), pos.left, pos.top+$(this).outerHeight()+8);
|
||||||
|
|
||||||
|
$(this).addClass("is-selected");
|
||||||
|
}
|
||||||
|
|
||||||
|
$(this).blur();
|
||||||
|
e.stopPropagation();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.documentClickEvent = function(e){
|
||||||
|
if (me.currentKeyboard && !e.target.classList.contains("js-compose-text")){
|
||||||
|
hideKeyboard();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.documentKeyEvent = function(e){
|
||||||
|
if (me.currentKeyboard && e.keyCode === 27){ // escape
|
||||||
|
hideKeyboard();
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO
|
||||||
|
* ----
|
||||||
|
* add emoji search if I can be bothered
|
||||||
|
* lazy emoji loading
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
ready(){
|
||||||
|
$(".emoji-keyboard-popup-btn").on("click", this.emojiKeyboardButtonClickEvent);
|
||||||
|
$(document).on("click", this.documentClickEvent);
|
||||||
|
$(document).on("keydown", this.documentKeyEvent);
|
||||||
|
|
||||||
|
// HTML generation
|
||||||
|
|
||||||
|
var convUnicode = function(codePt){
|
||||||
|
if (codePt > 0xFFFF){
|
||||||
|
codePt -= 0x10000;
|
||||||
|
return String.fromCharCode(0xD800+(codePt>>10), 0xDC00+(codePt&0x3FF));
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return String.fromCharCode(codePt);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$TDP.readFileRoot(this.$token, "emoji-ordering.txt").then(contents => {
|
||||||
|
let generated1 = [];
|
||||||
|
let generated2 = {};
|
||||||
|
let generated3 = [];
|
||||||
|
|
||||||
|
for(let skinTone of this.skinToneList){
|
||||||
|
generated2[skinTone] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// declaration inserters
|
||||||
|
|
||||||
|
let addDeclaration1 = decl => {
|
||||||
|
generated1.push(decl.split(" ").map(pt => convUnicode(parseInt(pt, 16))).join(""));
|
||||||
|
};
|
||||||
|
|
||||||
|
let addDeclaration2 = (tone, decl) => {
|
||||||
|
let gen = decl.split(" ").map(pt => convUnicode(parseInt(pt, 16))).join("");
|
||||||
|
|
||||||
|
if (tone === null){
|
||||||
|
for(let skinTone of this.skinToneList){
|
||||||
|
generated2[skinTone].push(gen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
generated2[tone].push(gen);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let addDeclaration3 = decl => {
|
||||||
|
generated3.push(decl.split(" ").map(pt => convUnicode(parseInt(pt, 16))).join(""));
|
||||||
|
};
|
||||||
|
|
||||||
|
// line reading
|
||||||
|
|
||||||
|
let skinToneState = 0;
|
||||||
|
|
||||||
|
for(let line of contents.split("\n")){
|
||||||
|
if (line[0] === '@'){
|
||||||
|
switch(skinToneState){
|
||||||
|
case 0: generated1.push("___"); break;
|
||||||
|
case 1: this.skinToneList.forEach(skinTone => generated2[skinTone].push("___")); break;
|
||||||
|
case 2: generated3.push("___"); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line[1] === '1'){
|
||||||
|
skinToneState = 1;
|
||||||
|
}
|
||||||
|
else if (line[1] === '2'){
|
||||||
|
skinToneState = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (skinToneState === 1){
|
||||||
|
let decl = line.slice(0, line.indexOf(';'));
|
||||||
|
let skinIndex = decl.indexOf('$');
|
||||||
|
|
||||||
|
if (skinIndex !== -1){
|
||||||
|
let declPre = decl.slice(0, skinIndex);
|
||||||
|
let declPost = decl.slice(skinIndex+1);
|
||||||
|
|
||||||
|
for(let skinTone of this.skinToneNonDefaultList){
|
||||||
|
generated2[skinTone].pop();
|
||||||
|
addDeclaration2(skinTone, declPre+skinTone+declPost);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
addDeclaration2(null, decl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (skinToneState === 2){
|
||||||
|
addDeclaration3(line.slice(0, line.indexOf(';')));
|
||||||
|
}
|
||||||
|
else if (skinToneState === 0){
|
||||||
|
addDeclaration1(line.slice(0, line.indexOf(';')));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// final processing
|
||||||
|
|
||||||
|
let replaceSeparators = str => str.replace(/___/g, "<div class='separator'></div>");
|
||||||
|
|
||||||
|
let start = "<p style='font-size:13px;color:#444;margin:4px;text-align:center'>Please, note that most emoji will not show up properly in the text box above, but they will display in the tweet.</p>";
|
||||||
|
this.emojiHTML1 = start+replaceSeparators(TD.util.cleanWithEmoji(generated1.join("")));
|
||||||
|
|
||||||
|
for(let skinTone of this.skinToneList){
|
||||||
|
this.emojiHTML2[skinTone] = replaceSeparators(TD.util.cleanWithEmoji(generated2[skinTone].join("")));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.emojiHTML3 = replaceSeparators(TD.util.cleanWithEmoji(generated3.join("")));
|
||||||
|
}).catch(err => {
|
||||||
|
$TD.alert("error", "Problem loading emoji keyboard: "+err.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
disabled(){
|
||||||
|
this.css.remove();
|
||||||
|
|
||||||
|
if (this.currentKeyboard){
|
||||||
|
$(this.currentKeyboard).remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
window.TDGF_tryPasteImage = this.prevTryPasteImage;
|
||||||
|
|
||||||
|
$(".emoji-keyboard-popup-btn").off("click", this.emojiKeyboardButtonClickEvent);
|
||||||
|
$(".emoji-keyboard-popup-btn").remove();
|
||||||
|
|
||||||
|
$(document).off("click", this.documentClickEvent);
|
||||||
|
$(document).off("keydown", this.documentKeyEvent);
|
||||||
|
TD.mustaches["compose/docked_compose.mustache"] = this.prevComposeMustache;
|
||||||
|
}
|
1606
Resources/Plugins/emoji-keyboard/emoji-ordering.txt
Normal file
1606
Resources/Plugins/emoji-keyboard/emoji-ordering.txt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ Custom reply account
|
|||||||
chylex
|
chylex
|
||||||
|
|
||||||
[version]
|
[version]
|
||||||
1.2
|
1.2.1
|
||||||
|
|
||||||
[website]
|
[website]
|
||||||
https://tweetduck.chylex.com
|
https://tweetduck.chylex.com
|
||||||
|
@@ -22,10 +22,19 @@ enabled(){
|
|||||||
var section = data.element.closest("section.column");
|
var section = data.element.closest("section.column");
|
||||||
|
|
||||||
var column = TD.controller.columnManager.get(section.attr("data-column"));
|
var column = TD.controller.columnManager.get(section.attr("data-column"));
|
||||||
var header = $("h1.column-title", section);
|
var header = $(".column-title", section);
|
||||||
|
var title = header.children(".column-head-title");
|
||||||
|
|
||||||
var columnTitle = header.children(".column-head-title").text();
|
var columnTitle, columnAccount;
|
||||||
var columnAccount = header.children(".attribution").text();
|
|
||||||
|
if (title.length){
|
||||||
|
columnTitle = title.text();
|
||||||
|
columnAccount = header.children(".attribution").text();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
columnTitle = header.children(".column-title-edit-box").val();
|
||||||
|
columnAccount = "";
|
||||||
|
}
|
||||||
|
|
||||||
try{
|
try{
|
||||||
query = configuration.customSelector(column.getColumnType(), columnTitle, columnAccount, column);
|
query = configuration.customSelector(column.getColumnType(), columnTitle, columnAccount, column);
|
||||||
|
@@ -16,7 +16,7 @@ enabled(){
|
|||||||
};
|
};
|
||||||
|
|
||||||
// add poll rendering to tweets
|
// 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}}");
|
injectLayout("status/tweet_single.mustache", "status/poll", "{{/quotedTweetMissing}} {{#translation}}", "{{/quotedTweetMissing}} <div class='timeline-poll-container'>{{#poll}}{{>duck/tweet_single/poll}}{{/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}}} · {{#hasTimeLeft}} {{{prettyTimeLeft}}} {{/hasTimeLeft}} {{^hasTimeLeft}} {{_i}}Final results{{/i}} {{/hasTimeLeft}} </span> {{/poll}} </div>';
|
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}}} · {{#hasTimeLeft}} {{{prettyTimeLeft}}} {{/hasTimeLeft}} {{^hasTimeLeft}} {{_i}}Final results{{/i}} {{/hasTimeLeft}} </span> {{/poll}} </div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
(function($, $TD, TD){
|
(function($, $TD, $TDX, TD){
|
||||||
//
|
//
|
||||||
// Variable: Current highlighted column jQuery object.
|
// Variable: Current highlighted column jQuery object.
|
||||||
//
|
//
|
||||||
@@ -10,71 +10,14 @@
|
|||||||
var highlightedTweetEle;
|
var highlightedTweetEle;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Function: Initializes TweetD*ck events. Called after the website app is loaded.
|
// Variable: Array of functions called after the website app is loaded.
|
||||||
//
|
//
|
||||||
var initializeTweetDck = function(){
|
var onAppReady = [];
|
||||||
// Settings button hook
|
|
||||||
$("[data-action='settings-menu']").click(function(){
|
|
||||||
setTimeout(function(){
|
|
||||||
var menu = $(".js-dropdown-content").children("ul").first();
|
|
||||||
if (menu.length === 0)return;
|
|
||||||
|
|
||||||
menu.children(".drp-h-divider").last().after([
|
//
|
||||||
'<li class="is-selectable" data-std><a href="#" data-action="td-settings">'+$TD.brandName+' settings</a></li>',
|
// Variable: DOM object containing the main app element.
|
||||||
'<li class="is-selectable" data-std><a href="#" data-action="td-plugins">'+$TD.brandName+' plugins</a></li>',
|
//
|
||||||
'<li class="drp-h-divider"></li>'
|
var app = $(document.body).children(".js-app");
|
||||||
].join(""));
|
|
||||||
|
|
||||||
var buttons = menu.children("[data-std]");
|
|
||||||
|
|
||||||
buttons.on("click", "a", function(){
|
|
||||||
var action = $(this).attr("data-action");
|
|
||||||
|
|
||||||
if (action === "td-settings"){
|
|
||||||
$TD.openSettingsMenu();
|
|
||||||
}
|
|
||||||
else if (action === "td-plugins"){
|
|
||||||
$TD.openPluginsMenu();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
buttons.hover(function(){
|
|
||||||
$(this).addClass("is-selected");
|
|
||||||
}, function(){
|
|
||||||
$(this).removeClass("is-selected");
|
|
||||||
});
|
|
||||||
}, 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Notification handling
|
|
||||||
$.subscribe("/notifications/new", function(obj){
|
|
||||||
for(let index = obj.items.length-1; index >= 0; index--){
|
|
||||||
onNewTweet(obj.column, obj.items[index]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 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'>► Open video in browser</a>");
|
|
||||||
});
|
|
||||||
|
|
||||||
$("a[rel='user']").attr("target", "_blank");
|
|
||||||
}).observe($(".js-app-columns")[0], {
|
|
||||||
childList: true,
|
|
||||||
subtree: true
|
|
||||||
});
|
|
||||||
|
|
||||||
// Finish init and load plugins
|
|
||||||
$TD.loadFontSizeClass(TD.settings.getFontSize());
|
|
||||||
$TD.loadNotificationHeadContents(getNotificationHeadContents());
|
|
||||||
|
|
||||||
window.TD_APP_READY = true;
|
|
||||||
|
|
||||||
if (window.TD_PLUGINS){
|
|
||||||
window.TD_PLUGINS.onReady();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Function: Prepends code at the beginning of a function. If the prepended function returns true, execution of the original function is cancelled.
|
// Function: Prepends code at the beginning of a function. If the prepended function returns true, execution of the original function is cancelled.
|
||||||
@@ -112,13 +55,15 @@
|
|||||||
|
|
||||||
html.css("border", "0");
|
html.css("border", "0");
|
||||||
html.find("footer").last().remove(); // apparently withTweetActions breaks for certain tweets, nice
|
html.find("footer").last().remove(); // apparently withTweetActions breaks for certain tweets, nice
|
||||||
|
html.find(".js-quote-detail").removeClass("is-actionable");
|
||||||
|
|
||||||
var url = html.find("time").first().children("a").first().attr("href") || "";
|
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
|
if (column.model.getHasSound()){
|
||||||
|
$TD.onTweetSound();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -135,23 +80,6 @@
|
|||||||
return tags.join("");
|
return tags.join("");
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
|
||||||
// Block: Observe the app <div> element and initialize TweetD*ck whenever possible.
|
|
||||||
//
|
|
||||||
var app = $("body").children(".js-app");
|
|
||||||
|
|
||||||
new MutationObserver(function(){
|
|
||||||
if (window.TD_APP_READY && app.hasClass("is-hidden")){
|
|
||||||
window.TD_APP_READY = false;
|
|
||||||
}
|
|
||||||
else if (!window.TD_APP_READY && !app.hasClass("is-hidden")){
|
|
||||||
initializeTweetDck();
|
|
||||||
}
|
|
||||||
}).observe(app[0], {
|
|
||||||
attributes: true,
|
|
||||||
attributeFilter: [ "class" ]
|
|
||||||
});
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Hook into settings object to detect when the settings change.
|
// Block: Hook into settings object to detect when the settings change.
|
||||||
//
|
//
|
||||||
@@ -166,7 +94,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Force popup notification settings.
|
// Block: Enable popup notifications.
|
||||||
//
|
//
|
||||||
TD.controller.notifications.hasNotifications = function(){
|
TD.controller.notifications.hasNotifications = function(){
|
||||||
return true;
|
return true;
|
||||||
@@ -176,6 +104,49 @@
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$.subscribe("/notifications/new", function(obj){
|
||||||
|
for(let index = obj.items.length-1; index >= 0; index--){
|
||||||
|
onNewTweet(obj.column, obj.items[index]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Add TweetDuck buttons to the settings menu.
|
||||||
|
//
|
||||||
|
onAppReady.push(function(){
|
||||||
|
$("[data-action='settings-menu']").click(function(){
|
||||||
|
setTimeout(function(){
|
||||||
|
var menu = $(".js-dropdown-content").children("ul").first();
|
||||||
|
if (menu.length === 0)return;
|
||||||
|
|
||||||
|
menu.children(".drp-h-divider").last().after([
|
||||||
|
'<li class="is-selectable" data-std><a href="#" data-action="td-settings">TweetDuck settings</a></li>',
|
||||||
|
'<li class="is-selectable" data-std><a href="#" data-action="td-plugins">TweetDuck plugins</a></li>',
|
||||||
|
'<li class="drp-h-divider"></li>'
|
||||||
|
].join(""));
|
||||||
|
|
||||||
|
var buttons = menu.children("[data-std]");
|
||||||
|
|
||||||
|
buttons.on("click", "a", function(){
|
||||||
|
var action = $(this).attr("data-action");
|
||||||
|
|
||||||
|
if (action === "td-settings"){
|
||||||
|
$TD.openSettingsMenu();
|
||||||
|
}
|
||||||
|
else if (action === "td-plugins"){
|
||||||
|
$TD.openPluginsMenu();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
buttons.hover(function(){
|
||||||
|
$(this).addClass("is-selected");
|
||||||
|
}, function(){
|
||||||
|
$(this).removeClass("is-selected");
|
||||||
|
});
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Expand shortened links on hover or display tooltip.
|
// Block: Expand shortened links on hover or display tooltip.
|
||||||
//
|
//
|
||||||
@@ -197,7 +168,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($TD.expandLinksOnHover){
|
if ($TDX.expandLinksOnHover){
|
||||||
tooltipTimer = window.setTimeout(function(){
|
tooltipTimer = window.setTimeout(function(){
|
||||||
var expanded = me.attr("data-full-url");
|
var expanded = me.attr("data-full-url");
|
||||||
expanded = cutStart(expanded, "https://");
|
expanded = cutStart(expanded, "https://");
|
||||||
@@ -216,7 +187,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (e.type === "mouseleave"){
|
else if (e.type === "mouseleave"){
|
||||||
if ($TD.expandLinksOnHover){
|
if ($TDX.expandLinksOnHover){
|
||||||
var prevText = me.attr("td-prev-text");
|
var prevText = me.attr("td-prev-text");
|
||||||
|
|
||||||
if (prevText){
|
if (prevText){
|
||||||
@@ -255,14 +226,10 @@
|
|||||||
var soundEle = document.getElementById("update-sound");
|
var soundEle = document.getElementById("update-sound");
|
||||||
|
|
||||||
soundEle.play = prependToFunction(soundEle.play, function(){
|
soundEle.play = prependToFunction(soundEle.play, function(){
|
||||||
return $TD.muteNotifications;
|
return $TDX.muteNotifications || $TDX.hasCustomNotificationSound;
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|
||||||
/* TODO document.getElementById("update-sound").play = function(){
|
|
||||||
$TD.onTweetSound();
|
|
||||||
};*/
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Update highlighted column.
|
// Block: Update highlighted column.
|
||||||
//
|
//
|
||||||
@@ -292,7 +259,7 @@
|
|||||||
if (e.type === "mouseenter"){
|
if (e.type === "mouseenter"){
|
||||||
highlightedTweetEle = $(this);
|
highlightedTweetEle = $(this);
|
||||||
|
|
||||||
var link = $(this).find("time").first().children("a").first();
|
var link = $(this).parent().hasClass("js-tweet-detail") ? $(this).find("a[rel='url']").first() : $(this).find("time").first().children("a").first();
|
||||||
var embedded = $(this).find(".quoted-tweet[data-tweet-id]").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") : "");
|
||||||
@@ -304,6 +271,67 @@
|
|||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Screenshot tweet to clipboard.
|
||||||
|
//
|
||||||
|
(function(){
|
||||||
|
var selectedTweet;
|
||||||
|
|
||||||
|
var setImportantProperty = function(obj, property, value){
|
||||||
|
if (obj.length === 1){
|
||||||
|
obj[0].style.setProperty(property, value, "important");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
app.delegate("article.js-stream-item", "contextmenu", function(){
|
||||||
|
selectedTweet = $(this);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.TDGF_triggerScreenshot = function(){
|
||||||
|
if (selectedTweet){
|
||||||
|
var tweetWidth = selectedTweet.width();
|
||||||
|
var parent = selectedTweet.parent();
|
||||||
|
|
||||||
|
var isDetail = parent.hasClass("js-tweet-detail");
|
||||||
|
var isReply = !isDetail && (parent.hasClass("js-replies-to") || parent.hasClass("js-replies-before"));
|
||||||
|
|
||||||
|
selectedTweet = selectedTweet.clone();
|
||||||
|
selectedTweet.children().first().addClass($(document.documentElement).attr("class")).css("padding-bottom", "12px");
|
||||||
|
|
||||||
|
setImportantProperty(selectedTweet.find(".js-quote-detail"), "margin-bottom", "0");
|
||||||
|
setImportantProperty(selectedTweet.find(".js-media-preview-container"), "margin-bottom", "0");
|
||||||
|
|
||||||
|
if (isDetail){
|
||||||
|
setImportantProperty(selectedTweet.find(".js-tweet-media"), "margin-bottom", "0");
|
||||||
|
selectedTweet.find(".js-translate-call-to-action").first().remove();
|
||||||
|
selectedTweet.find(".js-tweet").first().nextAll().remove();
|
||||||
|
selectedTweet.find("footer").last().prev().addBack().remove(); // footer & date
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
selectedTweet.find("footer").last().remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isReply){
|
||||||
|
selectedTweet.find(".is-conversation").removeClass("is-conversation");
|
||||||
|
selectedTweet.find(".timeline-poll-container").first().remove(); // fix for timeline polls plugin
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedTweet.find(".js-poll-link").remove();
|
||||||
|
|
||||||
|
var testTweet = selectedTweet.clone().css({
|
||||||
|
position: "absolute",
|
||||||
|
left: "-999px",
|
||||||
|
width: tweetWidth+"px"
|
||||||
|
}).appendTo(document.body);
|
||||||
|
|
||||||
|
var realHeight = testTweet.height();
|
||||||
|
testTweet.remove();
|
||||||
|
|
||||||
|
$TD.screenshotTweet(selectedTweet.html(), tweetWidth, realHeight);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Paste images when tweeting.
|
// Block: Paste images when tweeting.
|
||||||
//
|
//
|
||||||
@@ -316,6 +344,11 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
var clickUpload = function(){
|
var clickUpload = function(){
|
||||||
|
$(document).one("uiFilesAdded", function(){
|
||||||
|
getScroller().scrollTop(prevScrollTop);
|
||||||
|
$(".js-drawer").find(".js-compose-text").first()[0].focus();
|
||||||
|
});
|
||||||
|
|
||||||
var button = $(".js-add-image-button").first();
|
var button = $(".js-add-image-button").first();
|
||||||
|
|
||||||
var scroller = getScroller();
|
var scroller = getScroller();
|
||||||
@@ -328,7 +361,7 @@
|
|||||||
$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(){
|
app.delegate(".js-compose-text,.js-reply-tweetbox", "paste", function(){
|
||||||
lastPasteElement = $(this);
|
lastPasteElement = $(this);
|
||||||
$TD.tryPasteImage();
|
$TD.tryPasteImage();
|
||||||
});
|
});
|
||||||
@@ -367,13 +400,6 @@
|
|||||||
lastPasteElement = null;
|
lastPasteElement = null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
window.TDGF_tryPasteImageFinish = function(){
|
|
||||||
setTimeout(function(){
|
|
||||||
getScroller().scrollTop(prevScrollTop);
|
|
||||||
$(".js-drawer").find(".js-compose-text").first()[0].focus();
|
|
||||||
}, 10);
|
|
||||||
};
|
|
||||||
})();
|
})();
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -402,7 +428,7 @@
|
|||||||
tryClickSelector(".js-inline-compose-close") ||
|
tryClickSelector(".js-inline-compose-close") ||
|
||||||
tryCloseHighlightedColumn() ||
|
tryCloseHighlightedColumn() ||
|
||||||
tryClickSelector(".js-app-content.is-open .js-drawer-close:visible") ||
|
tryClickSelector(".js-app-content.is-open .js-drawer-close:visible") ||
|
||||||
tryClickSelector(".is-shifted-2 .js-tweet-social-proof-back") ||
|
tryClickSelector(".is-shifted-2 .js-tweet-social-proof-back, .is-shifted-2 .js-dm-participants-back") ||
|
||||||
$(".js-column-back").click();
|
$(".js-column-back").click();
|
||||||
}
|
}
|
||||||
else if (button === 2){ // forward button
|
else if (button === 2){ // forward button
|
||||||
@@ -428,6 +454,70 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Hold Shift to reset cleared column.
|
||||||
|
//
|
||||||
|
(function(){
|
||||||
|
var holdingShift = false;
|
||||||
|
|
||||||
|
var updateShiftState = (pressed) => {
|
||||||
|
if (pressed != holdingShift){
|
||||||
|
holdingShift = pressed;
|
||||||
|
$("button[data-action='clear']").children("span").text(holdingShift ? "Reset" : "Clear");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var resetActiveFocus = () => {
|
||||||
|
document.activeElement.blur();
|
||||||
|
};
|
||||||
|
|
||||||
|
$(document).keydown(function(e){
|
||||||
|
if (e.shiftKey && (document.activeElement === null || !("value" in document.activeElement))){
|
||||||
|
updateShiftState(true);
|
||||||
|
}
|
||||||
|
}).keyup(function(e){
|
||||||
|
if (!e.shiftKey){
|
||||||
|
updateShiftState(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
TD.vo.Column.prototype.clear = prependToFunction(TD.vo.Column.prototype.clear, function(){
|
||||||
|
window.setTimeout(resetActiveFocus, 0); // unfocuses the Clear button, otherwise it steals keyboard input
|
||||||
|
|
||||||
|
if (holdingShift){
|
||||||
|
this.model.setClearedTimestamp(0);
|
||||||
|
this.reloadTweets();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Swap shift key functionality for selecting accounts.
|
||||||
|
//
|
||||||
|
onAppReady.push(function(){
|
||||||
|
$(".js-drawer[data-drawer='compose']").delegate(".js-account-list > .js-account-item", "click", function(e){
|
||||||
|
e.shiftKey = !e.shiftKey;
|
||||||
|
});
|
||||||
|
|
||||||
|
TD.components.AccountSelector.prototype.refreshPostingAccounts = appendToFunction(TD.components.AccountSelector.prototype.refreshPostingAccounts, function(){
|
||||||
|
if (!this.$node.attr("td-account-selector-hook")){
|
||||||
|
this.$node.attr("td-account-selector-hook", "1");
|
||||||
|
|
||||||
|
this.$node.delegate(".js-account-item", "click", function(e){
|
||||||
|
e.shiftKey = !e.shiftKey;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Work around clipboard HTML formatting.
|
||||||
|
//
|
||||||
|
$(document).on("copy", function(e){
|
||||||
|
window.setTimeout($TD.fixClipboard, 0);
|
||||||
|
});
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Inject custom CSS and layout into the page.
|
// Block: Inject custom CSS and layout into the page.
|
||||||
//
|
//
|
||||||
@@ -440,10 +530,79 @@
|
|||||||
styleOfficial.sheet.insertRule(".txt-base-smallest .badge-verified:before { height: 13px !important; }", 0); // fix cut off badge icon
|
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
|
styleOfficial.sheet.insertRule(".keyboard-shortcut-list { vertical-align: top; }", 0); // fix keyboard navigation alignment
|
||||||
|
|
||||||
if ($TD.hasCustomBrowserCSS){
|
styleOfficial.sheet.insertRule(".is-video a:not([href*='youtu']), .is-gif .js-media-gif-container { cursor: alias; }", 0); // change cursor on unsupported videos
|
||||||
var styleCustom = document.createElement("style");
|
styleOfficial.sheet.insertRule(".is-video a:not([href*='youtu']) .icon-bg-dot, .is-gif .icon-bg-dot { color: #bd3d37; }", 0); // change play icon color on unsupported videos
|
||||||
styleCustom.innerHTML = $TD.customBrowserCSS;
|
|
||||||
document.head.appendChild(styleCustom);
|
TD.services.TwitterActionRetweetedRetweet.prototype.iconClass = "icon-retweet icon-retweet-color txt-base-medium"; // fix retweet icon mismatch
|
||||||
|
|
||||||
|
window.TDGF_reinjectCustomCSS = function(styles){
|
||||||
|
$("#tweetduck-custom-css").remove();
|
||||||
|
|
||||||
|
if (styles && styles.length){
|
||||||
|
$(document.head).append("<style type='text/css' id='tweetduck-custom-css'>"+styles+"</style>");
|
||||||
}
|
}
|
||||||
|
};
|
||||||
})();
|
})();
|
||||||
})($, $TD, TD);
|
|
||||||
|
//
|
||||||
|
// Block: Setup unsupported video element hook.
|
||||||
|
//
|
||||||
|
(function(){
|
||||||
|
var cancelModal = false;
|
||||||
|
|
||||||
|
TD.components.MediaGallery.prototype._loadTweet = appendToFunction(TD.components.MediaGallery.prototype._loadTweet, function(){
|
||||||
|
var media = this.chirp.getMedia().find(media => media.mediaId === this.clickedMediaEntityId);
|
||||||
|
|
||||||
|
if (media && media.isVideo && media.service !== "youtube"){
|
||||||
|
$TD.openBrowser(this.clickedLink);
|
||||||
|
cancelModal = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
TD.components.BaseModal.prototype.setAndShowContainer = prependToFunction(TD.components.BaseModal.prototype.setAndShowContainer, function(){
|
||||||
|
if (cancelModal){
|
||||||
|
cancelModal = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
TD.ui.Column.prototype.playGifIfNotManuallyPaused = function(){};
|
||||||
|
TD.mustaches["status/media_thumb.mustache"] = TD.mustaches["status/media_thumb.mustache"].replace("is-gif", "is-gif is-paused");
|
||||||
|
|
||||||
|
app.delegate(".js-gif-play", "click", function(e){
|
||||||
|
var parent = $(e.target).closest(".js-tweet").first();
|
||||||
|
var link = (parent.hasClass("tweet-detail") ? parent.find("a[rel='url']") : parent.find("time").first().children("a")).first();
|
||||||
|
|
||||||
|
$TD.openBrowser(link.attr("href"));
|
||||||
|
e.stopPropagation();
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Finish initialization and load plugins.
|
||||||
|
//
|
||||||
|
onAppReady.push(function(){
|
||||||
|
$TD.loadFontSizeClass(TD.settings.getFontSize());
|
||||||
|
$TD.loadNotificationHeadContents(getNotificationHeadContents());
|
||||||
|
|
||||||
|
if (window.TD_PLUGINS){
|
||||||
|
window.TD_PLUGINS.onReady();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Observe the main app element and call the ready event whenever the contents are loaded.
|
||||||
|
//
|
||||||
|
new MutationObserver(function(){
|
||||||
|
if (window.TD_APP_READY && app.hasClass("is-hidden")){
|
||||||
|
window.TD_APP_READY = false;
|
||||||
|
}
|
||||||
|
else if (!window.TD_APP_READY && !app.hasClass("is-hidden")){
|
||||||
|
onAppReady.forEach(func => func());
|
||||||
|
window.TD_APP_READY = true;
|
||||||
|
}
|
||||||
|
}).observe(app[0], {
|
||||||
|
attributes: true,
|
||||||
|
attributeFilter: [ "class" ]
|
||||||
|
});
|
||||||
|
})($, $TD, $TDX, TD);
|
||||||
|
49
Resources/Scripts/debug.js
Normal file
49
Resources/Scripts/debug.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
(function($, $TD, $TDX, TD){
|
||||||
|
var isDebugging = false;
|
||||||
|
|
||||||
|
$(document).keydown(function(e){
|
||||||
|
|
||||||
|
// ==========================
|
||||||
|
// F4 key - toggle debug mode
|
||||||
|
// ==========================
|
||||||
|
|
||||||
|
if (e.keyCode === 115){
|
||||||
|
isDebugging = !isDebugging;
|
||||||
|
$(".app-title").first().css("background-color", isDebugging ? "#5A6B75" : "#292F33");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug mode handling
|
||||||
|
|
||||||
|
else if (isDebugging){
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
// ===================================
|
||||||
|
// N key - simulate popup notification
|
||||||
|
// ===================================
|
||||||
|
|
||||||
|
if (e.keyCode === 78){
|
||||||
|
var col = TD.controller.columnManager.getAllOrdered()[0];
|
||||||
|
|
||||||
|
$.publish("/notifications/new",[{
|
||||||
|
column: col,
|
||||||
|
items: [
|
||||||
|
col.updateArray[Math.floor(Math.random()*col.updateArray.length)]
|
||||||
|
]
|
||||||
|
}]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===================================
|
||||||
|
// S key - simulate sound notification
|
||||||
|
// ===================================
|
||||||
|
|
||||||
|
else if (e.keyCode === 83){
|
||||||
|
if ($TDX.hasCustomNotificationSound){
|
||||||
|
$TD.onTweetSound();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
document.getElementById("update-sound").play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})($, $TD, $TDX, TD);
|
@@ -1,4 +1,4 @@
|
|||||||
(function($TD){
|
(function($TD, $TDX){
|
||||||
//
|
//
|
||||||
// Variable: Collection of all <a> tags.
|
// Variable: Collection of all <a> tags.
|
||||||
//
|
//
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($TD.expandLinksOnHover){
|
if ($TDX.expandLinksOnHover){
|
||||||
tooltipTimer = window.setTimeout(function(){
|
tooltipTimer = window.setTimeout(function(){
|
||||||
var expanded = url;
|
var expanded = url;
|
||||||
expanded = cutStart(expanded, "https://");
|
expanded = cutStart(expanded, "https://");
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
addEventListener(links, "mouseleave", function(e){
|
addEventListener(links, "mouseleave", function(e){
|
||||||
if (!e.currentTarget.hasAttribute("data-full-url"))return;
|
if (!e.currentTarget.hasAttribute("data-full-url"))return;
|
||||||
|
|
||||||
if ($TD.expandLinksOnHover){
|
if ($TDX.expandLinksOnHover){
|
||||||
var prevText = e.currentTarget.getAttribute("td-prev-text");
|
var prevText = e.currentTarget.getAttribute("td-prev-text");
|
||||||
|
|
||||||
if (prevText){
|
if (prevText){
|
||||||
@@ -146,9 +146,4 @@
|
|||||||
document.body.addEventListener("mouseleave", function(){
|
document.body.addEventListener("mouseleave", function(){
|
||||||
document.body.classList.remove("td-hover");
|
document.body.classList.remove("td-hover");
|
||||||
});
|
});
|
||||||
|
})($TD, $TDX);
|
||||||
//
|
|
||||||
// Block: Page fully loaded.
|
|
||||||
//
|
|
||||||
$TD.onNotificationReady();
|
|
||||||
})($TD);
|
|
||||||
|
@@ -7,33 +7,38 @@
|
|||||||
//
|
//
|
||||||
// Constant: Update exe file name.
|
// Constant: Update exe file name.
|
||||||
//
|
//
|
||||||
const updateFileName = $TDU.brandName+".Update.exe";
|
const updateFileName = "TweetDuck.Update.exe";
|
||||||
|
|
||||||
//
|
//
|
||||||
// Constant: Url that returns JSON data about latest version.
|
// Constant: Url that returns JSON data about latest version.
|
||||||
//
|
//
|
||||||
const updateCheckUrlLatest = "https://api.github.com/repos/chylex/"+$TDU.brandName+"/releases/latest";
|
const updateCheckUrlLatest = "https://api.github.com/repos/chylex/TweetDuck/releases/latest";
|
||||||
|
|
||||||
//
|
//
|
||||||
// Constant: Url that returns JSON data about all versions, including prereleases.
|
// Constant: Url that returns JSON data about all versions, including prereleases.
|
||||||
//
|
//
|
||||||
const updateCheckUrlAll = "https://api.github.com/repos/chylex/"+$TDU.brandName+"/releases";
|
const updateCheckUrlAll = "https://api.github.com/repos/chylex/TweetDuck/releases";
|
||||||
|
|
||||||
|
//
|
||||||
|
// Constant: Fallback url in case the update installer file is missing.
|
||||||
|
//
|
||||||
|
const updateDownloadFallback = "https://tweetduck.chylex.com/#download";
|
||||||
|
|
||||||
//
|
//
|
||||||
// Function: Creates the update notification element. Removes the old one if already exists.
|
// Function: Creates the update notification element. Removes the old one if already exists.
|
||||||
//
|
//
|
||||||
var createUpdateNotificationElement = function(version, download){
|
var displayNotification = function(version, download){
|
||||||
var outdated = version === "unsupported";
|
var outdated = version === "unsupported";
|
||||||
|
|
||||||
var ele = $("#tweetdck-update");
|
var ele = $("#tweetduck-update");
|
||||||
var existed = ele.length > 0;
|
var existed = ele.length > 0;
|
||||||
|
|
||||||
if (existed > 0){
|
if (existed){
|
||||||
ele.remove();
|
ele.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
var html = outdated ? [
|
var html = outdated ? [
|
||||||
"<div id='tweetdck-update'>",
|
"<div id='tweetduck-update'>",
|
||||||
"<p class='tdu-title'>Unsupported System</p>",
|
"<p class='tdu-title'>Unsupported System</p>",
|
||||||
"<p class='tdu-info'>You will not receive updates.</p>",
|
"<p class='tdu-info'>You will not receive updates.</p>",
|
||||||
"<div class='tdu-buttons'>",
|
"<div class='tdu-buttons'>",
|
||||||
@@ -42,8 +47,8 @@
|
|||||||
"</div>",
|
"</div>",
|
||||||
"</div>"
|
"</div>"
|
||||||
] : [
|
] : [
|
||||||
"<div id='tweetdck-update'>",
|
"<div id='tweetduck-update'>",
|
||||||
"<p class='tdu-title'>"+$TDU.brandName+" Update</p>",
|
"<p class='tdu-title'>TweetDuck Update</p>",
|
||||||
"<p class='tdu-info'>Version "+version+" is now available.</p>",
|
"<p class='tdu-info'>Version "+version+" is now available.</p>",
|
||||||
"<div class='tdu-buttons'>",
|
"<div class='tdu-buttons'>",
|
||||||
"<button class='btn btn-positive tdu-btn-download'><span class='label'>Download</span></button>",
|
"<button class='btn btn-positive tdu-btn-download'><span class='label'>Download</span></button>",
|
||||||
@@ -54,7 +59,7 @@
|
|||||||
|
|
||||||
$(document.body).append(html.join(""));
|
$(document.body).append(html.join(""));
|
||||||
|
|
||||||
ele = $("#tweetdck-update");
|
ele = $("#tweetduck-update");
|
||||||
|
|
||||||
var buttonDiv = ele.children("div.tdu-buttons").first();
|
var buttonDiv = ele.children("div.tdu-buttons").first();
|
||||||
|
|
||||||
@@ -106,7 +111,13 @@
|
|||||||
|
|
||||||
buttonDiv.children(".tdu-btn-download").click(function(){
|
buttonDiv.children(".tdu-btn-download").click(function(){
|
||||||
ele.remove();
|
ele.remove();
|
||||||
|
|
||||||
|
if (download){
|
||||||
$TDU.onUpdateAccepted(version, download);
|
$TDU.onUpdateAccepted(version, download);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
$TDU.openBrowser(updateDownloadFallback);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
buttonDiv.children(".tdu-btn-unsupported").click(function(){
|
buttonDiv.children(".tdu-btn-unsupported").click(function(){
|
||||||
@@ -125,36 +136,31 @@
|
|||||||
return ele;
|
return ele;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Function: Returns milliseconds until the start of the next hour, with an extra offset in seconds that can skip an hour if the clock would roll over too soon.
|
||||||
|
//
|
||||||
|
var getTimeUntilNextHour = function(extra){
|
||||||
|
var now = new Date();
|
||||||
|
var offset = new Date(+now+extra*1000);
|
||||||
|
return new Date(offset.getFullYear(), offset.getMonth(), offset.getDate(), offset.getHours()+1, 0, 0)-now;
|
||||||
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Function: Runs an update check and updates all DOM elements appropriately.
|
// Function: Runs an update check and updates all DOM elements appropriately.
|
||||||
//
|
//
|
||||||
var runUpdateCheck = function(force, eventID){
|
var runUpdateCheck = function(eventID, versionTag, dismissedVersionTag, allowPre){
|
||||||
if (!$TDU.isSystemSupported){
|
|
||||||
if ($TDU.dismissedVersionTag !== "unsupported"){
|
|
||||||
createUpdateNotificationElement("unsupported");
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
clearTimeout(updateCheckTimeoutID);
|
clearTimeout(updateCheckTimeoutID);
|
||||||
updateCheckTimeoutID = setTimeout(runUpdateCheck, 1000*60*60); // 1 hour
|
updateCheckTimeoutID = setTimeout($TDU.triggerUpdateCheck, getTimeUntilNextHour(60*30)); // 30 minute offset
|
||||||
|
|
||||||
if (!$TDU.updateCheckEnabled && !force){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var allowPre = $TDU.allowPreReleases;
|
|
||||||
|
|
||||||
$.getJSON(allowPre ? updateCheckUrlAll : updateCheckUrlLatest, function(response){
|
$.getJSON(allowPre ? updateCheckUrlAll : updateCheckUrlLatest, function(response){
|
||||||
var release = allowPre ? response[0] : response;
|
var release = allowPre ? response[0] : response;
|
||||||
|
|
||||||
var tagName = release.tag_name;
|
var tagName = release.tag_name;
|
||||||
var hasUpdate = tagName !== $TDU.versionTag && tagName !== $TDU.dismissedVersionTag && release.assets.length > 0;
|
var hasUpdate = tagName !== versionTag && tagName !== dismissedVersionTag && release.assets.length > 0;
|
||||||
|
|
||||||
if (hasUpdate){
|
if (hasUpdate){
|
||||||
var obj = release.assets.find(asset => asset.name === updateFileName) || release.assets[0];
|
var obj = release.assets.find(asset => asset.name === updateFileName) || { browser_download_url: "" };
|
||||||
createUpdateNotificationElement(tagName, obj.browser_download_url);
|
displayNotification(tagName, obj.browser_download_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (eventID){ // ignore undefined and 0
|
if (eventID){ // ignore undefined and 0
|
||||||
@@ -166,6 +172,6 @@
|
|||||||
//
|
//
|
||||||
// Block: Setup global functions.
|
// Block: Setup global functions.
|
||||||
//
|
//
|
||||||
|
window.TDUF_displayNotification = displayNotification;
|
||||||
window.TDUF_runUpdateCheck = runUpdateCheck;
|
window.TDUF_runUpdateCheck = runUpdateCheck;
|
||||||
runUpdateCheck();
|
|
||||||
})($, $TDU);
|
})($, $TDU);
|
||||||
|
@@ -1,9 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<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.WinForms.55.0.0\build\CefSharp.WinForms.props" Condition="Exists('packages\CefSharp.WinForms.55.0.0\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.Common.55.0.0\build\CefSharp.Common.props" Condition="Exists('packages\CefSharp.Common.55.0.0\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')" />
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
@@ -12,11 +10,10 @@
|
|||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
<RootNamespace>TweetDck</RootNamespace>
|
<RootNamespace>TweetDck</RootNamespace>
|
||||||
<AssemblyName Condition=" '$(Configuration)' == 'Debug' ">TweetDick</AssemblyName>
|
<AssemblyName>TweetDuck</AssemblyName>
|
||||||
<AssemblyName Condition=" '$(Configuration)' == 'Release' ">TweetDuck</AssemblyName>
|
|
||||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<NuGetPackageImportStamp>e83161d1</NuGetPackageImportStamp>
|
<NuGetPackageImportStamp>886d3074</NuGetPackageImportStamp>
|
||||||
<TargetFrameworkProfile>
|
<TargetFrameworkProfile>
|
||||||
</TargetFrameworkProfile>
|
</TargetFrameworkProfile>
|
||||||
<PublishUrl>publish\</PublishUrl>
|
<PublishUrl>publish\</PublishUrl>
|
||||||
@@ -61,9 +58,6 @@
|
|||||||
</DefineConstants>
|
</DefineConstants>
|
||||||
<Prefer32Bit>false</Prefer32Bit>
|
<Prefer32Bit>false</Prefer32Bit>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
|
||||||
<AssemblyName>TweetDuck</AssemblyName>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
@@ -73,6 +67,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Configuration\LockManager.cs" />
|
<Compile Include="Configuration\LockManager.cs" />
|
||||||
<Compile Include="Configuration\UserConfig.cs" />
|
<Compile Include="Configuration\UserConfig.cs" />
|
||||||
|
<Compile Include="Core\Bridge\PropertyBridge.cs" />
|
||||||
<Compile Include="Core\Controls\ControlExtensions.cs" />
|
<Compile Include="Core\Controls\ControlExtensions.cs" />
|
||||||
<Compile Include="Core\Controls\FlatButton.cs">
|
<Compile Include="Core\Controls\FlatButton.cs">
|
||||||
<SubType>Component</SubType>
|
<SubType>Component</SubType>
|
||||||
@@ -104,16 +99,27 @@
|
|||||||
<Compile Include="Core\FormBrowser.Designer.cs">
|
<Compile Include="Core\FormBrowser.Designer.cs">
|
||||||
<DependentUpon>FormBrowser.cs</DependentUpon>
|
<DependentUpon>FormBrowser.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Core\FormNotification.cs">
|
<Compile Include="Core\Notification\FormNotificationMain.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Core\FormNotification.Designer.cs">
|
<Compile Include="Core\Notification\FormNotificationMain.Designer.cs">
|
||||||
<DependentUpon>FormNotification.cs</DependentUpon>
|
<DependentUpon>FormNotificationMain.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Core\Notification\FormNotificationBase.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Core\Notification\FormNotificationBase.Designer.cs">
|
||||||
|
<DependentUpon>FormNotificationBase.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Core\Handling\ContextMenuNotification.cs" />
|
<Compile Include="Core\Handling\ContextMenuNotification.cs" />
|
||||||
<Compile Include="Core\Handling\DialogHandlerBrowser.cs" />
|
<Compile Include="Core\Handling\FileDialogHandler.cs" />
|
||||||
|
<Compile Include="Core\Handling\JavaScriptDialogHandler.cs" />
|
||||||
<Compile Include="Core\Handling\LifeSpanHandler.cs" />
|
<Compile Include="Core\Handling\LifeSpanHandler.cs" />
|
||||||
<Compile Include="Core\Handling\TweetNotification.cs" />
|
<Compile Include="Core\Notification\FormNotificationTweet.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Core\Notification\SoundNotification.cs" />
|
||||||
|
<Compile Include="Core\Notification\TweetNotification.cs" />
|
||||||
<Compile Include="Core\Other\FormAbout.cs">
|
<Compile Include="Core\Other\FormAbout.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -144,7 +150,14 @@
|
|||||||
<Compile Include="Core\Other\Settings\Dialogs\DialogSettingsCefArgs.Designer.cs">
|
<Compile Include="Core\Other\Settings\Dialogs\DialogSettingsCefArgs.Designer.cs">
|
||||||
<DependentUpon>DialogSettingsCefArgs.cs</DependentUpon>
|
<DependentUpon>DialogSettingsCefArgs.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Core\Other\Settings\Dialogs\DialogSettingsExport.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Core\Other\Settings\Dialogs\DialogSettingsExport.Designer.cs">
|
||||||
|
<DependentUpon>DialogSettingsExport.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Include="Core\Other\Settings\Export\CombinedFileStream.cs" />
|
<Compile Include="Core\Other\Settings\Export\CombinedFileStream.cs" />
|
||||||
|
<Compile Include="Core\Other\Settings\Export\ExportFileFlags.cs" />
|
||||||
<Compile Include="Core\Other\Settings\Export\ExportManager.cs" />
|
<Compile Include="Core\Other\Settings\Export\ExportManager.cs" />
|
||||||
<Compile Include="Core\Other\Settings\TabSettingsAdvanced.cs">
|
<Compile Include="Core\Other\Settings\TabSettingsAdvanced.cs">
|
||||||
<SubType>UserControl</SubType>
|
<SubType>UserControl</SubType>
|
||||||
@@ -176,11 +189,18 @@
|
|||||||
<Compile Include="Core\Other\Settings\TabSettingsUpdates.Designer.cs">
|
<Compile Include="Core\Other\Settings\TabSettingsUpdates.Designer.cs">
|
||||||
<DependentUpon>TabSettingsUpdates.cs</DependentUpon>
|
<DependentUpon>TabSettingsUpdates.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Core\Bridge\CallbackBridge.cs" />
|
||||||
<Compile Include="Core\Utils\CommandLineArgs.cs" />
|
<Compile Include="Core\Utils\CommandLineArgs.cs" />
|
||||||
<Compile Include="Core\Utils\CommandLineArgsParser.cs" />
|
<Compile Include="Core\Utils\CommandLineArgsParser.cs" />
|
||||||
|
<Compile Include="Core\Notification\Screenshot\FormNotificationScreenshotable.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Core\Notification\NotificationFlags.cs" />
|
||||||
|
<Compile Include="Core\Notification\Screenshot\TweetScreenshotManager.cs" />
|
||||||
|
<Compile Include="Core\Utils\TwoKeyDictionary.cs" />
|
||||||
<Compile Include="Core\Utils\WindowState.cs" />
|
<Compile Include="Core\Utils\WindowState.cs" />
|
||||||
<Compile Include="Core\Utils\WindowsUtils.cs" />
|
<Compile Include="Core\Utils\WindowsUtils.cs" />
|
||||||
<Compile Include="Core\Handling\TweetDeckBridge.cs" />
|
<Compile Include="Core\Bridge\TweetDeckBridge.cs" />
|
||||||
<Compile Include="Core\Other\FormSettings.cs">
|
<Compile Include="Core\Other\FormSettings.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -210,6 +230,7 @@
|
|||||||
<Compile Include="Plugins\PluginManager.cs" />
|
<Compile Include="Plugins\PluginManager.cs" />
|
||||||
<Compile Include="Plugins\PluginScriptGenerator.cs" />
|
<Compile Include="Plugins\PluginScriptGenerator.cs" />
|
||||||
<Compile Include="Reporter.cs" />
|
<Compile Include="Reporter.cs" />
|
||||||
|
<Compile Include="Updates\Events\UpdateDismissedEventArgs.cs" />
|
||||||
<Compile Include="Updates\FormUpdateDownload.cs">
|
<Compile Include="Updates\FormUpdateDownload.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -226,8 +247,8 @@
|
|||||||
<Compile Include="Core\Utils\BrowserUtils.cs" />
|
<Compile Include="Core\Utils\BrowserUtils.cs" />
|
||||||
<Compile Include="Core\Utils\HardwareAcceleration.cs" />
|
<Compile Include="Core\Utils\HardwareAcceleration.cs" />
|
||||||
<Compile Include="Core\Utils\NativeMethods.cs" />
|
<Compile Include="Core\Utils\NativeMethods.cs" />
|
||||||
<Compile Include="Updates\UpdateAcceptedEventArgs.cs" />
|
<Compile Include="Updates\Events\UpdateAcceptedEventArgs.cs" />
|
||||||
<Compile Include="Updates\UpdateCheckEventArgs.cs" />
|
<Compile Include="Updates\Events\UpdateCheckEventArgs.cs" />
|
||||||
<Compile Include="Updates\UpdateHandler.cs" />
|
<Compile Include="Updates\UpdateHandler.cs" />
|
||||||
<Compile Include="Updates\UpdateInfo.cs" />
|
<Compile Include="Updates\UpdateInfo.cs" />
|
||||||
<Compile Include="Program.cs" />
|
<Compile Include="Program.cs" />
|
||||||
@@ -240,7 +261,6 @@
|
|||||||
<Compile Include="Resources\ScriptLoader.cs" />
|
<Compile Include="Resources\ScriptLoader.cs" />
|
||||||
<Compile Include="Updates\UpdaterSettings.cs" />
|
<Compile Include="Updates\UpdaterSettings.cs" />
|
||||||
<None Include="Configuration\app.config" />
|
<None Include="Configuration\app.config" />
|
||||||
<None Include="Configuration\packages.config" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<BootstrapperPackage Include=".NETFramework,Version=v4.0,Profile=Client">
|
<BootstrapperPackage Include=".NETFramework,Version=v4.0,Profile=Client">
|
||||||
@@ -292,25 +312,34 @@
|
|||||||
</ContentWithTargetPath>
|
</ContentWithTargetPath>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<None Include="Configuration\packages.config" />
|
||||||
<None Include="Resources\icon-small.ico" />
|
<None Include="Resources\icon-small.ico" />
|
||||||
<None Include="Resources\icon-tray-new.ico" />
|
<None Include="Resources\icon-tray-new.ico" />
|
||||||
<None Include="Resources\icon-tray.ico" />
|
<None Include="Resources\icon-tray.ico" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="Resources\Plugins\" />
|
<Folder Include="Resources\Plugins\" />
|
||||||
<Folder Include="Resources\Scripts\" />
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="Resources\Scripts\code.js" />
|
||||||
|
<Content Include="Resources\Scripts\debug.js" />
|
||||||
|
<Content Include="Resources\Scripts\notification.js" />
|
||||||
|
<Content Include="Resources\Scripts\plugins.browser.js" />
|
||||||
|
<Content Include="Resources\Scripts\plugins.js" />
|
||||||
|
<Content Include="Resources\Scripts\plugins.notification.js" />
|
||||||
|
<Content Include="Resources\Scripts\update.js" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
<PropertyGroup>
|
<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>
|
<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>
|
</PropertyGroup>
|
||||||
<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.x86.3.2883.1552\build\cef.redist.x86.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x86.3.2883.1552\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\cef.redist.x64.3.2883.1552\build\cef.redist.x64.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x64.3.2883.1552\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.55.0.0\build\CefSharp.Common.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.55.0.0\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.Common.55.0.0\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.55.0.0\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.55.0.0\build\CefSharp.WinForms.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.55.0.0\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'))" />
|
<Error Condition="!Exists('packages\CefSharp.WinForms.55.0.0\build\CefSharp.WinForms.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.55.0.0\build\CefSharp.WinForms.targets'))" />
|
||||||
</Target>
|
</Target>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PostBuildEvent>del "$(TargetPath).config"
|
<PostBuildEvent>del "$(TargetPath).config"
|
||||||
@@ -329,12 +358,20 @@ mkdir "$(TargetDir)plugins\official"
|
|||||||
mkdir "$(TargetDir)plugins\user"
|
mkdir "$(TargetDir)plugins\user"
|
||||||
xcopy "$(ProjectDir)Resources\Plugins\*" "$(TargetDir)plugins\official\" /E /Y
|
xcopy "$(ProjectDir)Resources\Plugins\*" "$(TargetDir)plugins\official\" /E /Y
|
||||||
rmdir "$(ProjectDir)\bin\Debug"
|
rmdir "$(ProjectDir)\bin\Debug"
|
||||||
rmdir "$(ProjectDir)\bin\Release"</PostBuildEvent>
|
rmdir "$(ProjectDir)\bin\Release"
|
||||||
|
|
||||||
|
rmdir "$(TargetDir)plugins\official\.debug" /S /Q
|
||||||
|
|
||||||
|
if $(ConfigurationName) == Debug (
|
||||||
|
rmdir "$(TargetDir)plugins\official\.debug" /S /Q
|
||||||
|
mkdir "$(TargetDir)plugins\user\.debug"
|
||||||
|
xcopy "$(ProjectDir)Resources\Plugins\.debug\*" "$(TargetDir)plugins\user\.debug\" /E /Y
|
||||||
|
)</PostBuildEvent>
|
||||||
</PropertyGroup>
|
</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.x86.3.2883.1552\build\cef.redist.x86.targets" Condition="Exists('packages\cef.redist.x86.3.2883.1552\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\cef.redist.x64.3.2883.1552\build\cef.redist.x64.targets" Condition="Exists('packages\cef.redist.x64.3.2883.1552\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.Common.55.0.0\build\CefSharp.Common.targets" Condition="Exists('packages\CefSharp.Common.55.0.0\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')" />
|
<Import Project="packages\CefSharp.WinForms.55.0.0\build\CefSharp.WinForms.targets" Condition="Exists('packages\CefSharp.WinForms.55.0.0\build\CefSharp.WinForms.targets')" />
|
||||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
<!-- 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.
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
<Target Name="BeforeBuild">
|
<Target Name="BeforeBuild">
|
||||||
|
12
TweetDck.sln
12
TweetDck.sln
@@ -1,21 +1,31 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio 2013
|
# Visual Studio 2013
|
||||||
VisualStudioVersion = 12.0.40629.0
|
VisualStudioVersion = 12.0.40629.0
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetDck", "TweetDck.csproj", "{2389A7CD-E0D3-4706-8294-092929A33A2D}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetDck", "TweetDck.csproj", "{2389A7CD-E0D3-4706-8294-092929A33A2D}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "tests\UnitTests.csproj", "{A958FA7A-4A2C-42A7-BFA0-159343483F4E}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
Debug|x86 = Debug|x86
|
Debug|x86 = Debug|x86
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
Release|x86 = Release|x86
|
Release|x86 = Release|x86
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{2389A7CD-E0D3-4706-8294-092929A33A2D}.Debug|Any CPU.ActiveCfg = Debug|x86
|
||||||
{2389A7CD-E0D3-4706-8294-092929A33A2D}.Debug|x86.ActiveCfg = Debug|x86
|
{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.Build.0 = Debug|x86
|
||||||
{2389A7CD-E0D3-4706-8294-092929A33A2D}.Debug|x86.Deploy.0 = Debug|x86
|
{2389A7CD-E0D3-4706-8294-092929A33A2D}.Debug|x86.Deploy.0 = Debug|x86
|
||||||
|
{2389A7CD-E0D3-4706-8294-092929A33A2D}.Release|Any CPU.ActiveCfg = Release|x86
|
||||||
{2389A7CD-E0D3-4706-8294-092929A33A2D}.Release|x86.ActiveCfg = Release|x86
|
{2389A7CD-E0D3-4706-8294-092929A33A2D}.Release|x86.ActiveCfg = Release|x86
|
||||||
{2389A7CD-E0D3-4706-8294-092929A33A2D}.Release|x86.Build.0 = Release|x86
|
{2389A7CD-E0D3-4706-8294-092929A33A2D}.Release|x86.Build.0 = Release|x86
|
||||||
|
{A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Debug|Any CPU.ActiveCfg = Debug|x86
|
||||||
|
{A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Debug|x86.ActiveCfg = Debug|x86
|
||||||
|
{A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Debug|x86.Build.0 = Debug|x86
|
||||||
|
{A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Release|Any CPU.ActiveCfg = Release|x86
|
||||||
|
{A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Release|x86.ActiveCfg = Release|x86
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
2
TweetDck.sln.DotSettings
Normal file
2
TweetDck.sln.DotSettings
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
|
<s:String x:Key="/Default/FilterSettingsManager/CoverageFilterXml/@EntryValue"><data><IncludeFilters /><ExcludeFilters><Filter ModuleMask="UnitTests" ModuleVersionMask="*" ClassMask="*" FunctionMask="*" IsEnabled="True" /></ExcludeFilters></data></s:String></wpf:ResourceDictionary>
|
@@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace TweetDck.Updates{
|
namespace TweetDck.Updates.Events{
|
||||||
class UpdateAcceptedEventArgs : EventArgs{
|
class UpdateAcceptedEventArgs : EventArgs{
|
||||||
public readonly UpdateInfo UpdateInfo;
|
public readonly UpdateInfo UpdateInfo;
|
||||||
|
|
@@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace TweetDck.Updates{
|
namespace TweetDck.Updates.Events{
|
||||||
class UpdateCheckEventArgs : EventArgs{
|
class UpdateCheckEventArgs : EventArgs{
|
||||||
public int EventId { get; private set; }
|
public int EventId { get; private set; }
|
||||||
public bool UpdateAvailable { get; private set; }
|
public bool UpdateAvailable { get; private set; }
|
11
Updates/Events/UpdateDismissedEventArgs.cs
Normal file
11
Updates/Events/UpdateDismissedEventArgs.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace TweetDck.Updates.Events{
|
||||||
|
class UpdateDismissedEventArgs : EventArgs{
|
||||||
|
public readonly string VersionTag;
|
||||||
|
|
||||||
|
public UpdateDismissedEventArgs(string versionTag){
|
||||||
|
this.VersionTag = versionTag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Globalization;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
@@ -9,7 +8,7 @@ using TweetDck.Core.Utils;
|
|||||||
|
|
||||||
namespace TweetDck.Updates{
|
namespace TweetDck.Updates{
|
||||||
sealed partial class FormUpdateDownload : Form{
|
sealed partial class FormUpdateDownload : Form{
|
||||||
private const double BytesToMB = 1024.0*1024.0;
|
private const double BytesToKB = 1024.0;
|
||||||
|
|
||||||
public string InstallerPath{
|
public string InstallerPath{
|
||||||
get{
|
get{
|
||||||
@@ -35,13 +34,13 @@ namespace TweetDck.Updates{
|
|||||||
this.updateInfo = info;
|
this.updateInfo = info;
|
||||||
this.UpdateStatus = Status.Waiting;
|
this.UpdateStatus = Status.Waiting;
|
||||||
|
|
||||||
Disposed += (sender, args) => webClient.Dispose();
|
|
||||||
|
|
||||||
webClient.DownloadProgressChanged += webClient_DownloadProgressChanged;
|
webClient.DownloadProgressChanged += webClient_DownloadProgressChanged;
|
||||||
webClient.DownloadFileCompleted += webClient_DownloadFileCompleted;
|
webClient.DownloadFileCompleted += webClient_DownloadFileCompleted;
|
||||||
|
|
||||||
Text = "Updating "+Program.BrandName;
|
Text = "Updating "+Program.BrandName;
|
||||||
labelDescription.Text = "Downloading version "+info.VersionTag+"...";
|
labelDescription.Text = "Downloading version "+info.VersionTag+"...";
|
||||||
|
|
||||||
|
Disposed += (sender, args) => this.webClient.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FormUpdateDownload_Shown(object sender, EventArgs e){
|
private void FormUpdateDownload_Shown(object sender, EventArgs e){
|
||||||
@@ -69,7 +68,7 @@ namespace TweetDck.Updates{
|
|||||||
progressDownload.SetValueInstant(1000);
|
progressDownload.SetValueInstant(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
labelStatus.Text = (e.BytesReceived/BytesToMB).ToString("0.0", CultureInfo.CurrentCulture)+" MB";
|
labelStatus.Text = (long)(e.BytesReceived/BytesToKB)+" kB";
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
if (progressDownload.Style != ProgressBarStyle.Continuous){
|
if (progressDownload.Style != ProgressBarStyle.Continuous){
|
||||||
@@ -77,7 +76,7 @@ namespace TweetDck.Updates{
|
|||||||
}
|
}
|
||||||
|
|
||||||
progressDownload.SetValueInstant(e.ProgressPercentage*10);
|
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 = (long)(e.BytesReceived/BytesToKB)+" / "+(long)(e.TotalBytesToReceive/BytesToKB)+" kB";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -93,7 +92,7 @@ namespace TweetDck.Updates{
|
|||||||
Program.Reporter.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);
|
BrowserUtils.OpenExternalBrowserUnsafe(Program.Website);
|
||||||
UpdateStatus = Status.Manual;
|
UpdateStatus = Status.Manual;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
|
@@ -5,14 +5,28 @@ using TweetDck.Core;
|
|||||||
using TweetDck.Core.Controls;
|
using TweetDck.Core.Controls;
|
||||||
using TweetDck.Core.Utils;
|
using TweetDck.Core.Utils;
|
||||||
using TweetDck.Resources;
|
using TweetDck.Resources;
|
||||||
|
using TweetDck.Updates.Events;
|
||||||
|
|
||||||
namespace TweetDck.Updates{
|
namespace TweetDck.Updates{
|
||||||
class UpdateHandler{
|
class UpdateHandler{
|
||||||
|
private static bool IsSystemSupported{
|
||||||
|
get{
|
||||||
|
return true; // Environment.OSVersion.Version >= new Version("6.1"); // 6.1 NT version = Windows 7
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public UpdaterSettings Settings{
|
||||||
|
get{
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private readonly ChromiumWebBrowser browser;
|
private readonly ChromiumWebBrowser browser;
|
||||||
private readonly FormBrowser form;
|
private readonly FormBrowser form;
|
||||||
private readonly UpdaterSettings settings;
|
private readonly UpdaterSettings settings;
|
||||||
|
|
||||||
public event EventHandler<UpdateAcceptedEventArgs> UpdateAccepted;
|
public event EventHandler<UpdateAcceptedEventArgs> UpdateAccepted;
|
||||||
|
public event EventHandler<UpdateDismissedEventArgs> UpdateDismissed;
|
||||||
public event EventHandler<UpdateCheckEventArgs> CheckFinished;
|
public event EventHandler<UpdateCheckEventArgs> CheckFinished;
|
||||||
|
|
||||||
private int lastEventId;
|
private int lastEventId;
|
||||||
@@ -23,75 +37,67 @@ namespace TweetDck.Updates{
|
|||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
|
|
||||||
browser.FrameLoadEnd += browser_FrameLoadEnd;
|
browser.FrameLoadEnd += browser_FrameLoadEnd;
|
||||||
browser.RegisterJsObject("$TDU", new Bridge(this));
|
browser.RegisterAsyncJsObject("$TDU", new Bridge(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
|
private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
|
||||||
if (e.Frame.IsMain && BrowserUtils.IsTweetDeckWebsite(e.Frame)){
|
if (e.Frame.IsMain && BrowserUtils.IsTweetDeckWebsite(e.Frame)){
|
||||||
ScriptLoader.ExecuteFile(e.Frame, "update.js");
|
ScriptLoader.ExecuteFile(e.Frame, "update.js");
|
||||||
|
Check(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Check(bool force){
|
public int Check(bool force){
|
||||||
browser.ExecuteScriptAsync("TDUF_runUpdateCheck", force, ++lastEventId);
|
if (IsSystemSupported){
|
||||||
|
if (Program.UserConfig.EnableUpdateCheck || force){
|
||||||
|
string dismissedUpdate = force || settings.DismissedUpdate == null ? string.Empty : settings.DismissedUpdate;
|
||||||
|
|
||||||
|
browser.ExecuteScriptAsync("TDUF_runUpdateCheck", ++lastEventId, Program.VersionTag, dismissedUpdate, settings.AllowPreReleases);
|
||||||
return lastEventId;
|
return lastEventId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if (settings.DismissedUpdate != "unsupported"){
|
||||||
|
browser.ExecuteScriptAsync("TDUF_displayNotification", "unsupported");
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
private void TriggerUpdateAcceptedEvent(UpdateAcceptedEventArgs args){
|
private void TriggerUpdateAcceptedEvent(UpdateAcceptedEventArgs args){
|
||||||
if (UpdateAccepted != null){
|
if (UpdateAccepted != null){
|
||||||
form.InvokeSafe(() => UpdateAccepted(this, args));
|
form.InvokeAsyncSafe(() => UpdateAccepted(this, args));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TriggerUpdateDismissedEvent(UpdateDismissedEventArgs args){
|
||||||
|
form.InvokeAsyncSafe(() => {
|
||||||
|
settings.DismissedUpdate = args.VersionTag;
|
||||||
|
|
||||||
|
if (UpdateDismissed != null){
|
||||||
|
UpdateDismissed(this, args);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void TriggerCheckFinishedEvent(UpdateCheckEventArgs args){
|
private void TriggerCheckFinishedEvent(UpdateCheckEventArgs args){
|
||||||
if (CheckFinished != null){
|
if (CheckFinished != null){
|
||||||
form.InvokeSafe(() => CheckFinished(this, args));
|
form.InvokeAsyncSafe(() => CheckFinished(this, args));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Bridge{
|
public class Bridge{
|
||||||
public string BrandName{
|
|
||||||
get{
|
|
||||||
return Program.BrandName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string VersionTag{
|
|
||||||
get{
|
|
||||||
return Program.VersionTag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool UpdateCheckEnabled{
|
|
||||||
get{
|
|
||||||
return Program.UserConfig.EnableUpdateCheck;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string DismissedVersionTag{
|
|
||||||
get{
|
|
||||||
return Program.UserConfig.DismissedUpdate ?? string.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool AllowPreReleases{
|
|
||||||
get{
|
|
||||||
return owner.settings.AllowPreReleases;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsSystemSupported{
|
|
||||||
get{
|
|
||||||
return Environment.OSVersion.Version >= new Version("6.1"); // 6.1 NT version = Windows 7
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly UpdateHandler owner;
|
private readonly UpdateHandler owner;
|
||||||
|
|
||||||
public Bridge(UpdateHandler owner){
|
public Bridge(UpdateHandler owner){
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void TriggerUpdateCheck(){
|
||||||
|
owner.Check(false);
|
||||||
|
}
|
||||||
|
|
||||||
public void OnUpdateCheckFinished(int eventId, bool isUpdateAvailable, string latestVersion){
|
public void OnUpdateCheckFinished(int eventId, bool isUpdateAvailable, string latestVersion){
|
||||||
owner.TriggerCheckFinishedEvent(new UpdateCheckEventArgs(eventId, isUpdateAvailable, latestVersion));
|
owner.TriggerCheckFinishedEvent(new UpdateCheckEventArgs(eventId, isUpdateAvailable, latestVersion));
|
||||||
}
|
}
|
||||||
@@ -101,10 +107,7 @@ namespace TweetDck.Updates{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void OnUpdateDismissed(string versionTag){
|
public void OnUpdateDismissed(string versionTag){
|
||||||
owner.form.InvokeSafe(() => {
|
owner.TriggerUpdateDismissedEvent(new UpdateDismissedEventArgs(versionTag));
|
||||||
Program.UserConfig.DismissedUpdate = versionTag;
|
|
||||||
Program.UserConfig.Save();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OpenBrowser(string url){
|
public void OpenBrowser(string url){
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
namespace TweetDck.Updates{
|
namespace TweetDck.Updates{
|
||||||
class UpdaterSettings{
|
class UpdaterSettings{
|
||||||
public bool AllowPreReleases { get; set; }
|
public bool AllowPreReleases { get; set; }
|
||||||
|
public string DismissedUpdate { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,9 @@
|
|||||||
del "bin\x86\Release\*.xml"
|
del "bin\x86\Release\*.xml"
|
||||||
|
del "bin\x86\Release\*.pdb"
|
||||||
del "bin\x86\Release\devtools_resources.pak"
|
del "bin\x86\Release\devtools_resources.pak"
|
||||||
del "bin\x86\Release\d3dcompiler_43.dll"
|
del "bin\x86\Release\d3dcompiler_43.dll"
|
||||||
|
del "bin\x86\Release\widevinecdmadapter.dll"
|
||||||
|
del "bin\x86\Release\Scripts\debug.js"
|
||||||
|
|
||||||
del "bin\x86\Release\TweetDuck.Browser.exe"
|
del "bin\x86\Release\TweetDuck.Browser.exe"
|
||||||
ren "bin\x86\Release\CefSharp.BrowserSubprocess.exe" "TweetDuck.Browser.exe"
|
ren "bin\x86\Release\CefSharp.BrowserSubprocess.exe" "TweetDuck.Browser.exe"
|
@@ -39,7 +39,7 @@ Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{
|
|||||||
|
|
||||||
[Files]
|
[Files]
|
||||||
Source: "..\bin\x86\Release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "..\bin\x86\Release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
Source: "..\bin\x86\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: "*.xml,devtools_resources.pak,d3dcompiler_43.dll"
|
Source: "..\bin\x86\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: "*.xml,*.pdb,devtools_resources.pak,d3dcompiler_43.dll,widevinecdmadapter.dll,debug.js"
|
||||||
|
|
||||||
[Icons]
|
[Icons]
|
||||||
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Check: TDIsUninstallable
|
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Check: TDIsUninstallable
|
||||||
|
@@ -10,14 +10,14 @@
|
|||||||
|
|
||||||
[Setup]
|
[Setup]
|
||||||
AppId={{8C25A716-7E11-4AAD-9992-8B5D0C78AE06}
|
AppId={{8C25A716-7E11-4AAD-9992-8B5D0C78AE06}
|
||||||
AppName={#MyAppName}
|
AppName={#MyAppName} Portable
|
||||||
AppVersion={#MyAppVersion}
|
AppVersion={#MyAppVersion}
|
||||||
AppVerName={#MyAppName} {#MyAppVersion}
|
AppVerName={#MyAppName} {#MyAppVersion}
|
||||||
AppPublisher={#MyAppPublisher}
|
AppPublisher={#MyAppPublisher}
|
||||||
AppPublisherURL={#MyAppURL}
|
AppPublisherURL={#MyAppURL}
|
||||||
AppSupportURL={#MyAppURL}
|
AppSupportURL={#MyAppURL}
|
||||||
AppUpdatesURL={#MyAppURL}
|
AppUpdatesURL={#MyAppURL}
|
||||||
DefaultDirName={pf}\{#MyAppName}
|
DefaultDirName={sd}\{#MyAppName}
|
||||||
DefaultGroupName={#MyAppName}
|
DefaultGroupName={#MyAppName}
|
||||||
OutputBaseFilename={#MyAppName}.Portable
|
OutputBaseFilename={#MyAppName}.Portable
|
||||||
VersionInfoVersion={#MyAppVersion}
|
VersionInfoVersion={#MyAppVersion}
|
||||||
@@ -36,17 +36,21 @@ Name: "english"; MessagesFile: "compiler:Default.isl"
|
|||||||
|
|
||||||
[Files]
|
[Files]
|
||||||
Source: "..\bin\x86\Release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "..\bin\x86\Release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
Source: "..\bin\x86\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: "*.xml,devtools_resources.pak,d3dcompiler_43.dll"
|
Source: "..\bin\x86\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: "*.xml,*.pdb,devtools_resources.pak,d3dcompiler_43.dll,widevinecdmadapter.dll,debug.js"
|
||||||
|
|
||||||
[Run]
|
[Run]
|
||||||
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall shellexec
|
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall shellexec skipifsilent
|
||||||
|
|
||||||
[Code]
|
[Code]
|
||||||
|
var UpdatePath: String;
|
||||||
|
|
||||||
function TDGetNetFrameworkVersion: Cardinal; forward;
|
function TDGetNetFrameworkVersion: Cardinal; forward;
|
||||||
|
|
||||||
{ Check .NET Framework version on startup, ask user if they want to proceed if older than 4.5.2. }
|
{ Check .NET Framework version on startup, ask user if they want to proceed if older than 4.5.2. }
|
||||||
function InitializeSetup: Boolean;
|
function InitializeSetup: Boolean;
|
||||||
begin
|
begin
|
||||||
|
UpdatePath := ExpandConstant('{param:UPDATEPATH}')
|
||||||
|
|
||||||
if TDGetNetFrameworkVersion() >= 379893 then
|
if TDGetNetFrameworkVersion() >= 379893 then
|
||||||
begin
|
begin
|
||||||
Result := True;
|
Result := True;
|
||||||
@@ -62,6 +66,21 @@ begin
|
|||||||
Result := True;
|
Result := True;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ Set the installation path if updating. }
|
||||||
|
procedure InitializeWizard();
|
||||||
|
begin
|
||||||
|
if (UpdatePath <> '') then
|
||||||
|
begin
|
||||||
|
WizardForm.DirEdit.Text := UpdatePath;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ Skip the install path selection page if running from an update installer. }
|
||||||
|
function ShouldSkipPage(PageID: Integer): Boolean;
|
||||||
|
begin
|
||||||
|
Result := (PageID = wpSelectDir) and (UpdatePath <> '')
|
||||||
|
end;
|
||||||
|
|
||||||
{ Return DWORD value containing the build version of .NET Framework. }
|
{ Return DWORD value containing the build version of .NET Framework. }
|
||||||
function TDGetNetFrameworkVersion: Cardinal;
|
function TDGetNetFrameworkVersion: Cardinal;
|
||||||
var FrameworkVersion: Cardinal;
|
var FrameworkVersion: Cardinal;
|
||||||
|
@@ -8,11 +8,11 @@
|
|||||||
|
|
||||||
#define MyAppID "8C25A716-7E11-4AAD-9992-8B5D0C78AE06"
|
#define MyAppID "8C25A716-7E11-4AAD-9992-8B5D0C78AE06"
|
||||||
#define MyAppVersion GetFileVersion("..\bin\x86\Release\TweetDuck.exe")
|
#define MyAppVersion GetFileVersion("..\bin\x86\Release\TweetDuck.exe")
|
||||||
#define CefVersion "3.2785.1478.0"
|
#define CefVersion "3.2883.1552.0"
|
||||||
|
|
||||||
[Setup]
|
[Setup]
|
||||||
AppId={{{#MyAppID}}
|
AppId={{{#MyAppID}}
|
||||||
AppName={#MyAppName}
|
AppName={#MyAppName} Update
|
||||||
AppVersion={#MyAppVersion}
|
AppVersion={#MyAppVersion}
|
||||||
AppVerName={#MyAppName} {#MyAppVersion}
|
AppVerName={#MyAppName} {#MyAppVersion}
|
||||||
AppPublisher={#MyAppPublisher}
|
AppPublisher={#MyAppPublisher}
|
||||||
@@ -41,7 +41,7 @@ Name: "english"; MessagesFile: "compiler:Default.isl"
|
|||||||
|
|
||||||
[Files]
|
[Files]
|
||||||
Source: "..\bin\x86\Release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "..\bin\x86\Release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
Source: "..\bin\x86\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: "*.xml,*.dll,*.pak,*.bin,*.dat"
|
Source: "..\bin\x86\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: "*.xml,*.pdb,*.dll,*.pak,*.bin,*.dat,debug.js"
|
||||||
|
|
||||||
[Icons]
|
[Icons]
|
||||||
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Check: TDIsUninstallable
|
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Check: TDIsUninstallable
|
||||||
@@ -53,6 +53,7 @@ Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChang
|
|||||||
Type: files; Name: "{app}\*.xml"
|
Type: files; Name: "{app}\*.xml"
|
||||||
Type: files; Name: "{app}\*.js"
|
Type: files; Name: "{app}\*.js"
|
||||||
Type: files; Name: "{app}\d3dcompiler_43.dll"
|
Type: files; Name: "{app}\d3dcompiler_43.dll"
|
||||||
|
Type: files; Name: "{app}\widevinecdmadapter.dll"
|
||||||
Type: files; Name: "{app}\devtools_resources.pak"
|
Type: files; Name: "{app}\devtools_resources.pak"
|
||||||
Type: files; Name: "{app}\CefSharp.BrowserSubprocess.exe"
|
Type: files; Name: "{app}\CefSharp.BrowserSubprocess.exe"
|
||||||
Type: files; Name: "{app}\td-log.txt"
|
Type: files; Name: "{app}\td-log.txt"
|
||||||
@@ -71,6 +72,7 @@ function TDIsUninstallable: Boolean; forward;
|
|||||||
function TDFindUpdatePath: String; forward;
|
function TDFindUpdatePath: String; forward;
|
||||||
function TDGetNetFrameworkVersion: Cardinal; forward;
|
function TDGetNetFrameworkVersion: Cardinal; forward;
|
||||||
function TDGetAppVersionClean: String; forward;
|
function TDGetAppVersionClean: String; forward;
|
||||||
|
function TDGetFullDownloadFileName: String; forward;
|
||||||
function TDIsMatchingCEFVersion: Boolean; forward;
|
function TDIsMatchingCEFVersion: Boolean; forward;
|
||||||
procedure TDExecuteFullDownload; forward;
|
procedure TDExecuteFullDownload; forward;
|
||||||
|
|
||||||
@@ -92,7 +94,7 @@ begin
|
|||||||
|
|
||||||
if not TDIsMatchingCEFVersion() then
|
if not TDIsMatchingCEFVersion() then
|
||||||
begin
|
begin
|
||||||
idpAddFile('https://github.com/{#MyAppPublisher}/{#MyAppName}/releases/download/'+TDGetAppVersionClean()+'/{#MyAppName}.exe', ExpandConstant('{tmp}\{#MyAppName}.Full.exe'));
|
idpAddFile('https://github.com/{#MyAppPublisher}/{#MyAppName}/releases/download/'+TDGetAppVersionClean()+'/'+TDGetFullDownloadFileName(), ExpandConstant('{tmp}\{#MyAppName}.Full.exe'));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
if TDGetNetFrameworkVersion() >= 379893 then
|
if TDGetNetFrameworkVersion() >= 379893 then
|
||||||
@@ -165,15 +167,21 @@ end;
|
|||||||
{ Returns a validated installation path (including trailing backslash) using the /UPDATEPATH parameter or installation info in registry. Returns empty string on failure. }
|
{ Returns a validated installation path (including trailing backslash) using the /UPDATEPATH parameter or installation info in registry. Returns empty string on failure. }
|
||||||
function TDFindUpdatePath: String;
|
function TDFindUpdatePath: String;
|
||||||
var Path: String;
|
var Path: String;
|
||||||
|
var RegistryKey: String;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
Path := ExpandConstant('{param:UPDATEPATH}')
|
Path := ExpandConstant('{param:UPDATEPATH}')
|
||||||
|
|
||||||
if (Path = '') and not IsPortable and not RegQueryStringValue(HKEY_LOCAL_MACHINE, 'Software\Microsoft\Windows\CurrentVersion\Uninstall\{{#MyAppID}}_is1', 'InstallLocation', Path) then
|
if (Path = '') and not IsPortable then
|
||||||
|
begin
|
||||||
|
RegistryKey := 'Software\Microsoft\Windows\CurrentVersion\Uninstall\{{#MyAppID}}_is1'
|
||||||
|
|
||||||
|
if not (RegQueryStringValue(HKEY_CURRENT_USER, RegistryKey, 'InstallLocation', Path) or RegQueryStringValue(HKEY_LOCAL_MACHINE, RegistryKey, 'InstallLocation', Path)) then
|
||||||
begin
|
begin
|
||||||
Result := ''
|
Result := ''
|
||||||
Exit
|
Exit
|
||||||
end;
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
if not FileExists(Path+'{#MyAppExeName}') then
|
if not FileExists(Path+'{#MyAppExeName}') then
|
||||||
begin
|
begin
|
||||||
@@ -198,6 +206,12 @@ begin
|
|||||||
Result := 0;
|
Result := 0;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ Return the name of the full installer file to download from GitHub. }
|
||||||
|
function TDGetFullDownloadFileName: String;
|
||||||
|
begin
|
||||||
|
if IsPortable then Result := '{#MyAppName}.Portable.exe' else Result := '{#MyAppName}.exe';
|
||||||
|
end;
|
||||||
|
|
||||||
{ Return whether the version of the installed libcef.dll library matches internal one. }
|
{ Return whether the version of the installed libcef.dll library matches internal one. }
|
||||||
function TDIsMatchingCEFVersion: Boolean;
|
function TDIsMatchingCEFVersion: Boolean;
|
||||||
var CEFVersion: String;
|
var CEFVersion: String;
|
||||||
|
143
tests/Core/Settings/TestCombinedFileStream.cs
Normal file
143
tests/Core/Settings/TestCombinedFileStream.cs
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using TweetDck.Core.Other.Settings.Export;
|
||||||
|
|
||||||
|
namespace UnitTests.Core.Settings{
|
||||||
|
[TestClass]
|
||||||
|
public class TestCombinedFileStream{
|
||||||
|
[TestMethod]
|
||||||
|
public void TestNoFiles(){
|
||||||
|
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.WriteFile("cfs_empty"))){
|
||||||
|
cfs.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.IsTrue(File.Exists("cfs_empty"));
|
||||||
|
|
||||||
|
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.ReadFile("cfs_empty"))){
|
||||||
|
Assert.IsNull(cfs.ReadFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.ReadFile("cfs_empty"))){
|
||||||
|
Assert.IsNull(cfs.SkipFile());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestEmptyFiles(){
|
||||||
|
TestUtils.WriteText("cfs_input_empty_1", string.Empty);
|
||||||
|
TestUtils.WriteText("cfs_input_empty_2", string.Empty);
|
||||||
|
|
||||||
|
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.WriteFile("cfs_blank_files"))){
|
||||||
|
cfs.WriteFile("id1", "cfs_input_empty_1");
|
||||||
|
cfs.WriteFile("id2", "cfs_input_empty_2");
|
||||||
|
cfs.WriteFile("id2_clone", "cfs_input_empty_2");
|
||||||
|
cfs.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.IsTrue(File.Exists("cfs_blank_files"));
|
||||||
|
|
||||||
|
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.ReadFile("cfs_blank_files"))){
|
||||||
|
CombinedFileStream.Entry entry1 = cfs.ReadFile();
|
||||||
|
string entry2key = cfs.SkipFile();
|
||||||
|
CombinedFileStream.Entry entry3 = cfs.ReadFile();
|
||||||
|
|
||||||
|
Assert.IsNull(cfs.ReadFile());
|
||||||
|
Assert.IsNull(cfs.SkipFile());
|
||||||
|
|
||||||
|
Assert.AreEqual("id1", entry1.KeyName);
|
||||||
|
Assert.AreEqual("id1", entry1.Identifier);
|
||||||
|
CollectionAssert.AreEqual(new string[0], entry1.KeyValue);
|
||||||
|
|
||||||
|
Assert.AreEqual("id2", entry2key);
|
||||||
|
|
||||||
|
Assert.AreEqual("id2_clone", entry3.KeyName);
|
||||||
|
Assert.AreEqual("id2_clone", entry3.Identifier);
|
||||||
|
CollectionAssert.AreEqual(new string[0], entry3.KeyValue);
|
||||||
|
|
||||||
|
entry1.WriteToFile("cfs_blank_file_1");
|
||||||
|
entry3.WriteToFile("cfs_blank_file_2");
|
||||||
|
TestUtils.DeleteFileOnExit("cfs_blank_file_1");
|
||||||
|
TestUtils.DeleteFileOnExit("cfs_blank_file_2");
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.IsTrue(File.Exists("cfs_blank_file_1"));
|
||||||
|
Assert.IsTrue(File.Exists("cfs_blank_file_2"));
|
||||||
|
Assert.AreEqual(string.Empty, TestUtils.ReadText("cfs_blank_file_1"));
|
||||||
|
Assert.AreEqual(string.Empty, TestUtils.ReadText("cfs_blank_file_2"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestTextFilesAndComplexKeys(){
|
||||||
|
TestUtils.WriteText("cfs_input_text_1", "Hello World!"+Environment.NewLine);
|
||||||
|
|
||||||
|
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.WriteFile("cfs_text_files"))){
|
||||||
|
cfs.WriteFile(new string[]{ "key1", "a", "bb", "ccc", "dddd" }, "cfs_input_text_1");
|
||||||
|
cfs.WriteFile(new string[]{ "key2", "a", "bb", "ccc", "dddd" }, "cfs_input_text_1");
|
||||||
|
cfs.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.IsTrue(File.Exists("cfs_text_files"));
|
||||||
|
|
||||||
|
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.ReadFile("cfs_text_files"))){
|
||||||
|
CombinedFileStream.Entry entry = cfs.ReadFile();
|
||||||
|
|
||||||
|
Assert.AreEqual("key2", cfs.SkipFile());
|
||||||
|
Assert.IsNull(cfs.ReadFile());
|
||||||
|
Assert.IsNull(cfs.SkipFile());
|
||||||
|
|
||||||
|
Assert.AreEqual("key1|a|bb|ccc|dddd", entry.Identifier);
|
||||||
|
Assert.AreEqual("key1", entry.KeyName);
|
||||||
|
CollectionAssert.AreEqual(new string[]{ "a", "bb", "ccc", "dddd" }, entry.KeyValue);
|
||||||
|
|
||||||
|
entry.WriteToFile("cfs_text_file_1");
|
||||||
|
TestUtils.DeleteFileOnExit("cfs_text_file_1");
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.IsTrue(File.Exists("cfs_text_file_1"));
|
||||||
|
Assert.AreEqual("Hello World!"+Environment.NewLine, TestUtils.ReadText("cfs_text_file_1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestEntryWriteWithDirectory(){
|
||||||
|
if (Directory.Exists("cfs_directory")){
|
||||||
|
Directory.Delete("cfs_directory", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TestUtils.WriteText("cfs_input_dir_1", "test");
|
||||||
|
|
||||||
|
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.WriteFile("cfs_dir_test"))){
|
||||||
|
cfs.WriteFile("key1", "cfs_input_dir_1");
|
||||||
|
cfs.WriteFile("key2", "cfs_input_dir_1");
|
||||||
|
cfs.WriteFile("key3", "cfs_input_dir_1");
|
||||||
|
cfs.WriteFile("key4", "cfs_input_dir_1");
|
||||||
|
cfs.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.IsTrue(File.Exists("cfs_dir_test"));
|
||||||
|
|
||||||
|
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.ReadFile("cfs_dir_test"))){
|
||||||
|
try{
|
||||||
|
cfs.ReadFile().WriteToFile("cfs_directory/cfs_dir_test_file", false);
|
||||||
|
Assert.Fail("WriteToFile did not trigger an exception.");
|
||||||
|
}catch(DirectoryNotFoundException){}
|
||||||
|
|
||||||
|
cfs.ReadFile().WriteToFile("cfs_directory/cfs_dir_test_file", true);
|
||||||
|
cfs.ReadFile().WriteToFile("cfs_dir_test_file", true);
|
||||||
|
cfs.ReadFile().WriteToFile("cfs_dir_test_file.txt", true);
|
||||||
|
TestUtils.DeleteFileOnExit("cfs_dir_test_file");
|
||||||
|
TestUtils.DeleteFileOnExit("cfs_dir_test_file.txt");
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.IsTrue(Directory.Exists("cfs_directory"));
|
||||||
|
Assert.IsTrue(File.Exists("cfs_directory/cfs_dir_test_file"));
|
||||||
|
Assert.IsTrue(File.Exists("cfs_dir_test_file"));
|
||||||
|
Assert.IsTrue(File.Exists("cfs_dir_test_file.txt"));
|
||||||
|
Assert.AreEqual("test", TestUtils.ReadText("cfs_directory/cfs_dir_test_file"));
|
||||||
|
Assert.AreEqual("test", TestUtils.ReadText("cfs_dir_test_file"));
|
||||||
|
Assert.AreEqual("test", TestUtils.ReadText("cfs_dir_test_file.txt"));
|
||||||
|
|
||||||
|
Directory.Delete("cfs_directory", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
50
tests/Core/Utils/TestBrowserUtils.cs
Normal file
50
tests/Core/Utils/TestBrowserUtils.cs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using TweetDck.Core.Utils;
|
||||||
|
|
||||||
|
namespace UnitTests.Core.Utils{
|
||||||
|
[TestClass]
|
||||||
|
public class TestBrowserUtils{
|
||||||
|
[TestMethod]
|
||||||
|
public void TestIsValidUrl(){
|
||||||
|
Assert.IsTrue(BrowserUtils.IsValidUrl("http://google.com")); // base
|
||||||
|
Assert.IsTrue(BrowserUtils.IsValidUrl("http://www.google.com")); // www.
|
||||||
|
Assert.IsTrue(BrowserUtils.IsValidUrl("http://google.co.uk")); // co.uk
|
||||||
|
|
||||||
|
Assert.IsTrue(BrowserUtils.IsValidUrl("https://google.com")); // https
|
||||||
|
Assert.IsTrue(BrowserUtils.IsValidUrl("ftp://google.com")); // ftp
|
||||||
|
Assert.IsTrue(BrowserUtils.IsValidUrl("mailto:someone@google.com")); // mailto
|
||||||
|
|
||||||
|
Assert.IsTrue(BrowserUtils.IsValidUrl("http://google.com/")); // trailing slash
|
||||||
|
Assert.IsTrue(BrowserUtils.IsValidUrl("http://google.com/?")); // trailing question mark
|
||||||
|
Assert.IsTrue(BrowserUtils.IsValidUrl("http://google.com/?a=5&b=x")); // parameters
|
||||||
|
Assert.IsTrue(BrowserUtils.IsValidUrl("http://google.com/#hash")); // parameters + hash
|
||||||
|
Assert.IsTrue(BrowserUtils.IsValidUrl("http://google.com/?a=5&b=x#hash")); // parameters + hash
|
||||||
|
|
||||||
|
foreach(string tld in new string[]{ "accountants", "blackfriday", "cancerresearch", "coffee", "cool", "foo", "travelersinsurance" }){
|
||||||
|
Assert.IsTrue(BrowserUtils.IsValidUrl("http://test."+tld)); // long and unusual TLDs
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.IsFalse(BrowserUtils.IsValidUrl("explorer")); // file
|
||||||
|
Assert.IsFalse(BrowserUtils.IsValidUrl("explorer.exe")); // file
|
||||||
|
Assert.IsFalse(BrowserUtils.IsValidUrl("://explorer.exe")); // file-sorta
|
||||||
|
Assert.IsFalse(BrowserUtils.IsValidUrl("file://explorer.exe")); // file-proper
|
||||||
|
|
||||||
|
Assert.IsFalse(BrowserUtils.IsValidUrl("")); // empty
|
||||||
|
Assert.IsFalse(BrowserUtils.IsValidUrl("lol")); // random
|
||||||
|
|
||||||
|
Assert.IsFalse(BrowserUtils.IsValidUrl("gopher://nobody.cares")); // lmao rekt
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestGetFileNameFromUrl(){
|
||||||
|
Assert.AreEqual("index.html", BrowserUtils.GetFileNameFromUrl("http://test.com/index.html"));
|
||||||
|
Assert.AreEqual("index.html", BrowserUtils.GetFileNameFromUrl("http://test.com/index.html?"));
|
||||||
|
Assert.AreEqual("index.html", BrowserUtils.GetFileNameFromUrl("http://test.com/index.html?param1=abc¶m2=false"));
|
||||||
|
|
||||||
|
Assert.AreEqual("index", BrowserUtils.GetFileNameFromUrl("http://test.com/index"));
|
||||||
|
Assert.AreEqual("index.", BrowserUtils.GetFileNameFromUrl("http://test.com/index."));
|
||||||
|
|
||||||
|
Assert.IsNull(BrowserUtils.GetFileNameFromUrl("http://test.com/"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
157
tests/Core/Utils/TestCommandLineArgs.cs
Normal file
157
tests/Core/Utils/TestCommandLineArgs.cs
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using TweetDck.Core.Utils;
|
||||||
|
|
||||||
|
namespace UnitTests.Core.Utils{
|
||||||
|
[TestClass]
|
||||||
|
public class TestCommandLineArgs{
|
||||||
|
[TestMethod]
|
||||||
|
public void TestEmpty(){
|
||||||
|
CommandLineArgs args = new CommandLineArgs();
|
||||||
|
|
||||||
|
Assert.AreEqual(0, args.Count);
|
||||||
|
Assert.AreEqual(string.Empty, args.ToString());
|
||||||
|
|
||||||
|
Assert.IsFalse(args.HasFlag("x"));
|
||||||
|
Assert.IsFalse(args.HasValue("x"));
|
||||||
|
Assert.AreEqual("default", args.GetValue("x", "default"));
|
||||||
|
|
||||||
|
args.RemoveFlag("x");
|
||||||
|
args.RemoveValue("x");
|
||||||
|
|
||||||
|
var dict = new Dictionary<string, string>();
|
||||||
|
args.ToDictionary(dict);
|
||||||
|
Assert.AreEqual(0, dict.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestFlags(){
|
||||||
|
CommandLineArgs args = new CommandLineArgs();
|
||||||
|
|
||||||
|
args.AddFlag("my_test_flag_1");
|
||||||
|
args.AddFlag("my_test_flag_2");
|
||||||
|
args.AddFlag("aAaAa");
|
||||||
|
|
||||||
|
Assert.IsFalse(args.HasValue("aAaAa"));
|
||||||
|
|
||||||
|
Assert.AreEqual(3, args.Count);
|
||||||
|
Assert.IsTrue(args.HasFlag("my_test_flag_1"));
|
||||||
|
Assert.IsTrue(args.HasFlag("my_test_flag_2"));
|
||||||
|
Assert.IsTrue(args.HasFlag("aaaaa"));
|
||||||
|
Assert.IsTrue(args.HasFlag("AAAAA"));
|
||||||
|
Assert.AreEqual("my_test_flag_1 my_test_flag_2 aaaaa", args.ToString());
|
||||||
|
|
||||||
|
args.RemoveFlag("Aaaaa");
|
||||||
|
|
||||||
|
Assert.AreEqual(2, args.Count);
|
||||||
|
Assert.IsTrue(args.HasFlag("my_test_flag_1"));
|
||||||
|
Assert.IsTrue(args.HasFlag("my_test_flag_2"));
|
||||||
|
Assert.IsFalse(args.HasFlag("aaaaa"));
|
||||||
|
Assert.AreEqual("my_test_flag_1 my_test_flag_2", args.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestValues(){
|
||||||
|
CommandLineArgs args = new CommandLineArgs();
|
||||||
|
|
||||||
|
args.SetValue("test_value", "My Test Value");
|
||||||
|
args.SetValue("aAaAa", "aaaaa");
|
||||||
|
|
||||||
|
Assert.IsFalse(args.HasFlag("aAaAa"));
|
||||||
|
|
||||||
|
Assert.AreEqual(2, args.Count);
|
||||||
|
Assert.IsTrue(args.HasValue("test_value"));
|
||||||
|
Assert.IsTrue(args.HasValue("aaaaa"));
|
||||||
|
Assert.IsTrue(args.HasValue("AAAAA"));
|
||||||
|
Assert.AreEqual("My Test Value", args.GetValue("test_value", string.Empty));
|
||||||
|
Assert.AreEqual("aaaaa", args.GetValue("aaaaa", string.Empty));
|
||||||
|
Assert.AreEqual("test_value \"My Test Value\" aaaaa \"aaaaa\"", args.ToString());
|
||||||
|
|
||||||
|
args.RemoveValue("Aaaaa");
|
||||||
|
|
||||||
|
Assert.AreEqual(1, args.Count);
|
||||||
|
Assert.IsTrue(args.HasValue("test_value"));
|
||||||
|
Assert.IsFalse(args.HasValue("aaaaa"));
|
||||||
|
Assert.AreEqual("test_value \"My Test Value\"", args.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestFlagAndValueMix(){
|
||||||
|
CommandLineArgs args = new CommandLineArgs();
|
||||||
|
|
||||||
|
args.AddFlag("my_test_flag_1");
|
||||||
|
args.AddFlag("my_test_flag_2");
|
||||||
|
args.AddFlag("aAaAa");
|
||||||
|
|
||||||
|
args.SetValue("test_value", "My Test Value");
|
||||||
|
args.SetValue("aAaAa", "aaaaa");
|
||||||
|
|
||||||
|
Assert.AreEqual(5, args.Count);
|
||||||
|
Assert.IsTrue(args.HasFlag("aaaaa"));
|
||||||
|
Assert.IsTrue(args.HasValue("aaaaa"));
|
||||||
|
Assert.AreEqual("my_test_flag_1 my_test_flag_2 aaaaa test_value \"My Test Value\" aaaaa \"aaaaa\"", args.ToString());
|
||||||
|
|
||||||
|
var dict = new Dictionary<string, string>();
|
||||||
|
args.ToDictionary(dict); // loses 'aaaaa' flag
|
||||||
|
|
||||||
|
Assert.AreEqual(4, dict.Count);
|
||||||
|
Assert.AreEqual("1", dict["my_test_flag_1"]);
|
||||||
|
Assert.AreEqual("1", dict["my_test_flag_2"]);
|
||||||
|
Assert.AreEqual("My Test Value", dict["test_value"]);
|
||||||
|
Assert.AreEqual("aaaaa", dict["aaaaa"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestClone(){
|
||||||
|
CommandLineArgs args = new CommandLineArgs();
|
||||||
|
|
||||||
|
args.AddFlag("my_test_flag_1");
|
||||||
|
args.AddFlag("my_test_flag_2");
|
||||||
|
args.AddFlag("aAaAa");
|
||||||
|
|
||||||
|
args.SetValue("test_value", "My Test Value");
|
||||||
|
args.SetValue("aAaAa", "aaaaa");
|
||||||
|
|
||||||
|
CommandLineArgs clone = args.Clone();
|
||||||
|
args.RemoveFlag("aaaaa");
|
||||||
|
args.RemoveValue("aaaaa");
|
||||||
|
clone.RemoveFlag("my_test_flag_1");
|
||||||
|
clone.RemoveFlag("my_test_flag_2");
|
||||||
|
clone.RemoveValue("test_value");
|
||||||
|
|
||||||
|
Assert.AreEqual(3, args.Count);
|
||||||
|
Assert.AreEqual(2, clone.Count);
|
||||||
|
|
||||||
|
Assert.AreEqual("my_test_flag_1 my_test_flag_2 test_value \"My Test Value\"", args.ToString());
|
||||||
|
Assert.AreEqual("aaaaa aaaaa \"aaaaa\"", clone.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestEmptyStringArray(){
|
||||||
|
CommandLineArgs args;
|
||||||
|
|
||||||
|
args = CommandLineArgs.FromStringArray('-', new string[0]);
|
||||||
|
Assert.AreEqual(0, args.Count);
|
||||||
|
|
||||||
|
args = CommandLineArgs.FromStringArray('-', new string[]{ "", "+fail", "@nope" });
|
||||||
|
Assert.AreEqual(0, args.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestValidStringArray(){
|
||||||
|
CommandLineArgs args;
|
||||||
|
|
||||||
|
args = CommandLineArgs.FromStringArray('-', new string[]{ "-flag1", "-flag2", "-FLAG3" });
|
||||||
|
Assert.AreEqual(3, args.Count);
|
||||||
|
Assert.IsTrue(args.HasFlag("-flag1"));
|
||||||
|
Assert.IsTrue(args.HasFlag("-flag2"));
|
||||||
|
Assert.IsTrue(args.HasFlag("-flag3"));
|
||||||
|
|
||||||
|
args = CommandLineArgs.FromStringArray('-', new string[]{ "-flag", "-value", "Here is some text!" });
|
||||||
|
Assert.AreEqual(2, args.Count);
|
||||||
|
Assert.IsTrue(args.HasFlag("-flag"));
|
||||||
|
Assert.IsTrue(args.HasValue("-value"));
|
||||||
|
Assert.AreEqual("Here is some text!", args.GetValue("-value", string.Empty));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
tests/Core/Utils/TestCommandLineArgsParser.cs
Normal file
32
tests/Core/Utils/TestCommandLineArgsParser.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using TweetDck.Core.Utils;
|
||||||
|
|
||||||
|
namespace UnitTests.Core.Utils{
|
||||||
|
[TestClass]
|
||||||
|
public class TestCommandLineArgsParser{
|
||||||
|
[TestMethod]
|
||||||
|
public void TestEmptyString(){
|
||||||
|
Assert.AreEqual(0, CommandLineArgsParser.ReadCefArguments("").Count);
|
||||||
|
Assert.AreEqual(0, CommandLineArgsParser.ReadCefArguments(" ").Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestValidString(){
|
||||||
|
CommandLineArgs args = CommandLineArgsParser.ReadCefArguments("--aaa --bbb --first-value=123 --SECOND-VALUE=\"a b c d e\" --ccc");
|
||||||
|
// cef has no flags, flag arguments have a value of 1
|
||||||
|
// the processing removes all dashes in front of each key
|
||||||
|
|
||||||
|
Assert.AreEqual(5, args.Count);
|
||||||
|
Assert.IsTrue(args.HasValue("aaa"));
|
||||||
|
Assert.IsTrue(args.HasValue("bbb"));
|
||||||
|
Assert.IsTrue(args.HasValue("ccc"));
|
||||||
|
Assert.IsTrue(args.HasValue("first-value"));
|
||||||
|
Assert.IsTrue(args.HasValue("second-value"));
|
||||||
|
Assert.AreEqual("1", args.GetValue("aaa", string.Empty));
|
||||||
|
Assert.AreEqual("1", args.GetValue("bbb", string.Empty));
|
||||||
|
Assert.AreEqual("1", args.GetValue("ccc", string.Empty));
|
||||||
|
Assert.AreEqual("123", args.GetValue("first-value", string.Empty));
|
||||||
|
Assert.AreEqual("a b c d e", args.GetValue("second-value", string.Empty));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
201
tests/Core/Utils/TestTwoKeyDictionary.cs
Normal file
201
tests/Core/Utils/TestTwoKeyDictionary.cs
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using TweetDck.Core.Utils;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace UnitTests.Core.Utils{
|
||||||
|
[TestClass]
|
||||||
|
public class TestTwoKeyDictionary{
|
||||||
|
private static TwoKeyDictionary<string, int, string> CreateDict(){
|
||||||
|
TwoKeyDictionary<string, int, string> dict = new TwoKeyDictionary<string, int, string>();
|
||||||
|
|
||||||
|
dict.Add("aaa", 0, "x");
|
||||||
|
dict.Add("aaa", 1, "y");
|
||||||
|
dict.Add("aaa", 2, "z");
|
||||||
|
|
||||||
|
dict.Add("bbb", 0, "test 1");
|
||||||
|
dict.Add("bbb", 10, "test 2");
|
||||||
|
dict.Add("bbb", 20, "test 3");
|
||||||
|
dict.Add("bbb", 30, "test 4");
|
||||||
|
|
||||||
|
dict.Add("ccc", -5, "");
|
||||||
|
dict.Add("", 0, "");
|
||||||
|
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestAdd(){
|
||||||
|
CreateDict();
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
[ExpectedException(typeof(ArgumentException))]
|
||||||
|
public void TestAddDuplicate(){
|
||||||
|
var dict = new TwoKeyDictionary<string, int, string>();
|
||||||
|
|
||||||
|
dict.Add("aaa", 0, "test");
|
||||||
|
dict.Add("aaa", 0, "oops");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestAccessor(){
|
||||||
|
var dict = CreateDict();
|
||||||
|
|
||||||
|
// get
|
||||||
|
|
||||||
|
Assert.AreEqual("x", dict["aaa", 0]);
|
||||||
|
Assert.AreEqual("y", dict["aaa", 1]);
|
||||||
|
Assert.AreEqual("z", dict["aaa", 2]);
|
||||||
|
|
||||||
|
Assert.AreEqual("test 3", dict["bbb", 20]);
|
||||||
|
|
||||||
|
Assert.AreEqual("", dict["ccc", -5]);
|
||||||
|
Assert.AreEqual("", dict["", 0]);
|
||||||
|
|
||||||
|
// set
|
||||||
|
|
||||||
|
dict["aaa", 0] = "replaced entry";
|
||||||
|
Assert.AreEqual("replaced entry", dict["aaa", 0]);
|
||||||
|
|
||||||
|
dict["aaa", 3] = "new entry";
|
||||||
|
Assert.AreEqual("new entry", dict["aaa", 3]);
|
||||||
|
|
||||||
|
dict["xxxxx", 150] = "new key and entry";
|
||||||
|
Assert.AreEqual("new key and entry", dict["xxxxx", 150]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
[ExpectedException(typeof(KeyNotFoundException))]
|
||||||
|
public void TestAccessorMissingKey1(){
|
||||||
|
var _ = CreateDict()["missing", 0];
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
[ExpectedException(typeof(KeyNotFoundException))]
|
||||||
|
public void TestAccessorMissingKey2(){
|
||||||
|
var _ = CreateDict()["aaa", 3];
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestClear(){
|
||||||
|
var dict = CreateDict();
|
||||||
|
|
||||||
|
Assert.IsTrue(dict.Contains("bbb"));
|
||||||
|
dict.Clear("bbb");
|
||||||
|
Assert.IsTrue(dict.Contains("bbb"));
|
||||||
|
|
||||||
|
Assert.IsTrue(dict.Contains(""));
|
||||||
|
dict.Clear("");
|
||||||
|
Assert.IsTrue(dict.Contains(""));
|
||||||
|
|
||||||
|
Assert.IsTrue(dict.Contains("aaa"));
|
||||||
|
Assert.IsTrue(dict.Contains("ccc"));
|
||||||
|
dict.Clear();
|
||||||
|
Assert.IsFalse(dict.Contains("aaa"));
|
||||||
|
Assert.IsFalse(dict.Contains("ccc"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
[ExpectedException(typeof(KeyNotFoundException))]
|
||||||
|
public void TestClearMissingKey(){
|
||||||
|
CreateDict().Clear("missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestContains(){
|
||||||
|
var dict = CreateDict();
|
||||||
|
|
||||||
|
// positive
|
||||||
|
|
||||||
|
Assert.IsTrue(dict.Contains("aaa"));
|
||||||
|
Assert.IsTrue(dict.Contains("aaa", 0));
|
||||||
|
Assert.IsTrue(dict.Contains("aaa", 1));
|
||||||
|
Assert.IsTrue(dict.Contains("aaa", 2));
|
||||||
|
|
||||||
|
Assert.IsTrue(dict.Contains("ccc"));
|
||||||
|
Assert.IsTrue(dict.Contains("ccc", -5));
|
||||||
|
|
||||||
|
Assert.IsTrue(dict.Contains(""));
|
||||||
|
Assert.IsTrue(dict.Contains("", 0));
|
||||||
|
|
||||||
|
// negative
|
||||||
|
|
||||||
|
Assert.IsFalse(dict.Contains("missing"));
|
||||||
|
Assert.IsFalse(dict.Contains("missing", 999));
|
||||||
|
|
||||||
|
Assert.IsFalse(dict.Contains("aaa", 3));
|
||||||
|
Assert.IsFalse(dict.Contains("", -1));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestCount(){
|
||||||
|
var dict = CreateDict();
|
||||||
|
|
||||||
|
Assert.AreEqual(9, dict.Count());
|
||||||
|
Assert.AreEqual(3, dict.Count("aaa"));
|
||||||
|
Assert.AreEqual(4, dict.Count("bbb"));
|
||||||
|
Assert.AreEqual(1, dict.Count("ccc"));
|
||||||
|
Assert.AreEqual(1, dict.Count(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
[ExpectedException(typeof(KeyNotFoundException))]
|
||||||
|
public void TestCountMissingKey(){
|
||||||
|
CreateDict().Count("missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestRemove(){
|
||||||
|
var dict = CreateDict();
|
||||||
|
|
||||||
|
// negative
|
||||||
|
Assert.IsFalse(dict.Remove("missing"));
|
||||||
|
Assert.IsFalse(dict.Remove("aaa", 3));
|
||||||
|
|
||||||
|
// positive
|
||||||
|
|
||||||
|
Assert.IsTrue(dict.Contains("aaa"));
|
||||||
|
Assert.IsTrue(dict.Remove("aaa"));
|
||||||
|
Assert.IsFalse(dict.Contains("aaa"));
|
||||||
|
|
||||||
|
Assert.IsTrue(dict.Contains("bbb", 10));
|
||||||
|
Assert.IsTrue(dict.Remove("bbb", 10));
|
||||||
|
Assert.IsFalse(dict.Contains("bbb", 10));
|
||||||
|
Assert.IsTrue(dict.Contains("bbb"));
|
||||||
|
Assert.IsTrue(dict.Contains("bbb", 20));
|
||||||
|
|
||||||
|
Assert.IsTrue(dict.Remove("bbb", 0));
|
||||||
|
Assert.IsTrue(dict.Remove("bbb", 20));
|
||||||
|
Assert.IsTrue(dict.Remove("bbb", 30));
|
||||||
|
Assert.IsFalse(dict.Contains("bbb"));
|
||||||
|
|
||||||
|
Assert.IsTrue(dict.Contains(""));
|
||||||
|
Assert.IsTrue(dict.Remove("", 0));
|
||||||
|
Assert.IsFalse(dict.Contains(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestTryGetValue(){
|
||||||
|
var dict = CreateDict();
|
||||||
|
string val;
|
||||||
|
|
||||||
|
// positive
|
||||||
|
|
||||||
|
Assert.IsTrue(dict.TryGetValue("bbb", 10, out val));
|
||||||
|
Assert.AreEqual("test 2", val);
|
||||||
|
|
||||||
|
Assert.IsTrue(dict.TryGetValue("ccc", -5, out val));
|
||||||
|
Assert.AreEqual("", val);
|
||||||
|
|
||||||
|
Assert.IsTrue(dict.TryGetValue("", 0, out val));
|
||||||
|
Assert.AreEqual("", val);
|
||||||
|
|
||||||
|
// negative
|
||||||
|
|
||||||
|
Assert.IsFalse(dict.TryGetValue("ccc", -50, out val));
|
||||||
|
Assert.IsFalse(dict.TryGetValue("", 1, out val));
|
||||||
|
Assert.IsFalse(dict.TryGetValue("missing", 0, out val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
tests/Properties/AssemblyInfo.cs
Normal file
35
tests/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// General Information about an assembly is controlled through the following
|
||||||
|
// set of attributes. Change these attribute values to modify the information
|
||||||
|
// associated with an assembly.
|
||||||
|
[assembly: AssemblyTitle("UnitTests")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("UnitTests")]
|
||||||
|
[assembly: AssemblyCopyright("")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// Setting ComVisible to false makes the types in this assembly not visible
|
||||||
|
// to COM components. If you need to access a type in this assembly from
|
||||||
|
// COM, set the ComVisible attribute to true on that type.
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||||
|
[assembly: Guid("99036b78-aad6-4a76-8bf3-40c77eca2464")]
|
||||||
|
|
||||||
|
// Version information for an assembly consists of the following four values:
|
||||||
|
//
|
||||||
|
// Major Version
|
||||||
|
// Minor Version
|
||||||
|
// Build Number
|
||||||
|
// Revision
|
||||||
|
//
|
||||||
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
|
// by using the '*' as shown below:
|
||||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
65
tests/TestUtils.cs
Normal file
65
tests/TestUtils.cs
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
|
||||||
|
namespace UnitTests{
|
||||||
|
public static class TestUtils{
|
||||||
|
private static readonly HashSet<string> CreatedFiles = new HashSet<string>();
|
||||||
|
|
||||||
|
public static void WriteText(string file, string text){
|
||||||
|
DeleteFileOnExit(file);
|
||||||
|
File.WriteAllText(file, text, Encoding.UTF8);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WriteLines(string file, IEnumerable<string> lines){
|
||||||
|
DeleteFileOnExit(file);
|
||||||
|
File.WriteAllLines(file, lines, Encoding.UTF8);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FileStream WriteFile(string file){
|
||||||
|
DeleteFileOnExit(file);
|
||||||
|
return new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ReadText(string file){
|
||||||
|
try{
|
||||||
|
return File.ReadAllText(file, Encoding.UTF8);
|
||||||
|
}catch(Exception){
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<string> ReadLines(string file){
|
||||||
|
try{
|
||||||
|
return File.ReadLines(file, Encoding.UTF8);
|
||||||
|
}catch(Exception){
|
||||||
|
return Enumerable.Empty<string>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FileStream ReadFile(string file){
|
||||||
|
return new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DeleteFileOnExit(string file){
|
||||||
|
CreatedFiles.Add(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestClass]
|
||||||
|
public static class Cleanup{
|
||||||
|
[AssemblyCleanup]
|
||||||
|
public static void DeleteFilesOnExit(){
|
||||||
|
foreach(string file in CreatedFiles){
|
||||||
|
try{
|
||||||
|
File.Delete(file);
|
||||||
|
}catch(Exception){
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
91
tests/UnitTests.csproj
Normal file
91
tests/UnitTests.csproj
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{A958FA7A-4A2C-42A7-BFA0-159343483F4E}</ProjectGuid>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>UnitTests</RootNamespace>
|
||||||
|
<AssemblyName>UnitTests</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||||
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
||||||
|
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||||
|
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
|
||||||
|
<IsCodedUITest>False</IsCodedUITest>
|
||||||
|
<TestProjectType>UnitTest</TestProjectType>
|
||||||
|
<TargetFrameworkProfile />
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||||
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
|
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||||
|
<Prefer32Bit>false</Prefer32Bit>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||||
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
|
<OutputPath>bin\x86\Release\</OutputPath>
|
||||||
|
<Prefer32Bit>false</Prefer32Bit>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Core">
|
||||||
|
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
<Choose>
|
||||||
|
<When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'">
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
|
||||||
|
</ItemGroup>
|
||||||
|
</When>
|
||||||
|
<Otherwise>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Otherwise>
|
||||||
|
</Choose>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Core\Settings\TestCombinedFileStream.cs" />
|
||||||
|
<Compile Include="Core\Utils\TestBrowserUtils.cs" />
|
||||||
|
<Compile Include="Core\Utils\TestCommandLineArgs.cs" />
|
||||||
|
<Compile Include="Core\Utils\TestCommandLineArgsParser.cs" />
|
||||||
|
<Compile Include="Core\Utils\TestTwoKeyDictionary.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="TestUtils.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\TweetDck.csproj">
|
||||||
|
<Project>{2389a7cd-e0d3-4706-8294-092929a33a2d}</Project>
|
||||||
|
<Name>TweetDck</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<Choose>
|
||||||
|
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="Microsoft.VisualStudio.QualityTools.CodedUITestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.VisualStudio.TestTools.UITest.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.VisualStudio.TestTools.UITest.Extension, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.VisualStudio.TestTools.UITesting, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
</When>
|
||||||
|
</Choose>
|
||||||
|
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.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">
|
||||||
|
</Target>
|
||||||
|
<Target Name="AfterBuild">
|
||||||
|
</Target>
|
||||||
|
-->
|
||||||
|
</Project>
|
Reference in New Issue
Block a user